# == walking functions v4.3 for FlightGear version 1.9-2.0 OSG ==
# == plus coordinates for Bluebird Explorer Hovercraft 10.4    ==

# aircraft specific section:
var hatch_specs = {
	z_opening_ft: 6.0,
	z_floor_ft: 0,	#  z-axis offset for walker exit from aircraft origin
			#  generally floor z-axis converted to ft.
	rear_hatch_loc: 5,
	out_locations: func (loc) {
			if (loc == 0) {		# exit but not by hatch

				# get default exit coordinates from set file

				var defx = getprop ("sim/model/map/default_exit/x-offset-m");
				var defy = getprop ("sim/model/map/default_exit/y-offset-m");

		#		if no exit is specified in set file, start at 0,0

				if (defx == nil ) {
					defx =0;
				}
				if (defy == nil ) {
					defy =0;
				}

				var new_coord = xy2LatLonZ(defx,defy);	
			} elsif (loc == 1) {
				var new_coord = xy2LatLonZ(getprop("sim/model/bluebird/crew/walker/x-offset-m"),-4.9);
			} elsif (loc == 2) {
				var new_coord = xy2LatLonZ(getprop("sim/model/bluebird/crew/walker/x-offset-m"),4.9);
			} elsif (loc == 5) {
				var new_coord = xy2LatLonZ(11.0,getprop("sim/model/bluebird/crew/walker/y-offset-m"));
			} else {
				var new_coord = xy2LatLonZ(xViewNode.getValue(),yViewNode.getValue());
			}
			return new_coord;
	}
};
# end aircraft specific

var sin = func(a) { math.sin(a * math.pi / 180.0) }	# degrees
var cos = func(a) { math.cos(a * math.pi / 180.0) }
var asin = func(y) { math.atan2(y, math.sqrt(1-y*y)) }	# radians
var ERAD = 6378138.12; 		# Earth radius (m)
var ERAD_deg = 180 / (ERAD * math.pi);
var DEG2RAD = math.pi / 180;
var xViewNode = props.globals.getNode("sim/current-view/z-offset-m", 1);
var yViewNode = props.globals.getNode("sim/current-view/x-offset-m", 1);
var zViewNode = props.globals.getNode("sim/current-view/y-offset-m", 1);
var fps_node = props.globals.getNode("sim/frame-rate", 1);
var falling = 0;	# 0/1 = false/true
var last_altitude = 0;	# remember last position to detect falling from ground
var exit_time_sec = 0.0;
var parachute_ft = 0.0;
var parachute_deployed_sec = 0.0;
var elapsed_chute_sec = 0.0;
var lat_vector = 0.0;
var lon_vector = 0.0;
var z_vector_mps = 0.0;
var time_to_top_sec = 0.0;
var starting_lat = 0.0;
var starting_lon = 0.0;
var walk_heading = 0;
var walk_watch = 0;
var walk_factor = 1.0;

# debug section
var measure_main_count = 0;
var measure_walk_count = 0;	# momentum_walk loops
var measure_extmov_count = 0;
var measure_sec = 0;
var measure_alt = 0;
var last_elapsed_sec = 0;

# ===== functions ======
var normheading = func (a) {
	while (a >= 360)
		a -= 360;
	while (a < 0)
		a += 360;
	return a;
}

var distFromCraft = func (lat,lon) {
	var c_lat = getprop("position/latitude-deg");
	var c_lon = getprop("position/longitude-deg");
	var a = sin((lat - c_lat) * 0.5);
	var b = sin((lon - c_lon) * 0.5);
	return 2.0 * ERAD * asin(math.sqrt(a * a + cos(lat) * cos(c_lat) * b * b));
}

var xy2LatLonZ = func (x,y) {
	# given the x,y offsets of the cockpit view when walking (in meters)
	# or the hatch location upon exit
	# translate into lat,lon,z-offset-m for transfer to outside walker
	var c_head_rad = getprop("orientation/heading-deg") * DEG2RAD;
	var c_lat = getprop("position/latitude-deg");
	var c_lon = getprop("position/longitude-deg");
#	var c_gnd_pitch = getprop("orientation/ground-pitch");
	var c_pitch = getprop("orientation/pitch-deg");
	var c_roll = getprop("orientation/roll-deg");
	var xZ_factor = math.cos(c_pitch * DEG2RAD);
	var x_Zadjust = x * xZ_factor;	# adjusted for pitch
	var y_Zadjust = y * math.cos(c_roll * DEG2RAD);	# adjusted for roll
#	print(sprintf("W.xy2 x= %10.6f xZ= %10.6f  y= %10.6f yZ= %10.6f",x,x_Zadjust,y,y_Zadjust));
	var xy_hyp = math.sqrt((x_Zadjust * x_Zadjust) + (y_Zadjust * y_Zadjust));
	var a = (xy_hyp == 0 ? 0 : asin(y_Zadjust / xy_hyp));
	if (x > 0) {
		a = math.pi - a;
	}
	var xy_head_rad = c_head_rad + a;
#	print(sprintf ("W.xy2  c_head= %6.2f a= %6.2f xy_head= %6.2f",(c_head_rad*180/math.pi),(a*180/math.pi),(xy_head_rad*180/math.pi)));
	var xy_lat_m = xy_hyp * math.cos(xy_head_rad);
	var xy_lon_m = xy_hyp * math.sin(xy_head_rad);
#	print(sprintf ("W.xy2  x= %9.8f y= %9.8f xy_lat_m= %9.8f xy_lon_m= %9.8f",x_Zadjust,y_Zadjust,xy_lat_m,xy_lon_m));
	var xy_lat = xy_lat_m * ERAD_deg;
	var xy_lon = xy_lon_m * ERAD_deg / cos(c_lat);
#	print(sprintf ("W.xy2  position/lat= %9.8f lon= %9.8f xy_lat= %9.8f xy_lon= %9.8f",c_lat,c_lon,xy_lat,xy_lon));
	var zxZ_m = -(x * math.sin(c_pitch * DEG2RAD));
	var zyZ_m = -(y * math.sin(c_roll * DEG2RAD) * xZ_factor);	# goes to zero as pitch to 90

# MARK-pre-10.4: not Perfect yet: z of hatch and height of walker (1.67m) is not adjusted for at angle.
#	print (sprintf ("W.xy2  zxZ= %10.6f zyZ= %10.6f z= %10.6f",zxZ_m,zyZ_m,(zxZ_m + zyZ_m)));
	return [(c_lat + xy_lat) , (c_lon + xy_lon) , (zxZ_m + zyZ_m)];
}

