]> git.mxchange.org Git - flightgear.git/blobdiff - src/AIModel/submodel.cxx
Bugfix: set life timer to 0 on instantiation and when ballistic object dies.
[flightgear.git] / src / AIModel / submodel.cxx
index 7d065b8e10827259268d903d4a002b1cd2fa4fb8..a25212198584427cbcb3fa74c4013d6cce694153 100644 (file)
@@ -13,6 +13,7 @@
 #include <simgear/structure/exception.hxx>
 #include <simgear/misc/sg_path.hxx>
 #include <simgear/math/sg_geodesy.hxx>
+#include <simgear/props/props_io.hxx>
 
 #include <Main/fg_props.hxx>
 #include <Main/util.hxx>
@@ -27,22 +28,26 @@ const double FGSubmodelMgr::lbs_to_slugs = 0.031080950172;
 
 FGSubmodelMgr::FGSubmodelMgr()
 {
-    x_offset = y_offset = 0.0;
-    z_offset = -4.0;
-    pitch_offset = 2.0;
+    x_offset = y_offset = z_offset = 0.0;
+    pitch_offset = 0.0;
     yaw_offset = 0.0;
 
-    out[0] = out[1] = out[2] = 0;
-    in[3] = out[3] = 1;
+    //out[0] = out[1] = out[2] = 0;
     string contents_node;
     contrail_altitude = 30000;
     _count = 0;
+    _found_sub = true;
 }
 
 FGSubmodelMgr::~FGSubmodelMgr()
 {
 }
 
+FGAIManager* FGSubmodelMgr::aiManager()
+{
+   return (FGAIManager*)globals->get_subsystem("ai_model");
+}
+
 void FGSubmodelMgr::init()
 {
     index = 0;
@@ -74,17 +79,22 @@ void FGSubmodelMgr::init()
     _contrail_trigger       = fgGetNode("ai/submodels/contrails", true);
     _contrail_trigger->setBoolValue(false);
 
-    ai = (FGAIManager*)globals->get_subsystem("ai_model");
-
     load();
+
 }
 
 void FGSubmodelMgr::postinit() {
     // postinit, so that the AI list is populated
-    loadAI();
-    loadSubmodels();
+
+        loadAI();
+
+    while (_found_sub)
+        loadSubmodels();
 
     //TODO reload submodels if an MP ac joins
+
+    //_model_added_node = fgGetNode("ai/models/model-added", true);
+    //_model_added_node->addChangeListener(this, false);
 }
 
 void FGSubmodelMgr::bind()
@@ -106,23 +116,40 @@ void FGSubmodelMgr::update(double dt)
 
     _impact = false;
     _hit = false;
+    _expiry = false;
 
     // check if the submodel hit an object or terrain
