Stuart Buchanan:
Add chat menu
This commit is contained in:
parent
cf0f21dec3
commit
4ee3367452
6 changed files with 695 additions and 54 deletions
142
ATC/chat-menu-entries.xml
Normal file
142
ATC/chat-menu-entries.xml
Normal file
|
@ -0,0 +1,142 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<PropertyList>
|
||||||
|
<!--
|
||||||
|
|
||||||
|
[ at the start of the <name> tag indicates that the text will not be added to the message.
|
||||||
|
|
||||||
|
|
||||||
|
Special characters (in place of proper substitutions)
|
||||||
|
|
||||||
|
! - airport direction
|
||||||
|
^ - airport distance
|
||||||
|
% - aircraft type (First word from /sim/description
|
||||||
|
# - Callsign
|
||||||
|
$ - Current Altitude
|
||||||
|
* - Airport ID
|
||||||
|
( - current runway
|
||||||
|
|
||||||
|
-->
|
||||||
|
<config>
|
||||||
|
<menu>
|
||||||
|
<name>[SAFETYCOM/UNICOM]</name>
|
||||||
|
<menu>
|
||||||
|
<name>* Traffic, # lining up for departure runway (.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name>* Traffic, # taking off runway (.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name>* Traffic, # overhead, joining for runway (.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name>* Traffic, # crosswind, runway (.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name>* Traffic, # downwind, runway (.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name>* Traffic, # base leg, runway (.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name>* Traffic, # final, runway (.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name>* Traffic, # going around.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name>* Traffic, runway vacated.</name>
|
||||||
|
</menu>
|
||||||
|
</menu>
|
||||||
|
|
||||||
|
<menu>
|
||||||
|
<name>[ATC]</name>
|
||||||
|
<menu>
|
||||||
|
<name>[Ground]</name>
|
||||||
|
<menu>
|
||||||
|
<name>* Ground, #, request taxi instructions for departure.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name>* Ground, # request departure information.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name>* Ground, # request startup.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name>* Ground, # request pushback.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name>* Ground, # request taxi.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name>#, ready for departure.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name>Line up #.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name>Cleared for take-off #.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name># runway vacated.</name>
|
||||||
|
</menu>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name>[Air]</name>
|
||||||
|
<menu>
|
||||||
|
<name>* Tower, ^ miles ! altitude $ feet request joining instructions.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name>* Tower, ^ miles ! altitude $ feet request straight in approach runway (.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name># crosswind.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name># downwind</name>
|
||||||
|
<menu>
|
||||||
|
<name>touch and go.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name>full stop.</name>
|
||||||
|
</menu>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name># base.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name># final.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name># long final.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name># going around.</name>
|
||||||
|
</menu>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name>[Enroute]</name>
|
||||||
|
<menu>
|
||||||
|
<name>* Approach, # inbound from the !, IFR ^ miles at $ feet.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name>* Approach, # inbound from the !, VFR ^ miles at $ feet.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name>* Approach, request Radar Information Service.</name>
|
||||||
|
</menu>
|
||||||
|
</menu>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name>[Formation]</name>
|
||||||
|
<menu>
|
||||||
|
<name>Form up on my wing.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name>Break left.</name>
|
||||||
|
</menu>
|
||||||
|
<menu>
|
||||||
|
<name>Break right.</name>
|
||||||
|
</menu>
|
||||||
|
</menu>
|
||||||
|
</config>
|
||||||
|
</PropertyList>
|
|
@ -10,76 +10,158 @@
|
||||||
|
|
||||||
var messages = {};
|
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)
|
foreach (i; mp)
|
||||||
{
|
{
|
||||||
var lmsg = getprop(i.getPath() ~ "/sim/multiplay/chat");
|
var lmsg = getprop(i.getPath() ~ "/sim/multiplay/chat");
|
||||||
var lcallsign = getprop(i.getPath() ~ "/callsign");
|
var lcallsign = getprop(i.getPath() ~ "/callsign");
|
||||||
|
var lvalid = getprop(i.getPath() ~ "/valid");
|
||||||
|
|
||||||
if ((lmsg != nil) and (lmsg != "") and (lcallsign != nil) and (lcallsign != ""))
|
if ((lvalid) and
|
||||||
{
|
(lmsg != nil) and
|
||||||
#print("Call Sign: " ~ lcallsign);
|
(lmsg != "") and
|
||||||
#print("lmsg: " ~ lmsg);
|
(lcallsign != nil) and
|
||||||
#print("Freq: " ~ ltransmitfreq);
|
(lcallsign != ""))
|
||||||
|
{
|
||||||
|
#print("Call Sign: " ~ lcallsign);
|
||||||
|
#print("lmsg: " ~ lmsg);
|
||||||
|
#print("Freq: " ~ ltransmitfreq);
|
||||||
|
|
||||||
if ((! contains(messages, lcallsign)) or (lmsg != messages[lcallsign]))
|
if (! contains(lseen, lcallsign))
|
||||||
{
|
{
|
||||||
# Indicate we've seen this message.
|
# Indicate that we've seen this callsign. This handles the case
|
||||||
messages[lcallsign] = lmsg;
|
# where we have two aircraft with the same callsign in the MP
|
||||||
echo_message(lmsg, lcallsign);
|
# session.
|
||||||
}
|
lseen[lcallsign] = 1;
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check for new messages every couple of seconds.
|
if ((! contains(messages, lcallsign)) or
|
||||||
settimer(check_messages, 3);
|
(! 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)
|
if (callsign != nil)
|
||||||
{
|
{
|
||||||
msg = callsign ~ ": " ~ msg;
|
msg = callsign ~ ": " ~ msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
var ldisplay = getprop("/sim/multiplay/chat-display");
|
var ldisplay = getprop("/sim/multiplay/chat-display");
|
||||||
|
|
||||||
if ((ldisplay != nil) and (ldisplay == "1"))
|
if ((ldisplay != nil) and (ldisplay == "1"))
|
||||||
{
|
{
|
||||||
# Only display the message to screen if configured.
|
# Only display the message to screen if configured.
|
||||||
setprop("/sim/messages/ai-plane", msg);
|
setprop("/sim/messages/ai-plane", msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
# Add the chat to the chat history.
|
# Add the chat to the chat history.
|
||||||
var lchat = getprop("/sim/multiplay/chat-history");
|
var lchat = getprop("/sim/multiplay/chat-history");
|
||||||
|
|
||||||
if (lchat == nil)
|
if (lchat == nil)
|
||||||
{
|
{
|
||||||
setprop("/sim/multiplay/chat-history", msg);
|
setprop("/sim/multiplay/chat-history", msg);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (substr(lchat, size(lchat) -1, 1) != "\n")
|
if (substr(lchat, size(lchat) -1, 1) != "\n")
|
||||||
{
|
{
|
||||||
lchat = lchat ~ "\n";
|
lchat = lchat ~ "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
setprop("/sim/multiplay/chat-history", lchat ~ msg);
|
setprop("/sim/multiplay/chat-history", lchat ~ msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
settimer(func {
|
settimer(func {
|
||||||
# Call-back to ensure we see our own messages.
|
# Call-back to ensure we see our own messages.
|
||||||
setlistener("/sim/multiplay/chat", func(n) { echo_message(n.getValue(), getprop("/sim/multiplay/callsign")); });
|
setlistener("/sim/multiplay/chat", func(n) { echo_message(n.getValue(), getprop("/sim/multiplay/callsign")); });
|
||||||
|
|
||||||
# check for new messages
|
# check for new messages
|
||||||
check_messages();
|
check_messages();
|
||||||
|
|
||||||
}, 1);
|
}, 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
390
gui/dialogs/chat-menu.xml
Normal file
390
gui/dialogs/chat-menu.xml
Normal file
|
@ -0,0 +1,390 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
|
||||||
|
<PropertyList>
|
||||||
|
<name>chat-menu</name>
|
||||||
|
<x>10</x>
|
||||||
|
<y>-30</y>
|
||||||
|
<layout>vbox</layout>
|
||||||
|
<draggable>false</draggable>
|
||||||
|
<default-padding>1</default-padding>
|
||||||
|
<font>
|
||||||
|
<name>HELVETICA_12</name>
|
||||||
|
</font>
|
||||||
|
<color>
|
||||||
|
<red>0</red>
|
||||||
|
<green>0</green>
|
||||||
|
<blue>0</blue>
|
||||||
|
<alpha>0</alpha>
|
||||||
|
</color>
|
||||||
|
<text>
|
||||||
|
<label>0. Back</label>
|
||||||
|
<halign>left</halign>
|
||||||
|
<color>
|
||||||
|
<red>0.9</red>
|
||||||
|
<green>0.2</green>
|
||||||
|
<blue>0.2</blue>
|
||||||
|
<alpha>1</alpha>
|
||||||
|
</color>
|
||||||
|
</text>
|
||||||
|
<text>
|
||||||
|
<label>1. Edit</label>
|
||||||
|
<halign>left</halign>
|
||||||
|
<color>
|
||||||
|
<red>0.9</red>
|
||||||
|
<green>0.2</green>
|
||||||
|
<blue>0.2</blue>
|
||||||
|
<alpha>1</alpha>
|
||||||
|
</color>
|
||||||
|
</text>
|
||||||
|
<text>
|
||||||
|
<halign>left</halign>
|
||||||
|
<property>/sim/multiplay/chat-menu/entry[0]</property>
|
||||||
|
<live>true</live>
|
||||||
|
<color>
|
||||||
|
<red>0.9</red>
|
||||||
|
<green>0.2</green>
|
||||||
|
<blue>0.2</blue>
|
||||||
|
<alpha>1</alpha>
|
||||||
|
</color>
|
||||||
|
</text>
|
||||||
|
<text>
|
||||||
|
<halign>left</halign>
|
||||||
|
<property>/sim/multiplay/chat-menu/entry[1]</property>
|
||||||
|
<live>true</live>
|
||||||
|
<color>
|
||||||
|
<red>0.9</red>
|
||||||
|
<green>0.2</green>
|
||||||
|
<blue>0.2</blue>
|
||||||
|
<alpha>1</alpha>
|
||||||
|
</color>
|
||||||
|
</text>
|
||||||
|
<text>
|
||||||
|
<halign>left</halign>
|
||||||
|
<property>/sim/multiplay/chat-menu/entry[2]</property>
|
||||||
|
<live>true</live>
|
||||||
|
<color>
|
||||||
|
<red>0.9</red>
|
||||||
|
<green>0.2</green>
|
||||||
|
<blue>0.2</blue>
|
||||||
|
<alpha>1</alpha>
|
||||||
|
</color>
|
||||||
|
</text>
|
||||||
|
<text>
|
||||||
|
<halign>left</halign>
|
||||||
|
<property>/sim/multiplay/chat-menu/entry[3]</property>
|
||||||
|
<live>true</live>
|
||||||
|
<color>
|
||||||
|
<red>0.9</red>
|
||||||
|
<green>0.2</green>
|
||||||
|
<blue>0.2</blue>
|
||||||
|
<alpha>1</alpha>
|
||||||
|
</color>
|
||||||
|
</text>
|
||||||
|
<text>
|
||||||
|
<halign>left</halign>
|
||||||
|
<property>/sim/multiplay/chat-menu/entry[4]</property>
|
||||||
|
<live>true</live>
|
||||||
|
<color>
|
||||||
|
<red>0.9</red>
|
||||||
|
<green>0.2</green>
|
||||||
|
<blue>0.2</blue>
|
||||||
|
<alpha>1</alpha>
|
||||||
|
</color>
|
||||||
|
</text>
|
||||||
|
<text>
|
||||||
|
<halign>left</halign>
|
||||||
|
<property>/sim/multiplay/chat-menu/entry[5]</property>
|
||||||
|
<live>true</live>
|
||||||
|
<color>
|
||||||
|
<red>0.9</red>
|
||||||
|
<green>0.2</green>
|
||||||
|
<blue>0.2</blue>
|
||||||
|
<alpha>1</alpha>
|
||||||
|
</color>
|
||||||
|
</text>
|
||||||
|
<text>
|
||||||
|
<halign>left</halign>
|
||||||
|
<property>/sim/multiplay/chat-menu/entry[6]</property>
|
||||||
|
<live>true</live>
|
||||||
|
<color>
|
||||||
|
<red>0.9</red>
|
||||||
|
<green>0.2</green>
|
||||||
|
<blue>0.2</blue>
|
||||||
|
<alpha>1</alpha>
|
||||||
|
</color>
|
||||||
|
</text>
|
||||||
|
<text>
|
||||||
|
<halign>left</halign>
|
||||||
|
<property>/sim/multiplay/chat-menu/entry[7]</property>
|
||||||
|
<live>true</live>
|
||||||
|
<color>
|
||||||
|
<red>0.9</red>
|
||||||
|
<green>0.2</green>
|
||||||
|
<blue>0.2</blue>
|
||||||
|
<alpha>1</alpha>
|
||||||
|
</color>
|
||||||
|
</text>
|
||||||
|
|
||||||
|
<nasal>
|
||||||
|
<open>
|
||||||
|
<![CDATA[
|
||||||
|
var text = "";
|
||||||
|
var topNode = "/sim/multiplay/chat-menu/config";
|
||||||
|
|
||||||
|
setprop("/sim/multiplay/chat-menu/entry", "1");
|
||||||
|
setprop("/sim/multiplay/chat-menu/entry[1]", "2");
|
||||||
|
setprop("/sim/multiplay/chat-menu/entry[2]", "3");
|
||||||
|
setprop("/sim/multiplay/chat-menu/entry[3]", "4");
|
||||||
|
setprop("/sim/multiplay/chat-menu/entry[4]", "5");
|
||||||
|
setprop("/sim/multiplay/chat-menu/entry[5]", "6");
|
||||||
|
setprop("/sim/multiplay/chat-menu/entry[6]", "7");
|
||||||
|
setprop("/sim/multiplay/chat-menu/entry[7]", "8");
|
||||||
|
setprop("/sim/multiplay/chat-menu/entry[8]", "9");
|
||||||
|
setprop("/sim/multiplay/chat-menu/entry[9]", "0");
|
||||||
|
|
||||||
|
# Get the conversation tree.
|
||||||
|
var entryNodes = props.globals.getNode("/sim/multiplay/chat-menu").getChildren("entry");
|
||||||
|
var pos = props.globals.getNode(topNode);
|
||||||
|
|
||||||
|
# Get various substitution values.
|
||||||
|
var callsign = getprop("/sim/multiplay/callsign");
|
||||||
|
|
||||||
|
# Simple rounding of altitude to 100 ft.
|
||||||
|
var altitude = int(getprop("/position/altitude-ft")/100) * 100;
|
||||||
|
|
||||||
|
|
||||||
|
var type = getprop("/sim/description");
|
||||||
|
if (type != "")
|
||||||
|
{
|
||||||
|
# Get the first word of the description, which will (hopefully) be
|
||||||
|
# something appropriate, such as "Cessna", "Boeing".
|
||||||
|
type = split(" ", type)[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
# Get the nearest airport.
|
||||||
|
var airport = airportinfo();
|
||||||
|
|
||||||
|
# Get the complement of each runway to create a full set.
|
||||||
|
foreach (var rwy; keys(airport.runways)) {
|
||||||
|
if (!string.isdigit(rwy[0]))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var number = math.mod(num(substr(rwy, 0, 2)) + 18, 36);
|
||||||
|
var side = substr(rwy, 2, 1);
|
||||||
|
var comp = sprintf("%02d%s", number, side == "R" ? "L" : side == "L" ? "R" : side);
|
||||||
|
var r = airport.runways[rwy];
|
||||||
|
airport.runways[comp] = { lat: r.lat, lon: r.lon, length: r.length,
|
||||||
|
width: r.width, heading: math.mod(r.heading + 180, 360),
|
||||||
|
threshold1: r.threshold2,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
# Determine the active runway.
|
||||||
|
var wind_speed = getprop("/environment/wind-speed-kt");
|
||||||
|
var wind_from = wind_speed ? getprop("/environment/wind-from-heading-deg") : 270;
|
||||||
|
var max = -1;
|
||||||
|
var active_runway = "";
|
||||||
|
|
||||||
|
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(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();
|
||||||
|
]]>
|
||||||
|
</open>
|
||||||
|
</nasal>
|
||||||
|
</PropertyList>
|
|
@ -161,7 +161,13 @@
|
||||||
<dialog-name>chat</dialog-name>
|
<dialog-name>chat</dialog-name>
|
||||||
</binding>
|
</binding>
|
||||||
</item>
|
</item>
|
||||||
|
<item>
|
||||||
|
<label>Chat Menu</label>
|
||||||
|
<binding>
|
||||||
|
+ <command>dialog-show</command>
|
||||||
|
<dialog-name>chat-menu</dialog-name>
|
||||||
|
</binding>
|
||||||
|
</item>
|
||||||
</menu>
|
</menu>
|
||||||
|
|
||||||
<menu>
|
<menu>
|
||||||
|
|
24
keyboard.xml
24
keyboard.xml
|
@ -352,7 +352,17 @@ top down before the key bindings are parsed.
|
||||||
</mod-up>
|
</mod-up>
|
||||||
</key>
|
</key>
|
||||||
|
|
||||||
<key n="46">
|
<key n="45">
|
||||||
|
<name>-</name>
|
||||||
|
<repeatable type="bool">false</repeatable>
|
||||||
|
<desc>Compose Chat</desc>
|
||||||
|
<binding>
|
||||||
|
<command>dialog-show</command>
|
||||||
|
<dialog-name>chat-menu</dialog-name>
|
||||||
|
</binding>
|
||||||
|
</key>
|
||||||
|
|
||||||
|
<key n="46">
|
||||||
<name>.</name>
|
<name>.</name>
|
||||||
<desc>Right brake</desc>
|
<desc>Right brake</desc>
|
||||||
<binding>
|
<binding>
|
||||||
|
@ -773,7 +783,17 @@ top down before the key bindings are parsed.
|
||||||
</mod-up>
|
</mod-up>
|
||||||
</key>
|
</key>
|
||||||
|
|
||||||
<key n="97">
|
<key n="95">
|
||||||
|
<name>_</name>
|
||||||
|
<repeatable type="bool">false</repeatable>
|
||||||
|
<desc>Compose Chat</desc>
|
||||||
|
<binding>
|
||||||
|
<command>nasal</command>
|
||||||
|
<script>multiplayer.compose_message()</script>
|
||||||
|
</binding>
|
||||||
|
</key>
|
||||||
|
|
||||||
|
<key n="97">
|
||||||
<name>a</name>
|
<name>a</name>
|
||||||
<desc>Increase speed-up.</desc>
|
<desc>Increase speed-up.</desc>
|
||||||
<binding>
|
<binding>
|
||||||
|
|
|
@ -472,6 +472,7 @@ Started September 2000 by David Megginson, david@megginson.com
|
||||||
<chat type="string">Hello</chat>
|
<chat type="string">Hello</chat>
|
||||||
<transmission-freq-hz type="string">118500000</transmission-freq-hz>
|
<transmission-freq-hz type="string">118500000</transmission-freq-hz>
|
||||||
<chat-display type="bool" userarchive="y">true</chat-display>
|
<chat-display type="bool" userarchive="y">true</chat-display>
|
||||||
|
<chat-menu include="ATC/chat-menu-entries.xml"/>
|
||||||
</multiplay>
|
</multiplay>
|
||||||
|
|
||||||
<user>
|
<user>
|
||||||
|
|
Loading…
Reference in a new issue