Walker: fix Copilot Outfit error, Generalization
[fg:toms-fgdata.git] / Aircraft / Generic / Human / Nasal / walk.nas
1 # == walking functions v4.3 for FlightGear version 1.9-2.0 OSG ==
2 # == plus coordinates for Bluebird Explorer Hovercraft 10.4    ==
3
4 # aircraft specific section:
5 var hatch_specs = {
6         z_opening_ft: 6.0,
7         z_floor_ft: 0,  #  z-axis offset for walker exit from aircraft origin
8                         #  generally floor z-axis converted to ft.
9         rear_hatch_loc: 5,
10         out_locations: func (loc) {
11                         if (loc == 0) {         # exit but not by hatch
12
13                                 # get default exit coordinates from set file
14
15                                 var defx = getprop ("sim/model/map/default_exit/x-offset-m");
16                                 var defy = getprop ("sim/model/map/default_exit/y-offset-m");
17
18                 #               if no exit is specified in set file, start at 0,0
19
20                                 if (defx == nil ) {
21                                         defx =0;
22                                 }
23                                 if (defy == nil ) {
24                                         defy =0;
25                                 }
26
27                                 var new_coord = xy2LatLonZ(defx,defy);  
28                         } elsif (loc == 1) {
29                                 var new_coord = xy2LatLonZ(getprop("sim/model/bluebird/crew/walker/x-offset-m"),-4.9);
30                         } elsif (loc == 2) {
31                                 var new_coord = xy2LatLonZ(getprop("sim/model/bluebird/crew/walker/x-offset-m"),4.9);
32                         } elsif (loc == 5) {
33                                 var new_coord = xy2LatLonZ(11.0,getprop("sim/model/bluebird/crew/walker/y-offset-m"));
34                         } else {
35                                 var new_coord = xy2LatLonZ(xViewNode.getValue(),yViewNode.getValue());
36                         }
37                         return new_coord;
38         }
39 };
40 # end aircraft specific
41
42 var sin = func(a) { math.sin(a * math.pi / 180.0) }     # degrees
43 var cos = func(a) { math.cos(a * math.pi / 180.0) }
44 var asin = func(y) { math.atan2(y, math.sqrt(1-y*y)) }  # radians
45 var ERAD = 6378138.12;          # Earth radius (m)
46 var ERAD_deg = 180 / (ERAD * math.pi);
47 var DEG2RAD = math.pi / 180;
48 var xViewNode = props.globals.getNode("sim/current-view/z-offset-m", 1);
49 var yViewNode = props.globals.getNode("sim/current-view/x-offset-m", 1);
50 var zViewNode = props.globals.getNode("sim/current-view/y-offset-m", 1);
51 var fps_node = props.globals.getNode("sim/frame-rate", 1);
52 var falling = 0;        # 0/1 = false/true
53 var last_altitude = 0;  # remember last position to detect falling from ground
54 var exit_time_sec = 0.0;
55 var parachute_ft = 0.0;
56 var parachute_deployed_sec = 0.0;
57 var elapsed_chute_sec = 0.0;
58 var lat_vector = 0.0;
59 var lon_vector = 0.0;
60 var z_vector_mps = 0.0;
61 var time_to_top_sec = 0.0;
62 var starting_lat = 0.0;
63 var starting_lon = 0.0;
64 var walk_heading = 0;
65 var walk_watch = 0;
66 var walk_factor = 1.0;
67
68 # debug section
69 var measure_main_count = 0;
70 var measure_walk_count = 0;     # momentum_walk loops
71 var measure_extmov_count = 0;
72 var measure_sec = 0;
73 var measure_alt = 0;
74 var last_elapsed_sec = 0;
75
76 # ===== functions ======
77 var normheading = func (a) {
78         while (a >= 360)
79                 a -= 360;
80         while (a < 0)
81                 a += 360;
82         return a;
83 }
84
85 var distFromCraft = func (lat,lon) {
86         var c_lat = getprop("position/latitude-deg");
87         var c_lon = getprop("position/longitude-deg");
88         var a = sin((lat - c_lat) * 0.5);
89         var b = sin((lon - c_lon) * 0.5);
90         return 2.0 * ERAD * asin(math.sqrt(a * a + cos(lat) * cos(c_lat) * b * b));
91 }
92
93 var xy2LatLonZ = func (x,y) {
94         # given the x,y offsets of the cockpit view when walking (in meters)
95         # or the hatch location upon exit
96         # translate into lat,lon,z-offset-m for transfer to outside walker
97         var c_head_rad = getprop("orientation/heading-deg") * DEG2RAD;
98         var c_lat = getprop("position/latitude-deg");
99         var c_lon = getprop("position/longitude-deg");
100 #       var c_gnd_pitch = getprop("orientation/ground-pitch");
101         var c_pitch = getprop("orientation/pitch-deg");
102         var c_roll = getprop("orientation/roll-deg");
103         var xZ_factor = math.cos(c_pitch * DEG2RAD);
104         var x_Zadjust = x * xZ_factor;  # adjusted for pitch
105         var y_Zadjust = y * math.cos(c_roll * DEG2RAD); # adjusted for roll
106 #       print(sprintf("W.xy2 x= %10.6f xZ= %10.6f  y= %10.6f yZ= %10.6f",x,x_Zadjust,y,y_Zadjust));
107         var xy_hyp = math.sqrt((x_Zadjust * x_Zadjust) + (y_Zadjust * y_Zadjust));
108         var a = (xy_hyp == 0 ? 0 : asin(y_Zadjust / xy_hyp));
109         if (x > 0) {
110                 a = math.pi - a;
111         }
112         var xy_head_rad = c_head_rad + a;
113 #       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)));
114         var xy_lat_m = xy_hyp * math.cos(xy_head_rad);
115         var xy_lon_m = xy_hyp * math.sin(xy_head_rad);
116 #       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));
117         var xy_lat = xy_lat_m * ERAD_deg;
118         var xy_lon = xy_lon_m * ERAD_deg / cos(c_lat);
119 #       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));
120         var zxZ_m = -(x * math.sin(c_pitch * DEG2RAD));
121         var zyZ_m = -(y * math.sin(c_roll * DEG2RAD) * xZ_factor);      # goes to zero as pitch to 90
122
123 # MARK-pre-10.4: not Perfect yet: z of hatch and height of walker (1.67m) is not adjusted for at angle.
124 #       print (sprintf ("W.xy2  zxZ= %10.6f zyZ= %10.6f z= %10.6f",zxZ_m,zyZ_m,(zxZ_m + zyZ_m)));
125         return [(c_lat + xy_lat) , (c_lon + xy_lon) , (zxZ_m + zyZ_m)];
126 }
127
128 var calc_heading = func {
129         var w_forward = getprop("sim/walker/key-triggers/forward");
130         var w_left = getprop("sim/walker/key-triggers/slide");
131         var new_head = -999;
132         var new_walking = 0;
133         if (w_forward > 0) {
134                 new_walking = 1;
135                 if (w_left < 0) {
136                         new_head = 45;
137                 } elsif (w_left > 0) {
138                         new_head = -45;
139                 } else {
140                         new_head = 0;
141                 }
142         } elsif (w_forward < 0) {
143                 new_walking = -1;
144                 if (w_left < 0) {
145                         new_head = 135;
146                 } elsif (w_left > 0) {
147                         new_head = -135;
148                 } else {
149                         new_head = 180;
150                 }
151         } else {
152                 if (w_left < 0) {
153                         new_walking = 4;
154                         new_head = 90;
155                 } elsif (w_left > 0) {
156                         new_walking = 6;
157                         new_head = -90;
158                 } else {
159                         setprop ("sim/walker/walking", 0);
160                         return 0;
161                 }
162         }
163         walk_heading = new_head;
164         setprop ("sim/walker/walking", new_walking);
165 }
166
167 var momentum_walk = func {
168         measure_walk_count += 1;
169         if (walk_watch >= 3) {
170                 if (walk_factor < 2.0) {        # speed up when holding down key
171                         walk_factor += 0.025;
172                 }
173                 setprop ("sim/walker/walking-momentum", 1);
174         } elsif (walk_watch >= 2) {
175                 setprop ("sim/walker/walking-momentum", 1);
176                 walk_watch -= 1;
177         } else {
178                 walk_factor = ((walk_factor - 1.0) * 0.5) + 1.0;
179                 if (walk_factor < 1.1) {
180                         walk_factor = 1.0;
181                         walk_watch = 0;
182                 } else {
183                         setprop ("sim/walker/walking-momentum", 1);
184                 }
185         }
186         if (walk_watch) {
187                 settimer(momentum_walk,0.05);
188         } else {
189                 setprop ("sim/walker/walking-momentum", 0);
190         }
191 }
192
193 var main_loop = func {
194         measure_main_count += 1;
195         var c_view = getprop ("sim/current-view/view-number");
196         var moved = 0;
197         if (c_view == 0 and getprop("sim/walker/walking-momentum")) {
198                 # inside aircraft
199 #               bluebird.walk_about_cabin(0.1, walk_heading);
200                 moved = 1;
201         }
202         if (getprop("sim/walker/outside")) {
203                 if (falling or getprop("sim/walker/walking-momentum")) {
204                         ext_mov(moved);
205                 }
206                 # check for proximity to hatches for entry after 0.75 sec.
207                 var elapsed_sec = getprop("sim/time/elapsed-sec");
208                 var elapsed_fall_sec = elapsed_sec - exit_time_sec;
209                 if (elapsed_fall_sec > 0.75) {
210                         if (abs(getprop("sim/walker/altitude-ft") - getprop("position/altitude-ft") + hatch_specs.z_opening_ft) < 7) {
211                                 # must be within (opening) ft vertically to climb in
212                                 var posy = getprop("sim/walker/latitude-deg");
213                                 var posx = getprop("sim/walker/longitude-deg");
214
215                                 # the following section is aircraft specific for locations of entry hatches and doors
216         #                       if (getprop("sim/model/bluebird/doors/door[0]/position-norm") > 0.73) {
217         #                               var door0_ll = xy2LatLonZ(-2.6,-3.42);
218         #                               var a0 = sin((door0_ll[0] - posy) * 0.5);
219         #                               var b0 = sin((door0_ll[1] - posx) * 0.5);
220                                         # doesn't actually check z-axis, mis-alignments in rare orientations
221         #                               var d0 = 2.0 * ERAD * asin(math.sqrt(a0 * a0 + cos(door0_ll[0]) * cos(posy) * b0 * b0));
222         #                               if (d0 < 1.2) {
223         #                                       get_in(1);
224         #                               }
225         #                       }
226         #                       if (getprop("sim/model/bluebird/doors/door[1]/position-norm") > 0.73) {
227         #                               var door1_ll = xy2LatLonZ(-2.6,3.42);
228         #                               var a1 = sin((door1_ll[0] - posy) * 0.5);
229         #                               var b1 = sin((door1_ll[1] - posx) * 0.5);
230         #                               var d1 = 2.0 * ERAD * asin(math.sqrt(a1 * a1 + cos(door1_ll[0]) * cos(posy) * b1 * b1));
231         #                               if (d1 < 1.2) {
232         #                                       get_in(2);
233         #                               }
234         #                       }
235         #                       if (getprop("sim/model/bluebird/doors/door[5]/position-norm") > 0.78) {
236         #                               var door5_ll = xy2LatLonZ(9.0,0);
237         #                               var a5 = sin((door5_ll[0] - posy) * 0.5);
238         #                               var b5 = sin((door5_ll[1] - posx) * 0.5);
239         #                               var d5 = 2.0 * ERAD * asin(math.sqrt(a5 * a5 + cos(door5_ll[0]) * cos(posy) * b5 * b5));
240         #                               if (d5 < 1.9) {
241         #                                       get_in(5);
242         #                               }
243         #                       }
244                                 # end aircraft specific
245
246                         }
247                 }
248         } elsif (!moved and getprop("sim/walker/walking-momentum")) {
249 #               bluebird.walk_about_cabin(0.1, walk_heading);
250         }
251
252         if (getprop("logging/walker-debug") and getprop("sim/walker/outside")) {
253                 var elapsed_sec = getprop("sim/time/elapsed-sec");
254                 var t = elapsed_sec - measure_sec;
255                 if (t >= 0.991) {
256                         var posz1 = getprop("sim/walker/altitude-ft");
257                         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)));
258                         measure_alt = posz1;
259                         measure_sec = elapsed_sec;
260                         measure_main_count = 0;
261                         measure_walk_count = 0;
262                         measure_extmov_count = 0;
263                 }
264         }
265
266         settimer(main_loop, 0.01)
267 }
268
269 var walker_model = {
270         add:    func {
271                         if (getprop("sim/model/crew/walker/visible")) {
272                                 if (getprop("logging/walker-position")) {
273                                         print("walker_model.add");
274                                 }
275                                 props.globals.getNode("models/model/path", 1);
276                                 props.globals.getNode("models/model/longitude-deg-prop", 1);
277                                 props.globals.getNode("models/model/latitude-deg-prop", 1);
278                                 props.globals.getNode("models/model/elevation-ft-prop", 1);
279                                 props.globals.getNode("models/model/heading-deg-prop", 1);
280                                 
281                                 setprop ("models/model/path", "Aircraft/Generic/Human/Models/walker.xml");
282                                 setprop ("models/model/longitude-deg-prop", "sim/walker/longitude-deg");
283                                 setprop ("models/model/latitude-deg-prop", "sim/walker/latitude-deg");
284                                 setprop ("models/model/elevation-ft-prop", "sim/walker/altitude-ft");
285                                 setprop ("models/model/heading-deg-prop", "sim/walker/model-heading-deg");
286                                 props.globals.getNode("models/model/load", 1);
287                         }
288                 },
289         remove: func {
290                         if (getprop("logging/walker-position")) {
291                                 print("walker_model.remove");
292                         }
293                         props.globals.getNode("models", 1).removeChild("model", 0);
294                         walker_model.reset_fall();
295                 },
296         reset_fall: func {
297                         falling = 0;
298                         walk_factor = 1.0;
299                         setprop("sim/walker/airborne", 0);
300                         setprop("sim/walker/parachute-opened-altitude-ft", 0);
301                         parachute_deployed_sec = 0;
302                         setprop("sim/walker/parachute-opened-sec", 0);
303                         setprop("sim/walker/starting-trajectory-lat", 0.0);
304                         setprop("sim/walker/starting-trajectory-lon", 0.0);
305                         setprop("sim/walker/starting-trajectory-z-mps", 0.0);
306                         setprop("sim/walker/time-to-zero-z-sec", 0.0);
307                 },
308         land:   func (lon,lat,alt) {
309                         walker_model.reset_fall();
310                         setprop("sim/walker/latitude-deg", lat);
311                         setprop("sim/walker/longitude-deg", lon);
312                         setprop("sim/walker/altitude-ft", alt);
313                         last_altitude = alt;
314                 },
315 };
316
317 var open_chute = func {
318         if (getprop("sim/walker/airborne") and exit_time_sec and !parachute_ft and getprop("sim/walker/parachute-equipped")) {
319                 setprop("sim/walker/parachute-opened-altitude-ft", getprop("sim/walker/altitude-ft"));
320                 parachute_deployed_sec = getprop("sim/time/elapsed-sec");
321                 setprop("sim/walker/parachute-opened-sec", 0);
322         }
323 }
324
325 setlistener("sim/walker/walking", func(n) {
326         var wdir = n.getValue();
327         if (wdir) {     # repetitive input or bug in mod-up keeps triggering
328                 walk_dir = wdir;        # remember current direction
329                 if (walk_watch == 0) {
330                         walk_watch = 3; # start or reset timer for countdown
331                         momentum_walk();        # starting from rest, start new loop
332                 } else {
333                         walk_watch = 3; # reset watcher
334                 }
335         } else {
336                 # last heard was zero
337                 walk_watch -= 1;
338                 if (walk_watch < 0) {
339                         walk_watch = 0;
340                 }
341         }
342 });
343
344 setlistener("sim/walker/key-triggers/outside-toggle", func {
345         var c_view = getprop ("sim/current-view/view-number");
346         if (c_view == 0) {
347                 if (getprop("sim/walker/outside")) {
348                         setprop("sim/current-view/view-number", view.indexof("Walk View"));
349
350                 } else {
351                         get_out(0);
352                 }
353         } elsif (c_view == view.indexof("Walk View")) {
354                 get_in(0);
355         } else {
356                 if (getprop("sim/walker/outside")) {
357                         get_in(0);
358                 } else {
359                         get_out(0);
360                 }
361         }
362 });
363
364 var ext_mov = func (moved) {
365         measure_extmov_count += 1;
366         var c_view = getprop("sim/current-view/view-number");
367         if (c_view == view.indexof("Walker Orbit View")) {
368                 var head_v = 360 - getprop("sim/walker/model-heading-deg");
369         } else {
370                 var head_v = getprop("sim/current-view/heading-offset-deg");
371         }
372         var c_head_deg = getprop("orientation/heading-deg");
373         var posy1 = getprop("sim/walker/latitude-deg");
374         var posx1 = getprop("sim/walker/longitude-deg");
375         var posz1 = getprop("sim/walker/altitude-ft");
376         var posx2 = posx1;      # new after calculations
377         var posy2 = posy1;
378         var posz2 = posz1;
379         var check_movement = 1;
380         var fps = fps_node.getValue();
381         fps = (fps < 10 ? 10 : fps);    # only realistic above 10fps. Slow down below that so that walker pauses instead of jumping.
382         var speed = getprop("sim/walker/key-triggers/speed") * walk_factor / fps;
383         if (c_view >= 1 and c_view <=3) {
384                 head_v = normheading(360 - c_head_deg + head_v);
385         } elsif (c_view == 5) {
386                 head_v = normheading(c_head_deg + head_v + 90);
387         }
388         var head_w = normheading(head_v + walk_heading);
389         if (!moved) {
390                 setprop("sim/walker/model-heading-deg" , 360 - head_v);
391         }
392         var elapsed_sec = getprop("sim/time/elapsed-sec");
393         if (falling) {
394                 var elapsed_fall_sec = elapsed_sec - exit_time_sec;
395                 if (elapsed_fall_sec > 0.1) {
396                         check_movement = 0;
397                 }
398         }
399         if (check_movement and !moved) {
400                 var lat_m = speed * cos(head_w);
401                 var lon_m = speed * sin(head_w);
402                 var lat3 = lat_m * ERAD_deg;
403                 var lon3 = lon_m * ERAD_deg / cos(posy1);
404                 posx2 = posx1 - lon3;   # heading is offset or reversed west to east
405                 posy2 = (posy1 < 0 ? posy1 - lat3 : posy1 + lat3);
406                                 #        southern or northern hemisphere
407         }
408         if (falling) {  # add movement from aircraft upon jumping
409                 var parabola = 0;
410                 var parachute_drag = 0;
411                 var zero_xy_sec = (elapsed_fall_sec < 10 ? elapsed_fall_sec : 10.0);
412                 if (parachute_ft) {     # chute open
413                         setprop("sim/walker/parachute-opened-sec", (elapsed_sec - parachute_deployed_sec - 1.0));
414                         # chute starts to add drag at 1 second, fully open at 3 sec. Slows to 17 ft/sec
415                         if (elapsed_chute_sec >= 3.0) {
416                                 parachute_drag = 0.9;
417                         } elsif (elapsed_chute_sec >= 0.0) {    # 1 second delay for chute to deploy
418                                 parachute_drag = sin(elapsed_chute_sec * 30) * 0.9;     # 0 to 0.9 in 3 sec.
419                         }
420                 }
421                 if (parachute_drag) {
422                         zero_xy_sec = parachute_deployed_sec - exit_time_sec + 1.0;
423                         if (zero_xy_sec > 10.0) {
424                                 zero_xy_sec = 10.0;
425                         }
426                 }
427                 parabola = sin(90 - zero_xy_sec ) * zero_xy_sec - (0.096 * zero_xy_sec * zero_xy_sec / 2);
428                 if (parachute_drag and zero_xy_sec < 7) {
429                         posy2 = starting_lat + (lat_vector * parabola) + (lat_vector * parachute_drag);
430                         posx2 = starting_lon + (lon_vector * parabola) + (lon_vector * parachute_drag);
431                 } else {
432                         posy2 = starting_lat + (lat_vector * parabola);
433                         posx2 = starting_lon + (lon_vector * parabola);
434                 }
435         }
436         var posz_geo = geo.elevation(posy2, posx2, ((posz1 * 0.3048) + 2));
437         if (posz_geo == nil) {
438                 posz_geo = geo.elevation(posy2, posx2); # underground? try without current walker altitude
439                 if (posz_geo == nil) {
440                         posz_geo = 0;
441                         print(sprintf("Error. Attempting to move to latitude %13.8f longitude %13.8f",posy2,posx2));
442                 }
443         }
444         posz_geo = posz_geo / 0.3048;   # convert to ft
445 #       if (getprop("logging/walker-debug")) {
446 #               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));
447 #       }
448         if (falling) {  # 13,000 to 12,000 ft = 10 sec. 12,000 - 4,000 = 44 sec.
449                         # 5.5 sec to cover each 1000 ft at terminal velocity (ignoring altitude density and surface area)
450                 var dist_traveled_z = 0;        # feet
451                 if (posz_geo < posz1) { # ground is below walker
452                         dist_traveled_z = -32.185 * time_to_top_sec * time_to_top_sec / 2;      # upward half of arc
453                         var elapsed1 = elapsed_fall_sec - time_to_top_sec;
454                                 # excludes wind resistance and cross section of projectile. Assume negligible for now.
455                         if (elapsed_fall_sec > time_to_top_sec) {       # past zero_z and falling
456                                 # drag constant is actually 0.515 kg/s for spread eagle, and 0.067 for feet first.
457                                 # I am ignoring these distinctions for now, until a more complex formula can be made,
458                                 # to go along with the new walker model visibility.
459                                 # also needs to be improved is loss of acceleration due to drag forces
460                                 if (elapsed_fall_sec > (time_to_top_sec + 5.358)) {     # time to reach terminal velocity
461                                         dist_traveled_z += 461.99 + ((elapsed1 - 5.358) * 172);
462                                 } else {        # 9.81m/s/s up to terminal velocity 172ft/s 54m/s spread eagle
463                                         dist_traveled_z += 32.185 * elapsed1 * elapsed1 / 2;
464                                 }
465                         } else {        # started going up, arch to zero_z before falling
466                                 dist_traveled_z += 32.185 * elapsed1 * elapsed1 / 2;
467                                 if (getprop("logging/walker-debug")) {
468                                         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)));
469                                 }
470                         }
471                         if (parachute_ft) {     # chute open
472                                 # need to better model deceleration due to opening of chute, change in surface area.
473                                 var subtract_z = 0;
474                                 if (elapsed_chute_sec >= 5.0) {
475                                         subtract_z = 363.14 + ((elapsed_chute_sec - 5.0) * 155);
476                                 } elsif (elapsed_chute_sec >= 2.0) {
477                                         subtract_z = 32.15 * parachute_drag * elapsed_chute_sec * elapsed_chute_sec / 2;
478                                 } elsif (elapsed_chute_sec >= 0.0) {
479                                         subtract_z = 32.15 * parachute_drag * elapsed_chute_sec * elapsed_chute_sec / 2;
480                                 }
481                                 dist_traveled_z -= subtract_z;
482                         }
483                         posz2 = getprop("sim/walker/altitude-at-exit-ft") - dist_traveled_z;
484                         if (posz2 < posz_geo) { # below ground
485                                 if ((parachute_drag < 0.7) and (dist_traveled_z > 20)) {
486                                         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")));
487                                         if (getprop("sim/current-view/view-number") == view.indexof("Walk View")) {
488                                                 setprop("sim/current-view/pitch-offset-deg", -80);
489                                         }
490                                         setprop("sim/model/bluebird/position/landing-wow", 1);
491                                         setprop("sim/walker/crashed", 1);
492                                         setprop("sim/walker/airborne", 0);
493                                 }
494                                 posz2 = posz_geo;
495                                 walker_model.land(posx2,posy2,posz_geo);
496                         } else {
497                                 if (getprop("logging/walker-position")) {
498                                         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));
499                                         measure_alt = posz2;
500                                         last_elapsed_sec = elapsed_sec;
501                                 }
502                         }
503                 } else {
504                         walker_model.land(posx2,posy2,posz_geo);
505                         posz2 = posz_geo;
506                 }
507         } else {        # not falling
508                 # check for sudden change in ground elevation
509                 if ((abs(posz1 - last_altitude) > 1.6) or ((posz_geo + 1.6) < posz1)) {
510                         setprop("sim/walker/time-of-exit-sec", getprop("sim/time/elapsed-sec"));
511                         setprop("sim/walker/altitude-at-exit-ft", last_altitude);
512                         # add "forward" momentum upon step out and down
513                         var lat_m = getprop("sim/walker/key-triggers/speed") * walk_factor * cos(head_w);
514                         var lon_m = getprop("sim/walker/key-triggers/speed") * walk_factor * (0 - sin(head_w));
515                         var lat3 = lat_m * ERAD_deg;
516                         var lon3 = lon_m * ERAD_deg / cos(posy1);
517                         posy3 = posy1 + lat3;
518                         posx3 = posx1 + lon3;
519                         setprop("sim/walker/starting-trajectory-lat", lat3);
520                         setprop("sim/walker/starting-trajectory-lon", lon3);
521                         setprop("sim/walker/starting-lat", posy2);
522                         setprop("sim/walker/starting-lon", posx2);
523                         setprop("sim/walker/latitude-deg", posy3);
524                         setprop("sim/walker/longitude-deg", posx3);
525                         setprop("sim/walker/starting-trajectory-z-mps", 0.0);
526                         falling = 1;
527                         if ((posz1 - posz_geo) > 50) {
528                                 setprop("sim/walker/airborne", 1);
529                                 setprop("sim/walker/parachute-equipped", 0);
530                         }
531                 }
532                 if (!falling) {
533                         if (posz_geo < (posz1 + 5)) {   # walking, ground within 10 ft below walker
534                                 var posz3_geo = geo.elevation(posy2, posx2, ((posz1 * 0.3048) + 2));
535                                 if (posz3_geo == nil) {
536                                         posz3_geo = 0;
537                                 }
538                                 posz3_geo = posz3_geo / 0.3048; # convert to ft
539                                 var posz_geodiff = abs(posz_geo - posz1);
540                                 if ((posz3_geo - posz_geo) > 1.5 and posz_geodiff < 7 and posz_geodiff > 1.5) { # under object and nearly hitting head
541                                         print(sprintf ("Stopped by overhead obstruction, has height %6.2f ft above your position.", posz_geodiff));
542                                         posx2 -= (5 * (posx2 - posx1));
543                                         posy2 -= (5 * (posy2 - posy1)); # fall backward and reload ground level
544                                         posz_geo = geo.elevation(posy2, posx2, ((posz1 * 0.3048) + 1));
545                                         if (posz_geo == nil) {
546                                                 posz_geo = 0;
547                                         }
548                                         posz_geo = posz_geo / 0.3048;   # convert to ft
549                                 }
550                                 if ((posz1+0.3) > posz_geo or (posz1-0.3) < posz_geo) { # smoothen stride
551                                         interpolate ("sim/walker/altitude-ft", posz_geo, 0.25, 0.3);
552                                         posz2 = getprop("sim/walker/altitude-ft");
553                                 }
554                                 if (getprop("logging/walker-position")) {
555                                         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));
556                                 }
557                         } else {
558                                 print(sprintf ("Stopped by wall, has height %6.2f ft above your position.",(posz1-posz_geo)));
559                                 posx2 -= (5 * (posx2 - posx1));
560                                 posy2 -= (5 * (posy2 - posy1));
561                         }
562                 }
563         }
564         setprop("sim/walker/latitude-deg", posy2);
565         setprop("sim/walker/longitude-deg", posx2);
566         last_altitude = posz2;
567         setprop("sim/walker/altitude-ft", posz2);
568 }
569
570 setlistener("sim/current-view/heading-offset-deg", func(n) {
571         var c_view = getprop("sim/current-view/view-number");
572         if (c_view == 0) {
573                 var head_v = n.getValue();
574                 setprop("sim/model/bluebird/crew/walker/head-offset-deg" , head_v);
575         } elsif (c_view == view.indexof("Walk View")) {
576                 var head_v = n.getValue();
577                 setprop("sim/walker/model-heading-deg" , 360 - head_v);
578 #       } elsif (c_view == view.indexof("Walker Orbit View")) {
579 #               var head_v = getprop("sim/current-view/heading-offset-deg");
580         }
581 });
582
583 var get_out = func (loc) {
584         var c_view = getprop("sim/current-view/view-number");
585         var head_add = 0;
586         if (c_view == 0) {      # remember point of exit
587                 setprop("sim/walker/keep-inside-offset-x", getprop("sim/current-view/x-offset-m"));
588                 setprop("sim/walker/keep-inside-offset-y", getprop("sim/current-view/y-offset-m"));
589                 setprop("sim/walker/keep-inside-offset-z", getprop("sim/current-view/z-offset-m"));
590                 setprop("sim/walker/keep-pitch-offset-deg", getprop("sim/current-view/pitch-offset-deg"));
591                 setprop("sim/view[110]/enabled",1);
592                 setprop("sim/view[111]/enabled",1);
593                 head_add = getprop("sim/current-view/heading-offset-deg");
594         }
595         var c_airspeed_mps = getprop("velocities/airspeed-kt") * 0.51444444;
596         var walk_dir = getprop("sim/walker/walking");
597         if (walk_dir and loc == hatch_specs.rear_hatch_loc) {
598                 if (c_airspeed_mps > 0) {
599                         c_airspeed_mps -= 1;    # add momentum toward rear
600                 } else {
601                         c_airspeed_mps += 1;
602                 }
603         }
604         var c_head_deg = getprop("orientation/heading-deg");
605         var c_pitch = getprop("orientation/pitch-deg");
606         # for powered ejections, add to the next line the rocket thrust
607         var c_z_vector_mps = sin(c_pitch) * c_airspeed_mps;
608         # x and y are in meters, but z axis needs to be in feet once it enters altitude calculations
609         setprop("sim/walker/starting-trajectory-z-mps", c_z_vector_mps);
610         if (c_airspeed_mps < 0) {
611                 c_airspeed_mps = abs(c_airspeed_mps);
612                 c_head_deg = normheading(c_head_deg + 180);
613         }
614         var c_head_rad = c_head_deg * 0.01745329252;
615         var c_lat = getprop("position/latitude-deg");
616         var c_lon = getprop("position/longitude-deg");
617         var xy_Z_factor = math.cos(c_pitch * 0.01745329252);    # factor to zero when pitch = +- 90
618         var xy_lat_m = c_airspeed_mps * math.cos(c_head_rad) * xy_Z_factor;
619         var xy_lon_m = c_airspeed_mps * math.sin(c_head_rad) * xy_Z_factor;
620         var xy_lat = xy_lat_m * ERAD_deg;
621         var xy_lon = xy_lon_m * ERAD_deg / cos(c_lat);
622         setprop("sim/walker/starting-trajectory-lat", xy_lat);
623         setprop("sim/walker/starting-trajectory-lon", xy_lon);
624         var c_time0z_sec = math.sqrt(c_z_vector_mps * c_z_vector_mps / 9.81 / 9.81);    # time to top of arc
625         if (c_z_vector_mps < 0) {       # going down
626                 c_time0z_sec = 0 - c_time0z_sec;
627         }
628         setprop("sim/walker/time-to-zero-z-sec", c_time0z_sec);
629         if (getprop("logging/walker-debug")) {
630                 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));
631         }
632         var new_coord = hatch_specs.out_locations(loc);
633         var head = normheading(abs(getprop("orientation/heading-deg") -360.00) + head_add);
634         setprop("sim/walker/latitude-deg" , (getprop("position/latitude-deg")));
635         setprop("sim/walker/longitude-deg" , (getprop("position/longitude-deg")));
636         setprop("sim/walker/roll-deg" , (getprop("orientation/roll-deg")));
637         setprop("sim/walker/pitch-deg" , (getprop("orientation/pitch-deg")));
638         setprop("sim/walker/heading-deg" , (getprop("orientation/heading-deg")));
639         # setprop("sim/view[100]/enabled", 1);
640         # setprop("sim/view[101]/enabled", 1);
641         var posy = new_coord[0];
642         var posx = new_coord[1];
643         var posz_ft = new_coord[2] * globals.M2FT;
644         setprop("sim/walker/outside", 1);
645         if (c_view == 0) {
646                 setprop("sim/current-view/view-number", view.indexof("Walk View"));
647                 setprop("sim/current-view/pitch-offset-deg", getprop("sim/walker/keep-pitch-offset-deg"));
648                 setprop("sim/current-view/roll-offset-deg", 0);
649                 setprop("sim/current-view/heading-offset-deg", head);
650         }
651         setprop("sim/walker/heading-deg", 0);
652         setprop("sim/walker/roll-deg", 0);
653         setprop("sim/walker/pitch-deg", 0);
654         setprop("sim/walker/latitude-deg", new_coord[0]);
655         setprop("sim/walker/longitude-deg", new_coord[1]);
656         falling = 1;
657         setprop("sim/walker/time-of-exit-sec", getprop("sim/time/elapsed-sec"));
658         var alt1 = getprop("position/altitude-ft") + posz_ft + hatch_specs.z_floor_ft;
659         setprop("sim/walker/altitude-at-exit-ft", alt1);
660         setprop("sim/walker/altitude-ft" , alt1);
661         measure_alt = alt1;
662         if ((alt1 - getprop("position/ground-elev-ft")) > 20) {
663                 setprop("sim/walker/airborne", 1);
664                 setprop("sim/walker/parachute-equipped", 1);
665         }
666         setprop("sim/walker/starting-lat", new_coord[0]);
667         setprop("sim/walker/starting-lon", new_coord[1]);
668         walk_factor = 1.0;
669         walker_model.add();
670 }
671
672 var get_in = func (loc) {
673         walker_model.remove();
674 #       var c_view = getprop("sim/current-view/view-number");
675         # the following section is aircraft specific for locations of entry hatches and doors
676 #       var new_pos = 4;
677 #       var c_pos = getprop("sim/model/bluebird/crew/cockpit-position");
678 #       if (c_view > 0) {
679 #               if (loc == 0) { # find open hatch
680         #                       loc = 1;
681         #                       setprop("sim/model/bluebird/crew/cockpit-position", 4);
682         #                       c_pos = 5;
683         #               } elsif (getprop("sim/model/bluebird/doors/door[1]/position-norm") > 0.2) {
684         #                       loc = 2;
685         #                       setprop("sim/model/bluebird/crew/cockpit-position", 4);
686         #                       c_pos = 5;
687         #               } elsif (getprop("sim/model/bluebird/doors/door[5]/position-norm") > 0.2) {
688         #                       loc = 5;
689         #                       setprop("sim/model/bluebird/crew/cockpit-position", 4);
690         #                       c_pos = 5;
691         #               }
692         #       }
693         #       if (loc >= 1) {
694         #               if (c_pos == 5) {
695         #                       var new_walker_x = (loc == 1 ? -2.55 : (loc == 2 ? -2.55 : 10.0));
696         #                       var new_walker_y = (loc == 1 ? -2.8 : (loc == 2 ? 2.8 : 0));
697         #               } else {
698         #                       if (loc == 1) {
699         #                               var new_walker_x = -2.55;
700         #                               var new_walker_y = -1.9;
701         #                       } elsif (loc == 2) {
702         #                               var new_walker_x = -2.55;
703         #                               var new_walker_y = 1.9;
704         #                       } else {
705         #                               var new_walker_x = getprop("sim/model/bluebird/crew/walker/x-offset-m");
706         #                               var new_walker_y = getprop("sim/model/bluebird/crew/walker/y-offset-m");
707         #                               if (new_walker_x < 9) {
708         #                                       new_walker_x = 10.4;
709         #                               }
710         #                               if (new_walker_y < -1.6) {
711         #                                       new_walker_y = -1.6;
712         #                               } elsif (new_walker_y > 1.6) {
713         #                                       new_walker_y = 1.6;
714         #                               }
715         #                       }
716         #               }
717         #               var c_head_deg = getprop("orientation/heading-deg");
718         #               c_pos = 5;
719         #               var new_walker_h = normheading(getprop("sim/current-view/heading-offset-deg") + c_head_deg);
720         #       } else {
721         #               var new_walker_x = bluebird.cockpit_locations[c_pos].x;
722         #               var new_walker_y = bluebird.cockpit_locations[c_pos].y;
723         #               var new_walker_h = bluebird.cockpit_locations[c_pos].h;
724         #       }
725         #       var new_walker_z = bluebird.cockpit_locations[c_pos].z[0];
726         #       var new_walker_p = bluebird.cockpit_locations[c_pos].p;
727         #       var new_walker_fov = bluebird.cockpit_locations[c_pos].fov;
728                 # end aircraft specific
729         #       if (c_view == view.indexof("Walk View")) {
730         #               setprop("sim/current-view/view-number", 0);
731         #               setprop("sim/current-view/z-offset-m", new_walker_x);
732         #               setprop("sim/current-view/x-offset-m", new_walker_y);
733         #               setprop("sim/current-view/y-offset-m", new_walker_z);
734         #               setprop("sim/current-view/goal-heading-offset-deg", new_walker_h);
735         #               setprop("sim/current-view/heading-offset-deg", new_walker_h);
736         #               setprop("sim/current-view/goal-pitch-offset-deg", new_walker_p);
737         #               setprop("sim/current-view/pitch-offset-deg", new_walker_p);
738         #               setprop("sim/current-view/field-of-view", new_walker_fov);
739         #       }
740         #       setprop("sim/model/bluebird/crew/walker/x-offset-m", new_walker_x);
741         #       setprop("sim/model/bluebird/crew/walker/y-offset-m", new_walker_y);
742         #}
743 setprop("sim/current-view/view-number", 0);
744         setprop("sim/walker/crashed", 0);
745         setprop("sim/walker/airborne", 0);
746         setprop("sim/walker/outside", 0);
747         setprop("sim/walker/parachute-opened-altitude-ft", 0);
748         parachute_deployed_sec = 0;
749         setprop("sim/walker/parachute-opened-sec", 0);
750         setprop("sim/view[110]/enabled", 0);
751         setprop("sim/view[111]/enabled", 0);
752 }
753
754 var reinit_walker = func {
755         setprop("sim/walker/outside", 0);
756         setprop("sim/view[100]/enabled",0);
757         setprop("sim/view[101]/enabled",0);
758         setprop("sim/walker/crashed", 0);
759         setprop("sim/walker/airborne", 0);
760         falling = 0;
761         walk_factor = 1.0;
762         setprop("sim/walker/parachute-opened-altitude-ft", 0);
763         parachute_deployed_sec = 0;
764         setprop("sim/walker/parachute-opened-sec", 0);
765 }
766
767 var init_common = func {
768         setlistener("sim/walker/time-of-exit-sec", func(n) exit_time_sec = n.getValue());
769
770         setlistener("sim/walker/parachute-opened-altitude-ft", func(n) parachute_ft = n.getValue());
771
772         setlistener("sim/walker/parachute-opened-sec", func(n) elapsed_chute_sec = n.getValue());
773
774         setlistener("sim/walker/starting-trajectory-lat", func(n) lat_vector = n.getValue());
775
776         setlistener("sim/walker/starting-trajectory-lon", func(n) lon_vector = n.getValue());
777
778         setlistener("sim/walker/starting-trajectory-z-mps", func(n) z_vector_mps = n.getValue());
779
780         setlistener("sim/walker/time-to-zero-z-sec", func(n) time_to_top_sec = n.getValue());
781
782         setlistener("sim/walker/starting-lat", func(n) starting_lat = n.getValue());
783
784         setlistener("sim/walker/starting-lon", func(n) starting_lon = n.getValue());
785
786         setlistener("sim/walker/key-triggers/forward", func {
787                 calc_heading();
788         });
789
790         setlistener("sim/walker/key-triggers/slide", func {
791                 calc_heading();
792         });
793
794         setlistener("sim/model/bluebird/crew/walker/visible", func(n) {
795                 if (n.getValue()) {
796                         if (getprop("sim/walker/outside")) {
797                                 walker_model.add();
798                         }
799                 } else {
800                         walker_model.remove();
801                 }
802         });
803
804         setlistener("sim/current-view/view-number", func(n) {
805                 if (n.getValue() == view.indexof("Walk View")) {
806                         yViewNode.setValue(0);
807                         zViewNode.setValue(1.67);       # matches person height when inside due to aircraft offsets
808                         xViewNode.setValue(0);
809                 }
810         });
811
812         setlistener("sim/signals/reinit", func {
813                 reinit_walker();
814         });
815
816         setlistener("sim/signals/fdm-initialized", func {
817                 reinit_walker();
818         });
819 }
820 settimer(init_common,0);
821
822 var theme_dialog = gui.OverlaySelector.new("Select Theme", "Aircraft/Generic/Human/Models/Themes", "sim/walker/theme-name", nil, "sim/multiplay/generic/string[10]");
823