From 4ee336745272604f832c1d6cb0be9e7245447107 Mon Sep 17 00:00:00 2001 From: vmmeazza Date: Sat, 8 Dec 2007 19:01:03 +0000 Subject: [PATCH] Stuart Buchanan: Add chat menu --- ATC/chat-menu-entries.xml | 142 ++++++++++++++ Nasal/multiplayer.nas | 184 +++++++++++++----- gui/dialogs/chat-menu.xml | 390 ++++++++++++++++++++++++++++++++++++++ gui/menubar.xml | 8 +- keyboard.xml | 24 ++- preferences.xml | 1 + 6 files changed, 695 insertions(+), 54 deletions(-) create mode 100644 ATC/chat-menu-entries.xml create mode 100644 gui/dialogs/chat-menu.xml diff --git a/ATC/chat-menu-entries.xml b/ATC/chat-menu-entries.xml new file mode 100644 index 000000000..2abc2023e --- /dev/null +++ b/ATC/chat-menu-entries.xml @@ -0,0 +1,142 @@ + + + + + + [SAFETYCOM/UNICOM] + + * Traffic, # lining up for departure runway (. + + + * Traffic, # taking off runway (. + + + * Traffic, # overhead, joining for runway (. + + + * Traffic, # crosswind, runway (. + + + * Traffic, # downwind, runway (. + + + * Traffic, # base leg, runway (. + + + * Traffic, # final, runway (. + + + * Traffic, # going around. + + + * Traffic, runway vacated. + + + + + [ATC] + + [Ground] + + * Ground, #, request taxi instructions for departure. + + + * Ground, # request departure information. + + + * Ground, # request startup. + + + * Ground, # request pushback. + + + * Ground, # request taxi. + + + #, ready for departure. + + + Line up #. + + + Cleared for take-off #. + + + # runway vacated. + + + + [Air] + + * Tower, ^ miles ! altitude $ feet request joining instructions. + + + * Tower, ^ miles ! altitude $ feet request straight in approach runway (. + + + # crosswind. + + + # downwind + + touch and go. + + + full stop. + + + + # base. + + + # final. + + + # long final. + + + # going around. + + + + [Enroute] + + * Approach, # inbound from the !, IFR ^ miles at $ feet. + + + * Approach, # inbound from the !, VFR ^ miles at $ feet. + + + * Approach, request Radar Information Service. + + + + + [Formation] + + Form up on my wing. + + + Break left. + + + Break right. + + + + diff --git a/Nasal/multiplayer.nas b/Nasal/multiplayer.nas index eabc2c76a..635f779c4 100644 --- a/Nasal/multiplayer.nas +++ b/Nasal/multiplayer.nas @@ -10,76 +10,158 @@ var messages = {}; -check_messages = func +var check_messages = func { - var mp = props.globals.getNode("/ai/models").getChildren("multiplayer"); + var mp = props.globals.getNode("/ai/models").getChildren("multiplayer"); + var lseen = {}; - foreach (i; mp) - { - var lmsg = getprop(i.getPath() ~ "/sim/multiplay/chat"); - var lcallsign = getprop(i.getPath() ~ "/callsign"); + foreach (i; mp) + { + var lmsg = getprop(i.getPath() ~ "/sim/multiplay/chat"); + var lcallsign = getprop(i.getPath() ~ "/callsign"); + var lvalid = getprop(i.getPath() ~ "/valid"); - if ((lmsg != nil) and (lmsg != "") and (lcallsign != nil) and (lcallsign != "")) - { - #print("Call Sign: " ~ lcallsign); - #print("lmsg: " ~ lmsg); - #print("Freq: " ~ ltransmitfreq); + if ((lvalid) and + (lmsg != nil) and + (lmsg != "") and + (lcallsign != nil) and + (lcallsign != "")) + { +#print("Call Sign: " ~ lcallsign); +#print("lmsg: " ~ lmsg); +#print("Freq: " ~ ltransmitfreq); - if ((! contains(messages, lcallsign)) or (lmsg != messages[lcallsign])) - { - # Indicate we've seen this message. - messages[lcallsign] = lmsg; - echo_message(lmsg, lcallsign); - } - } - } + if (! contains(lseen, lcallsign)) + { +# Indicate that we've seen this callsign. This handles the case +# where we have two aircraft with the same callsign in the MP +# session. + lseen[lcallsign] = 1; - # Check for new messages every couple of seconds. - settimer(check_messages, 3); + if ((! contains(messages, lcallsign)) or + (! streq(lmsg, messages[lcallsign]))) + { +# Save the message so we don't repeat it. + messages[lcallsign] = lmsg; + +# Display the message. + echo_message(lmsg, lcallsign); + } + } + } + } + +# Check for new messages every couple of seconds. + settimer(check_messages, 3); } -echo_message = func(msg, callsign) +var echo_message = func(msg, callsign) { - if (callsign != nil) - { - msg = callsign ~ ": " ~ msg; - } + if (callsign != nil) + { + msg = callsign ~ ": " ~ msg; + } - var ldisplay = getprop("/sim/multiplay/chat-display"); + var ldisplay = getprop("/sim/multiplay/chat-display"); - if ((ldisplay != nil) and (ldisplay == "1")) - { - # Only display the message to screen if configured. - setprop("/sim/messages/ai-plane", msg); - } + if ((ldisplay != nil) and (ldisplay == "1")) + { +# Only display the message to screen if configured. + setprop("/sim/messages/ai-plane", msg); + } - # Add the chat to the chat history. - var lchat = getprop("/sim/multiplay/chat-history"); +# Add the chat to the chat history. + var lchat = getprop("/sim/multiplay/chat-history"); - if (lchat == nil) - { - setprop("/sim/multiplay/chat-history", msg); - } - else - { - if (substr(lchat, size(lchat) -1, 1) != "\n") - { - lchat = lchat ~ "\n"; - } + if (lchat == nil) + { + setprop("/sim/multiplay/chat-history", msg); + } + else + { + if (substr(lchat, size(lchat) -1, 1) != "\n") + { + lchat = lchat ~ "\n"; + } - setprop("/sim/multiplay/chat-history", lchat ~ msg); - } + setprop("/sim/multiplay/chat-history", lchat ~ msg); + } } - settimer(func { - # Call-back to ensure we see our own messages. - setlistener("/sim/multiplay/chat", func(n) { echo_message(n.getValue(), getprop("/sim/multiplay/callsign")); }); +# Call-back to ensure we see our own messages. + setlistener("/sim/multiplay/chat", func(n) { echo_message(n.getValue(), getprop("/sim/multiplay/callsign")); }); - # check for new messages - check_messages(); +# check for new messages + check_messages(); }, 1); +# Message composition function, activated using the - key. +var prefix = "Chat Message:"; +var input = ""; +var kbdlistener = nil; +var compose_message = func(msg = "") +{ + input = prefix ~ msg; + gui.popupTip(input, 1000000); + + kbdlistener = setlistener("/devices/status/keyboard/event", func (event) { + var key = event.getNode("key"); + +# Only check the key when pressed. + if (!event.getNode("pressed").getValue()) + return; + + if (handle_key(key.getValue())) + key.setValue(0); # drop key event + }); +} + +var handle_key = func(key) +{ + if (key == `\n` or key == `\r`) + { +# CR/LF -> send the message + +# Trim off the prefix + input = substr(input, size(prefix)); +# Send the message and switch off the listener. + setprop("/sim/multiplay/chat", input); + removelistener(kbdlistener); + gui.popdown(); + return 1; + } + elsif (key == 8) + { +# backspace -> remove a character + + if (size(input) > size(prefix)) + { + input = substr(input, 0, size(input) - 1); + gui.popupTip(input, 1000000); + return 1; + } + } + elsif (key == 27) + { +# escape -> cancel + removelistener(kbdlistener); + gui.popdown(); + return 1; + } + elsif ((key > 31) and (key < 128)) + { +# Normal character - add it to the input + input ~= chr(key); + gui.popupTip(input, 1000000); + return 1; + } + else + { +# Unknown character - pass through + return 0; + } +} diff --git a/gui/dialogs/chat-menu.xml b/gui/dialogs/chat-menu.xml new file mode 100644 index 000000000..6a0337d0e --- /dev/null +++ b/gui/dialogs/chat-menu.xml @@ -0,0 +1,390 @@ + + + + chat-menu + 10 + -30 + vbox + false + 1 + + HELVETICA_12 + + + 0 + 0 + 0 + 0 + + + + left + + 0.9 + 0.2 + 0.2 + 1 + + + + + left + + 0.9 + 0.2 + 0.2 + 1 + + + + left + /sim/multiplay/chat-menu/entry[0] + true + + 0.9 + 0.2 + 0.2 + 1 + + + + left + /sim/multiplay/chat-menu/entry[1] + true + + 0.9 + 0.2 + 0.2 + 1 + + + + left + /sim/multiplay/chat-menu/entry[2] + true + + 0.9 + 0.2 + 0.2 + 1 + + + + left + /sim/multiplay/chat-menu/entry[3] + true + + 0.9 + 0.2 + 0.2 + 1 + + + + left + /sim/multiplay/chat-menu/entry[4] + true + + 0.9 + 0.2 + 0.2 + 1 + + + + left + /sim/multiplay/chat-menu/entry[5] + true + + 0.9 + 0.2 + 0.2 + 1 + + + + left + /sim/multiplay/chat-menu/entry[6] + true + + 0.9 + 0.2 + 0.2 + 1 + + + + left + /sim/multiplay/chat-menu/entry[7] + true + + 0.9 + 0.2 + 0.2 + 1 + + + + + + = 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(0); # 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(); + ]]> + + + diff --git a/gui/menubar.xml b/gui/menubar.xml index d98b355e7..fb2bd1148 100644 --- a/gui/menubar.xml +++ b/gui/menubar.xml @@ -161,7 +161,13 @@ chat - + + + ++ dialog-show + chat-menu + + diff --git a/keyboard.xml b/keyboard.xml index b8a2a31da..2bd9e7f05 100644 --- a/keyboard.xml +++ b/keyboard.xml @@ -352,7 +352,17 @@ top down before the key bindings are parsed. - + + - + false + Compose Chat + + dialog-show + chat-menu + + + + . Right brake @@ -773,7 +783,17 @@ top down before the key bindings are parsed. - + + _ + false + Compose Chat + + nasal + + + + + a Increase speed-up. diff --git a/preferences.xml b/preferences.xml index a424bed49..f5200f37c 100644 --- a/preferences.xml +++ b/preferences.xml @@ -472,6 +472,7 @@ Started September 2000 by David Megginson, david@megginson.com Hello 118500000 true +