var calc_heading = func {
	var w_forward = getprop("sim/walker/key-triggers/forward");
	var w_left = getprop("sim/walker/key-triggers/slide");
	var new_head = -999;
	var new_walking = 0;
	if (w_forward > 0) {
		new_walking = 1;
		if (w_left < 0) {
			new_head = 45;
		} elsif (w_left > 0) {
			new_head = -45;
		} else {
			new_head = 0;
		}
	} elsif (w_forward < 0) {
		new_walking = -1;
		if (w_left < 0) {
			new_head = 135;
		} elsif (w_left > 0) {
			new_head = -135;
		} else {
			new_head = 180;
		}
	} else {
		if (w_left < 0) {
			new_walking = 4;
			new_head = 90;
		} elsif (w_left > 0) {
			new_walking = 6;
			new_head = -90;
		} else {
			setprop ("sim/walker/walking", 0);
			return 0;
		}
	}
	walk_heading = new_head;
	setprop ("sim/walker/walking", new_walking);
}

var momentum_walk = func {
	measure_walk_count += 1;
	if (walk_watch >= 3) {
		if (walk_factor < 2.0) {	# speed up when holding down key
			walk_factor += 0.025;
		}
		setprop ("sim/walker/walking-momentum", 1);
	} elsif (walk_watch >= 2) {
		setprop ("sim/walker/walking-momentum", 1);
		walk_watch -= 1;
	} else {
		walk_factor = ((walk_factor - 1.0) * 0.5) + 1.0;
		if (walk_factor < 1.1) {
			walk_factor = 1.0;
			walk_watch = 0;
		} else {
			setprop ("sim/walker/walking-momentum", 1);
		}
	}
	if (walk_watch) {
		settimer(momentum_walk,0.05);
	} else {
		setprop ("sim/walker/walking-momentum", 0);
	}
}

