1 ###############################################################################
3 ## Short S.23 'C'-class Empire flying boat
5 ## Copyright (C) 2010 - 2012 Anders Gidenstam (anders(at)gidenstam.org)
6 ## This file is licensed under the GPL license v2 or later.
8 ###############################################################################
10 # Do terrain modelling ourselves.
11 setprop("sim/fdm/surface/override-level", 1);
14 ###########################################################################
15 ## Initialization and reset.
17 var init = func(reinit=0) {
19 if (getprop("/sim/presets/onground")) {
21 if (!mooring.pick_up_mooring()) {
22 # No mooring available, set up a local mooring.
23 mooring.add_fixed_mooring(geo.aircraft_position(), 0.0);
25 # We need the FDM to run in between.
27 mooring.pick_up_mooring();
33 # Add the predefined moorings.
34 foreach (var m; EAMS_MOORINGS_EUROPE ~
39 var pos = geo.Coord.new().set_latlon(m[1], m[2]);
40 mooring.add_fixed_mooring(pos, 0.0, m[0]);
44 # Enable Alt+Click to place the mooring
45 setlistener("/sim/signals/click", func {
46 var click_pos = geo.click_position();
47 if (__kbd.alt.getBoolValue()) {
48 mooring.add_fixed_mooring(click_pos, 0.0);
54 var _mooring_initialized = 0;
55 setlistener("/sim/signals/fdm-initialized", func {
56 init(_mooring_initialized);
57 _mooring_initialized = 1;
60 ###########################################################################
61 ## Mooring location support.
63 ##################################################
65 me.UPDATE_INTERVAL = 0.0;
66 me.MP_ANNOUNCE_INTERVAL = 60.0;
68 me.last_mp_announce = systime();
69 ## Hash containing all supported mooring locations.
71 ## Fixed {position : <coord>, alt_offset : <m>}
72 ## AI {base : <node>, alt_offset : <m>}
75 me.mooring_model = {local : nil};
76 me.fairway_model = {local : nil};
78 me.mooring_model_path =
79 me.find_model_path("Short_Empire/Models/Moorings/buoy.xml");
80 me.fairway_model_path =
81 me.find_model_path("Short_Empire/Models/Moorings/flare_path.xml");
83 me.active_mooring = props.globals.getNode("/fdm/jsbsim/mooring");
86 print("Short Empire Mooring ... Standing by.");
88 ##################################################
89 add_fixed_mooring : func(pos, alt_offset, name="local") {
90 me.moorings[name] = { position : pos,
91 alt_offset : alt_offset };
92 me.display_mooring(name);
93 if (name == "local") {
94 #announce_fixed_mooring(pos, alt_offset);
97 ##################################################
98 display_mooring : func(name) {
99 if (!contains(me.moorings[name], "position")) return;
100 var geo_info = geodinfo(me.moorings[name].position.lat(),
101 me.moorings[name].position.lon());
102 if (geo_info == nil) return;
103 me.moorings[name].position.set_alt(geo_info[0]);
104 # Put a mooring buoy model here.
105 if (me.mooring_model[name] != nil) me.mooring_model[name].remove();
106 me.mooring_model[name] =
107 geo.put_model(me.mooring_model_path,
108 me.moorings[name].position);
109 # Display the associated fairway, if any.
110 if (FAIRWAY[name] != nil) {
111 if (me.fairway_model[name] != nil) me.fairway_model[name].remove();
112 if (FAIRWAY[name][3] == 0.0) {
113 me.fairway_model[name] =
115 (me.fairway_model_path,
116 FAIRWAY[name][1], FAIRWAY[name][2],
118 -getprop("/environment/wind-from-heading-deg"));
120 me.fairway_model[name] =
122 (me.fairway_model_path,
123 FAIRWAY[name][1], FAIRWAY[name][2],
129 ##################################################
130 remove_fixed_mooring : func(name) {
131 if (me.mooring_model[name] != nil) me.mooring_model[name].remove();
132 delete(me.moorings, name);
134 ##################################################
135 add_ai_mooring : func(ai, alt_offset) {
136 if (ai == nil) return;
137 var name = ai.getNode("callsign").getValue();
138 if (name == "") { name = ai.getNode("name").getValue(); }
139 me.moorings[name] = { base : ai,
140 alt_offset : alt_offset };
142 ##################################################
143 remove_ai_mooring : func(ai) {
144 if (ai == nil) return;
145 foreach (var name; keys(me.moorings)) {
146 if (contains(me.moorings[name], "base") and
147 me.moorings[name].base == ai) {
148 delete(me.moorings, name);
153 ##################################################
154 pick_up_mooring : func {
155 if (me.active_mooring.getNode("mooring-connected").getBoolValue())
157 var dist = me.active_mooring.getNode("total-distance-ft").getValue();
159 me.active_mooring.getNode("rope-length-ft").getValue();
160 if (dist < rope_length/FT2M) {
161 me.active_mooring.getNode("mooring-connected").setValue(1.0);
162 setprop("controls/lighting/anchor-light", 1.0);
163 copilot.announce("We picked up the mooring.");
166 copilot.announce("We are too far from the buoy.");
170 ##################################################
171 release_mooring : func {
172 if (me.active_mooring.getNode("mooring-connected").getValue() >= 1.0) {
173 me.active_mooring.getNode("mooring-connected").setValue(0.0);
174 setprop("controls/lighting/anchor-light", 0.0);
175 copilot.announce("Mooring slipped.");
178 ##################################################
179 # filename should include the aircraft's directory.
180 find_model_path : func (filename) {
181 # FIXME WORKAROUND: Search for the model in all aircraft dirs.
182 var base = "/" ~ filename;
183 var file = props.globals.getNode("/sim/fg-root").getValue() ~
185 if (io.stat(file) != nil) {
189 props.globals.getNode("/sim").getChildren("fg-aircraft")) {
190 file = d.getValue() ~ base;
191 if (io.stat(file) != nil) {
196 ##################################################
198 var ac_pos = geo.aircraft_position();
200 # Compute distance to the current mooring.
201 var distance = 1000000;
202 var cur_pos = geo.Coord.new();
203 var cur_name = me.selected;
204 if (contains(me.moorings, me.selected)) {
205 if (contains(me.moorings[me.selected], "position")) {
206 cur_pos = geo.Coord.new(me.moorings[me.selected].position);
208 var ai = me.moorings[me.selected].base;
209 cur_pos = geo.Coord.new().set_latlon
210 (ai.getNode("position/latitude-deg").getValue(),
211 ai.getNode("position/longitude-deg").getValue(),
212 FT2M * ai.getNode("position/altitude-ft").getValue());
214 distance = cur_pos.distance_to(ac_pos);
217 # Break connection if too far way.
219 me.active_mooring.getNode("rope-length-ft").getValue();
220 if (distance > 2.0 * rope_length and
221 me.active_mooring.getNode("mooring-connected").getBoolValue()) {
222 me.release_mooring();
225 # Find the closest mooring position.
226 foreach (var name; keys(me.moorings)) {
228 if (contains(me.moorings[name], "position")) {
229 pos = me.moorings[name].position;
231 var ai = me.moorings[name].base;
232 pos = geo.Coord.new().set_latlon
233 (ai.getNode("position/latitude-deg").getValue(),
234 ai.getNode("position/longitude-deg").getValue(),
235 FT2M * ai.getNode("position/altitude-ft").getValue());
237 if (pos.direct_distance_to(ac_pos) < distance) {
239 cur_pos = geo.Coord.new(pos);
240 distance = pos.distance_to(ac_pos);
244 if (cur_pos.is_defined()) {
245 if (cur_name != me.selected) {
246 print("Short Empire Mooring: Switched mooring to " ~
248 me.selected = cur_name;
249 me.display_mooring(cur_name);
252 # The position might be new, so update active mooring.
253 me.active_mooring.getNode("latitude-deg").setValue(cur_pos.lat());
254 me.active_mooring.getNode("longitude-deg").setValue(cur_pos.lon());
255 # First check if the offset is fixed or a AI/MP property.
257 if (dual_control_tools.is_num(me.moorings[name].alt_offset)) {
258 offset = me.moorings[name].alt_offset;
261 me.moorings[name].base.
262 getNode(me.moorings[name].alt_offset).getValue();
264 me.active_mooring.getNode("altitude-ft").
265 setValue(M2FT * (cur_pos.alt() + offset));
268 # Announce local mooring.
270 if (now > me.last_mp_announce + me.MP_ANNOUNCE_INTERVAL) {
271 #announce_fixed_mooring(me.moorings["local"].position,
272 # me.moorings["local"].alt_offset);
273 me.last_mp_announce = now;
276 ##################################################
279 me._loop_(me.loopid);
281 ##################################################
283 id == me.loopid or return;
285 settimer(func { me._loop_(id); }, me.UPDATE_INTERVAL);
289 ###############################################################################
290 ## Lists containing EAMS mooring locations. See also ROUTES.txt.
292 ## [[name, lat, lon]]
293 var EAMS_MOORINGS_EUROPE =
295 ["Hythe 1", 50.872210, -1.390830],
296 ["Hythe 2", 50.872349, -1.387512],
297 ["Saint-Nazaire", 47.297423, -2.134309],
298 ["Bordeaux/Biscarosse", 44.383172, -1.184227],
299 ["Macon", 46.290932, 4.830000],
300 ["Marseille/Marignane", 43.446937, 5.185410],
301 ["Rome/Lake Bracciano", 42.113768, 12.187239],
302 ["Brindisi", 40.652277, 17.959625],
303 ["Corfu", 39.614731, 19.929714],
304 ["Athens/Phaleron Bay", 37.939527, 23.666230],
305 ["Heraklion", 35.344386, 25.140345],
306 ["Mirabella Bay", 35.200427, 25.723003],
307 ["Alexandria/East Harbour", 31.196233, 29.872788]
309 var EAMS_MOORINGS_EAST =
311 ["Tiberias", 32.804, 35.547],
312 ["Lake Habbaniyeh", 33.34625, 43.547359],
313 ["Basra/Margil", 30.5203, 47.8455],
314 ["Kuwait", 29.354583, 47.934932],
315 ["Bahrein", 26.240, 50.623],
316 ["Dubai", 25.237, 55.325],
317 ["Jiwani", 25.053, 61.723],
318 ["Karachi", 24.780, 67.138],
319 ["Raj Samand", 25.071, 73.888],
320 ["Lake Udaipur", 24.574, 73.680],
321 ["Gwalior", 26.214, 77.994],
322 ["Jhansi/Parichha Reservoir", 25.505293,78.755804],
323 ["Allahabad", 25.425375, 81.86501],
324 ["Calcutta", 22.588625, 88.353534],
325 ["Akyab (Sittwe)", 20.139, 92.908],
326 ["Rangoon (Yangon)", 16.733619, 96.209164],
327 ["Bangkok", 13.657976, 100.550448],
328 ["Ko Samu (Ko Samui)", 9.5633, 100.054464],
329 ["Penang (Pinang)", 5.415, 100.350],
330 ["Sinagpore/Kallang", 1.302283, 103.870003],
331 ["Batavia (Jakarta)", -6.118777, 106.835255],
332 ["Sourabaya (Surabaya)", -7.18759, 112.733803],
333 ["Bima", -8.450368, 118.713026],
334 ["Koepang (Kupang)", -10.152625, 123.587474],
335 ["Darwin", -12.473265, 130.846809],
336 ["Groote", -13.953581, 136.411035],
337 ["Karumba", -17.472431, 140.834674],
338 ["Townsville", -19.248107, 146.826745],
339 ["Gladstone", -23.828888, 151.252084],
340 ["Brisbane", -27.427547, 153.128225],
341 ["Sydney/Rose Bay", -33.868387, 151.263121]
343 var EAMS_MOORINGS_SOUTH =
345 ["Cairo", 30.0327, 31.222],
346 ["Luxor", 25.7011, 32.6365],
347 ["Wadi Halfa", 21.805, 31.300],
348 ["Khartoum", 15.606, 32.537],
349 ["Kosti", 13.169, 32.665],
350 ["Malakal", 9.528, 31.652],
351 ["Bor", 6.220, 31.543],
352 ["Juba", 4.834, 31.614],
353 ["Laropi", 3.54863, 31.812716],
354 ["Butabia", 1.829127, 31.329403],
355 ["Kampala/Port Bell", 0.287703, 32.652279],
356 ["Kisumu", -0.090, 34.744],
357 ["Nairobi/Lake Naivasha", -0.7806, 36.4002],
358 ["Mombasa", -4.0524, 39.6799],
359 ["Dar-es-Salaam", -6.820, 39.292],
360 ["Lindi", -10.001025, 39.721466],
361 ["Mozambique", -15.038182, 40.704099],
362 ["Beira", -19.826, 34.829],
363 ["Inhambane", -23.8687, 35.3722],
364 ["Lourenco Morques", -25.9689, 32.5376],
365 ["Durban", -29.870, 31.033]
369 ["New York/Port Washington", 40.832081, -73.719479],
370 ["Bermuda/Darrel's Island", 32.273, -64.825],
371 ["Foynes", 52.618, -9.130],
372 ["Botwood", 49.133, -55.334]
376 ["Auckland/Mechanics Bay", -36.8440, 174.7942],
377 ["Wellington/Evans Bay", -41.3142, 174.8065]
380 ###############################################################################
381 ## Hash containing fairway locations. See also ROUTES.txt.
383 ## {"mooring : [name, lat, lon, heading]}
386 "Hythe 1": ["Netley", 50.873, -1.365, 0.0],
387 "Hythe 2": ["Netley", 50.873, -1.365, 0.0],
388 "Saint-Nazaire": ["", 47.289, -2.152, 0.0],
389 "Bordeaux/Biscarosse": ["", 44.379, -1.186, 0.0],
390 "Macon": ["", 46.291, 4.831, 5.0],
391 "Marseille/Marignane": ["", 43.452, 5.181, 0.0],
392 "Rome/Lake Bracciano": ["", 42.119, 12.196, 0.0],
393 "Brindisi": ["", 40.651, 17.962, 0.0],
394 "Athens/Phaleron Bay": ["", 37.930, 23.673, 0.0],
395 "Mirabella Bay": ["", 35.210, 25.740, 0.0],
397 "Alexandria/East Harbour": ["", 31.193, 29.874, 0.0],
398 "Cairo": ["", 30.036, 31.225, 10.0],
399 "Luxor": ["", 25.710, 32.642, 30.0],
400 "Wadi Halfa": ["", 21.810, 31.292, 0.0],
401 "Khartoum": ["", 15.610, 32.537, 90.0],
402 "Kosti": ["", 13.172, 32.665, 315.0],
403 "Malakal": ["", 9.528, 31.650, 340.0],
404 "Bor": ["", 6.225, 31.537, 315.0],
405 "Juba": ["", 4.834, 31.617, 30.0],
406 #"Laropi": ["", 3.548, 31.812, 90.0],
407 "Butabia": ["", 1.837, 31.329, 0.0],
408 "Kampala/Port Bell": ["", 0.280, 32.652, 0.0],
409 "Kisumu": ["", -0.094, 34.740, 0.0],
410 "Nairobi/Lake Naivasha": ["", -0.780, 36.392, 0.0],
411 "Mombasa": ["", -4.050, 39.682, 335.0],
412 "Dar-es-Salaam": ["", -6.823, 39.294, 0.0],
413 "Lindi": ["", -10.006, 39.720, 0.0],
414 "Mozambique": ["", -15.038, 40.710, 0.0],
415 "Beira": ["", -19.820, 34.825, 0.0],
416 "Inhambane": ["", -23.864, 35.363, 0.0],
417 "Lourenco Morques": ["", -25.965, 32.535, 0.0],
418 "Durban": ["Congella Bay", -29.873, 31.028, 0.0],
420 "Tiberias": ["", 32.804, 35.560, 0.0],
421 "Lake Habbaniyeh": ["", 33.338, 43.546, 0.0],
422 "Basra/Margil": ["", 30.530, 47.839, 330.0],
423 "Kuwait": ["", 29.363, 47.938, 0.0],
424 "Bahrein": ["", 26.233, 50.624, 0.0],
425 "Dubai": ["Dubai Creek", 25.230, 55.330, 130.0],
427 "Jiwani": ["", 25.057, 61.717, 0.0],
428 "Karachi": ["Karangi Creek", 24.775, 67.144, 0.0],
429 "Raj Samand": ["", 25.075, 73.882, 0.0],
430 "Lake Udaipur": ["", 24.575, 73.676, 0.0],
431 "Gwalior": ["", 26.217, 77.989, 0.0],
432 "Jhansi/Parichha Reservoir":["", 25.508, 78.768, 75.0],
433 "Allahabad": ["", 25.423, 81.872, 100.0],
434 "Calcutta": ["River Hooghly", 22.593, 88.356, 30.0],
436 "Akyab (Sittwe)": ["", 20.138, 92.913, 0.0],
437 "Rangoon (Yangon)": ["", 16.756, 96.203, 0.0],
438 "Bangkok": ["", 13.657, 100.550, 95.0],
439 "Ko Samu (Ko Samui)": ["", 9.566, 100.051, 0.0],
440 "Penang (Pinang)": ["", 5.412, 100.353, 0.0],
441 "Sinagpore/Kallang": ["", 1.289, 103.867, 165.0],
442 "Batavia (Jakarta)": ["", -6.108, 106.835, 0.0],
443 "Sourabaya (Surabaya)": ["", -7.184, 112.743, 0.0],
444 "Bima": ["", -8.444, 118.705, 0.0],
445 "Koepang (Kupang)": ["", -10.150, 123.579, 0.0],
447 "Darwin": ["", -12.471, 130.856, 0.0],
448 "Groote": ["", -13.946, 136.407, 0.0],
449 "Karumba": ["", -17.469, 140.829, 135.0],
450 "Townsville": ["", -19.243, 146.822, 0.0],
451 "Gladstone": ["", -23.816, 151.260, 0.0],
452 "Brisbane": ["", -27.430, 153.128, 65.0],
453 "Sydney/Rose Bay": ["Rose Bay", -33.863, 151.260, 0.0],
455 "Bermuda/Darrel's Island": ["", 32.271, -64.840, 0.0],
456 "New York/Port Washington":["", 40.828, -73.719, 0.0],
457 "Foynes": ["", 52.618, -9.140, 0.0],
458 "Botwood": ["", 49.140, -55.324, 0.0],
460 "Auckland/Mechanics Bay": ["Mechanics Bay",-36.840, 174.805, 0.0],
461 "Wellington/Evans Bay": ["Evans Bay", -41.309, 174.808, 0.0]