-    sm_list = ai->get_ai_list();
-    sm_list_iterator sm_list_itr = sm_list.begin();
-    sm_list_iterator end = sm_list.end();
+    FGAIManager::ai_list_type sm_list(aiManager()->get_ai_list());
+    FGAIManager::ai_list_iterator sm_list_itr = sm_list.begin(),
+      end = sm_list.end();
 
     for (; sm_list_itr != end; ++sm_list_itr) {
-        _impact = (*sm_list_itr)->_getImpactData();
-        _hit = (*sm_list_itr)->_getCollisionData();
+        FGAIBase::object_type object_type =(*sm_list_itr)->getType();
+
+        if (object_type != FGAIBase::otBallistic){// only work on ballistic objects
+            continue; // so continue 
+        }
+
         int parent_subID = (*sm_list_itr)->_getSubID();
-        SG_LOG(SG_GENERAL, SG_DEBUG, "Submodel: Impact " << _impact << " hit! "
-                << _hit <<" parent_subID " << parent_subID);
-        if ( parent_subID == 0) // this entry in the list has no associated submodel
-            continue;           // so we can continue
+        int id = (*sm_list_itr)->getID();
+
+        if ( parent_subID == 0 || id == -1) // this entry in the list has no associated submodel
+            continue;                       // or is invalid so we can continue
+
+        //SG_LOG(SG_GENERAL, SG_DEBUG, "Submodel: Impact " << _impact << " hit! "
+        //        << _hit <<" parent_subID " << parent_subID);
+
+        _hit = (*sm_list_itr)->_getCollisionData();
+        _impact = (*sm_list_itr)->_getImpactData();
+        _expiry = (*sm_list_itr)->_getExpiryData();
 
-        if (_impact || _hit) {
-            SG_LOG(SG_GENERAL, SG_DEBUG, "Submodel: Impact " << _impact << " hit! " << _hit );
+        //SG_LOG(SG_GENERAL, SG_ALERT, "Submodel: " << (*sm_list_itr)->_getName()
+        //    << " Impact " << _impact << " hit! " << _hit
+        //    << " exipiry :-( " << _expiry );
+
+        if (_impact || _hit || _expiry) {
+    //        SG_LOG(SG_GENERAL, SG_ALERT, "Submodel: Impact " << _impact << " hit! " << _hit 
+                //<< " exipiry :-( " << _expiry );
 
             submodel_iterator = submodels.begin();
 
@@ -139,9 +166,13 @@ void FGSubmodelMgr::update(double dt)
                     _parent_roll = (*sm_list_itr)->_getImpactRoll();
                     _parent_speed = (*sm_list_itr)->_getImpactSpeed();
                     (*submodel_iterator)->first_time = true;
+                    //cout << "Impact: parent SubID = child_ID elev " << _parent_elev << endl;
 
-                    if (release(*submodel_iterator, dt))
+                    if (release(*submodel_iterator, dt)){
                         (*sm_list_itr)->setDie(true);
+                        //cout << "Impact: set die" << (*sm_list_itr)->_getName() << endl;
+                    }
+
                 }
 
                 ++submodel_iterator;
@@ -152,78 +183,41 @@ void FGSubmodelMgr::update(double dt)
     _contrail_trigger->setBoolValue(_user_alt_node->getDoubleValue() > contrail_altitude);
 
 
-    bool in_range = true;
+//    bool in_range = true;
     bool trigger = false;
     int i = -1;
 
     submodel_iterator = submodels.begin();
     while (submodel_iterator != submodels.end())  {
         i++;
-        in_range = true;
 
-        SG_LOG(SG_GENERAL, SG_DEBUG,
+        /*SG_LOG(SG_GENERAL, SG_DEBUG,
                 "Submodels:  " << (*submodel_iterator)->id
                 << " name " << (*submodel_iterator)->name
-                << " in range " << in_range);
+                );*/
 
         if ((*submodel_iterator)->trigger_node != 0) {
             _trigger_node = (*submodel_iterator)->trigger_node;
             trigger = _trigger_node->getBoolValue();
-            //cout << "trigger node found " <<  trigger << endl;
+            //cout << (*submodel_iterator)->name << "trigger node found " <<  trigger << endl;
         } else {
-            trigger = true;
-            //cout << (*submodel_iterator)->name << "trigger node not found " << trigger << endl;
+            trigger = false;
+            //cout << (*submodel_iterator)->name << " trigger node not found " << trigger << endl;
         }
 
-        if (trigger) {
-            int id = (*submodel_iterator)->id;
-            string name = (*submodel_iterator)->name;
-            // don't release submodels from AI Objects if they are
-            // too far away to be seen. id 0 is not an AI model,
-            // so we can skip the whole process
-            sm_list_iterator sm_list_itr = sm_list.begin();
-            sm_list_iterator end = sm_list.end();
-
-            while (sm_list_itr != end) {
-
-                if (id == 0) {
-                    SG_LOG(SG_GENERAL, SG_DEBUG,
-                            "Submodels: continuing: " << id << " name " << name );
-                    in_range = true;
-                    ++sm_list_itr;
-                    continue;
-                }
-
-                int parent_id = (*submodel_iterator)->id;
-
-                if (parent_id == id) {
-                    double parent_lat = (*sm_list_itr)->_getLatitude();
-                    double parent_lon = (*sm_list_itr)->_getLongitude();
-                    double own_lat    = _user_lat_node->getDoubleValue();
-                    double own_lon    = _user_lon_node->getDoubleValue();
-                    double range_nm   = getRange(parent_lat, parent_lon, own_lat, own_lon);
-                    // cout << "parent " << parent_id << ", "<< parent_lat << ", " << parent_lon << endl;
-                    //cout << "own " << own_lat << ", " << own_lon << " range " << range_nm << endl;
-
-                    if (range_nm > 15) {
-                        SG_LOG(SG_GENERAL, SG_DEBUG,
-                                "Submodels: skipping release: " << id);
-                        in_range = false;
-                    }
-                }
-
-                ++sm_list_itr;
-            } // end while
+        if (trigger && (*submodel_iterator)->count != 0) {
 
+            //int id = (*submodel_iterator)->id;
+            string name = (*submodel_iterator)->name;
+            
             SG_LOG(SG_GENERAL, SG_DEBUG,
-                    "Submodels end:  " << (*submodel_iterator)->id
-                    << " name " << (*submodel_iterator)->name
-                    << " count " << (*submodel_iterator)->count
-                    << " in range " << in_range);
-
-            if ((*submodel_iterator)->count != 0 && in_range)
-                release(*submodel_iterator, dt);
+            "Submodels release:  " << (*submodel_iterator)->id
+            << " name " << (*submodel_iterator)->name
+            << " count " << (*submodel_iterator)->count
+            << " slaved " << (*submodel_iterator)->slaved
+            );
 
+            release(*submodel_iterator, dt);
         } else
             (*submodel_iterator)->first_time = true;
 
@@ -233,22 +227,31 @@ void FGSubmodelMgr::update(double dt)
 
 bool FGSubmodelMgr::release(submodel *sm, double dt)
 {
-    //cout << "release id " << sm->id << " name " << sm->name
-    //<< " first time " << sm->first_time  << " repeat " << sm->repeat  <<
-    //    endl;
+    //cout << "release id " << sm->id 
+    //    << " name " << sm->name
+    //    << " first time " << sm->first_time
+    //    << " repeat " << sm->repeat
+    //    << " slaved " << sm->slaved
+    //    << endl;
 
     // only run if first time or repeat is set to true
     if (!sm->first_time && !sm->repeat) {
-        //cout<< "not first time " << sm->first_time<< " repeat " << sm->repeat <<endl;
+        //cout<< "returning: "<< sm->name 
+        //    << " not first time " << sm->first_time 
+        //    << " repeat " << sm->repeat
+        //    << " slaved " << sm->slaved
+        //    << endl;
         return false;
     }
 
     sm->timer += dt;
 
     if (sm->timer < sm->delay) {
-        //cout << "not yet: timer" << sm->timer << " delay " << sm->delay<< endl;
+        //cout << "not yet: timer " << sm->timer << " delay " << sm->delay << endl;
         return false;
     }
+    
+    //cout << "released timer: " << sm->timer << " delay " << sm->delay << endl;
 
     sm->timer = 0.0;
 
@@ -261,9 +264,13 @@ bool FGSubmodelMgr::release(submodel *sm, double dt)
 
     FGAIBallistic* ballist = new FGAIBallistic;
     ballist->setPath(sm->model.c_str());
-    ballist->setLatitude(IC.lat);
-    ballist->setLongitude(IC.lon);
-    ballist->setAltitude(IC.alt);
+    ballist->setName(sm->name);
+    ballist->setSlaved(sm->slaved);
+    ballist->setRandom(sm->random);
+    ballist->setRandomness(sm->randomness);
+    ballist->setLatitude(offsetpos.getLatitudeDeg());
+    ballist->setLongitude(offsetpos.getLongitudeDeg());
+    ballist->setAltitude(offsetpos.getElevationFt());
     ballist->setAzimuth(IC.azimuth);
     ballist->setElevation(IC.elevation);
     ballist->setRoll(IC.roll);
@@ -278,18 +285,29 @@ bool FGSubmodelMgr::release(submodel *sm, double dt)
     ballist->setCd(sm->cd);
     ballist->setStabilisation(sm->aero_stabilised);
     ballist->setNoRoll(sm->no_roll);
-    ballist->setName(sm->name);
     ballist->setCollision(sm->collision);
+    ballist->setExpiry(sm->expiry);
     ballist->setImpact(sm->impact);
     ballist->setImpactReportNode(sm->impact_report);
     ballist->setFuseRange(sm->fuse_range);
     ballist->setSubmodel(sm->submodel.c_str());
     ballist->setSubID(sm->sub_id);
-    ai->attach(ballist);
+    ballist->setForceStabilisation(sm->force_stabilised);
+    ballist->setExternalForce(sm->ext_force);
+    ballist->setForcePath(sm->force_path.c_str());
+    ballist->setXoffset(sm->x_offset);
+    ballist->setYoffset(sm->y_offset);
+    ballist->setZoffset(sm->z_offset);
+    ballist->setPitchoffset(sm->pitch_offset);
+    ballist->setYawoffset(sm->yaw_offset);
+    ballist->setParentNodes(_selected_ac);
+    ballist->setContentsNode(sm->contents_node);
+    ballist->setWeight(sm->weight);
+    
+    aiManager()->attach(ballist);
 
     if (sm->count > 0)
         sm->count--;
-
     return true;
 }
 
@@ -308,38 +326,38 @@ void FGSubmodelMgr::load()
 void FGSubmodelMgr::transform(submodel *sm)
 {
     // set initial conditions
-    if (sm->contents_node != 0) {
+    if (sm->contents_node != 0 && !sm->slaved) {
         // get the weight of the contents (lbs) and convert to mass (slugs)
-        sm->contents = sm->contents_node->getDoubleValue();
+        sm->contents = sm->contents_node->getChild("level-lbs",0,1)->getDoubleValue();
+        //cout << "transform: contents " << sm->contents << endl;
         IC.mass = (sm->weight + sm->contents) * lbs_to_slugs;
+        //cout << "mass inc contents"  << IC.mass << endl;
 
         // set contents to 0 in the parent
-        sm->contents_node->setDoubleValue(0);
-    } else {
+        sm->contents_node->getChild("level-gal_us",0,1)->setDoubleValue(0);
+        /*cout << "contents " << sm->contents_node->getChild("level-gal_us")->getDoubleValue()
+        << " " << sm->contents_node->getChild("level-lbs",0,1)->getDoubleValue()
+        << endl;*/
+    } else
         IC.mass = sm->weight * lbs_to_slugs;
-    }
 
-    // cout << "mass "  << IC.mass << endl;
+    int id = sm->id;
+    //int sub_id = sm->sub_id;
+    string name = sm->name;
+
 
     if (sm->speed_node != 0)
         sm->speed = sm->speed_node->getDoubleValue();
 
-    int id = sm->id;
-    //int sub_id = (*submodel)->sub_id;
-    string name = sm->name;
 
     //cout << " name " << name << " id " << id << " sub id" << sub_id << endl;
 
-    if (_impact || _hit) {
+    // set the Initial Conditions for the types of submodel parent 
+
+    if (_impact || _hit || _expiry) {
         // set the data for a submodel tied to a submodel
+
         _count++;
-        //cout << "Submodels: release sub sub " << _count<< endl;
-        //cout << " id " << sm->id
-        //    << " lat " << _parent_lat
-        //    << " lon " << _parent_lon
-        //    << " elev " << _parent_elev
-        //    << " name " << sm->name
-        //    << endl;
 
         IC.lat             = _parent_lat;
         IC.lon             = _parent_lon;
@@ -354,10 +372,7 @@ void FGSubmodelMgr::transform(submodel *sm)
 
     } else if (id == 0) {
         //set the data for a submodel tied to the main model
-        /*cout << "Submodels: release main sub " << endl;
-        cout << " name " << sm->name
-        << " id" << sm->id
-        << endl;*/
+
         IC.lat             = _user_lat_node->getDoubleValue();
         IC.lon             = _user_lon_node->getDoubleValue();
         IC.alt             = _user_alt_node->getDoubleValue();
@@ -371,46 +386,38 @@ void FGSubmodelMgr::transform(submodel *sm)
 
     } else {
         // set the data for a submodel tied to an AI Object
-        sm_list_iterator sm_list_itr = sm_list.begin();
-        sm_list_iterator end = sm_list.end();
+        //cout << " set the data for a submodel tied to an AI Object " << id << endl;
+        setParentNode(id);
+    }
 
-        while (sm_list_itr != end) {
-            int parent_id = (*sm_list_itr)->getID();
+    //cout << "Submodel: setting IC "<< name << endl;
+    //cout << "heading " << IC.azimuth << endl ;
+    //cout << "speed down " << IC.speed_down_fps << endl ;
+    //cout << "speed east " << IC.speed_east_fps << endl ;
+    //cout << "speed north " << IC.speed_north_fps << endl ;
+    //cout << "parent speed fps in " << IC.speed << "sm speed in " << sm->speed << endl ;
+    //cout << "lat " << IC.lat;
+    //cout << "alt " << IC.alt <<  endl ;
 
-            if (id != parent_id) {
-                ++sm_list_itr;
-                continue;
-            }
 
-            //cout << "found id " << id << endl;
-            IC.lat             = (*sm_list_itr)->_getLatitude();
-            IC.lon             = (*sm_list_itr)->_getLongitude();
-            IC.alt             = (*sm_list_itr)->_getAltitude();
-            IC.roll            = (*sm_list_itr)->_getRoll();
-            IC.elevation       = (*sm_list_itr)->_getPitch();
-            IC.azimuth         = (*sm_list_itr)->_getHeading();
-            IC.alt             = (*sm_list_itr)->_getAltitude();
-            IC.speed           = (*sm_list_itr)->_getSpeed() * SG_KT_TO_FPS;
-            IC.speed_down_fps  = -(*sm_list_itr)->_getVS_fps();
-            IC.speed_east_fps  = (*sm_list_itr)->_get_speed_east_fps();
-            IC.speed_north_fps = (*sm_list_itr)->_get_speed_north_fps();
+    // Set the Initial Conditions that are common to all types of parent
+    IC.wind_from_east =  _user_wind_from_east_node->getDoubleValue();
+    IC.wind_from_north = _user_wind_from_north_node->getDoubleValue();
 
-            ++sm_list_itr;
-        }
-    }
+//cout << "wind e " << IC.wind_from_east << " n " << IC.wind_from_north << endl;
 
-    /*cout << "heading " << IC.azimuth << endl ;
-    cout << "speed down " << IC.speed_down_fps << endl ;
-    cout << "speed east " << IC.speed_east_fps << endl ;
-    cout << "speed north " << IC.speed_north_fps << endl ;
-    cout << "parent speed fps in" << IC.speed << "sm speed in " << sm->speed << endl ;*/
+    userpos.setLatitudeDeg(IC.lat);
+    userpos.setLongitudeDeg(IC.lon);
+    userpos.setElevationFt(IC.alt);
 
-    IC.wind_from_east =  _user_wind_from_east_node->getDoubleValue();
-    IC.wind_from_north = _user_wind_from_north_node->getDoubleValue();
+    _x_offset = sm->x_offset;
+    _y_offset = sm->y_offset;
+    _z_offset = sm->z_offset;
+
+    setOffsetPos();
 
-    in[0] = sm->x_offset;
-    in[1] = sm->y_offset;
-    in[2] = sm->z_offset;
+    //IC.elevation += sm->pitch_offset;
+    //IC.azimuth   += sm->yaw_offset ;
 
     // pre-process the trig functions
     cosRx = cos(-IC.roll * SG_DEGREES_TO_RADIANS);
@@ -420,43 +427,9 @@ void FGSubmodelMgr::transform(submodel *sm)
     cosRz = cos(IC.azimuth * SG_DEGREES_TO_RADIANS);
     sinRz = sin(IC.azimuth * SG_DEGREES_TO_RADIANS);
 
-    // set up the transform matrix
-    trans[0][0] =  cosRy * cosRz;
-    trans[0][1] =  -1 * cosRx * sinRz + sinRx * sinRy * cosRz ;
-    trans[0][2] =  sinRx * sinRz + cosRx * sinRy * cosRz;
-
-    trans[1][0] =  cosRy * sinRz;
-    trans[1][1] =  cosRx * cosRz + sinRx * sinRy * sinRz;
-    trans[1][2] =  -1 * sinRx * cosRx + cosRx * sinRy * sinRz;
-
-    trans[2][0] =  -1 * sinRy;
-    trans[2][1] =  sinRx * cosRy;
-    trans[2][2] =  cosRx * cosRy;
-
-
-    // multiply the input and transform matrices
-    out[0] = in[0] * trans[0][0] + in[1] * trans[0][1] + in[2] * trans[0][2];
-    out[1] = in[0] * trans[1][0] + in[1] * trans[1][1] + in[2] * trans[1][2];
-    out[2] = in[0] * trans[2][0] + in[1] * trans[2][1] + in[2] * trans[2][2];
-
-    // convert ft to degrees of latitude
-    out[0] = out[0] / (366468.96 - 3717.12 * cos(IC.lat * SG_DEGREES_TO_RADIANS));
-
-    // convert ft to degrees of longitude
-    out[1] = out[1] / (365228.16 * cos(IC.lat * SG_DEGREES_TO_RADIANS));
-
-    // set submodel initial position
-    IC.lat += out[0];
-    IC.lon += out[1];
-    IC.alt += out[2];
-
-    // get aircraft velocity vector angles in XZ and XY planes
-    //double alpha = _user_alpha_node->getDoubleValue();
-    //double velXZ = IC.elevation - alpha * cosRx;
-    //double velXY = IC.azimuth - (IC.elevation - alpha * sinRx);
 
     // Get submodel initial velocity vector angles in XZ and XY planes.
-    // This needs to be fixed. This vector should be added to aircraft's vector.
+    // This vector should be added to aircraft's vector.
     IC.elevation += (sm->yaw_offset * sinRx) + (sm->pitch_offset * cosRx);
     IC.azimuth   += (sm->yaw_offset * cosRx) - (sm->pitch_offset * sinRx);
 
@@ -479,7 +452,7 @@ void FGSubmodelMgr::transform(submodel *sm)
 
     // if speeds are low this calculation can become unreliable
     if (IC.speed > 1) {
-        IC.azimuth = atan2(IC.total_speed_east , IC.total_speed_north) * SG_RADIANS_TO_DEGREES;
+        IC.azimuth = atan2(IC.total_speed_east, IC.total_speed_north) * SG_RADIANS_TO_DEGREES;
         //        cout << "azimuth1 " << IC.azimuth<<endl;
 
         // rationalise the output
@@ -487,13 +460,13 @@ void FGSubmodelMgr::transform(submodel *sm)
             IC.azimuth += 360;
         else if (IC.azimuth >= 360)
             IC.azimuth -= 360;
-    }
+        // cout << "azimuth2 " << IC.azimuth<<endl;
 
-    // cout << "azimuth2 " << IC.azimuth<<endl;
-
-    IC.elevation = -atan(IC.total_speed_down / sqrt(IC.total_speed_north
+        IC.elevation = -atan(IC.total_speed_down / sqrt(IC.total_speed_north
             * IC.total_speed_north + IC.total_speed_east * IC.total_speed_east))
             * SG_RADIANS_TO_DEGREES;
+    }
+    //cout << "IC.speed " << IC.speed / SG_KT_TO_FPS << endl;
 }
 
 void FGSubmodelMgr::updatelat(double lat)
@@ -506,15 +479,15 @@ void FGSubmodelMgr::loadAI()
 {
     SG_LOG(SG_GENERAL, SG_DEBUG, "Submodels: Loading AI submodels ");
 
-    sm_list = ai->get_ai_list();
+    FGAIManager::ai_list_type sm_list(aiManager()->get_ai_list());
 
     if (sm_list.empty()) {
-        SG_LOG(SG_GENERAL, SG_DEBUG, "Submodels: Unable to read AI submodel list");
+        SG_LOG(SG_GENERAL, SG_ALERT, "Submodels: Unable to read AI submodel list");
         return;
     }
 
-    sm_list_iterator sm_list_itr = sm_list.begin();
-    sm_list_iterator end = sm_list.end();
+    FGAIManager::ai_list_iterator sm_list_itr = sm_list.begin(),
+      end = sm_list.end();
 
     while (sm_list_itr != end) {
         string path = (*sm_list_itr)->_getSMPath();
@@ -525,36 +498,29 @@ void FGSubmodelMgr::loadAI()
         }
 
         int id = (*sm_list_itr)->getID();
+        string type = (*sm_list_itr)->getTypeString();
         bool serviceable = (*sm_list_itr)->_getServiceable();
+
+        //cout << "loadAI: type " << type << " path "<< path << " serviceable " << serviceable << endl;
+
         setData(id, path, serviceable);
         ++sm_list_itr;
     }
 }
 
 
-double FGSubmodelMgr::getRange(double lat, double lon, double lat2, double lon2) const
-{
-    double course, distance, az2;
-
-    //calculate the bearing and range of the second pos from the first
-    geo_inverse_wgs_84(lat, lon, lat2, lon2, &course, &az2, &distance);
-    distance *= SG_METER_TO_NM;
-    return distance;
-}
 
 void FGSubmodelMgr::setData(int id, string& path, bool serviceable)
 {
     SGPropertyNode root;
 
-    SGPath config(globals->get_fg_root());
-    config.append(path);
-    SG_LOG(SG_GENERAL, SG_DEBUG, "Submodels: path " << path);
+    SGPath config = globals->resolve_aircraft_path(path);
     try {
         SG_LOG(SG_GENERAL, SG_DEBUG,
                 "Submodels: Trying to read AI submodels file: " << config.str());
         readProperties(config.str(), &root);
-    } catch (const sg_exception &e) {
-        SG_LOG(SG_GENERAL, SG_DEBUG,
+    } catch (const sg_exception &) {
+        SG_LOG(SG_GENERAL, SG_ALERT,
                 "Submodels: Unable to read AI submodels file: " << config.str());
         return;
     }
@@ -588,14 +554,19 @@ void FGSubmodelMgr::setData(int id, string& path, bool serviceable)
         sm->aero_stabilised = entry_node->getBoolValue("aero-stabilised", true);
         sm->no_roll         = entry_node->getBoolValue("no-roll", false);
         sm->collision       = entry_node->getBoolValue("collision", false);
+        sm->expiry                     = entry_node->getBoolValue("expiry", false);
         sm->impact          = entry_node->getBoolValue("impact", false);
         sm->impact_report   = entry_node->getStringValue("impact-reports");
         sm->fuse_range      = entry_node->getDoubleValue("fuse-range", 0.0);
         sm->contents_node   = fgGetNode(entry_node->getStringValue("contents", "none"), false);
-        sm->speed_node      = fgGetNode(entry_node->getStringValue("speed-node", "none"), false);
+        sm->speed_node      = fgGetNode(entry_node->getStringValue("speed-prop", "none"), false);
         sm->submodel        = entry_node->getStringValue("submodel-path", "");
+        sm->force_stabilised= entry_node->getBoolValue("force-stabilised", false);
+        sm->ext_force       = entry_node->getBoolValue("external-force", false);
+        sm->force_path      = entry_node->getStringValue("force-path", "");
+        sm->random                     = entry_node->getBoolValue("random", false);
+        sm->randomness         = entry_node->getDoubleValue("randomness", 0.5);
 
-        //cout <<  "sm->contents_node " << sm->contents_node << endl;
         if (sm->contents_node != 0)
             sm->contents = sm->contents_node->getDoubleValue();
 
@@ -607,7 +578,6 @@ void FGSubmodelMgr::setData(int id, string& path, bool serviceable)
             sm->trigger_node = 0;
         }
 
-        SG_LOG(SG_GENERAL, SG_DEBUG, "Submodels: trigger " << sm->trigger_node->getBoolValue() );
         if (sm->speed_node != 0)
             sm->speed = sm->speed_node->getDoubleValue();
 
@@ -618,18 +588,23 @@ void FGSubmodelMgr::setData(int id, string& path, bool serviceable)
         sm->sub_id = 0;
 
         sm->prop = fgGetNode("/ai/submodels/submodel", index, true);
+        sm->prop->tie("delay", SGRawValuePointer<double>(&(sm->delay)));
         sm->prop->tie("count", SGRawValuePointer<int>(&(sm->count)));
         sm->prop->tie("repeat", SGRawValuePointer<bool>(&(sm->repeat)));
         sm->prop->tie("id", SGRawValuePointer<int>(&(sm->id)));
         sm->prop->tie("sub-id", SGRawValuePointer<int>(&(sm->sub_id)));
-
         sm->prop->tie("serviceable", SGRawValuePointer<bool>(&(sm->serviceable)));
+        sm->prop->tie("random", SGRawValuePointer<bool>(&(sm->random)));
+        sm->prop->tie("slaved", SGRawValuePointer<bool>(&(sm->slaved)));
         string name = sm->name;
         sm->prop->setStringValue("name", name.c_str());
 
         string submodel = sm->submodel;
         sm->prop->setStringValue("submodel", submodel.c_str());
-        //cout << " set submodel path " << submodel << endl;
+
+        string force_path = sm->force_path;
+        sm->prop->setStringValue("force_path", force_path.c_str());
+        //cout << "set force_path Sub " << force_path << endl;
 
         if (sm->contents_node != 0)
             sm->prop->tie("contents-lbs", SGRawValuePointer<double>(&(sm->contents)));
@@ -642,18 +617,15 @@ void FGSubmodelMgr::setData(int id, string& path, bool serviceable)
 void FGSubmodelMgr::setSubData(int id, string& path, bool serviceable)
 {
     SGPropertyNode root;
+    SGPath config = globals->resolve_aircraft_path(path);
 
-    SGPath config(globals->get_fg_root());
-    config.append(path);
-    SG_LOG(SG_GENERAL, SG_DEBUG,
-        "Submodels: path " << path);
     try {
         SG_LOG(SG_GENERAL, SG_DEBUG,
                 "Submodels: Trying to read AI submodels file: " << config.str());
         readProperties(config.str(), &root);
 
-    } catch (const sg_exception &e) {
-        SG_LOG(SG_GENERAL, SG_DEBUG,
+    } catch (const sg_exception &) {
+        SG_LOG(SG_GENERAL, SG_ALERT,
                 "Submodels: Unable to read AI submodels file: " << config.str());
         return;
     }
@@ -687,14 +659,19 @@ void FGSubmodelMgr::setSubData(int id, string& path, bool serviceable)
         sm->aero_stabilised = entry_node->getBoolValue("aero-stabilised", true);
         sm->no_roll         = entry_node->getBoolValue("no-roll", false);
         sm->collision       = entry_node->getBoolValue("collision", false);
+        sm->expiry          = entry_node->getBoolValue("expiry", false);
         sm->impact          = entry_node->getBoolValue("impact", false);
         sm->impact_report   = entry_node->getStringValue("impact-reports");
         sm->fuse_range      = entry_node->getDoubleValue("fuse-range", 0.0);
         sm->contents_node   = fgGetNode(entry_node->getStringValue("contents", "none"), false);
-        sm->speed_node      = fgGetNode(entry_node->getStringValue("speed-node", "none"), false);
+        sm->speed_node      = fgGetNode(entry_node->getStringValue("speed-prop", "none"), false);
         sm->submodel        = entry_node->getStringValue("submodel-path", "");
+        sm->force_stabilised= entry_node->getBoolValue("force-stabilised", false);
+        sm->ext_force       = entry_node->getBoolValue("external-force", false);
+        sm->force_path      = entry_node->getStringValue("force-path", "");
+        sm->random          = entry_node->getBoolValue("random", false);
+        sm->randomness      = entry_node->getDoubleValue("randomness", 0.5);
 
-        //cout <<  "sm->contents_node " << sm->contents_node << endl;
         if (sm->contents_node != 0)
             sm->contents = sm->contents_node->getDoubleValue();
 
@@ -721,12 +698,19 @@ void FGSubmodelMgr::setSubData(int id, string& path, bool serviceable)
         sm->prop->tie("id", SGRawValuePointer<int>(&(sm->id)));
         sm->prop->tie("sub-id", SGRawValuePointer<int>(&(sm->sub_id)));
         sm->prop->tie("serviceable", SGRawValuePointer<bool>(&(sm->serviceable)));
+        sm->prop->tie("random", SGRawValuePointer<bool>(&(sm->random)));
+        sm->prop->tie("slaved", SGRawValuePointer<bool>(&(sm->slaved)));
+
         string name = sm->name;
         sm->prop->setStringValue("name", name.c_str());
 
         string submodel = sm->submodel;
-        sm->prop->setStringValue("submodel", submodel.c_str());
-        // cout << " set submodel path " << submodel<< endl;
+        sm->prop->setStringValue("submodel-path", submodel.c_str());
+        // cout << " set submodel path AI" << submodel<< endl;
+
+        string force_path = sm->force_path;
+        sm->prop->setStringValue("force_path", force_path.c_str());
+        //cout << "set force_path  AI" << force_path << endl;
 
         if (sm->contents_node != 0)
             sm->prop->tie("contents-lbs", SGRawValuePointer<double>(&(sm->contents)));
@@ -740,6 +724,8 @@ void FGSubmodelMgr::loadSubmodels()
 {
     SG_LOG(SG_GENERAL, SG_DEBUG, "Submodels: Loading sub submodels");
 
+    _found_sub = false;
+
     submodel_iterator = submodels.begin();
 
     while (submodel_iterator != submodels.end()) {
@@ -750,33 +736,170 @@ void FGSubmodelMgr::loadSubmodels()
             SG_LOG(SG_GENERAL, SG_DEBUG, "found path sub sub "
                     << submodel
                     << " index " << index
-                    << "name " << (*submodel_iterator)->name);
+                    << " name " << (*submodel_iterator)->name);
 
-            (*submodel_iterator)->sub_id = index;
-            setSubData(index, submodel, serviceable);
+            if ((*submodel_iterator)->sub_id == 0){
+                (*submodel_iterator)->sub_id = index;
+                _found_sub = true;
+                setSubData(index, submodel, serviceable);
+            }
         }
 
         ++submodel_iterator;
-    }
+    } // end while
 
     subsubmodel_iterator = subsubmodels.begin();
 
     while (subsubmodel_iterator != subsubmodels.end()) {
+
         submodels.push_back(*subsubmodel_iterator);
         ++subsubmodel_iterator;
-    }
+    } // end while
 
-    submodel_iterator = submodels.begin();
+    subsubmodels.clear();
 
-    while (submodel_iterator != submodels.end()) {
-        int id = (*submodel_iterator)->id;
-        SG_LOG(SG_GENERAL, SG_DEBUG,"after pusback "
-                << " id " << id
-                << " name " << (*submodel_iterator)->name
-                << " sub id " << (*submodel_iterator)->sub_id);
+    //submodel_iterator = submodels.begin();
 
-        ++submodel_iterator;
-    }
+    //int subcount = 0;
+
+    //while (submodel_iterator != submodels.end()) {
+    //    int id = (*submodel_iterator)->id;
+    //    subcount++;
+
+    //    SG_LOG(SG_GENERAL, SG_ALERT,"after pushback "
+    //            << " parent id " << id
+    //            << " name " << (*submodel_iterator)->name
+    //            << " sub id " << (*submodel_iterator)->sub_id
+    //            << " subcount "<< subcount);
+
+    //    ++submodel_iterator;
+    //}
+}
+
+SGVec3d FGSubmodelMgr::getCartOffsetPos() const{
+
+    // convert geodetic positions to geocentered
+    SGVec3d cartuserPos = SGVec3d::fromGeod(userpos);
+    // Transform to the right coordinate frame, configuration is done in
+    // the x-forward, y-right, z-up coordinates (feet), computation
+    // in the simulation usual body x-forward, y-right, z-down coordinates
+    // (meters) )
+
+    SGVec3d _off(_x_offset * SG_FEET_TO_METER,
+        _y_offset * SG_FEET_TO_METER,
+        -_z_offset * SG_FEET_TO_METER);
+
+    // Transform the user position to the horizontal local coordinate system.
+    SGQuatd hlTrans = SGQuatd::fromLonLat(userpos);
+
+    // and postrotate the orientation of the user model wrt the horizontal
+    // local frame
+    hlTrans *= SGQuatd::fromYawPitchRollDeg(
+       IC.azimuth,            
+       IC.elevation,
+       IC.roll);
+
+    // The offset converted to the usual body fixed coordinate system
+    // rotated to the earth-fixed coordinates axis
+    SGVec3d off = hlTrans.backTransform(_off);
+
+    // Add the position offset of the user model to get the geocentered position
+    SGVec3d offsetPos = cartuserPos + off;
+    return offsetPos;
 }
 
+void FGSubmodelMgr::setOffsetPos(){
+    // convert the offset geocentered position to geodetic
+    SGVec3d cartoffsetPos = getCartOffsetPos();
+
+    SGGeodesy::SGCartToGeod(cartoffsetPos, offsetpos);
+
+    //cout << "OFFSET POS" << offsetpos.getElevationFt();
+
+}
+
+void FGSubmodelMgr::valueChanged(SGPropertyNode *prop)
+{
+    return; // this isn't working atm
+
+    const char* _model_added = _model_added_node->getStringValue();
+
+    basic_string <char>::size_type indexCh2b;
+
+    string str2 = _model_added;
+    const char *cstr2b = "multiplayer";
+    indexCh2b = str2.find( cstr2b, 0 );
+
+    if (indexCh2b != string::npos ){        // we will ignore Ballistic Objects - there are potentially too many 
+
+        //cout << "Submodels: model added - " << str2 <<" read path "<< endl;
+        //return;
+        SGPropertyNode *a_node = fgGetNode(_model_added, true);
+        SGPropertyNode *sub_node = a_node->getChild("sim", 0, true);
+        SGPropertyNode_ptr path_node = sub_node->getChild("path", 0, true);
+        SGPropertyNode_ptr callsign_node = a_node->getChild("callsign", 0, true);
+
+        string callsign = callsign_node->getStringValue();
+
+        //cout << "Submodels: model added - " << callsign <<" read callsign "<< endl;
+            return;
+
+        } else {
+            cout << "model added - " << str2 <<" returning "<< endl;
+        return;
+        }
+
+}
+
+void FGSubmodelMgr::setParentNode(int id) {
+
+    const SGPropertyNode_ptr ai = fgGetNode("/ai/models", true);
+
+    for (int i = ai->nChildren() - 1; i >= -1; i--) {
+        SGPropertyNode_ptr model;
+
+        if (i < 0) { // last iteration: selected model
+            model = _selected_ac;
+        } else {
+            model = ai->getChild(i);
+            string path = ai->getPath();
+            const string name = model->getStringValue("name");
+            int parent_id = model->getIntValue("id");
+            if (!model->nChildren()){
+                continue;
+            }
+            if (parent_id == id) {
+                _selected_ac = model;  // save selected model for last iteration
+                break;
+            }
+
+        }
+        if (!model)
+            continue;
+
+    }// end for loop 
+
+    if (_selected_ac != 0){
+
+        //cout << " parent node found"<< endl;
+
+        const string name  = _selected_ac->getStringValue("name");
+        IC.lat             = _selected_ac->getDoubleValue("position/latitude-deg");
+        IC.lon             = _selected_ac->getDoubleValue("position/longitude-deg");
+        IC.alt             = _selected_ac->getDoubleValue("position/altitude-ft");
+        IC.roll            = _selected_ac->getDoubleValue("orientation/roll-deg");
+        IC.elevation       = _selected_ac->getDoubleValue("orientation/pitch-deg");
+        IC.azimuth         = _selected_ac->getDoubleValue("orientation/true-heading-deg");
+        IC.speed           = _selected_ac->getDoubleValue("velocities/true-airspeed-kt") * SG_KT_TO_FPS;
+        IC.speed_down_fps  = -_selected_ac->getDoubleValue("velocities/vertical-speed-fps");
+        IC.speed_east_fps  = _selected_ac->getDoubleValue("velocities/speed-east-fps");
+        IC.speed_north_fps = _selected_ac->getDoubleValue("velocities/speed-north-fps");
+
+        //cout << name << " IC.speed " << IC.speed << endl;
+
+    } else {
+        SG_LOG(SG_GENERAL, SG_ALERT, "AISubmodel: parent node not found ");
+    }
+
+}
 // end of submodel.cxx