var main_loop = func {
	measure_main_count += 1;
	var c_view = getprop ("sim/current-view/view-number");
	var moved = 0;
	if (c_view == 0 and getprop("sim/walker/walking-momentum")) {
		# inside aircraft
#		bluebird.walk_about_cabin(0.1, walk_heading);
		moved = 1;
	}
	if (getprop("sim/walker/outside")) {
		if (falling or getprop("sim/walker/walking-momentum")) {
			ext_mov(moved);
		}
		# check for proximity to hatches for entry after 0.75 sec.
		var elapsed_sec = getprop("sim/time/elapsed-sec");
		var elapsed_fall_sec = elapsed_sec - exit_time_sec;
		if (elapsed_fall_sec > 0.75) {
			if (abs(getprop("sim/walker/altitude-ft") - getprop("position/altitude-ft") + hatch_specs.z_opening_ft) < 7) {
				# must be within (opening) ft vertically to climb in
				var posy = getprop("sim/walker/latitude-deg");
				var posx = getprop("sim/walker/longitude-deg");

				# the following section is aircraft specific for locations of entry hatches and doors
	#			if (getprop("sim/model/bluebird/doors/door[0]/position-norm") > 0.73) {
	#				var door0_ll = xy2LatLonZ(-2.6,-3.42);
	#				var a0 = sin((door0_ll[0] - posy) * 0.5);
	#				var b0 = sin((door0_ll[1] - posx) * 0.5);
					# doesn't actually check z-axis, mis-alignments in rare orientations
	#				var d0 = 2.0 * ERAD * asin(math.sqrt(a0 * a0 + cos(door0_ll[0]) * cos(posy) * b0 * b0));
	#				if (d0 < 1.2) {
	#					get_in(1);
	#				}
	#			}
	#			if (getprop("sim/model/bluebird/doors/door[1]/position-norm") > 0.73) {
	#				var door1_ll = xy2LatLonZ(-2.6,3.42);
	#				var a1 = sin((door1_ll[0] - posy) * 0.5);
	#				var b1 = sin((door1_ll[1] - posx) * 0.5);
	#				var d1 = 2.0 * ERAD * asin(math.sqrt(a1 * a1 + cos(door1_ll[0]) * cos(posy) * b1 * b1));
	#				if (d1 < 1.2) {
	#					get_in(2);
	#				}
	#			}
	#			if (getprop("sim/model/bluebird/doors/door[5]/position-norm") > 0.78) {
	#				var door5_ll = xy2LatLonZ(9.0,0);
	#				var a5 = sin((door5_ll[0] - posy) * 0.5);
	#				var b5 = sin((door5_ll[1] - posx) * 0.5);
	#				var d5 = 2.0 * ERAD * asin(math.sqrt(a5 * a5 + cos(door5_ll[0]) * cos(posy) * b5 * b5));
	#				if (d5 < 1.9) {
	#					get_in(5);
	#				}
	#			}
				# end aircraft specific

			}
		}
	} elsif (!moved and getprop("sim/walker/walking-momentum")) {
#		bluebird.walk_about_cabin(0.1, walk_heading);
	}

	if (getprop("logging/walker-debug") and getprop("sim/walker/outside")) {
		var elapsed_sec = getprop("sim/time/elapsed-sec");
		var t = elapsed_sec - measure_sec;
		if (t >= 0.991) {
			var posz1 = getprop("sim/walker/altitude-ft");
			print(sprintf("========= at %6.2f : %3i %3i %3i : Z-axis %6.2f ft / %6.4f sec = %6.2f mps",elapsed_sec,measure_main_count,measure_walk_count,measure_extmov_count,(measure_alt-posz1),t,((measure_alt-posz1)*0.3028/t)));
			measure_alt = posz1;
			measure_sec = elapsed_sec;
			measure_main_count = 0;
			measure_walk_count = 0;
			measure_extmov_count = 0;
		}
	}

	settimer(main_loop, 0.01)
}

    var walker_model = {
       index:   0,
       add:   func {
             if (getprop("sim/model/crew/walker/visible")) {
                if (getprop("logging/walker-position")) {
                   print("walker_model.add");
                }
             var manager = props.globals.getNode("/models", 1);
                var i = 0;
                for (; 1; i += 1)
                   if (manager.getChild("model", i, 0) == nil)
                      break;
                
                props.globals.getNode("models/model[" ~ i ~ "]/path", 1);
                props.globals.getNode("models/model[" ~ i ~ "]/longitude-deg-prop", 1);
                props.globals.getNode("models/model[" ~ i ~ "]/latitude-deg-prop", 1);
                props.globals.getNode("models/model[" ~ i ~ "]/elevation-ft-prop", 1);
                props.globals.getNode("models/model[" ~ i ~ "]/heading-deg-prop", 1);
                
                setprop ("models/model[" ~ i ~ "]/path", "Aircraft/Generic/Human/Models/walker.xml");
                setprop ("models/model[" ~ i ~ "]/longitude-deg-prop", "sim/walker/longitude-deg");
                setprop ("models/model[" ~ i ~ "]/latitude-deg-prop", "sim/walker/latitude-deg");
                setprop ("models/model[" ~ i ~ "]/elevation-ft-prop", "sim/walker/altitude-ft");
                setprop ("models/model[" ~ i ~ "]/heading-deg-prop", "sim/walker/model-heading-deg");
                props.globals.getNode("models/model[" ~ i ~ "]/load", 1);
                me.index = i;
             }
          },
       remove:   func {
             if (getprop("logging/walker-position")) {
                print("walker_model.remove");
             }
             props.globals.getNode("/models", 1).removeChild("model", me.index);
             walker_model.reset_fall();
          },

	reset_fall: func {
			falling = 0;
			walk_factor = 1.0;
			setprop("sim/walker/airborne", 0);
			setprop("sim/walker/parachute-opened-altitude-ft", 0);
			parachute_deployed_sec = 0;
			setprop("sim/walker/parachute-opened-sec", 0);
			setprop("sim/walker/starting-trajectory-lat", 0.0);
			setprop("sim/walker/starting-trajectory-lon", 0.0);
			setprop("sim/walker/starting-trajectory-z-mps", 0.0);
			setprop("sim/walker/time-to-zero-z-sec", 0.0);
		},
	land:	func (lon,lat,alt) {
			walker_model.reset_fall();
			setprop("sim/walker/latitude-deg", lat);
			setprop("sim/walker/longitude-deg", lon);
			setprop("sim/walker/altitude-ft", alt);
			last_altitude = alt;
		},
};

var open_chute = func {
	if (getprop("sim/walker/airborne") and exit_time_sec and !parachute_ft and getprop("sim/walker/parachute-equipped")) {
		setprop("sim/walker/parachute-opened-altitude-ft", getprop("sim/walker/altitude-ft"));
		parachute_deployed_sec = getprop("sim/time/elapsed-sec");
		setprop("sim/walker/parachute-opened-sec", 0);
	}
}

setlistener("sim/walker/walking", func(n) {
	var wdir = n.getValue();
	if (wdir) {	# repetitive input or bug in mod-up keeps triggering
		walk_dir = wdir;	# remember current direction
		if (walk_watch == 0) {
			walk_watch = 3;	# start or reset timer for countdown
			momentum_walk();	# starting from rest, start new loop
		} else {
			walk_watch = 3;	# reset watcher
		}
	} else {
		# last heard was zero
		walk_watch -= 1;
		if (walk_watch < 0) {
			walk_watch = 0;
		}
	}
});

