Multiplayer: Added passinger
This commit is contained in:
parent
f884138a1a
commit
f8cbebf797
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--####################################################################
|
||||
Lake of Constance Hangar
|
||||
Boeing 707 for Flightgear
|
||||
Copyright (C) 2013 M.Kraus
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Every software has a developer, also free software.
|
||||
As a gesture of courtesy and respect, I would be delighted
|
||||
if you contacted me before making any changes to this software.
|
||||
<info (at) marc-kraus.de> April, 2017
|
||||
########################################################################-->
|
||||
<PropertyList>
|
||||
|
||||
<path>707-pax.ac</path>
|
||||
|
||||
|
||||
</PropertyList>
|
|
@ -0,0 +1,3 @@
|
|||
AC3Db
|
||||
OBJECT poly
|
||||
kids 0
|
|
@ -0,0 +1,258 @@
|
|||
###############################################################################
|
||||
## $Id$
|
||||
##
|
||||
## Nasal for copilot for dual control over the multiplayer network.
|
||||
##
|
||||
## Copyright (C) 2007 - 2010 Anders Gidenstam (anders(at)gidenstam.org)
|
||||
## This file is licensed under the GPL license version 2 or later.
|
||||
##
|
||||
###############################################################################
|
||||
# Renaming (almost :)
|
||||
var DCT = dual_control_tools;
|
||||
var ADC = aircraft_dual_control;
|
||||
# NOTE: By loading the aircraft specific dual control module
|
||||
# as <aircraft_dual_control> this file is generic.
|
||||
# The aircraft specific modul must set the variables
|
||||
# pilot_type and copilot_type to the name (with full path) of
|
||||
# main 3d model XML for the pilot and copilot aircraft.
|
||||
# This module should be loades under the name dual_control.
|
||||
|
||||
# Allow aircraft to override the copilot view name. Deprecated.
|
||||
if (!contains(ADC, "copilot_view")) {
|
||||
ADC.copilot_view = "Copilot View";
|
||||
}
|
||||
|
||||
# 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";
|
||||
|
||||
# Default external views to slave to the MP pilot.
|
||||
var views = {};
|
||||
views["Helicopter View"] = 2;
|
||||
views["Chase View"] = 3;
|
||||
views["Tower View"] = 0;
|
||||
views["Fly-By View"] = 1;
|
||||
views["Chase View Without Yaw"] = 1;
|
||||
|
||||
######################################################################
|
||||
# Connect to new pilot
|
||||
var process_data = 0;
|
||||
|
||||
var connect = func (pilot) {
|
||||
# Set external view eye and target paths.
|
||||
foreach (var vn; keys(views)) {
|
||||
var view_cfg = "sim/view[" ~ view.indexof(vn) ~ "]/config/";
|
||||
setprop(view_cfg ~ "at-model", 0);
|
||||
|
||||
if (views[vn] > 0) {
|
||||
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());
|
||||
}
|
||||
if (views[vn] > 1) {
|
||||
setprop(view_cfg ~ "eye-heading-deg-path",
|
||||
pilot.getNode(DCT.heading_mpp).getPath());
|
||||
}
|
||||
if (views[vn] > 2) {
|
||||
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());
|
||||
}
|
||||
|
||||
setprop(view_cfg ~ "target-lat-deg-path",
|
||||
pilot.getNode(DCT.lat_mpp).getPath());
|
||||
setprop(view_cfg ~ "target-lon-deg-path",
|
||||
pilot.getNode(DCT.lon_mpp).getPath());
|
||||
setprop(view_cfg ~ "target-alt-ft-path",
|
||||
pilot.getNode(DCT.alt_mpp).getPath());
|
||||
setprop(view_cfg ~ "target-heading-deg-path",
|
||||
pilot.getNode(DCT.heading_mpp).getPath());
|
||||
setprop(view_cfg ~ "target-pitch-deg-path",
|
||||
pilot.getNode(DCT.pitch_mpp).getPath());
|
||||
setprop(view_cfg ~ "target-roll-deg-path",
|
||||
pilot.getNode(DCT.roll_mpp).getPath());
|
||||
}
|
||||
|
||||
# Tweak MP/AI filters
|
||||
pilot.getNode("controls/allow-extrapolation").setBoolValue(1);
|
||||
pilot.getNode("controls/lag-adjust-system-speed").setValue(5.0);
|
||||
|
||||
# Set up property aliases
|
||||
|
||||
# Set up property mappings.
|
||||
process_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_connect_pilot(pilot);
|
||||
|
||||
print("Dual control ... connected to pilot.");
|
||||
setprop("sim/messages/copilot", "Welcome aboard.");
|
||||
}
|
||||
|
||||
var disconnect = func {
|
||||
# Reset external view eye and target paths.
|
||||
foreach (var vn; keys(views)) {
|
||||
var view_cfg = "sim/view[" ~ view.indexof(vn) ~ "]/config";
|
||||
|
||||
if (views[vn] > 0) {
|
||||
setprop(view_cfg ~ "eye-lat-deg-path",
|
||||
"position/latitude-deg");
|
||||
setprop(view_cfg ~ "eye-lon-deg-path",
|
||||
"position/longitude-deg");
|
||||
setprop(view_cfg ~ "eye-alt-ft-path",
|
||||
"position/altitude-ft");
|
||||
}
|
||||
if (views[vn] > 1) {
|
||||
setprop(view_cfg ~ "eye-heading-deg-path",
|
||||
"orientation/heading-deg");
|
||||
}
|
||||
if (views[vn] > 2) {
|
||||
setprop(view_cfg ~ "eye-pitch-deg-path",
|
||||
"orientation/pitch-deg");
|
||||
setprop(view_cfg ~ "eye-roll-deg-path",
|
||||
"orientation/roll-deg");
|
||||
}
|
||||
setprop(view_cfg ~ "target-lat-deg-path",
|
||||
"sim/viewer/target/latitude-deg");
|
||||
setprop(view_cfg ~ "target-lon-deg-path",
|
||||
"sim/viewer/target/longitude-deg");
|
||||
setprop(view_cfg ~ "target-alt-ft-path",
|
||||
"sim/viewer/target/altitude-ft");
|
||||
setprop(view_cfg ~ "target-heading-deg-path",
|
||||
"sim/viewer/target/heading-deg");
|
||||
setprop(view_cfg ~ "target-pitch-deg-path",
|
||||
"sim/viewer/target/pitch-deg");
|
||||
setprop(view_cfg ~ "target-roll-deg-path",
|
||||
"sim/viewer/target/roll-deg");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
######################################################################
|
||||
# Main loop singleton class.
|
||||
var main = {
|
||||
init : func {
|
||||
me.loopid = 0;
|
||||
me.active = 0;
|
||||
setlistener("ai/models/model-added", func {
|
||||
settimer(func { me.activate(); }, 2);
|
||||
});
|
||||
print("Copilot dual control ... initialized");
|
||||
settimer(func { me.activate(); }, 5);
|
||||
},
|
||||
reset : func {
|
||||
if (me.active) {
|
||||
print("Dual control ... disconnected from pilot.");
|
||||
disconnect();
|
||||
ADC.copilot_disconnect_pilot();
|
||||
}
|
||||
me.active = 0;
|
||||
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 (var pilot; mpplayers) {
|
||||
if ((pilot.getChild("valid").getValue()) and
|
||||
(pilot.getChild("callsign") != nil) and
|
||||
(pilot.getChild("callsign").getValue() == r_callsign)) {
|
||||
|
||||
if (me.active == 0) {
|
||||
# Note: sim/model/path contains the model XML file.
|
||||
if ((pilot.getNode("sim/model/path") != nil) and
|
||||
(pilot.getNode("sim/model/path").getValue() ==
|
||||
ADC.pilot_type)) {
|
||||
me.active = 1;
|
||||
connect(pilot);
|
||||
} else {
|
||||
print("Dual control ... pilot rejected - wrong aircraft type.");
|
||||
me.loopid += 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
# 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 (var w; process_data) {
|
||||
w.update();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
# The pilot player is not around. Idle loop.
|
||||
if (me.active) {
|
||||
print("Dual control ... disconnected from pilot.");
|
||||
disconnect();
|
||||
ADC.copilot_disconnect_pilot();
|
||||
}
|
||||
me.active = 0;
|
||||
me.loopid += 1;
|
||||
},
|
||||
_loop_ : func(id) {
|
||||
id == me.loopid or return;
|
||||
me.update();
|
||||
settimer(func { me._loop_(id); }, 0);
|
||||
}
|
||||
};
|
||||
|
||||
###############################################################################
|
||||
# Initialization.
|
||||
|
||||
var last_view = 0;
|
||||
|
||||
setlistener("sim/signals/fdm-initialized", func {
|
||||
main.init();
|
||||
});
|
||||
|
|
@ -0,0 +1,643 @@
|
|||
###############################################################################
|
||||
##
|
||||
## Nasal module for dual control over the multiplayer network.
|
||||
##
|
||||
## Copyright (C) 2007 - 2010 Anders Gidenstam (anders(at)gidenstam.org)
|
||||
## This file is licensed under the GPL license version 2 or later.
|
||||
##
|
||||
###############################################################################
|
||||
## MP properties
|
||||
var lat_mpp = "position/latitude-deg";
|
||||
var lon_mpp = "position/longitude-deg";
|
||||
var alt_mpp = "position/altitude-ft";
|
||||
var heading_mpp = "orientation/true-heading-deg";
|
||||
var pitch_mpp = "orientation/pitch-deg";
|
||||
var roll_mpp = "orientation/roll-deg";
|
||||
|
||||
# Import components from the mp_broadcast module.
|
||||
var Binary = mp_broadcast.Binary;
|
||||
var MessageChannel = mp_broadcast.MessageChannel;
|
||||
|
||||
###############################################################################
|
||||
# Utility classes
|
||||
|
||||
############################################################
|
||||
# Translate a property into another.
|
||||
# Factor and offsets are only used for numeric values.
|
||||
# src - source : property node
|
||||
# dest - destination : property node
|
||||
# factor - : double
|
||||
# offset - : double
|
||||
var Translator = {};
|
||||
Translator.new = func (src = nil, dest = nil, factor = 1, offset = 0) {
|
||||
var obj = { parents : [Translator],
|
||||
src : src,
|
||||
dest : dest,
|
||||
factor : factor,
|
||||
offset : offset };
|
||||
if (obj.src == nil or obj.dest == nil) {
|
||||
print("Translator[");
|
||||
print(" ", debug.string(obj.src));
|
||||
print(" ", debug.string(obj.dest));
|
||||
print("]");
|
||||
fail();
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
Translator.update = func () {
|
||||
var v = me.src.getValue();
|
||||
if (is_num(v)) {
|
||||
me.dest.setValue(me.factor * v + me.offset);
|
||||
} else {
|
||||
if (typeof(v) == "scalar")
|
||||
me.dest.setValue(v);
|
||||
}
|
||||
}
|
||||
|
||||
############################################################
|
||||
# Detects flanks on two insignals encoded in a property.
|
||||
# - positive signal up/down flank
|
||||
# - negative signal up/down flank
|
||||
# n - source : property node
|
||||
# on_positive_flank - action : func (v)
|
||||
# on_negative_flank - action : func (v)
|
||||
var EdgeTrigger = {};
|
||||
EdgeTrigger.new = func (n, on_positive_flank, on_negative_flank) {
|
||||
var obj = { parents : [EdgeTrigger],
|
||||
old : 0,
|
||||
node : n,
|
||||
pos_flank : on_positive_flank,
|
||||
neg_flank : on_negative_flank };
|
||||
if (obj.node == nil) {
|
||||
print("EdgeTrigger[");
|
||||
print(" ", debug.string(obj.node));
|
||||
print("]");
|
||||
fail();
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
EdgeTrigger.update = func {
|
||||
# NOTE: float MP properties get interpolated.
|
||||
# This detector relies on that steady state is reached between
|
||||
# flanks.
|
||||
var val = me.node.getValue();
|
||||
if (!is_num(val)) return;
|
||||
if (me.old == 1) {
|
||||
if (val < me.old) {
|
||||
me.pos_flank(0);
|
||||
}
|
||||
} elsif (me.old == 0) {
|
||||
if (val > me.old) {
|
||||
me.pos_flank(1);
|
||||
} elsif (val < me.old) {
|
||||
me.neg_flank(1);
|
||||
}
|
||||
} elsif (me.old == -1) {
|
||||
if (val > me.old) {
|
||||
me.neg_flank(0);
|
||||
}
|
||||
}
|
||||
me.old = val;
|
||||
}
|
||||
|
||||
############################################################
|
||||
# StableTrigger: Triggers an action when a MPP property
|
||||
# becomes stable (i.e. doesn't change for
|
||||
# MIN_STABLE seconds).
|
||||
# src - MP prop : property node
|
||||
# action - action to take when the value becomes stable : [func(v)]
|
||||
# An action is triggered when value has stabilized.
|
||||
var StableTrigger = {};
|
||||
StableTrigger.new = func (src, action) {
|
||||
var obj = { parents : [StableTrigger],
|
||||
src : src,
|
||||
action : action,
|
||||
old : 0,
|
||||
stable_since : 0,
|
||||
wait : 0,
|
||||
MIN_STABLE : 0.01 };
|
||||
# Error checking.
|
||||
var bad = (obj.src == nil) or (action = nil);
|
||||
|
||||
if (bad) {
|
||||
print("StableTrigger[");
|
||||
print(" ", debug.string(obj.src));
|
||||
print(" ", debug.string(obj.action));
|
||||
print("]");
|
||||
fail();
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
StableTrigger.update = func () {
|
||||
var v = me.src.getValue();
|
||||
if (!is_num(v)) return;
|
||||
var t = getprop("/sim/time/elapsed-sec"); # NOTE: simulated time.
|
||||
|
||||
if ((me.old == v) and
|
||||
((t - me.stable_since) > me.MIN_STABLE) and (me.wait == 1)) {
|
||||
# Trigger action.
|
||||
me.action(v);
|
||||
|
||||
me.wait = 0;
|
||||
} elsif (me.old == v) {
|
||||
# Wait. This is either before the signal is stable or after the action.
|
||||
} else {
|
||||
me.stable_since = t;
|
||||
me.wait = 1;
|
||||
me.old = me.src.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
############################################################
|
||||
# Selects the most recent value of two properties.
|
||||
# src1 - : property node
|
||||
# src2 - : property node
|
||||
# dest - : property node
|
||||
# threshold - : double
|
||||
var MostRecentSelector = {};
|
||||
MostRecentSelector.new = func (src1, src2, dest, threshold) {
|
||||
var obj = { parents : [MostRecentSelector],
|
||||
old1 : 0,
|
||||
old2 : 0,
|
||||
src1 : src1,
|
||||
src2 : src2,
|
||||
dest : dest,
|
||||
thres : threshold };
|
||||
if (obj.src1 == nil or obj.src2 == nil or obj.dest == nil) {
|
||||
print("MostRecentSelector[");
|
||||
print(" ", debug.string(obj.src1));
|
||||
print(" ", debug.string(obj.src2));
|
||||
print(" ", debug.string(obj.dest));
|
||||
print("]");
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
MostRecentSelector.update = func {
|
||||
var v1 = me.src1.getValue();
|
||||
var v2 = me.src2.getValue();
|
||||
if (!is_num(v1) and !is_num(v2)) return;
|
||||
elsif (!is_num(v1)) me.dest.setValue(v2);
|
||||
elsif (!is_num(v2)) me.dest.setValue(v1);
|
||||
else {
|
||||
if (abs (v2 - me.old2) > me.thres) {
|
||||
me.old2 = v2;
|
||||
me.dest.setValue(me.old2);
|
||||
}
|
||||
if (abs (v1 - me.old1) > me.thres) {
|
||||
me.old1 = v1;
|
||||
me.dest.setValue(me.old1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
############################################################
|
||||
# Adds two input properties.
|
||||
# src1 - : property node
|
||||
# src2 - : property node
|
||||
# dest - : property node
|
||||
var Adder = {};
|
||||
Adder.new = func (src1, src2, dest) {
|
||||
var obj = { parents : [DeltaAccumulator],
|
||||
src1 : src1,
|
||||
src2 : src2,
|
||||
dest : dest };
|
||||
if (obj.src1 == nil or obj.src2 == nil or obj.dest == nil) {
|
||||
print("Adder[");
|
||||
print(" ", debug.string(obj.src1));
|
||||
print(" ", debug.string(obj.src2));
|
||||
print(" ", debug.string(obj.dest));
|
||||
print("]");
|
||||
fail();
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
Adder.update = func () {
|
||||
var v1 = me.src1.getValue();
|
||||
var v2 = me.src2.getValue();
|
||||
if (!is_num(v1) or !is_num(v2)) return;
|
||||
me.dest.setValue(v1 + v2);
|
||||
}
|
||||
|
||||
############################################################
|
||||
# Adds the delta of src to dest.
|
||||
# src - : property node
|
||||
# dest - : property node
|
||||
var DeltaAdder = {};
|
||||
DeltaAdder.new = func (src, dest) {
|
||||
var obj = { parents : [DeltaAdder],
|
||||
old : 0,
|
||||
src : src,
|
||||
dest : dest };
|
||||
if (obj.src == nil or obj.dest == nil) {
|
||||
print("DeltaAdder[", debug.string(obj.src), ", ",
|
||||
debug.string(obj.dest), "]");
|
||||
fail();
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
DeltaAdder.update = func () {
|
||||
var v = me.src.getValue();
|
||||
if (!is_num(v)) return;
|
||||
me.dest.setValue((v - me.old) + me.dest.getValue());
|
||||
me.old = v;
|
||||
}
|
||||
|
||||
############################################################
|
||||
# Switch encoder: Encodes upto 32 boolean properties in one
|
||||
# int property.
|
||||
# inputs - list of property nodes
|
||||
# dest - where the bitmask is stored : property node
|
||||
var SwitchEncoder = {};
|
||||
SwitchEncoder.new = func (inputs, dest) {
|
||||
var obj = { parents : [SwitchEncoder],
|
||||
inputs : inputs,
|
||||
dest : dest };
|
||||
# Error checking.
|
||||
var bad = (obj.dest == nil);
|
||||
foreach (var i; inputs) {
|
||||
if (i == nil) { bad = 1; }
|
||||
}
|
||||
|
||||
if (bad) {
|
||||
print("SwitchEncoder[");
|
||||
foreach (var i; inputs) {
|
||||
print(" ", debug.string(i));
|
||||
}
|
||||
print(" ", debug.string(obj.dest));
|
||||
print("]");
|
||||
fail();
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
SwitchEncoder.update = func () {
|
||||
var v = 0;
|
||||
var b = 1;
|
||||
forindex (var i; me.inputs) {
|
||||
if (me.inputs[i].getBoolValue()) {
|
||||
v = v + b;
|
||||
}
|
||||
b *= 2;
|
||||
}
|
||||
me.dest.setIntValue(v);
|
||||
}
|
||||
|
||||
############################################################
|
||||
# Switch decoder: Decodes a bitmask in an int property.
|
||||
# src - : property node
|
||||
# actions - list of actions : [func(b)]
|
||||
# Actions are triggered when their input bit change.
|
||||
# Due to interpolation the decoder needs to wait for a
|
||||
# stable input value.
|
||||
var SwitchDecoder = {};
|
||||
SwitchDecoder.new = func (src, actions) {
|
||||
var obj = { parents : [SwitchDecoder],
|
||||
wait : 0,
|
||||
old : 0,
|
||||
old_stable : 0,
|
||||
stable_since : 0,
|
||||
reset : 1,
|
||||
src : src,
|
||||
actions : actions,
|
||||
MIN_STABLE : 0.1 };
|
||||
# Error checking.
|
||||
var bad = (obj.src == nil);
|
||||
foreach (var a; obj.actions) {
|
||||
if (a == nil) { bad = 1; }
|
||||
}
|
||||
|
||||
if (bad) {
|
||||
print("SwitchDecoder[");
|
||||
print(" ", debug.string(obj.src));
|
||||
foreach (var a; obj.actions) {
|
||||
print(" ", debug.string(a));
|
||||
}
|
||||
print("]");
|
||||
fail();
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
SwitchDecoder.update = func () {
|
||||
var t = getprop("/sim/time/elapsed-sec"); # NOTE: simulated time.
|
||||
var v = me.src.getValue();
|
||||
if (!is_num(v)) return;
|
||||
|
||||
if ((me.old == v) and ((t - me.stable_since) > me.MIN_STABLE) and
|
||||
(me.wait == 1)) {
|
||||
var ov = me.old_stable;
|
||||
# Use this to improve.
|
||||
#<cptf> here's the boring version: var bittest = func(u, b) { while (b) { u = int(u / 2); b -= 1; } u != int(u / 2) * 2; }
|
||||
forindex (var i; me.actions) {
|
||||
var m = math.mod(v, 2);
|
||||
var om = math.mod(ov, 2);
|
||||
if ((m != om or me.reset)) { me.actions[i](m?1:0); }
|
||||
v = (v - m)/2;
|
||||
ov = (ov - om)/2;
|
||||
}
|
||||
me.old_stable = me.src.getValue();
|
||||
me.wait = 0;
|
||||
me.reset = 0;
|
||||
} elsif (me.old == v) {
|
||||
# Wait. This is either before the bitmask is stable or after
|
||||
# it has been processed.
|
||||
} else {
|
||||
me.stable_since = t;
|
||||
me.wait = 1;
|
||||
me.old = me.src.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
############################################################
|
||||
# Time division multiplexing encoder: Transmits a list of
|
||||
# properties over a MP enabled string property.
|
||||
# inputs - input properties : [property node]
|
||||
# dest - MP string prop : property node
|
||||
# Note: TDM can have high latency so it is best used for
|
||||
# non-time critical properties.
|
||||
var TDMEncoder = {};
|
||||
TDMEncoder.new = func (inputs, dest) {
|
||||
var obj = { parents : [TDMEncoder],
|
||||
inputs : inputs,
|
||||
channel : MessageChannel.new(dest,
|
||||
func (msg) {
|
||||
print("This should not happen!");
|
||||
}),
|
||||
MIN_INT : 0.25,
|
||||
last_time : 0,
|
||||
next_item : 0,
|
||||
old : [] };
|
||||
# Error checking.
|
||||
var bad = (dest == nil) or (obj.channel == nil);
|
||||
foreach (var i; inputs) {
|
||||
if (i == nil) { bad = 1; }
|
||||
}
|
||||
|
||||
if (bad) {
|
||||
print("TDMEncoder[");
|
||||
foreach (var i; inputs) {
|
||||
print(" ", debug.string(i));
|
||||
}
|
||||
print(" ", debug.string(dest));
|
||||
print("]");
|
||||
}
|
||||
|
||||
setsize(obj.old, size(obj.inputs));
|
||||
|
||||
return obj;
|
||||
}
|
||||
TDMEncoder.update = func () {
|
||||
var t = getprop("/sim/time/elapsed-sec"); # NOTE: simulated time.
|
||||
if (t > me.last_time + me.MIN_INT) {
|
||||
var n = size(me.inputs);
|
||||
while (1) {
|
||||
var v = me.inputs[me.next_item].getValue();
|
||||
|
||||
if ((n <= 0) or (me.old[me.next_item] != v)) {
|
||||
# Set the MP properties to send the next item.
|
||||
me.channel.send(Binary.encodeByte(me.next_item) ~
|
||||
Binary.encodeDouble(v));
|
||||
|
||||
me.old[me.next_item] = v;
|
||||
|
||||
me.last_time = t;
|
||||
me.next_item += 1;
|
||||
if (me.next_item >= size(me.inputs)) { me.next_item = 0; }
|
||||
return;
|
||||
} else {
|
||||
# Search for changed property.
|
||||
n -= 1;
|
||||
me.next_item += 1;
|
||||
if (me.next_item >= size(me.inputs)) { me.next_item = 0; }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
############################################################
|
||||
# Time division multiplexing decoder: Receives a list of
|
||||
# properties over a MP enabled string property.
|
||||
# src - MP string prop : property node
|
||||
# actions - list of actions : [func(v)]
|
||||
# An action is triggered when its value is received.
|
||||
# Note: TDM can have high latency so it is best used for
|
||||
# non-time critical properties.
|
||||
var TDMDecoder = {};
|
||||
TDMDecoder.new = func (src, actions) {
|
||||
var obj = { parents : [TDMDecoder],
|
||||
actions : actions };
|
||||
obj.channel = MessageChannel.new(src,
|
||||
func (msg) {
|
||||
obj.process(msg);
|
||||
});
|
||||
|
||||
# Error checking.
|
||||
var bad = (src == nil) or (obj.channel == nil);
|
||||
foreach (var a; actions) {
|
||||
if (a == nil) { bad = 1; }
|
||||
}
|
||||
|
||||
if (bad) {
|
||||
print("TDMDecoder[");
|
||||
print(" ", debug.string(src));
|
||||
foreach (var a; actions) {
|
||||
print(" ", debug.string(a));
|
||||
}
|
||||
print("]");
|
||||
fail();
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
TDMDecoder.process = func (msg) {
|
||||
var v1 = Binary.decodeByte(msg);
|
||||
var v2 = Binary.decodeDouble(substr(msg, 1));
|
||||
# Trigger action.
|
||||
me.actions[v1](v2);
|
||||
}
|
||||
TDMDecoder.update = func {
|
||||
me.channel.update();
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
# Internal utility functions
|
||||
|
||||
var is_num = func (v) {
|
||||
return num(v) != nil;
|
||||
}
|
||||
|
||||
# fail causes a Nasal runtime error so we get a backtrace.
|
||||
var fail = func {
|
||||
error_detected_in_calling_context();
|
||||
}
|
||||
|
||||
###############################################################################
|
||||
|
||||
###############################################################################
|
||||
# Copilot selection dialog.
|
||||
#
|
||||
# Usage: dual_control_tools.copilot_dialog.show(<copilot type string>);
|
||||
#
|
||||
var COPILOT_DLG = 0;
|
||||
var copilot_dialog = {};
|
||||
############################################################
|
||||
copilot_dialog.init = func (copilot_type, x = nil, y = nil) {
|
||||
me.x = x;
|
||||
me.y = y;
|
||||
me.bg = [0, 0, 0, 0.3]; # background color
|
||||
me.fg = [[1.0, 1.0, 1.0, 1.0]];
|
||||
#
|
||||
# "private"
|
||||
if (contains(aircraft_dual_control, "copilot_view")) {
|
||||
me.title = "Pilot selection";
|
||||
} else {
|
||||
me.title = "Copilot selection";
|
||||
}
|
||||
me.basenode = props.globals.getNode("sim/remote", 1);
|
||||
me.dialog = nil;
|
||||
me.namenode = props.Node.new({"dialog-name" : me.title });
|
||||
me.listeners = [];
|
||||
me.copilot_type = copilot_type;
|
||||
}
|
||||
############################################################
|
||||
copilot_dialog.create = func {
|
||||
if (me.dialog != nil)
|
||||
me.close();
|
||||
|
||||
me.dialog = gui.Widget.new();
|
||||
me.dialog.set("name", me.title);
|
||||
if (me.x != nil)
|
||||
me.dialog.set("x", me.x);
|
||||
if (me.y != nil)
|
||||
me.dialog.set("y", me.y);
|
||||
|
||||
me.dialog.set("layout", "vbox");
|
||||
me.dialog.set("default-padding", 0);
|
||||
var titlebar = me.dialog.addChild("group");
|
||||
titlebar.set("layout", "hbox");
|
||||
titlebar.addChild("empty").set("stretch", 1);
|
||||
if (contains(aircraft_dual_control, "copilot_view")) {
|
||||
titlebar.addChild("text").set("label", "Book your flight");
|
||||
} else {
|
||||
titlebar.addChild("text").set("label", "Passengers online");
|
||||
}
|
||||
var w = titlebar.addChild("button");
|
||||
w.set("pref-width", 16);
|
||||
w.set("pref-height", 16);
|
||||
w.set("legend", "");
|
||||
w.set("default", 0);
|
||||
w.set("key", "esc");
|
||||
w.setBinding("nasal", "dual_control_tools.copilot_dialog.destroy(); ");
|
||||
w.setBinding("dialog-close");
|
||||
me.dialog.addChild("hrule");
|
||||
|
||||
var content = me.dialog.addChild("group");
|
||||
content.set("layout", "vbox");
|
||||
content.set("halign", "center");
|
||||
content.set("default-padding", 5);
|
||||
|
||||
# Generate the dialog contents.
|
||||
me.players = me.find_copilot_players();
|
||||
var i = 0;
|
||||
var tmpbase = me.basenode.getNode("dialog", 1);
|
||||
var selected = me.basenode.getNode("pilot-callsign").getValue();
|
||||
foreach (var p; me.players) {
|
||||
var tmp = tmpbase.getNode("b[" ~ i ~ "]", 1);
|
||||
tmp.setBoolValue(streq(selected, p));
|
||||
var w = content.addChild("checkbox");
|
||||
w.node.setValues({"label" : p,
|
||||
"halign" : "left",
|
||||
"property" : tmp.getPath()});
|
||||
w.setBinding
|
||||
("nasal",
|
||||
"dual_control_tools.copilot_dialog.select_action(" ~ i ~ ");");
|
||||
i = i + 1;
|
||||
}
|
||||
me.dialog.addChild("hrule");
|
||||
|
||||
# Display the dialog.
|
||||
fgcommand("dialog-new", me.dialog.prop());
|
||||
fgcommand("dialog-show", me.namenode);
|
||||
}
|
||||
############################################################
|
||||
copilot_dialog.close = func {
|
||||
fgcommand("dialog-close", me.namenode);
|
||||
}
|
||||
############################################################
|
||||
copilot_dialog.destroy = func {
|
||||
COPILOT_DLG = 0;
|
||||
me.close();
|
||||
foreach(var l; me.listeners)
|
||||
removelistener(l);
|
||||
delete(gui.dialog, "\"" ~ me.title ~ "\"");
|
||||
}
|
||||
############################################################
|
||||
copilot_dialog.show = func (copilot_type) {
|
||||
# print("Showing MPCopilots dialog!");
|
||||
if (!COPILOT_DLG) {
|
||||
COPILOT_DLG = int(getprop("/sim/time/elapsed-sec"));
|
||||
me.init(copilot_type);
|
||||
me.create();
|
||||
me._update_(COPILOT_DLG);
|
||||
}
|
||||
}
|
||||
############################################################
|
||||
copilot_dialog._redraw_ = func {
|
||||
if (me.dialog != nil) {
|
||||
me.close();
|
||||
me.create();
|
||||
}
|
||||
}
|
||||
############################################################
|
||||
copilot_dialog._update_ = func (id) {
|
||||
if (COPILOT_DLG != id) return;
|
||||
me._redraw_();
|
||||
settimer(func { me._update_(id); }, 4.1);
|
||||
}
|
||||
############################################################
|
||||
copilot_dialog.select_action = func (n) {
|
||||
var selected = me.basenode.getNode("pilot-callsign").getValue();
|
||||
var bs = me.basenode.getNode("dialog").getChildren();
|
||||
# Assumption: There are two true b:s or none. The one not matching selected
|
||||
# is the new selection.
|
||||
var i = 0;
|
||||
me.basenode.getNode("pilot-callsign").setValue("");
|
||||
foreach (var b; bs) {
|
||||
if (!b.getValue() and (i == n)) {
|
||||
b.setValue(1);
|
||||
me.basenode.getNode("pilot-callsign").setValue(me.players[i]);
|
||||
} else {
|
||||
b.setValue(0);
|
||||
}
|
||||
i = i + 1;
|
||||
}
|
||||
dual_control.main.reset();
|
||||
me._redraw_();
|
||||
}
|
||||
############################################################
|
||||
# Return a list containing all nearby copilot players of the right type.
|
||||
copilot_dialog.find_copilot_players = func {
|
||||
var mpplayers =
|
||||
props.globals.getNode("ai/models").getChildren("multiplayer");
|
||||
|
||||
var res = [];
|
||||
foreach (var pilot; mpplayers) {
|
||||
if ((pilot.getNode("valid") != nil) and
|
||||
(pilot.getNode("valid").getValue()) and
|
||||
(pilot.getNode("sim/model/path") != nil)) {
|
||||
var type = pilot.getNode("sim/model/path").getValue();
|
||||
|
||||
if (type == me.copilot_type) {
|
||||
append(res, pilot.getNode("callsign").getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
# debug.dump(res);
|
||||
return res;
|
||||
}
|
||||
###############################################################################
|
|
@ -0,0 +1,29 @@
|
|||
#################################################################################
|
||||
# Lake of Constance Hangar #
|
||||
# Boeing 707 for Flightgear #
|
||||
# Copyright (C) 2013 M.Kraus #
|
||||
# #
|
||||
# This program is free software: you can redistribute it and/or modify #
|
||||
# it under the terms of the GNU General Public License as published by #
|
||||
# the Free Software Foundation, either version 3 of the License, or #
|
||||
# (at your option) any later version. #
|
||||
# #
|
||||
# This program is distributed in the hope that it will be useful, #
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
|
||||
# GNU General Public License for more details. #
|
||||
# #
|
||||
# You should have received a copy of the GNU General Public License #
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
|
||||
# #
|
||||
# Every software has a developer, also free software. #
|
||||
# As a gesture of courtesy and respect, I would be delighted #
|
||||
# if you contacted me before making any changes to this software. #
|
||||
# <info (at) marc-kraus.de> April, 2017 #
|
||||
#################################################################################
|
||||
setlistener("sim/current-view/view-number", func (n){
|
||||
var n = n.getValue() or 0;
|
||||
if (n == 0){
|
||||
setprop("sim/current-view/view-number",8);
|
||||
}
|
||||
},0,1);
|
|
@ -0,0 +1,93 @@
|
|||
###############################################################################
|
||||
## Nasal for dual control of the Common-Spruce CS 1 over the multiplayer network.
|
||||
##
|
||||
## Copyright (C) 2007 - 2008 Anders Gidenstam (anders(at)gidenstam.org)
|
||||
## This file is licensed under the GPL license version 2 or later.
|
||||
##
|
||||
## For the CS 1, written in January 2012 by Marc Kraus
|
||||
###############################################################################
|
||||
|
||||
## Renaming (almost :)
|
||||
var DCT = dual_control_tools;
|
||||
|
||||
## Pilot/copilot aircraft identifiers. Used by dual_control.
|
||||
var pilot_type = "Aircraft/followme_e-tron/Models/followme.xml";
|
||||
var copilot_type = "Aircraft/followme_e-tron/Models/followme-PAX.xml";
|
||||
|
||||
############################ PROPERTIES MP ###########################
|
||||
var compressionW = "sim/multiplay/generic/float[12]";
|
||||
var rollspeedW = "sim/multiplay/generic/float[13]";
|
||||
|
||||
var l_dual_control = "dual-control/active";
|
||||
|
||||
######################################################################
|
||||
###### Used by dual_control to set up the mappings for the pilot #####
|
||||
######################## PILOT TO COPILOT ############################
|
||||
######################################################################
|
||||
|
||||
var pilot_connect_copilot = func (copilot) {
|
||||
# Make sure dual-control is activated in the FDM FCS.
|
||||
print("Pilot section");
|
||||
setprop(l_dual_control, 1);
|
||||
|
||||
return [
|
||||
##################################################
|
||||
# Map copilot properties to buffer properties
|
||||
|
||||
# copilot to pilot
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
##############
|
||||
var pilot_disconnect_copilot = func {
|
||||
setprop(l_dual_control, 0);
|
||||
}
|
||||
|
||||
######################################################################
|
||||
##### Used by dual_control to set up the mappings for the copilot ####
|
||||
######################## COPILOT TO PILOT ############################
|
||||
######################################################################
|
||||
|
||||
var copilot_connect_pilot = func (pilot) {
|
||||
# Make sure dual-control is activated in the FDM FCS.
|
||||
print("Copilot section");
|
||||
setprop(l_dual_control, 1);
|
||||
|
||||
setprop("sim/current-view/view-number",8);
|
||||
#setprop("b707/shake-effect/effect",1);
|
||||
|
||||
return [
|
||||
|
||||
##################################################
|
||||
# Map pilot properties to buffer properties
|
||||
|
||||
# float[1] and float[2] for the rumble effect on ground
|
||||
DCT.Translator.new(pilot.getNode("sim/multiplay/generic/float[1]"),
|
||||
props.globals.getNode("sim/multiplay/generic/float[1]", 1)),
|
||||
DCT.Translator.new(pilot.getNode("sim/multiplay/generic/float[2]"),
|
||||
props.globals.getNode("sim/multiplay/generic/float[2]", 1)),
|
||||
#DCT.Translator.new(pilot.getNode("engines/engine[0]/n1"),
|
||||
# props.globals.getNode("engines/engine[0]/n1", 1)),
|
||||
#DCT.Translator.new(pilot.getNode("engines/engine[0]/n2"),
|
||||
# props.globals.getNode("engines/engine[0]/n2", 1)),
|
||||
#DCT.Translator.new(pilot.getNode("engines/engine[1]/n1"),
|
||||
# props.globals.getNode("engines/engine[1]/n1", 1)),
|
||||
#DCT.Translator.new(pilot.getNode("engines/engine[1]/n2"),
|
||||
# props.globals.getNode("engines/engine[1]/n2", 1)),
|
||||
#DCT.Translator.new(pilot.getNode("engines/engine[2]/n1"),
|
||||
# props.globals.getNode("engines/engine[2]/n1", 1)),
|
||||
#DCT.Translator.new(pilot.getNode("engines/engine[2]/n2"),
|
||||
# props.globals.getNode("engines/engine[2]/n2", 1)),
|
||||
#DCT.Translator.new(pilot.getNode("engines/engine[3]/n1"),
|
||||
# props.globals.getNode("engines/engine[3]/n1", 1)),
|
||||
#DCT.Translator.new(pilot.getNode("engines/engine[3]/n2"),
|
||||
# props.globals.getNode("engines/engine[3]/n2", 1))
|
||||
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
var copilot_disconnect_pilot = func {
|
||||
setprop(l_dual_control, 0);
|
||||
}
|
|
@ -0,0 +1,115 @@
|
|||
###############################################################################
|
||||
## $Id$
|
||||
##
|
||||
## Nasal for main pilot for dual control over the multiplayer network.
|
||||
##
|
||||
## Copyright (C) 2007 - 2010 Anders Gidenstam (anders(at)gidenstam.org)
|
||||
## This file is licensed under the GPL license version 2 or later.
|
||||
##
|
||||
###############################################################################
|
||||
# Renaming (almost :)
|
||||
var DCT = dual_control_tools;
|
||||
var ADC = aircraft_dual_control;
|
||||
# NOTE: By loading the aircraft specific dual control module
|
||||
# as <aircraft_dual_control> this file is generic.
|
||||
# The aircraft specific modul must set the variables
|
||||
# pilot_type and copilot_type to the name (with full path) of
|
||||
# main 3d model XML for the pilot and copilot aircraft.
|
||||
# This module should be loades under the name dual_control.
|
||||
|
||||
######################################################################
|
||||
# Connect new copilot
|
||||
var process_data = 0;
|
||||
|
||||
var connect = func (copilot) {
|
||||
# Tweak MP/AI filters
|
||||
copilot.getNode("controls/allow-extrapolation").setBoolValue(0);
|
||||
copilot.getNode("controls/lag-adjust-system-speed").setValue(5);
|
||||
|
||||
process_data = ADC.pilot_connect_copilot(copilot);
|
||||
|
||||
print("Dual control ... copilot connected.");
|
||||
setprop("sim/messages/copilot", "Hi.");
|
||||
}
|
||||
|
||||
######################################################################
|
||||
# Main loop singleton class.
|
||||
var main = {
|
||||
init : func {
|
||||
me.loopid = 0;
|
||||
me.active = 0;
|
||||
setlistener("ai/models/model-added", func {
|
||||
settimer(func { me.activate(); }, 2);
|
||||
});
|
||||
settimer(func { me.activate(); }, 5);
|
||||
print("Pilot dual control ... initialized");
|
||||
},
|
||||
reset : func {
|
||||
if (me.active) {
|
||||
print("Dual control ... copilot disconnected.");
|
||||
ADC.pilot_disconnect_copilot();
|
||||
}
|
||||
me.active = 0;
|
||||
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 (var copilot; mpplayers) {
|
||||
if ((copilot.getChild("valid").getValue()) and
|
||||
(copilot.getChild("callsign") != nil) and
|
||||
(copilot.getChild("callsign").getValue() == r_callsign)) {
|
||||
|
||||
if (me.active == 0) {
|
||||
# Note: sim/model/path tells the 3d XML file of the model.
|
||||
if ((copilot.getNode("sim/model/path") != nil) and
|
||||
(copilot.getNode("sim/model/path").getValue() ==
|
||||
ADC.copilot_type)) {
|
||||
connect(copilot);
|
||||
me.active = 1;
|
||||
} else {
|
||||
print("Dual control ... copilot rejected - wrong aircraft type.");
|
||||
me.loopid += 1;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
# Mess with the MP filters. Highly experimental.
|
||||
if (copilot.getNode("controls/lag-time-offset") != nil) {
|
||||
var v = copilot.getNode("controls/lag-time-offset").getValue();
|
||||
copilot.getNode("controls/lag-time-offset").setValue(0.97 * v);
|
||||
}
|
||||
|
||||
foreach (var w; process_data) {
|
||||
w.update();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (me.active) {
|
||||
print("Dual control ... copilot disconnected.");
|
||||
ADC.pilot_disconnect_copilot();
|
||||
}
|
||||
me.loopid += 1;
|
||||
me.active = 0;
|
||||
},
|
||||
_loop_ : func(id) {
|
||||
id == me.loopid or return;
|
||||
me.update();
|
||||
settimer(func { me._loop_(id); }, 0);
|
||||
}
|
||||
};
|
||||
|
||||
######################################################################
|
||||
# Initialization.
|
||||
setlistener("sim/signals/fdm-initialized", func {
|
||||
main.init();
|
||||
});
|
|
@ -50,7 +50,7 @@ props.getNode("systems/display-speed", 1).setValue(0);
|
|||
props.getNode("systems/speedometer/type", 1).setValue("Type_A");
|
||||
props.getNode("systems/battery-gauge/type", 1).setValue("Type_A");
|
||||
props.getNode("controls/lighting/headlight-als", 1).setValue(0);
|
||||
props.getNode("controls/lighting/highBeam", 1).setValue(0);
|
||||
props.getNode("sim/remote/pilot-callsign", 1).setValue("");
|
||||
|
||||
#var Led = {
|
||||
#
|
||||
|
|
|
@ -0,0 +1,270 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--####################################################################
|
||||
Lake of Constance Hangar
|
||||
Boeing 707 for Flightgear
|
||||
Copyright (C) 2013 M.Kraus
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Every software has a developer, also free software.
|
||||
As a gesture of courtesy and respect, I would be delighted
|
||||
if you contacted me before making any changes to this software.
|
||||
<info (at) marc-kraus.de> April, 2017
|
||||
########################################################################-->
|
||||
<PropertyList>
|
||||
|
||||
<sim n="0">
|
||||
<description>Follow Me e-tron Passenger</description>
|
||||
<author>Sidi Liang</author>
|
||||
<flight-model>null</flight-model>
|
||||
|
||||
<variant-of>followme_e-tron</variant-of>
|
||||
|
||||
<model>
|
||||
<path archive="y">Aircraft/followme_e-tron/Models/followme-PAX.xml</path>
|
||||
</model>
|
||||
|
||||
<remote>
|
||||
<pilot-callsign type="string"/>
|
||||
</remote>
|
||||
|
||||
<sound>
|
||||
<path>Aircraft/followme_e-tron/followme-sound.xml</path>
|
||||
</sound>
|
||||
|
||||
<previews>
|
||||
<preview>
|
||||
<type>exterior</type>
|
||||
<splash type="bool">true</splash>
|
||||
<path>Splash/01.png</path>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>exterior</type>
|
||||
<splash type="bool">true</splash>
|
||||
<path>Splash/02.jpg</path>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>exterior</type>
|
||||
<splash type="bool">true</splash>
|
||||
<path>Splash/03.jpg</path>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>exterior</type>
|
||||
<splash type="bool">true</splash>
|
||||
<path>Splash/04.png</path>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>exterior</type>
|
||||
<splash type="bool">true</splash>
|
||||
<path>Splash/05.jpg</path>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>exterior</type>
|
||||
<splash type="bool">true</splash>
|
||||
<path>Splash/06.jpg</path>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>interior</type>
|
||||
<splash type="bool">true</splash>
|
||||
<path>Splash/07.jpg</path>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>exterior</type>
|
||||
<splash type="bool">true</splash>
|
||||
<path>Splash/08.jpg</path>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>exterior</type>
|
||||
<splash type="bool">true</splash>
|
||||
<path>Splash/09.jpg</path>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>exterior</type>
|
||||
<splash type="bool">true</splash>
|
||||
<path>Splash/10.jpg</path>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>exterior</type>
|
||||
<splash type="bool">true</splash>
|
||||
<path>Splash/11.jpg</path>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>exterior</type>
|
||||
<splash type="bool">true</splash>
|
||||
<path>Splash/12.jpg</path>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>exterior</type>
|
||||
<splash type="bool">true</splash>
|
||||
<path>Splash/13.jpg</path>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>exterior</type>
|
||||
<splash type="bool">true</splash>
|
||||
<path>Splash/14.jpg</path>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>exterior</type>
|
||||
<splash type="bool">true</splash>
|
||||
<path>Splash/15.jpg</path>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>exterior</type>
|
||||
<splash type="bool">true</splash>
|
||||
<path>Splash/16.jpg</path>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>exterior</type>
|
||||
<splash type="bool">true</splash>
|
||||
<path>Splash/17.jpg</path>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>interior</type>
|
||||
<splash type="bool">true</splash>
|
||||
<path>Splash/18.jpg</path>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>interior</type>
|
||||
<splash type="bool">true</splash>
|
||||
<path>Splash/19.jpg</path>
|
||||
</preview>
|
||||
|
||||
<preview>
|
||||
<type>exterior</type>
|
||||
<splash type="bool">true</splash>
|
||||
<path>Splash/20.jpg</path>
|
||||
</preview>
|
||||
</previews>
|
||||
|
||||
|
||||
<menubar>
|
||||
<default>
|
||||
<menu n="100">
|
||||
<label>FM e-tron PAX</label>
|
||||
<enabled type="bool">true</enabled>
|
||||
<item>
|
||||
<label>Select driver in range</label>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script>
|
||||
dual_control_tools.copilot_dialog.show(aircraft_dual_control.pilot_type);
|
||||
</script>
|
||||
</binding>
|
||||
</item>
|
||||
</menu>
|
||||
</default>
|
||||
</menubar>
|
||||
|
||||
<view>
|
||||
<internal archive="y">true</internal>
|
||||
<enabled type="bool">false</enabled>
|
||||
<config>
|
||||
<limits>
|
||||
<enabled archive="y" type="bool">false</enabled>
|
||||
</limits>
|
||||
<!-- x/y/z == right/up/back -->
|
||||
<x-offset-m archive="y">-0.46</x-offset-m>
|
||||
<y-offset-m archive="y">1.19</y-offset-m>
|
||||
<z-offset-m archive="y">-18.2</z-offset-m>
|
||||
<pitch-offset-deg archive="y">-16.0</pitch-offset-deg>
|
||||
<default-field-of-view-deg type="double">72</default-field-of-view-deg>
|
||||
</config>
|
||||
</view>
|
||||
<view n="1">
|
||||
<enabled type="bool">true</enabled>
|
||||
<config>
|
||||
<!-- big plane, so extend chase view offset a bit -->
|
||||
<z-offset-m type="double" archive="y">-50.0</z-offset-m>
|
||||
</config>
|
||||
</view>
|
||||
<view n="2">
|
||||
<enabled type="bool">false</enabled>
|
||||
</view>
|
||||
<view n="3">
|
||||
<enabled type="bool">false</enabled>
|
||||
</view>
|
||||
<view n="4">
|
||||
<enabled type="bool">false</enabled>
|
||||
</view>
|
||||
<view n="5">
|
||||
<enabled type="bool">true</enabled>
|
||||
</view>
|
||||
<view n="6">
|
||||
<enabled type="bool">false</enabled>
|
||||
</view>
|
||||
<view n="7">
|
||||
<enabled type="bool">true</enabled>
|
||||
</view>
|
||||
<view n="100">
|
||||
<name>Copilot View</name>
|
||||
<internal archive="y">true</internal>
|
||||
<type>lookfrom</type>
|
||||
<config>
|
||||
<from-model type="bool">true</from-model>
|
||||
<from-model-idx type="int">0</from-model-idx>
|
||||
<dynamic-view type="bool">true</dynamic-view>
|
||||
<x-offset-m archive="y" type="double">0.35</x-offset-m>
|
||||
<y-offset-m archive="y" type="double">1.35</y-offset-m>
|
||||
<z-offset-m archive="y" type="double">1.88</z-offset-m>
|
||||
<pitch-offset-deg>-10.0</pitch-offset-deg>
|
||||
<field-of-view>65</field-of-view>
|
||||
</config>
|
||||
</view>
|
||||
|
||||
|
||||
|
||||
<chase-distance-m>-45</chase-distance-m>
|
||||
|
||||
</sim>
|
||||
|
||||
<nasal>
|
||||
<followme>
|
||||
<file>Aircraft/followme_e-tron/Nasal/DualControl/followme_e-tron-PAX.nas</file>
|
||||
</followme>
|
||||
<!-- Dual control. -->
|
||||
<dual_control_tools>
|
||||
<file>Aircraft/followme_e-tron/Nasal/DualControl/dual-control-tools.nas</file>
|
||||
</dual_control_tools>
|
||||
<aircraft_dual_control>
|
||||
<file>Aircraft/followme_e-tron/Nasal/DualControl/followme-dual-control.nas</file>
|
||||
</aircraft_dual_control>
|
||||
<dual_control>
|
||||
<file>Aircraft/followme_e-tron/Nasal/DualControl/copilot-dual-control.nas</file>
|
||||
</dual_control>
|
||||
</nasal>
|
||||
|
||||
<!-- Dual control depend on this value -->
|
||||
<dual-control>
|
||||
<active type="bool">0</active>
|
||||
</dual-control>
|
||||
|
||||
</PropertyList>
|
|
@ -220,6 +220,21 @@
|
|||
<target-y-offset-m archive="y" type="double">1.6</target-y-offset-m>
|
||||
</config>
|
||||
</view>
|
||||
<view n="100">
|
||||
<name>Copilot View</name>
|
||||
<internal archive="y">true</internal>
|
||||
<type>lookfrom</type>
|
||||
<config>
|
||||
<from-model type="bool">true</from-model>
|
||||
<from-model-idx type="int">0</from-model-idx>
|
||||
<dynamic-view type="bool">true</dynamic-view>
|
||||
<x-offset-m archive="y" type="double">0.35</x-offset-m>
|
||||
<y-offset-m archive="y" type="double">1.35</y-offset-m>
|
||||
<z-offset-m archive="y" type="double">1.88</z-offset-m>
|
||||
<pitch-offset-deg>-10.0</pitch-offset-deg>
|
||||
<field-of-view>65</field-of-view>
|
||||
</config>
|
||||
</view>
|
||||
|
||||
<hud>
|
||||
<enable3d>false</enable3d>
|
||||
|
@ -317,6 +332,15 @@
|
|||
<script>engine.stopEngine()</script>
|
||||
</binding>
|
||||
</item>
|
||||
<item>
|
||||
<label>Select Passenger</label>
|
||||
<binding>
|
||||
<command>nasal</command>
|
||||
<script>
|
||||
dual_control_tools.copilot_dialog.show(aircraft_dual_control.copilot_type);
|
||||
</script>
|
||||
</binding>
|
||||
</item>
|
||||
</menu>
|
||||
</default>
|
||||
</menubar>
|
||||
|
@ -579,6 +603,15 @@
|
|||
<light>
|
||||
<file>Aircraft/followme_e-tron/Nasal/light-manager.nas</file>
|
||||
</light>
|
||||
<dual_control_tools>
|
||||
<file>Aircraft/followme_e-tron/Nasal/DualControl/dual-control-tools.nas</file>
|
||||
</dual_control_tools>
|
||||
<aircraft_dual_control>
|
||||
<file>Aircraft/followme_e-tron/Nasal/DualControl/followme-dual-control.nas</file>
|
||||
</aircraft_dual_control>
|
||||
<dual_control>
|
||||
<file>Aircraft/followme_e-tron/Nasal/DualControl/pilot-dual-control.nas</file>
|
||||
</dual_control>
|
||||
</nasal>
|
||||
|
||||
</PropertyList>
|
||||
|
|
Loading…
Reference in New Issue