]> git.mxchange.org Git - flightgear.git/blobdiff - src/AIModel/AIManager.cxx
commradio: improvements for atis speech
[flightgear.git] / src / AIModel / AIManager.cxx
index bf7fe5c7718e6ea00199a7091d90451fcc169ca6..27582d9ca708a2fb52a236acce96d99989006886 100644 (file)
 #include <simgear/props/props_io.hxx>
 #include <simgear/structure/exception.hxx>
 #include <simgear/structure/commands.hxx>
+#include <simgear/structure/SGBinding.hxx>
 
 #include <boost/mem_fn.hpp>
 #include <boost/foreach.hpp>
 
 #include <Main/globals.hxx>
 #include <Airports/airport.hxx>
+#include <Scripting/NasalSys.hxx>
 
 #include "AIManager.hxx"
 #include "AIAircraft.hxx"
@@ -50,8 +52,8 @@
 class FGAIManager::Scenario
 {
 public:
-    Scenario(FGAIManager* man, SGPropertyNode* scenarios) :
-        _manager(man)
+    Scenario(FGAIManager* man, const std::string& nm, SGPropertyNode* scenarios) :
+        _internalName(nm)
     {
         BOOST_FOREACH(SGPropertyNode* scEntry, scenarios->getChildren("entry")) {
             FGAIBasePtr ai = man->addObject(scEntry);
@@ -59,6 +61,21 @@ public:
                 _objects.push_back(ai);
             }
         } // of scenario entry iteration
+        
+        SGPropertyNode* nasalScripts = scenarios->getChild("nasal");
+        if (!nasalScripts) {
+            return;
+        }
+        
+        _unloadScript = nasalScripts->getStringValue("unload");
+        std::string loadScript = nasalScripts->getStringValue("load");
+        if (!loadScript.empty()) {
+            FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal");
+            std::string moduleName = "scenario_" + _internalName;
+            nasalSys->createModule(moduleName.c_str(), moduleName.c_str(),
+                                   loadScript.c_str(), loadScript.size(),
+                                   0);
+        }
     }
     
     ~Scenario()
@@ -66,10 +83,24 @@ public:
         BOOST_FOREACH(FGAIBasePtr ai, _objects) {
             ai->setDie(true);
         }
+        
+        FGNasalSys* nasalSys = (FGNasalSys*) globals->get_subsystem("nasal");
+        if (!nasalSys)
+            return;
+        
+        std::string moduleName = "scenario_" + _internalName;
+        if (!_unloadScript.empty()) {
+            nasalSys->createModule(moduleName.c_str(), moduleName.c_str(),
+                                   _unloadScript.c_str(), _unloadScript.size(),
+                                   0);
+        }
+        
+        nasalSys->deleteModule(moduleName.c_str());
     }
 private:
-    FGAIManager* _manager;
     std::vector<FGAIBasePtr> _objects;
+    std::string _internalName;
+    std::string _unloadScript;
 };
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -99,20 +130,18 @@ FGAIManager::init() {
     wind_from_north_node = fgGetNode("/environment/wind-from-north-fps",true);
 
     user_altitude_agl_node  = fgGetNode("/position/altitude-agl-ft", true);
-    user_yaw_node       = fgGetNode("/orientation/side-slip-deg", true);
     user_speed_node     = fgGetNode("/velocities/uBody-fps", true);
     
     globals->get_commands()->addCommand("load-scenario", this, &FGAIManager::loadScenarioCommand);
     globals->get_commands()->addCommand("unload-scenario", this, &FGAIManager::unloadScenarioCommand);
+    _environmentVisiblity = fgGetNode("/environment/visibility-m");
 }
 
 void
