New Nasal code, with lots of input binding handlers in controls.nas.
[fg:toms-fgdata.git] / Nasal / controls.nas
1 startEngine = func {
2     sel = props.globals.getNode("/sim/input/selected");
3     engs = props.globals.getNode("/controls/engines").getChildren("engine");
4     for(i=0; i<size(engs); i=i+1) {
5         select = sel.getChild("engine", i);
6         if(select != nil and select.getValue()) {
7             engs[i].getNode("starter").setBoolValue(1);
8         }
9     }
10 }
11
12 selectEngine = func {
13     sel = props.globals.getNode("/sim/input/selected").getChildren("engine");
14     foreach(node; sel) { node.setBoolValue(node.getIndex() == arg[0]); }
15 }
16
17 selectAllEngines = func {
18     sel = props.globals.getNode("/sim/input/selected").getChildren("engine");
19     foreach(node; sel) { node.setBoolValue(1); }
20 }
21
22 stepMagnetos = func {
23     change = arg[0];
24     engs = props.globals.getNode("/controls/engines").getChildren("engine");
25     sel = props.globals.getNode("/sim/input/selected");
26     for(i=0; i<size(engs); i=i+1) {
27         select = sel.getChild("engine", i);
28         if(select != nil and select.getValue()) {
29             mag = engs[i].getNode("magnetos", 1);
30             mag.setIntValue(mag.getValue() + change);
31         }
32     }
33 }
34
35 centerFlightControls = func {
36     setprop("/controls/flight/elevator", 0);
37     setprop("/controls/flight/aileron", 0);
38     setprop("/controls/flight/rudder", 0);
39 }
40
41 throttleMouse = func {
42     if(!getprop("/devices/status/mice/mouse[0]/button[1]")) { return; }
43     val = (cmdarg().getNode("offset").getValue() * -4
44            + getprop("/controls/engines/engine/throttle"));
45     if(size(arg) > 0) { val = -val; }
46     props.setAll("/controls/engines/engine", "throttle", val);
47 }
48
49 # Joystick axis handlers (uses cmdarg).  Shouldn't be called from
50 # other contexts.
51 throttleAxis = func {
52     val = cmdarg().getNode("setting").getValue();
53     if(size(arg) > 0) { val = -val; }
54     props.setAll("/controls/engines/engine", "throttle", (1 - val)/2);
55 }
56 mixtureAxis = func {
57     val = cmdarg().getNode("setting").getValue();
58     if(size(arg) > 0) { val = -val; }
59     props.setAll("/controls/engines/engine", "mixture", (1 - val)/2);
60 }
61 propellerAxis = func {
62     val = cmdarg().getNode("setting").getValue();
63     if(size(arg) > 0) { val = -val; }
64     props.setAll("/controls/engines/engine", "propeller-pitch", (1 - val)/2);
65 }
66
67 ##
68 # Wrapper around stepProps() which emulates the "old" flap behavior for
69 # configurations that aren't using the new mechanism.
70 #
71 stepFlaps = func {
72     if(props.globals.getNode("/sim/flaps") != nil) {
73         stepProps("/controls/flight/flaps", "/sim/flaps", arg[0]);
74         return;
75     }
76     # Hard-coded flaps movement in 3 equal steps:
77     val = 0.3333334 * arg[0] + getprop("/controls/flight/flaps");
78     if(val > 1) { val = 1 } elsif(val < 0) { val = 0 }
79     setprop("/controls/flight/flaps", val);
80 }
81
82 ##
83 # Steps through an "array" of property settings.  The first argument
84 # specifies a destination property.  The second is a string containing
85 # a global property tree.  This tree should contain an array of
86 # indexed <setting> children.  This function will maintain a
87 # <current-setting> child, which contains the index of the currently
88 # active setting.  The third argument specifies an integer delta,
89 # indicating how many steps to move through the setting array.
90 # Note that because of the magic of the property system, this
91 # mechanism works for all scalar property types (bool, int, double,
92 # string).
93 #
94 # TODO: This interface could easily be extended to allow for wrapping,
95 # in addition to clamping, allowing a "cycle" of settings to be
96 # defined.  It could also be hooked up with the interpolate() call,
97 # which would allow the removal of the transition-time feature from
98 # YASim.  Finally, other pre-existing features (the views and engine
99 # magnetos, for instance), work similarly but not compatibly, and
100 # could be integrated.
101 #
102 stepProps = func {
103     dst = props.globals.getNode(arg[0]);
104     array = props.globals.getNode(arg[1]);
105     delta = arg[2];
106     if(dst == nil or array == nil) { return; }
107
108     sets = array.getChildren("setting");
109
110     curr = array.getNode("current-setting", 1).getValue();
111     if(curr == nil) { curr = 0; }
112     curr = curr + delta;
113     if   (curr < 0)           { curr = 0; }
114     elsif(curr >= size(sets)) { curr = size(sets) - 1; }
115
116     array.getNode("current-setting").setIntValue(curr);
117     dst.setValue(sets[curr].getValue());
118 }
119
120 ##
121 # "Slews" a property smoothly, without dependence on the simulator
122 # frame rate.  The first argument is the property name.  The second is
123 # a rate, in units per second.
124 #
125 slewProp = func {
126     prop = arg[0];
127     delta = arg[1] * getprop("/sim/time/delta-realtime-sec");
128     setprop(prop, getprop(prop) + delta);
129 }
130
131 # Standard trim rate, in units per second.  Remember that the full
132 # range of a trim axis is 2.0.  Should probably read this out of a
133 # property...
134 TRIM_RATE = 0.045;
135
136 ##
137 # Handlers.  These are suitable for binding to repeatable button press
138 # events.  They are *not* good for binding to the keyboard, since (at
139 # least) X11 synthesizes its own key repeats.
140 #
141 elevatorTrim = func {
142     slewProp("/controls/flight/elevator-trim", arg[0] * TRIM_RATE); }
143 aileronTrim = func {
144     slewProp("/controls/flight/aileron-trim", arg[0] * TRIM_RATE); }
145 rudderTrim = func {
146     slewProp("/controls/flight/rudder-trim", arg[0] * TRIM_RATE); }
147
148 THROTTLE_RATE = 0.33;
149
150 adjThrottle = func {
151     adjEngControl("throttle", arg[0]); }
152 adjMixture = func {
153     adjEngControl("mixture", arg[0]); }
154 adjPropeller = func {
155     adjEngControl("propeller-pitch", arg[0]); }
156
157 adjEngControl = func {
158     engs = props.globals.getNode("/controls/engines").getChildren("engine");
159     delta = arg[1] * THROTTLE_RATE * getprop("/sim/time/delta-reltime-sec");
160     foreach(e; engs) {
161         node = e.getNode(arg[0], 1);
162         node.setValue(node.getValue + delta);
163     }
164 }
165
166 ##
167 # Joystick axis handlers.  Don't call from other contexts.
168 #
169 elevatorTrimAxis = func { elevatorTrim(cmdarg().getNode("value").getValue()); }
170 aileronTrimAxis = func { aileronTrim(cmdarg().getNode("value").getValue()); }
171 rudderTrimAxis = func { rudderTrim(cmdarg().getNode("value").getValue()); }
172