diff --git a/Nasal/aircraft.nas b/Nasal/aircraft.nas index e7f716545..c907c9839 100644 --- a/Nasal/aircraft.nas +++ b/Nasal/aircraft.nas @@ -330,18 +330,19 @@ var lowpass = { # angular lowpass # ============================================================================== # same as above, but for angles. Filters sin/cos separately and calculates the -# angle again from them. This avoids unexpected jumps from 180 to -179.99 degree. +# angle again from them. This avoids unexpected jumps from 179.99 to -180 degree. # var angular_lowpass = { new : func(coeff) { var m = { parents : [angular_lowpass] }; m.sin = lowpass.new(coeff); m.cos = lowpass.new(coeff); + m.buf = nil; return m; }, filter : func(v) { v *= D2R; - math.atan2(me.sin.filter(math.sin(v)), me.cos.filter(math.cos(v))) * R2D; + me.buf = math.atan2(me.sin.filter(math.sin(v)), me.cos.filter(math.cos(v))) * R2D; }, set : func(v) { v *= D2R; @@ -349,7 +350,7 @@ var angular_lowpass = { me.cos.set(math.cos(v)); }, get : func { - math.atan2(me.sin.get(), me.cos.get()) * R2D; + me.buf; }, }; diff --git a/Nasal/controls.nas b/Nasal/controls.nas index 006ef28f5..c5d092819 100644 --- a/Nasal/controls.nas +++ b/Nasal/controls.nas @@ -1,8 +1,8 @@ -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", "mixture", (1 - val)/2); } -propellerAxis = func { - val = cmdarg().getNode("setting").getValue(); +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); } -carbHeatAxis = func { - val = cmdarg().getNode("setting").getValue(); +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); } @@ -110,36 +110,36 @@ carbHeatAxis = func { # Wrapper around stepProps() which emulates the "old" flap behavior for # configurations that aren't using the new mechanism. # -flapsDown = func { +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: - val = 0.3333334 * arg[0] + getprop("/controls/flight/flaps"); + 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); } -stepSpoilers = func { +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: - val = 0.25 * arg[0] + getprop("/controls/flight/spoilers"); + 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); } -stepSlats = func { +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: - val = 0.25 * arg[0] + getprop("/controls/flight/slats"); + 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); } @@ -164,15 +164,15 @@ stepSlats = func { # 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]; +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; } - sets = array.getChildren("setting"); + var sets = array.getChildren("setting"); - curr = array.getNode("current-setting", 1).getValue(); + var curr = array.getNode("current-setting", 1).getValue(); if(curr == nil) { curr = 0; } curr = curr + delta; if (curr < 0) { curr = 0; } @@ -190,47 +190,46 @@ stepProps = func { # 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"); +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... -TRIM_RATE = 0.045; +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. # -elevatorTrim = func { +var elevatorTrim = func { slewProp("/controls/flight/elevator-trim", arg[0] * TRIM_RATE); } -aileronTrim = func { +var aileronTrim = func { slewProp("/controls/flight/aileron-trim", arg[0] * TRIM_RATE); } -rudderTrim = func { +var rudderTrim = func { slewProp("/controls/flight/rudder-trim", arg[0] * TRIM_RATE); } -THROTTLE_RATE = 0.33; +var THROTTLE_RATE = 0.33; -adjThrottle = func { +var adjThrottle = func { adjEngControl("throttle", arg[0]); } -adjMixture = func { +var adjMixture = func { adjEngControl("mixture", arg[0]); } -adjCondition = func { +var adjCondition = func { adjEngControl("condition", arg[0]); } -adjPropeller = func { +var 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) { +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()) { - node = e.getNode(arg[0], 1); + var node = e.getNode(arg[0], 1); node.setValue(node.getValue() + delta); } } @@ -242,116 +241,116 @@ adjEngControl = func { 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); + 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 { - 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 ); - } + 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 -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 ); - } +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" ) { - 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() == "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" ) { - 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 ); - } + 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 -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 ); - } +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. # -elevatorTrimAxis = func { elevatorTrim(cmdarg().getNode("value").getValue()); } -aileronTrimAxis = func { aileronTrim(cmdarg().getNode("value").getValue()); } -rudderTrimAxis = func { rudderTrim(cmdarg().getNode("value").getValue()); } +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 { - if (arg[0] < 0) { +var gearDown = func(v) { + if (v < 0) { setprop("/controls/gear/gear-down", 0); - } elsif (arg[0] > 0) { + } elsif (v > 0) { setprop("/controls/gear/gear-down", 1); } } diff --git a/Nasal/fuel.nas b/Nasal/fuel.nas index 86654111d..f9bfeb94d 100644 --- a/Nasal/fuel.nas +++ b/Nasal/fuel.nas @@ -13,10 +13,9 @@ var UPDATE_PERIOD = 0.3; -update = func { - if (fuel_freeze) { +var update = func { + if (fuel_freeze) return; - } var consumed_fuel = 0; foreach (var e; engines) { @@ -25,16 +24,14 @@ update = func { fuel.setDoubleValue(0); } - if (!consumed_fuel) { + if (!consumed_fuel) return; - } var selected_tanks = []; foreach (var t; tanks) { var cap = t.getNode("capacity-gal_us").getValue(); - if (cap > 0.01 and t.getNode("selected").getBoolValue()) { + if (cap > 0.01 and t.getNode("selected").getBoolValue()) append(selected_tanks, t); - } } # Subtract fuel from tanks, set auxilliary properties. Set out-of-fuel @@ -52,11 +49,10 @@ update = func { lbs = 0; # Kill the engines if we're told to, otherwise simply # deselect the tank. - if (t.getNode("kill-when-empty", 1).getBoolValue()) { + if (t.getNode("kill-when-empty", 1).getBoolValue()) out_of_fuel = 1; - } else { + else t.getNode("selected").setBoolValue(0); - } } var gals = lbs / ppg; t.getNode("level-gal_us").setDoubleValue(gals); @@ -79,22 +75,21 @@ update = func { total_gals.setDoubleValue(gals); total_norm.setDoubleValue(gals / cap); - foreach (var e; engines) { + foreach (var e; engines) e.getNode("out-of-fuel").setBoolValue(out_of_fuel); - } } -loop = func { +var loop = func { update(); settimer(loop, UPDATE_PERIOD); } -init_double_prop = func(node, prop, val) { - if (node.getNode(prop) != nil) { +var init_double_prop = func(node, prop, val) { + if (node.getNode(prop) != nil) val = num(node.getNode(prop).getValue()); - } + node.getNode(prop, 1).setDoubleValue(val); } @@ -108,9 +103,7 @@ var total_lbs = nil; var total_norm = nil; -var L = _setlistener("/sim/signals/fdm-initialized", func { - removelistener(L); - +_setlistener("/sim/signals/fdm-initialized", func { setlistener("/sim/freeze/fuel", func { fuel_freeze = cmdarg().getBoolValue() }, 1); total_gals = props.globals.getNode("/consumables/fuel/total-fuel-gals", 1); @@ -124,18 +117,17 @@ var L = _setlistener("/sim/signals/fdm-initialized", func { } foreach (var t; props.globals.getNode("/consumables/fuel", 1).getChildren("tank")) { - if (!size(t.getChildren())) { + if (!size(t.getChildren())) continue; # skip native_fdm.cxx generated zombie tanks - } + append(tanks, t); init_double_prop(t, "level-gal_us", 0.0); init_double_prop(t, "level-lbs", 0.0); init_double_prop(t, "capacity-gal_us", 0.01); # not zero (div/zero issue) init_double_prop(t, "density-ppg", 6.0); # gasoline - if (t.getNode("selected") == nil) { + if (t.getNode("selected") == nil) t.getNode("selected", 1).setBoolValue(1); - } } loop(); diff --git a/Nasal/geo.nas b/Nasal/geo.nas index 5fc4c9ee7..d72b5c234 100644 --- a/Nasal/geo.nas +++ b/Nasal/geo.nas @@ -296,7 +296,7 @@ var _put_model = func(path, lat, lon, elev_m = nil, hdg = 0, pitch = 0, roll = 0 if (elev_m == nil) elev_m = elevation(lat, lon); if (elev_m == nil) - die("can't get elevation for " ~ lat ~ "/" ~ lon); + die("geo.put_model(): can't get elevation for " ~ lat ~ "/" ~ lon); var n = props.globals.getNode("/models"); for (var i = 0; 1; i += 1) if (n.getChild("model", i, 0) == nil) diff --git a/Nasal/globals.nas b/Nasal/globals.nas index 96af408eb..a794e6715 100644 --- a/Nasal/globals.nas +++ b/Nasal/globals.nas @@ -116,21 +116,21 @@ printlog = func(level, args...) { # _setlistener("/sim/signals/nasal-dir-initialized", func { var path = getprop("/sim/fg-home") ~ "/Nasal"; - if ((var dir = directory(path)) == nil) return; - foreach (var file; sort(dir, cmp)) { - if (substr(file, -4) != ".nas") continue; + if((var dir = directory(path)) == nil) return; + foreach(var file; sort(dir, cmp)) { + if(substr(file, -4) != ".nas") continue; var module = substr(file, 0, size(file) - 4); file = path ~ "/" ~ file; printlog("info", ">>> executing local Nasal file ", file); - if (!contains(globals, module)) var locals = globals[module] = {}; - elsif (typeof(globals[module]) == "hash") var locals = globals[module]; + if(!contains(globals, module)) var locals = globals[module] = {}; + elsif(typeof(globals[module]) == "hash") var locals = globals[module]; else var locals = {}; var err = []; var code = call(func { compile(io.readfile(file), file) }, nil, err); - if (size(err)) { + if(size(err)) { print(file ~ ": " ~ err[0]); continue; } diff --git a/Nasal/io.nas b/Nasal/io.nas index cea82f4aa..14a7b9ff5 100644 --- a/Nasal/io.nas +++ b/Nasal/io.nas @@ -43,7 +43,7 @@ foreach(fmt; keys(ifmts)) # prefix prepended to the name. If the prefix is nil, then attributes # are ignored. Returns nil on error. # -var readxml = func(file, prefix = "___") { +var readxml = func(path, prefix = "___") { var stack = [[{}, ""]]; var node = props.Node.new(); var tree = node; # prevent GC @@ -69,7 +69,7 @@ var readxml = func(file, prefix = "___") { var data = func(d) { stack[-1][1] ~= d; } - return parsexml(file, start, end, data) == nil ? nil : tree; + return parsexml(path, start, end, data) == nil ? nil : tree; } # Writes a property tree as returned by readxml() to a file. Children @@ -77,39 +77,38 @@ var readxml = func(file, prefix = "___") { # their parent. must contain exactly one child, which will # become the XML file's outermost element. # -var writexml = func(file, node, indent = "\t", prefix = "___") { +var writexml = func(path, node, indent = "\t", prefix = "___") { var root = node.getChildren(); if(!size(root)) die("writexml(): tree doesn't have a root node"); - if(substr(file, -4) != ".xml") - file ~= ".xml"; - var fh = open(file, "w"); - var pre = size(prefix); - write(fh, "\n\n"); + if(substr(path, -4) != ".xml") + path ~= ".xml"; + var file = open(path, "w"); + write(file, "\n\n"); var writenode = func(n, ind = "") { var name = n.getName(); var name_attr = name; var children = []; foreach(var c; n.getChildren()) { - var aname = c.getName(); - if(substr(aname, 0, pre) == prefix) - name_attr ~= " " ~ substr(aname, pre) ~ '="' ~ c.getValue() ~ '"'; + var a = c.getName(); + if(substr(a, 0, size(prefix)) == prefix) + name_attr ~= " " ~ substr(a, size(prefix)) ~ '="' ~ c.getValue() ~ '"'; else append(children, c); } if(size(children)) { - write(fh, ind ~ "<" ~ name_attr ~ ">\n"); + write(file, ind ~ "<" ~ name_attr ~ ">\n"); foreach(var c; children) writenode(c, ind ~ indent); - write(fh, ind ~ "\n"); + write(file, ind ~ "\n"); } elsif((var value = n.getValue()) != nil) { - write(fh, ind ~ "<" ~ name_attr ~ ">" ~ value ~ "\n"); + write(file, ind ~ "<" ~ name_attr ~ ">" ~ value ~ "\n"); } else { - write(fh, ind ~ "<" ~ name_attr ~ "/>\n"); + write(file, ind ~ "<" ~ name_attr ~ "/>\n"); } } writenode(root[0]); - close(fh); + close(file); if(size(root) != 1) die("writexml(): tree has more than one root node"); } diff --git a/Nasal/string.nas b/Nasal/string.nas index 5461ece20..7873958cd 100644 --- a/Nasal/string.nas +++ b/Nasal/string.nas @@ -68,7 +68,7 @@ var imatch = func(a, b) match(lc(a), lc(b)); # \? stands for a question mark (not the "any single character" placeholder) # [] stands for a group of characters: # [abc] stands for letters a, b or c -# [^abc] stands for any character but a, b, and c +# [^abc] stands for any character but a, b, and c (^ as first character -> inversion) # [1-4] stands for digits 1 to 4 (1, 2, 3, 4) # [1-4-] stands for digits 1 to 4, and the minus # [-1-4] same as above @@ -76,8 +76,11 @@ var imatch = func(a, b) match(lc(a), lc(b)); # [1-3-6-9] stands for digits 1 to 3, minus, and 6 to 9 # [][] stands for the closing and the opening bracket (']' must be first!) # [^^] stands for all characters but the caret symbol +# [\/] stands for a backslash or a slash (the backslash isn't an +# escape character in a [] character group) # -# Note that a minus can't be a range delimiter, as in [a--b] +# Note that a minus can't be a range delimiter, as in [a--e], +# which would be interpreted as any of a, e, or minus. # # Example: # string.match(name, "*[0-9].xml"); ... true if 'name' ends with digit followed by ".xml"