diff --git a/Nasal/IOrules b/Nasal/IOrules index 07117c0f1..b130958f7 100644 --- a/Nasal/IOrules +++ b/Nasal/IOrules @@ -37,3 +37,4 @@ WRITE ALLOW $FG_HOME/Export/* WRITE ALLOW $FG_HOME/state/*.xml WRITE ALLOW $FG_HOME/aircraft-data/*.xml WRITE ALLOW $FG_HOME/Wildfire/*.xml +WRITE ALLOW $FG_HOME/runtime-jetways/*.xml diff --git a/Nasal/jetways.nas b/Nasal/jetways.nas new file mode 100644 index 000000000..68d0b68fa --- /dev/null +++ b/Nasal/jetways.nas @@ -0,0 +1,889 @@ +############################################################################### +## +## Animated Jetway System. Spawns and manages interactive jetway models. +## +## Copyright (C) 2011 Ryan Miller +## This file is licensed under the GPL license version 2 or later. +## +############################################################################### + +############################################################################### +# (See http://wiki.flightgear.org/Howto:_Animated_jetways) +# +# Special jetway definition files located in $FG_ROOT/Airports/Jetways/XXXX.xml +# for each airport are loaded when the user's aircraft is within 50 nm of the +# airport. The script dynamically generates runtime model files, writes them to +# $FG_ROOT/Models/Airport/Jetway/runtimeX.xml, and places them into the +# simulator using the model manager. +# +# Different jetway models can be defined and are placed under +# $FG_ROOT/Models/Airport/Jetway/XXX.xml. +# +# Jetways can be extended/retracted independently either by user operation or +# by automatic extension for AI models and multiplayer aircraft. +# +# UTILITY FUNCTIONS +# ----------------- +# +# print_debug(<message>) - prints debug messages +# <message> - message to print +# +# print_error(<messsage>) - prints error messages +# <message> - error to print +# +# alert(<message>) - displays an alert message in-sim +# <message> - the message +# +# normdeg(<angle>) - normalizes angle measures between -180° and 180° +# <angle> - angle to normalize +# +# remove(<vector>, <item>) - removes an element from a vector +# <vector> - vector +# <index> - item +# +# isin(<vector>, <item>) - checks if an item exists in a vector +# <vector> - vector +# <item> - item +# +# putmodel(<path>, <lat>, <lon>, <alt>, <hdg>) - add a model to the scene graph (unlike geo.put_model(), models added with this function can be adjusted) +# <path> - model path +# <lat> - latitude +# <lon> - longitude +# <alt> - altitude in m +# <hdg> - heading +# +# interpolate_table(<table>, <value>) - interpolates a value within a table +# <table> - interpolation table/vector, in the format of [[<ind>, <dep>], [<ind>, <dep>], ... ] +# <value> - value +# +# get_relative_filepath(<path>, <target>) - gets a relative file path from a directory +# <path> - directory path should be relative to +# <target> - target directory +# +# find_airports(<max dist>) - gets a list of nearest airports +# <max dist> - maximum search distance in nm (currently unused) +# +# JETWAY CLASS +# ------------ +# +# Jetway. - creates a new jetway object/model +# new(<airport>, <model>, <gate>, <door>, +# <airline>, <lat>, <lon>, <elev>, +# <heading>, [, <init_extend>] +# [, <init_heading>] [, <init_pitch>] +# [, <init_ent_heading>]) +# <airport> - ICAO of associated airport +# <model> - jetway model definition (i.e. Models/Airport/Jetway/generic.xml) +# <gate> - gate number (i.e. "A1") +# <door> - door number (i.e. 0) +# <airline> - airline code (i.e. "AAL") +# <lat> - latitude location of model +# <lon> - longitude location of model +# <elev> - elevation of model in m +# <heading> - (optional) heading of model +# <init_extend> - (optional) initial extension of tunnel in m +# <init_heading> - (optional) initial rotation of tunnel along the Z axis +# <init_pitch> - (optional) initial pitch of tunnel (rotation along Y axis) +# <init_ent_heading> - (optional) initial rotation of entrance along the Z axis +# +# toggle(<user>, <heading>, <coord> - extends/retracts a jetway +# [, <hood>]) +# <user> - whether or not jetway is toggled by user command (0/1) +# <heading> - heading of aircraft to connect to +# <coord> - a geo.Coord of the target aircraft's door +# <hood> - (optional) amount to rotate jetway hood (only required when <user> != 1) +# +# extend(<user>, <heading>, <coord> - extends a jetway (should be called by Jetway.toggle()) +# [, <hood>]) +# <user> - whether or not jetway is toggled by user command (0/1) +# <heading> - heading of aircraft to connect to +# <coord> - a geo.Coord of the target aircraft's door +# <hood> - (optional) amount to rotate jetway hood (only required when <user> != 1) +# +# retract(<user>) - retracts a jetway (should be called by Jetway.toggle()) +# <user> - whether or not a jetway is toggled by user command (0/1) +# +# remove() - removes a jetway object and its model +# +# reload() - reloads a jetway object and its model +# +# setpos(<lat>, <lon>, <heading>, <alt>) - moves a jetway to a new location +# <lat> - new latitude +# <lon> - new longitude +# <heading> - new heading +# <alt> - new altitude in m +# +# setmodel(<model>, <airline>, <gate>) - changes the jetway model +# <model> - new model +# <airline> - new airline sign code +# <gate> - new gate number +# +# INTERACTION FUNCTIONS +# --------------------- +# +# dialog() - open settings dialog +# +# toggle_jetway(<id>) - toggles a jetway by user command (should be called by a pick animation in a jetway model) +# <id> - id number of jetway to toggle +# +# toggle_jetway_from_coord(<door>, <hood>, - toggles a jetway with the target door at the specified coordinates +# <heading>, [<lat>, +# <lon>] [<coord>]) +# <door> - door number (i.e. 0) +# <hood> - amount to rotate jetway hood +# <lat> - (required or <coord>) latitude location of door +# <lon> - (required or <coord>) longitude location of door +# <coord> - (required or <lat>, <lon>) a geo.Coord of the door +# +# toggle_jetway_from_model(<model node>) - toggles a jetway using an AI model instead of the user's aircraft +# <model node> - path of AI model (i.e. /ai/models/aircraft[0])- can be the path in a string or a props.Node +# +# INTERNAL FUNCTIONS +# ------------------ +# +# load_airport_jetways(<airport>) - loads jetways at an airport +# <airport> - ICAO of airport +# +# unload_airport_jetways(<airport>) - unloads jetways at an airport +# <airport> - ICAO of airport +# +# update_jetways() - interpolates model animation values and keeps the system tidy +# +# load_jetways() - loads new jetway models and unloads out-of-range models every 30 seconds +# + +## Utility functions +#################### + +# prints debug messages +var print_debug = func(msg) + { + if (debug_switch.getBoolValue()) + { + print(msg); + } + }; +# prints error messages +var print_error = func(msg) + { + print("\x1b[31m" ~ msg ~ "\x1b[m"); + }; +# alerts the user +var alert = func(msg) + { + setprop("/sim/messages/ground", msg); + }; +# normalizes headings between -180 and 180 +var normdeg = func(x) + { + while (x >= 180) + { + x -= 360; + } + while (x <= -180) + { + x += 360; + } + return x; + }; +# deletes an item in a vector +var remove = func(vector, item) + { + var s = size(vector); + var found = 0; + for (var i = 0; i < s; i += 1) + { + if (found) + { + vector[i - 1] = vector[i]; + } + elsif (vector[i] == item) + { + found = 1; + } + } + if (found) setsize(vector, s - 1); + return vector; + }; +# checks if an item is in a vector +var isin = func(vector, v) + { + for (var i = 0; i < size(vector); i += 1) + { + if (vector[i] == v) return 1; + } + return 0; + }; +# adds a model +var putmodel = func(path, lat, lon, alt, hdg) + { + var models = props.globals.getNode("/models"); + var model = nil; + for (var i = 0; 1; i += 1) + { + if (models.getChild("model", i, 0) == nil) + { + model = models.getChild("model", i, 1); + break; + } + } + var model_path = model.getPath(); + model.getNode("path", 1).setValue(path); + model.getNode("latitude-deg", 1).setDoubleValue(lat); + model.getNode("latitude-deg-prop", 1).setValue(model_path ~ "/latitude-deg"); + model.getNode("longitude-deg", 1).setDoubleValue(lon); + model.getNode("longitude-deg-prop", 1).setValue(model_path ~ "/longitude-deg"); + model.getNode("elevation-ft", 1).setDoubleValue(alt * M2FT); + model.getNode("elevation-ft-prop", 1).setValue(model_path ~ "/elevation-ft"); + model.getNode("heading-deg", 1).setDoubleValue(hdg); + model.getNode("heading-deg-prop", 1).setValue(model_path ~ "/heading-deg"); + model.getNode("pitch-deg", 1).setDoubleValue(0); + model.getNode("pitch-deg-prop", 1).setValue(model_path ~ "/pitch-deg"); + model.getNode("roll-deg", 1).setDoubleValue(0); + model.getNode("roll-deg-prop", 1).setValue(model_path ~ "/roll-deg"); + model.getNode("load", 1).remove(); + return model; + }; + +# interpolates a value +var interpolate_table = func(table, v) + { + var x = 0; + forindex (i; table) + { + if (v >= table[i][0]) + { + x = i + 1 < size(table) ? (v - table[i][0]) / (table[i + 1][0] - table[i][0]) * (table[i + 1][1] - table[i][1]) + table[i][1] : table[i][1]; + } + } + return x; + }; +# gets a relative file path +var get_relative_filepath = func(path, target) + { + var newpath = ""; + for (var i = size(path) - 1; i >= 0; i -= 1) + { + var char = substr(path, i, 1); + if (char == "/") newpath ~= "../"; + } + # we can just append the target path for UNIX systems, but we need to remove the drive letter prefix for DOS systems + return newpath ~ (string.match(substr(target, 0, 3), "?:/") ? substr(target, 2, size(target) - 2) : target); + }; +# gets a list of nearest airports +# TODO: Don't use /sim/airport/nearest-airport-id, which restricts the list to 1 airport +var find_airports = func(max_distance) + { + var apt = getprop("/sim/airport/closest-airport-id"); + return apt == "" ? nil : [apt]; + }; + +## Global variables +################### + +var root = nil; +var home = nil; +var scenery = []; + +var UPDATE_PERIOD = 1 / 30; +var LOAD_PERIOD = 30; +var LOAD_DISTANCE = 50; # in nautical miles +var LOAD_JETWAY_PERIOD = 0.05; +var NUMBER_OF_JETWAYS = 2000; # approx max number of jetways loadable in FG +var runtime_files = NUMBER_OF_JETWAYS / LOAD_PERIOD * LOAD_JETWAY_PERIOD; +runtime_files = int(runtime_files) == runtime_files ? runtime_files : int(runtime_files) + 1; +var runtime_file = 0; +var update_loopid = -1; +var load_loopid = -1; +var load_listenerid = nil; +var loadids = {}; +var dialog_object = nil; +var loaded_airports = []; +var jetways = []; + +# properties +var on_switch = "/sim/jetways/enabled"; +var debug_switch = "/sim/jetways/debug"; +var mp_switch = "/sim/jetways/interact-with-multiplay"; +var jetway_id_prop = "/sim/jetways/last-loaded-jetway"; + +# interpolation tables +var extend_rate = 0.5; +var extend_table = [ + [0.0, 0.0], + [0.2, 0.3], + [0.6, 0.3], + [0.8, 1.0], + [1.0, 1.0] + ]; +var pitch_rate = 1; +var pitch_table = [ + [0.0, 0.0], + [0.4, 0.7], + [0.7, 1.0], + [1.0, 1.0] + ]; +var heading_rate = 1; +var heading_table = [ + [0.0, 0.0], + [0.2, 0.0], + [0.6, 0.7], + [0.9, 1.0], + [1.0, 1.0] + ]; +var heading_entrance_rate = 5; +var heading_entrance_table = [ + [0.0, 0.0], + [0.3, 0.0], + [0.6, 0.7], + [0.8, 1.0], + [1.0, 1.0] + ]; +var hood_rate = 1; +var hood_table = [ + [0.0, 0.0], + [0.9, 0.0], + [1.0, 1.0] + ]; + +## Classes +########## + +# main jetway class +var Jetway = + { + new: func(airport, model, gate, door, airline, lat, lon, elev, heading, init_extend = 0, init_heading = 0, init_pitch = 0, init_ent_heading = 0) + { + var id = 0; + for (var i = 0; 1; i += 1) + { + if (i == size(jetways)) + { + setsize(jetways, i + 1); + id = i; + break; + } + elsif (jetways[i] == nil) + { + id = i; + } + } + # locate the jetway model directory and load the model tree + var model_tree = nil; + var model_file = ""; + var model_dir = ""; + var airline_file = ""; + if (props.globals.getNode("/sim/paths/use-custom-scenery-data", 1).getBoolValue()) + { + # search in scenery directories + foreach (var scenery_path; scenery) + { + model_dir = scenery_path ~ "/Models/Airport/Jetway"; + model_file = model_dir ~ "/" ~ model ~ ".xml"; + print_debug("Trying to load a jetway model from " ~ model_file); + if (io.stat(model_file) == nil) continue; + model_tree = io.read_properties(model_file); + airline_file = get_relative_filepath(home ~ "/runtime-jetways", model_dir ~ "/Airlines/" ~ airline ~ ".png"); + if (io.stat(airline_file) == nil) airline_file = get_relative_filepath(home ~ "/runtime-jetways", model_dir ~ "/Airlines/None.png"); + break; + } + } + if (model_tree == nil) + { + model_dir = root ~ "/Models/Airport/Jetway"; + model_file = model_dir ~ "/" ~ model ~ ".xml"; + print_debug("Falling back to " ~ model_file); + if (io.stat(model_file) == nil) + { + print_error("Failed to load jetway model: " ~ model); + return; + } + model_tree = io.read_properties(model_file); + airline_file = get_relative_filepath(home ~ "/runtime-jetways", model_dir ~ "/Airlines/" ~ airline ~ ".png"); + if (io.stat(airline_file) == nil) airline_file = get_relative_filepath(home ~ "/runtime-jetways", model_dir ~ "/Airlines/None.png"); + } + + var m = + { + parents: [Jetway] + }; + m.airport = airport; + m.gate = gate; + m.airline = airline; + m.id = id; + m.model = model; + m.extended = 0; + m.door = door; + m.lat = lat; + m.lon = lon; + m.elev = elev; + m.heading = geo.normdeg(180 - heading); + m.init_extend = init_extend; + m.init_heading = init_heading; + m.init_pitch = init_pitch; + m.init_ent_heading = init_ent_heading; + m.target_extend = 0; + m.target_pitch = 0; + m.target_heading = 0; + m.target_ent_heading = 0; + m.target_hood = 0; + m.rotunda_x = model_tree.getNode("rotunda/x-m").getValue(); + m.rotunda_y = model_tree.getNode("rotunda/y-m").getValue(); + m.rotunda_z = model_tree.getNode("rotunda/z-m").getValue(); + m.offset_extend = model_tree.getNode("extend-offset-m").getValue(); + m.offset_entrance = model_tree.getNode("entrance-offset-m").getValue(); + m.min_extend = model_tree.getNode("min-extend-m").getValue(); + m.max_extend = model_tree.getNode("max-extend-m").getValue(); + + # get the runtime file path + if (runtime_file == runtime_files) + { + runtime_file = 0; + } + var runtime_file_path = home ~ "/runtime-jetways/" ~ runtime_file ~ ".xml"; + runtime_file += 1; + + # create the model node and the door object + m.node = putmodel(runtime_file_path, lat, lon, elev, geo.normdeg(360 - heading)); + var node_path = m.node.getPath(); + m.door_object = aircraft.door.new(node_path ~ "/jetway-position", 0); + + # manipulate the model tree + model_tree.getNode("path").setValue(model_dir ~ "/" ~ model_tree.getNode("path").getValue()); + model_tree.getNode("toggle-action-script").setValue("jetways.toggle_jetway(" ~ id ~ ");"); + model_tree.getNode("gate").setValue(m.gate); + model_tree.getNode("airline-code").setValue(airline_file); + model_tree.getNode("extend-m").setValue(props.globals.initNode(node_path ~ "/jetway-position/extend-m", 0, "DOUBLE").getPath()); + model_tree.getNode("pitch-deg").setValue(props.globals.initNode(node_path ~ "/jetway-position/pitch-deg", 0, "DOUBLE").getPath()); + model_tree.getNode("heading-deg").setValue(props.globals.initNode(node_path ~ "/jetway-position/heading-deg", 0, "DOUBLE").getPath()); + model_tree.getNode("entrance-heading-deg").setValue(props.globals.initNode(node_path ~ "/jetway-position/entrance-heading-deg", 0, "DOUBLE").getPath()); + model_tree.getNode("hood-deg").setValue(props.globals.initNode(node_path ~ "/jetway-position/hood-deg", 0, "DOUBLE").getPath()); + # write the model tree + io.write_properties(runtime_file_path, model_tree); + + jetways[id] = m; + print_debug("Loaded jetway #" ~ id); + jetway_id_prop.setValue(id); + return m; + }, + toggle: func(user, heading, coord, hood = 0) + { + if (me.extended) + { + me.retract(user, heading, coord); + } + else + { + me.extend(user, heading, coord, hood); + } + }, + extend: func(user, heading, door_coord, hood = 0) + { + me.extended = 1; + + # get the coordinates of the jetway and offset for the rotunda position + var jetway_coord = geo.Coord.new(); + jetway_coord.set_latlon(me.lat, me.lon); + jetway_coord.apply_course_distance(me.heading, me.rotunda_x); + jetway_coord.apply_course_distance(me.heading - 90, me.rotunda_y); + jetway_coord.set_alt(me.elev + me.rotunda_z); + + if (debug_switch.getBoolValue()) + { + # place UFO cursors at the calculated door and jetway positions for debugging purposes + geo.put_model("Aircraft/ufo/Models/cursor.ac", door_coord); + geo.put_model("Aircraft/ufo/Models/cursor.ac", jetway_coord); + } + + # offset the door for the length of the jetway entrance + door_coord.apply_course_distance(heading - 90, me.offset_entrance); + + # calculate the bearing to the aircraft and the distance from the door + me.target_heading = normdeg(jetway_coord.course_to(door_coord) - me.heading - me.init_heading); + me.target_extend = jetway_coord.distance_to(door_coord) - me.offset_extend - me.init_extend; + + # check if distance exceeds maximum jetway extension length + if (me.target_extend + me.init_extend > me.max_extend) + { + me.extended = 0; + me.target_extend = 0; + me.target_heading = 0; + if (user) alert("Your aircraft is too far from this jetway."); + print_debug("Jetway #" ~ me.id ~ " is too far from the door"); + return; + } + # check if distance fails to meet minimum jetway extension length + if (me.target_extend + me.init_extend < me.min_extend) + { + me.extended = 0; + me.target_extend = 0; + me.target_heading = 0; + if (user) alert("Your aircraft is too close to this jetway."); + print_debug("Jetway #" ~ me.id ~ " is too close to the door"); + return; + } + + # calculate the jetway pitch, entrance heading, and hood + me.target_pitch = math.atan2((door_coord.alt() - jetway_coord.alt()) / (me.target_extend + me.offset_extend + me.init_extend), 1) * R2D - me.init_pitch; + me.target_ent_heading = normdeg((heading + 90) - (me.heading + (me.target_heading + me.init_heading) + me.init_ent_heading)); + me.target_hood = user ? getprop("/sim/model/door[" ~ me.door ~ "]/jetway-hood-deg") : hood; + + # fire up the animation + if (user) alert("Extending jetway."); + var animation_time = math.abs(me.target_extend / extend_rate) + math.abs(me.target_pitch / pitch_rate) + math.abs(me.target_heading / heading_rate) + math.abs(me.target_ent_heading / heading_entrance_rate) + math.abs(me.target_hood / hood_rate); + me.door_object.swingtime = animation_time; + me.door_object.open(); + + print_debug("************************************************"); + print_debug("Activated jetway #" ~ me.id); + print_debug("Using door #" ~ me.door); + print_debug("Jetway heading: " ~ me.heading ~ " deg"); + print_debug("Extension: " ~ me.target_extend ~ " m"); + print_debug("Pitch: " ~ me.target_pitch ~ " deg"); + print_debug("Heading: " ~ me.target_heading ~ " deg"); + print_debug("Entrance heading: " ~ me.target_ent_heading ~ " deg"); + print_debug("Hood: " ~ me.target_hood ~ " deg"); + print_debug("Total animation time: " ~ animation_time ~ " sec"); + print_debug("Jetway extending"); + print_debug("************************************************"); + }, + retract: func(user) + { + if (user) alert("Retracting jetway."); + me.door_object.close(); + me.extended = 0; + + print_debug("************************************************"); + print_debug("Activated jetway #" ~ me.id); + print_debug("Total animation time: " ~ me.door_object.swingtime ~ " sec"); + print_debug("Jetway retracting"); + print_debug("************************************************"); + }, + remove: func + { + me.node.remove(); + var id = me.id; + jetways[me.id] = nil; + print_debug("Unloaded jetway #" ~ id); + }, + reload: func + { + var airport = me.airport; + var model = me.model; + var gate = me.gate; + var door = me.door; + var airline = me.airline; + var lat = me.lat; + var lon = me.lon; + var elev = me.elev; + var heading = geo.normdeg(180 - (me.heading - 360)); + var init_extend = me.init_extend; + var init_heading = me.init_heading; + var init_pitch = me.init_pitch; + var init_ent_heading = me.init_ent_heading; + me.remove(); + Jetway.new(airport, model, gate, door, airline, lat, lon, elev, heading, init_extend, init_heading, init_pitch, init_ent_heading); + }, + setpos: func(lat, lon, hdg, alt) + { + me.node.getNode("latitude-deg").setValue(lat); + me.lat = lat; + me.node.getNode("longitude-deg").setValue(lon); + me.lon = lon; + me.node.getNode("heading-deg").setValue(geo.normdeg(hdg - 180)); + me.heading = hdg; + me.node.getNode("elevation-ft").setValue(alt * M2FT); + me.elev = alt; + }, + setmodel: func(model, airline, gate) + { + me.airline = airline; + me.gate = gate; + me.model = model; + me.extended = 0; + me.target_extend = 0; + me.target_pitch = 0; + me.target_heading = 0; + me.target_ent_heading = 0; + me.target_hood = 0; + me.door_object.setpos(0); + me.reload(); + } + }; + +## Interaction functions +######################## + +var dialog = func + { + if (dialog_object == nil) dialog_object = gui.Dialog.new("/sim/gui/dialogs/jetways/dialog", "gui/dialogs/jetways.xml"); + dialog_object.open(); + }; + +var toggle_jetway = func(n) + { + var jetway = jetways[n]; + if (jetway == nil) return; + var door = props.globals.getNode("/sim/model/door[" ~ jetway.door ~ "]"); + if (door == nil) + { + alert("Your aircraft does not define the location of door " ~ (jetway.door + 1) ~ ", cannot extend this jetway."); + return; + } + + # get the coordinates of the user's aircraft and offset for the door position and aircraft pitch + var coord = geo.aircraft_position(); + var heading = getprop("/orientation/heading-deg"); + var pitch = getprop("/orientation/pitch-deg"); + coord.apply_course_distance(heading, -door.getChild("position-x-m").getValue()); + coord.apply_course_distance(heading + 90, door.getChild("position-y-m").getValue()); + coord.set_alt(coord.alt() + door.getChild("position-z-m").getValue()); + coord.set_alt(coord.alt() + math.tan(pitch * D2R) * -door.getChild("position-x-m").getValue()); + + jetway.toggle(1, heading, coord); + }; +var toggle_jetway_from_coord = func(door, hood, heading, lat, lon = nil) + { + if (isa(lat, geo.Coord)) + { + var coord = lat; + } + else + { + var coord = geo.Coord.new(); + coord.set_latlon(lat, lon); + } + var closest_jetway = nil; + var closest_jetway_dist = nil; + var closest_jetway_coord = nil; + for (var i = 0; i < size(jetways); i += 1) + { + if (jetways[i] == nil) continue; + var jetway = jetways[i]; + var jetway_coord = geo.Coord.new(); + jetway_coord.set_latlon(jetway.lat, jetway.lon); + + var distance = jetway_coord.distance_to(coord); + if ((closest_jetway_dist == nil or distance < closest_jetway_dist) and jetway.door == door) + { + closest_jetway = jetway; + closest_jetway_dist = distance; + closest_jetway_coord = jetway_coord; + } + } + if (closest_jetway == nil) + { + print_debug("No jetways available"); + } + elsif (!closest_jetway.extended) + { + closest_jetway.toggle(0, heading, coord, hood); + } + }; +var toggle_jetway_from_model = func(model) + { + model = aircraft.makeNode(model); + var doors = model.getChildren("door"); + if (doors == nil or size(doors) == 0) return; + for (var i = 0; i < size(doors); i += 1) + { + var coord = geo.Coord.new(); + var hdg = model.getNode("orientation/true-heading-deg").getValue(); + var lat = model.getNode("position/latitude-deg").getValue(); + var lon = model.getNode("position/longitude-deg").getValue(); + var alt = model.getNode("position/altitude-ft").getValue() * FT2M + doors[i].getNode("position-z-m").getValue(); + coord.set_latlon(lat, lon, alt); + coord.apply_course_distance(hdg, -doors[i].getNode("position-x-m").getValue()); + coord.apply_course_distance(hdg + 90, doors[i].getNode("position-y-m").getValue()); + print_debug("Connecting a jetway to door #" ~ i ~ " for model " ~ model.getPath()); + toggle_jetway_from_coord(i, doors[i].getNode("jetway-hood-deg").getValue(), hdg, coord); + } + }; + +## Internal functions +##################### + +# loads jetways at an airport +var load_airport_jetways = func(airport) + { + if (isin(loaded_airports, airport)) return; + var tree = props.globals.getNode("/sim/paths/use-custom-scenery-data", 1).getBoolValue() ? io.read_airport_properties(airport, "jetways") : (io.stat(root ~ "/AI/Airports/" ~ airport ~ "/jetways.xml") == nil ? nil : io.read_properties(root ~ "/AI/Airports/" ~ airport ~ "/jetways.xml")); + if (tree == nil) return; + print_debug("Loading jetways for airport " ~ airport); + var nodes = tree.getChildren("jetway"); + + loadids[airport] = loadids[airport] == nil ? 0 : loadids[airport] + 1; + var i = 0; + var loop = func(id) + { + if (id != loadids[airport]) return; + if (i >= size(nodes)) + { + append(loaded_airports, airport); + return; + } + var jetway = nodes[i]; + var model = jetway.getNode("model", 1).getValue() or return; + var gate = jetway.getNode("gate", 1).getValue() or ""; + var door = jetway.getNode("door", 1).getValue() or 0; + var airline = jetway.getNode("airline", 1).getValue() or "None"; + var lat = jetway.getNode("latitude-deg", 1).getValue() or return; + var lon = jetway.getNode("longitude-deg", 1).getValue() or return; + var elev = jetway.getNode("elevation-m", 1).getValue() or 0; + var heading = jetway.getNode("heading-deg", 1).getValue() or 0; + var init_extend = jetway.getNode("initial-position/jetway-extension-m", 1).getValue() or 0; + var init_heading = jetway.getNode("initial-position/jetway-heading-deg", 1).getValue() or 0; + var init_pitch = jetway.getNode("initial-position/jetway-pitch-deg", 1).getValue() or 0; + var init_ent_heading = jetway.getNode("initial-position/entrance-heading-deg", 1).getValue() or 0; + Jetway.new(airport, model, gate, door, airline, lat, lon, elev, heading, init_extend, init_heading, init_pitch, init_ent_heading); + + i += 1; + settimer(func loop(id), LOAD_JETWAY_PERIOD); + }; + settimer(func loop(loadids[airport]), 0); + }; +# unloads jetways at an airport +var unload_airport_jetways = func(airport) + { + print_debug("Unloading jetways for airport " ~ airport); + foreach (var jetway; jetways) + { + if (jetway != nil and jetway.airport == airport) jetway.remove(); + } + remove(loaded_airports, airport); + }; + +# restarts the main update loop +var restart = func(node) + { + if (!node.getBoolValue()) return; + update_loopid += 1; + update_jetways(update_loopid); + settimer(func + { + load_loopid += 1; + load_jetways(load_loopid); + }, 2); + print("Animated jetways ... initialized"); + }; +# main update loop (runs when jetways are enabled) +var update_jetways = func(loopid) + { + # terminate if loopid does not match + if (loopid != update_loopid) return; + # if jetways disabled, unload jetways and terminate + if (!on_switch.getBoolValue()) + { + for (var i = 0; i < size(jetways); i += 1) + { + if (jetways[i] != nil) jetways[i].remove(); + } + setsize(jetways, 0); + setsize(loaded_airports, 0); + return; + } + + var nearest_airport = airportinfo(); + nearest_airport = nearest_airport == nil ? nil : nearest_airport.id; + if (isin(loaded_airports, nearest_airport)) + { + # loop through the AI aircraft and extend/retract jetways + var ai_aircraft = props.globals.getNode("ai/models").getChildren("aircraft"); + foreach (var aircraft; ai_aircraft) + { + if (!aircraft.getNode("valid", 1).getBoolValue()) continue; + var connected = aircraft.getNode("connected-to-jetways", 1); + var velocity = aircraft.getNode("velocities/true-airspeed-kt").getValue(); + # TODO: Find a better way to know when the aircraft is "parked" + if (velocity > -1 and velocity < 1) + { + if (!connected.getBoolValue()) toggle_jetway_from_model(aircraft); + connected.setBoolValue(1); + } + else + { + if (connected.getBoolValue()) toggle_jetway_from_model(aircraft); + connected.setBoolValue(0); + } + } + # loop through the multiplayer aircraft and extend/retract jetways + # TODO: In the future, broadcast jetway properties over MP, making this part obselete + if (mp_switch.getBoolValue()) + { + var multiplayers = props.globals.getNode("ai/models").getChildren("multiplayer"); + foreach (var aircraft; multiplayers) + { + if (!aircraft.getNode("valid", 1).getBoolValue()) continue; + var connected = aircraft.getNode("connected-to-jetways", 1); + var velocity = aircraft.getNode("velocities/true-airspeed-kt").getValue(); + if (velocity > -1 and velocity < 1) + { + if (!connected.getBoolValue()) toggle_jetway_from_model(aircraft); + connected.setBoolValue(1); + } + else + { + if (connected.getBoolValue()) toggle_jetway_from_model(aircraft); + connected.setBoolValue(0); + } + } + } + } + # interpolate jetway values + for (var i = 0; i < size(jetways); i += 1) + { + if (jetways[i] == nil) continue; + var jetway = jetways[i]; + var position = jetway.door_object.getpos(); + jetway.node.getNode("jetway-position/extend-m").setValue(interpolate_table(extend_table, position) * jetway.target_extend + jetway.init_extend); + jetway.node.getNode("jetway-position/pitch-deg").setValue(interpolate_table(pitch_table, position) * jetway.target_pitch + jetway.init_pitch); + jetway.node.getNode("jetway-position/heading-deg").setValue(interpolate_table(heading_table, position) * jetway.target_heading + jetway.init_heading); + jetway.node.getNode("jetway-position/entrance-heading-deg").setValue(interpolate_table(heading_entrance_table, position) * jetway.target_ent_heading + jetway.init_ent_heading); + jetway.node.getNode("jetway-position/hood-deg").setValue(interpolate_table(hood_table, position) * jetway.target_hood); + } + settimer(func update_jetways(loopid), UPDATE_PERIOD); + }; +# loading/unloading loop (runs continuously) +var load_jetways = func(loopid) + { + if (load_listenerid != nil) removelistener(load_listenerid); + # terminate if loopid does not match + # unloading jetways if jetways are disabled is handled by update loop + if (loopid != load_loopid or !on_switch.getBoolValue()) return; + var airports = find_airports(LOAD_DISTANCE); + if (airports == nil) return; + # search for any airports out of range and unload their jetways + foreach (var airport; loaded_airports) + { + if (!isin(airports, airport)) + { + unload_airport_jetways(airport); + } + } + # load any airports in range + foreach (var airport; airports) + { + load_airport_jetways(airport); + } + settimer(func load_jetways(loopid), LOAD_PERIOD); + }; +## fire it up +_setlistener("/sim/signals/fdm-initialized", func + { + # global variables + root = string.normpath(getprop("/sim/fg-root")); + home = string.normpath(getprop("/sim/fg-home")); + foreach (var scenery_path; props.globals.getNode("/sim").getChildren("fg-scenery")) + { + append(scenery, string.normpath(scenery_path.getValue())); + } + if (size(scenery) == 0) append(scenery, root ~ "/Scenery"); + + # properties + on_switch = props.globals.getNode(on_switch, 1); + debug_switch = props.globals.getNode(debug_switch, 1); + mp_switch = props.globals.getNode(mp_switch, 1); + + jetway_id_prop = props.globals.getNode(jetway_id_prop, 1); + setlistener(on_switch, restart, 1, 0); + }); diff --git a/Nasal/jetways_edit.nas b/Nasal/jetways_edit.nas new file mode 100644 index 000000000..1b16925c7 --- /dev/null +++ b/Nasal/jetways_edit.nas @@ -0,0 +1,593 @@ +############################################################################### +## +## Animated Jetway System. Allows the user to edit jetways during runtime. +## +## Copyright (C) 2011 Ryan Miller +## This file is licensed under the GPL license version 2 or later. +## +############################################################################### + +############################################################################### +# (See http://wiki.flightgear.org/Howto:_Animated_jetways) +# + +### Static jetway model profiles ### +### This class specifies the offsets used when converting static jetways using the STG converter ### +var Static_jetway = + [ + # Models/Airport/jetway-movable.ac + # Models/Airport/jetway-movable.xml + # Models/Airport/jetway-movable-2.ac + # Models/Airport/jetway-movable-2.xml + # Models/Airport/jetway-movable-3.ac + # Models/Airport/jetway-movable-3.xml + { + models: + [ + "Models/Airport/jetway-movable.ac", + "Models/Airport/jetway-movable.xml", + "Models/Airport/jetway-movable-2.ac", + "Models/Airport/jetway-movable-2.xml", + "Models/Airport/jetway-movable-3.ac", + "Models/Airport/jetway-movable-3.xml" + ], + offsets: + { + x: -2.042, + y: 0, + z: 0, + heading: 0 + }, + init_pos: + { + extend: 7.24, + heading: 0, + pitch: 0, + ent_heading: -90 + }, + model: "generic", + airline: "None" + }, + # Models/Airport/jetway.xml + # Models/Airport/jetway-ba.ac + # Models/Airport/jetway-ba.xml + { + models: + [ + "Models/Airport/jetway.xml", + "Models/Airport/jetway-ba.ac", + "Models/Airport/jetway-ba.xml" + ], + offsets: + { + x: 0, + y: 0, + z: -0.25, + heading: 0 + }, + init_pos: + { + extend: 7.24, + heading: -6.7, + pitch: -3.6, + ent_heading: -83.3 + }, + model: "generic", + airline: "None" + }, + # Models/Airport/jetway-737-ba.ac + # Models/Airport/jetway-737-ba.xml + { + models: + [ + "Models/Airport/jetway-737-ba.ac", + "Models/Airport/jetway-737-ba.xml" + ], + offsets: + { + x: 0, + y: 0, + z: -0.25, + heading: 0 + }, + init_pos: + { + extend: 7.24, + heading: -6.7, + pitch: -4, + ent_heading: -83.3 + }, + model: "generic", + airline: "None" + }, + # Models/Airport/jetway-747-ba.ac + # Models/Airport/jetway-747-ba.xml + { + models: + [ + "Models/Airport/jetway-747-ba.ac", + "Models/Airport/jetway-747-ba.xml" + ], + offsets: + { + x: 0, + y: 0, + z: -0.25, + heading: 0 + }, + init_pos: + { + extend: 7.24, + heading: -6.7, + pitch: 2, + ent_heading: -83.3 + }, + model: "generic", + airline: "None" + }, + # Models/Airport/jetway-a320-ba.ac + # Models/Airport/jetway-a320-ba.xml + { + models: + [ + "Models/Airport/jetway-a320-ba.ac", + "Models/Airport/jetway-a320-ba.xml" + ], + offsets: + { + x: 0, + y: 0, + z: -0.25, + heading: 0 + }, + init_pos: + { + extend: 7.24, + heading: -6.7, + pitch: -1.6, + ent_heading: -83.3 + }, + model: "generic", + airline: "None" + }, + # Models/Airport/AutoGate-ba.ac + # Models/Airport/AutoGate.xml + { + models: + [ + "Models/Airport/AutoGate-ba.ac", + "Models/Airport/AutoGate.xml" + ], + offsets: + { + x: -10, + y: 25, + z: 0, + heading: -90 + }, + init_pos: + { + extend: 7.68, + heading: 0, + pitch: 0, + ent_heading: -90 + }, + model: "generic", + airline: "None" + }, + # Models/Airport/DockingGate-ba.ac + # Models/Airport/DockingGate.xml + { + models: + [ + "Models/Airport/DockingGate-ba.ac", + "Models/Airport/DockingGate.xml" + ], + offsets: + { + x: -10, + y: 5, + z: 0, + heading: -90 + }, + init_pos: + { + extend: 7.68, + heading: 0, + pitch: 0, + ent_heading: -90 + }, + model: "generic", + airline: "None" + } + ]; + +### Rest of script follows below ### +### Watch your step! :) ### +var dialog_object = nil; +var selected_jetway = nil; +var mouse_mmb = 0; +var kbd_shift = nil; +var kbd_ctrl = nil; +var kbd_alt = nil; +var enabled = nil; +var FLASH_PERIOD = 0.3; +var FLASH_NUM = 3; +var filedialog_listener = 0; + +var click = func(pos) + { + if (kbd_alt.getBoolValue()) + { + if (selected_jetway == nil) return; + selected_jetway.setpos(pos.lat(), pos.lon(), selected_jetway.heading, pos.alt()); + } + elsif (kbd_shift.getBoolValue()) + { + selected_jetway = nil; + } + elsif (kbd_ctrl.getBoolValue()) + { + var nearest_jetway = nil; + var min_dist = geo.ERAD; + for (var i = 0; i < size(jetways.jetways); i += 1) + { + var jetway = jetways.jetways[i]; + if (jetway == nil) continue; + var dist = geo.Coord.new().set_latlon(jetway.lat, jetway.lon, jetway.elev).direct_distance_to(pos); + if (dist < min_dist) + { + min_dist = dist; + nearest_jetway = jetway; + } + } + if (nearest_jetway != nil) + { + selected_jetway = nearest_jetway; + setprop("/sim/jetways/adjust/model", selected_jetway.model); + setprop("/sim/jetways/adjust/door", selected_jetway.door); + setprop("/sim/jetways/adjust/airline", selected_jetway.airline); + setprop("/sim/jetways/adjust/gate", selected_jetway.gate); + flash(nearest_jetway); + } + } + else + { + var airport = getprop("/sim/airport/closest-airport-id"); + if (airport == "") return; + selected_jetway = jetways.Jetway.new(airport, "generic", "FG", 0, "FGFS", pos.lat(), pos.lon(), pos.alt(), 0); + if (!jetways.isin(jetways.loaded_airports, airport)) append(jetways.loaded_airports, airport); + setprop("/sim/jetways/adjust/model", selected_jetway.model); + setprop("/sim/jetways/adjust/door", selected_jetway.door); + setprop("/sim/jetways/adjust/airline", selected_jetway.airline); + setprop("/sim/jetways/adjust/gate", selected_jetway.gate); + flash(selected_jetway); + } + }; +var delete = func + { + if (selected_jetway == nil) return; + selected_jetway.remove(); + selected_jetway = nil; + }; +var adjust = func(name, value) + { + if (selected_jetway == nil) return; + if (name == "longitudinal") + { + var jetway_pos = geo.Coord.new(); + jetway_pos.set_latlon(selected_jetway.lat, selected_jetway.lon, selected_jetway.elev); + var dir = geo.aircraft_position().course_to(jetway_pos); + jetway_pos.apply_course_distance(dir, value); + selected_jetway.setpos(jetway_pos.lat(), jetway_pos.lon(), selected_jetway.heading, selected_jetway.elev); + } + elsif (name == "transversal") + { + var jetway_pos = geo.Coord.new(); + jetway_pos.set_latlon(selected_jetway.lat, selected_jetway.lon, selected_jetway.elev); + var dir = geo.aircraft_position().course_to(jetway_pos) + 90; + jetway_pos.apply_course_distance(dir, value); + selected_jetway.setpos(jetway_pos.lat(), jetway_pos.lon(), selected_jetway.heading, selected_jetway.elev); + } + elsif (name == "altitude") + { + var elev = selected_jetway.elev + value * 0.4; + selected_jetway.setpos(selected_jetway.lat, selected_jetway.lon, selected_jetway.heading, elev); + } + elsif (name == "heading") + { + var hdg = geo.normdeg(selected_jetway.heading + value * 4); + selected_jetway.setpos(selected_jetway.lat, selected_jetway.lon, hdg, selected_jetway.elev); + } + elsif (name == "initial-extension") + { + var newvalue = selected_jetway.init_extend + value; + if (newvalue > selected_jetway.max_extend) + { + gui.popupTip("Value exceeds maximum jetway extension limit"); + } + elsif (newvalue < selected_jetway.min_extend) + { + gui.popupTip("Value lower than minimum jetway extension limit"); + } + else + { + selected_jetway.init_extend = newvalue; + } + } + elsif (name == "initial-pitch") + { + selected_jetway.init_pitch += value; + } + elsif (name == "initial-heading") + { + selected_jetway.init_heading += value; + } + elsif (name == "initial-entrance-heading") + { + selected_jetway.init_ent_heading += value; + } + elsif (name == "model") + { + selected_jetway.setmodel(value, selected_jetway.airline, selected_jetway.gate); + selected_jetway = jetways.jetways[getprop("/sim/jetways/last-loaded-jetway")]; + } + elsif (name == "door") + { + selected_jetway.door = value; + } + elsif (name == "airline") + { + selected_jetway.setmodel(selected_jetway.model, value, selected_jetway.gate); + selected_jetway = jetways.jetways[getprop("/sim/jetways/last-loaded-jetway")]; + } + elsif (name == "gate") + { + selected_jetway.setmodel(selected_jetway.model, selected_jetway.airline, value); + selected_jetway = jetways.jetways[getprop("/sim/jetways/last-loaded-jetway")]; + } + }; +var export = func + { + var path = getprop("/sim/fg-home") ~ "/Export/"; + var airports = {}; + var airportarray = []; + foreach (var jetway; jetways.jetways) + { + if (jetway == nil) continue; + if (airports[jetway.airport] == nil) + { + airports[jetway.airport] = []; + append(airportarray, jetway.airport); + } + var node = props.Node.new(); + node.getNode("model", 1).setValue(jetway.model); + node.getNode("gate", 1).setValue(jetway.gate); + node.getNode("door", 1).setIntValue(jetway.door); + node.getNode("airline", 1).setValue(jetway.airline); + node.getNode("latitude-deg", 1).setDoubleValue(jetway.lat); + node.getNode("longitude-deg", 1).setDoubleValue(jetway.lon); + node.getNode("elevation-m", 1).setDoubleValue(jetway.elev); + node.getNode("heading-deg", 1).setDoubleValue(geo.normdeg(180 - jetway.heading)); + node.getNode("initial-position/jetway-extension-m", 1).setDoubleValue(jetway.init_extend); + node.getNode("initial-position/jetway-heading-deg", 1).setDoubleValue(jetway.init_heading); + node.getNode("initial-position/jetway-pitch-deg", 1).setDoubleValue(jetway.init_pitch); + node.getNode("initial-position/entrance-heading-deg", 1).setDoubleValue(jetway.init_ent_heading); + append(airports[jetway.airport], node); + } + foreach (var airport; airportarray) + { + var file = path ~ airport ~ ".xml"; + var args = props.Node.new({ filename: file }); + var nodes = airports[airport]; + foreach (var node; nodes) + { + var data = args.getNode("data", 1); + for (var i = 0; 1; i += 1) + { + if (data.getChild("jetway", i, 0) == nil) + { + props.copy(node, data.getChild("jetway", i, 1)); + break; + } + } + } + fgcommand("savexml", args); + print("jetway definitions for airport " ~ airport ~ " exported to " ~ file); + } + }; +var convert_stg = func + { + fgcommand("dialog-show", props.Node.new({ "dialog-name": "file-select" })); + setprop("/sim/gui/dialogs/file-select/path", ""); + filedialog_listener = setlistener("/sim/gui/dialogs/file-select/path", func(n) + { + removelistener(filedialog_listener); + var path = n.getValue(); + if (path == "") return; + var stg = io.readfile(path); + var stg_lines = [[]]; + var current_word = ""; + for (var i = 0; i < size(stg); i += 1) + { + var char = substr(stg, i, 1); + if (char == " " or char == "\n") + { + append(stg_lines[size(stg_lines) - 1], current_word); + current_word = ""; + if (char == "\n") append(stg_lines, []); + } + else + { + current_word ~= char; + } + } + + var jetway_array = []; + foreach (var line; stg_lines) + { + if (size(line) < 6 or line[0] != "OBJECT_SHARED") continue; + var foundmodel = 0; + var jetway = nil; + foreach (var profile; Static_jetway) + { + foreach (var model; profile.models) + { + if (model == line[1]) foundmodel = 1; + } + if (foundmodel) + { + jetway = profile; + break; + } + } + if (jetway == nil) continue; + var heading = num(line[5]); + var coord = geo.Coord.new(); + coord.set_latlon(line[3], line[2], line[4]); + coord.apply_course_distance(360 - heading, -jetway.offsets.x); + coord.apply_course_distance(360 - heading + 90, jetway.offsets.y); + coord.set_alt(coord.alt() + jetway.offsets.z); + var hash = {}; + hash.coord = coord; + hash.heading = heading + jetway.offsets.heading; + hash.init_extend = jetway.init_pos.extend; + hash.init_heading = jetway.init_pos.heading; + hash.init_pitch = jetway.init_pos.pitch; + hash.init_ent_heading = jetway.init_pos.ent_heading; + hash.model = jetway.model; + hash.airline = jetway.airline; + append(jetway_array, hash); + } + + var airport = getprop("/sim/jetways/closest-airport-id"); + if (airport == "") return; + var i = 0; + var loop = func + { + if (i >= size(jetway_array)) return; + var jetway = jetway_array[i]; + 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); + if (!jetways.isin(jetways.loaded_airports, airport)) append(jetways.loaded_airports, airport); + i += 1; + settimer(loop, jetways.LOAD_JETWAY_PERIOD); + }; + settimer(loop, 0); + jetways.alert("Creating " ~ size(jetway_array) ~ " jetways for airport " ~ airport); + }, 0, 1); + }; +var flash = func(jetway) + { + if (!contains(jetway, "_flashnum") or jetway._flashnum == -1) + { + jetway._elev = jetway.elev; + jetway.setpos(jetway.lat, jetway.lon, jetway.heading, -geo.ERAD); + jetway._flashnum = 0; + settimer(func flash(jetway), FLASH_PERIOD); + } + elsif (!contains(jetway, "_elev")) + { + jetway._flashnum = -1; + jetway.setpos(jetway.lat, jetway.lon, geo.elevation(jetway.lat, jetway.lon)); + return; + } + elsif (jetway._flashnum == FLASH_NUM + 1) + { + jetway.setpos(jetway.lat, jetway.lon, jetway.heading, jetway._elev); + jetway._elev = nil; + jetway._flashnum = -1; + } + else + { + if (jetway.elev == -geo.ERAD) + { + jetway.setpos(jetway.lat, jetway.lon, jetway.heading, jetway._elev); + } + else + { + jetway.setpos(jetway.lat, jetway.lon, jetway.heading, -geo.ERAD); + } + jetway._flashnum += 1; + settimer(func flash(jetway), FLASH_PERIOD); + } + }; + +var dialog = func + { + if (dialog_object == nil) dialog_object = gui.Dialog.new("/sim/gui/dialogs/jetways-adjust/dialog", "gui/dialogs/jetways-adjust.xml"); + dialog_object.open(); + }; +var print_help = func + { + print("JETWAY EDITOR HELP"); + print("*******************************************************"); + print("See: http://wiki.flightgear.org/Howto:_Animated_jetways"); + print(""); + print("Adjust position, heading, and altitude with top sliders"); + print("Adjust initial jetway positions with bottom sliders"); + print(""); + print("<Model> model of selected jetway"); + print("<Door> aircraft door number of selected jetway"); + print("<Airline sign> airline sign code of selected jetway"); + print("<Gate> gate number of selected jetway"); + print(""); + print("[Center sliders] apply slider offsets and return sliders to 0"); + print("[Export] export jetway definition file(s)"); + print("[STG converter] convert static jetways in STG files to animated jetways"); + print("[?] show this help text"); + print(""); + print("Click add jetway on click position"); + print("Alt-click move selected jetway to click position"); + print("Ctrl-click select a jetway near click position"); + print("Shift-click deselect selected jetway"); + print("Backspace delete selected jetway"); + print("*******************************************************"); + }; + +_setlistener("/sim/signals/nasal-dir-initialized", func + { + kbd_shift = props.globals.getNode("/devices/status/keyboard/shift"); + kbd_ctrl = props.globals.getNode("/devices/status/keyboard/ctrl"); + kbd_alt = props.globals.getNode("/devices/status/keyboard/alt"); + enabled = props.globals.getNode("/sim/jetways/enable-editor"); + + setlistener("/sim/jetways/adjust/model", func(n) + { + var v = n.getValue(); + if (selected_jetway != nil and v != selected_jetway.model) + { + adjust("model", v); + } + }, 0, 0); + setlistener("/sim/jetways/adjust/door", func(n) + { + var v = n.getValue(); + if (selected_jetway != nil and v != selected_jetway.door) + { + adjust("door", v); + } + }, 0, 0); + setlistener("/sim/jetways/adjust/airline", func(n) + { + var v = n.getValue(); + if (selected_jetway != nil and v != selected_jetway.airline) + { + adjust("airline", v); + } + }, 0, 0); + setlistener("/sim/jetways/adjust/gate", func(n) + { + var v = n.getValue(); + if (selected_jetway != nil and v != selected_jetway.gate) + { + adjust("gate", v); + } + }, 0, 0); + setlistener("/devices/status/keyboard/event", func(event) + { + if (!event.getNode("pressed").getValue()) return; + if (enabled.getBoolValue() and event.getNode("key").getValue() == 8) delete(); + }); + setlistener("/devices/status/mice/mouse/button[1]", func(n) mouse_mmb = n.getBoolValue(), 1, 0); + setlistener("/sim/signals/click", func if (!mouse_mmb and enabled.getBoolValue()) click(geo.click_position())); + }); diff --git a/gui/dialogs/jetways-adjust.xml b/gui/dialogs/jetways-adjust.xml new file mode 100644 index 000000000..692acc7b9 --- /dev/null +++ b/gui/dialogs/jetways-adjust.xml @@ -0,0 +1,990 @@ +<?xml version="1.0" ?> + +<PropertyList> + + <name>jetways-adjust</name> + <modal>false</modal> + <layout>vbox</layout> + + <nasal> + <open><![CDATA[ + var self = cmdarg(); + var dlgname = self.getNode("name").getValue(); + var root = getprop("/sim/fg-root"); + var modelfiles = directory(root ~ "/Models/Airport/Jetway"); + var modelcombo = self.getNode("group[9]/combo[0]"); + var models = []; + foreach (var file; modelfiles) + { + if (substr(file, -3) == "xml") + { + append(models, substr(file, 0, size(file) - 4)); + } + } + for (var i = 0; i < size(models); i += 1) + { + modelcombo.getNode("value[" ~ i ~ "]", 1).setValue(models[i]); + } + + var airlinefiles = directory(root ~ "/Models/Airport/Jetway/Airlines"); + var airlinecombo = self.getNode("group[9]/combo[2]"); + var airlines = []; + foreach (var file; airlinefiles) + { + append(airlines, substr(file, 0, size(file) - 4)); + } + for (var i = 0; i < size(airlines); i += 1) + { + airlinecombo.getNode("value[" ~ i ~ "]", 1).setValue(airlines[i]); + } + + var Value = + { + new: func(name) + { + var m = { parents: [Value] }; + m.name = name; + var n = props.globals.getNode("/sim/jetways/adjust/" ~ m.name, 1); + m.sliderN = n.getNode("slider", 1); + m.offsetN = n.getNode("offset", 1); + m.offsetN.setDoubleValue(0); + m.sliderN.setDoubleValue(0); + m.last_slider = 0; + m.center(); + m.sliderL = setlistener(m.sliderN, func m.update()); + return m; + }, + update: func + { + var offset = me.sliderN.getValue(); + var value = me.offsetN.getValue() + me.sliderN.getValue() - me.last_slider; + jetways_edit.adjust(me.name, value); + me.offsetN.setDoubleValue(0); + me.last_slider = offset; + }, + center : func + { + me.offsetN.setValue(me.offsetN.getValue() + me.sliderN.getValue()); + me.sliderN.setDoubleValue(0); + }, + remove: func + { + removelistener(me.sliderL); + } + }; + var values = + [ + Value.new("longitudinal"), + Value.new("transversal"), + Value.new("altitude"), + Value.new("heading"), + Value.new("initial-extension"), + Value.new("initial-heading"), + Value.new("initial-pitch"), + Value.new("initial-entrance-heading") + ]; + var center_sliders = func + { + foreach (var v; values) + { + v.center(); + } + }; + var update = func(w) + { + self.setValues({"dialog-name": dlgname, "object-name": w}); + fgcommand("dialog-update", self); + center_sliders(); + }; + ]]></open> + <close> + foreach (var v; values) + { + v.remove(); + } + </close> + </nasal> + + <group> + <layout>hbox</layout> + <empty><stretch>1</stretch></empty> + + <text> + <label>Jetway Editor</label> + </text> + + <empty><stretch>1</stretch></empty> + + <button> + <pref-width>16</pref-width> + <pref-height>16</pref-height> + <legend></legend> + <default>1</default> + <keynum>27</keynum> + <border>2</border> + <binding> + <command>dialog-close</command> + </binding> + <binding> + <command>dialog-apply</command> + </binding> + </button> + </group> + + <hrule /> + + <group> + <layout>hbox</layout> + <default-padding>0</default-padding> + + <button> + <legend><<</legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/longitudinal/offset</property> + <step>-10</step> + </binding> + <binding> + <command>nasal</command> + <script>update("longitudinal")</script> + </binding> + </button> + + <button> + <legend><</legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/longitudinal/offset</property> + <step>-1</step> + </binding> + <binding> + <command>nasal</command> + <script>update("longitudinal")</script> + </binding> + </button> + + <slider> + <name>longitudinal</name> + <property>/sim/jetways/adjust/longitudinal/slider</property> + <legend>near/far</legend> + <pref-width>250</pref-width> + <live>1</live> + <min>-1</min> + <max>1</max> + + <color> + <red>1.0</red> + <green>0.6</green> + <blue>0.6</blue> + <alpha>1</alpha> + </color> + + <binding> + <command>dialog-apply</command> + <object-name>longitudinal</object-name> + </binding> + </slider> + + <button> + <legend>></legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/longitudinal/offset</property> + <step>1</step> + </binding> + <binding> + <command>nasal</command> + <script>update("longitudinal")</script> + </binding> + </button> + + <button> + <legend>>></legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/longitudinal/offset</property> + <step>10</step> + </binding> + <binding> + <command>nasal</command> + <script>update("longitudinal")</script> + </binding> + </button> + </group> + + <group> + <layout>hbox</layout> + <default-padding>0</default-padding> + + <button> + <legend><<</legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/transversal/offset</property> + <step>-10</step> + </binding> + <binding> + <command>nasal</command> + <script>update("transversal")</script> + </binding> + </button> + + <button> + <legend><</legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/transversal/offset</property> + <step>-1</step> + </binding> + <binding> + <command>nasal</command> + <script>update("transversal")</script> + </binding> + </button> + + <slider> + <name>transversal</name> + <property>/sim/jetways/adjust/transversal/slider</property> + <legend>left/right</legend> + <pref-width>250</pref-width> + <live>1</live> + <min>-1</min> + <max>1</max> + + <color> + <red>0.6</red> + <green>1.0</green> + <blue>0.6</blue> + <alpha>1.0</alpha> + </color> + + <binding> + <command>dialog-apply</command> + <object-name>transversal</object-name> + </binding> + </slider> + + <button> + <legend>></legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/transversal/offset</property> + <step>1</step> + </binding> + <binding> + <command>nasal</command> + <script>update("transversal")</script> + </binding> + </button> + + <button> + <legend>>></legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/transversal/offset</property> + <step>10</step> + </binding> + <binding> + <command>nasal</command> + <script>update("transversal")</script> + </binding> + </button> + </group> + + <group> + <layout>hbox</layout> + <default-padding>0</default-padding> + + <button> + <legend><<</legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/altitude/offset</property> + <step>-10</step> + </binding> + <binding> + <command>nasal</command> + <script>update("altitude")</script> + </binding> + </button> + + <button> + <legend><</legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/altitude/offset</property> + <step>-1</step> + </binding> + <binding> + <command>nasal</command> + <script>update("altitude")</script> + </binding> + </button> + + <slider> + <name>altitude</name> + <property>/sim/jetways/adjust/altitude/slider</property> + <legend>altitude</legend> + <pref-width>250</pref-width> + <live>1</live> + <min>-1</min> + <max>1</max> + + <color> + <red>0.6</red> + <green>0.6</green> + <blue>1.0</blue> + <alpha>1.0</alpha> + </color> + + <binding> + <command>dialog-apply</command> + <object-name>altitude</object-name> + </binding> + </slider> + + <button> + <legend>></legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/altitude/offset</property> + <step>1</step> + </binding> + <binding> + <command>nasal</command> + <script>update("altitude")</script> + </binding> + </button> + + <button> + <legend>>></legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/altitude/offset</property> + <step>10</step> + </binding> + <binding> + <command>nasal</command> + <script>update("altitude")</script> + </binding> + </button> + </group> + + <group> + <layout>hbox</layout> + <default-padding>0</default-padding> + + <button> + <legend><<</legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/heading/offset</property> + <step>-6</step> + </binding> + <binding> + <command>nasal</command> + <script>update("heading")</script> + </binding> + </button> + + <button> + <legend><</legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/heading/offset</property> + <step>-1</step> + </binding> + <binding> + <command>nasal</command> + <script>update("heading")</script> + </binding> + </button> + + <slider> + <name>heading</name> + <property>/sim/jetways/adjust/heading/slider</property> + <legend>heading</legend> + <pref-width>250</pref-width> + <live>1</live> + <min>-1</min> + <max>1</max> + + <color> + <red>1.0</red> + <green>1.0</green> + <blue>0.6</blue> + <alpha>1.0</alpha> + </color> + + <binding> + <command>dialog-apply</command> + <object-name>heading</object-name> + </binding> + </slider> + + <button> + <legend>></legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/heading/offset</property> + <step>1</step> + </binding> + <binding> + <command>nasal</command> + <script>update("heading")</script> + </binding> + </button> + + <button> + <legend>>></legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/heading/offset</property> + <step>6</step> + </binding> + <binding> + <command>nasal</command> + <script>update("heading")</script> + </binding> + </button> + </group> + + <hrule /> + + <group> + <layout>hbox</layout> + <default-padding>0</default-padding> + + <button> + <legend><<</legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/initial-extension/offset</property> + <step>-3</step> + </binding> + <binding> + <command>nasal</command> + <script>update("initial-extension")</script> + </binding> + </button> + + <button> + <legend><</legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/initial-extension/offset</property> + <step>-1</step> + </binding> + <binding> + <command>nasal</command> + <script>update("initial-extension")</script> + </binding> + </button> + + <slider> + <name>initial-extension</name> + <property>/sim/jetways/adjust/initial-extension/slider</property> + <legend>extension offset</legend> + <pref-width>250</pref-width> + <live>1</live> + <min>-1</min> + <max>1</max> + + <color> + <red>1.0</red> + <green>1.0</green> + <blue>1.0</blue> + <alpha>1.0</alpha> + </color> + + <binding> + <command>dialog-apply</command> + <object-name>initial-extension</object-name> + </binding> + </slider> + + <button> + <legend>></legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/initial-extension/offset</property> + <step>1</step> + </binding> + <binding> + <command>nasal</command> + <script>update("initial-extension")</script> + </binding> + </button> + + <button> + <legend>>></legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/initial-extension/offset</property> + <step>3</step> + </binding> + <binding> + <command>nasal</command> + <script>update("initial-extension")</script> + </binding> + </button> + </group> + + <group> + <layout>hbox</layout> + <default-padding>0</default-padding> + + <button> + <legend><<</legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/initial-pitch/offset</property> + <step>-2</step> + </binding> + <binding> + <command>nasal</command> + <script>update("initial-pitch")</script> + </binding> + </button> + + <button> + <legend><</legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/initial-pitch/offset</property> + <step>-1</step> + </binding> + <binding> + <command>nasal</command> + <script>update("initial-pitch")</script> + </binding> + </button> + + <slider> + <name>initial-pitch</name> + <property>/sim/jetways/adjust/initial-pitch/slider</property> + <legend>pitch offset</legend> + <pref-width>250</pref-width> + <live>1</live> + <min>-1</min> + <max>1</max> + + <color> + <red>0.8</red> + <green>0.8</green> + <blue>0.8</blue> + <alpha>0.8</alpha> + </color> + + <binding> + <command>dialog-apply</command> + <object-name>initial-pitch</object-name> + </binding> + </slider> + + <button> + <legend>></legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/initial-pitch/offset</property> + <step>1</step> + </binding> + <binding> + <command>nasal</command> + <script>update("initial-pitch")</script> + </binding> + </button> + + <button> + <legend>>></legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/initial-pitch/offset</property> + <step>2</step> + </binding> + <binding> + <command>nasal</command> + <script>update("initial-pitch")</script> + </binding> + </button> + </group> + + <group> + <layout>hbox</layout> + <default-padding>0</default-padding> + + <button> + <legend><<</legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/initial-heading/offset</property> + <step>-2</step> + </binding> + <binding> + <command>nasal</command> + <script>update("initial-heading")</script> + </binding> + </button> + + <button> + <legend><</legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/initial-heading/offset</property> + <step>-1</step> + </binding> + <binding> + <command>nasal</command> + <script>update("initial-heading")</script> + </binding> + </button> + + <slider> + <name>initial-heading</name> + <property>/sim/jetways/adjust/initial-heading/slider</property> + <legend>rotation offset</legend> + <pref-width>250</pref-width> + <live>1</live> + <min>-1</min> + <max>1</max> + + <color> + <red>0.6</red> + <green>0.6</green> + <blue>0.6</blue> + <alpha>0.6</alpha> + </color> + + <binding> + <command>dialog-apply</command> + <object-name>initial-heading</object-name> + </binding> + </slider> + + <button> + <legend>></legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/initial-heading/offset</property> + <step>1</step> + </binding> + <binding> + <command>nasal</command> + <script>update("initial-heading")</script> + </binding> + </button> + + <button> + <legend>>></legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/initial-heading/offset</property> + <step>2</step> + </binding> + <binding> + <command>nasal</command> + <script>update("initial-heading")</script> + </binding> + </button> + </group> + + <group> + <layout>hbox</layout> + <default-padding>0</default-padding> + + <button> + <legend><<</legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/initial-entrance-heading/offset</property> + <step>-6</step> + </binding> + <binding> + <command>nasal</command> + <script>update("initial-entrance-heading")</script> + </binding> + </button> + + <button> + <legend><</legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/initial-entrance-heading/offset</property> + <step>-1</step> + </binding> + <binding> + <command>nasal</command> + <script>update("initial-entrance-heading")</script> + </binding> + </button> + + <slider> + <name>initial-entrance-heading</name> + <property>/sim/jetways/adjust/initial-entrance-heading/slider</property> + <legend>entrance rotation offset</legend> + <pref-width>250</pref-width> + <live>1</live> + <min>-1</min> + <max>1</max> + + <color> + <red>0.4</red> + <green>0.4</green> + <blue>0.4</blue> + <alpha>0.4</alpha> + </color> + + <binding> + <command>dialog-apply</command> + <object-name>initial-entrance-heading</object-name> + </binding> + </slider> + + <button> + <legend>></legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/initial-entrance-heading/offset</property> + <step>1</step> + </binding> + <binding> + <command>nasal</command> + <script>update("initial-entrance-heading")</script> + </binding> + </button> + + <button> + <legend>>></legend> + <pref-width>22</pref-width> + <pref-height>22</pref-height> + + <binding> + <command>property-adjust</command> + <property>/sim/jetways/adjust/initial-entrance-heading/offset</property> + <step>6</step> + </binding> + <binding> + <command>nasal</command> + <script>update("initial-entrance-heading")</script> + </binding> + </button> + </group> + + <group> + <layout>table</layout> + <text> + <row>0</row> + <col>0</col> + <halign>right</halign> + <label>Model:</label> + </text> + <combo> + <name>model</name> + <row>0</row> + <col>1</col> + <property>/sim/jetways/adjust/model</property> + <live type="bool">true</live> + <binding> + <command>dialog-apply</command> + <object-name>model</object-name> + </binding> + </combo> + <text> + <row>1</row> + <col>0</col> + <halign>right</halign> + <label>Door:</label> + </text> + <combo> + <name>door</name> + <row>1</row> + <col>1</col> + <property>/sim/jetways/adjust/door</property> + <live type="bool">true</live> + <value>0</value> + <value>1</value> + <value>2</value> + <binding> + <command>dialog-apply</command> + <object-name>door</object-name> + </binding> + </combo> + <text> + <row>0</row> + <col>2</col> + <halign>right</halign> + <label>Airline sign:</label> + </text> + <combo> + <name>airline</name> + <row>0</row> + <col>3</col> + <property>/sim/jetways/adjust/airline</property> + <live type="bool">true</live> + <binding> + <command>dialog-apply</command> + <object-name>airline</object-name> + </binding> + </combo> + <text> + <row>1</row> + <col>2</col> + <halign>right</halign> + <label>Gate:</label> + </text> + <input> + <name>gate</name> + <row>1</row> + <col>3</col> + <property>/sim/jetways/adjust/gate</property> + <live type="bool">true</live> + <binding> + <command>dialog-apply</command> + <object-name>gate</object-name> + </binding> + </input> + </group> + + <hrule /> + + <group> + <layout>hbox</layout> + <button> + <pref-height>24</pref-height> + <legend>Center sliders</legend> + <binding> + <command>nasal</command> + <script> + center_sliders(); + </script> + </binding> + </button> + <button> + <pref-height>24</pref-height> + <legend>Export</legend> + <border>2</border> + <binding> + <command>nasal</command> + <script> + jetways_edit.export(); + </script> + </binding> + </button> + <button> + <pref-height>24</pref-height> + <legend>STG converter</legend> + <border>2</border> + <binding> + <command>nasal</command> + <script> + jetways_edit.convert_stg(); + </script> + </binding> + </button> + <button> + <pref-height>24</pref-height> + <legend>?</legend> + <border>2</border> + <binding> + <command>nasal</command> + <script> + jetways_edit.print_help(); + </script> + </binding> + </button> + </group> + +</PropertyList> diff --git a/gui/dialogs/jetways.xml b/gui/dialogs/jetways.xml new file mode 100644 index 000000000..8215178fa --- /dev/null +++ b/gui/dialogs/jetways.xml @@ -0,0 +1,131 @@ +<?xml version="1.0" ?> + +<PropertyList> + + <name>jetways</name> + <modal>false</modal> + <layout>vbox</layout> + + <nasal> + <open><![CDATA[ + var self = cmdarg(); + var dlgname = self.getNode("name").getValue(); + var aptlist = props.globals.getNode(self.getNode("text/property").getValue(), 1); + var UPDATE_PERIOD = 1; + var update = func + { + var list = ""; + foreach (var apt; jetways.loaded_airports) + { + list ~= apt ~ " "; + } + aptlist.setValue(list == "" ? "No airports loaded" : "Loaded airports: " ~ list); + settimer(update, UPDATE_PERIOD); + }; + settimer(update, 0); + ]]></open> + </nasal> + + <group> + <layout>hbox</layout> + <empty><stretch>1</stretch></empty> + + <text> + <label>Jetway Settings</label> + </text> + + <empty><stretch>1</stretch></empty> + + <button> + <pref-width>16</pref-width> + <pref-height>16</pref-height> + <legend></legend> + <default>1</default> + <keynum>27</keynum> + <border>2</border> + <binding> + <command>dialog-close</command> + </binding> + <binding> + <command>dialog-apply</command> + </binding> + </button> + </group> + + <hrule /> + + <checkbox> + <halign>left</halign> + <label>Enable animated jetways</label> + <property>/sim/jetways/enabled</property> + <live type="bool">true</live> + <binding> + <command>dialog-apply</command> + </binding> + </checkbox> + <checkbox> + <halign>left</halign> + <label>Connect to multiplayer aircraft</label> + <property>/sim/jetways/interact-with-multiplay</property> + <live type="bool">true</live> + <enable> + <property>/sim/jetways/enabled</property> + </enable> + <binding> + <command>dialog-apply</command> + </binding> + </checkbox> + <checkbox> + <halign>left</halign> + <label>Enable jetway editor</label> + <property>/sim/jetways/enable-editor</property> + <live type="bool">true</live> + <enable> + <property>/sim/jetways/enabled</property> + </enable> + <binding> + <command>dialog-apply</command> + </binding> + </checkbox> + <checkbox> + <halign>left</halign> + <label>Debug mode</label> + <property>/sim/jetways/debug</property> + <live type="bool">true</live> + <enable> + <property>/sim/jetways/enabled</property> + </enable> + <binding> + <command>dialog-apply</command> + </binding> + </checkbox> + + <group> + <layout>hbox</layout> + <button> + <legend>Open editor</legend> + <enable> + <and> + <property>/sim/jetways/enabled</property> + <property>/sim/jetways/enable-editor</property> + </and> + </enable> + <binding> + <command>nasal</command> + <script> + jetways_edit.dialog(); + </script> + </binding> + </button> + </group> + + <hrule /> + + <text> + <halign>left</halign> + <label>No airports loaded</label> + <property>/sim/gui/dialogs/jetways/dialog/loaded-airports</property> + <live type="bool">true</live> + </text> + +</PropertyList> diff --git a/gui/dialogs/rendering.xml b/gui/dialogs/rendering.xml index fe6197cb1..e64e98e79 100644 --- a/gui/dialogs/rendering.xml +++ b/gui/dialogs/rendering.xml @@ -207,6 +207,17 @@ </binding> </checkbox> + <checkbox> + <halign>left</halign> + <label>Animated jetways</label> + <name>jetways</name> + <property>/sim/jetways/enabled</property> + <binding> + <command>dialog-apply</command> + <object-name>jetways</object-name> + </binding> + </checkbox> + <group> <layout>hbox</layout> <text> diff --git a/gui/menubar.xml b/gui/menubar.xml index 753304b63..15381a9f3 100644 --- a/gui/menubar.xml +++ b/gui/menubar.xml @@ -367,6 +367,14 @@ </binding> </item> + <item> + <label>Jetway Settings</label> + <binding> + <command>nasal</command> + <script>jetways.dialog()</script> + </binding> + </item> + </menu> <menu> diff --git a/preferences.xml b/preferences.xml index a4938454b..cc5f58aa9 100644 --- a/preferences.xml +++ b/preferences.xml @@ -746,6 +746,14 @@ Started September 2000 by David Megginson, david@megginson.com archive="n">100.0</min-time-ms> </timing-statistics> + <!-- animated jetway options --> + <jetways> + <enabled type="bool" archive="y">false</enabled> <!-- disabled due to FPS impact --> + <enable-editor type="bool" archive="y">false</enable-editor> + <interact-with-multiplay type="bool" archive="y">true</interact-with-multiplay> + <debug type="bool" archive="y">false</debug> + </jetways> + </sim>