From 5c41d1a99bd85d369dd00a88cc6d4204f858ed00 Mon Sep 17 00:00:00 2001
From: mfranz <mfranz>
Date: Sun, 15 Mar 2009 16:13:55 +0000
Subject: [PATCH] allow aar-equipped aircraft to request a tanker everywhere
 without scenario

---
 Nasal/tanker.nas       | 155 +++++++++++++++++++++++++++++++++++++++++
 gui/dialogs/tanker.xml |  50 +++++++++++++
 gui/menubar.xml        |  10 +++
 3 files changed, 215 insertions(+)
 create mode 100644 Nasal/tanker.nas
 create mode 100644 gui/dialogs/tanker.xml

diff --git a/Nasal/tanker.nas b/Nasal/tanker.nas
new file mode 100644
index 000000000..bea0ffdf3
--- /dev/null
+++ b/Nasal/tanker.nas
@@ -0,0 +1,155 @@
+var boom_tanker = "Models/Geometry/KC135/KC135.xml";
+var probe_tanker = "Models/Geometry/KA6-D/KA6-D.xml";
+
+
+var wind_speed_from = func(azimuth) {
+	var dir = (getprop("/environment/wind-from-heading-deg") - azimuth) * D2R;
+	return getprop("/environment/wind-speed-kt") * math.cos(dir);
+}
+
+
+var clock = func(bearing) {
+	var d = sprintf("%.0f", geo.normdeg(bearing) / 30);
+	return d ? d : 12;
+}
+
+
+var Tanker = {
+	new: func(callsign, tacan, type, kias, heading, coord) {
+		var m = { parents: [Tanker] };
+		me.callsign = callsign;
+		me.tacan = tacan;
+		me.kias = kias;
+		me.heading = heading;
+		me.coord = geo.Coord.new(coord);
+		me.out_of_range_time = 0;
+
+		var n = props.globals.getNode("models", 1);
+		for (var i = 0; 1; i += 1)
+			if (n.getChild("model", i, 0) == nil)
+				break;
+		me.model = n.getChild("model", i, 1);
+
+		var n = props.globals.getNode("ai/models", 1);
+		for (var i = 0; 1; i += 1)
+			if (n.getChild("tanker", i, 0) == nil)
+				break;
+		me.ai = n.getChild("tanker", i, 1);
+
+		me.ai.getNode("callsign", 1).setValue(me.callsign);
+		me.ai.getNode("tanker", 1).setBoolValue(1);
+		me.ai.getNode("valid", 1).setBoolValue(1);
+		me.ai.getNode("navaids/tacan/channel-ID", 1).setValue(me.tacan);
+		me.ai.getNode("refuel/type", 1).setValue(type);
+
+		me.contactN = me.ai.initNode("refuel/contact", 1, "BOOL");
+
+		var ai = me.ai.getPath() ~ "/";
+		me.model.setValues({
+			"path": type == "boom" ? boom_tanker : probe_tanker,
+			"latitude-deg-prop": ai ~ "position/latitude-deg",
+			"longitude-deg-prop": ai ~ "position/longitude-deg",
+			"elevation-ft-prop": ai ~ "position/altitude-ft",
+			"heading-deg-prop": ai ~ "orientation/heading-deg",
+			"pitch-deg-prop": ai ~ "orientation/pitch-deg",
+			"roll-deg-prop": ai ~ "orientation/roll-deg",
+		});
+
+		me.update();
+		me.model.getNode("load", 1).remove();
+		me.identify();
+		return Tanker.active[me.callsign] = m;
+	},
+	del: func {
+		me.model.remove();
+		me.ai.remove();
+		delete(Tanker.active, me.callsign);
+	},
+	update: func {
+		var dt = getprop("sim/time/delta-sec");
+		var alt = me.coord.alt();
+		var wind = wind_speed_from(me.heading);
+		var ktas = me.kias + (me.kias * alt * M2FT / 50000); # +2% per 1000 ft
+
+		me.coord.apply_course_distance(me.heading, dt * (ktas + wind) * NM2M / 3600);
+
+		me.ac = geo.aircraft_position();
+		me.distance = me.ac.distance_to(me.coord);
+		me.bearing = me.ac.course_to(me.coord);
+		me.contactN.setBoolValue(me.distance < 76 and me.ac.alt() < me.coord.alt());  # 250 ft
+
+		me.ai.setValues({
+			"position/latitude-deg": me.coord.lat(),
+			"position/longitude-deg": me.coord.lon(),
+			"position/altitude-ft": me.coord.alt() * M2FT,
+			"orientation/heading-deg": me.heading,
+			"orientation/pitch-deg": 0,
+			"orientation/roll-deg": 0,
+		});
+
+		var now = getprop("/sim/time/elapsed-sec");
+		if (me.distance < 100000)
+			me.out_of_range_time = now;
+		if (now - me.out_of_range_time > 600)
+			return me.del();
+		settimer(func me.update(), 0);
+	},
+	identify: func {
+		var alt = int((me.coord.alt() * M2FT + 50) / 100) * 100;
+		var msg = sprintf("%s at %.0f, heading %.0f with %.0f knots, TACAN %s",
+				me.callsign, alt, me.heading, me.kias, me.tacan);
+		setprop("sim/messages/ai-plane", msg);
+	},
+	report: func {
+		var dist = int(me.distance * M2NM);
+		var hdg = getprop("orientation/heading-deg");
+		var diff = (me.coord.alt() - me.ac.alt()) * M2FT;
+		var qual = diff > 3000 ? " well" : abs(diff) > 1000 ? " slightly" : "";
+		var rel = diff > 1000 ? " above" : diff < -1000 ? " below" : "";
+		var msg = sprintf("Tanker %s is at %s o'clock%s",
+				me.callsign, clock(me.ac.course_to(me.coord) - hdg),
+				qual ~ rel);
+		setprop("sim/messages/ground", msg);
+	},
+	active: {},
+};
+
+
+
+var request = func {
+	var tanker = values(Tanker.active);
+	if (size(tanker))
+		return tanker[0].identify();
+	var type = props.globals.getNode("systems/refuel").getChildren("type");
+	if (!size(type))
+		return;
+
+	var hdg = getprop("orientation/heading-deg");
+	var course = hdg + (rand() - 0.5) * 60;
+	var dist = 8000 + rand() * 8000;
+	var alt = int(10 + rand() * 15) * 1000;  # FL100--FL250
+	var coord = geo.aircraft_position().apply_course_distance(course, dist).set_alt(alt * FT2M);
+	type = type[rand() * size(type)].getValue();
+	Tanker.new("MOBIL3", "062X", type, 250, hdg, coord);
+}
+
+
+var report = func {
+	var tanker = values(Tanker.active);
+	if (size(tanker))
+		tanker[0].report();
+}
+
+
+_setlistener("/sim/signals/nasal-dir-initialized", func {
+	var aar_capable = size(props.globals.getNode("systems/refuel").getChildren("type"));
+	gui.menuEnable("tanker", aar_capable);
+	if (!aar_capable)
+		request = func setprop("sim/messages/ai-plane", "no tanker in range");
+
+	setlistener("/sim/signals/reinit", func(n) {
+		foreach (var t; values(Tanker.active))
+			t.del();
+	});
+});
+
diff --git a/gui/dialogs/tanker.xml b/gui/dialogs/tanker.xml
new file mode 100644
index 000000000..73e1c18d1
--- /dev/null
+++ b/gui/dialogs/tanker.xml
@@ -0,0 +1,50 @@
+<?xml version="1.0"?>
+
+<PropertyList>
+	<name>tanker</name>
+	<x>-10</x>
+	<layout>vbox</layout>
+
+	<group>
+		<layout>hbox</layout>
+		<empty><stretch>1</stretch></empty>
+
+		<text>
+			<label>Tanker</label>
+		</text>
+
+		<empty><stretch>1</stretch></empty>
+
+		<button>
+			<pref-width>16</pref-width>
+			<pref-height>16</pref-height>
+			<legend></legend>
+			<keynum>27</keynum>
+			<border>2</border>
+
+			<binding>
+				<command>dialog-close</command>
+			</binding>
+		</button>
+	</group>
+
+	<hrule/>
+
+	<button>
+		<legend>Request</legend>
+		<equal>true</equal>
+		<binding>
+			<command>nasal</command>
+			<script>tanker.request()</script>
+		</binding>
+	</button>
+
+	<button>
+		<legend>Get Position</legend>
+		<equal>true</equal>
+		<binding>
+			<command>nasal</command>
+			<script>tanker.report()</script>
+		</binding>
+	</button>
+</PropertyList>
diff --git a/gui/menubar.xml b/gui/menubar.xml
index f37e0a7cc..ec360b8fa 100644
--- a/gui/menubar.xml
+++ b/gui/menubar.xml
@@ -399,6 +399,16 @@
     <dialog-name>atc-ai</dialog-name>
    </binding>
   </item>
+
+  <item>
+   <label>Tanker</label>
+   <name>tanker</name>
+   <enabled>false</enabled>
+   <binding>
+    <command>dialog-show</command>
+    <dialog-name>tanker</dialog-name>
+   </binding>
+  </item>
  </menu>
 
  <menu>