-FGAIManager::postinit() {
+FGAIManager::postinit()
+{
     // postinit, so that it can access the Nasal subsystem
 
-    if (!root->getBoolValue("scenarios-enabled", true))
-        return;
-
     // scenarios enabled, AI subsystem required
     if (!enabled->getBoolValue())
         enabled->setBoolValue(true);
@@ -136,8 +165,30 @@ FGAIManager::postinit() {
 void
 FGAIManager::reinit()
 {
+    // shutdown scenarios
+    unloadAllScenarios();
+    
     update(0.0);
     std::for_each(ai_list.begin(), ai_list.end(), boost::mem_fn(&FGAIBase::reinit));
+    
+    // (re-)load scenarios
+    postinit();
+}
+
+void
+FGAIManager::shutdown()
+{
+    unloadAllScenarios();
+    
+    BOOST_FOREACH(FGAIBase* ai, ai_list) {
+        ai->unbind();
+    }
+    
+    ai_list.clear();
+    _environmentVisiblity.clear();
+    
+    globals->get_commands()->removeCommand("load-scenario");
+    globals->get_commands()->removeCommand("unload-scenario");
 }
 
 void
@@ -189,12 +240,20 @@ FGAIManager::update(double dt) {
   
     ai_list.erase(ai_list.begin(), firstAlive);
   
-    // every remaining item is alive
+    // every remaining item is alive. update them in turn, but guard for
+    // exceptions, so a single misbehaving AI object doesn't bring down the
+    // entire subsystem.
     BOOST_FOREACH(FGAIBase* base, ai_list) {
-        if (base->isa(FGAIBase::otThermal)) {
-            processThermal(dt, (FGAIThermal*)base);
-        } else {
-            base->update(dt);
+        try {
+            if (base->isa(FGAIBase::otThermal)) {
+                processThermal(dt, (FGAIThermal*)base);
+            } else {
+                base->update(dt);
+            }
+        } catch (sg_exception& e) {
+            SG_LOG(SG_AI, SG_WARN, "caught exception updating AI model:" << base->_getName()<< ", which will be killed."
+                   "\n\tError:" << e.getFormattedMessage());
+            base->setDie(true);
         }
     } // of live AI objects iteration
 
@@ -241,8 +300,14 @@ FGAIManager::attach(FGAIBase *model)
     p->setBoolValue("valid", true);
 }
 
+bool FGAIManager::isVisible(const SGGeod& pos) const
+{
+  double visibility_meters = _environmentVisiblity->getDoubleValue();
+  return ( dist(globals->get_view_position_cart(), SGVec3d::fromGeod(pos)) ) <= visibility_meters;
+}
+
 int
-FGAIManager::getNumAiObjects(void) const
+FGAIManager::getNumAiObjects() const
 {
     return ai_list.size();
 }
@@ -250,9 +315,7 @@ FGAIManager::getNumAiObjects(void) const
 void
 FGAIManager::fetchUserState( void ) {
 
-    user_yaw       = user_yaw_node->getDoubleValue();
     globals->get_aircraft_orientation(user_heading, user_pitch, user_roll);
-
     user_speed     = user_speed_node->getDoubleValue() * 0.592484;
     wind_from_east = wind_from_east_node->getDoubleValue();
     wind_from_north   = wind_from_north_node->getDoubleValue();
@@ -275,14 +338,38 @@ FGAIManager::processThermal( double dt, FGAIThermal* thermal ) {
 bool FGAIManager::loadScenarioCommand(const SGPropertyNode* args)
 {
     std::string name = args->getStringValue("name");
-    return loadScenario(name);
+    if (args->hasChild("load-property")) {
+        // slightly ugly, to simplify life in the dialogs, make load allow
+        // loading or unloading based on a bool property.
+        bool loadIt = fgGetBool(args->getStringValue("load-property"));
+        if (!loadIt) {
+            // user actually wants to unload, fine.
+            return unloadScenario(name);
+        }
+    }
+    
+    if (_scenarios.find(name) != _scenarios.end()) {
+        SG_LOG(SG_AI, SG_WARN, "scenario '" << name << "' already loaded");
+        return false;
+    }
+    
+    bool ok = loadScenario(name);
+    if (ok) {
+        // create /sim/ai node for consistency
+        int index = 0;
+        for (; root->hasChild("scenario", index); ++index) {}
+        
+        SGPropertyNode* scenarioNode = root->getChild("scenario", index, true);
+        scenarioNode->setStringValue(name);
+    }
+    
+    return ok;
 }
 
 bool FGAIManager::unloadScenarioCommand(const SGPropertyNode* args)
 {
     std::string name = args->getStringValue("name");
-    unloadScenario(name);
-    return true;
+    return unloadScenario(name);
 }
 
 bool FGAIManager::addObjectCommand(const SGPropertyNode* definition)
@@ -338,6 +425,17 @@ bool FGAIManager::removeObject(const SGPropertyNode* args)
     return false;
 }
 
+FGAIBasePtr FGAIManager::getObjectFromProperty(const SGPropertyNode* aProp) const
+{
+    BOOST_FOREACH(FGAIBase* ai, get_ai_list()) {
+        if (ai->_getProps() == aProp) {
+            return ai;
+        }
+    } // of AI objects iteration
+    
+    return NULL;
+}
+
 bool
 FGAIManager::loadScenario( const string &filename )
 {
@@ -351,24 +449,49 @@ FGAIManager::loadScenario( const string &filename )
         return false;
     }
     
-    _scenarios[filename] = new Scenario(this, scNode);
+    _scenarios[filename] = new Scenario(this, filename, scNode);
     return true;
 }
 
 
-void
+bool
 FGAIManager::unloadScenario( const string &filename)
 {
     ScenarioDict::iterator it = _scenarios.find(filename);
     if (it == _scenarios.end()) {
         SG_LOG(SG_AI, SG_WARN, "unload scenario: not found:" << filename);
-        return;
+        return false;
+    }
+    
+// remove /sim/ai node
+    unsigned int index = 0;
+    for (SGPropertyNode* n = NULL; (n = root->getChild("scenario", index)) != NULL; ++index) {
+        if (n->getStringValue() == filename) {
+            root->removeChild("scenario", index);
+            break;
+        }
     }
     
     delete it->second;
     _scenarios.erase(it);
+    return true;
 }
 
+void
+FGAIManager::unloadAllScenarios()
+{
+    ScenarioDict::iterator it = _scenarios.begin();
+    for (; it != _scenarios.end(); ++it) {
+        delete it->second;
+    } // of scenarios iteration
+    
+    
+    // remove /sim/ai node
+    root->removeChildren("scenario");
+    _scenarios.clear();
+}
+
+
 SGPropertyNode_ptr
 FGAIManager::loadScenarioFile(const std::string& filename)
 {
@@ -454,7 +577,7 @@ FGAIManager::calcCollision(double alt, double lat, double lon, double fuse_range
 
         int id         = (*ai_list_itr)->getID();
 
-        double range = calcRange(cartPos, (*ai_list_itr));
+        double range = calcRangeFt(cartPos, (*ai_list_itr));
 
         //SG_LOG(SG_AI, SG_DEBUG, "AIManager:  AI list size "
         //    << ai_list.size()
@@ -482,7 +605,7 @@ FGAIManager::calcCollision(double alt, double lat, double lon, double fuse_range
 }
 
 double
-FGAIManager::calcRange(const SGVec3d& aCartPos, FGAIBase* aObject) const
+FGAIManager::calcRangeFt(const SGVec3d& aCartPos, FGAIBase* aObject) const
 {
     double distM = dist(aCartPos, aObject->getCartPos());
     return distM * SG_METER_TO_FEET;