setlistener("sim/walker/key-triggers/outside-toggle", func {
	var c_view = getprop ("sim/current-view/view-number");
	if (c_view == 0) {
		if (getprop("sim/walker/outside")) {
			setprop("sim/current-view/view-number", view.indexof("Walk View"));

		} else {
			get_out(0);
		}
	} elsif (c_view == view.indexof("Walk View")) {
		get_in(0);
	} else {
		if (getprop("sim/walker/outside")) {
			get_in(0);
		} else {
			get_out(0);
		}
	}
});

var ext_mov = func (moved) {
	measure_extmov_count += 1;
	var c_view = getprop("sim/current-view/view-number");
	if (c_view == view.indexof("Walker Orbit View")) {
		var head_v = 360 - getprop("sim/walker/model-heading-deg");
	} else {
		var head_v = getprop("sim/current-view/heading-offset-deg");
	}
	var c_head_deg = getprop("orientation/heading-deg");
	var posy1 = getprop("sim/walker/latitude-deg");
	var posx1 = getprop("sim/walker/longitude-deg");
	var posz1 = getprop("sim/walker/altitude-ft");
	var posx2 = posx1;	# new after calculations
	var posy2 = posy1;
	var posz2 = posz1;
	var check_movement = 1;
	var fps = fps_node.getValue();
	fps = (fps < 10 ? 10 : fps);	# only realistic above 10fps. Slow down below that so that walker pauses instead of jumping.
	var speed = getprop("sim/walker/key-triggers/speed") * walk_factor / fps;
	if (c_view >= 1 and c_view <=3) {
		head_v = normheading(360 - c_head_deg + head_v);
	} elsif (c_view == 5) {
		head_v = normheading(c_head_deg + head_v + 90);
	}
	var head_w = normheading(head_v + walk_heading);
	if (!moved) {
		setprop("sim/walker/model-heading-deg" , 360 - head_v);
	}
	var elapsed_sec = getprop("sim/time/elapsed-sec");
	if (falling) {
		var elapsed_fall_sec = elapsed_sec - exit_time_sec;
		if (elapsed_fall_sec > 0.1) {
			check_movement = 0;
		}
	}
	if (check_movement and !moved) {
		var lat_m = speed * cos(head_w);
		var lon_m = speed * sin(head_w);
		var lat3 = lat_m * ERAD_deg;
		var lon3 = lon_m * ERAD_deg / cos(posy1);
		posx2 = posx1 - lon3;	# heading is offset or reversed west to east
	#	if (posy1 < 0 ) {
	#			posy2 = posy1 + lat3;
	#			print (" South");
	#	} else {
	#			posy2 = posy1 + lat3;
	#			print (" North");
	#	}
		posy2 = posy1 + lat3;
	#	posy2 = (posy1 < 0 ? posy1 - lat3 : posy1 + lat3);
				#        southern or northern hemisphere
	}
	if (falling) {	# add movement from aircraft upon jumping
		var parabola = 0;
		var parachute_drag = 0;
		var zero_xy_sec = (elapsed_fall_sec < 10 ? elapsed_fall_sec : 10.0);
		if (parachute_ft) {	# chute open
			setprop("sim/walker/parachute-opened-sec", (elapsed_sec - parachute_deployed_sec - 1.0));
			# chute starts to add drag at 1 second, fully open at 3 sec. Slows to 17 ft/sec
			if (elapsed_chute_sec >= 3.0) {
				parachute_drag = 0.9;
			} elsif (elapsed_chute_sec >= 0.0) {	# 1 second delay for chute to deploy
				parachute_drag = sin(elapsed_chute_sec * 30) * 0.9;	# 0 to 0.9 in 3 sec.
			}
		}
		if (parachute_drag) {
			zero_xy_sec = parachute_deployed_sec - exit_time_sec + 1.0;
			if (zero_xy_sec > 10.0) {
				zero_xy_sec = 10.0;
			}
		}
		parabola = sin(90 - zero_xy_sec ) * zero_xy_sec - (0.096 * zero_xy_sec * zero_xy_sec / 2);
		if (parachute_drag and zero_xy_sec < 7) {
			posy2 = starting_lat + (lat_vector * parabola) + (lat_vector * parachute_drag);
			posx2 = starting_lon + (lon_vector * parabola) + (lon_vector * parachute_drag);
		} else {
			posy2 = starting_lat + (lat_vector * parabola);
			posx2 = starting_lon + (lon_vector * parabola);
		}
	}
	var posz_geo = geo.elevation(posy2, posx2, ((posz1 * 0.3048) + 2));
	if (posz_geo == nil) {
		posz_geo = geo.elevation(posy2, posx2);	# underground? try without current walker altitude
		if (posz_geo == nil) {
			posz_geo = 0;
			print(sprintf("Error. Attempting to move to latitude %13.8f longitude %13.8f",posy2,posx2));
		}
	}
	posz_geo = posz_geo / 0.3048;	# convert to ft
#	if (getprop("logging/walker-debug")) {
#		print(sprintf("walker_lat= , %9.8f , lon= , %9.8f , groundDistanceFromAircraft= , %6.3f , geo.elev= , %8.3f , altitude= , %8.3f",posy2,posx2,distFromCraft(posy2,posx2),posz_geo,posz2));
#	}
	if (falling) {	# 13,000 to 12,000 ft = 10 sec. 12,000 - 4,000 = 44 sec.
			# 5.5 sec to cover each 1000 ft at terminal velocity (ignoring altitude density and surface area)
		var dist_traveled_z = 0;	# feet
		if (posz_geo < posz1) {	# ground is below walker
			dist_traveled_z = -32.185 * time_to_top_sec * time_to_top_sec / 2;	# upward half of arc
			var elapsed1 = elapsed_fall_sec - time_to_top_sec;
				# excludes wind resistance and cross section of projectile. Assume negligible for now.
			if (elapsed_fall_sec > time_to_top_sec) {	# past zero_z and falling
				# drag constant is actually 0.515 kg/s for spread eagle, and 0.067 for feet first.
				# I am ignoring these distinctions for now, until a more complex formula can be made,
				# to go along with the new walker model visibility.
				# also needs to be improved is loss of acceleration due to drag forces
				if (elapsed_fall_sec > (time_to_top_sec + 5.358)) {	# time to reach terminal velocity
					dist_traveled_z += 461.99 + ((elapsed1 - 5.358) * 172);
				} else {	# 9.81m/s/s up to terminal velocity 172ft/s 54m/s spread eagle
					dist_traveled_z += 32.185 * elapsed1 * elapsed1 / 2;
				}
			} else {	# started going up, arch to zero_z before falling
				dist_traveled_z += 32.185 * elapsed1 * elapsed1 / 2;
				if (getprop("logging/walker-debug")) {
					print(sprintf("time_to_top_sec= %6.2f elapsed1= %6.2f  dist_traveled_z_ft = %8.3f  z_vector_mps= %6.2f exit_alt= %9.3f posz1= %9.3f posz2= %9.3f" , time_to_top_sec,elapsed1,dist_traveled_z,z_vector_mps,getprop("sim/walker/altitude-at-exit-ft"),posz1,(getprop("sim/walker/altitude-at-exit-ft")-posz1)));
				}
			}
			if (parachute_ft) {	# chute open
				# need to better model deceleration due to opening of chute, change in surface area.
				var subtract_z = 0;
				if (elapsed_chute_sec >= 5.0) {
					subtract_z = 363.14 + ((elapsed_chute_sec - 5.0) * 155);
				} elsif (elapsed_chute_sec >= 2.0) {
					subtract_z = 32.15 * parachute_drag * elapsed_chute_sec * elapsed_chute_sec / 2;
				} elsif (elapsed_chute_sec >= 0.0) {
					subtract_z = 32.15 * parachute_drag * elapsed_chute_sec * elapsed_chute_sec / 2;
				}
				dist_traveled_z -= subtract_z;
			}
			posz2 = getprop("sim/walker/altitude-at-exit-ft") - dist_traveled_z;
			if (posz2 < posz_geo) {	# below ground
				if ((parachute_drag < 0.7) and (dist_traveled_z > 20)) {
					print(sprintf("OUCH! You fell %9.2f ft from an exit at %10.2f ft.  Parachute was Not deployed.",dist_traveled_z,getprop("sim/walker/altitude-at-exit-ft")));
					if (getprop("sim/current-view/view-number") == view.indexof("Walk View")) {
						setprop("sim/current-view/pitch-offset-deg", -80);
					}
					setprop("sim/model/bluebird/position/landing-wow", 1);
					setprop("sim/walker/crashed", 1);
					setprop("sim/walker/airborne", 0);
				}
				posz2 = posz_geo;
				walker_model.land(posx2,posy2,posz_geo);
			} else {
				if (getprop("logging/walker-position")) {
					print(sprintf("falling_lat= %11.8f lon= %13.8f altitude= %9.2f elapsed_fall_sec= %5.2f speed_ft/s= %7.2f chute_drag= %4.2f",posy2,posx2,posz1,elapsed_fall_sec,((measure_alt-posz2)/(elapsed_sec - last_elapsed_sec)),parachute_drag));
					measure_alt = posz2;
					last_elapsed_sec = elapsed_sec;
				}
			}
		} else {
			walker_model.land(posx2,posy2,posz_geo);
			posz2 = posz_geo;
		}
	} else {	# not falling
		# check for sudden change in ground elevation
		if ((abs(posz1 - last_altitude) > 1.6) or ((posz_geo + 1.6) < posz1)) {
			setprop("sim/walker/time-of-exit-sec", getprop("sim/time/elapsed-sec"));
			setprop("sim/walker/altitude-at-exit-ft", last_altitude);
			# add "forward" momentum upon step out and down
			var lat_m = getprop("sim/walker/key-triggers/speed") * walk_factor * cos(head_w);
			var lon_m = getprop("sim/walker/key-triggers/speed") * walk_factor * (0 - sin(head_w));
			var lat3 = lat_m * ERAD_deg;
			var lon3 = lon_m * ERAD_deg / cos(posy1);
			posy3 = posy1 + lat3;
			posx3 = posx1 + lon3;
			setprop("sim/walker/starting-trajectory-lat", lat3);
			setprop("sim/walker/starting-trajectory-lon", lon3);
			setprop("sim/walker/starting-lat", posy2);
			setprop("sim/walker/starting-lon", posx2);
			setprop("sim/walker/latitude-deg", posy3);
			setprop("sim/walker/longitude-deg", posx3);
			setprop("sim/walker/starting-trajectory-z-mps", 0.0);
			falling = 1;
			if ((posz1 - posz_geo) > 50) {
				setprop("sim/walker/airborne", 1);
				setprop("sim/walker/parachute-equipped", 0);
			}
		}
		if (!falling) {
			if (posz_geo < (posz1 + 5)) {	# walking, ground within 10 ft below walker
				var posz3_geo = geo.elevation(posy2, posx2, ((posz1 * 0.3048) + 2));
				if (posz3_geo == nil) {
					posz3_geo = 0;
				}
				posz3_geo = posz3_geo / 0.3048;	# convert to ft
				var posz_geodiff = abs(posz_geo - posz1);
				if ((posz3_geo - posz_geo) > 1.5 and posz_geodiff < 7 and posz_geodiff > 1.5) { # under object and nearly hitting head
					print(sprintf ("Stopped by overhead obstruction, has height %6.2f ft above your position.", posz_geodiff));
					posx2 -= (5 * (posx2 - posx1));
					posy2 -= (5 * (posy2 - posy1));	# fall backward and reload ground level
					posz_geo = geo.elevation(posy2, posx2, ((posz1 * 0.3048) + 1));
					if (posz_geo == nil) {
						posz_geo = 0;
					}
					posz_geo = posz_geo / 0.3048;	# convert to ft
				}
				if ((posz1+0.3) > posz_geo or (posz1-0.3) < posz_geo) {	# smoothen stride
					interpolate ("sim/walker/altitude-ft", posz_geo, 0.25, 0.3);
					posz2 = getprop("sim/walker/altitude-ft");
				}
				if (getprop("logging/walker-position")) {
					print(sprintf("walker_lat= %12.8f lon= %11.8f altitude= %9.2f heading= %6.2f speed= %4.2f walk_factor= %4.2f groundDistanceFromAircraft= %3.2f geo.elev= %8.3f",posy2,posx2,posz2,head_v,speed,walk_factor,distFromCraft(posy2,posx2),posz_geo));
				}
			} else {
				print(sprintf ("Stopped by wall, has height %6.2f ft above your position.",(posz1-posz_geo)));
				posx2 -= (5 * (posx2 - posx1));
				posy2 -= (5 * (posy2 - posy1));
			}
		}
	}
	setprop("sim/walker/latitude-deg", posy2);
	setprop("sim/walker/longitude-deg", posx2);
	last_altitude = posz2;
	setprop("sim/walker/altitude-ft", posz2);
}

