Here's a new batch of AI code which includes a working radar instrument.
I put the radar calculations into the existing AIAircraft class. It was
easier that way, and it can always be migrated out later if we have to.
Every tenth sim cycle the AIManager makes a copy of the current user state
information. When the AIAircraft updates it uses this information to
calculate the radar numbers. It calculates:
1) bearing from user to target
2) range to target in nautical miles
3) "horizontal offset" to target. This is the angle from the nose to the
target, in degrees, from -180 to 180. This will be useful later for a HUD.
4) elevation, in degrees (vertical angle from user's position to target
position)
5) vertical offset, in degrees (this is elevation corrected for user's pitch)
6) rdot (range rate in knots, note: not working yet, so I commented it out)
and three items used by the radar instrument to place the "blip"
7) y_shift, in nautical miles
8) x_shift, in nautical miles
9) rotation, in degrees
The radar instrument uses the above three items, and applies a scale factor to
the x-shift and y-shift in order to match the instrument's scale. Changing
the display scale can be done entirely in the XML code for the instrument.
Right now it's set up only to display a 40 mile scale.
The radar is an AWACS view, which is not very realistic, but it is useful and
demonstrates the technology. With just a little more work I can get a HUD
marker. All I need to do there is make a bank angle adjustment to the
current values.
FGAIAircraft *FGAIAircraft::_self = NULL;
-FGAIAircraft::FGAIAircraft() {
+FGAIAircraft::FGAIAircraft(FGAIManager* mgr) {
+ manager = mgr;
_self = this;
// set heading and altitude locks
// adjust altitude (meters) based on current vertical speed (fpm)
altitude += vs * 0.0166667 * dt * SG_FEET_TO_METER;
+ double altitude_ft = altitude * SG_METER_TO_FEET;
// find target vertical speed if altitude lock engaged
if (alt_lock) {
- double altitude_ft = altitude * SG_METER_TO_FEET;
if (altitude_ft < tgt_altitude) {
tgt_vs = tgt_altitude - altitude_ft;
if (tgt_vs > performance->climb_rate)
// match pitch angle to vertical speed
pitch = vs * 0.005;
+ //###########################//
+ // do calculations for radar //
+ //###########################//
+
+ // copy values from the AIManager
+ double user_latitude = manager->get_user_latitude();
+ double user_longitude = manager->get_user_longitude();
+ double user_altitude = manager->get_user_altitude();
+ double user_heading = manager->get_user_heading();
+ double user_pitch = manager->get_user_pitch();
+ double user_yaw = manager->get_user_yaw();
+ double user_speed = manager->get_user_speed();
+
+ // calculate range to target in feet and nautical miles
+ double lat_range = fabs(pos.lat() - user_latitude) * ft_per_deg_lat;
+ double lon_range = fabs(pos.lon() - user_longitude) * ft_per_deg_lon;
+ double range_ft = sqrt( lat_range*lat_range + lon_range*lon_range );
+ range = range_ft / 6076.11549;
+
+ // calculate bearing to target
+ if (pos.lat() >= user_latitude) {
+ bearing = atan2(lat_range, lon_range) * SG_RADIANS_TO_DEGREES;
+ if (pos.lon() >= user_longitude) {
+ bearing = 90.0 - bearing;
+ } else {
+ bearing = 270.0 + bearing;
+ }
+ } else {
+ bearing = atan2(lon_range, lat_range) * SG_RADIANS_TO_DEGREES;
+ if (pos.lon() >= user_longitude) {
+ bearing = 180.0 - bearing;
+ } else {
+ bearing = 180.0 + bearing;
+ }
+ }
+
+ // calculate look left/right to target, without yaw correction
+ horiz_offset = bearing - user_heading;
+ if (horiz_offset > 180.0) horiz_offset -= 360.0;
+ if (horiz_offset < -180.0) horiz_offset += 360.0;
+
+ // calculate elevation to target
+ elevation = atan2( altitude_ft - user_altitude, range_ft )
+ * SG_RADIANS_TO_DEGREES;
+
+ // calculate look up/down to target
+ vert_offset = elevation + user_pitch;
+
+/* this calculation needs to be fixed
+ // calculate range rate
+ double recip_bearing = bearing + 180.0;
+ if (recip_bearing > 360.0) recip_bearing -= 360.0;
+ double my_horiz_offset = recip_bearing - hdg;
+ if (my_horiz_offset > 180.0) my_horiz_offset -= 360.0;
+ if (my_horiz_offset < -180.0) my_horiz_offset += 360.0;
+ rdot = (-user_speed * cos( horiz_offset * SG_DEGREES_TO_RADIANS ))
+ + (-speed * 1.686 * cos( my_horiz_offset * SG_DEGREES_TO_RADIANS ));
+*/
+
+ // now correct look left/right for yaw
+ horiz_offset += user_yaw;
+
+ // calculate values for radar display
+ y_shift = range * cos( horiz_offset * SG_DEGREES_TO_RADIANS);
+ x_shift = range * sin( horiz_offset * SG_DEGREES_TO_RADIANS);
+ rotation = hdg - user_heading;
+ if (rotation < 0.0) rotation += 360.0;
+
}
enum aircraft_e {LIGHT=0, WW2_FIGHTER, JET_TRANSPORT, JET_FIGHTER};
static const PERF_STRUCT settings[];
- FGAIAircraft();
+ FGAIAircraft(FGAIManager* mgr);
~FGAIAircraft();
bool init();
#include "AIBallistic.hxx"
-FGAIBallistic::FGAIBallistic() {
+FGAIBallistic::FGAIBallistic(FGAIManager* mgr) {
+ manager = mgr;
_type_str = "ballistic";
}
public:
- FGAIBallistic();
+ FGAIBallistic(FGAIManager* mgr);
~FGAIBallistic();
bool init();
_self = this;
_type_str = "model";
tgt_roll = roll = tgt_pitch = tgt_yaw = tgt_vs = vs = pitch = 0.0;
+ bearing = elevation = range = rdot = 0.0;
+ x_shift = y_shift = rotation = 0.0;
}
FGAIBase::~FGAIBase() {
+ unbind();
_self = NULL;
}
}
void FGAIBase::bind() {
- props->tie("velocities/airspeed-kt", SGRawValuePointer<double>(&speed));
+ props->tie("id", SGRawValuePointer<int>(&id));
+ props->tie("velocities/true-airspeed-kt", SGRawValuePointer<double>(&speed));
props->tie("velocities/vertical-speed-fps",
SGRawValueFunctions<double>(FGAIBase::_getVS_fps,
FGAIBase::_setVS_fps));
props->tie("orientation/pitch-deg", SGRawValuePointer<double>(&pitch));
props->tie("orientation/roll-deg", SGRawValuePointer<double>(&roll));
- props->tie("orientation/heading-deg", SGRawValuePointer<double>(&hdg));
+ props->tie("orientation/true-heading-deg", SGRawValuePointer<double>(&hdg));
+
+ props->tie("radar/bearing-deg", SGRawValueFunctions<double>(FGAIBase::_getBearing));
+ props->tie("radar/elevation-deg", SGRawValueFunctions<double>(FGAIBase::_getElevation));
+ props->tie("radar/range-nm", SGRawValueFunctions<double>(FGAIBase::_getRange));
+// props->tie("radar/rdot-kts", SGRawValueFunctions<double>(FGAIBase::_getRdot));
+ props->tie("radar/h-offset", SGRawValueFunctions<double>(FGAIBase::_getH_offset));
+ props->tie("radar/v-offset", SGRawValueFunctions<double>(FGAIBase::_getV_offset));
+ props->tie("radar/x-shift", SGRawValueFunctions<double>(FGAIBase::_getX_shift));
+ props->tie("radar/y-shift", SGRawValueFunctions<double>(FGAIBase::_getY_shift));
+ props->tie("radar/rotation", SGRawValueFunctions<double>(FGAIBase::_getRotation));
props->tie("controls/lighting/nav-lights",
SGRawValueFunctions<bool>(FGAIBase::_isNight));
}
void FGAIBase::unbind() {
- props->untie("velocities/airspeed-kt");
+ props->untie("id");
+ props->untie("velocities/true-airspeed-kt");
props->untie("velocities/vertical-speed-fps");
props->untie("position/altitude-ft");
props->untie("orientation/pitch-deg");
props->untie("orientation/roll-deg");
- props->untie("orientation/heading-deg");
+ props->untie("orientation/true-heading-deg");
+
+ props->untie("radar/bearing-deg");
+ props->untie("radar/elevation-deg");
+ props->untie("radar/range-nm");
+// props->untie("radar/rdot-kts");
+ props->untie("radar/h-offset");
+ props->untie("radar/v-offset");
+ props->untie("radar/x-shift");
+ props->untie("radar/y-shift");
+ props->untie("radar/rotation");
props->untie("controls/controls/lighting/nav-lights");
}
SG_USING_STD(string);
+class FGAIManager;
+
class FGAIBase {
public:
void setLongitude( double longitude );
void setBank( double bank );
+ void setID( int ID );
+ int getID();
void setDie( bool die );
bool getDie();
protected:
SGPropertyNode *props;
+ FGAIManager* manager;
+ // these describe the model's actual state
Point3D pos; // WGS84 lat & lon in degrees, elev above sea-level in meters
double hdg; // True heading in degrees
double roll; // degrees, left is negative
double pitch; // degrees, nose-down is negative
double speed; // knots true airspeed
double altitude; // meters above sea level
- double vs; // vertical speed, feet per minute
+ double vs; // vertical speed, feet per minute
+ // these describe the model's desired state
double tgt_heading; // target heading, degrees true
double tgt_altitude; // target altitude, *feet* above sea level
double tgt_speed; // target speed, KTAS
double tgt_yaw;
double tgt_vs;
+ // these describe radar information for the user
+ double bearing; // true bearing from user to this model
+ double elevation; // elevation in degrees from user to this model
+ double range; // range from user to this model, nm
+ double rdot; // range rate, in knots
+ double horiz_offset; // look left/right from user to me, deg
+ double vert_offset; // look up/down from user to me, deg
+ double x_shift; // value used by radar display instrument
+ double y_shift; // value used by radar display instrument
+ double rotation; // value used by radar display instrument
+
string model_path; //Path to the 3D model
SGModelPlacement aip;
bool delete_me;
+ int id;
void Transform();
static double _getLongitude();
static double _getLatitude ();
+ static double _getBearing();
+ static double _getElevation();
+ static double _getRange();
+ static double _getRdot();
+ static double _getH_offset();
+ static double _getV_offset();
+ static double _getX_shift();
+ static double _getY_shift();
+ static double _getRotation();
+
static bool _isNight();
};
inline double FGAIBase::_getLongitude() { return _self->pos.lon(); }
inline double FGAIBase::_getLatitude () { return _self->pos.lat(); }
+inline double FGAIBase::_getBearing() { return _self->bearing; }
+inline double FGAIBase::_getElevation() { return _self->elevation; }
+inline double FGAIBase::_getRange() { return _self->range; }
+inline double FGAIBase::_getRdot() { return _self->rdot; }
+inline double FGAIBase::_getH_offset() { return _self->horiz_offset; }
+inline double FGAIBase::_getV_offset() { return _self->vert_offset; }
+inline double FGAIBase::_getX_shift() { return _self->x_shift; }
+inline double FGAIBase::_getY_shift() { return _self->y_shift; }
+inline double FGAIBase::_getRotation() { return _self->rotation; }
+
inline double FGAIBase::_getVS_fps() { return _self->vs*60.0; }
inline void FGAIBase::_setVS_fps( double _vs ) { _self->vs = _vs/60.0; }
return (fgGetFloat("/sim/time/sun-angle-rad") > 1.57);
}
+inline void FGAIBase::setID( int ID ) { id = ID; }
+inline int FGAIBase::getID() { return id; }
+
#endif // _FG_AIBASE_HXX
FGAIManager::FGAIManager() {
initDone = false;
+ numObjects = 0;
+ dt_count = 9;
}
FGAIManager::~FGAIManager() {
+ ai_list_itr = ai_list.begin();
+ while(ai_list_itr != ai_list.end()) {
+ delete (*ai_list_itr);
+ ++ai_list_itr;
+ }
ai_list.clear();
+ ids.clear();
}
+
void FGAIManager::init() {
- SGPropertyNode * node = fgGetNode("sim/ai", true);
+ int rval;
+ root = fgGetNode("sim/ai", true);
- for (int i = 0; i < node->nChildren(); i++) {
- const SGPropertyNode * entry = node->getChild(i);
+ for (int i = 0; i < root->nChildren(); i++) {
+ const SGPropertyNode * entry = root->getChild(i);
if (!strcmp(entry->getName(), "entry")) {
if (!strcmp(entry->getStringValue("type", ""), "aircraft")) {
- FGAIAircraft* ai_plane = new FGAIAircraft;
- ai_list.push_back(ai_plane);
- string model_class = entry->getStringValue("class", "");
- if (model_class == "light") {
- ai_plane->SetPerformance(&FGAIAircraft::settings[FGAIAircraft::LIGHT]);
+ rval = createAircraft( entry->getStringValue("class", ""),
+ entry->getStringValue("path"),
+ entry->getDoubleValue("latitude"),
+ entry->getDoubleValue("longitude"),
+ entry->getDoubleValue("altitude-ft"),
+ entry->getDoubleValue("heading"),
+ entry->getDoubleValue("speed-KTAS"),
+ 0.0,
+ entry->getDoubleValue("bank") );
- } else if (model_class == "ww2_fighter") {
- ai_plane->SetPerformance(&FGAIAircraft::settings[FGAIAircraft::WW2_FIGHTER]);
-
- } else if (model_class == "jet_transport") {
- ai_plane->SetPerformance(&FGAIAircraft::settings[FGAIAircraft::JET_TRANSPORT]);
+ } else if (!strcmp(entry->getStringValue("type", ""), "ship")) {
- } else if (model_class == "jet_fighter") {
- ai_plane->SetPerformance(&FGAIAircraft::settings[FGAIAircraft::JET_FIGHTER]);
- }
+ rval = createShip( entry->getStringValue("path"),
+ entry->getDoubleValue("latitude"),
+ entry->getDoubleValue("longitude"),
+ entry->getDoubleValue("altitude-ft"),
+ entry->getDoubleValue("heading"),
+ entry->getDoubleValue("speed-KTAS"),
+ entry->getDoubleValue("rudder") );
- ai_plane->setHeading(entry->getDoubleValue("heading"));
- ai_plane->setSpeed(entry->getDoubleValue("speed-KTAS"));
- ai_plane->setPath(entry->getStringValue("path"));
- ai_plane->setAltitude(entry->getDoubleValue("altitude-ft"));
- ai_plane->setLongitude(entry->getDoubleValue("longitude"));
- ai_plane->setLatitude(entry->getDoubleValue("latitude"));
- ai_plane->setBank(entry->getDoubleValue("bank"));
- ai_plane->init();
- ai_plane->bind();
+ } else if (!strcmp(entry->getStringValue("type", ""), "ballistic")) {
- } else if (!strcmp(entry->getStringValue("type", ""), "ship")) {
- FGAIShip* ai_ship = new FGAIShip;
- ai_list.push_back(ai_ship);
- ai_ship->setHeading(entry->getDoubleValue("heading"));
- ai_ship->setSpeed(entry->getDoubleValue("speed-KTAS"));
- ai_ship->setPath(entry->getStringValue("path"));
- ai_ship->setAltitude(entry->getDoubleValue("altitude-ft"));
- ai_ship->setLongitude(entry->getDoubleValue("longitude"));
- ai_ship->setLatitude(entry->getDoubleValue("latitude"));
- ai_ship->setBank(entry->getDoubleValue("rudder"));
- ai_ship->init();
- ai_ship->bind();
+ rval = createBallistic( entry->getStringValue("path"),
+ entry->getDoubleValue("latitude"),
+ entry->getDoubleValue("longitude"),
+ entry->getDoubleValue("altitude-ft"),
+ entry->getDoubleValue("azimuth"),
+ entry->getDoubleValue("elevation"),
+ entry->getDoubleValue("speed") );
- } else if (!strcmp(entry->getStringValue("type", ""), "ballistic")) {
- FGAIBallistic* ai_ballistic = new FGAIBallistic;
- ai_list.push_back(ai_ballistic);
- ai_ballistic->setAzimuth(entry->getDoubleValue("azimuth"));
- ai_ballistic->setElevation(entry->getDoubleValue("elevation"));
- ai_ballistic->setSpeed(entry->getDoubleValue("speed-fps"));
- ai_ballistic->setPath(entry->getStringValue("path"));
- ai_ballistic->setAltitude(entry->getDoubleValue("altitude-ft"));
- ai_ballistic->setLongitude(entry->getDoubleValue("longitude"));
- ai_ballistic->setLatitude(entry->getDoubleValue("latitude"));
- ai_ballistic->init();
- ai_ballistic->bind();
}
}
}
void FGAIManager::bind() {
+ root = globals->get_props()->getNode("ai/models", true);
+ root->tie("count", SGRawValuePointer<int>(&numObjects));
}
void FGAIManager::unbind() {
- ai_list_itr = ai_list.begin();
- while(ai_list_itr != ai_list.end()) {
- (*ai_list_itr)->unbind();
- ++ai_list_itr;
- }
+ root->untie("count");
}
void FGAIManager::update(double dt) {
-#if 0
- if(!initDone) {
- init();
- SG_LOG(SG_ATC, SG_WARN, "Warning - AIManager::update(...) called before AIManager::init()");
- }
-#endif
ai_list_itr = ai_list.begin();
while(ai_list_itr != ai_list.end()) {
if ((*ai_list_itr)->getDie()) {
- ai_list.erase(ai_list_itr, ai_list_itr);
+ freeID((*ai_list_itr)->getID());
+ delete (*ai_list_itr);
+ ai_list.erase(ai_list_itr);
+ --ai_list_itr;
+ --numObjects;
} else {
+ fetchUserState();
(*ai_list_itr)->update(dt);
}
++ai_list_itr;
}
}
+
+
+// This function returns the next available ID
+int FGAIManager::assignID() {
+ int maxint = 30000;
+ int x;
+ bool used;
+ for (x=0; x<maxint; x++) {
+ used = false;
+ id_itr = ids.begin();
+ while( id_itr != ids.end() ) {
+ if ((*id_itr) == x) used = true;
+ ++id_itr;
+ }
+ if (!used) {
+ ids.push_back(x);
+ return x;
+ }
+ }
+ return -1; // no available ID's
+}
+
+
+// This function removes an ID from the ID array, making it
+// available for assignment to another AI object
+void FGAIManager::freeID( int ID ) {
+ id_itr = ids.begin();
+ while( id_itr != ids.end() ) {
+ if (*id_itr == ID) {
+ ids.erase( id_itr );
+ return;
+ }
+ ++id_itr;
+ }
+}
+
+int FGAIManager::createAircraft( string model_class, string path,
+ double latitude, double longitude, double altitude,
+ double heading, double speed, double pitch, double roll ) {
+
+ FGAIAircraft* ai_plane = new FGAIAircraft(this);
+ ai_list.push_back(ai_plane);
+ ai_plane->setID( assignID() );
+ ++numObjects;
+ if (model_class == "light") {
+ ai_plane->SetPerformance(&FGAIAircraft::settings[FGAIAircraft::LIGHT]);
+ } else if (model_class == "ww2_fighter") {
+ ai_plane->SetPerformance(&FGAIAircraft::settings[FGAIAircraft::WW2_FIGHTER]);
+ } else if (model_class == "jet_transport") {
+ ai_plane->SetPerformance(&FGAIAircraft::settings[FGAIAircraft::JET_TRANSPORT]);
+ } else if (model_class == "jet_fighter") {
+ ai_plane->SetPerformance(&FGAIAircraft::settings[FGAIAircraft::JET_FIGHTER]);
+ }
+ ai_plane->setHeading(heading);
+ ai_plane->setSpeed(speed);
+ ai_plane->setPath(path.c_str());
+ ai_plane->setAltitude(altitude);
+ ai_plane->setLongitude(longitude);
+ ai_plane->setLatitude(latitude);
+ ai_plane->setBank(roll);
+ ai_plane->init();
+ ai_plane->bind();
+ return ai_plane->getID();
+}
+
+
+int FGAIManager::createShip( string path, double latitude, double longitude,
+ double altitude, double heading, double speed,
+ double rudder ) {
+
+ FGAIShip* ai_ship = new FGAIShip(this);
+ ai_list.push_back(ai_ship);
+ ai_ship->setID( assignID() );
+ ++numObjects;
+ ai_ship->setHeading(heading);
+ ai_ship->setSpeed(speed);
+ ai_ship->setPath(path.c_str());
+ ai_ship->setAltitude(altitude);
+ ai_ship->setLongitude(longitude);
+ ai_ship->setLatitude(latitude);
+ ai_ship->setBank(rudder);
+ ai_ship->init();
+ ai_ship->bind();
+ return ai_ship->getID();
+}
+
+
+int FGAIManager::createBallistic( string path, double latitude, double longitude,
+ double altitude, double azimuth, double elevation,
+ double speed ) {
+
+ FGAIBallistic* ai_ballistic = new FGAIBallistic(this);
+ ai_list.push_back(ai_ballistic);
+ ai_ballistic->setID( assignID() );
+ ++numObjects;
+ ai_ballistic->setAzimuth(azimuth);
+ ai_ballistic->setElevation(elevation);
+ ai_ballistic->setSpeed(speed);
+ ai_ballistic->setPath(path.c_str());
+ ai_ballistic->setAltitude(altitude);
+ ai_ballistic->setLongitude(longitude);
+ ai_ballistic->setLatitude(latitude);
+ ai_ballistic->init();
+ ai_ballistic->bind();
+ return ai_ballistic->getID();
+}
+
+void FGAIManager::destroyObject( int ID ) {
+ ai_list_itr = ai_list.begin();
+ while(ai_list_itr != ai_list.end()) {
+ if ((*ai_list_itr)->getID() == ID) {
+ freeID( ID );
+ delete (*ai_list_itr);
+ ai_list.erase(ai_list_itr);
+ --ai_list_itr;
+ --numObjects;
+ return;
+ }
+ ++ai_list_itr;
+ }
+}
+
+// fetch the user's state every 10 sim cycles
+void FGAIManager::fetchUserState( void ) {
+ ++dt_count;
+ if (dt_count == 10) {
+ user_latitude = fgGetDouble("/position/latitude-deg");
+ user_longitude = fgGetDouble("/position/longitude-deg");
+ user_altitude = fgGetDouble("/position/altitude-ft");
+ user_heading = fgGetDouble("/orientation/heading-deg");
+ user_pitch = fgGetDouble("/orientation/pitch-deg");
+ user_yaw = fgGetDouble("/orientation/side-slip-deg");
+ user_speed = fgGetDouble("/velocities/uBody-fps") * 0.592484;
+ dt_count = 0;
+ }
+}
ai_list_type ai_list;
ai_list_iterator ai_list_itr;
+ // array of already-assigned ID's
+ typedef vector <int> id_vector_type;
+ id_vector_type ids;
+ id_vector_type::iterator id_itr;
+
public:
enum object_type { otAircraft, otShip, otBallistic, otRocket };
void unbind();
void update(double dt);
+ int assignID();
+ void freeID(int ID);
+
+ int createAircraft( string model_class, // see FGAIAircraft.hxx for possible classes
+ string path, // path to exterior model
+ double latitude, // in degrees -90 to 90
+ double longitude, // in degrees -180 to 180
+ double altitude, // in feet
+ double heading, // true heading in degrees
+ double speed, // in knots true airspeed (KTAS)
+ double pitch = 0, // in degrees
+ double roll = 0 ); // in degrees
+
+ int createShip( string path, // path to exterior model
+ double latitude, // in degrees -90 to 90
+ double longitude, // in degrees -180 to 180
+ double altitude, // in feet (ex. for a lake!)
+ double heading, // true heading in degrees
+ double speed, // in knots true
+ double rudder ); // in degrees (between 0 and 5 works best)
+
+
+ int createBallistic( string path, // path to exterior model
+ double latitude, // in degrees -90 to 90
+ double longitude, // in degrees -180 to 180
+ double altitude, // in feet
+ double azimuth, // in degrees (same as heading)
+ double elevation, // in degrees (same as pitch)
+ double speed ); // in feet per second
+
+ void destroyObject( int ID );
+
+ inline double get_user_latitude() { return user_latitude; }
+ inline double get_user_longitude() { return user_longitude; }
+ inline double get_user_altitude() { return user_altitude; }
+ inline double get_user_heading() { return user_heading; }
+ inline double get_user_pitch() { return user_pitch; }
+ inline double get_user_yaw() { return user_yaw; }
+ inline double get_user_speed() {return user_speed; }
private:
bool initDone;
+ int numObjects;
+ SGPropertyNode* root;
+
+ double user_latitude;
+ double user_longitude;
+ double user_altitude;
+ double user_heading;
+ double user_pitch;
+ double user_yaw;
+ double user_speed;
+ int dt_count;
+ void fetchUserState( void );
};
#include "AIShip.hxx"
-FGAIShip::FGAIShip() {
+FGAIShip::FGAIShip(FGAIManager* mgr) {
+ manager = mgr;
hdg_lock = false;
rudder = 0.0;
_type_str = "ship";
#ifndef _FG_AISHIP_HXX
#define _FG_AISHIP_HXX
-#include "AIManager.hxx"
#include "AIBase.hxx"
-
+class FGAIManager;
class FGAIShip : public FGAIBase {
public:
- FGAIShip();
+ FGAIShip(FGAIManager* mgr);
~FGAIShip();
bool init();