]> git.mxchange.org Git - flightgear.git/blob - src/Main/fg_props.cxx
5218ab7ac26f22fa461171f45eacccc264e28fb7
[flightgear.git] / src / Main / fg_props.cxx
1 // fg_props.cxx -- support for FlightGear properties.
2 //
3 // Written by David Megginson, started 2000.
4 //
5 // Copyright (C) 2000, 2001 David Megginson - david@megginson.com
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 //
21 // $Id$
22
23 #ifdef HAVE_CONFIG_H
24 #  include <simgear/compiler.h>
25 #endif
26
27 #include <simgear/misc/exception.hxx>
28
29 #include STL_IOSTREAM
30
31 #include <Autopilot/newauto.hxx>
32 #include <Aircraft/aircraft.hxx>
33 #include <Time/tmp.hxx>
34 #include <FDM/UIUCModel/uiuc_aircraftdir.h>
35 #ifndef FG_OLD_WEATHER
36 #  include <WeatherCM/FGLocalWeatherDatabase.h>
37 #else
38 #  include <Weather/weather.hxx>
39 #endif
40 #include <Objects/matlib.hxx>
41
42 #include <GUI/gui.h>
43
44 #include "fgfs.hxx"
45 #include "fg_props.hxx"
46 #include "viewmgr.hxx"
47
48 #if !defined(SG_HAVE_NATIVE_SGI_COMPILERS)
49 SG_USING_STD(istream);
50 SG_USING_STD(ostream);
51 #endif
52
53 static double getWindNorth ();
54 static double getWindEast ();
55 static double getWindDown ();
56
57 // Allow the view to be set from two axes (i.e. a joystick hat)
58 // This needs to be in FGViewer itself, somehow.
59 static double axisLong = 0.0;
60 static double axisLat = 0.0;
61
62 static bool winding_ccw = true; // FIXME: temporary
63
64 static bool fdm_data_logging = false; // FIXME: temporary
65
66
67 /**
68  * Utility function.
69  */
70 static inline void
71 _set_view_from_axes ()
72 {
73                                 // Take no action when hat is centered
74   if ( ( axisLong <  0.01 ) &&
75        ( axisLong > -0.01 ) &&
76        ( axisLat  <  0.01 ) &&
77        ( axisLat  > -0.01 )
78      )
79     return;
80
81   double viewDir = 999;
82
83   /* Do all the quick and easy cases */
84   if (axisLong < 0) {           // Longitudinal axis forward
85     if (axisLat == axisLong)
86       viewDir = 45;
87     else if (axisLat == - axisLong)
88       viewDir = 315;
89     else if (axisLat == 0)
90       viewDir = 0;
91   } else if (axisLong > 0) {    // Longitudinal axis backward
92     if (axisLat == - axisLong)
93       viewDir = 135;
94     else if (axisLat == axisLong)
95       viewDir = 225;
96     else if (axisLat == 0)
97       viewDir = 180;
98   } else if (axisLong == 0) {   // Longitudinal axis neutral
99     if (axisLat < 0)
100       viewDir = 90;
101     else if (axisLat > 0)
102       viewDir = 270;
103     else return; /* And assertion failure maybe? */
104   }
105
106   /* Do all the difficult cases */
107   if ( viewDir > 900 )
108     viewDir = SGD_RADIANS_TO_DEGREES * atan2 ( -axisLat, -axisLong );
109   if ( viewDir < -1 ) viewDir += 360;
110
111 //  SG_LOG(SG_INPUT, SG_ALERT, "Joystick Lat=" << axisLat << "   and Long="
112 //      << axisLong << "  gave angle=" << viewDir );
113
114   globals->get_current_view()->set_goal_view_offset(viewDir*SGD_DEGREES_TO_RADIANS);
115 //   globals->get_current_view()->set_view_offset(viewDir*SGD_DEGREES_TO_RADIANS);
116 }
117
118 \f
119 ////////////////////////////////////////////////////////////////////////
120 // Default property bindings (not yet handled by any module).
121 ////////////////////////////////////////////////////////////////////////
122
123 struct LogClassMapping {
124   sgDebugClass c;
125   string name;
126   LogClassMapping(sgDebugClass cc, string nname) { c = cc; name = nname; }
127 };
128
129 LogClassMapping log_class_mappings [] = {
130   LogClassMapping(SG_NONE, "none"),
131   LogClassMapping(SG_TERRAIN, "terrain"),
132   LogClassMapping(SG_ASTRO, "astro"),
133   LogClassMapping(SG_FLIGHT, "flight"),
134   LogClassMapping(SG_INPUT, "input"),
135   LogClassMapping(SG_GL, "gl"),
136   LogClassMapping(SG_VIEW, "view"),
137   LogClassMapping(SG_COCKPIT, "cockpit"),
138   LogClassMapping(SG_GENERAL, "general"),
139   LogClassMapping(SG_MATH, "math"),
140   LogClassMapping(SG_EVENT, "event"),
141   LogClassMapping(SG_AIRCRAFT, "aircraft"),
142   LogClassMapping(SG_AUTOPILOT, "autopilot"),
143   LogClassMapping(SG_IO, "io"),
144   LogClassMapping(SG_CLIPPER, "clipper"),
145   LogClassMapping(SG_NETWORK, "network"),
146   LogClassMapping(SG_UNDEFD, "")
147 };
148
149
150 /**
151  * Get the logging classes.
152  */
153 static string
154 getLoggingClasses ()
155 {
156   sgDebugClass classes = logbuf::get_log_classes();
157   string result = "";
158   for (int i = 0; log_class_mappings[i].c != SG_UNDEFD; i++) {
159     if ((classes&log_class_mappings[i].c) > 0) {
160       if (result != "")
161         result += '|';
162       result += log_class_mappings[i].name;
163     }
164   }
165   return result;
166 }
167
168
169 static void addLoggingClass (const string &name)
170 {
171   sgDebugClass classes = logbuf::get_log_classes();
172   for (int i = 0; log_class_mappings[i].c != SG_UNDEFD; i++) {
173     if (name == log_class_mappings[i].name) {
174       logbuf::set_log_classes(sgDebugClass(classes|log_class_mappings[i].c));
175       return;
176     }
177   }
178   SG_LOG(SG_GENERAL, SG_ALERT, "Unknown logging class: " << name);
179 }
180
181
182 /**
183  * Set the logging classes.
184  */
185 static void
186 setLoggingClasses (string classes)
187 {
188   logbuf::set_log_classes(SG_NONE);
189
190   if (classes == "none") {
191     SG_LOG(SG_GENERAL, SG_INFO, "Disabled all logging classes");
192     return;
193   }
194
195   if (classes == "" || classes == "all") { // default
196     logbuf::set_log_classes(SG_ALL);
197     SG_LOG(SG_GENERAL, SG_INFO, "Enabled all logging classes: "
198            << getLoggingClasses());
199     return;
200   }
201
202   string rest = classes;
203   string name = "";
204   int sep = rest.find('|');
205   while (sep > 0) {
206     name = rest.substr(0, sep);
207     rest = rest.substr(sep+1);
208     addLoggingClass(name);
209     sep = rest.find('|');
210   }
211   addLoggingClass(rest);
212   SG_LOG(SG_GENERAL, SG_INFO, "Set logging classes to "
213          << getLoggingClasses());
214 }
215
216
217 /**
218  * Get the logging priority.
219  */
220 static string
221 getLoggingPriority ()
222 {
223   switch (logbuf::get_log_priority()) {
224   case SG_BULK:
225     return "bulk";
226   case SG_DEBUG:
227     return "debug";
228   case SG_INFO:
229     return "info";
230   case SG_WARN:
231     return "warn";
232   case SG_ALERT:
233     return "alert";
234   default:
235     SG_LOG(SG_GENERAL, SG_WARN, "Internal: Unknown logging priority number: "
236            << logbuf::get_log_priority());
237     return "unknown";
238   }
239 }
240
241
242 /**
243  * Set the logging priority.
244  */
245 static void
246 setLoggingPriority (string priority)
247 {
248   if (priority == "bulk") {
249     logbuf::set_log_priority(SG_BULK);
250   } else if (priority == "debug") {
251     logbuf::set_log_priority(SG_DEBUG);
252   } else if (priority == "" || priority == "info") { // default
253     logbuf::set_log_priority(SG_INFO);
254   } else if (priority == "warn") {
255     logbuf::set_log_priority(SG_WARN);
256   } else if (priority == "alert") {
257     logbuf::set_log_priority(SG_ALERT);
258   } else {
259     SG_LOG(SG_GENERAL, SG_WARN, "Unknown logging priority " << priority);
260   }
261   SG_LOG(SG_GENERAL, SG_INFO, "Logging priority is " << getLoggingPriority());
262 }
263
264
265 /**
266  * Get the pause state of the sim.
267  */
268 static bool
269 getFreeze ()
270 {
271   return globals->get_freeze();
272 }
273
274
275 /**
276  * Set the pause state of the sim.
277  */
278 static void
279 setFreeze (bool freeze)
280 {
281   globals->set_freeze(freeze);
282 }
283
284 /**
285  * Return the current aircraft directory (UIUC) as a string.
286  */
287 static string 
288 getAircraftDir ()
289 {
290   return aircraft_dir;
291 }
292
293
294 /**
295  * Set the current aircraft directory (UIUC).
296  */
297 static void
298 setAircraftDir (string dir)
299 {
300   if (getAircraftDir() != dir) {
301     aircraft_dir = dir;
302 //     needReinit(); FIXME!!
303   }
304 }
305
306
307 /**
308  * Get the current view offset in degrees.
309  */
310 static double
311 getViewOffset ()
312 {
313   return (globals->get_current_view()
314           ->get_view_offset() * SGD_RADIANS_TO_DEGREES);
315 }
316
317
318 static void
319 setViewOffset (double offset)
320 {
321   globals->get_current_view()->set_view_offset(offset * SGD_DEGREES_TO_RADIANS);
322 }
323
324 static double
325 getGoalViewOffset ()
326 {
327   return (globals->get_current_view()
328           ->get_goal_view_offset() * SGD_RADIANS_TO_DEGREES);
329 }
330
331 static void
332 setGoalViewOffset (double offset)
333 {
334     while ( offset < 0 ) {
335         offset += 360.0;
336     }
337     while ( offset > 360.0 ) {
338         offset -= 360.0;
339     }
340     // Snap to center if we are close
341     if ( fabs(offset) < 1.0 ||  fabs(offset) > 359.0 ) {
342         offset = 0.0;
343     }
344
345     globals->get_current_view()
346         ->set_goal_view_offset(offset * SGD_DEGREES_TO_RADIANS);
347 }
348
349
350 /**
351  * Pilot position offset from CG.
352  */
353 static float
354 getPilotPositionXOffset ()
355 {
356   FGViewer * pilot_view = globals->get_viewmgr()->get_view(0);
357   float * offset = pilot_view->get_pilot_offset();
358   return offset[0];
359 }
360
361 static void
362 setPilotPositionXOffset (float x)
363 {
364   FGViewer * pilot_view = globals->get_viewmgr()->get_view(0);
365   float * offset = pilot_view->get_pilot_offset();
366   pilot_view->set_pilot_offset(x, offset[1], offset[2]);
367 }
368
369 static float
370 getPilotPositionYOffset ()
371 {
372   FGViewer * pilot_view = globals->get_viewmgr()->get_view(0);
373   float * offset = pilot_view->get_pilot_offset();
374   return offset[1];
375 }
376
377 static void
378 setPilotPositionYOffset (float y)
379 {
380   FGViewer * pilot_view = globals->get_viewmgr()->get_view(0);
381   float * offset = pilot_view->get_pilot_offset();
382   pilot_view->set_pilot_offset(offset[0], y, offset[2]);
383 }
384
385 static float
386 getPilotPositionZOffset ()
387 {
388   FGViewer * pilot_view = globals->get_viewmgr()->get_view(0);
389   float * offset = pilot_view->get_pilot_offset();
390   return offset[2];
391 }
392
393 static void
394 setPilotPositionZOffset (float z)
395 {
396   FGViewer * pilot_view = globals->get_viewmgr()->get_view(0);
397   float * offset = pilot_view->get_pilot_offset();
398   pilot_view->set_pilot_offset(offset[0], offset[1], z);
399 }
400
401
402 /**
403  * Return the current Zulu time.
404  */
405 static string 
406 getDateString ()
407 {
408   string out;
409   char buf[64];
410   struct tm * t = globals->get_time_params()->getGmt();
411   sprintf(buf, "%.4d-%.2d-%.2dT%.2d:%.2d:%.2d",
412           t->tm_year + 1900, t->tm_mon + 1, t->tm_mday,
413           t->tm_hour, t->tm_min, t->tm_sec);
414   out = buf;
415   return out;
416 }
417
418
419 /**
420  * Set the current Zulu time.
421  */
422 static void
423 setDateString (string date_string)
424 {
425   SGTime * st = globals->get_time_params();
426   struct tm * current_time = st->getGmt();
427   struct tm new_time;
428
429                                 // Scan for basic ISO format
430                                 // YYYY-MM-DDTHH:MM:SS
431   int ret = sscanf(date_string.c_str(), "%d-%d-%dT%d:%d:%d",
432                    &(new_time.tm_year), &(new_time.tm_mon),
433                    &(new_time.tm_mday), &(new_time.tm_hour),
434                    &(new_time.tm_min), &(new_time.tm_sec));
435
436                                 // Be pretty picky about this, so
437                                 // that strange things don't happen
438                                 // if the save file has been edited
439                                 // by hand.
440   if (ret != 6) {
441     SG_LOG(SG_INPUT, SG_ALERT, "Date/time string " << date_string
442            << " not in YYYY-MM-DDTHH:MM:SS format; skipped");
443     return;
444   }
445
446                                 // OK, it looks like we got six
447                                 // values, one way or another.
448   new_time.tm_year -= 1900;
449   new_time.tm_mon -= 1;
450
451                                 // Now, tell flight gear to use
452                                 // the new time.  This was far
453                                 // too difficult, by the way.
454   long int warp =
455     mktime(&new_time) - mktime(current_time) + globals->get_warp();
456   double lon = current_aircraft.fdm_state->get_Longitude();
457   double lat = current_aircraft.fdm_state->get_Latitude();
458   globals->set_warp(warp);
459   st->update(lon, lat, warp);
460   fgUpdateSkyAndLightingParams();
461 }
462
463 /**
464  * Return the GMT as a string.
465  */
466 static string 
467 getGMTString ()
468 {
469   string out;
470   char buf[16];
471   struct tm * t = globals->get_time_params()->getGmt();
472   sprintf(buf, " %.2d:%.2d:%.2d",
473           t->tm_hour, t->tm_min, t->tm_sec);
474   out = buf;
475   return out;
476 }
477
478
479 /**
480  * Get the texture rendering state.
481  */
482 static bool
483 getTextures ()
484 {
485   return (material_lib.get_step() == 0);
486 }
487
488
489 /**
490  * Set the texture rendering state.
491  */
492 static void
493 setTextures (bool textures)
494 {
495   if (textures)
496     material_lib.set_step(0);
497   else
498     material_lib.set_step(1);
499 }
500
501
502 /**
503  * Return the magnetic variation
504  */
505 static double
506 getMagVar ()
507 {
508   return globals->get_mag()->get_magvar() * SGD_RADIANS_TO_DEGREES;
509 }
510
511
512 /**
513  * Return the magnetic dip
514  */
515 static double
516 getMagDip ()
517 {
518   return globals->get_mag()->get_magdip() * SGD_RADIANS_TO_DEGREES;
519 }
520
521
522 /**
523  * Return the current heading in degrees.
524  */
525 static double
526 getHeadingMag ()
527 {
528   return current_aircraft.fdm_state->get_Psi() * SGD_RADIANS_TO_DEGREES - getMagVar();
529 }
530
531
532 /**
533  * Return the fuel level in tank 1
534  */
535 static double
536 getTank1Fuel ()
537 {
538   return current_aircraft.fdm_state->get_Tank1Fuel();
539 }
540
541 static void
542 setTank1Fuel ( double gals )
543 {
544   current_aircraft.fdm_state->set_Tank1Fuel( gals );
545 }
546
547 /**
548  * Return the fuel level in tank 2
549  */
550 static double
551 getTank2Fuel ()
552 {
553   return current_aircraft.fdm_state->get_Tank2Fuel();
554 }
555
556 static void
557 setTank2Fuel ( double gals )
558 {
559   current_aircraft.fdm_state->set_Tank2Fuel( gals );
560 }
561
562
563 /**
564  * Get the autopilot altitude lock (true=on).
565  */
566 static bool
567 getAPAltitudeLock ()
568 {
569     return (current_autopilot->get_AltitudeEnabled() &&
570             current_autopilot->get_AltitudeMode()
571             == FGAutopilot::FG_ALTITUDE_LOCK);
572 }
573
574
575 /**
576  * Set the autopilot altitude lock (true=on).
577  */
578 static void
579 setAPAltitudeLock (bool lock)
580 {
581   current_autopilot->set_AltitudeMode(FGAutopilot::FG_ALTITUDE_LOCK);
582   current_autopilot->set_AltitudeEnabled(lock);
583 }
584
585
586 /**
587  * Get the autopilot target altitude in feet.
588  */
589 static double
590 getAPAltitude ()
591 {
592   return current_autopilot->get_TargetAltitude() * SG_METER_TO_FEET;
593 }
594
595
596 /**
597  * Set the autopilot target altitude in feet.
598  */
599 static void
600 setAPAltitude (double altitude)
601 {
602     current_autopilot->set_TargetAltitude( altitude * SG_FEET_TO_METER );
603 }
604
605 /**
606  * Get the autopilot altitude lock (true=on).
607  */
608 static bool
609 getAPGSLock ()
610 {
611     return (current_autopilot->get_AltitudeEnabled() &&
612             (current_autopilot->get_AltitudeMode()
613              == FGAutopilot::FG_ALTITUDE_GS1));
614 }
615
616
617 /**
618  * Set the autopilot altitude lock (true=on).
619  */
620 static void
621 setAPGSLock (bool lock)
622 {
623   current_autopilot->set_AltitudeMode(FGAutopilot::FG_ALTITUDE_GS1);
624   current_autopilot->set_AltitudeEnabled(lock);
625 }
626
627
628 /**
629  * Get the autopilot terrain lock (true=on).
630  */
631 static bool
632 getAPTerrainLock ()
633 {
634     return (current_autopilot->get_AltitudeEnabled() &&
635             (current_autopilot->get_AltitudeMode()
636              == FGAutopilot::FG_ALTITUDE_TERRAIN));
637 }
638
639
640 /**
641  * Set the autopilot terrain lock (true=on).
642  */
643 static void
644 setAPTerrainLock (bool lock)
645 {
646   current_autopilot->set_AltitudeMode(FGAutopilot::FG_ALTITUDE_TERRAIN);
647   current_autopilot->set_AltitudeEnabled(lock);
648   current_autopilot->set_TargetAGL(
649       current_aircraft.fdm_state->get_Altitude_AGL() * SG_FEET_TO_METER
650     );
651   cout << "Target AGL = "
652        << current_aircraft.fdm_state->get_Altitude_AGL() * SG_FEET_TO_METER
653        << endl;
654 }
655
656
657 /**
658  * Get the autopilot target altitude in feet.
659  */
660 static double
661 getAPClimb ()
662 {
663   return current_autopilot->get_TargetClimbRate() * SG_METER_TO_FEET;
664 }
665
666
667 /**
668  * Set the autopilot target altitude in feet.
669  */
670 static void
671 setAPClimb (double rate)
672 {
673     current_autopilot->set_TargetClimbRate( rate * SG_FEET_TO_METER );
674 }
675
676
677 /**
678  * Get the autopilot heading lock (true=on).
679  */
680 static bool
681 getAPHeadingLock ()
682 {
683     return
684       (current_autopilot->get_HeadingEnabled() &&
685        current_autopilot->get_HeadingMode() == DEFAULT_AP_HEADING_LOCK);
686 }
687
688
689 /**
690  * Set the autopilot heading lock (true=on).
691  */
692 static void
693 setAPHeadingLock (bool lock)
694 {
695     if (lock) {
696         current_autopilot->set_HeadingMode(DEFAULT_AP_HEADING_LOCK);
697         current_autopilot->set_HeadingEnabled(true);
698     } else {
699         current_autopilot->set_HeadingEnabled(false);
700     }
701 }
702
703
704 /**
705  * Get the autopilot heading bug in degrees.
706  */
707 static double
708 getAPHeadingBug ()
709 {
710   return current_autopilot->get_DGTargetHeading();
711 }
712
713
714 /**
715  * Set the autopilot heading bug in degrees.
716  */
717 static void
718 setAPHeadingBug (double heading)
719 {
720   current_autopilot->set_DGTargetHeading( heading );
721 }
722
723
724 /**
725  * Get the autopilot wing leveler lock (true=on).
726  */
727 static bool
728 getAPWingLeveler ()
729 {
730     return
731       (current_autopilot->get_HeadingEnabled() &&
732        current_autopilot->get_HeadingMode() == FGAutopilot::FG_TC_HEADING_LOCK);
733 }
734
735
736 /**
737  * Set the autopilot wing leveler lock (true=on).
738  */
739 static void
740 setAPWingLeveler (bool lock)
741 {
742     if (lock) {
743         current_autopilot->set_HeadingMode(FGAutopilot::FG_TC_HEADING_LOCK);
744         current_autopilot->set_HeadingEnabled(true);
745     } else {
746         current_autopilot->set_HeadingEnabled(false);
747     }
748 }
749
750 /**
751  * Return true if the autopilot is locked to NAV1.
752  */
753 static bool
754 getAPNAV1Lock ()
755 {
756   return
757     (current_autopilot->get_HeadingEnabled() &&
758      current_autopilot->get_HeadingMode() == FGAutopilot::FG_HEADING_NAV1);
759 }
760
761
762 /**
763  * Set the autopilot NAV1 lock.
764  */
765 static void
766 setAPNAV1Lock (bool lock)
767 {
768   if (lock) {
769     current_autopilot->set_HeadingMode(FGAutopilot::FG_HEADING_NAV1);
770     current_autopilot->set_HeadingEnabled(true);
771   } else if (current_autopilot->get_HeadingMode() ==
772              FGAutopilot::FG_HEADING_NAV1) {
773     current_autopilot->set_HeadingEnabled(false);
774   }
775 }
776
777 /**
778  * Get the autopilot autothrottle lock.
779  */
780 static bool
781 getAPAutoThrottleLock ()
782 {
783   return current_autopilot->get_AutoThrottleEnabled();
784 }
785
786
787 /**
788  * Set the autothrottle lock.
789  */
790 static void
791 setAPAutoThrottleLock (bool lock)
792 {
793   current_autopilot->set_AutoThrottleEnabled(lock);
794 }
795
796
797 // kludge
798 static double
799 getAPRudderControl ()
800 {
801     if (getAPHeadingLock())
802         return current_autopilot->get_TargetHeading();
803     else
804         return globals->get_controls()->get_rudder();
805 }
806
807 // kludge
808 static void
809 setAPRudderControl (double value)
810 {
811     if (getAPHeadingLock()) {
812         SG_LOG(SG_GENERAL, SG_DEBUG, "setAPRudderControl " << value );
813         value -= current_autopilot->get_TargetHeading();
814         current_autopilot->HeadingAdjust(value < 0.0 ? -1.0 : 1.0);
815     } else {
816         globals->get_controls()->set_rudder(value);
817     }
818 }
819
820 // kludge
821 static double
822 getAPElevatorControl ()
823 {
824   if (getAPAltitudeLock())
825       return current_autopilot->get_TargetAltitude();
826   else
827     return globals->get_controls()->get_elevator();
828 }
829
830 // kludge
831 static void
832 setAPElevatorControl (double value)
833 {
834     if (getAPAltitudeLock()) {
835         SG_LOG(SG_GENERAL, SG_DEBUG, "setAPElevatorControl " << value );
836         value -= current_autopilot->get_TargetAltitude();
837         current_autopilot->AltitudeAdjust(value < 0.0 ? 100.0 : -100.0);
838     } else {
839         globals->get_controls()->set_elevator(value);
840     }
841 }
842
843 // kludge
844 static double
845 getAPThrottleControl ()
846 {
847   if (getAPAutoThrottleLock())
848     return 0.0;                 // always resets
849   else
850     return globals->get_controls()->get_throttle(0);
851 }
852
853 // kludge
854 static void
855 setAPThrottleControl (double value)
856 {
857   if (getAPAutoThrottleLock())
858     current_autopilot->AutoThrottleAdjust(value < 0.0 ? -0.01 : 0.01);
859   else
860     globals->get_controls()->set_throttle(FGControls::ALL_ENGINES, value);
861 }
862
863
864 /**
865  * Get the current visibility (meters).
866  */
867 static double
868 getVisibility ()
869 {
870 #ifndef FG_OLD_WEATHER
871   return WeatherDatabase->getWeatherVisibility();
872 #else
873   return current_weather.get_visibility();
874 #endif
875 }
876
877
878 /**
879  * Set the current visibility (meters).
880  */
881 static void
882 setVisibility (double visibility)
883 {
884 #ifndef FG_OLD_WEATHER
885   WeatherDatabase->setWeatherVisibility(visibility);
886 #else
887   current_weather.set_visibility(visibility);
888 #endif
889 }
890
891 /**
892  * Get the current wind north velocity (feet/second).
893  */
894 static double
895 getWindNorth ()
896 {
897   return current_aircraft.fdm_state->get_V_north_airmass();
898 }
899
900
901 /**
902  * Set the current wind north velocity (feet/second).
903  */
904 static void
905 setWindNorth (double speed)
906 {
907   current_aircraft.fdm_state
908     ->set_Velocities_Local_Airmass(speed, getWindEast(), getWindDown());
909 }
910
911
912 /**
913  * Get the current wind east velocity (feet/second).
914  */
915 static double
916 getWindEast ()
917 {
918   return current_aircraft.fdm_state->get_V_east_airmass();
919 }
920
921
922 /**
923  * Set the current wind east velocity (feet/second).
924  */
925 static void
926 setWindEast (double speed)
927 {
928   cout << "Set wind-east to " << speed << endl;
929   current_aircraft.fdm_state->set_Velocities_Local_Airmass(getWindNorth(),
930                                                            speed,
931                                                            getWindDown());
932 }
933
934
935 /**
936  * Get the current wind down velocity (feet/second).
937  */
938 static double
939 getWindDown ()
940 {
941   return current_aircraft.fdm_state->get_V_down_airmass();
942 }
943
944
945 /**
946  * Set the current wind down velocity (feet/second).
947  */
948 static void
949 setWindDown (double speed)
950 {
951   current_aircraft.fdm_state->set_Velocities_Local_Airmass(getWindNorth(),
952                                                            getWindEast(),
953                                                            speed);
954 }
955
956 /*
957  * Set the current engine0 running flag.
958  */
959 static void
960 setRunningFlag (bool flag)
961 {
962   if ( current_aircraft.fdm_state->get_num_engines() > 0 ) {
963     current_aircraft.fdm_state->get_engine(0)->set_Running_Flag( flag );
964   }
965 }
966
967 /*
968  * Set the current engine0 cranking flag.
969  */
970 //Although there is no real reason to want to tell the engine that it is cranking,
971 //this is currently necessary to avoid the cranking sound being played 
972 //before the engine inits.
973 static void
974 setCrankingFlag (bool flag)
975 {
976   if ( current_aircraft.fdm_state->get_num_engines() > 0 ) {
977     current_aircraft.fdm_state->get_engine(0)->set_Cranking_Flag( flag );
978   }
979 }
980
981 static double
982 getFOV ()
983 {
984   return globals->get_current_view()->get_fov();
985 }
986
987 static void
988 setFOV (double fov)
989 {
990   if ( fov < 180 ) {
991       globals->get_current_view()->set_fov( fov );
992   }
993 }
994
995 static long
996 getWarp ()
997 {
998   return globals->get_warp();
999 }
1000
1001 static void
1002 setWarp (long warp)
1003 {
1004   globals->set_warp(warp);
1005 }
1006
1007 static long
1008 getWarpDelta ()
1009 {
1010   return globals->get_warp_delta();
1011 }
1012
1013 static void
1014 setWarpDelta (long delta)
1015 {
1016   globals->set_warp_delta(delta);
1017 }
1018
1019 static void
1020 setViewAxisLong (double axis)
1021 {
1022   axisLong = axis;
1023 }
1024
1025 static void
1026 setViewAxisLat (double axis)
1027 {
1028   axisLat = axis;
1029 }
1030
1031 static bool
1032 getWindingCCW ()
1033 {
1034   return winding_ccw;
1035 }
1036
1037 static void
1038 setWindingCCW (bool state)
1039 {
1040   winding_ccw = state;
1041   if ( winding_ccw )
1042     glFrontFace ( GL_CCW );
1043   else
1044     glFrontFace ( GL_CW );
1045 }
1046
1047 static bool
1048 getFullScreen ()
1049 {
1050 #if defined(FX) && !defined(WIN32)
1051   return global_fullscreen;
1052 #else
1053   return false;
1054 #endif
1055 }
1056
1057 static void
1058 setFullScreen (bool state)
1059 {
1060 #if defined(FX) && !defined(WIN32)
1061   global_fullscreen = state;
1062 #  if defined(XMESA_FX_FULLSCREEN) && defined(XMESA_FX_WINDOW)
1063   XMesaSetFXmode( global_fullscreen ? XMESA_FX_FULLSCREEN : XMESA_FX_WINDOW );
1064 #  endif
1065 #endif
1066 }
1067
1068 static bool
1069 getFDMDataLogging ()
1070 {
1071   return fdm_data_logging;
1072 }
1073
1074 static void
1075 setFDMDataLogging (bool state)
1076 {
1077                                 // kludge; no getter or setter available
1078   if (state != fdm_data_logging) {
1079     fgToggleFDMdataLogging();
1080     fdm_data_logging = state;
1081   }
1082 }
1083
1084 \f
1085 ////////////////////////////////////////////////////////////////////////
1086 // Tie the properties.
1087 ////////////////////////////////////////////////////////////////////////
1088
1089 void
1090 fgInitProps ()
1091 {
1092                                 // Simulation
1093   fgTie("/sim/logging/priority", getLoggingPriority, setLoggingPriority);
1094   fgTie("/sim/logging/classes", getLoggingClasses, setLoggingClasses);
1095   fgTie("/sim/freeze", getFreeze, setFreeze);
1096   fgTie("/sim/aircraft-dir", getAircraftDir, setAircraftDir);
1097   fgTie("/sim/view/offset-deg", getViewOffset, setViewOffset, false);
1098   fgSetArchivable("/sim/view/offset-deg");
1099   fgTie("/sim/view/goal-offset-deg", getGoalViewOffset, setGoalViewOffset, false);
1100   fgSetArchivable("/sim/view/goal-offset-deg");
1101   fgTie("/sim/view/pilot/x-offset-m",
1102         getPilotPositionXOffset, setPilotPositionXOffset);
1103   fgSetArchivable("/sim/view/pilot/x-offset-m");
1104   fgTie("/sim/view/pilot/y-offset-m",
1105         getPilotPositionYOffset, setPilotPositionYOffset);
1106   fgSetArchivable("/sim/view/pilot/y-offset-m");
1107   fgTie("/sim/view/pilot/z-offset-m",
1108         getPilotPositionZOffset, setPilotPositionZOffset);
1109   fgSetArchivable("/sim/view/pilot/z-offset-m");
1110   fgTie("/sim/time/gmt", getDateString, setDateString);
1111   fgSetArchivable("/sim/time/gmt");
1112   fgTie("/sim/time/gmt-string", getGMTString);
1113   fgTie("/sim/rendering/textures", getTextures, setTextures);
1114
1115                                 // Orientation
1116   fgTie("/orientation/heading-magnetic-deg", getHeadingMag);
1117
1118   //consumables
1119   fgTie("/consumables/fuel/tank[0]/level-gal_us",
1120         getTank1Fuel, setTank1Fuel, false);
1121   fgSetArchivable("/consumables/fuel/tank[0]/level-gal_us");
1122   fgTie("/consumables/fuel/tank[1]/level-gal_us",
1123         getTank2Fuel, setTank2Fuel, false);
1124   fgSetArchivable("/consumables/fuel/tank[1]/level-gal_us");
1125
1126                                 // Autopilot
1127   fgTie("/autopilot/locks/altitude", getAPAltitudeLock, setAPAltitudeLock);
1128   fgSetArchivable("/autopilot/locks/altitude");
1129   fgTie("/autopilot/settings/altitude-ft", getAPAltitude, setAPAltitude);
1130   fgSetArchivable("/autopilot/settings/altitude-ft");
1131   fgTie("/autopilot/locks/glide-slope", getAPGSLock, setAPGSLock);
1132   fgSetArchivable("/autopilot/locks/glide-slope");
1133   fgTie("/autopilot/locks/terrain", getAPTerrainLock, setAPTerrainLock);
1134   fgSetArchivable("/autopilot/locks/terrain");
1135   fgTie("/autopilot/settings/agl-ft", getAPAltitude, setAPAltitude);
1136   fgSetArchivable("/autopilot/settings/agl-ft");
1137   fgTie("/autopilot/settings/climb-rate-fpm", getAPClimb, setAPClimb, false);
1138   fgSetArchivable("/autopilot/settings/climb-rate-fpm");
1139   fgTie("/autopilot/locks/heading", getAPHeadingLock, setAPHeadingLock);
1140   fgSetArchivable("/autopilot/locks/heading");
1141   fgTie("/autopilot/settings/heading-bug-deg",
1142         getAPHeadingBug, setAPHeadingBug, false);
1143   fgSetArchivable("/autopilot/settings/heading-bug-deg");
1144   fgTie("/autopilot/locks/wing-leveler", getAPWingLeveler, setAPWingLeveler);
1145   fgSetArchivable("/autopilot/locks/wing-leveler");
1146   fgTie("/autopilot/locks/nav[0]", getAPNAV1Lock, setAPNAV1Lock);
1147   fgSetArchivable("/autopilot/locks/nav[0]");
1148   fgTie("/autopilot/locks/auto-throttle",
1149         getAPAutoThrottleLock, setAPAutoThrottleLock);
1150   fgSetArchivable("/autopilot/locks/auto-throttle");
1151   fgTie("/autopilot/control-overrides/rudder",
1152         getAPRudderControl, setAPRudderControl);
1153   fgSetArchivable("/autopilot/control-overrides/rudder");
1154   fgTie("/autopilot/control-overrides/elevator",
1155         getAPElevatorControl, setAPElevatorControl);
1156   fgSetArchivable("/autopilot/control-overrides/elevator");
1157   fgTie("/autopilot/control-overrides/throttle",
1158         getAPThrottleControl, setAPThrottleControl);
1159   fgSetArchivable("/autopilot/control-overrides/throttle");
1160
1161                                 // Environment
1162   fgTie("/environment/visibility-m", getVisibility, setVisibility);
1163   fgSetArchivable("/environment/visibility-m");
1164   fgTie("/environment/wind-north-fps", getWindNorth, setWindNorth);
1165   fgSetArchivable("/environment/wind-north-fps");
1166   fgTie("/environment/wind-east-fps", getWindEast, setWindEast);
1167   fgSetArchivable("/environment/wind-east-fps");
1168   fgTie("/environment/wind-down-fps", getWindDown, setWindDown);
1169   fgSetArchivable("/environment/wind-down-fps");
1170
1171   fgTie("/environment/magnetic-variation-deg", getMagVar);
1172   fgTie("/environment/magnetic-dip-deg", getMagDip);
1173
1174                                 // View
1175   fgTie("/sim/field-of-view", getFOV, setFOV);
1176   fgSetArchivable("/sim/field-of-view");
1177   fgTie("/sim/time/warp", getWarp, setWarp, false);
1178   fgTie("/sim/time/warp-delta", getWarpDelta, setWarpDelta);
1179   fgTie("/sim/view/axes/long", (double(*)())0, setViewAxisLong);
1180   fgTie("/sim/view/axes/lat", (double(*)())0, setViewAxisLat);
1181
1182                                 // Misc. Temporary junk.
1183   fgTie("/sim/temp/winding-ccw", getWindingCCW, setWindingCCW, false);
1184   fgTie("/sim/temp/full-screen", getFullScreen, setFullScreen);
1185   fgTie("/sim/temp/fdm-data-logging", getFDMDataLogging, setFDMDataLogging);
1186         
1187 }
1188
1189
1190 void
1191 fgUpdateProps ()
1192 {
1193   _set_view_from_axes();
1194 }
1195
1196
1197 \f
1198 ////////////////////////////////////////////////////////////////////////
1199 // Save and restore.
1200 ////////////////////////////////////////////////////////////////////////
1201
1202
1203 /**
1204  * Save the current state of the simulator to a stream.
1205  */
1206 bool
1207 fgSaveFlight (ostream &output)
1208 {
1209   try {
1210     writeProperties(output, globals->get_props());
1211   } catch (const sg_exception &e) {
1212     guiErrorMessage("Error saving flight: ", e);
1213     return false;
1214   }
1215   return true;
1216 }
1217
1218
1219 /**
1220  * Restore the current state of the simulator from a stream.
1221  */
1222 bool
1223 fgLoadFlight (istream &input)
1224 {
1225   SGPropertyNode props;
1226   try {
1227     readProperties(input, &props);
1228   } catch (const sg_exception &e) {
1229     guiErrorMessage("Error reading saved flight: ", e);
1230     return false;
1231   }
1232   copyProperties(&props, globals->get_props());
1233   // When loading a flight, make it the
1234   // new initial state.
1235   globals->saveInitialState();
1236   return true;
1237 }
1238
1239
1240 \f
1241 ////////////////////////////////////////////////////////////////////////
1242 // Implementation of FGCondition.
1243 ////////////////////////////////////////////////////////////////////////
1244
1245 FGCondition::FGCondition ()
1246 {
1247 }
1248
1249 FGCondition::~FGCondition ()
1250 {
1251 }
1252
1253
1254 \f
1255 ////////////////////////////////////////////////////////////////////////
1256 // Implementation of FGPropertyCondition.
1257 ////////////////////////////////////////////////////////////////////////
1258
1259 FGPropertyCondition::FGPropertyCondition (const string &propname)
1260   : _node(fgGetNode(propname, true))
1261 {
1262 }
1263
1264 FGPropertyCondition::~FGPropertyCondition ()
1265 {
1266 }
1267
1268
1269 \f
1270 ////////////////////////////////////////////////////////////////////////
1271 // Implementation of FGNotCondition.
1272 ////////////////////////////////////////////////////////////////////////
1273
1274 FGNotCondition::FGNotCondition (FGCondition * condition)
1275   : _condition(condition)
1276 {
1277 }
1278
1279 FGNotCondition::~FGNotCondition ()
1280 {
1281   delete _condition;
1282 }
1283
1284 bool
1285 FGNotCondition::test () const
1286 {
1287   return !(_condition->test());
1288 }
1289
1290
1291 \f
1292 ////////////////////////////////////////////////////////////////////////
1293 // Implementation of FGAndCondition.
1294 ////////////////////////////////////////////////////////////////////////
1295
1296 FGAndCondition::FGAndCondition ()
1297 {
1298 }
1299
1300 FGAndCondition::~FGAndCondition ()
1301 {
1302   for (unsigned int i = 0; i < _conditions.size(); i++)
1303     delete _conditions[i];
1304 }
1305
1306 bool
1307 FGAndCondition::test () const
1308 {
1309   int nConditions = _conditions.size();
1310   for (int i = 0; i < nConditions; i++) {
1311     if (!_conditions[i]->test())
1312       return false;
1313   }
1314   return true;
1315 }
1316
1317 void
1318 FGAndCondition::addCondition (FGCondition * condition)
1319 {
1320   _conditions.push_back(condition);
1321 }
1322
1323
1324 \f
1325 ////////////////////////////////////////////////////////////////////////
1326 // Implementation of FGOrCondition.
1327 ////////////////////////////////////////////////////////////////////////
1328
1329 FGOrCondition::FGOrCondition ()
1330 {
1331 }
1332
1333 FGOrCondition::~FGOrCondition ()
1334 {
1335   for (unsigned int i = 0; i < _conditions.size(); i++)
1336     delete _conditions[i];
1337 }
1338
1339 bool
1340 FGOrCondition::test () const
1341 {
1342   int nConditions = _conditions.size();
1343   for (int i = 0; i < nConditions; i++) {
1344     if (_conditions[i]->test())
1345       return true;
1346   }
1347   return false;
1348 }
1349
1350 void
1351 FGOrCondition::addCondition (FGCondition * condition)
1352 {
1353   _conditions.push_back(condition);
1354 }
1355
1356
1357 \f
1358 ////////////////////////////////////////////////////////////////////////
1359 // Implementation of FGComparisonCondition.
1360 ////////////////////////////////////////////////////////////////////////
1361
1362 static int
1363 doComparison (const SGPropertyNode * left, const SGPropertyNode *right)
1364 {
1365   switch (left->getType()) {
1366   case SGPropertyNode::BOOL: {
1367     bool v1 = left->getBoolValue();
1368     bool v2 = right->getBoolValue();
1369     if (v1 < v2)
1370       return FGComparisonCondition::LESS_THAN;
1371     else if (v1 > v2)
1372       return FGComparisonCondition::GREATER_THAN;
1373     else
1374       return FGComparisonCondition::EQUALS;
1375     break;
1376   }
1377   case SGPropertyNode::INT: {
1378     int v1 = left->getIntValue();
1379     int v2 = right->getIntValue();
1380     if (v1 < v2)
1381       return FGComparisonCondition::LESS_THAN;
1382     else if (v1 > v2)
1383       return FGComparisonCondition::GREATER_THAN;
1384     else
1385       return FGComparisonCondition::EQUALS;
1386     break;
1387   }
1388   case SGPropertyNode::LONG: {
1389     long v1 = left->getLongValue();
1390     long v2 = right->getLongValue();
1391     if (v1 < v2)
1392       return FGComparisonCondition::LESS_THAN;
1393     else if (v1 > v2)
1394       return FGComparisonCondition::GREATER_THAN;
1395     else
1396       return FGComparisonCondition::EQUALS;
1397     break;
1398   }
1399   case SGPropertyNode::FLOAT: {
1400     float v1 = left->getFloatValue();
1401     float v2 = right->getFloatValue();
1402     if (v1 < v2)
1403       return FGComparisonCondition::LESS_THAN;
1404     else if (v1 > v2)
1405       return FGComparisonCondition::GREATER_THAN;
1406     else
1407       return FGComparisonCondition::EQUALS;
1408     break;
1409   }
1410   case SGPropertyNode::DOUBLE: {
1411     double v1 = left->getDoubleValue();
1412     double v2 = right->getDoubleValue();
1413     if (v1 < v2)
1414       return FGComparisonCondition::LESS_THAN;
1415     else if (v1 > v2)
1416       return FGComparisonCondition::GREATER_THAN;
1417     else
1418       return FGComparisonCondition::EQUALS;
1419     break;
1420   }
1421   case SGPropertyNode::STRING: 
1422   case SGPropertyNode::NONE:
1423   case SGPropertyNode::UNSPECIFIED: {
1424     string v1 = left->getStringValue();
1425     string v2 = right->getStringValue();
1426     if (v1 < v2)
1427       return FGComparisonCondition::LESS_THAN;
1428     else if (v1 > v2)
1429       return FGComparisonCondition::GREATER_THAN;
1430     else
1431       return FGComparisonCondition::EQUALS;
1432     break;
1433   }
1434   }
1435   throw sg_exception("Unrecognized node type");
1436   return 0;
1437 }
1438
1439
1440 FGComparisonCondition::FGComparisonCondition (Type type, bool reverse)
1441   : _type(type),
1442     _reverse(reverse),
1443     _left_property(0),
1444     _right_property(0),
1445     _right_value(0)
1446 {
1447 }
1448
1449 FGComparisonCondition::~FGComparisonCondition ()
1450 {
1451   delete _right_value;
1452 }
1453
1454 bool
1455 FGComparisonCondition::test () const
1456 {
1457                                 // Always fail if incompletely specified
1458   if (_left_property == 0 ||
1459       (_right_property == 0 && _right_value == 0))
1460     return false;
1461
1462                                 // Get LESS_THAN, EQUALS, or GREATER_THAN
1463   int cmp =
1464     doComparison(_left_property,
1465                  (_right_property != 0 ? _right_property : _right_value));
1466   if (!_reverse)
1467     return (cmp == _type);
1468   else
1469     return (cmp != _type);
1470 }
1471
1472 void
1473 FGComparisonCondition::setLeftProperty (const string &propname)
1474 {
1475   _left_property = fgGetNode(propname, true);
1476 }
1477
1478 void
1479 FGComparisonCondition::setRightProperty (const string &propname)
1480 {
1481   delete _right_value;
1482   _right_value = 0;
1483   _right_property = fgGetNode(propname, true);
1484 }
1485
1486 void
1487 FGComparisonCondition::setRightValue (const SGPropertyNode *node)
1488 {
1489   _right_property = 0;
1490   delete _right_value;
1491   _right_value = new SGPropertyNode(*node);
1492 }
1493
1494
1495 \f
1496 ////////////////////////////////////////////////////////////////////////
1497 // Read a condition and use it if necessary.
1498 ////////////////////////////////////////////////////////////////////////
1499
1500                                 // Forward declaration
1501 static FGCondition * readCondition (const SGPropertyNode * node);
1502
1503 static FGCondition *
1504 readPropertyCondition (const SGPropertyNode * node)
1505 {
1506   return new FGPropertyCondition(node->getStringValue());
1507 }
1508
1509 static FGCondition *
1510 readNotCondition (const SGPropertyNode * node)
1511 {
1512   int nChildren = node->nChildren();
1513   for (int i = 0; i < nChildren; i++) {
1514     const SGPropertyNode * child = node->getChild(i);
1515     FGCondition * condition = readCondition(child);
1516     if (condition != 0)
1517       return new FGNotCondition(condition);
1518   }
1519   SG_LOG(SG_COCKPIT, SG_ALERT, "Panel: empty 'not' condition");
1520   return 0;
1521 }
1522
1523 static FGCondition *
1524 readAndConditions (const SGPropertyNode * node)
1525 {
1526   FGAndCondition * andCondition = new FGAndCondition;
1527   int nChildren = node->nChildren();
1528   for (int i = 0; i < nChildren; i++) {
1529     const SGPropertyNode * child = node->getChild(i);
1530     FGCondition * condition = readCondition(child);
1531     if (condition != 0)
1532       andCondition->addCondition(condition);
1533   }
1534   return andCondition;
1535 }
1536
1537 static FGCondition *
1538 readOrConditions (const SGPropertyNode * node)
1539 {
1540   FGOrCondition * orCondition = new FGOrCondition;
1541   int nChildren = node->nChildren();
1542   for (int i = 0; i < nChildren; i++) {
1543     const SGPropertyNode * child = node->getChild(i);
1544     FGCondition * condition = readCondition(child);
1545     if (condition != 0)
1546       orCondition->addCondition(condition);
1547   }
1548   return orCondition;
1549 }
1550
1551 static FGCondition *
1552 readComparison (const SGPropertyNode * node,
1553                 FGComparisonCondition::Type type,
1554                 bool reverse)
1555 {
1556   FGComparisonCondition * condition = new FGComparisonCondition(type, reverse);
1557   condition->setLeftProperty(node->getStringValue("property[0]"));
1558   if (node->hasValue("property[1]"))
1559     condition->setRightProperty(node->getStringValue("property[1]"));
1560   else
1561     condition->setRightValue(node->getChild("value", 0));
1562
1563   return condition;
1564 }
1565
1566 static FGCondition *
1567 readCondition (const SGPropertyNode * node)
1568 {
1569   const string &name = node->getName();
1570   if (name == "property")
1571     return readPropertyCondition(node);
1572   else if (name == "not")
1573     return readNotCondition(node);
1574   else if (name == "and")
1575     return readAndConditions(node);
1576   else if (name == "or")
1577     return readOrConditions(node);
1578   else if (name == "less-than")
1579     return readComparison(node, FGComparisonCondition::LESS_THAN, false);
1580   else if (name == "less-than-equals")
1581     return readComparison(node, FGComparisonCondition::GREATER_THAN, true);
1582   else if (name == "greater-than")
1583     return readComparison(node, FGComparisonCondition::GREATER_THAN, false);
1584   else if (name == "greater-than-equals")
1585     return readComparison(node, FGComparisonCondition::LESS_THAN, true);
1586   else if (name == "equals")
1587     return readComparison(node, FGComparisonCondition::EQUALS, false);
1588   else if (name == "not-equals")
1589     return readComparison(node, FGComparisonCondition::EQUALS, true);
1590   else
1591     return 0;
1592 }
1593
1594
1595 \f
1596 ////////////////////////////////////////////////////////////////////////
1597 // Implementation of FGConditional.
1598 ////////////////////////////////////////////////////////////////////////
1599
1600 FGConditional::FGConditional ()
1601   : _condition (0)
1602 {
1603 }
1604
1605 FGConditional::~FGConditional ()
1606 {
1607   delete _condition;
1608 }
1609
1610 void
1611 FGConditional::setCondition (FGCondition * condition)
1612 {
1613   delete _condition;
1614   _condition = condition;
1615 }
1616
1617 bool
1618 FGConditional::test () const
1619 {
1620   return ((_condition == 0) || _condition->test());
1621 }
1622
1623
1624 \f
1625 // The top-level is always an implicit 'and' group
1626 FGCondition *
1627 fgReadCondition (const SGPropertyNode * node)
1628 {
1629   return readAndConditions(node);
1630 }
1631
1632
1633 // end of fg_props.cxx