setlistener("sim/current-view/heading-offset-deg", func(n) {
	var c_view = getprop("sim/current-view/view-number");
	if (c_view == 0) {
		var head_v = n.getValue();
		setprop("sim/model/bluebird/crew/walker/head-offset-deg" , head_v);
	} elsif (c_view == view.indexof("Walk View")) {
		var head_v = n.getValue();
		setprop("sim/walker/model-heading-deg" , 360 - head_v);
#	} elsif (c_view == view.indexof("Walker Orbit View")) {
#		var head_v = getprop("sim/current-view/heading-offset-deg");
	}
});

var get_out = func (loc) {
	var c_view = getprop("sim/current-view/view-number");
	var head_add = 0;
	if (c_view == 0) {	# remember point of exit
		setprop("sim/walker/keep-inside-offset-x", getprop("sim/current-view/x-offset-m"));
		setprop("sim/walker/keep-inside-offset-y", getprop("sim/current-view/y-offset-m"));
		setprop("sim/walker/keep-inside-offset-z", getprop("sim/current-view/z-offset-m"));
		setprop("sim/walker/keep-pitch-offset-deg", getprop("sim/current-view/pitch-offset-deg"));
		setprop("sim/view[110]/enabled",1);
		setprop("sim/view[111]/enabled",1);
		head_add = getprop("sim/current-view/heading-offset-deg");
	}
	var c_airspeed_mps = getprop("velocities/airspeed-kt") * 0.51444444;
	var walk_dir = getprop("sim/walker/walking");
	if (walk_dir and loc == hatch_specs.rear_hatch_loc) {
		if (c_airspeed_mps > 0) {
			c_airspeed_mps -= 1;	# add momentum toward rear
		} else {
			c_airspeed_mps += 1;
		}
	}
	var c_head_deg = getprop("orientation/heading-deg");
	var c_pitch = getprop("orientation/pitch-deg");
	# for powered ejections, add to the next line the rocket thrust
	var c_z_vector_mps = sin(c_pitch) * c_airspeed_mps;
	# x and y are in meters, but z axis needs to be in feet once it enters altitude calculations
	setprop("sim/walker/starting-trajectory-z-mps", c_z_vector_mps);
	if (c_airspeed_mps < 0) {
		c_airspeed_mps = abs(c_airspeed_mps);
		c_head_deg = normheading(c_head_deg + 180);
	}
	var c_head_rad = c_head_deg * 0.01745329252;
	var c_lat = getprop("position/latitude-deg");
	var c_lon = getprop("position/longitude-deg");
	var xy_Z_factor = math.cos(c_pitch * 0.01745329252);	# factor to zero when pitch = +- 90
	var xy_lat_m = c_airspeed_mps * math.cos(c_head_rad) * xy_Z_factor;
	var xy_lon_m = c_airspeed_mps * math.sin(c_head_rad) * xy_Z_factor;
	var xy_lat = xy_lat_m * ERAD_deg;
	var xy_lon = xy_lon_m * ERAD_deg / cos(c_lat);
	setprop("sim/walker/starting-trajectory-lat", xy_lat);
	setprop("sim/walker/starting-trajectory-lon", xy_lon);
	var c_time0z_sec = math.sqrt(c_z_vector_mps * c_z_vector_mps / 9.81 / 9.81);	# time to top of arc
	if (c_z_vector_mps < 0) {	# going down
		c_time0z_sec = 0 - c_time0z_sec;
	}
	setprop("sim/walker/time-to-zero-z-sec", c_time0z_sec);
	if (getprop("logging/walker-debug")) {
		print(sprintf("get_out: traj-lat= %12.8f traj-lon= %12.8f  c_z_vector_mps= %12.8f",xy_lat,xy_lon,c_z_vector_mps));
	}
	var new_coord = hatch_specs.out_locations(loc);
	var head = normheading(abs(getprop("orientation/heading-deg") -360.00) + head_add);
	setprop("sim/walker/latitude-deg" , (getprop("position/latitude-deg")));
	setprop("sim/walker/longitude-deg" , (getprop("position/longitude-deg")));
	setprop("sim/walker/roll-deg" , (getprop("orientation/roll-deg")));
	setprop("sim/walker/pitch-deg" , (getprop("orientation/pitch-deg")));
	setprop("sim/walker/heading-deg" , (getprop("orientation/heading-deg")));
	# setprop("sim/view[100]/enabled", 1);
	# setprop("sim/view[101]/enabled", 1);
	var posy = new_coord[0];
	var posx = new_coord[1];
	var posz_ft = new_coord[2] * globals.M2FT;
	setprop("sim/walker/outside", 1);
	if (c_view == 0) {
		setprop("sim/current-view/view-number", view.indexof("Walk View"));
		setprop("sim/current-view/pitch-offset-deg", getprop("sim/walker/keep-pitch-offset-deg"));
		setprop("sim/current-view/roll-offset-deg", 0);
		setprop("sim/current-view/heading-offset-deg", head);
	}
	setprop("sim/walker/heading-deg", 0);
	setprop("sim/walker/roll-deg", 0);
	setprop("sim/walker/pitch-deg", 0);
	setprop("sim/walker/latitude-deg", new_coord[0]);
	setprop("sim/walker/longitude-deg", new_coord[1]);
	falling = 1;
	setprop("sim/walker/time-of-exit-sec", getprop("sim/time/elapsed-sec"));
	var alt1 = getprop("position/altitude-ft") + posz_ft + hatch_specs.z_floor_ft;
	setprop("sim/walker/altitude-at-exit-ft", alt1);
	setprop("sim/walker/altitude-ft" , alt1);
	measure_alt = alt1;
	if ((alt1 - getprop("position/ground-elev-ft")) > 20) {
		setprop("sim/walker/airborne", 1);
		setprop("sim/walker/parachute-equipped", 1);
	}
	setprop("sim/walker/starting-lat", new_coord[0]);
	setprop("sim/walker/starting-lon", new_coord[1]);
	walk_factor = 1.0;
	walker_model.add();
}

