############################################################################### ## $Id: copilot-dual-control.nas,v 1.29 2007-11-16 20:29:09 anders Exp $ ## ## Nasal for copilot for dual control over the multiplayer network. ## ## Copyright (C) 2007 Anders Gidenstam (anders(at)gidenstam.org) ## This file is licensed under the GPL license. ## ############################################################################### # Renaming (almost :) var DCT = dual_control_tools; var ADC = aircraft_dual_control; # Properties for position and orientation of local aircraft. var l_lat = "/position/latitude-deg"; var l_lon = "/position/longitude-deg"; var l_alt = "/position/altitude-ft"; var l_heading = "/orientation/heading-deg"; var l_pitch = "/orientation/pitch-deg"; var l_roll = "/orientation/roll-deg"; # Replicate remote state. var r_airspeed = "velocities/true-airspeed-kt"; var l_airspeed = "/velocities/airspeed-kt"; var vertspeed = "velocities/vertical-speed-fps"; ###################################################################### # Connect to new pilot var in_data = 0; var out_data = 0; var connect = func (pilot) { # Set view eye paths. var view_cfg = "/sim/view[" ~ view.indexof("Cockpit View")~ "]/config"; setprop(view_cfg ~ "/eye-lat-deg-path", pilot.getNode(DCT.lat_mpp).getPath()); setprop(view_cfg ~ "/eye-lon-deg-path", pilot.getNode(DCT.lon_mpp).getPath()); setprop(view_cfg ~ "/eye-alt-ft-path", pilot.getNode(DCT.alt_mpp).getPath()); setprop(view_cfg ~ "/eye-heading-deg-path", pilot.getNode(DCT.heading_mpp).getPath()); setprop(view_cfg ~ "/eye-pitch-deg-path", pilot.getNode(DCT.pitch_mpp).getPath()); setprop(view_cfg ~ "/eye-roll-deg-path", pilot.getNode(DCT.roll_mpp).getPath()); view_cfg = "/sim/view[" ~ view.indexof("Helicopter View")~ "]/config"; setprop(view_cfg ~ "/eye-lat-deg-path", pilot.getNode(DCT.lat_mpp).getPath()); setprop(view_cfg ~ "/eye-lon-deg-path", pilot.getNode(DCT.lon_mpp).getPath()); setprop(view_cfg ~ "/eye-alt-ft-path", pilot.getNode(DCT.alt_mpp).getPath()); setprop(view_cfg ~ "/eye-heading-deg-path", pilot.getNode(DCT.heading_mpp).getPath()); setprop(view_cfg ~ "/at_model", 0); # Tweak MP/AI filters pilot.getNode("controls/allow-extrapolation").setBoolValue(1); pilot.getNode("controls/lag-adjust-system-speed").setValue(5.0); # Set up property mappings. in_data = [ # Map /postition/* DCT.Translator.new (pilot.getNode(DCT.lat_mpp), props.globals.getNode(l_lat)), DCT.Translator.new (pilot.getNode(DCT.lon_mpp), props.globals.getNode(l_lon)), DCT.Translator.new (pilot.getNode(DCT.alt_mpp), props.globals.getNode(l_alt)), # Map /orientation/* DCT.Translator.new (pilot.getNode(DCT.heading_mpp), props.globals.getNode(l_heading)), DCT.Translator.new (pilot.getNode(DCT.pitch_mpp), props.globals.getNode(l_pitch)), DCT.Translator.new (pilot.getNode(DCT.roll_mpp), props.globals.getNode(l_roll)), # Map /velocities/* DCT.Translator.new (pilot.getNode(r_airspeed), props.globals.getNode(l_airspeed)), DCT.Translator.new (pilot.getNode(vertspeed), props.globals.getNode(vertspeed)), ] ~ ADC.copilot_in_data(pilot); out_data = ADC.copilot_out_data(pilot); print("Dual control ... connected to pilot."); setprop("/sim/messages/copilot", "Welcome onboard."); } ###################################################################### # Main loop singleton class. var main_loop = { init : func { me.loopid = 0; me.active = 0; setlistener("/ai/models/model-added", me.activate); print("Copilot dual control ... initialized"); settimer(me.activate, 5); }, reset : func { me.loopid += 1; me._loop_(me.loopid); }, activate : func { if (!me.active) { me.reset(); } }, update : func { var mpplayers = props.globals.getNode("/ai/models").getChildren("multiplayer"); var r_callsign = getprop("/sim/remote/pilot-callsign"); foreach (pilot; mpplayers) { if ((pilot.getChild("valid").getValue()) and (pilot.getChild("callsign") != nil) and (pilot.getChild("callsign").getValue() == r_callsign)) { if (me.active == 0) { me.active = 1; connect(pilot); } # Mess with the MP filters. Highly experimental. if (pilot.getNode("controls/lag-time-offset") != nil) { var v = pilot.getNode("controls/lag-time-offset").getValue(); #pilot.getNode("controls/lag-time-offset").setValue(0.99 * v); } foreach (w; in_data) { w.update(); } foreach (w; out_data) { w.update(); } return; } } # The pilot player is not around. Idle loop. me.active = 0; me.loopid += 1; print("Dual control ... disconnected from pilot."); }, _loop_ : func(id) { id == me.loopid or return; me.update(); settimer(func { me._loop_(id); }, 0); } }; ############################################################################### # Control wrapper overrides. # Flap control input is -1 for step decrease; 1 for step increase; 0 idle controls.flapsDown = func { var val = arg[0]; if(val > 1) { val = 1 } elsif(val < -1) { val = -1 } setprop(DCT.copilot_flap_mpp, val); } # Brake control controls.applyBrakes = func(v, which = 0) { if (which <= 0) { setprop(DCT.copilot_lbrake_mpp, v); } if (which >= 0) { setprop(DCT.copilot_rbrake_mpp, v); } } # FIXME: Odd. controls.applyParkingBrake = func(v) { if (!v) { return; } setprop(DCT.copilot_lbrake_mpp, -v); return 0; } ############################################################################### # Initialization. var last_view = 0; setlistener("/sim/signals/fdm-initialized", func { # setlistener("/sim/current-view/view-number", func { # var vn = getprop("/sim/current-view/view-number"); # if (vn == 0) { # print("View change to view 0"); # if (last_view != 1) { # last_view = vn; # setprop("/sim/current-view/view-number", view.indexof("Chase View")); # print("View skip"); # } else { # last_view = vn; # setprop("/sim/current-view/view-number", view.indexof("Copilot View")); # } # } # }); main_loop.init(); });