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