1 ###############################################################################
3 ## Animated Jetway System. Allows the user to edit jetways during runtime.
5 ## Copyright (C) 2011 Ryan Miller
6 ## This file is licensed under the GPL license version 2 or later.
8 ###############################################################################
10 ###############################################################################
11 # (See http://wiki.flightgear.org/Howto:_Animated_jetways)
14 ### Static jetway model profiles ###
15 ### This class specifies the offsets used when converting static jetways using the STG converter ###
18 # Models/Airport/Jetway/jetway-movable.ac
19 # Models/Airport/Jetway/jetway-movable.xml
20 # Models/Airport/Jetway/jetway-movable-2.ac
21 # Models/Airport/Jetway/jetway-movable-2.xml
22 # Models/Airport/Jetway/jetway-movable-3.ac
23 # Models/Airport/Jetway/jetway-movable-3.xml
27 "Models/Airport/Jetway/jetway-movable.ac",
28 "Models/Airport/Jetway/jetway-movable.xml",
29 "Models/Airport/Jetway/jetway-movable-2.ac",
30 "Models/Airport/Jetway/jetway-movable-2.xml",
31 "Models/Airport/Jetway/jetway-movable-3.ac",
32 "Models/Airport/Jetway/jetway-movable-3.xml"
51 # Models/Airport/Jetway/jetway.xml
52 # Models/Airport/Jetway/jetway-ba.ac
53 # Models/Airport/Jetway/jetway-ba.xml
57 "Models/Airport/Jetway/jetway.xml",
58 "Models/Airport/Jetway/jetway-ba.ac",
59 "Models/Airport/Jetway/jetway-ba.xml"
78 # Models/Airport/Jetway/jetway-737-ba.ac
79 # Models/Airport/Jetway/jetway-737-ba.xml
83 "Models/Airport/Jetway/jetway-737-ba.ac",
84 "Models/Airport/Jetway/jetway-737-ba.xml"
103 # Models/Airport/Jetway/jetway-747-ba.ac
104 # Models/Airport/Jetway/jetway-747-ba.xml
108 "Models/Airport/Jetway/jetway-747-ba.ac",
109 "Models/Airport/Jetway/jetway-747-ba.xml"
128 # Models/Airport/Jetway/jetway-a320-ba.ac
129 # Models/Airport/Jetway/jetway-a320-ba.xml
133 "Models/Airport/Jetway/jetway-a320-ba.ac",
134 "Models/Airport/Jetway/jetway-a320-ba.xml"
153 # Models/Airport/Jetway/AutoGate-ba.ac
154 # Models/Airport/Jetway/AutoGate.xml
158 "Models/Airport/Jetway/AutoGate-ba.ac",
159 "Models/Airport/Jetway/AutoGate.xml"
178 # Models/Airport/Jetway/DockingGate-ba.ac
179 # Models/Airport/Jetway/DockingGate.xml
183 "Models/Airport/Jetway/DockingGate-ba.ac",
184 "Models/Airport/Jetway/DockingGate.xml"
205 ### Rest of script follows below ###
206 ### Watch your step! :) ###
207 var dialog_object = nil;
208 var selected_jetway = nil;
214 var FLASH_PERIOD = 0.3;
216 var filedialog_listener = 0;
218 var click = func(pos)
220 if (kbd_alt.getBoolValue())
222 if (selected_jetway == nil) return;
223 selected_jetway.setpos(pos.lat(), pos.lon(), selected_jetway.heading, pos.alt());
225 elsif (kbd_shift.getBoolValue())
227 if (selected_jetway != nil) selected_jetway._edit = 0;
228 selected_jetway = nil;
230 elsif (kbd_ctrl.getBoolValue())
232 var nearest_jetway = nil;
233 var min_dist = geo.ERAD;
234 for (var i = 0; i < size(jetways.jetways); i += 1)
236 var jetway = jetways.jetways[i];
237 if (jetway == nil) continue;
238 var dist = geo.Coord.new().set_latlon(jetway.lat, jetway.lon, jetway.alt).direct_distance_to(pos);
242 nearest_jetway = jetway;
245 if (nearest_jetway != nil)
247 if (selected_jetway != nil) selected_jetway._edit = 0;
248 selected_jetway = nearest_jetway;
249 setprop("/sim/jetways/adjust/model", selected_jetway.model);
250 setprop("/sim/jetways/adjust/door", selected_jetway.door);
251 setprop("/sim/jetways/adjust/airline", selected_jetway.airline);
252 setprop("/sim/jetways/adjust/gate", selected_jetway.gate);
253 selected_jetway._edit = 1;
254 flash(nearest_jetway);
259 var airport = getprop("/sim/airport/closest-airport-id");
260 if (airport == "") return;
261 selected_jetway = jetways.Jetway.new(airport, "generic", "FG", 0, "FGFS", pos.lat(), pos.lon(), pos.alt(), 0);
262 selected_jetway._edit = 1;
263 if (!jetways.isin(jetways.loaded_airports, airport)) append(jetways.loaded_airports, airport);
264 setprop("/sim/jetways/adjust/model", selected_jetway.model);
265 setprop("/sim/jetways/adjust/door", selected_jetway.door);
266 setprop("/sim/jetways/adjust/airline", selected_jetway.airline);
267 setprop("/sim/jetways/adjust/gate", selected_jetway.gate);
268 flash(selected_jetway);
273 if (selected_jetway == nil) return;
274 selected_jetway.remove();
275 selected_jetway = nil;
277 var adjust = func(name, value)
279 if (selected_jetway == nil) return;
280 if (name == "longitudinal")
282 var jetway_pos = geo.Coord.new();
283 jetway_pos.set_latlon(selected_jetway.lat, selected_jetway.lon, selected_jetway.alt);
284 var dir = geo.aircraft_position().course_to(jetway_pos);
285 jetway_pos.apply_course_distance(dir, value);
286 selected_jetway.setpos(jetway_pos.lat(), jetway_pos.lon(), selected_jetway.heading, selected_jetway.alt);
288 elsif (name == "transversal")
290 var jetway_pos = geo.Coord.new();
291 jetway_pos.set_latlon(selected_jetway.lat, selected_jetway.lon, selected_jetway.alt);
292 var dir = geo.aircraft_position().course_to(jetway_pos) + 90;
293 jetway_pos.apply_course_distance(dir, value);
294 selected_jetway.setpos(jetway_pos.lat(), jetway_pos.lon(), selected_jetway.heading, selected_jetway.alt);
296 elsif (name == "altitude")
298 var alt = selected_jetway.alt + value * 0.4;
299 selected_jetway.setpos(selected_jetway.lat, selected_jetway.lon, selected_jetway.heading, alt);
301 elsif (name == "heading")
303 var hdg = geo.normdeg(selected_jetway.heading + value * 4);
304 selected_jetway.setpos(selected_jetway.lat, selected_jetway.lon, hdg, selected_jetway.alt);
306 elsif (name == "initial-extension")
308 var newvalue = selected_jetway.init_extend + value;
309 if (newvalue > selected_jetway.max_extend)
311 gui.popupTip("Value exceeds maximum jetway extension limit");
313 elsif (newvalue < selected_jetway.min_extend)
315 gui.popupTip("Value lower than minimum jetway extension limit");
319 selected_jetway.init_extend = newvalue;
322 elsif (name == "initial-pitch")
324 selected_jetway.init_pitch += value;
326 elsif (name == "initial-heading")
328 selected_jetway.init_heading += value;
330 elsif (name == "initial-entrance-heading")
332 selected_jetway.init_ent_heading += value;
334 elsif (name == "model")
336 selected_jetway.setmodel(value, selected_jetway.airline, selected_jetway.gate);
337 selected_jetway = jetways.jetways[getprop("/sim/jetways/last-loaded-jetway")];
339 elsif (name == "door")
341 selected_jetway.door = value;
343 elsif (name == "airline")
345 selected_jetway.setmodel(selected_jetway.model, value, selected_jetway.gate);
346 selected_jetway = jetways.jetways[getprop("/sim/jetways/last-loaded-jetway")];
348 elsif (name == "gate")
350 selected_jetway.setmodel(selected_jetway.model, selected_jetway.airline, value);
351 selected_jetway = jetways.jetways[getprop("/sim/jetways/last-loaded-jetway")];
356 var path = getprop("/sim/fg-home") ~ "/Export/";
358 var airportarray = [];
359 foreach (var jetway; jetways.jetways)
361 if (jetway == nil) continue;
362 if (airports[jetway.airport] == nil)
364 airports[jetway.airport] = [];
365 append(airportarray, jetway.airport);
367 var node = props.Node.new();
368 node.getNode("model", 1).setValue(jetway.model);
369 node.getNode("gate", 1).setValue(jetway.gate);
370 node.getNode("door", 1).setIntValue(jetway.door);
371 node.getNode("airline", 1).setValue(jetway.airline);
372 node.getNode("latitude-deg", 1).setDoubleValue(jetway.lat);
373 node.getNode("longitude-deg", 1).setDoubleValue(jetway.lon);
374 var alt = jetway.alt;
375 jetway.setpos(jetway.lat, jetway.lon, jetway.heading, -geo.ERAD);
376 node.getNode("elevation-m", 1).setDoubleValue(alt - geo.elevation(jetway.lat, jetway.lon));
377 jetway.setpos(jetway.lat, jetway.lon, jetway.heading, alt);
378 node.getNode("heading-deg", 1).setDoubleValue(geo.normdeg(180 - jetway.heading));
379 node.getNode("initial-position/jetway-extension-m", 1).setDoubleValue(jetway.init_extend);
380 node.getNode("initial-position/jetway-heading-deg", 1).setDoubleValue(jetway.init_heading);
381 node.getNode("initial-position/jetway-pitch-deg", 1).setDoubleValue(jetway.init_pitch);
382 node.getNode("initial-position/entrance-heading-deg", 1).setDoubleValue(jetway.init_ent_heading);
383 append(airports[jetway.airport], node);
385 foreach (var airport; airportarray)
387 var file = path ~ airport ~ ".xml";
388 var args = props.Node.new({ filename: file });
389 var nodes = airports[airport];
390 foreach (var node; nodes)
392 var data = args.getNode("data", 1);
393 for (var i = 0; 1; i += 1)
395 if (data.getChild("jetway", i, 0) == nil)
397 props.copy(node, data.getChild("jetway", i, 1));
402 fgcommand("savexml", args);
403 print("jetway definitions for airport " ~ airport ~ " exported to " ~ file);
406 var convert_stg = func
408 fgcommand("dialog-show", props.Node.new({ "dialog-name": "file-select" }));
409 setprop("/sim/gui/dialogs/file-select/path", "");
410 filedialog_listener = setlistener("/sim/gui/dialogs/file-select/path", func(n)
412 removelistener(filedialog_listener);
413 var path = n.getValue();
414 if (path == "") return;
415 var stg = io.readfile(path);
416 var stg_lines = [[]];
417 var current_word = "";
418 for (var i = 0; i < size(stg); i += 1)
420 var char = substr(stg, i, 1);
421 if (char == " " or char == "\n")
423 append(stg_lines[size(stg_lines) - 1], current_word);
425 if (char == "\n") append(stg_lines, []);
429 current_word ~= char;
433 var jetway_array = [];
434 foreach (var line; stg_lines)
436 if (size(line) < 6 or line[0] != "OBJECT_SHARED") continue;
439 foreach (var profile; Static_jetway)
441 foreach (var model; profile.models)
443 if (model == line[1]) foundmodel = 1;
451 if (jetway == nil) continue;
452 var heading = num(line[5]);
453 var coord = geo.Coord.new();
454 coord.set_latlon(line[3], line[2], line[4]);
455 coord.apply_course_distance(360 - heading, -jetway.offsets.x);
456 coord.apply_course_distance(360 - heading + 90, jetway.offsets.y);
457 coord.set_alt(coord.alt() + jetway.offsets.z);
460 hash.heading = heading + jetway.offsets.heading;
461 hash.init_extend = jetway.init_pos.extend;
462 hash.init_heading = jetway.init_pos.heading;
463 hash.init_pitch = jetway.init_pos.pitch;
464 hash.init_ent_heading = jetway.init_pos.ent_heading;
465 hash.model = jetway.model;
466 hash.airline = jetway.airline;
467 append(jetway_array, hash);
470 var airport = getprop("/sim/airport/closest-airport-id");
471 if (airport == "") return;
475 if (i >= size(jetway_array)) return;
476 var jetway = jetway_array[i];
477 jetways.Jetway.new(airport, jetway.model, "", 0, jetway.airline, jetway.coord.lat(), jetway.coord.lon(), jetway.coord.alt(), jetway.heading, jetway.init_extend, jetway.init_heading, jetway.init_pitch, jetway.init_ent_heading);
478 if (!jetways.isin(jetways.loaded_airports, airport)) append(jetways.loaded_airports, airport);
480 settimer(loop, jetways.LOAD_JETWAY_PERIOD);
483 jetways.alert("Creating " ~ size(jetway_array) ~ " jetways for airport " ~ airport);
486 var flash = func(jetway)
488 if (!contains(jetway, "_flashnum") or jetway._flashnum == -1)
490 jetway._alt = jetway.alt;
491 jetway.setpos(jetway.lat, jetway.lon, jetway.heading, -geo.ERAD);
492 jetway._flashnum = 0;
493 settimer(func flash(jetway), FLASH_PERIOD);
495 elsif (!contains(jetway, "_alt"))
497 jetway._flashnum = -1;
498 jetway.setpos(jetway.lat, jetway.lon, jetway.heading, geo.elevation(jetway.lat, jetway.lon));
501 elsif (jetway._flashnum == FLASH_NUM + 1)
503 jetway.setpos(jetway.lat, jetway.lon, jetway.heading, jetway._alt);
505 jetway._flashnum = -1;
509 if (jetway.alt == -geo.ERAD)
511 jetway.setpos(jetway.lat, jetway.lon, jetway.heading, jetway._alt);
515 jetway.setpos(jetway.lat, jetway.lon, jetway.heading, -geo.ERAD);
517 jetway._flashnum += 1;
518 settimer(func flash(jetway), FLASH_PERIOD);
524 if (dialog_object == nil) dialog_object = gui.Dialog.new("/sim/gui/dialogs/jetways-adjust/dialog", "gui/dialogs/jetways-adjust.xml");
525 dialog_object.open();
527 var print_help = func
529 print("JETWAY EDITOR HELP");
530 print("*******************************************************");
531 print("See: http://wiki.flightgear.org/Howto:_Animated_jetways");
533 print("Adjust position, heading, and altitude with top sliders");
534 print("Adjust initial jetway positions with bottom sliders");
536 print("<Model> model of selected jetway");
537 print("<Door> aircraft door number of selected jetway");
538 print("<Airline sign> airline sign code of selected jetway");
539 print("<Gate> gate number of selected jetway");
541 print("[Center sliders] apply slider offsets and return sliders to 0");
542 print("[Export] export jetway definition file(s)");
543 print("[STG converter] convert static jetways in STG files to animated jetways");
544 print("[?] show this help text");
546 print("Click add jetway on click position");
547 print("Alt-click move selected jetway to click position");
548 print("Ctrl-click select a jetway near click position");
549 print("Shift-click deselect selected jetway");
550 print("Backspace delete selected jetway");
551 print("*******************************************************");
554 _setlistener("/nasal/jetways_edit/loaded", func
556 print("Animated jetway editor ... loaded");
557 kbd_shift = props.globals.getNode("/devices/status/keyboard/shift");
558 kbd_ctrl = props.globals.getNode("/devices/status/keyboard/ctrl");
559 kbd_alt = props.globals.getNode("/devices/status/keyboard/alt");
560 enabled = props.globals.getNode("/nasal/jetways_edit/enabled");
562 setlistener("/sim/jetways/adjust/model", func(n)
564 var v = n.getValue();
565 if (selected_jetway != nil and v != selected_jetway.model)
570 setlistener("/sim/jetways/adjust/door", func(n)
572 var v = n.getValue();
573 if (selected_jetway != nil and v != selected_jetway.door)
578 setlistener("/sim/jetways/adjust/airline", func(n)
580 var v = n.getValue();
581 if (selected_jetway != nil and v != selected_jetway.airline)
583 adjust("airline", v);
586 setlistener("/sim/jetways/adjust/gate", func(n)
588 var v = n.getValue();
589 if (selected_jetway != nil and v != selected_jetway.gate)
594 setlistener("/devices/status/keyboard/event", func(event)
596 if (!event.getNode("pressed").getValue()) return;
597 if (enabled.getBoolValue() and event.getNode("key").getValue() == 8) delete();
599 setlistener("/devices/status/mice/mouse/button[1]", func(n) mouse_mmb = n.getBoolValue(), 1, 0);
600 setlistener("/sim/signals/click", func if (!mouse_mmb and enabled.getBoolValue()) click(geo.click_position()));