]> git.mxchange.org Git - flightgear.git/blob - src/AIModel/AIManager.cxx
Merge branch 'vivian/trainz'
[flightgear.git] / src / AIModel / AIManager.cxx
1 // AIManager.cxx  Based on David Luff's AIMgr:
2 // - a global management type for AI objects
3 //
4 // Written by David Culp, started October 2003.
5 // - davidculp2@comcast.net
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., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
21 #include <simgear/math/sg_geodesy.hxx>
22 #include <simgear/props/props_io.hxx>
23 #include <simgear/structure/exception.hxx>
24
25 #include <Main/globals.hxx>
26
27 #include <Airports/simple.hxx>
28 #include <Traffic/TrafficMgr.hxx>
29
30 #include "AIManager.hxx"
31 #include "AIAircraft.hxx"
32 #include "AIShip.hxx"
33 #include "AIBallistic.hxx"
34 #include "AIStorm.hxx"
35 #include "AIThermal.hxx"
36 #include "AICarrier.hxx"
37 #include "AIStatic.hxx"
38 #include "AIMultiplayer.hxx"
39 #include "AITanker.hxx"
40 #include "AIWingman.hxx"
41 #include "AIGroundVehicle.hxx"
42
43 FGAIManager::FGAIManager() {
44     _dt = 0.0;
45     mNumAiModels = 0;
46
47     for (unsigned i = 0; i < FGAIBase::MAX_OBJECTS; ++i)
48         mNumAiTypeModels[i] = 0;
49 }
50
51 FGAIManager::~FGAIManager() {
52     ai_list_iterator ai_list_itr = ai_list.begin();
53
54     while(ai_list_itr != ai_list.end()) {
55         (*ai_list_itr)->unbind();
56         ++ai_list_itr;
57     }
58 }
59
60 void
61 FGAIManager::init() {
62     root = fgGetNode("sim/ai", true);
63
64     enabled = root->getNode("enabled", true)->getBoolValue();
65
66     if (!enabled)
67         return;
68
69     thermal_lift_node = fgGetNode("/environment/thermal-lift-fps", true);
70     wind_from_east_node  = fgGetNode("/environment/wind-from-east-fps",true);
71     wind_from_north_node = fgGetNode("/environment/wind-from-north-fps",true);
72
73     user_latitude_node  = fgGetNode("/position/latitude-deg", true);
74     user_longitude_node = fgGetNode("/position/longitude-deg", true);
75     user_altitude_node  = fgGetNode("/position/altitude-ft", true);
76     user_heading_node   = fgGetNode("/orientation/heading-deg", true);
77     user_pitch_node     = fgGetNode("/orientation/pitch-deg", true);
78     user_yaw_node       = fgGetNode("/orientation/side-slip-deg", true);
79     user_roll_node      = fgGetNode("/orientation/roll-deg", true);
80     user_speed_node     = fgGetNode("/velocities/uBody-fps", true);
81 }
82
83 void
84 FGAIManager::postinit() {
85     // postinit, so that it can access the Nasal subsystem
86     map<string, bool> scenarios;
87     for (int i = 0 ; i < root->nChildren() ; i++) {
88         SGPropertyNode *n = root->getChild(i);
89         if (strcmp(n->getName(), "scenario"))
90             continue;
91
92         string name = n->getStringValue();
93         if (name.empty())
94             continue;
95
96         if (scenarios.find(name) != scenarios.end()) {
97             SG_LOG(SG_GENERAL, SG_WARN, "won't load scenario '" << name << "' twice");
98             continue;
99         }
100
101         SG_LOG(SG_GENERAL, SG_ALERT, "loading scenario '" << name << '\'');
102         processScenario(name);
103         scenarios[name] = true;
104     }
105 }
106
107 void
108 FGAIManager::reinit() {
109     update(0.0);
110     ai_list_iterator ai_list_itr = ai_list.begin();
111
112     while(ai_list_itr != ai_list.end()) {
113         (*ai_list_itr)->reinit();
114         ++ai_list_itr;
115     }
116 }
117
118 void
119 FGAIManager::bind() {
120     root = globals->get_props()->getNode("ai/models", true);
121     root->tie("count", SGRawValueMethods<FGAIManager, int>(*this,
122         &FGAIManager::getNumAiObjects));
123 }
124
125 void
126 FGAIManager::unbind() {
127     root->untie("count");
128 }
129
130 void
131 FGAIManager::update(double dt) {
132     // initialize these for finding nearest thermals
133     range_nearest = 10000.0;
134     strength = 0.0;
135
136     if (!enabled)
137         return;
138
139     FGTrafficManager *tmgr = (FGTrafficManager*) globals->get_subsystem("Traffic Manager");
140     _dt = dt;
141
142     ai_list_iterator ai_list_itr = ai_list.begin();
143
144     while(ai_list_itr != ai_list.end()) {
145
146         if ((*ai_list_itr)->getDie()) {
147             tmgr->release((*ai_list_itr)->getID());
148             --mNumAiModels;
149             --(mNumAiTypeModels[(*ai_list_itr)->getType()]);
150             FGAIBase *base = (*ai_list_itr).get();
151             SGPropertyNode *props = base->_getProps();
152
153             props->setBoolValue("valid", false);
154             base->unbind();
155
156             // for backward compatibility reset properties, so that aircraft,
157             // which don't know the <valid> property, keep working
158             // TODO: remove after a while
159             props->setIntValue("id", -1);
160             props->setBoolValue("radar/in-range", false);
161             props->setIntValue("refuel/tanker", false);
162
163             ai_list_itr = ai_list.erase(ai_list_itr);
164         } else {
165             fetchUserState();
166             if ((*ai_list_itr)->isa(FGAIBase::otThermal)) {
167                 FGAIBase *base = (*ai_list_itr).get();
168                 processThermal((FGAIThermal*)base);
169             } else {
170                 (*ai_list_itr)->update(_dt);
171             }
172             ++ai_list_itr;
173         }
174     }
175
176     thermal_lift_node->setDoubleValue( strength );  // for thermals
177 }
178
179 void
180 FGAIManager::attach(FGAIBase *model)
181 {
182     //unsigned idx = mNumAiTypeModels[model->getType()];
183     const char* typeString = model->getTypeString();
184     SGPropertyNode* root = globals->get_props()->getNode("ai/models", true);
185     SGPropertyNode* p;
186     int i;
187
188     // find free index in the property tree, if we have
189     // more than 10000 mp-aircrafts in the property tree we should optimize the mp-server
190     for (i = 0; i < 10000; i++) {
191         p = root->getNode(typeString, i, false);
192
193         if (!p || !p->getBoolValue("valid", false))
194             break;
195
196         if (p->getIntValue("id",-1)==model->getID()) {
197             p->setStringValue("callsign","***invalid node***"); //debug only, should never set!
198         }
199     }
200
201     p = root->getNode(typeString, i, true);
202     model->setManager(this, p);
203     ai_list.push_back(model);
204     ++mNumAiModels;
205     ++(mNumAiTypeModels[model->getType()]);
206     model->init(model->getType()==FGAIBase::otAircraft
207         || model->getType()==FGAIBase::otMultiplayer
208         || model->getType()==FGAIBase::otStatic);
209     model->bind();
210     p->setBoolValue("valid", true);
211 }
212
213 void
214 FGAIManager::destroyObject( int ID ) {
215     ai_list_iterator ai_list_itr = ai_list.begin();
216
217     while(ai_list_itr != ai_list.end()) {
218
219         if ((*ai_list_itr)->getID() == ID) {
220             --mNumAiModels;
221             --(mNumAiTypeModels[(*ai_list_itr)->getType()]);
222             (*ai_list_itr)->unbind();
223             ai_list_itr = ai_list.erase(ai_list_itr);
224         } else
225             ++ai_list_itr;
226     }
227
228 }
229
230 int
231 FGAIManager::getNumAiObjects(void) const
232 {
233     return mNumAiModels;
234 }
235
236 void
237 FGAIManager::fetchUserState( void ) {
238     user_latitude  = user_latitude_node->getDoubleValue();
239     user_longitude = user_longitude_node->getDoubleValue();
240     user_altitude  = user_altitude_node->getDoubleValue();
241     user_heading   = user_heading_node->getDoubleValue();
242     user_pitch     = user_pitch_node->getDoubleValue();
243     user_yaw       = user_yaw_node->getDoubleValue();
244     user_speed     = user_speed_node->getDoubleValue() * 0.592484;
245     user_roll      = user_roll_node->getDoubleValue();
246     wind_from_east = wind_from_east_node->getDoubleValue();
247     wind_from_north = wind_from_north_node->getDoubleValue();
248 }
249
250 // only keep the results from the nearest thermal
251 void
252 FGAIManager::processThermal( FGAIThermal* thermal ) {
253     thermal->update(_dt);
254
255     if ( thermal->_getRange() < range_nearest ) {
256         range_nearest = thermal->_getRange();
257         strength = thermal->getStrength();
258     }
259
260 }
261
262
263
264 void
265 FGAIManager::processScenario( const string &filename ) {
266
267     SGPropertyNode_ptr scenarioTop = loadScenarioFile(filename);
268
269     if (!scenarioTop)
270         return;
271
272     SGPropertyNode* scenarios = scenarioTop->getChild("scenario");
273
274     if (!scenarios)
275         return;
276
277     for (int i = 0; i < scenarios->nChildren(); i++) {
278         SGPropertyNode* scEntry = scenarios->getChild(i);
279
280         if (strcmp(scEntry->getName(), "entry"))
281             continue;
282         std::string type = scEntry->getStringValue("type", "aircraft");
283
284         if (type == "tanker") { // refueling scenarios
285             FGAITanker* tanker = new FGAITanker;
286             tanker->readFromScenario(scEntry);
287             attach(tanker);
288
289         } else if (type == "wingman") {
290             FGAIWingman* wingman = new FGAIWingman;
291             wingman->readFromScenario(scEntry);
292             attach(wingman);
293
294         } else if (type == "aircraft") {
295             FGAIAircraft* aircraft = new FGAIAircraft;
296             aircraft->readFromScenario(scEntry);
297             attach(aircraft);
298
299         } else if (type == "ship") {
300             FGAIShip* ship = new FGAIShip;
301             ship->readFromScenario(scEntry);
302             attach(ship);
303
304         } else if (type == "carrier") {
305             FGAICarrier* carrier = new FGAICarrier;
306             carrier->readFromScenario(scEntry);
307             attach(carrier);
308
309         } else if (type == "groundvehicle") {
310             FGAIGroundVehicle* groundvehicle = new FGAIGroundVehicle;
311             groundvehicle->readFromScenario(scEntry);
312             attach(groundvehicle);
313
314         } else if (type == "thunderstorm") {
315             FGAIStorm* storm = new FGAIStorm;
316             storm->readFromScenario(scEntry);
317             attach(storm);
318
319         } else if (type == "thermal") {
320             FGAIThermal* thermal = new FGAIThermal;
321             thermal->readFromScenario(scEntry);
322             attach(thermal);
323
324         } else if (type == "ballistic") {
325             FGAIBallistic* ballistic = new FGAIBallistic;
326             ballistic->readFromScenario(scEntry);
327             attach(ballistic);
328
329         } else if (type == "static") {
330             FGAIStatic* aistatic = new FGAIStatic;
331             aistatic->readFromScenario(scEntry);
332             attach(aistatic);
333         }
334
335     }
336
337 }
338
339 SGPropertyNode_ptr
340 FGAIManager::loadScenarioFile(const std::string& filename)
341 {
342     SGPath path(globals->get_fg_root());
343     path.append("AI/" + filename + ".xml");
344     try {
345         SGPropertyNode_ptr root = new SGPropertyNode;
346         readProperties(path.str(), root);
347         return root;
348     } catch (const sg_exception &) {
349         SG_LOG(SG_GENERAL, SG_DEBUG, "Incorrect path specified for AI "
350             "scenario: \"" << path.str() << "\"");
351         return 0;
352     }
353 }
354
355 bool
356 FGAIManager::getStartPosition(const string& id, const string& pid,
357                               SGGeod& geodPos, double& hdng, SGVec3d& uvw)
358 {
359     bool found = false;
360     SGPropertyNode* root = fgGetNode("sim/ai", true);
361     if (!root->getNode("enabled", true)->getBoolValue())
362         return found;
363
364     for (int i = 0 ; (!found) && i < root->nChildren() ; i++) {
365         SGPropertyNode *aiEntry = root->getChild( i );
366         if ( !strcmp( aiEntry->getName(), "scenario" ) ) {
367             string filename = aiEntry->getStringValue();
368             SGPropertyNode_ptr scenarioTop = loadScenarioFile(filename);
369             if (scenarioTop) {
370                 SGPropertyNode* scenarios = scenarioTop->getChild("scenario");
371                 if (scenarios) {
372                     for (int i = 0; i < scenarios->nChildren(); i++) {
373                         SGPropertyNode* scEntry = scenarios->getChild(i);
374                         std::string type = scEntry->getStringValue("type");
375                         std::string pnumber = scEntry->getStringValue("pennant-number");
376                         std::string name = scEntry->getStringValue("name");
377                         if (type == "carrier" && (pnumber == id || name == id)) {
378                             osg::ref_ptr<FGAICarrier> carrier = new FGAICarrier;
379                             carrier->readFromScenario(scEntry);
380
381                             if (carrier->getParkPosition(pid, geodPos, hdng, uvw)) {
382                                 found = true;
383                                 break;
384                             }
385                         }
386                     }
387                 }
388             }
389         }
390     }
391     return found;
392 }
393
394 const FGAIBase *
395 FGAIManager::calcCollision(double alt, double lat, double lon, double fuse_range)
396 {
397     // we specify tgt extent (ft) according to the AIObject type
398     double tgt_ht[]     = {0, 50 ,100, 250, 0, 100, 0, 0, 50, 50, 50};
399     double tgt_length[] = {0, 100, 200, 750, 0, 50, 0, 0, 200, 100, 100};
400     ai_list_iterator ai_list_itr = ai_list.begin();
401     ai_list_iterator end = ai_list.end();
402
403     while (ai_list_itr != end) {
404         double tgt_alt = (*ai_list_itr)->_getAltitude();
405         int type       = (*ai_list_itr)->getType();
406         tgt_ht[type] += fuse_range;
407
408         if (fabs(tgt_alt - alt) > tgt_ht[type] || type == FGAIBase::otBallistic
409             || type == FGAIBase::otStorm || type == FGAIBase::otThermal ) {
410                 SG_LOG(SG_GENERAL, SG_DEBUG, "AIManager: skipping "
411                     << fabs(tgt_alt - alt)
412                     << " "
413                     << type
414                     );
415                 ++ai_list_itr;
416                 continue;
417         }
418
419         double tgt_lat = (*ai_list_itr)->_getLatitude();
420         double tgt_lon = (*ai_list_itr)->_getLongitude();
421         int id         = (*ai_list_itr)->getID();
422
423         double range = calcRange(lat, lon, tgt_lat, tgt_lon);
424
425         SG_LOG(SG_GENERAL, SG_DEBUG, "AIManager:  AI list size "
426             << ai_list.size()
427             << " type " << type
428             << " ID " << id
429             << " range " << range
430             //<< " bearing " << bearing
431             << " alt " << tgt_alt
432             );
433
434         tgt_length[type] += fuse_range;
435
436         if (range < tgt_length[type]){
437             SG_LOG(SG_GENERAL, SG_DEBUG, "AIManager: HIT! "
438                 << " type " << type
439                 << " ID " << id
440                 << " range " << range
441                 << " alt " << tgt_alt
442                 );
443             return (*ai_list_itr).get();
444         }
445         ++ai_list_itr;
446     }
447     return 0;
448 }
449
450 double
451 FGAIManager::calcRange(double lat, double lon, double lat2, double lon2) const
452 {
453     double course, az2, distance;
454
455     //calculate the bearing and range of the second pos from the first
456     geo_inverse_wgs_84(lat, lon, lat2, lon2, &course, &az2, &distance);
457     distance *= SG_METER_TO_FEET;
458     return distance;
459 }
460
461 //end AIManager.cxx