diff --git a/Nasal/multiplayer.nas b/Nasal/multiplayer.nas index 9bf4d351f..c7ed0ce85 100644 --- a/Nasal/multiplayer.nas +++ b/Nasal/multiplayer.nas @@ -7,19 +7,24 @@ # 2) Display a complete history of chat via dialog. # # 3) Allow chat messages to be written by the user. - +# +# 4) Pilot list dialog, including ability to hide aircraft var is_active = func getprop("/sim/multiplay/txport") or getprop("/sim/multiplay/rxport"); - +# Hashes of last message and aircraft visibility. var lastmsg = {}; -var ignore = {}; +var visibility = {}; + +# Constants for the visibility hash +var HIDDEN = 0; +var VISIBLE = 1; var check_messages = func { foreach (var mp; values(model.callsign)) { var msg = mp.node.getNode("sim/multiplay/chat", 1).getValue(); if (msg and msg != lastmsg[mp.callsign]) { - if (!contains(ignore, mp.callsign)) + if (visibility[mp.callsign] == VISIBLE) echo_message(mp.callsign, msg); lastmsg[mp.callsign] = msg; } @@ -149,6 +154,80 @@ var handle_key = func(key) } } +# Determine the visibility of an MP aircraft +# based on previous ignore/show settings and the +# class of the aircraft +determineVisibility = func(callsign, ai_node) { + + if (! contains(visibility, callsign)) { + # Not previously seen. Look at class + + var class = ai_node.getNode("sim/multiplay/usage-class-hash", 1).getValue(); + + if ((class == nil) or (class == "")) { + # Default to "Default" + class = string.hash("Default"); + } + + + foreach (var n; props.globals.getNode("/sim/multiplay/display").getChildren("usage-class")) { + var h = n.getNode("hash", 1); + var visible = n.getNode("visible", 1).getBoolValue(); + + if ((h == nil) or (h.getValue() == nil) or (h.getValue() == 0)) { + # No hash found, generate one. + var name = n.getNode("name", 1).getValue(); + h.setValue(string.hash(name)); + } + + if (h.getValue() == class) { + # Class found. Determine whether visible or not + if (visible) { + ai_node.getNode("controls/invisible", 1).setValue(0); + visibility[callsign] = VISIBLE; + } else { + ai_node.getNode("controls/invisible", 1).setValue(1); + visibility[callsign] = HIDDEN; + } + + found_usage = 1; + continue; + } + } + + if (!found_usage) { + # If we don't recognize the class, don't display. + ai_node.getNode("controls/invisible", 1).setValue(1); + visibility[callsign] = HIDDEN; + } + + } else if (visibility[callsign] == HIDDEN) { + # Previously seen and set to invisible. + ai_node.getNode("controls/invisible", 1).setValue(1); + } else if (visibility[callsign] == VISIBLE) { + # Previously seen and shown + ai_node.getNode("controls/invisible", 1).setValue(0); + } +} + +# Check all aircraft visibility based on class. To be used when +# the set of visible classes change. +resetVisibility = func() { + + visibility = {}; + + foreach (var n; props.globals.getNode("ai/models", 1).getChildren("multiplayer")) { + if (!n.getNode("valid").getValue()) + continue; + + if ((var callsign = n.getNode("callsign")) == nil or !(callsign = callsign.getValue())) + continue; + if (!(callsign = string.trim(callsign))) + continue; + + determineVisibility(callsign, n); + } +} # multiplayer.dialog.show() -- displays pilot list dialog @@ -285,7 +364,7 @@ var dialog = { "distance-to-km": distance / 1000.0, "distance-to-nm": distance * M2NM, "position/altitude-m": n.getNode("position/altitude-ft").getValue() * FT2M, - "controls/invisible": contains(ignore, mp.callsign), + "controls/invisible": (visibility[mp.callsign] == HIDDEN), }); } if (PILOTSDLG_RUNNING) @@ -314,10 +393,10 @@ var dialog = { } }, toggle_ignore: func (callsign) { - if (contains(ignore, callsign)) { - delete(ignore, callsign); + if (visibility[callsign] == VISIBLE) { + visibility[callsign] = HIDDEN; } else { - ignore[callsign] = 1; + visibility[callsign] = VISIBLE; } }, close: func { @@ -424,6 +503,9 @@ var model = { var data = { node: n, callsign: callsign, model: model, root: root, sort: string.lc(callsign), available: available }; + # Control visibility + determineVisibility(callsign, n); + me.data[root] = data; me.callsign[callsign] = data; append(available ? me.available : me.unavailable, data); diff --git a/Nasal/string.nas b/Nasal/string.nas index e5be2990a..3e6f29148 100644 --- a/Nasal/string.nas +++ b/Nasal/string.nas @@ -75,7 +75,18 @@ var uc = func(str) { var icmp = func(a, b) cmp(lc(a), lc(b)); var imatch = func(a, b) match(lc(a), lc(b)); - +## +# Very simple hash function +# +var hash = func(str) { + var hash_val = 0; + if (str != nil) { + for (var i = 0; i < size(str); i += 1) { + hash_val += math.mod(str[i], 2*(i+1)); + } + } + return int(hash_val); +} ## diff --git a/gui/menubar.xml b/gui/menubar.xml index f6f8738ba..e684d6014 100644 --- a/gui/menubar.xml +++ b/gui/menubar.xml @@ -248,7 +248,7 @@ <script>setprop("/autopilot/route-manager/input", "@previous")</script> </binding> </item> - + <item> <label>Next Waypoint</label> <binding> @@ -267,7 +267,7 @@ </script> </binding> </item> - + <item> <label>Map</label> <binding> @@ -335,7 +335,7 @@ <dialog-name>local_weather</dialog-name> </binding> </item> - + <item> <label>Local Weather Tiles</label> <binding> @@ -442,6 +442,7 @@ </binding> </item> + <item> <label>Scenario</label> <binding> @@ -479,6 +480,15 @@ </binding> </item> + <item> + <label>Usage Class</label> + <binding> + <command>dialog-show</command> + <dialog-name>mp-display</dialog-name> + </binding> + </item> + + <item> <label>MPCarrier selection</label> <binding> diff --git a/preferences.xml b/preferences.xml index daa3e8506..757894058 100644 --- a/preferences.xml +++ b/preferences.xml @@ -595,6 +595,33 @@ Started September 2000 by David Megginson, david@megginson.com <chat-menu include="ATC/chat-menu-entries.xml"/> <write-message-log type="bool">false</write-message-log> <default-model type="string">Models/Geometry/glider.ac</default-model> + <usage-class type="string">Default</usage-class> + <display> + <usage-class> + <name type="string">Default</name> + <visible type="bool">true</visible> + </usage-class> + <usage-class> + <name type="string">Newbie</name> + <visible type="bool">true</visible> + </usage-class> + <usage-class> + <name type="string">Student</name> + <visible type="bool">true</visible> + </usage-class> + <usage-class> + <name type="string">FGCom</name> + <visible type="bool">true</visible> + </usage-class> + <usage-class> + <name type="string">Dogfight</name> + <visible type="bool">false</visible> + </usage-class> + <usage-class> + <name type="string">Ignore</name> + <visible type="bool">false</visible> + </usage-class> + </display> </multiplay> <user> @@ -677,7 +704,7 @@ Started September 2000 by David Megginson, david@megginson.com <scenario> <name>Marginal VFR</name> <metar>XXXX 012345Z 23010KT 5000 SHRA SCT012 BKN018 OVC060 15/11 Q1010</metar> - <description>After the storm - limited visibility and some showers. + <description>After the storm - limited visibility and some showers. Go or No-Go?</description> </scenario> <scenario>