]> git.mxchange.org Git - flightgear.git/blob - src/Main/fg_commands.cxx
Add the AIModel based air traffic subsystem from Durk Talsma.
[flightgear.git] / src / Main / fg_commands.cxx
1 // fg_commands.cxx - internal FGFS commands.
2
3 #include <string.h>             // strcmp()
4
5 #include <simgear/compiler.h>
6
7 #include STL_STRING
8 #include STL_FSTREAM
9
10 #include <simgear/sg_inlines.h>
11 #include <simgear/debug/logstream.hxx>
12 #include <simgear/math/sg_random.h>
13 #include <simgear/structure/exception.hxx>
14 #include <simgear/structure/commands.hxx>
15 #include <simgear/props/props.hxx>
16
17 #include <Cockpit/panel.hxx>
18 #include <Cockpit/panel_io.hxx>
19 #include <Cockpit/hud.hxx>
20 #include <Environment/environment.hxx>
21 #include <FDM/flight.hxx>
22 #include <GUI/gui.h>
23 #include <GUI/new_gui.hxx>
24 #include <GUI/dialog.hxx>
25 #include <Replay/replay.hxx>
26 #include <Scenery/tilemgr.hxx>
27 #if defined(HAVE_PLIB_PSL)
28 #  include <Scripting/scriptmgr.hxx>
29 #endif
30 #include <Scripting/NasalSys.hxx>
31 #include <Time/sunsolver.hxx>
32 #include <Time/tmp.hxx>
33
34 #include "fg_init.hxx"
35 #include "fg_io.hxx"
36 #include "fg_commands.hxx"
37 #include "fg_props.hxx"
38 #include "globals.hxx"
39 #include "logger.hxx"
40 #include "util.hxx"
41 #include "viewmgr.hxx"
42
43 SG_USING_STD(string);
44 SG_USING_STD(ifstream);
45 SG_USING_STD(ofstream);
46
47
48 \f
49 ////////////////////////////////////////////////////////////////////////
50 // Static helper functions.
51 ////////////////////////////////////////////////////////////////////////
52
53
54 static inline SGPropertyNode *
55 get_prop (const SGPropertyNode * arg)
56 {
57     return fgGetNode(arg->getStringValue("property[0]", "/null"), true);
58 }
59
60 static inline SGPropertyNode *
61 get_prop2 (const SGPropertyNode * arg)
62 {
63     return fgGetNode(arg->getStringValue("property[1]", "/null"), true);
64 }
65
66
67 /**
68  * Get a double value and split it as required.
69  */
70 static void
71 split_value (double full_value, const char * mask,
72              double * unmodifiable, double * modifiable)
73 {
74     if (!strcmp("integer", mask)) {
75         *modifiable = (full_value < 0 ? ceil(full_value) : floor (full_value));
76         *unmodifiable = full_value - *modifiable;
77     } else if (!strcmp("decimal", mask)) {
78         *unmodifiable = (full_value < 0 ? ceil(full_value) : floor(full_value));
79         *modifiable = full_value - *unmodifiable;
80     } else {
81         if (strcmp("all", mask))
82             SG_LOG(SG_GENERAL, SG_ALERT, "Bad value " << mask << " for mask;"
83                    << " assuming 'all'");
84         *unmodifiable = 0;
85         *modifiable = full_value;
86     }
87 }
88
89
90 /**
91  * Clamp or wrap a value as specified.
92  */
93 static void
94 limit_value (double * value, const SGPropertyNode * arg)
95 {
96     const SGPropertyNode * min_node = arg->getChild("min");
97     const SGPropertyNode * max_node = arg->getChild("max");
98
99     bool wrap = arg->getBoolValue("wrap");
100
101     if (min_node == 0 || max_node == 0)
102         wrap = false;
103   
104     if (wrap) {                 // wrap such that min <= x < max
105         double min_val = min_node->getDoubleValue();
106         double max_val = max_node->getDoubleValue();
107         double resolution = arg->getDoubleValue("resolution");
108         if (resolution > 0.0) {
109             // snap to (min + N*resolution), taking special care to handle imprecision
110             int n = (int)floor((*value - min_val) / resolution + 0.5);
111             int steps = (int)floor((max_val - min_val) / resolution + 0.5);
112             SG_NORMALIZE_RANGE(n, 0, steps);
113             *value = min_val + resolution * n;
114         } else {
115             // plain circular wrapping
116             SG_NORMALIZE_RANGE(*value, min_val, max_val);
117         }
118     } else {                    // clamp such that min <= x <= max
119         if ((min_node != 0) && (*value < min_node->getDoubleValue()))
120             *value = min_node->getDoubleValue();
121         else if ((max_node != 0) && (*value > max_node->getDoubleValue()))
122             *value = max_node->getDoubleValue();
123     }
124 }
125
126 static bool
127 compare_values (SGPropertyNode * value1, SGPropertyNode * value2)
128 {
129     switch (value1->getType()) {
130     case SGPropertyNode::BOOL:
131         return (value1->getBoolValue() == value2->getBoolValue());
132     case SGPropertyNode::INT:
133         return (value1->getIntValue() == value2->getIntValue());
134     case SGPropertyNode::LONG:
135         return (value1->getLongValue() == value2->getLongValue());
136     case SGPropertyNode::FLOAT:
137         return (value1->getFloatValue() == value2->getFloatValue());
138     case SGPropertyNode::DOUBLE:
139         return (value1->getDoubleValue() == value2->getDoubleValue());
140     default:
141         return !strcmp(value1->getStringValue(), value2->getStringValue());
142     }
143 }
144
145
146 \f
147 ////////////////////////////////////////////////////////////////////////
148 // Command implementations.
149 ////////////////////////////////////////////////////////////////////////
150
151
152 /**
153  * Built-in command: do nothing.
154  */
155 static bool
156 do_null (const SGPropertyNode * arg)
157 {
158   return true;
159 }
160
161 #if defined(HAVE_PLIB_PSL)
162 /**
163  * Built-in command: run a PSL script.
164  *
165  * script: the PSL script to execute
166  */
167 static bool
168 do_script (const SGPropertyNode * arg)
169 {
170     FGScriptMgr * mgr = (FGScriptMgr *)globals->get_subsystem("scripting");
171     return mgr->run(arg->getStringValue("script"));
172 }
173 #endif // HAVE_PLIB_PSL
174
175 /**
176  * Built-in command: run a Nasal script.
177  */
178 static bool
179 do_nasal (const SGPropertyNode * arg)
180 {
181     return ((FGNasalSys*)globals->get_subsystem("nasal"))->handleCommand(arg);
182 }
183
184 /**
185  * Built-in command: exit FlightGear.
186  *
187  * status: the exit status to return to the operating system (defaults to 0)
188  */
189 static bool
190 do_exit (const SGPropertyNode * arg)
191 {
192   SG_LOG(SG_INPUT, SG_INFO, "Program exit requested.");
193   fgExit(arg->getIntValue("status", 0));
194   return true;
195 }
196
197
198 /**
199  * Built-in command: reinitialize one or more subsystems.
200  *
201  * subsystem[*]: the name(s) of the subsystem(s) to reinitialize; if
202  * none is specified, reinitialize all of them.
203  */
204 static bool
205 do_reinit (const SGPropertyNode * arg)
206 {
207     bool result = true;
208
209     vector<SGPropertyNode_ptr> subsystems = arg->getChildren("subsystem");
210     if (subsystems.size() == 0) {
211         globals->get_subsystem_mgr()->reinit();
212     } else {
213         for ( unsigned int i = 0; i < subsystems.size(); i++ ) {
214             const char * name = subsystems[i]->getStringValue();
215             SGSubsystem * subsystem = globals->get_subsystem(name);
216             if (subsystem == 0) {
217                 result = false;
218                 SG_LOG( SG_GENERAL, SG_ALERT,
219                         "Subsystem " << name << "not found" );
220             } else {
221                 subsystem->reinit();
222             }
223         }
224     }
225
226     globals->get_event_mgr()->reinit();
227
228     return result;
229 }
230
231 #if 0
232   //
233   // these routines look useful ??? but are never used in the code ???
234   //
235
236 /**
237  * Built-in command: suspend one or more subsystems.
238  *
239  * subsystem[*] - the name(s) of the subsystem(s) to suspend.
240  */
241 static bool
242 do_suspend (const SGPropertyNode * arg)
243 {
244     bool result = true;
245
246     vector<SGPropertyNode_ptr> subsystems = arg->getChildren("subsystem");
247     for ( unsigned int i = 0; i < subsystems.size(); i++ ) {
248         const char * name = subsystems[i]->getStringValue();
249         SGSubsystem * subsystem = globals->get_subsystem(name);
250         if (subsystem == 0) {
251             result = false;
252             SG_LOG(SG_GENERAL, SG_ALERT, "Subsystem " << name << "not found");
253         } else {
254             subsystem->suspend();
255         }
256     }
257     return result;
258 }
259
260 /**
261  * Built-in command: suspend one or more subsystems.
262  *
263  * subsystem[*] - the name(s) of the subsystem(s) to suspend.
264  */
265 static bool
266 do_resume (const SGPropertyNode * arg)
267 {
268     bool result = true;
269
270     vector<SGPropertyNode_ptr> subsystems = arg->getChildren("subsystem");
271     for ( unsigned int i = 0; i < subsystems.size(); i++ ) {
272         const char * name = subsystems[i]->getStringValue();
273         SGSubsystem * subsystem = globals->get_subsystem(name);
274         if (subsystem == 0) {
275             result = false;
276             SG_LOG(SG_GENERAL, SG_ALERT, "Subsystem " << name << "not found");
277         } else {
278             subsystem->resume();
279         }
280     }
281     return result;
282 }
283
284 #endif
285
286
287 /**
288  * Built-in command: load flight.
289  *
290  * file (optional): the name of the file to load (relative to current
291  *   directory).  Defaults to "fgfs.sav"
292  */
293 static bool
294 do_load (const SGPropertyNode * arg)
295 {
296   const string &file = arg->getStringValue("file", "fgfs.sav");
297   ifstream input(file.c_str());
298   if (input.good() && fgLoadFlight(input)) {
299     input.close();
300     SG_LOG(SG_INPUT, SG_INFO, "Restored flight from " << file);
301     return true;
302   } else {
303     SG_LOG(SG_INPUT, SG_ALERT, "Cannot load flight from " << file);
304     return false;
305   }
306 }
307
308
309 /**
310  * Built-in command: save flight.
311  *
312  * file (optional): the name of the file to save (relative to the
313  * current directory).  Defaults to "fgfs.sav".
314  */
315 static bool
316 do_save (const SGPropertyNode * arg)
317 {
318   const string &file = arg->getStringValue("file", "fgfs.sav");
319   bool write_all = arg->getBoolValue("write-all", false);
320   SG_LOG(SG_INPUT, SG_INFO, "Saving flight");
321   ofstream output(file.c_str());
322   if (output.good() && fgSaveFlight(output, write_all)) {
323     output.close();
324     SG_LOG(SG_INPUT, SG_INFO, "Saved flight to " << file);
325     return true;
326   } else {
327     SG_LOG(SG_INPUT, SG_ALERT, "Cannot save flight to " << file);
328     return false;
329   }
330 }
331
332
333 /**
334  * Built-in command: (re)load the panel.
335  *
336  * path (optional): the file name to load the panel from 
337  * (relative to FG_ROOT).  Defaults to the value of /sim/panel/path,
338  * and if that's unspecified, to "Panels/Default/default.xml".
339  */
340 static bool
341 do_panel_load (const SGPropertyNode * arg)
342 {
343   string panel_path =
344     arg->getStringValue("path",
345                         fgGetString("/sim/panel/path",
346                                     "Panels/Default/default.xml"));
347   FGPanel * new_panel = fgReadPanel(panel_path);
348   if (new_panel == 0) {
349     SG_LOG(SG_INPUT, SG_ALERT,
350            "Error reading new panel from " << panel_path);
351     return false;
352   }
353   SG_LOG(SG_INPUT, SG_INFO, "Loaded new panel from " << panel_path);
354   globals->get_current_panel()->unbind();
355   delete globals->get_current_panel();
356   globals->set_current_panel( new_panel );
357   globals->get_current_panel()->bind();
358   return true;
359 }
360
361
362 /**
363  * Built-in command: pass a mouse click to the panel.
364  *
365  * button: the mouse button number, zero-based.
366  * is-down: true if the button is down, false if it is up.
367  * x-pos: the x position of the mouse click.
368  * y-pos: the y position of the mouse click.
369  */
370 static bool
371 do_panel_mouse_click (const SGPropertyNode * arg)
372 {
373   if (globals->get_current_panel() != 0)
374     return globals->get_current_panel()
375       ->doMouseAction(arg->getIntValue("button"),
376                       arg->getBoolValue("is-down") ? PU_DOWN : PU_UP,
377                       arg->getIntValue("x-pos"),
378                       arg->getIntValue("y-pos"));
379   else
380     return false;
381 }
382
383
384 /**
385  * Built-in command: (re)load preferences.
386  *
387  * path (optional): the file name to load the panel from (relative
388  * to FG_ROOT). Defaults to "preferences.xml".
389  */
390 static bool
391 do_preferences_load (const SGPropertyNode * arg)
392 {
393   try {
394     fgLoadProps(arg->getStringValue("path", "preferences.xml"),
395                 globals->get_props());
396   } catch (const sg_exception &e) {
397     guiErrorMessage("Error reading global preferences: ", e);
398     return false;
399   }
400   SG_LOG(SG_INPUT, SG_INFO, "Successfully read global preferences.");
401   return true;
402 }
403
404
405 static void
406 fix_hud_visibility()
407 {
408   if ( !strcmp(fgGetString("/sim/flight-model"), "ada") ) {
409       globals->get_props()->setBoolValue( "/sim/hud/visibility", true );
410       if ( globals->get_viewmgr()->get_current() == 1 ) {
411           globals->get_props()->setBoolValue( "/sim/hud/visibility", false );
412       }
413   }
414 }
415
416 static void
417 do_view_next( bool )
418 {
419     globals->get_current_view()->setHeadingOffset_deg(0.0);
420     globals->get_viewmgr()->next_view();
421     fix_hud_visibility();
422     globals->get_tile_mgr()->refresh_view_timestamps();
423 }
424
425 static void
426 do_view_prev( bool )
427 {
428     globals->get_current_view()->setHeadingOffset_deg(0.0);
429     globals->get_viewmgr()->prev_view();
430     fix_hud_visibility();
431     globals->get_tile_mgr()->refresh_view_timestamps();
432 }
433
434 /**
435  * Built-in command: cycle view.
436  */
437 static bool
438 do_view_cycle (const SGPropertyNode * arg)
439 {
440   globals->get_current_view()->setHeadingOffset_deg(0.0);
441   globals->get_viewmgr()->next_view();
442   fix_hud_visibility();
443   globals->get_tile_mgr()->refresh_view_timestamps();
444 //   fgReshape(fgGetInt("/sim/startup/xsize"), fgGetInt("/sim/startup/ysize"));
445   return true;
446 }
447
448 /**
449  * Built-in command: capture screen.
450  */
451 static bool
452 do_screen_capture (const SGPropertyNode * arg)
453 {
454   fgDumpSnapShot();
455   return true;
456 }
457
458
459 /**
460  * Reload the tile cache.
461  */
462 static bool
463 do_tile_cache_reload (const SGPropertyNode * arg)
464 {
465     static const SGPropertyNode *master_freeze
466         = fgGetNode("/sim/freeze/master");
467     bool freeze = master_freeze->getBoolValue();
468     SG_LOG(SG_INPUT, SG_INFO, "ReIniting TileCache");
469     if ( !freeze ) {
470         fgSetBool("/sim/freeze/master", true);
471     }
472     // BusyCursor(0);
473     if ( globals->get_tile_mgr()->init() ) {
474         // Load the local scenery data
475         double visibility_meters = fgGetDouble("/environment/visibility-m");
476         globals->get_tile_mgr()->update( visibility_meters );
477     } else {
478         SG_LOG( SG_GENERAL, SG_ALERT, 
479                 "Error in Tile Manager initialization!" );
480         exit(-1);
481     }
482     // BusyCursor(1);
483     if ( !freeze ) {
484         fgSetBool("/sim/freeze/master", false);
485     }
486     return true;
487 }
488
489
490 /**
491  * Set the sea level outside air temperature and assigning that to all
492  * boundary and aloft environment layers.
493  */
494 static bool
495 do_set_sea_level_degc (const SGPropertyNode * arg)
496 {
497     double temp_sea_level_degc = arg->getDoubleValue("temp-degc", 15.0);
498
499     SGPropertyNode *node, *child;
500
501     // boundary layers
502     node = fgGetNode( "/environment/config/boundary" );
503     if ( node != NULL ) {
504       int i = 0;
505       while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
506         child->setDoubleValue( "temperature-sea-level-degc",
507                                temp_sea_level_degc );
508         ++i;
509       }
510     }
511
512     // aloft layers
513     node = fgGetNode( "/environment/config/aloft" );
514     if ( node != NULL ) {
515       int i = 0;
516       while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
517         child->setDoubleValue( "temperature-sea-level-degc",
518                                temp_sea_level_degc );
519         ++i;
520       }
521     }
522
523     return true;
524 }
525
526
527 /**
528  * Set the outside air temperature at the "current" altitude by first
529  * calculating the corresponding sea level temp, and assigning that to
530  * all boundary and aloft environment layers.
531  */
532 static bool
533 do_set_oat_degc (const SGPropertyNode * arg)
534 {
535     const string &temp_str = arg->getStringValue("temp-degc", "15.0");
536
537     // check for an altitude specified in the arguments, otherwise use
538     // current aircraft altitude.
539     const SGPropertyNode *altitude_ft = arg->getChild("altitude-ft");
540     if ( altitude_ft == NULL ) {
541         altitude_ft = fgGetNode("/position/altitude-ft");
542     }
543
544     FGEnvironment dummy;        // instantiate a dummy so we can leech a method
545     dummy.set_elevation_ft( altitude_ft->getDoubleValue() );
546     dummy.set_temperature_degc( atof( temp_str.c_str() ) );
547     double temp_sea_level_degc = dummy.get_temperature_sea_level_degc();
548
549     cout << "Altitude = " << altitude_ft->getDoubleValue() << endl;
550     cout << "Temp at alt (C) = " << atof( temp_str.c_str() ) << endl;
551     cout << "Temp sea level (C) = " << temp_sea_level_degc << endl;
552  
553     SGPropertyNode *node, *child;
554
555     // boundary layers
556     node = fgGetNode( "/environment/config/boundary" );
557     if ( node != NULL ) {
558       int i = 0;
559       while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
560         child->setDoubleValue( "temperature-sea-level-degc",
561                                temp_sea_level_degc );
562         ++i;
563       }
564     }
565
566     // aloft layers
567     node = fgGetNode( "/environment/config/aloft" );
568     if ( node != NULL ) {
569       int i = 0;
570       while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
571         child->setDoubleValue( "temperature-sea-level-degc",
572                                temp_sea_level_degc );
573         ++i;
574       }
575     }
576
577     return true;
578 }
579
580 /**
581  * Set the sea level outside air dewpoint and assigning that to all
582  * boundary and aloft environment layers.
583  */
584 static bool
585 do_set_dewpoint_sea_level_degc (const SGPropertyNode * arg)
586 {
587     double dewpoint_sea_level_degc = arg->getDoubleValue("dewpoint-degc", 5.0);
588
589     SGPropertyNode *node, *child;
590
591     // boundary layers
592     node = fgGetNode( "/environment/config/boundary" );
593     if ( node != NULL ) {
594       int i = 0;
595       while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
596         child->setDoubleValue( "dewpoint-sea-level-degc",
597                                dewpoint_sea_level_degc );
598         ++i;
599       }
600     }
601
602     // aloft layers
603     node = fgGetNode( "/environment/config/aloft" );
604     if ( node != NULL ) {
605       int i = 0;
606       while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
607         child->setDoubleValue( "dewpoint-sea-level-degc",
608                                dewpoint_sea_level_degc );
609         ++i;
610       }
611     }
612
613     return true;
614 }
615
616
617 /**
618  * Set the outside air dewpoint at the "current" altitude by first
619  * calculating the corresponding sea level dewpoint, and assigning
620  * that to all boundary and aloft environment layers.
621  */
622 static bool
623 do_set_dewpoint_degc (const SGPropertyNode * arg)
624 {
625     const string &dewpoint_str = arg->getStringValue("dewpoint-degc", "5.0");
626
627     // check for an altitude specified in the arguments, otherwise use
628     // current aircraft altitude.
629     const SGPropertyNode *altitude_ft = arg->getChild("altitude-ft");
630     if ( altitude_ft == NULL ) {
631         altitude_ft = fgGetNode("/position/altitude-ft");
632     }
633
634     FGEnvironment dummy;        // instantiate a dummy so we can leech a method
635     dummy.set_elevation_ft( altitude_ft->getDoubleValue() );
636     dummy.set_dewpoint_degc( atof( dewpoint_str.c_str() ) );
637     double dewpoint_sea_level_degc = dummy.get_dewpoint_sea_level_degc();
638
639     cout << "Altitude = " << altitude_ft->getDoubleValue() << endl;
640     cout << "Dewpoint at alt (C) = " << atof( dewpoint_str.c_str() ) << endl;
641     cout << "Dewpoint at sea level (C) = " << dewpoint_sea_level_degc << endl;
642  
643     SGPropertyNode *node, *child;
644
645     // boundary layers
646     node = fgGetNode( "/environment/config/boundary" );
647     if ( node != NULL ) {
648       int i = 0;
649       while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
650         child->setDoubleValue( "dewpoint-sea-level-degc",
651                                dewpoint_sea_level_degc );
652         ++i;
653       }
654     }
655
656     // aloft layers
657     node = fgGetNode( "/environment/config/aloft" );
658     if ( node != NULL ) {
659       int i = 0;
660       while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
661         child->setDoubleValue( "dewpoint-sea-level-degc",
662                                dewpoint_sea_level_degc );
663         ++i;
664       }
665     }
666
667     return true;
668 }
669
670 /**
671  * Update the lighting manually.
672  */
673 static bool
674 do_timeofday (const SGPropertyNode * arg)
675 {
676     const string &offset_type = arg->getStringValue("timeofday", "noon");
677
678     static const SGPropertyNode *longitude
679         = fgGetNode("/position/longitude-deg");
680     static const SGPropertyNode *latitude
681         = fgGetNode("/position/latitude-deg");
682     static const SGPropertyNode *cur_time_override
683         = fgGetNode("/sim/time/cur-time-override", true);
684
685     int orig_warp = globals->get_warp();
686     SGTime *t = globals->get_time_params();
687     time_t cur_time = t->get_cur_time();
688     // cout << "cur_time = " << cur_time << endl;
689     // cout << "orig_warp = " << orig_warp << endl;
690
691     int warp = 0;
692     if ( offset_type == "real" ) {
693         warp = -orig_warp;
694     } else if ( offset_type == "dawn" ) {
695         warp = fgTimeSecondsUntilSunAngle( cur_time,
696                                            longitude->getDoubleValue()
697                                              * SGD_DEGREES_TO_RADIANS,
698                                            latitude->getDoubleValue()
699                                              * SGD_DEGREES_TO_RADIANS,
700                                            90.0, true ); 
701     } else if ( offset_type == "morning" ) {
702         warp = fgTimeSecondsUntilSunAngle( cur_time,
703                                            longitude->getDoubleValue()
704                                              * SGD_DEGREES_TO_RADIANS,
705                                            latitude->getDoubleValue()
706                                              * SGD_DEGREES_TO_RADIANS,
707                                            75.0, true ); 
708     } else if ( offset_type == "noon" ) {
709         warp = fgTimeSecondsUntilSunAngle( cur_time,
710                                            longitude->getDoubleValue()
711                                              * SGD_DEGREES_TO_RADIANS,
712                                            latitude->getDoubleValue()
713                                              * SGD_DEGREES_TO_RADIANS,
714                                            0.0, true ); 
715     } else if ( offset_type == "afternoon" ) {
716         warp = fgTimeSecondsUntilSunAngle( cur_time,
717                                            longitude->getDoubleValue()
718                                              * SGD_DEGREES_TO_RADIANS,
719                                            latitude->getDoubleValue()
720                                              * SGD_DEGREES_TO_RADIANS,
721                                            60.0, false ); 
722      } else if ( offset_type == "dusk" ) {
723         warp = fgTimeSecondsUntilSunAngle( cur_time,
724                                            longitude->getDoubleValue()
725                                              * SGD_DEGREES_TO_RADIANS,
726                                            latitude->getDoubleValue()
727                                              * SGD_DEGREES_TO_RADIANS,
728                                            90.0, false ); 
729      } else if ( offset_type == "evening" ) {
730         warp = fgTimeSecondsUntilSunAngle( cur_time,
731                                            longitude->getDoubleValue()
732                                              * SGD_DEGREES_TO_RADIANS,
733                                            latitude->getDoubleValue()
734                                              * SGD_DEGREES_TO_RADIANS,
735                                            100.0, false ); 
736     } else if ( offset_type == "midnight" ) {
737         warp = fgTimeSecondsUntilSunAngle( cur_time,
738                                            longitude->getDoubleValue()
739                                              * SGD_DEGREES_TO_RADIANS,
740                                            latitude->getDoubleValue()
741                                              * SGD_DEGREES_TO_RADIANS,
742                                            180.0, false ); 
743     }
744     // cout << "warp = " << warp << endl;
745     globals->set_warp( orig_warp + warp );
746
747     t->update( longitude->getDoubleValue() * SGD_DEGREES_TO_RADIANS,
748                latitude->getDoubleValue() * SGD_DEGREES_TO_RADIANS,
749                cur_time_override->getLongValue(),
750                globals->get_warp() );
751
752     return true;
753 }
754
755
756 /**
757  * Built-in command: toggle a bool property value.
758  *
759  * property: The name of the property to toggle.
760  */
761 static bool
762 do_property_toggle (const SGPropertyNode * arg)
763 {
764   SGPropertyNode * prop = get_prop(arg);
765   return prop->setBoolValue(!prop->getBoolValue());
766 }
767
768
769 /**
770  * Built-in command: assign a value to a property.
771  *
772  * property: the name of the property to assign.
773  * value: the value to assign; or
774  * property[1]: the property to copy from.
775  */
776 static bool
777 do_property_assign (const SGPropertyNode * arg)
778 {
779   SGPropertyNode * prop = get_prop(arg);
780   const SGPropertyNode * prop2 = get_prop2(arg);
781   const SGPropertyNode * value = arg->getNode("value");
782
783   if (value != 0)
784       return prop->setUnspecifiedValue(value->getStringValue());
785   else if (prop2)
786       return prop->setUnspecifiedValue(prop2->getStringValue());
787   else
788       return false;
789 }
790
791
792 /**
793  * Built-in command: increment or decrement a property value.
794  *
795  * If the 'step' argument is present, it will be used; otherwise,
796  * the command uses 'offset' and 'factor', usually from the mouse.
797  *
798  * property: the name of the property to increment or decrement.
799  * step: the amount of the increment or decrement (default: 0).
800  * offset: offset from the current setting (used for the mouse; multiplied 
801  *         by factor)
802  * factor: scaling amount for the offset (defaults to 1).
803  * min: the minimum allowed value (default: no minimum).
804  * max: the maximum allowed value (default: no maximum).
805  * mask: 'integer' to apply only to the left of the decimal point, 
806  *       'decimal' to apply only to the right of the decimal point,
807  *       or 'all' to apply to the whole number (the default).
808  * wrap: true if the value should be wrapped when it passes min or max;
809  *       both min and max must be present for this to work (default:
810  *       false).
811  */
812 static bool
813 do_property_adjust (const SGPropertyNode * arg)
814 {
815   SGPropertyNode * prop = get_prop(arg);
816
817   double amount = 0;
818   if (arg->hasValue("step"))
819       amount = arg->getDoubleValue("step");
820   else
821       amount = (arg->getDoubleValue("factor", 1)
822                 * arg->getDoubleValue("offset"));
823           
824   double unmodifiable, modifiable;
825   split_value(prop->getDoubleValue(), arg->getStringValue("mask", "all"),
826               &unmodifiable, &modifiable);
827   modifiable += amount;
828   limit_value(&modifiable, arg);
829
830   prop->setDoubleValue(unmodifiable + modifiable);
831
832   return true;
833 }
834
835
836 /**
837  * Built-in command: multiply a property value.
838  *
839  * property: the name of the property to multiply.
840  * factor: the amount by which to multiply.
841  * min: the minimum allowed value (default: no minimum).
842  * max: the maximum allowed value (default: no maximum).
843  * mask: 'integer' to apply only to the left of the decimal point, 
844  *       'decimal' to apply only to the right of the decimal point,
845  *       or 'all' to apply to the whole number (the default).
846  * wrap: true if the value should be wrapped when it passes min or max;
847  *       both min and max must be present for this to work (default:
848  *       false).
849  */
850 static bool
851 do_property_multiply (const SGPropertyNode * arg)
852 {
853   SGPropertyNode * prop = get_prop(arg);
854   double factor = arg->getDoubleValue("factor", 1);
855
856   double unmodifiable, modifiable;
857   split_value(prop->getDoubleValue(), arg->getStringValue("mask", "all"),
858               &unmodifiable, &modifiable);
859   modifiable *= factor;
860   limit_value(&modifiable, arg);
861
862   prop->setDoubleValue(unmodifiable + modifiable);
863
864   return true;
865 }
866
867
868 /**
869  * Built-in command: swap two property values.
870  *
871  * property[0]: the name of the first property.
872  * property[1]: the name of the second property.
873  */
874 static bool
875 do_property_swap (const SGPropertyNode * arg)
876 {
877   SGPropertyNode * prop1 = get_prop(arg);
878   SGPropertyNode * prop2 = get_prop2(arg);
879
880                                 // FIXME: inefficient
881   const string & tmp = prop1->getStringValue();
882   return (prop1->setUnspecifiedValue(prop2->getStringValue()) &&
883           prop2->setUnspecifiedValue(tmp.c_str()));
884 }
885
886
887 /**
888  * Built-in command: Set a property to an axis or other moving input.
889  *
890  * property: the name of the property to set.
891  * setting: the current input setting, usually between -1.0 and 1.0.
892  * offset: the offset to shift by, before applying the factor.
893  * factor: the factor to multiply by (use negative to reverse).
894  */
895 static bool
896 do_property_scale (const SGPropertyNode * arg)
897 {
898   SGPropertyNode * prop = get_prop(arg);
899   double setting = arg->getDoubleValue("setting");
900   double offset = arg->getDoubleValue("offset", 0.0);
901   double factor = arg->getDoubleValue("factor", 1.0);
902   bool squared = arg->getBoolValue("squared", false);
903   int power = arg->getIntValue("power", (squared ? 2 : 1));
904
905   int sign = (setting < 0 ? -1 : 1);
906
907   switch (power) {
908   case 1:
909       break;
910   case 2:
911       setting = setting * setting * sign;
912       break;
913   case 3:
914       setting = setting * setting * setting;
915       break;
916   case 4:
917       setting = setting * setting * setting * setting * sign;
918       break;
919   default:
920       setting =  pow(setting, power);
921       if ((power % 2) == 0)
922           setting *= sign;
923       break;
924   }
925
926   return prop->setDoubleValue((setting + offset) * factor);
927 }
928
929
930 /**
931  * Built-in command: cycle a property through a set of values.
932  *
933  * If the current value isn't in the list, the cycle will
934  * (re)start from the beginning.
935  *
936  * property: the name of the property to cycle.
937  * value[*]: the list of values to cycle through.
938  */
939 static bool
940 do_property_cycle (const SGPropertyNode * arg)
941 {
942     SGPropertyNode * prop = get_prop(arg);
943     vector<SGPropertyNode_ptr> values = arg->getChildren("value");
944     int selection = -1;
945     int nSelections = values.size();
946
947     if (nSelections < 1) {
948         SG_LOG(SG_GENERAL, SG_ALERT, "No values for property-cycle");
949         return false;
950     }
951
952                                 // Try to find the current selection
953     for (int i = 0; i < nSelections; i++) {
954         if (compare_values(prop, values[i])) {
955             selection = i + 1;
956             break;
957         }
958     }
959
960                                 // Default or wrap to the first selection
961     if (selection < 0 || selection >= nSelections)
962         selection = 0;
963
964     prop->setUnspecifiedValue(values[selection]->getStringValue());
965     return true;
966 }
967
968
969 /**
970  * Built-in command: randomize a numeric property value.
971  *
972  * property: the name of the property value to randomize.
973  * min: the minimum allowed value.
974  * max: the maximum allowed value.
975  */
976 static bool
977 do_property_randomize (const SGPropertyNode * arg)
978 {
979     SGPropertyNode * prop = get_prop(arg);
980     double min = arg->getDoubleValue("min", DBL_MIN);
981     double max = arg->getDoubleValue("max", DBL_MAX);
982     prop->setDoubleValue(sg_random() * (max - min) + min);
983     return true;
984 }
985
986
987 /**
988  * Built-in command: reinit the data logging system based on the
989  * current contents of the /logger tree.
990  */
991 static bool
992 do_data_logging_commit (const SGPropertyNode * arg)
993 {
994     FGLogger *log = (FGLogger *)globals->get_subsystem("logger");
995     log->reinit();
996     return true;
997 }
998
999 /**
1000  * Built-in command: Add a dialog to the GUI system.  Does *not*
1001  * display the dialog.  The property node should have the same format
1002  * as a dialog XML configuration.  It must include:
1003  *
1004  * name: the name of the GUI dialog for future reference.
1005  */
1006 static bool
1007 do_dialog_new (const SGPropertyNode * arg)
1008 {
1009     NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
1010
1011     // Note the casting away of const: this is *real*.  Doing a
1012     // "dialog-apply" command later on will mutate this property node.
1013     // I'm not convinced that this isn't the Right Thing though; it
1014     // allows client to create a node, pass it to dialog-new, and get
1015     // the values back from the dialog by reading the same node.
1016     // Perhaps command arguments are not as "const" as they would
1017     // seem?
1018     gui->newDialog((SGPropertyNode*)arg);
1019     return true;
1020 }
1021
1022 /**
1023  * Built-in command: Show an XML-configured dialog.
1024  *
1025  * dialog-name: the name of the GUI dialog to display.
1026  */
1027 static bool
1028 do_dialog_show (const SGPropertyNode * arg)
1029 {
1030     NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
1031     gui->showDialog(arg->getStringValue("dialog-name"));
1032     return true;
1033 }
1034
1035
1036 /**
1037  * Built-in Command: Hide the active XML-configured dialog.
1038  */
1039 static bool
1040 do_dialog_close (const SGPropertyNode * arg)
1041 {
1042     NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
1043     if(arg->hasValue("dialog-name"))
1044         return gui->closeDialog(arg->getStringValue("dialog-name"));
1045     return gui->closeActiveDialog();
1046 }
1047
1048
1049 /**
1050  * Update a value in the active XML-configured dialog.
1051  *
1052  * object-name: The name of the GUI object(s) (all GUI objects if omitted).
1053  */
1054 static bool
1055 do_dialog_update (const SGPropertyNode * arg)
1056 {
1057     NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
1058     FGDialog * dialog = gui->getActiveDialog();
1059     if (dialog != 0) {
1060         if (arg->hasValue("object-name")) {
1061             dialog->updateValue(arg->getStringValue("object-name"));
1062         } else {
1063             dialog->updateValues();
1064         }
1065         return true;
1066     } else {
1067         return false;
1068     }
1069 }
1070
1071
1072 /**
1073  * Apply a value in the active XML-configured dialog.
1074  *
1075  * object-name: The name of the GUI object(s) (all GUI objects if omitted).
1076  */
1077 static bool
1078 do_dialog_apply (const SGPropertyNode * arg)
1079 {
1080     NewGUI * gui = (NewGUI *)globals->get_subsystem("gui");
1081     FGDialog * dialog = gui->getActiveDialog();
1082     if (dialog != 0) {
1083         if (arg->hasValue("object-name")) {
1084             const char * name = arg->getStringValue("object-name");
1085             dialog->applyValue(name);
1086             dialog->updateValue(name);
1087         } else {
1088             dialog->applyValues();
1089             dialog->updateValues();
1090         }
1091         return true;
1092     } else {
1093         return false;
1094     }
1095 }
1096
1097
1098 /**
1099  * Built-in command: commit presets (read from in /sim/presets/)
1100  */
1101 static bool
1102 do_presets_commit (const SGPropertyNode * arg)
1103 {
1104     // unbind the current fdm state so property changes
1105     // don't get lost when we subsequently delete this fdm
1106     // and create a new one.
1107     cur_fdm_state->unbind();
1108         
1109     // set position from presets
1110     fgInitPosition();
1111
1112     // BusyCursor(0);
1113     fgReInitSubsystems();
1114
1115     globals->get_tile_mgr()->update( fgGetDouble("/environment/visibility-m") );
1116
1117 #if 0
1118     if ( ! fgGetBool("/sim/presets/onground") ) {
1119         fgSetBool( "/sim/freeze/master", true );
1120         fgSetBool( "/sim/freeze/clock", true );
1121     }
1122 #endif
1123
1124     return true;
1125 }
1126
1127 /**
1128  * Built-in command: set log level (0 ... 7)
1129  */
1130 static bool
1131 do_log_level (const SGPropertyNode * arg)
1132 {
1133    sglog().setLogLevels( SG_ALL, (sgDebugPriority)arg->getIntValue() );
1134
1135    return true;
1136 }
1137
1138 /**
1139  * Built-in command: replay the FDR buffer
1140  */
1141 static bool
1142 do_replay (const SGPropertyNode * arg)
1143 {
1144     // freeze the master fdm
1145     fgSetBool( "/sim/freeze/replay", true );
1146
1147     FGReplay *r = (FGReplay *)(globals->get_subsystem( "replay" ));
1148
1149     fgSetDouble( "/sim/replay/start-time", r->get_start_time() );
1150     fgSetDouble( "/sim/replay/end-time", r->get_end_time() );
1151     double duration = fgGetDouble( "/sim/replay/duration" );
1152     if( duration && duration < (r->get_end_time() - r->get_start_time()) ) {
1153         fgSetDouble( "/sim/replay/time", r->get_end_time() - duration );
1154     } else {
1155         fgSetDouble( "/sim/replay/time", r->get_start_time() );
1156     }
1157
1158     cout << "start = " << r->get_start_time()
1159          << "  end = " << r->get_end_time() << endl;
1160
1161     return true;
1162 }
1163
1164
1165
1166 static bool
1167 do_decrease_visibility (const SGPropertyNode * arg)
1168 {
1169     double new_value = fgGetDouble("/environment/visibility-m") * 0.9;
1170     fgSetDouble("/environment/visibility-m", new_value);
1171     fgDefaultWeatherValue("visibility-m", new_value);
1172     globals->get_subsystem("environment")->reinit();
1173
1174     return true;
1175 }
1176  
1177 static bool
1178 do_increase_visibility (const SGPropertyNode * arg)
1179 {
1180     double new_value = fgGetDouble("/environment/visibility-m") * 1.1;
1181     fgSetDouble("/environment/visibility-m", new_value);
1182     fgDefaultWeatherValue("visibility-m", new_value);
1183     globals->get_subsystem("environment")->reinit();
1184
1185     return true;
1186 }
1187
1188 static bool
1189 do_hud_brightkey(const SGPropertyNode *)
1190 {
1191     HUD_brightkey( true );
1192     return true;
1193 }
1194
1195 static bool
1196 do_hud_masterswitch(const SGPropertyNode *)
1197 {
1198     HUD_masterswitch( true );
1199     return true;
1200 }
1201
1202 static bool
1203 do_hud_init(const SGPropertyNode *)
1204 {
1205     fgHUDInit(0); // minimal HUD
1206     return true;
1207 }
1208
1209 static bool
1210 do_hud_init2(const SGPropertyNode *)
1211 {
1212     fgHUDInit2(0);  // normal HUD
1213     return true;
1214 }
1215
1216 ////////////////////////////////////////////////////////////////////////
1217 // Command setup.
1218 ////////////////////////////////////////////////////////////////////////
1219
1220
1221 /**
1222  * Table of built-in commands.
1223  *
1224  * New commands do not have to be added here; any module in the application
1225  * can add a new command using globals->get_commands()->addCommand(...).
1226  */
1227 static struct {
1228   const char * name;
1229   SGCommandMgr::command_t command;
1230 } built_ins [] = {
1231     { "null", do_null },
1232 #if defined(HAVE_PLIB_PSL)
1233     { "script", do_script },
1234 #endif // HAVE_PLIB_PSL
1235     { "nasal", do_nasal },
1236     { "exit", do_exit },
1237     { "reinit", do_reinit },
1238     { "suspend", do_reinit },
1239     { "resume", do_reinit },
1240     { "load", do_load },
1241     { "save", do_save },
1242     { "panel-load", do_panel_load },
1243     { "panel-mouse-click", do_panel_mouse_click },
1244     { "preferences-load", do_preferences_load },
1245     { "view-cycle", do_view_cycle },
1246     { "screen-capture", do_screen_capture },
1247     { "tile-cache-reload", do_tile_cache_reload },
1248     { "set-sea-level-air-temp-degc", do_set_sea_level_degc },
1249     { "set-outside-air-temp-degc", do_set_oat_degc },
1250     { "set-dewpoint-sea-level-air-temp-degc", do_set_dewpoint_sea_level_degc },
1251     { "set-dewpoint-temp-degc", do_set_dewpoint_degc },
1252     { "timeofday", do_timeofday },
1253     { "property-toggle", do_property_toggle },
1254     { "property-assign", do_property_assign },
1255     { "property-adjust", do_property_adjust },
1256     { "property-multiply", do_property_multiply },
1257     { "property-swap", do_property_swap },
1258     { "property-scale", do_property_scale },
1259     { "property-cycle", do_property_cycle },
1260     { "property-randomize", do_property_randomize },
1261     { "data-logging-commit", do_data_logging_commit },
1262     { "dialog-new", do_dialog_new },
1263     { "dialog-show", do_dialog_show },
1264     { "dialog-close", do_dialog_close },
1265     { "dialog-show", do_dialog_show },
1266     { "dialog-update", do_dialog_update },
1267     { "dialog-apply", do_dialog_apply },
1268     { "presets-commit", do_presets_commit },
1269     { "log-level", do_log_level },
1270     { "replay", do_replay },
1271     { "decrease-visibility", do_decrease_visibility },
1272     { "increase-visibility", do_increase_visibility },
1273     { "hud-brightkey", do_hud_brightkey },
1274     { "hud-masterswitch", do_hud_masterswitch },
1275     { "hud-init", do_hud_init },
1276     { "hud-init2", do_hud_init2 },
1277     { 0, 0 }                    // zero-terminated
1278 };
1279
1280
1281 /**
1282  * Initialize the default built-in commands.
1283  *
1284  * Other commands may be added by other parts of the application.
1285  */
1286 void
1287 fgInitCommands ()
1288 {
1289   SG_LOG(SG_GENERAL, SG_INFO, "Initializing basic built-in commands:");
1290   for (int i = 0; built_ins[i].name != 0; i++) {
1291     SG_LOG(SG_GENERAL, SG_INFO, "  " << built_ins[i].name);
1292     globals->get_commands()->addCommand(built_ins[i].name,
1293                                         built_ins[i].command);
1294   }
1295
1296   typedef bool (*dummy)();
1297   fgTie( "/command/view/next", dummy(0), do_view_next );
1298   fgTie( "/command/view/prev", dummy(0), do_view_prev );
1299 }
1300
1301 // end of fg_commands.cxx