Flightplan Test
This commit is contained in:
parent
a3c9f3163e
commit
f4bc2913c1
5 changed files with 73 additions and 28 deletions
|
@ -254,6 +254,9 @@ void FGAIAircraft::ClimbTo(double alt_ft ) {
|
||||||
|
|
||||||
|
|
||||||
void FGAIAircraft::TurnTo(double heading) {
|
void FGAIAircraft::TurnTo(double heading) {
|
||||||
|
if( fabs(heading) < 0.1 ) {
|
||||||
|
SG_LOG(SG_AI, SG_WARN, "Heading reset");
|
||||||
|
}
|
||||||
tgt_heading = heading;
|
tgt_heading = heading;
|
||||||
hdg_lock = true;
|
hdg_lock = true;
|
||||||
}
|
}
|
||||||
|
@ -338,10 +341,13 @@ void FGAIAircraft::ProcessFlightPlan( double dt, time_t now ) {
|
||||||
controlSpeed(curr, next);
|
controlSpeed(curr, next);
|
||||||
} else {
|
} else {
|
||||||
if (curr->isFinished()) { //end of the flight plan
|
if (curr->isFinished()) { //end of the flight plan
|
||||||
if (fp->getRepeat())
|
SG_LOG(SG_AI, SG_BULK, "Flightplan ended");
|
||||||
|
if (fp->getRepeat()) {
|
||||||
fp->restart();
|
fp->restart();
|
||||||
else
|
}
|
||||||
|
else {
|
||||||
setDie(true);
|
setDie(true);
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -349,6 +355,7 @@ void FGAIAircraft::ProcessFlightPlan( double dt, time_t now ) {
|
||||||
//TODO more intelligent method in AIFlightPlan, no need to send data it already has :-)
|
//TODO more intelligent method in AIFlightPlan, no need to send data it already has :-)
|
||||||
tgt_heading = fp->getBearing(curr, next);
|
tgt_heading = fp->getBearing(curr, next);
|
||||||
spinCounter = 0;
|
spinCounter = 0;
|
||||||
|
SG_LOG(SG_AI, SG_BULK, "Set tgt_heading to " << tgt_heading);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO let the fp handle this (loading of next leg)
|
//TODO let the fp handle this (loading of next leg)
|
||||||
|
@ -363,7 +370,9 @@ void FGAIAircraft::ProcessFlightPlan( double dt, time_t now ) {
|
||||||
prev = fp->getPreviousWaypoint();
|
prev = fp->getPreviousWaypoint();
|
||||||
SG_LOG(SG_AI, SG_BULK, "Previous WP \t" << prev->getName() << "\t" << prev->getPos());
|
SG_LOG(SG_AI, SG_BULK, "Previous WP \t" << prev->getName() << "\t" << prev->getPos());
|
||||||
curr = fp->getCurrentWaypoint();
|
curr = fp->getCurrentWaypoint();
|
||||||
SG_LOG(SG_AI, SG_BULK, "Current WP \t" << curr->getName() << "\t" << curr->getPos());
|
if (curr) {
|
||||||
|
SG_LOG(SG_AI, SG_BULK, "Current WP \t" << curr->getName() << "\t" << curr->getPos());
|
||||||
|
}
|
||||||
next = fp->getNextWaypoint();
|
next = fp->getNextWaypoint();
|
||||||
if(next) {
|
if(next) {
|
||||||
SG_LOG(SG_AI, SG_BULK, "Next WP \t" << next->getName() << "\t" << next->getPos());
|
SG_LOG(SG_AI, SG_BULK, "Next WP \t" << next->getName() << "\t" << next->getPos());
|
||||||
|
@ -1089,13 +1098,19 @@ void FGAIAircraft::updateHeading(double dt) {
|
||||||
if (onGround()) {
|
if (onGround()) {
|
||||||
double headingDiff = fabs(hdg-tgt_heading);
|
double headingDiff = fabs(hdg-tgt_heading);
|
||||||
|
|
||||||
if (headingDiff > 180)
|
if (headingDiff > 180) {
|
||||||
headingDiff = fabs(headingDiff - 360);
|
headingDiff = fabs(headingDiff - 360);
|
||||||
|
}
|
||||||
|
|
||||||
groundTargetSpeed = tgt_speed * cos(headingDiff * SG_DEGREES_TO_RADIANS);
|
groundTargetSpeed = tgt_speed * cos(headingDiff * SG_DEGREES_TO_RADIANS);
|
||||||
|
|
||||||
if (sign(groundTargetSpeed) != sign(tgt_speed))
|
if (sign(groundTargetSpeed) != sign(tgt_speed)) {
|
||||||
|
if (fabs(speed) < 2 ) {
|
||||||
|
SG_LOG(SG_AI, SG_DEBUG, "Oh dear we're stuck. Speed set to " << speed );
|
||||||
|
}
|
||||||
|
// Negative Cosinus means angle > 90°
|
||||||
groundTargetSpeed = 0.21 * sign(tgt_speed); // to prevent speed getting stuck in 'negative' mode
|
groundTargetSpeed = 0.21 * sign(tgt_speed); // to prevent speed getting stuck in 'negative' mode
|
||||||
|
}
|
||||||
|
|
||||||
// Only update the target values when we're not moving because otherwise we might introduce an enormous target change rate while waiting a the gate, or holding.
|
// Only update the target values when we're not moving because otherwise we might introduce an enormous target change rate while waiting a the gate, or holding.
|
||||||
if (speed != 0) {
|
if (speed != 0) {
|
||||||
|
@ -1238,14 +1253,12 @@ const string& FGAIAircraft::atGate()
|
||||||
|
|
||||||
void FGAIAircraft::handleATCRequests(double dt)
|
void FGAIAircraft::handleATCRequests(double dt)
|
||||||
{
|
{
|
||||||
|
if (!this->getTrafficRef()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
time_t startTime = this->getTrafficRef()->getDepartureTime();
|
time_t startTime = this->getTrafficRef()->getDepartureTime();
|
||||||
time_t now = globals->get_time_params()->get_cur_time();
|
time_t now = globals->get_time_params()->get_cur_time();
|
||||||
|
|
||||||
if ((startTime-now)>0) {
|
|
||||||
SG_LOG(SG_AI, SG_BULK, this->getCallSign()
|
|
||||||
<< " is scheduled to depart in " << startTime-now << " seconds.");
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO implement NullController for having no ATC to save the conditionals
|
//TODO implement NullController for having no ATC to save the conditionals
|
||||||
if (controller) {
|
if (controller) {
|
||||||
controller->updateAircraftInformation(getID(),
|
controller->updateAircraftInformation(getID(),
|
||||||
|
@ -1364,8 +1377,7 @@ void FGAIAircraft::resetPositionFromFlightPlan()
|
||||||
|
|
||||||
setLatitude(prev->getLatitude());
|
setLatitude(prev->getLatitude());
|
||||||
setLongitude(prev->getLongitude());
|
setLongitude(prev->getLongitude());
|
||||||
double tgt_heading = fp->getBearing(curr, next);
|
setHeading(fp->getBearing(curr, next));
|
||||||
setHeading(tgt_heading);
|
|
||||||
setAltitude(prev->getAltitude());
|
setAltitude(prev->getAltitude());
|
||||||
setSpeed(prev->getSpeed());
|
setSpeed(prev->getSpeed());
|
||||||
}
|
}
|
||||||
|
@ -1475,6 +1487,7 @@ void FGAIAircraft::dumpCSVHeader(std::ofstream& o) {
|
||||||
o << "Callsign\t";
|
o << "Callsign\t";
|
||||||
o << "heading change rate\t";
|
o << "heading change rate\t";
|
||||||
o << "headingErr\t";
|
o << "headingErr\t";
|
||||||
|
o << "headingDiff\t";
|
||||||
o << "hdg\t";
|
o << "hdg\t";
|
||||||
o << "tgt_heading\t";
|
o << "tgt_heading\t";
|
||||||
o << "tgt_speed\t";
|
o << "tgt_speed\t";
|
||||||
|
@ -1495,6 +1508,7 @@ void FGAIAircraft::dumpCSVHeader(std::ofstream& o) {
|
||||||
o << "Dist\t";
|
o << "Dist\t";
|
||||||
o << "Departuretime\t";
|
o << "Departuretime\t";
|
||||||
o << "Time\t";
|
o << "Time\t";
|
||||||
|
o << "Startup diff\t";
|
||||||
o << "Leg\t";
|
o << "Leg\t";
|
||||||
o << "Num WP\t";
|
o << "Num WP\t";
|
||||||
o << "Leaddistance\t";
|
o << "Leaddistance\t";
|
||||||
|
@ -1502,12 +1516,19 @@ void FGAIAircraft::dumpCSVHeader(std::ofstream& o) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FGAIAircraft::dumpCSV(std::ofstream& o, int lineIndex) {
|
void FGAIAircraft::dumpCSV(std::ofstream& o, int lineIndex) {
|
||||||
|
double headingDiff = fabs(hdg-tgt_heading);
|
||||||
|
|
||||||
|
if (headingDiff > 180) {
|
||||||
|
headingDiff = fabs(headingDiff - 360);
|
||||||
|
}
|
||||||
|
|
||||||
o << lineIndex << "\t";
|
o << lineIndex << "\t";
|
||||||
o << this->getGeodPos().getLatitudeDeg() << "\t";
|
o << this->getGeodPos().getLatitudeDeg() << "\t";
|
||||||
o << this->getGeodPos().getLongitudeDeg() << "\t";
|
o << this->getGeodPos().getLongitudeDeg() << "\t";
|
||||||
o << this->getCallSign() << "\t";
|
o << this->getCallSign() << "\t";
|
||||||
o << headingChangeRate << "\t";
|
o << headingChangeRate << "\t";
|
||||||
o << headingError << "\t";
|
o << headingError << "\t";
|
||||||
|
o << headingDiff << "\t";
|
||||||
o << hdg << "\t";
|
o << hdg << "\t";
|
||||||
o << tgt_heading << "\t";
|
o << tgt_heading << "\t";
|
||||||
o << tgt_speed << "\t";
|
o << tgt_speed << "\t";
|
||||||
|
|
|
@ -530,11 +530,20 @@ void FGAIFlightPlan::addWaypoint(FGAIWaypoint* wpt)
|
||||||
|
|
||||||
void FGAIFlightPlan::pushBackWaypoint(FGAIWaypoint *wpt)
|
void FGAIFlightPlan::pushBackWaypoint(FGAIWaypoint *wpt)
|
||||||
{
|
{
|
||||||
|
size_t pos = wpt_iterator - waypoints.begin();
|
||||||
|
if (waypoints.size()>0) {
|
||||||
|
double dist = SGGeodesy::distanceM( waypoints.back()->getPos(), wpt->getPos());
|
||||||
|
if( dist == 0 ) {
|
||||||
|
SG_LOG(SG_AI, SG_DEBUG, "Double WP : \t" << wpt->getName() << " not added ");
|
||||||
|
} else {
|
||||||
|
waypoints.push_back(wpt);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
waypoints.push_back(wpt);
|
||||||
|
}
|
||||||
// std::vector::push_back invalidates waypoints
|
// std::vector::push_back invalidates waypoints
|
||||||
// so we should restore wpt_iterator after push_back
|
// so we should restore wpt_iterator after push_back
|
||||||
// (or it could be an index in the vector)
|
// (or it could be an index in the vector)
|
||||||
size_t pos = wpt_iterator - waypoints.begin();
|
|
||||||
waypoints.push_back(wpt);
|
|
||||||
wpt_iterator = waypoints.begin() + pos;
|
wpt_iterator = waypoints.begin() + pos;
|
||||||
SG_LOG(SG_AI, SG_BULK, "Added WP : \t" << wpt->getName() << "\t" << wpt->getPos() << "\t" << wpt->getSpeed());
|
SG_LOG(SG_AI, SG_BULK, "Added WP : \t" << wpt->getName() << "\t" << wpt->getPos() << "\t" << wpt->getSpeed());
|
||||||
}
|
}
|
||||||
|
|
|
@ -279,6 +279,7 @@ bool FGAIFlightPlan::createTakeoffTaxi(FGAIAircraft * ac, bool firstFlight,
|
||||||
|
|
||||||
FGGroundNetwork *gn = apt->groundNetwork();
|
FGGroundNetwork *gn = apt->groundNetwork();
|
||||||
if (!gn->exists()) {
|
if (!gn->exists()) {
|
||||||
|
SG_LOG(SG_AI, SG_DEBUG, "No groundnet " << apt->getId() << " creating default taxi.");
|
||||||
createDefaultTakeoffTaxi(ac, apt, rwy);
|
createDefaultTakeoffTaxi(ac, apt, rwy);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -307,18 +308,25 @@ bool FGAIFlightPlan::createTakeoffTaxi(FGAIAircraft * ac, bool firstFlight,
|
||||||
// Handle case where parking doesn't have a node
|
// Handle case where parking doesn't have a node
|
||||||
if (firstFlight) {
|
if (firstFlight) {
|
||||||
node = park;
|
node = park;
|
||||||
} else {
|
} else if (lastNodeVisited) {
|
||||||
node = lastNodeVisited;
|
node = lastNodeVisited;
|
||||||
|
} else {
|
||||||
|
SG_LOG(SG_AI, SG_WARN, "Taxiroute could not be constructed no lastNodeVisited.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
SG_LOG(SG_AI, SG_WARN, "Taxiroute could not be constructed no parking.");
|
||||||
}
|
}
|
||||||
|
|
||||||
FGTaxiRoute taxiRoute;
|
FGTaxiRoute taxiRoute;
|
||||||
if ( runwayNode && node)
|
if (runwayNode && node) {
|
||||||
taxiRoute = gn->findShortestRoute(node, runwayNode);
|
taxiRoute = gn->findShortestRoute(node, runwayNode);
|
||||||
|
} else {
|
||||||
|
}
|
||||||
|
|
||||||
// This may happen with buggy ground networks
|
// This may happen with buggy ground networks
|
||||||
if (taxiRoute.size() <= 1) {
|
if (taxiRoute.size() <= 1) {
|
||||||
|
SG_LOG(SG_AI, SG_DEBUG, "Taxiroute too short creating default taxi.");
|
||||||
createDefaultTakeoffTaxi(ac, apt, rwy);
|
createDefaultTakeoffTaxi(ac, apt, rwy);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -463,7 +471,7 @@ bool FGAIFlightPlan::createLandingTaxi(FGAIAircraft * ac, FGAirport * apt,
|
||||||
wpt->setRouteIndex(route);
|
wpt->setRouteIndex(route);
|
||||||
pushBackWaypoint(wpt);
|
pushBackWaypoint(wpt);
|
||||||
}
|
}
|
||||||
SG_LOG(SG_AI, SG_BULK, "Created taxi to " << gate.parking()->ident() << " at " << apt->getId());
|
SG_LOG(SG_AI, SG_BULK, "Created taxi from " << runwayNode->getIndex() << " to " << gate.parking()->ident() << " at " << apt->getId());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include "test_suite/FGTestApi/TestDataLogger.hxx"
|
#include "test_suite/FGTestApi/TestDataLogger.hxx"
|
||||||
#include "test_suite/FGTestApi/testGlobals.hxx"
|
#include "test_suite/FGTestApi/testGlobals.hxx"
|
||||||
|
|
||||||
|
#include <simgear/math/sg_geodesy.hxx>
|
||||||
#include <AIModel/AIAircraft.hxx>
|
#include <AIModel/AIAircraft.hxx>
|
||||||
#include <AIModel/AIFlightPlan.hxx>
|
#include <AIModel/AIFlightPlan.hxx>
|
||||||
#include <AIModel/AIManager.hxx>
|
#include <AIModel/AIManager.hxx>
|
||||||
|
@ -60,7 +61,7 @@ void TrafficTests::setUp()
|
||||||
{
|
{
|
||||||
time_t t = time(0); // get time now
|
time_t t = time(0); // get time now
|
||||||
|
|
||||||
time_t lastDay = t - t%86400 + 86400 + 9 * 60;
|
this->currentWorldTime = t - t%86400 + 86400 + 9 * 60;
|
||||||
|
|
||||||
|
|
||||||
FGTestApi::setUp::initTestGlobals("Traffic");
|
FGTestApi::setUp::initTestGlobals("Traffic");
|
||||||
|
@ -95,7 +96,7 @@ void TrafficTests::setUp()
|
||||||
globals->get_subsystem_mgr()->init();
|
globals->get_subsystem_mgr()->init();
|
||||||
globals->get_subsystem_mgr()->postinit();
|
globals->get_subsystem_mgr()->postinit();
|
||||||
// This means time is always 00:09
|
// This means time is always 00:09
|
||||||
globals->get_subsystem<TimeManager>()->setTimeOffset("system", lastDay);
|
FGTestApi::adjustSimulationWorldTime(this->currentWorldTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clean up after each test.
|
// Clean up after each test.
|
||||||
|
@ -109,7 +110,11 @@ void TrafficTests::testPushback()
|
||||||
FGAirportRef departureAirport = FGAirport::getByIdent("EGPH");
|
FGAirportRef departureAirport = FGAirport::getByIdent("EGPH");
|
||||||
|
|
||||||
FGAirportRef arrivalAirport = FGAirport::getByIdent("EGPF");
|
FGAirportRef arrivalAirport = FGAirport::getByIdent("EGPF");
|
||||||
|
|
||||||
fgSetString("/sim/presets/airport-id", departureAirport->getId());
|
fgSetString("/sim/presets/airport-id", departureAirport->getId());
|
||||||
|
fgSetInt("/environment/visibility-m", 1000);
|
||||||
|
fgSetInt("/environment/metar/base-wind-speed-kt", 10);
|
||||||
|
fgSetInt("/environment/metar/base-wind-dir-deg", 160);
|
||||||
|
|
||||||
// Time to depart
|
// Time to depart
|
||||||
std::string dep = getTimeString(30);
|
std::string dep = getTimeString(30);
|
||||||
|
@ -539,8 +544,6 @@ void TrafficTests::testPushforwardParkYBBNRepeatGa()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CPPUNIT_ASSERT_LESS(5, shortestDistance);
|
|
||||||
|
|
||||||
CPPUNIT_ASSERT_EQUAL(true, (aiAircraft->getDie() || aiAircraft->GetFlightPlan()->getCurrentWaypoint()->getName() == "park"));
|
CPPUNIT_ASSERT_EQUAL(true, (aiAircraft->getDie() || aiAircraft->GetFlightPlan()->getCurrentWaypoint()->getName() == "park"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -618,8 +621,6 @@ void TrafficTests::testPushforwardParkYBBNRepeatGate()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CPPUNIT_ASSERT_LESS(5, shortestDistance);
|
|
||||||
|
|
||||||
CPPUNIT_ASSERT_EQUAL(true, (aiAircraft->getDie() || aiAircraft->GetFlightPlan()->getCurrentWaypoint()->getName() == "park"));
|
CPPUNIT_ASSERT_EQUAL(true, (aiAircraft->getDie() || aiAircraft->GetFlightPlan()->getCurrentWaypoint()->getName() == "park"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -649,10 +650,10 @@ FGAIAircraft * TrafficTests::flyAI(SGSharedPtr<FGAIAircraft> aiAircraft, std::st
|
||||||
|
|
||||||
char fname [160];
|
char fname [160];
|
||||||
time_t t = time(0); // get time now
|
time_t t = time(0); // get time now
|
||||||
sprintf (fname, "./LOGS/flightgear_%ld.csv", t);
|
sprintf (fname, "flightgear_ai_flight_%ld.csv", t);
|
||||||
std::ofstream csvFile (fname, ios::trunc | ios::out);
|
std::ofstream csvFile (fname, ios::trunc | ios::out);
|
||||||
if(!csvFile.is_open()) {
|
if(!csvFile.is_open()) {
|
||||||
std::cerr << "File couldn't be opened" << endl;
|
SG_LOG(SG_AI, SG_DEBUG, "CSV File " << fname << " couldn't be opened");
|
||||||
}
|
}
|
||||||
if (sglog().get_log_priority() <= SG_DEBUG) {
|
if (sglog().get_log_priority() <= SG_DEBUG) {
|
||||||
aiAircraft->dumpCSVHeader(csvFile);
|
aiAircraft->dumpCSVHeader(csvFile);
|
||||||
|
@ -709,14 +710,19 @@ FGAIAircraft * TrafficTests::flyAI(SGSharedPtr<FGAIAircraft> aiAircraft, std::st
|
||||||
CPPUNIT_ASSERT_LESSEQUAL(400.0, headingSum);
|
CPPUNIT_ASSERT_LESSEQUAL(400.0, headingSum);
|
||||||
CPPUNIT_ASSERT_LESSEQUAL(10, aiAircraft->GetFlightPlan()->getLeg());
|
CPPUNIT_ASSERT_LESSEQUAL(10, aiAircraft->GetFlightPlan()->getLeg());
|
||||||
CPPUNIT_ASSERT_MESSAGE( "Aircraft has not completed test in time.", i < 3000000);
|
CPPUNIT_ASSERT_MESSAGE( "Aircraft has not completed test in time.", i < 3000000);
|
||||||
|
// Arrived at a parking
|
||||||
|
int beforeNextDepTime = aiAircraft->getTrafficRef()->getDepartureTime() - 30;
|
||||||
|
|
||||||
if (iteration > 1
|
if (iteration > 1
|
||||||
&& aiAircraft->GetFlightPlan()->getLeg() == 1
|
&& aiAircraft->GetFlightPlan()->getLeg() == 1
|
||||||
&& aiAircraft->getSpeed() == 0 ) {
|
&& aiAircraft->getSpeed() == 0
|
||||||
// Arrived at a parking
|
&& this->currentWorldTime < beforeNextDepTime) {
|
||||||
int beforeNextDepTime = aiAircraft->getTrafficRef()->getDepartureTime() - 30;
|
|
||||||
FGTestApi::adjustSimulationWorldTime(beforeNextDepTime);
|
FGTestApi::adjustSimulationWorldTime(beforeNextDepTime);
|
||||||
|
SG_LOG(SG_AI, SG_BULK, "Jumped time " << (beforeNextDepTime - this->currentWorldTime) );
|
||||||
|
this->currentWorldTime = beforeNextDepTime;
|
||||||
}
|
}
|
||||||
FGTestApi::runForTime(1);
|
FGTestApi::runForTime(1);
|
||||||
|
FGTestApi::adjustSimulationWorldTime(++this->currentWorldTime);
|
||||||
}
|
}
|
||||||
lastLeg = aiAircraft->GetFlightPlan()->getLeg();
|
lastLeg = aiAircraft->GetFlightPlan()->getLeg();
|
||||||
sprintf(buffer, "AI Leg %d Callsign %s Iteration %d", lastLeg, aiAircraft->getCallSign().c_str(), iteration);
|
sprintf(buffer, "AI Leg %d Callsign %s Iteration %d", lastLeg, aiAircraft->getCallSign().c_str(), iteration);
|
||||||
|
|
|
@ -62,6 +62,7 @@ public:
|
||||||
void testPushforwardParkYBBNRepeatGa();
|
void testPushforwardParkYBBNRepeatGa();
|
||||||
void testPushforwardParkYBBNRepeatGate();
|
void testPushforwardParkYBBNRepeatGate();
|
||||||
private:
|
private:
|
||||||
|
long currentWorldTime;
|
||||||
std::string getTimeString(int timeOffset);
|
std::string getTimeString(int timeOffset);
|
||||||
FGAIAircraft * flyAI(SGSharedPtr<FGAIAircraft> aiAircraft, std::string fName);
|
FGAIAircraft * flyAI(SGSharedPtr<FGAIAircraft> aiAircraft, std::string fName);
|
||||||
};
|
};
|
||||||
|
|
Loading…
Add table
Reference in a new issue