Completed miss on purpose functionality

This commit is contained in:
Pax1601
2023-11-05 17:26:39 +01:00
parent bc1c3994d3
commit fa3d65bde6
12 changed files with 12579 additions and 12330 deletions

View File

@@ -77,6 +77,28 @@ namespace State
};
};
namespace ShotsScatter
{
enum ShotsScatters
{
NONE = 0,
HIGH,
MEDIUM,
LOW
};
};
namespace ShotsIntensity
{
enum ShotsIntensities
{
NONE = 0,
LOW,
MEDIUM,
HIGH
};
};
#pragma pack(push, 1)
namespace DataTypes {
struct TACAN

View File

@@ -24,6 +24,7 @@ public:
void acquireControl(unsigned int ID);
void loadDatabases();
Unit* getClosestUnit(Unit* unit, unsigned char coalition, vector<string> categories, double &distance);
map<Unit*, double> getUnitsInRange(Unit* unit, unsigned char coalition, vector<string> categories, double range);
private:
map<unsigned int, Unit*> units;

View File

@@ -245,116 +245,132 @@ void GroundUnit::AIloop()
canAAA = databaseEntry[L"canAAA"].as_bool();
}
if (!canAAA)
setState(State::IDLE);
if (canAAA) {
/* Only run this when the internal counter reaches 0 to avoid excessive computations when no nearby target */
if (internalCounter == 0 && getOperateAs() > 0) {
double distance = 0;
unsigned char targetCoalition = getOperateAs() == 2 ? 1 : 2;
/* Default gun values */
double barrelHeight = 1.0; /* m */
double muzzleVelocity = 860; /* m/s */
double aimTime = 10; /* s */
unsigned int shotsToFire = 10;
double shotsBaseInterval = 15; /* s */
double shotsBaseScatter = 2; /* degs */
double engagementRange = 10000; /* m */
double targetingRange = 0; /* m */
double aimMethodRange = 0; /* m */
/* Only run this when the internal counter reaches 0 to avoid excessive computations when no nearby target */
if (internalCounter == 0 && getOperateAs() > 0) {
double distance = 0;
unsigned char targetCoalition = getOperateAs() == 2 ? 1 : 2;
Unit* target = unitsManager->getClosestUnit(this, targetCoalition, {"Aircraft", "Helicopter"}, distance);
/* Default gun values */
double barrelHeight = 1.0; /* m */
double muzzleVelocity = 860; /* m/s */
double aimTime = 10; /* s */
unsigned int shotsToFire = 10;
double shotsBaseInterval = 15; /* s */
double shotsBaseScatter = 2; /* degs */
double engagementRange = 10000; /* m */
double targetingRange = 0; /* m */
if (database.has_object_field(to_wstring(name))) {
json::value databaseEntry = database[to_wstring(name)];
if (databaseEntry.has_number_field(L"barrelHeight"))
barrelHeight = databaseEntry[L"barrelHeight"].as_number().to_double();
if (databaseEntry.has_number_field(L"muzzleVelocity"))
muzzleVelocity = databaseEntry[L"muzzleVelocity"].as_number().to_double();
if (databaseEntry.has_number_field(L"aimTime"))
aimTime = databaseEntry[L"aimTime"].as_number().to_double();
if (databaseEntry.has_number_field(L"shotsToFire"))
shotsToFire = databaseEntry[L"shotsToFire"].as_number().to_uint32();
if (databaseEntry.has_number_field(L"engagementRange"))
engagementRange = databaseEntry[L"engagementRange"].as_number().to_double();
if (databaseEntry.has_number_field(L"shotsBaseInterval"))
shotsBaseInterval = databaseEntry[L"shotsBaseInterval"].as_number().to_double();
if (databaseEntry.has_number_field(L"shotsBaseScatter"))
shotsBaseScatter = databaseEntry[L"shotsBaseScatter"].as_number().to_double();
if (databaseEntry.has_number_field(L"targetingRange"))
targetingRange = databaseEntry[L"targetingRange"].as_number().to_double();
}
/* Only do if we have a valid target close enough for AAA */
if (target != nullptr && distance < 3 * engagementRange) {
/* Approximate the flight time */
if (muzzleVelocity != 0)
aimTime += distance / muzzleVelocity;
internalCounter = (aimTime + (3 - shotsIntensity) * shotsBaseInterval + 2) / FRAMERATE_TIME_INTERVAL;
/* If the target is in targeting range and we are in highest precision mode, target it */
if (distance < targetingRange && shotsScatter == 3) {
/* Send the command */
std::ostringstream taskSS;
taskSS.precision(10);
taskSS << "{id = 'AttackUnit', unitID = " << target->getID() << " }";
Command* command = dynamic_cast<Command*>(new SetTask(groupName, taskSS.str(), [this]() { this->setHasTaskAssigned(true); }));
scheduler->appendCommand(command);
setHasTask(true);
/* Load gun values from database */
if (database.has_object_field(to_wstring(name))) {
json::value databaseEntry = database[to_wstring(name)];
if (databaseEntry.has_number_field(L"barrelHeight"))
barrelHeight = databaseEntry[L"barrelHeight"].as_number().to_double();
if (databaseEntry.has_number_field(L"muzzleVelocity"))
muzzleVelocity = databaseEntry[L"muzzleVelocity"].as_number().to_double();
if (databaseEntry.has_number_field(L"aimTime"))
aimTime = databaseEntry[L"aimTime"].as_number().to_double();
if (databaseEntry.has_number_field(L"shotsToFire"))
shotsToFire = databaseEntry[L"shotsToFire"].as_number().to_uint32();
if (databaseEntry.has_number_field(L"engagementRange"))
engagementRange = databaseEntry[L"engagementRange"].as_number().to_double();
if (databaseEntry.has_number_field(L"shotsBaseInterval"))
shotsBaseInterval = databaseEntry[L"shotsBaseInterval"].as_number().to_double();
if (databaseEntry.has_number_field(L"shotsBaseScatter"))
shotsBaseScatter = databaseEntry[L"shotsBaseScatter"].as_number().to_double();
if (databaseEntry.has_number_field(L"targetingRange"))
targetingRange = databaseEntry[L"targetingRange"].as_number().to_double();
if (databaseEntry.has_number_field(L"aimMethodRange"))
aimMethodRange = databaseEntry[L"aimMethodRange"].as_number().to_double();
}
/* Else, do miss on purpose */
else {
/* Compute where the target will be in aimTime seconds. */
double aimDistance = target->getHorizontalVelocity() * aimTime;
double aimLat = 0;
double aimLng = 0;
Geodesic::WGS84().Direct(target->getPosition().lat, target->getPosition().lng, target->getHeading() * 57.29577, aimDistance, aimLat, aimLng); /* TODO make util function */
double aimAlt = target->getPosition().alt + target->getVerticalVelocity() * aimTime;
/* Send the command */
if (distance > engagementRange) {
aimAtPoint(Coords(aimLat, aimLng, aimAlt));
log("Aiming at point!");
/* Get all the units in range and select one at random */
double range = aimMethodRange > engagementRange ? aimMethodRange : engagementRange;
map<Unit*, double> targets = unitsManager->getUnitsInRange(this, targetCoalition, { "Aircraft", "Helicopter" }, range);
Unit* target = nullptr;
unsigned int index = static_cast<unsigned int>((RANDOM_ZERO_TO_ONE * (targets.size() - 1)));
for (auto const& p : targets) {
if (index-- == 0) {
target = p.first;
distance = p.second;
}
else {
/* Compute a random scattering depending on the distance and the selected shots scatter */
double scatterDistance = distance * tan(shotsBaseScatter * (3 - shotsScatter) / 57.29577) * RANDOM_MINUS_ONE_TO_ONE;
double scatterAngle = 180 * RANDOM_MINUS_ONE_TO_ONE;
Geodesic::WGS84().Direct(aimLat, aimLng, scatterAngle, scatterDistance, aimLat, aimLng); /* TODO make util function */
aimAlt = aimAlt + distance * tan(shotsBaseScatter * (3 - shotsScatter) / 57.29577) * RANDOM_MINUS_ONE_TO_ONE;
}
/* Only do if we have a valid target close enough for AAA */
if (target != nullptr) {
/* Approximate the flight time */
if (muzzleVelocity != 0)
aimTime += distance / muzzleVelocity;
internalCounter = (aimTime + (ShotsIntensity::HIGH - shotsIntensity) * shotsBaseInterval + 2) / FRAMERATE_TIME_INTERVAL;
/* If the target is in targeting range and we are in highest precision mode, target it */
if (distance < targetingRange && shotsScatter == ShotsScatter::LOW) {
/* Send the command */
std::ostringstream taskSS;
taskSS.precision(10);
taskSS << "{id = 'FireAtPoint', lat = " << aimLat << ", lng = " << aimLng << ", alt = " << aimAlt << ", radius = 0.001, expendQty = " << shotsToFire << " }";
taskSS << "{id = 'AttackUnit', unitID = " << target->getID() << " }";
Command* command = dynamic_cast<Command*>(new SetTask(groupName, taskSS.str(), [this]() { this->setHasTaskAssigned(true); }));
scheduler->appendCommand(command);
setHasTask(true);
}
/* Else, do miss on purpose */
else {
/* Compute where the target will be in aimTime seconds. */
double aimDistance = target->getHorizontalVelocity() * aimTime;
double aimLat = 0;
double aimLng = 0;
Geodesic::WGS84().Direct(target->getPosition().lat, target->getPosition().lng, target->getHeading() * 57.29577, aimDistance, aimLat, aimLng); /* TODO make util to convert degrees and radians function */
double aimAlt = target->getPosition().alt + target->getVerticalVelocity() * aimTime;
setTargetPosition(Coords(aimLat, aimLng, target->getPosition().alt));
/* Send the command */
if (distance > engagementRange) {
aimAtPoint(Coords(aimLat, aimLng, aimAlt));
}
else {
/* Compute a random scattering depending on the distance and the selected shots scatter */
double scatterDistance = distance * tan(shotsBaseScatter * (ShotsScatter::LOW - shotsScatter) / 57.29577) * RANDOM_MINUS_ONE_TO_ONE;
double scatterAngle = 180 * RANDOM_MINUS_ONE_TO_ONE;
Geodesic::WGS84().Direct(aimLat, aimLng, scatterAngle, scatterDistance, aimLat, aimLng); /* TODO make util function */
aimAlt = aimAlt + distance * tan(shotsBaseScatter * (ShotsScatter::LOW - shotsScatter) / 57.29577) * RANDOM_MINUS_ONE_TO_ONE;
std::ostringstream taskSS;
taskSS.precision(10);
taskSS << "{id = 'FireAtPoint', lat = " << aimLat << ", lng = " << aimLng << ", alt = " << aimAlt << ", radius = 0.001, expendQty = " << shotsToFire << " }";
Command* command = dynamic_cast<Command*>(new SetTask(groupName, taskSS.str(), [this]() { this->setHasTaskAssigned(true); }));
scheduler->appendCommand(command);
setHasTask(true);
}
setTargetPosition(Coords(aimLat, aimLng, target->getPosition().alt));
}
missOnPurposeTarget = target;
}
else {
if (getHasTask())
resetTask();
}
missOnPurposeTarget = target;
}
else {
if (getHasTask())
resetTask();
}
}
/* If no valid target was detected */
if (internalCounter == 0) {
double alertnessTimeConstant = 10; /* s */
if (database.has_object_field(to_wstring(name))) {
json::value databaseEntry = database[to_wstring(name)];
if (databaseEntry.has_number_field(L"alertnessTimeConstant"))
alertnessTimeConstant = databaseEntry[L"alertnessTimeConstant"].as_number().to_double();
/* If no valid target was detected */
if (internalCounter == 0) {
double alertnessTimeConstant = 10; /* s */
if (database.has_object_field(to_wstring(name))) {
json::value databaseEntry = database[to_wstring(name)];
if (databaseEntry.has_number_field(L"alertnessTimeConstant"))
alertnessTimeConstant = databaseEntry[L"alertnessTimeConstant"].as_number().to_double();
}
internalCounter = (5 + RANDOM_ZERO_TO_ONE * alertnessTimeConstant) / FRAMERATE_TIME_INTERVAL;
missOnPurposeTarget = nullptr;
setTargetPosition(Coords(NULL));
}
internalCounter = (5 + RANDOM_ZERO_TO_ONE * alertnessTimeConstant) / FRAMERATE_TIME_INTERVAL;
missOnPurposeTarget = nullptr;
setTargetPosition(Coords(NULL));
internalCounter--;
}
else {
setState(State::IDLE);
}
internalCounter--;
break;
}
@@ -363,6 +379,57 @@ void GroundUnit::AIloop()
}
}
void GroundUnit::aimAtPoint(Coords aimTarget, double horizontalScatterMultiplier, double verticalScatterMultiplier) {
double dist;
double bearing1;
double bearing2;
Geodesic::WGS84().Inverse(position.lat, position.lng, aimTarget.lat, aimTarget.lng, dist, bearing1, bearing2);
/* Aim point distance */
double r = 15; /* m */
/* Default gun values */
double barrelHeight = 1.0; /* m */
double muzzleVelocity = 860; /* m/s */
double shotsBaseScatter = 5; /* degs */
if (database.has_object_field(to_wstring(name))) {
json::value databaseEntry = database[to_wstring(name)];
if (databaseEntry.has_number_field(L"barrelHeight") && databaseEntry.has_number_field(L"muzzleVelocity")) {
barrelHeight = databaseEntry[L"barrelHeight"].as_number().to_double();
muzzleVelocity = databaseEntry[L"muzzleVelocity"].as_number().to_double();
}
if (databaseEntry.has_number_field(L"shotsBaseScatter"))
shotsBaseScatter = databaseEntry[L"shotsBaseScatter"].as_number().to_double();
}
/* Compute the elevation angle of the gun*/
double deltaHeight = (aimTarget.alt - (position.alt + barrelHeight));
double alpha = 9.81 / 2 * dist * dist / (muzzleVelocity * muzzleVelocity);
double inner = dist * dist - 4 * alpha * (alpha + deltaHeight);
/* Check we can reach the target*/
if (inner > 0) {
/* Compute elevation and bearing */
double barrelElevation = r * tan(atan((dist - sqrt(inner)) / (2 * alpha)) + RANDOM_MINUS_ONE_TO_ONE * (ShotsScatter::LOW - shotsScatter) * verticalScatterMultiplier);
double lat = 0;
double lng = 0;
double randomBearing = bearing1 + RANDOM_MINUS_ONE_TO_ONE * (ShotsScatter::LOW - shotsScatter) * horizontalScatterMultiplier;
Geodesic::WGS84().Direct(position.lat, position.lng, randomBearing, r, lat, lng);
std::ostringstream taskSS;
taskSS.precision(10);
taskSS << "{id = 'FireAtPoint', lat = " << lat << ", lng = " << lng << ", alt = " << position.alt + barrelElevation + barrelHeight << ", radius = 0.001}";
Command* command = dynamic_cast<Command*>(new SetTask(groupName, taskSS.str(), [this]() { this->setHasTaskAssigned(true); }));
scheduler->appendCommand(command);
setHasTask(true);
}
else {
log("Target out of range for " + unitName + "(" + name + ")");
}
}
void GroundUnit::changeSpeed(string change)
{
if (change.compare("stop") == 0)
@@ -392,45 +459,3 @@ void GroundUnit::setFollowRoads(bool newFollowRoads, bool force)
resetActiveDestination(); /* Reset active destination to apply option*/
}
}
void GroundUnit::aimAtPoint(Coords aimTarget, double horizontalScatterMultiplier, double verticalScatterMultiplier) {
double dist;
double bearing1;
double bearing2;
Geodesic::WGS84().Inverse(position.lat, position.lng, aimTarget.lat, aimTarget.lng, dist, bearing1, bearing2);
double r = 15; /* m */
/* Default gun values */
double barrelHeight = 1.0; /* m */
double muzzleVelocity = 860; /* m/s */
double shotsBaseScatter = 5; /* degs */
if (database.has_object_field(to_wstring(name))) {
json::value databaseEntry = database[to_wstring(name)];
if (databaseEntry.has_number_field(L"barrelHeight") && databaseEntry.has_number_field(L"muzzleVelocity")) {
barrelHeight = databaseEntry[L"barrelHeight"].as_number().to_double();
muzzleVelocity = databaseEntry[L"muzzleVelocity"].as_number().to_double();
}
if (databaseEntry.has_number_field(L"shotsBaseScatter"))
shotsBaseScatter = databaseEntry[L"shotsBaseScatter"].as_number().to_double();
}
//double barrelElevation = r * (9.81 * dist / (2 * muzzleVelocity * muzzleVelocity) + (aimTarget.alt - (position.alt + barrelHeight)) / dist); /* m */
double deltaHeight = (aimTarget.alt - (position.alt + barrelHeight));
double alpha = 9.81 / 2 * dist * dist / (muzzleVelocity * muzzleVelocity);
double inner = dist * dist - 4 * alpha * (alpha + deltaHeight);
if (inner > 0) {
double barrelElevation = r * tan(atan((dist - sqrt(inner)) / (2 * alpha)) + RANDOM_MINUS_ONE_TO_ONE * (3 - shotsScatter) * verticalScatterMultiplier);
double lat = 0;
double lng = 0;
double randomBearing = bearing1 + RANDOM_MINUS_ONE_TO_ONE * (3 - shotsScatter) * horizontalScatterMultiplier;
Geodesic::WGS84().Direct(position.lat, position.lng, randomBearing, r, lat, lng);
std::ostringstream taskSS;
taskSS.precision(10);
taskSS << "{id = 'FireAtPoint', lat = " << lat << ", lng = " << lng << ", alt = " << position.alt + barrelElevation + barrelHeight << ", radius = 0.001}";
Command* command = dynamic_cast<Command*>(new SetTask(groupName, taskSS.str(), [this]() { this->setHasTaskAssigned(true); }));
scheduler->appendCommand(command);
setHasTask(true);
}
}

View File

@@ -640,7 +640,7 @@ void Scheduler::handleRequest(string key, json::value value, string username, js
if (unit != nullptr) {
unsigned char shotsScatter = value[L"shotsScatter"].as_number().to_uint32();
unit->setShotsScatter(shotsScatter);
log(username + " set unit " + + unit->getUnitName() + "(" + unit->getName() + ") shots scatter to " + to_string(shotsScatter), true);
log(username + " set unit " + unit->getUnitName() + "(" + unit->getName() + ") shots scatter to " + to_string(shotsScatter), true);
}
}
/************************/

View File

@@ -194,6 +194,35 @@ Unit* UnitsManager::getClosestUnit(Unit* unit, unsigned char coalition, vector<s
return closestUnit;
}
map<Unit*, double> UnitsManager::getUnitsInRange(Unit* unit, unsigned char coalition, vector<string> categories, double range) {
map<Unit*, double> unitsInRange;
for (auto const& p : units) {
/* Check if the units category is of the correct type */
bool requestedCategory = false;
for (auto const& category : categories) {
if (p.second->getCategory().compare(category) == 0) {
requestedCategory = true;
break;
}
}
/* Check if the unit belongs to the desired coalition, is alive, and is of the category requested */
if (requestedCategory && p.second->getCoalition() == coalition && p.second->getAlive()) {
/* Compute the distance from the unit to the tested unit */
double dist;
double bearing1;
double bearing2;
Geodesic::WGS84().Inverse(unit->getPosition().lat, unit->getPosition().lng, p.second->getPosition().lat, p.second->getPosition().lng, dist, bearing1, bearing2);
if (dist <= range)
unitsInRange[p.second] = dist;
}
}
return unitsInRange;
}
void UnitsManager::acquireControl(unsigned int ID) {
Unit* leader = getGroupLeader(ID);
if (leader != nullptr) {