chat-menu10-30vboxfalse1HELVETICA_120000left0.90.20.21left0.90.20.21left/sim/multiplay/chat-menu/entry[0]true0.90.20.21left/sim/multiplay/chat-menu/entry[1]true0.90.20.21left/sim/multiplay/chat-menu/entry[2]true0.90.20.21left/sim/multiplay/chat-menu/entry[3]true0.90.20.21left/sim/multiplay/chat-menu/entry[4]true0.90.20.21left/sim/multiplay/chat-menu/entry[5]true0.90.20.21left/sim/multiplay/chat-menu/entry[6]true0.90.20.21left/sim/multiplay/chat-menu/entry[7]true0.90.20.21
= 180) course -= 360;
while (course < -180) course += 360;
var deviation = math.abs(course);
if (deviation < max)
{
active_runway = r;
max = deviation;
}
}
# It may be the case that we're taxiing down the runway, past the mid-point.
# If the aircraft is facing the "wrong" way for this runway, then take the
# reciprocal runway.
var heading = getprop("/orientation/heading-magnetic-deg");
var deviation = heading - airport.runways[active_runway].heading;
while (deviation >= 180) deviation -= 360;
while (deviation < -180) deviation += 360;
deviation = math.abs(deviation);
if (deviation > 90)
{
var number = math.mod(num(substr(active_runway, 0, 2)) + 18, 36);
var side = substr(active_runway, 2, 1);
active_runway = sprintf("%02d%s", number, side == "R" ? "L" : side == "L" ? "R" : side);
}
}
else
{
var wind_speed = getprop("/environment/wind-speed-kt");
var wind_from = wind_speed ? getprop("/environment/wind-from-heading-deg") : 270;
var max = -1;
foreach (var r; keys(airport.runways)) {
var curr = airport.runways[r];
var wind = wind_from - curr.heading;
while (wind >= 180) wind -= 360;
while (wind < -180) wind += 360;
var deviation = math.abs(wind) + 1e-20;
var v = (0.01 * curr.length + 0.01 * curr.width) / deviation;
if (v > max) {
max = v;
active_runway = r;
}
}
}
# Find our distance and cardinal direction to the airport.
var directions = split(",", "North,North East,East,South East,South,South West,West,North West");
var loc = geo.aircraft_position();
var airportloc = geo.Coord.new();
airportloc.set_latlon(airport.lat, airport.lon, airport.elevation);
# We want the course _from_ the airport to the aircraft - for reporting ".. approaching from the SW"
var airport_course = airportloc.course_to(loc);
var airport_distance = int((airportloc.distance_to(loc) / 1609) + 0.5);
# Get an index into our array of directions.
var dir = int(math.mod((airport_course + 22.5), 360) / 45);
var airport_direction = directions[dir];
# Now launch the keyboard listener.
var kbdevent = setlistener("/devices/status/keyboard/event", func (event) {
# Only check the key when pressed.
if (!event.getNode("pressed").getValue())
return;
var key = event.getNode("key");
if (handle_key(key.getValue()))
key.setValue(-1); # drop key event
});
var handle_key = func (key) {
# We only handle keys 0-9 and Esc
if (key == 27)
{
# escape -> cancel
removelistener(kbdevent);
gui.popdown();
fgcommand("dialog-close", props.Node.new({"dialog-name": "chat-menu"}));
return 1;
}
if ((key < `0`) or (key > `9`))
{
# pass the event back.
return 0;
}
if (key == `0`)
{
# Go back one level.
text = "";
if (pos.getName() != "config")
{
# Build up the chat string again.
pos = pos.getParent();
var p = pos;
while ((p.getName() != "config") and
(p.getChild("name").getValue() != nil))
{
var t = string.trim(p.getChild("name").getValue());
# Entries that begin with "[" are silent.
if (t[0] != `[`)
{
text = t ~ " " ~ text;
}
p = p.getParent();
}
}
updateDialog();
}
if (key == `1`)
{
# Go to edit mode using the inline editor.
removelistener(kbdevent);
gui.popdown();
multiplayer.compose_message(text);
fgcommand("dialog-close", props.Node.new({"dialog-name": "chat-menu"}));
}
if ((key > `1`) and (key <= `9`))
{
# Select the appropriate new node and update.
# The index starts from position 2.
var i = key - `2`;
if (i > (size(pos.getChildren("menu")) -1))
{
# Drop out if the user has entered a too large value.
return 0;
}
var t = entryNodes[i].getValue();
t = string.trim(substr(t, 3, size(t) -3));
# Entries that begin with "[" are silent.
if (t[0] != `[`)
{
text = text ~ " " ~ t;
}
pos = pos.getChildren("menu")[i];
if (size(pos.getChildren("menu")) == 0)
{
# We've come to the end of the tree - send the message and close
setprop("/sim/multiplay/chat", text);
removelistener(kbdevent);
gui.popdown();
fgcommand("dialog-close", props.Node.new({"dialog-name": "chat-menu"}));
}
else
{
# We've got more tree to traverse.
updateDialog();
}
}
# If we got here, we consumed the event
return 1;
}
# Substitute simple values into the string.
# The values we handle are
#
# % - Type (first word of /sim/description)
# # - callsign
# $ - altitude
# * - airport ID.
# & - course from airport to aircraft - "North West"
# ! - distance from aircraft to airport in miles.
#
var subvals = func (str) {
var t = "";
for (var p = 0; p < size(str); p += 1)
{
if (str[p] == `!`) t ~= airport_direction;
elsif (str[p] == `^`) t ~= airport_distance;
elsif (str[p] == `(`) t ~= active_runway;
elsif (str[p] == `*`) t ~= airport.name;
elsif (str[p] == `%`) t ~= type;
elsif (str[p] == `#`) t ~= callsign;
elsif (str[p] == `$`) t ~= altitude;
else t ~= chr(str[p]);
}
return t;
}
var updateDialog = func {
var children = pos.getChildren("menu");
var i = 0;
foreach(var c; children)
{
var p = i + 2;
var txt = p ~ ': ' ~ subvals(c.getChild("name").getValue());
if (i < 9)
{
entryNodes[i].setValue(txt);
}
i = i + 1;
}
# Set the rest of the dialog to blank.
while (i <= 9)
{
entryNodes[i].setValue("");
i = i + 1;
}
# Write the popup.
gui.popupTip(text, 1000000);
}
# Start by updating the dialog.
updateDialog();
]]>