var get_in = func (loc) {
	walker_model.remove();
#	var c_view = getprop("sim/current-view/view-number");
	# the following section is aircraft specific for locations of entry hatches and doors
#	var new_pos = 4;
#	var c_pos = getprop("sim/model/bluebird/crew/cockpit-position");
#	if (c_view > 0) {
#		if (loc == 0) {	# find open hatch
	#			loc = 1;
	#			setprop("sim/model/bluebird/crew/cockpit-position", 4);
	#			c_pos = 5;
	#		} elsif (getprop("sim/model/bluebird/doors/door[1]/position-norm") > 0.2) {
	#			loc = 2;
	#			setprop("sim/model/bluebird/crew/cockpit-position", 4);
	#			c_pos = 5;
	#		} elsif (getprop("sim/model/bluebird/doors/door[5]/position-norm") > 0.2) {
	#			loc = 5;
	#			setprop("sim/model/bluebird/crew/cockpit-position", 4);
	#			c_pos = 5;
	#		}
	#	}
	#	if (loc >= 1) {
	#		if (c_pos == 5) {
	#			var new_walker_x = (loc == 1 ? -2.55 : (loc == 2 ? -2.55 : 10.0));
	#			var new_walker_y = (loc == 1 ? -2.8 : (loc == 2 ? 2.8 : 0));
	#		} else {
	#			if (loc == 1) {
	#				var new_walker_x = -2.55;
	#				var new_walker_y = -1.9;
	#			} elsif (loc == 2) {
	#				var new_walker_x = -2.55;
	#				var new_walker_y = 1.9;
	#			} else {
	#				var new_walker_x = getprop("sim/model/bluebird/crew/walker/x-offset-m");
	#				var new_walker_y = getprop("sim/model/bluebird/crew/walker/y-offset-m");
	#				if (new_walker_x < 9) {
	#					new_walker_x = 10.4;
	#				}
	#				if (new_walker_y < -1.6) {
	#					new_walker_y = -1.6;
	#				} elsif (new_walker_y > 1.6) {
	#					new_walker_y = 1.6;
	#				}
	#			}
	#		}
	#		var c_head_deg = getprop("orientation/heading-deg");
	#		c_pos = 5;
	#		var new_walker_h = normheading(getprop("sim/current-view/heading-offset-deg") + c_head_deg);
	#	} else {
	#		var new_walker_x = bluebird.cockpit_locations[c_pos].x;
	#		var new_walker_y = bluebird.cockpit_locations[c_pos].y;
	#		var new_walker_h = bluebird.cockpit_locations[c_pos].h;
	#	}
	#	var new_walker_z = bluebird.cockpit_locations[c_pos].z[0];
	#	var new_walker_p = bluebird.cockpit_locations[c_pos].p;
	#	var new_walker_fov = bluebird.cockpit_locations[c_pos].fov;
		# end aircraft specific
	#	if (c_view == view.indexof("Walk View")) {
	#		setprop("sim/current-view/view-number", 0);
	#		setprop("sim/current-view/z-offset-m", new_walker_x);
	#		setprop("sim/current-view/x-offset-m", new_walker_y);
	#		setprop("sim/current-view/y-offset-m", new_walker_z);
	#		setprop("sim/current-view/goal-heading-offset-deg", new_walker_h);
	#		setprop("sim/current-view/heading-offset-deg", new_walker_h);
	#		setprop("sim/current-view/goal-pitch-offset-deg", new_walker_p);
	#		setprop("sim/current-view/pitch-offset-deg", new_walker_p);
	#		setprop("sim/current-view/field-of-view", new_walker_fov);
	#	}
	#	setprop("sim/model/bluebird/crew/walker/x-offset-m", new_walker_x);
	#	setprop("sim/model/bluebird/crew/walker/y-offset-m", new_walker_y);
	#}
setprop("sim/current-view/view-number", 0);
	setprop("sim/walker/crashed", 0);
	setprop("sim/walker/airborne", 0);
	setprop("sim/walker/outside", 0);
	setprop("sim/walker/parachute-opened-altitude-ft", 0);
	parachute_deployed_sec = 0;
	setprop("sim/walker/parachute-opened-sec", 0);
	setprop("sim/view[110]/enabled", 0);
	setprop("sim/view[111]/enabled", 0);
}

