Walker: fix a Bug while walking in southern Hemisphere, include file now
[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         #       if (posy1 < 0 ) {
406         #                       posy2 = posy1 + lat3;
407         #                       print (" South");
408         #       } else {
409         #                       posy2 = posy1 + lat3;
410         #                       print (" North");
411         #       }
412                 posy2 = posy1 + lat3;
413         #       posy2 = (posy1 < 0 ? posy1 - lat3 : posy1 + lat3);
414                                 #        southern or northern hemisphere
415         }
416         if (falling) {  # add movement from aircraft upon jumping
417                 var parabola = 0;
418                 var parachute_drag = 0;
419                 var zero_xy_sec = (elapsed_fall_sec < 10 ? elapsed_fall_sec : 10.0);
420                 if (parachute_ft) {     # chute open
421                         setprop("sim/walker/parachute-opened-sec", (elapsed_sec - parachute_deployed_sec - 1.0));
422                         # chute starts to add drag at 1 second, fully open at 3 sec. Slows to 17 ft/sec
423                         if (elapsed_chute_sec >= 3.0) {
424                                 parachute_drag = 0.9;
425                         } elsif (elapsed_chute_sec >= 0.0) {    # 1 second delay for chute to deploy
426                                 parachute_drag = sin(elapsed_chute_sec * 30) * 0.9;     # 0 to 0.9 in 3 sec.
427                         }
428                 }
429                 if (parachute_drag) {
430                         zero_xy_sec = parachute_deployed_sec - exit_time_sec + 1.0;
431                         if (zero_xy_sec > 10.0) {
432                                 zero_xy_sec = 10.0;
433                         }
434                 }
435                 parabola = sin(90 - zero_xy_sec ) * zero_xy_sec - (0.096 * zero_xy_sec * zero_xy_sec / 2);
436                 if (parachute_drag and zero_xy_sec < 7) {
437                         posy2 = starting_lat + (lat_vector * parabola) + (lat_vector * parachute_drag);
438                         posx2 = starting_lon + (lon_vector * parabola) + (lon_vector * parachute_drag);
439                 } else {
440                         posy2 = starting_lat + (lat_vector * parabola);
441                         posx2 = starting_lon + (lon_vector * parabola);
442                 }
443         }
444         var posz_geo = geo.elevation(posy2, posx2, ((posz1 * 0.3048) + 2));
445         if (posz_geo == nil) {
446                 posz_geo = geo.elevation(posy2, posx2); # underground? try without current walker altitude
447                 if (posz_geo == nil) {
448                         posz_geo = 0;
449                         print(sprintf("Error. Attempting to move to latitude %13.8f longitude %13.8f",posy2,posx2));
450                 }
451         }
452         posz_geo = posz_geo / 0.3048;   # convert to ft
453 #       if (getprop("logging/walker-debug")) {
454 #               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));
455 #       }
456         if (falling) {  # 13,000 to 12,000 ft = 10 sec. 12,000 - 4,000 = 44 sec.
457                         # 5.5 sec to cover each 1000 ft at terminal velocity (ignoring altitude density and surface area)
458                 var dist_traveled_z = 0;        # feet
459                 if (posz_geo < posz1) { # ground is below walker
460                         dist_traveled_z = -32.185 * time_to_top_sec * time_to_top_sec / 2;      # upward half of arc
461                         var elapsed1 = elapsed_fall_sec - time_to_top_sec;
462                                 # excludes wind resistance and cross section of projectile. Assume negligible for now.
463                         if (elapsed_fall_sec > time_to_top_sec) {       # past zero_z and falling
464                                 # drag constant is actually 0.515 kg/s for spread eagle, and 0.067 for feet first.
465                                 # I am ignoring these distinctions for now, until a more complex formula can be made,
466                                 # to go along with the new walker model visibility.
467                                 # also needs to be improved is loss of acceleration due to drag forces
468                                 if (elapsed_fall_sec > (time_to_top_sec + 5.358)) {     # time to reach terminal velocity
469                                         dist_traveled_z += 461.99 + ((elapsed1 - 5.358) * 172);
470                                 } else {        # 9.81m/s/s up to terminal velocity 172ft/s 54m/s spread eagle
471                                         dist_traveled_z += 32.185 * elapsed1 * elapsed1 / 2;
472                                 }
473                         } else {        # started going up, arch to zero_z before falling
474                                 dist_traveled_z += 32.185 * elapsed1 * elapsed1 / 2;
475                                 if (getprop("logging/walker-debug")) {
476                                         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)));
477                                 }
478                         }
479                         if (parachute_ft) {     # chute open
480                                 # need to better model deceleration due to opening of chute, change in surface area.
481                                 var subtract_z = 0;
482                                 if (elapsed_chute_sec >= 5.0) {
483                                         subtract_z = 363.14 + ((elapsed_chute_sec - 5.0) * 155);
484                                 } elsif (elapsed_chute_sec >= 2.0) {
485                                         subtract_z = 32.15 * parachute_drag * elapsed_chute_sec * elapsed_chute_sec / 2;
486                                 } elsif (elapsed_chute_sec >= 0.0) {
487                                         subtract_z = 32.15 * parachute_drag * elapsed_chute_sec * elapsed_chute_sec / 2;
488                                 }
489                                 dist_traveled_z -= subtract_z;
490                         }
491                         posz2 = getprop("sim/walker/altitude-at-exit-ft") - dist_traveled_z;
492                         if (posz2 < posz_geo) { # below ground
493                                 if ((parachute_drag < 0.7) and (dist_traveled_z > 20)) {
494                                         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")));
495                                         if (getprop("sim/current-view/view-number") == view.indexof("Walk View")) {
496                                                 setprop("sim/current-view/pitch-offset-deg", -80);
497                                         }
498                                         setprop("sim/model/bluebird/position/landing-wow", 1);
499                                         setprop("sim/walker/crashed", 1);
500                                         setprop("sim/walker/airborne", 0);
501                                 }
502                                 posz2 = posz_geo;
503                                 walker_model.land(posx2,posy2,posz_geo);
504                         } else {
505                                 if (getprop("logging/walker-position")) {
506                                         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));
507                                         measure_alt = posz2;
508                                         last_elapsed_sec = elapsed_sec;
509                                 }
510                         }
511                 } else {
512                         walker_model.land(posx2,posy2,posz_geo);
513                         posz2 = posz_geo;
514                 }
515         } else {        # not falling
516                 # check for sudden change in ground elevation
517                 if ((abs(posz1 - last_altitude) > 1.6) or ((posz_geo + 1.6) < posz1)) {
518                         setprop("sim/walker/time-of-exit-sec", getprop("sim/time/elapsed-sec"));
519                         setprop("sim/walker/altitude-at-exit-ft", last_altitude);
520                         # add "forward" momentum upon step out and down
521                         var lat_m = getprop("sim/walker/key-triggers/speed") * walk_factor * cos(head_w);
522                         var lon_m = getprop("sim/walker/key-triggers/speed") * walk_factor * (0 - sin(head_w));
523                         var lat3 = lat_m * ERAD_deg;
524                         var lon3 = lon_m * ERAD_deg / cos(posy1);
525                         posy3 = posy1 + lat3;
526                         posx3 = posx1 + lon3;
527                         setprop("sim/walker/starting-trajectory-lat", lat3);
528                         setprop("sim/walker/starting-trajectory-lon", lon3);
529                         setprop("sim/walker/starting-lat", posy2);
530                         setprop("sim/walker/starting-lon", posx2);
531                         setprop("sim/walker/latitude-deg", posy3);
532                         setprop("sim/walker/longitude-deg", posx3);
533                         setprop("sim/walker/starting-trajectory-z-mps", 0.0);
534                         falling = 1;
535                         if ((posz1 - posz_geo) > 50) {
536                                 setprop("sim/walker/airborne", 1);
537                                 setprop("sim/walker/parachute-equipped", 0);
538                         }
539                 }
540                 if (!falling) {
541                         if (posz_geo < (posz1 + 5)) {   # walking, ground within 10 ft below walker
542                                 var posz3_geo = geo.elevation(posy2, posx2, ((posz1 * 0.3048) + 2));
543                                 if (posz3_geo == nil) {
544                                         posz3_geo = 0;
545                                 }
546                                 posz3_geo = posz3_geo / 0.3048; # convert to ft
547                                 var posz_geodiff = abs(posz_geo - posz1);
548                                 if ((posz3_geo - posz_geo) > 1.5 and posz_geodiff < 7 and posz_geodiff > 1.5) { # under object and nearly hitting head
549                                         print(sprintf ("Stopped by overhead obstruction, has height %6.2f ft above your position.", posz_geodiff));
550                                         posx2 -= (5 * (posx2 - posx1));
551                                         posy2 -= (5 * (posy2 - posy1)); # fall backward and reload ground level
552                                         posz_geo = geo.elevation(posy2, posx2, ((posz1 * 0.3048) + 1));
553                                         if (posz_geo == nil) {
554                                                 posz_geo = 0;
555                                         }
556                                         posz_geo = posz_geo / 0.3048;   # convert to ft
557                                 }
558                                 if ((posz1+0.3) > posz_geo or (posz1-0.3) < posz_geo) { # smoothen stride
559                                         interpolate ("sim/walker/altitude-ft", posz_geo, 0.25, 0.3);
560                                         posz2 = getprop("sim/walker/altitude-ft");
561                                 }
562                                 if (getprop("logging/walker-position")) {
563                                         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));
564                                 }
565                         } else {
566                                 print(sprintf ("Stopped by wall, has height %6.2f ft above your position.",(posz1-posz_geo)));
567                                 posx2 -= (5 * (posx2 - posx1));
568                                 posy2 -= (5 * (posy2 - posy1));
569                         }
570                 }
571         }
572         setprop("sim/walker/latitude-deg", posy2);
573         setprop("sim/walker/longitude-deg", posx2);
574         last_altitude = posz2;
575         setprop("sim/walker/altitude-ft", posz2);
576 }
577
578 setlistener("sim/current-view/heading-offset-deg", func(n) {
579         var c_view = getprop("sim/current-view/view-number");
580         if (c_view == 0) {
581                 var head_v = n.getValue();
582                 setprop("sim/model/bluebird/crew/walker/head-offset-deg" , head_v);
583         } elsif (c_view == view.indexof("Walk View")) {
584                 var head_v = n.getValue();
585                 setprop("sim/walker/model-heading-deg" , 360 - head_v);
586 #       } elsif (c_view == view.indexof("Walker Orbit View")) {
587 #               var head_v = getprop("sim/current-view/heading-offset-deg");
588         }
589 });
590
591 var get_out = func (loc) {
592         var c_view = getprop("sim/current-view/view-number");
593         var head_add = 0;
594         if (c_view == 0) {      # remember point of exit
595                 setprop("sim/walker/keep-inside-offset-x", getprop("sim/current-view/x-offset-m"));
596                 setprop("sim/walker/keep-inside-offset-y", getprop("sim/current-view/y-offset-m"));
597                 setprop("sim/walker/keep-inside-offset-z", getprop("sim/current-view/z-offset-m"));
598                 setprop("sim/walker/keep-pitch-offset-deg", getprop("sim/current-view/pitch-offset-deg"));
599                 setprop("sim/view[110]/enabled",1);
600                 setprop("sim/view[111]/enabled",1);
601                 head_add = getprop("sim/current-view/heading-offset-deg");
602         }
603         var c_airspeed_mps = getprop("velocities/airspeed-kt") * 0.51444444;
604         var walk_dir = getprop("sim/walker/walking");
605         if (walk_dir and loc == hatch_specs.rear_hatch_loc) {
606                 if (c_airspeed_mps > 0) {
607                         c_airspeed_mps -= 1;    # add momentum toward rear
608                 } else {
609                         c_airspeed_mps += 1;
610                 }
611         }
612         var c_head_deg = getprop("orientation/heading-deg");
613         var c_pitch = getprop("orientation/pitch-deg");
614         # for powered ejections, add to the next line the rocket thrust
615         var c_z_vector_mps = sin(c_pitch) * c_airspeed_mps;
616         # x and y are in meters, but z axis needs to be in feet once it enters altitude calculations
617         setprop("sim/walker/starting-trajectory-z-mps", c_z_vector_mps);
618         if (c_airspeed_mps < 0) {
619                 c_airspeed_mps = abs(c_airspeed_mps);
620                 c_head_deg = normheading(c_head_deg + 180);
621         }
622         var c_head_rad = c_head_deg * 0.01745329252;
623         var c_lat = getprop("position/latitude-deg");
624         var c_lon = getprop("position/longitude-deg");
625         var xy_Z_factor = math.cos(c_pitch * 0.01745329252);    # factor to zero when pitch = +- 90
626         var xy_lat_m = c_airspeed_mps * math.cos(c_head_rad) * xy_Z_factor;
627         var xy_lon_m = c_airspeed_mps * math.sin(c_head_rad) * xy_Z_factor;
628         var xy_lat = xy_lat_m * ERAD_deg;
629         var xy_lon = xy_lon_m * ERAD_deg / cos(c_lat);
630         setprop("sim/walker/starting-trajectory-lat", xy_lat);
631         setprop("sim/walker/starting-trajectory-lon", xy_lon);
632         var c_time0z_sec = math.sqrt(c_z_vector_mps * c_z_vector_mps / 9.81 / 9.81);    # time to top of arc
633         if (c_z_vector_mps < 0) {       # going down
634                 c_time0z_sec = 0 - c_time0z_sec;
635         }
636         setprop("sim/walker/time-to-zero-z-sec", c_time0z_sec);
637         if (getprop("logging/walker-debug")) {
638                 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));
639         }
640         var new_coord = hatch_specs.out_locations(loc);
641         var head = normheading(abs(getprop("orientation/heading-deg") -360.00) + head_add);
642         setprop("sim/walker/latitude-deg" , (getprop("position/latitude-deg")));
643         setprop("sim/walker/longitude-deg" , (getprop("position/longitude-deg")));
644         setprop("sim/walker/roll-deg" , (getprop("orientation/roll-deg")));
645         setprop("sim/walker/pitch-deg" , (getprop("orientation/pitch-deg")));
646         setprop("sim/walker/heading-deg" , (getprop("orientation/heading-deg")));
647         # setprop("sim/view[100]/enabled", 1);
648         # setprop("sim/view[101]/enabled", 1);
649         var posy = new_coord[0];
650         var posx = new_coord[1];
651         var posz_ft = new_coord[2] * globals.M2FT;
652         setprop("sim/walker/outside", 1);
653         if (c_view == 0) {
654                 setprop("sim/current-view/view-number", view.indexof("Walk View"));
655                 setprop("sim/current-view/pitch-offset-deg", getprop("sim/walker/keep-pitch-offset-deg"));
656                 setprop("sim/current-view/roll-offset-deg", 0);
657                 setprop("sim/current-view/heading-offset-deg", head);
658         }
659         setprop("sim/walker/heading-deg", 0);
660         setprop("sim/walker/roll-deg", 0);
661         setprop("sim/walker/pitch-deg", 0);
662         setprop("sim/walker/latitude-deg", new_coord[0]);
663         setprop("sim/walker/longitude-deg", new_coord[1]);
664         falling = 1;
665         setprop("sim/walker/time-of-exit-sec", getprop("sim/time/elapsed-sec"));
666         var alt1 = getprop("position/altitude-ft") + posz_ft + hatch_specs.z_floor_ft;
667         setprop("sim/walker/altitude-at-exit-ft", alt1);
668         setprop("sim/walker/altitude-ft" , alt1);
669         measure_alt = alt1;
670         if ((alt1 - getprop("position/ground-elev-ft")) > 20) {
671                 setprop("sim/walker/airborne", 1);
672                 setprop("sim/walker/parachute-equipped", 1);
673         }
674         setprop("sim/walker/starting-lat", new_coord[0]);
675         setprop("sim/walker/starting-lon", new_coord[1]);
676         walk_factor = 1.0;
677         walker_model.add();
678 }
679
680 var get_in = func (loc) {
681         walker_model.remove();
682 #       var c_view = getprop("sim/current-view/view-number");
683         # the following section is aircraft specific for locations of entry hatches and doors
684 #       var new_pos = 4;
685 #       var c_pos = getprop("sim/model/bluebird/crew/cockpit-position");
686 #       if (c_view > 0) {
687 #               if (loc == 0) { # find open hatch
688         #                       loc = 1;
689         #                       setprop("sim/model/bluebird/crew/cockpit-position", 4);
690         #                       c_pos = 5;
691         #               } elsif (getprop("sim/model/bluebird/doors/door[1]/position-norm") > 0.2) {
692         #                       loc = 2;
693         #                       setprop("sim/model/bluebird/crew/cockpit-position", 4);
694         #                       c_pos = 5;
695         #               } elsif (getprop("sim/model/bluebird/doors/door[5]/position-norm") > 0.2) {
696         #                       loc = 5;
697         #                       setprop("sim/model/bluebird/crew/cockpit-position", 4);
698         #                       c_pos = 5;
699         #               }
700         #       }
701         #       if (loc >= 1) {
702         #               if (c_pos == 5) {
703         #                       var new_walker_x = (loc == 1 ? -2.55 : (loc == 2 ? -2.55 : 10.0));
704         #                       var new_walker_y = (loc == 1 ? -2.8 : (loc == 2 ? 2.8 : 0));
705         #               } else {
706         #                       if (loc == 1) {
707         #                               var new_walker_x = -2.55;
708         #                               var new_walker_y = -1.9;
709         #                       } elsif (loc == 2) {
710         #                               var new_walker_x = -2.55;
711         #                               var new_walker_y = 1.9;
712         #                       } else {
713         #                               var new_walker_x = getprop("sim/model/bluebird/crew/walker/x-offset-m");
714         #                               var new_walker_y = getprop("sim/model/bluebird/crew/walker/y-offset-m");
715         #                               if (new_walker_x < 9) {
716         #                                       new_walker_x = 10.4;
717         #                               }
718         #                               if (new_walker_y < -1.6) {
719         #                                       new_walker_y = -1.6;
720         #                               } elsif (new_walker_y > 1.6) {
721         #                                       new_walker_y = 1.6;
722         #                               }
723         #                       }
724         #               }
725         #               var c_head_deg = getprop("orientation/heading-deg");
726         #               c_pos = 5;
727         #               var new_walker_h = normheading(getprop("sim/current-view/heading-offset-deg") + c_head_deg);
728         #       } else {
729         #               var new_walker_x = bluebird.cockpit_locations[c_pos].x;
730         #               var new_walker_y = bluebird.cockpit_locations[c_pos].y;
731         #               var new_walker_h = bluebird.cockpit_locations[c_pos].h;
732         #       }
733         #       var new_walker_z = bluebird.cockpit_locations[c_pos].z[0];
734         #       var new_walker_p = bluebird.cockpit_locations[c_pos].p;
735         #       var new_walker_fov = bluebird.cockpit_locations[c_pos].fov;
736                 # end aircraft specific
737         #       if (c_view == view.indexof("Walk View")) {
738         #               setprop("sim/current-view/view-number", 0);
739         #               setprop("sim/current-view/z-offset-m", new_walker_x);
740         #               setprop("sim/current-view/x-offset-m", new_walker_y);
741         #               setprop("sim/current-view/y-offset-m", new_walker_z);
742         #               setprop("sim/current-view/goal-heading-offset-deg", new_walker_h);
743         #               setprop("sim/current-view/heading-offset-deg", new_walker_h);
744         #               setprop("sim/current-view/goal-pitch-offset-deg", new_walker_p);
745         #               setprop("sim/current-view/pitch-offset-deg", new_walker_p);
746         #               setprop("sim/current-view/field-of-view", new_walker_fov);
747         #       }
748         #       setprop("sim/model/bluebird/crew/walker/x-offset-m", new_walker_x);
749         #       setprop("sim/model/bluebird/crew/walker/y-offset-m", new_walker_y);
750         #}
751 setprop("sim/current-view/view-number", 0);
752         setprop("sim/walker/crashed", 0);
753         setprop("sim/walker/airborne", 0);
754         setprop("sim/walker/outside", 0);
755         setprop("sim/walker/parachute-opened-altitude-ft", 0);
756         parachute_deployed_sec = 0;
757         setprop("sim/walker/parachute-opened-sec", 0);
758         setprop("sim/view[110]/enabled", 0);
759         setprop("sim/view[111]/enabled", 0);
760 }
761
762 var reinit_walker = func {
763         setprop("sim/walker/outside", 0);
764         setprop("sim/view[100]/enabled",0);
765         setprop("sim/view[101]/enabled",0);
766         setprop("sim/walker/crashed", 0);
767         setprop("sim/walker/airborne", 0);
768         falling = 0;
769         walk_factor = 1.0;
770         setprop("sim/walker/parachute-opened-altitude-ft", 0);
771         parachute_deployed_sec = 0;
772         setprop("sim/walker/parachute-opened-sec", 0);
773         setprop("sim/walker/key-triggers/outside-toggle",1);
774         walker_model.remove();
775 }
776
777 var init_common = func {
778         setlistener("sim/walker/time-of-exit-sec", func(n) exit_time_sec = n.getValue());
779
780         setlistener("sim/walker/parachute-opened-altitude-ft", func(n) parachute_ft = n.getValue());
781
782         setlistener("sim/walker/parachute-opened-sec", func(n) elapsed_chute_sec = n.getValue());
783
784         setlistener("sim/walker/starting-trajectory-lat", func(n) lat_vector = n.getValue());
785
786         setlistener("sim/walker/starting-trajectory-lon", func(n) lon_vector = n.getValue());
787
788         setlistener("sim/walker/starting-trajectory-z-mps", func(n) z_vector_mps = n.getValue());
789
790         setlistener("sim/walker/time-to-zero-z-sec", func(n) time_to_top_sec = n.getValue());
791
792         setlistener("sim/walker/starting-lat", func(n) starting_lat = n.getValue());
793
794         setlistener("sim/walker/starting-lon", func(n) starting_lon = n.getValue());
795
796         setlistener("sim/walker/key-triggers/forward", func {
797                 calc_heading();
798         });
799
800         setlistener("sim/walker/key-triggers/slide", func {
801                 calc_heading();
802         });
803
804         setlistener("sim/model/bluebird/crew/walker/visible", func(n) {
805                 if (n.getValue()) {
806                         if (getprop("sim/walker/outside")) {
807                                 walker_model.add();
808                         }
809                 } else {
810                         walker_model.remove();
811                 }
812         });
813
814         setlistener("sim/current-view/view-number", func(n) {
815                 if (n.getValue() == view.indexof("Walk View")) {
816                         yViewNode.setValue(0);
817                         zViewNode.setValue(1.67);       # matches person height when inside due to aircraft offsets
818                         xViewNode.setValue(0);
819                 }
820         });
821
822         setlistener("sim/signals/reinit", func {
823                 reinit_walker();
824         });
825
826         setlistener("sim/signals/fdm-initialized", func {
827                 reinit_walker();
828         });
829 }
830 settimer(init_common,0);
831
832 var theme_dialog = gui.OverlaySelector.new("Select Theme", "Aircraft/Generic/Human/Models/Themes", "sim/walker/theme-name", nil, "sim/multiplay/generic/string[10]");
833