var reinit_walker = func {
	setprop("sim/walker/outside", 0);
	setprop("sim/view[100]/enabled",0);
	setprop("sim/view[101]/enabled",0);
	setprop("sim/walker/crashed", 0);
	setprop("sim/walker/airborne", 0);
	falling = 0;
	walk_factor = 1.0;
	setprop("sim/walker/parachute-opened-altitude-ft", 0);
	parachute_deployed_sec = 0;
	setprop("sim/walker/parachute-opened-sec", 0);
	#setprop("sim/walker/key-triggers/outside-toggle",1);
	walker_model.remove();
}

var init_common = func {
	setlistener("sim/walker/time-of-exit-sec", func(n) exit_time_sec = n.getValue());

	setlistener("sim/walker/parachute-opened-altitude-ft", func(n) parachute_ft = n.getValue());

	setlistener("sim/walker/parachute-opened-sec", func(n) elapsed_chute_sec = n.getValue());

	setlistener("sim/walker/starting-trajectory-lat", func(n) lat_vector = n.getValue());

	setlistener("sim/walker/starting-trajectory-lon", func(n) lon_vector = n.getValue());

	setlistener("sim/walker/starting-trajectory-z-mps", func(n) z_vector_mps = n.getValue());

	setlistener("sim/walker/time-to-zero-z-sec", func(n) time_to_top_sec = n.getValue());

	setlistener("sim/walker/starting-lat", func(n) starting_lat = n.getValue());

	setlistener("sim/walker/starting-lon", func(n) starting_lon = n.getValue());

	setlistener("sim/walker/key-triggers/forward", func {
		calc_heading();
	});

	setlistener("sim/walker/key-triggers/slide", func {
		calc_heading();
	});
	
	setlistener("sim/walker/flashlight/mode", func(n) {
		var mode = n.getValue();
		
		# flashlight on/off
		if (mode == 0){
			setprop("sim/walker/flashlight/dim-factor", 0.0);
		} else {
			setprop("sim/walker/flashlight/dim-factor", getprop("sim/walker/flashlight/brightness-norm"));
		}
		
		# flashlight color
		if (mode == 2) {
			setprop("sim/walker/flashlight/color-red-factor",   1.0);
			setprop("sim/walker/flashlight/color-green-factor", 0.0);
			setprop("sim/walker/flashlight/color-blue-factor",  0.0);
		} else {
			setprop("sim/walker/flashlight/color-red-factor",   1.0);
			setprop("sim/walker/flashlight/color-green-factor", 1.0);
			setprop("sim/walker/flashlight/color-blue-factor",  1.0);
		}
	}, 1);

	setlistener("sim/model/bluebird/crew/walker/visible", func(n) {
		if (n.getValue()) {
			if (getprop("sim/walker/outside")) {
				walker_model.add();
			}
		} else {
			walker_model.remove();
		}
	});

	setlistener("sim/current-view/view-number", func(n) {
		if (n.getValue() == view.indexof("Walk View")) {
			yViewNode.setValue(0);
			zViewNode.setValue(1.67);	# matches person height when inside due to aircraft offsets
			xViewNode.setValue(0);
		}
	});

	setlistener("sim/signals/reinit", func {
		reinit_walker();
	});

	setlistener("sim/signals/fdm-initialized", func {
		reinit_walker();
	});
}
settimer(init_common,0);

var theme_dialog = gui.OverlaySelector.new("Select Theme", "Aircraft/Generic/Human/Models/Themes", "sim/walker/theme-name", nil, "sim/multiplay/generic/string[10]");
var equip_dialog = gui.OverlaySelector.new("Select Equipment", "Aircraft/Generic/Human/Models/equipment/Themes", "sim/walker/equipment-name", nil, "sim/multiplay/generic/string[11]");