mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
fix: minor improvements to miss on purpose mode
This commit is contained in:
@@ -108,12 +108,14 @@ void GroundUnit::setState(unsigned char newState)
|
|||||||
/************ Perform any action required when ENTERING a state ************/
|
/************ Perform any action required when ENTERING a state ************/
|
||||||
switch (newState) {
|
switch (newState) {
|
||||||
case State::IDLE: {
|
case State::IDLE: {
|
||||||
|
setTask("Idle");
|
||||||
setEnableTaskCheckFailed(false);
|
setEnableTaskCheckFailed(false);
|
||||||
clearActivePath();
|
clearActivePath();
|
||||||
resetActiveDestination();
|
resetActiveDestination();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case State::REACH_DESTINATION: {
|
case State::REACH_DESTINATION: {
|
||||||
|
setTask("Reaching destination");
|
||||||
setEnableTaskCheckFailed(true);
|
setEnableTaskCheckFailed(true);
|
||||||
resetActiveDestination();
|
resetActiveDestination();
|
||||||
break;
|
break;
|
||||||
@@ -125,6 +127,7 @@ void GroundUnit::setState(unsigned char newState)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case State::FIRE_AT_AREA: {
|
case State::FIRE_AT_AREA: {
|
||||||
|
setTask("Firing at area");
|
||||||
setTargetPosition(currentTargetPosition);
|
setTargetPosition(currentTargetPosition);
|
||||||
setEnableTaskCheckFailed(true);
|
setEnableTaskCheckFailed(true);
|
||||||
clearActivePath();
|
clearActivePath();
|
||||||
@@ -132,6 +135,7 @@ void GroundUnit::setState(unsigned char newState)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case State::SIMULATE_FIRE_FIGHT: {
|
case State::SIMULATE_FIRE_FIGHT: {
|
||||||
|
setTask("Simulating fire fight");
|
||||||
setTargetPosition(currentTargetPosition);
|
setTargetPosition(currentTargetPosition);
|
||||||
setEnableTaskCheckFailed(false);
|
setEnableTaskCheckFailed(false);
|
||||||
clearActivePath();
|
clearActivePath();
|
||||||
@@ -139,12 +143,14 @@ void GroundUnit::setState(unsigned char newState)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case State::SCENIC_AAA: {
|
case State::SCENIC_AAA: {
|
||||||
|
setTask("Scenic AAA");
|
||||||
setEnableTaskCheckFailed(false);
|
setEnableTaskCheckFailed(false);
|
||||||
clearActivePath();
|
clearActivePath();
|
||||||
resetActiveDestination();
|
resetActiveDestination();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case State::MISS_ON_PURPOSE: {
|
case State::MISS_ON_PURPOSE: {
|
||||||
|
setTask("Miss on purpose");
|
||||||
setEnableTaskCheckFailed(false);
|
setEnableTaskCheckFailed(false);
|
||||||
clearActivePath();
|
clearActivePath();
|
||||||
resetActiveDestination();
|
resetActiveDestination();
|
||||||
@@ -156,6 +162,7 @@ void GroundUnit::setState(unsigned char newState)
|
|||||||
|
|
||||||
setHasTask(false);
|
setHasTask(false);
|
||||||
resetTaskFailedCounter();
|
resetTaskFailedCounter();
|
||||||
|
nextTaskingMilliseconds = 0;
|
||||||
|
|
||||||
log(unitName + " setting state from " + to_string(state) + " to " + to_string(newState));
|
log(unitName + " setting state from " + to_string(state) + " to " + to_string(newState));
|
||||||
state = newState;
|
state = newState;
|
||||||
@@ -183,14 +190,12 @@ void GroundUnit::AIloop()
|
|||||||
|
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case State::IDLE: {
|
case State::IDLE: {
|
||||||
setTask("Idle");
|
|
||||||
if (getHasTask())
|
if (getHasTask())
|
||||||
resetTask();
|
resetTask();
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case State::REACH_DESTINATION: {
|
case State::REACH_DESTINATION: {
|
||||||
setTask("Reaching destination");
|
|
||||||
|
|
||||||
string enrouteTask = "";
|
string enrouteTask = "";
|
||||||
bool looping = false;
|
bool looping = false;
|
||||||
|
|
||||||
@@ -237,8 +242,6 @@ void GroundUnit::AIloop()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case State::FIRE_AT_AREA: {
|
case State::FIRE_AT_AREA: {
|
||||||
setTask("Firing at area");
|
|
||||||
|
|
||||||
if (!getHasTask()) {
|
if (!getHasTask()) {
|
||||||
std::ostringstream taskSS;
|
std::ostringstream taskSS;
|
||||||
taskSS.precision(10);
|
taskSS.precision(10);
|
||||||
@@ -258,55 +261,59 @@ void GroundUnit::AIloop()
|
|||||||
case State::SIMULATE_FIRE_FIGHT: {
|
case State::SIMULATE_FIRE_FIGHT: {
|
||||||
string taskString = "";
|
string taskString = "";
|
||||||
|
|
||||||
if (
|
if ((totalShellsFired - shellsFiredAtTasking >= shotsToFire || timeNow >= nextTaskingMilliseconds) && targetPosition != Coords(NULL)) {
|
||||||
(totalShellsFired - shellsFiredAtTasking >= shotsToFire || timeNow >= nextTaskingMilliseconds) &&
|
if (scheduler->getLoad() > 100) {
|
||||||
targetPosition != Coords(NULL) &&
|
taskString = "Excessive load, skipping tasking of unit";
|
||||||
scheduler->getLoad() < 30
|
setTargetPosition(Coords(NULL));
|
||||||
) {
|
if (getHasTask())
|
||||||
/* Get the distance and bearing to the target */
|
resetTask();
|
||||||
Coords scatteredTargetPosition = targetPosition;
|
|
||||||
double distance;
|
|
||||||
double bearing1;
|
|
||||||
double bearing2;
|
|
||||||
Geodesic::WGS84().Inverse(getPosition().lat, getPosition().lng, scatteredTargetPosition.lat, scatteredTargetPosition.lng, distance, bearing1, bearing2);
|
|
||||||
|
|
||||||
/* Apply a scatter to the aim */
|
|
||||||
bearing1 += RANDOM_MINUS_ONE_TO_ONE * (ShotsScatter::LOW - shotsScatter + 1) * 10;
|
|
||||||
|
|
||||||
/* Compute the scattered position applying a random scatter to the shot */
|
|
||||||
double scatterDistance = distance * tan(10 /* degs */ * (ShotsScatter::LOW - shotsScatter) / 57.29577 + 2 / 57.29577 /* degs */) * RANDOM_MINUS_ONE_TO_ONE;
|
|
||||||
Geodesic::WGS84().Direct(scatteredTargetPosition.lat, scatteredTargetPosition.lng, bearing1, scatterDistance, scatteredTargetPosition.lat, scatteredTargetPosition.lng);
|
|
||||||
|
|
||||||
/* Recover the data from the database */
|
|
||||||
bool indirectFire = false;
|
|
||||||
if (database.has_object_field(to_wstring(name))) {
|
|
||||||
json::value databaseEntry = database[to_wstring(name)];
|
|
||||||
if (databaseEntry.has_boolean_field(L"indirectFire"))
|
|
||||||
indirectFire = databaseEntry[L"indirectFire"].as_bool();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If the unit is of the indirect fire type, like a mortar, simply shoot at the target */
|
|
||||||
if (indirectFire) {
|
|
||||||
taskString += "Simulating fire fight with indirect fire";
|
|
||||||
log(unitName + "(" + name + ")" + " simulating fire fight with indirect fire");
|
|
||||||
std::ostringstream taskSS;
|
|
||||||
taskSS.precision(10);
|
|
||||||
taskSS << "{id = 'FireAtPoint', lat = " << scatteredTargetPosition.lat << ", lng = " << scatteredTargetPosition.lng << ", radius = 0.01}";
|
|
||||||
Command* command = dynamic_cast<Command*>(new SetTask(groupName, taskSS.str(), [this]() { this->setHasTaskAssigned(true); }));
|
|
||||||
scheduler->appendCommand(command);
|
|
||||||
shellsFiredAtTasking = totalShellsFired;
|
|
||||||
setHasTask(true);
|
|
||||||
}
|
|
||||||
/* Otherwise use the aim method */
|
|
||||||
else {
|
else {
|
||||||
taskString += "Simulating fire fight with aim point method. ";
|
/* Get the distance and bearing to the target */
|
||||||
log(unitName + "(" + name + ")" + " simulating fire fight with aim at point method");
|
Coords scatteredTargetPosition = targetPosition;
|
||||||
string aimTaskString = aimAtPoint(scatteredTargetPosition);
|
double distance;
|
||||||
taskString += aimTaskString;
|
double bearing1;
|
||||||
}
|
double bearing2;
|
||||||
|
Geodesic::WGS84().Inverse(getPosition().lat, getPosition().lng, scatteredTargetPosition.lat, scatteredTargetPosition.lng, distance, bearing1, bearing2);
|
||||||
|
|
||||||
/* Wait an amout of time depending on the shots intensity */
|
/* Apply a scatter to the aim */
|
||||||
nextTaskingMilliseconds = timeNow + static_cast<unsigned long>(2 * aimTime * 1000);
|
bearing1 += RANDOM_MINUS_ONE_TO_ONE * (ShotsScatter::LOW - shotsScatter + 1) * 10;
|
||||||
|
|
||||||
|
/* Compute the scattered position applying a random scatter to the shot */
|
||||||
|
double scatterDistance = distance * tan(10 /* degs */ * (ShotsScatter::LOW - shotsScatter) / 57.29577 + 2 / 57.29577 /* degs */) * RANDOM_MINUS_ONE_TO_ONE;
|
||||||
|
Geodesic::WGS84().Direct(scatteredTargetPosition.lat, scatteredTargetPosition.lng, bearing1, scatterDistance, scatteredTargetPosition.lat, scatteredTargetPosition.lng);
|
||||||
|
|
||||||
|
/* Recover the data from the database */
|
||||||
|
bool indirectFire = false;
|
||||||
|
if (database.has_object_field(to_wstring(name))) {
|
||||||
|
json::value databaseEntry = database[to_wstring(name)];
|
||||||
|
if (databaseEntry.has_boolean_field(L"indirectFire"))
|
||||||
|
indirectFire = databaseEntry[L"indirectFire"].as_bool();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If the unit is of the indirect fire type, like a mortar, simply shoot at the target */
|
||||||
|
if (indirectFire) {
|
||||||
|
taskString += "Simulating fire fight with indirect fire";
|
||||||
|
log(unitName + "(" + name + ")" + " simulating fire fight with indirect fire");
|
||||||
|
std::ostringstream taskSS;
|
||||||
|
taskSS.precision(10);
|
||||||
|
taskSS << "{id = 'FireAtPoint', lat = " << scatteredTargetPosition.lat << ", lng = " << scatteredTargetPosition.lng << ", radius = 0.01}";
|
||||||
|
Command* command = dynamic_cast<Command*>(new SetTask(groupName, taskSS.str(), [this]() { this->setHasTaskAssigned(true); }));
|
||||||
|
scheduler->appendCommand(command);
|
||||||
|
shellsFiredAtTasking = totalShellsFired;
|
||||||
|
setHasTask(true);
|
||||||
|
}
|
||||||
|
/* Otherwise use the aim method */
|
||||||
|
else {
|
||||||
|
taskString += "Simulating fire fight with aim point method. ";
|
||||||
|
log(unitName + "(" + name + ")" + " simulating fire fight with aim at point method");
|
||||||
|
string aimTaskString = aimAtPoint(scatteredTargetPosition);
|
||||||
|
taskString += aimTaskString;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Wait an amout of time depending on the shots intensity */
|
||||||
|
nextTaskingMilliseconds = timeNow + static_cast<unsigned long>(2 * aimTime * 1000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (targetPosition == Coords(NULL))
|
if (targetPosition == Coords(NULL))
|
||||||
@@ -327,63 +334,71 @@ void GroundUnit::AIloop()
|
|||||||
string taskString = "";
|
string taskString = "";
|
||||||
|
|
||||||
/* Only perform scenic functions when the scheduler is "free" */
|
/* Only perform scenic functions when the scheduler is "free" */
|
||||||
if ((totalShellsFired - shellsFiredAtTasking >= shotsToFire || timeNow >= nextTaskingMilliseconds) &&
|
if (totalShellsFired - shellsFiredAtTasking >= shotsToFire || timeNow >= nextTaskingMilliseconds) {
|
||||||
scheduler->getLoad() < 30) {
|
if (scheduler->getLoad() > 100) {
|
||||||
double distance = 0;
|
taskString = "Excessive load, skipping tasking of unit";
|
||||||
unsigned char unitCoalition = coalition == 0 ? getOperateAs() : coalition;
|
setTargetPosition(Coords(NULL));
|
||||||
unsigned char targetCoalition = unitCoalition == 2 ? 1 : 2;
|
if (getHasTask())
|
||||||
Unit* target = unitsManager->getClosestUnit(this, targetCoalition, { "Aircraft", "Helicopter" }, distance);
|
resetTask();
|
||||||
|
|
||||||
/* Recover the data from the database */
|
|
||||||
bool flak = false;
|
|
||||||
if (database.has_object_field(to_wstring(name))) {
|
|
||||||
json::value databaseEntry = database[to_wstring(name)];
|
|
||||||
if (databaseEntry.has_boolean_field(L"flak"))
|
|
||||||
flak = databaseEntry[L"flak"].as_bool();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Only run if an enemy air unit is closer than 20km to avoid useless load */
|
|
||||||
double activationDistance = 20000;
|
|
||||||
if (2 * engagementRange > activationDistance)
|
|
||||||
activationDistance = 2 * engagementRange;
|
|
||||||
|
|
||||||
if (target != nullptr && distance < activationDistance /* m */) {
|
|
||||||
double r = 15; /* m */
|
|
||||||
double barrelElevation = position.alt + barrelHeight + r * tan(acos(((double)(rand()) / (double)(RAND_MAX))));
|
|
||||||
|
|
||||||
double lat = 0;
|
|
||||||
double lng = 0;
|
|
||||||
double randomBearing = ((double)(rand()) / (double)(RAND_MAX)) * 360;
|
|
||||||
Geodesic::WGS84().Direct(position.lat, position.lng, randomBearing, r, lat, lng);
|
|
||||||
|
|
||||||
if (flak) {
|
|
||||||
lat = position.lat + RANDOM_MINUS_ONE_TO_ONE * (1 + (ShotsScatter::LOW - shotsScatter)) * 0.01;
|
|
||||||
lng = position.lng + RANDOM_MINUS_ONE_TO_ONE * (1 + (ShotsScatter::LOW - shotsScatter)) * 0.01;
|
|
||||||
barrelElevation = target->getPosition().alt + RANDOM_MINUS_ONE_TO_ONE * (ShotsScatter::LOW - shotsScatter) * 1000;
|
|
||||||
taskString += "Flak box mode.";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
taskString += "Scenic AAA. Bearing: " + to_string((int)round(randomBearing)) + "deg";
|
|
||||||
}
|
|
||||||
|
|
||||||
taskString += ". Aim point elevation " + to_string((int) round(barrelElevation - position.alt)) + "m AGL";
|
|
||||||
|
|
||||||
std::ostringstream taskSS;
|
|
||||||
taskSS.precision(10);
|
|
||||||
taskSS << "{id = 'FireAtPoint', lat = " << lat << ", lng = " << lng << ", alt = " << barrelElevation << ", radius = 0.001 }";
|
|
||||||
Command* command = dynamic_cast<Command*>(new SetTask(groupName, taskSS.str(), [this]() { this->setHasTaskAssigned(true); }));
|
|
||||||
scheduler->appendCommand(command);
|
|
||||||
shellsFiredAtTasking = totalShellsFired;
|
|
||||||
setHasTask(true);
|
|
||||||
|
|
||||||
/* Wait an amout of time depending on the shots intensity */
|
|
||||||
nextTaskingMilliseconds = timeNow + static_cast<unsigned long>(2 * aimTime * 1000);
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (target == nullptr)
|
double distance = 0;
|
||||||
taskString += "Scenic AAA. No valid target.";
|
unsigned char unitCoalition = coalition == 0 ? getOperateAs() : coalition;
|
||||||
else
|
unsigned char targetCoalition = unitCoalition == 2 ? 1 : 2;
|
||||||
taskString += "Scenic AAA. Target outside max range: " + to_string((int)round(distance)) + "m.";
|
Unit* target = unitsManager->getClosestUnit(this, targetCoalition, { "Aircraft", "Helicopter" }, distance);
|
||||||
|
|
||||||
|
/* Recover the data from the database */
|
||||||
|
bool flak = false;
|
||||||
|
if (database.has_object_field(to_wstring(name))) {
|
||||||
|
json::value databaseEntry = database[to_wstring(name)];
|
||||||
|
if (databaseEntry.has_boolean_field(L"flak"))
|
||||||
|
flak = databaseEntry[L"flak"].as_bool();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only run if an enemy air unit is closer than 20km to avoid useless load */
|
||||||
|
double activationDistance = 20000;
|
||||||
|
if (2 * engagementRange > activationDistance)
|
||||||
|
activationDistance = 2 * engagementRange;
|
||||||
|
|
||||||
|
if (target != nullptr && distance < activationDistance /* m */) {
|
||||||
|
double r = 15; /* m */
|
||||||
|
double barrelElevation = position.alt + barrelHeight + r * tan(acos(((double)(rand()) / (double)(RAND_MAX))));
|
||||||
|
|
||||||
|
double lat = 0;
|
||||||
|
double lng = 0;
|
||||||
|
double randomBearing = ((double)(rand()) / (double)(RAND_MAX)) * 360;
|
||||||
|
Geodesic::WGS84().Direct(position.lat, position.lng, randomBearing, r, lat, lng);
|
||||||
|
|
||||||
|
if (flak) {
|
||||||
|
lat = position.lat + RANDOM_MINUS_ONE_TO_ONE * (1 + (ShotsScatter::LOW - shotsScatter)) * 0.01;
|
||||||
|
lng = position.lng + RANDOM_MINUS_ONE_TO_ONE * (1 + (ShotsScatter::LOW - shotsScatter)) * 0.01;
|
||||||
|
barrelElevation = target->getPosition().alt + RANDOM_MINUS_ONE_TO_ONE * (ShotsScatter::LOW - shotsScatter) * 1000;
|
||||||
|
taskString += "Flak box mode.";
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
taskString += "Scenic AAA. Bearing: " + to_string((int)round(randomBearing)) + "deg";
|
||||||
|
}
|
||||||
|
|
||||||
|
taskString += ". Aim point elevation " + to_string((int)round(barrelElevation - position.alt)) + "m AGL";
|
||||||
|
|
||||||
|
std::ostringstream taskSS;
|
||||||
|
taskSS.precision(10);
|
||||||
|
taskSS << "{id = 'FireAtPoint', lat = " << lat << ", lng = " << lng << ", alt = " << barrelElevation << ", radius = 0.001 }";
|
||||||
|
Command* command = dynamic_cast<Command*>(new SetTask(groupName, taskSS.str(), [this]() { this->setHasTaskAssigned(true); }));
|
||||||
|
scheduler->appendCommand(command);
|
||||||
|
shellsFiredAtTasking = totalShellsFired;
|
||||||
|
setHasTask(true);
|
||||||
|
|
||||||
|
/* Wait an amout of time depending on the shots intensity */
|
||||||
|
nextTaskingMilliseconds = timeNow + static_cast<unsigned long>(2 * aimTime * 1000);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setTargetPosition(Coords(NULL));
|
||||||
|
if (target == nullptr)
|
||||||
|
taskString += "Scenic AAA. No valid target.";
|
||||||
|
else
|
||||||
|
taskString += "Scenic AAA. Target outside max range: " + to_string((int)round(distance)) + "m.";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -418,113 +433,121 @@ void GroundUnit::AIloop()
|
|||||||
if (canAAA) {
|
if (canAAA) {
|
||||||
/* Only perform scenic functions when the scheduler is "free" */
|
/* Only perform scenic functions when the scheduler is "free" */
|
||||||
/* Only run this when the internal counter reaches 0 to avoid excessive computations when no nearby target */
|
/* Only run this when the internal counter reaches 0 to avoid excessive computations when no nearby target */
|
||||||
if ((totalShellsFired - shellsFiredAtTasking >= shotsToFire || timeNow >= nextTaskingMilliseconds) &&
|
if (totalShellsFired - shellsFiredAtTasking >= shotsToFire || timeNow >= nextTaskingMilliseconds) {
|
||||||
scheduler->getLoad() < 30) {
|
if (scheduler->getLoad() > 100) {
|
||||||
double distance = 0;
|
taskString = "Excessive load, skipping tasking of unit";
|
||||||
unsigned char unitCoalition = coalition == 0 ? getOperateAs() : coalition;
|
setTargetPosition(Coords(NULL));
|
||||||
unsigned char targetCoalition = unitCoalition == 2 ? 1 : 2;
|
|
||||||
|
|
||||||
/* Get all the units in range and select one at random */
|
|
||||||
double range = max(max(engagementRange, aimMethodRange), acquisitionRange);
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Only do if we have a valid target close enough for AAA */
|
|
||||||
if (target != nullptr) {
|
|
||||||
taskString += "Missing on purpose. Valid target at range: " + to_string((int) round(distance)) + "m";
|
|
||||||
|
|
||||||
// Very simplified algorithm ignoring drag
|
|
||||||
double correctedAimTime = aimTime + distance / muzzleVelocity;
|
|
||||||
|
|
||||||
/* If the target is in targeting range and we are in highest precision mode, target it */
|
|
||||||
if (distance < targetingRange && shotsScatter == ShotsScatter::LOW) {
|
|
||||||
taskString += ". Range is less than targeting range (" + to_string((int) round(targetingRange)) + "m) and scatter is LOW, aiming at target.";
|
|
||||||
|
|
||||||
/* 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);
|
|
||||||
shellsFiredAtTasking = totalShellsFired;
|
|
||||||
setHasTask(true);
|
|
||||||
|
|
||||||
nextTaskingMilliseconds = timeNow + static_cast<unsigned long>(2 * aimTime * 1000);
|
|
||||||
}
|
|
||||||
/* Else, do miss on purpose */
|
|
||||||
else {
|
|
||||||
/* Compute where the target will be in aimTime seconds. */
|
|
||||||
double aimDistance = target->getHorizontalVelocity() * correctedAimTime;
|
|
||||||
double aimLat = 0;
|
|
||||||
double aimLng = 0;
|
|
||||||
Geodesic::WGS84().Direct(target->getPosition().lat, target->getPosition().lng, target->getTrack() * 57.29577, aimDistance, aimLat, aimLng); /* TODO make util to convert degrees and radians function */
|
|
||||||
double aimAlt = target->getPosition().alt + target->getVerticalVelocity();
|
|
||||||
|
|
||||||
if (flak) {
|
|
||||||
aimLat += RANDOM_MINUS_ONE_TO_ONE * (1 + (ShotsScatter::LOW - shotsScatter)) * 0.01;
|
|
||||||
aimLng += RANDOM_MINUS_ONE_TO_ONE * (1 + (ShotsScatter::LOW - shotsScatter)) * 0.01;
|
|
||||||
aimAlt += RANDOM_MINUS_ONE_TO_ONE * (1 + (ShotsScatter::LOW - shotsScatter)) * 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Send the command */
|
|
||||||
if (distance < engagementRange) {
|
|
||||||
taskString += ". Range is less than engagement range (" + to_string((int) round(engagementRange)) + "m), using FIRE AT POINT method";
|
|
||||||
|
|
||||||
/* If the unit is closer than the engagement range, use the fire at point method */
|
|
||||||
std::ostringstream taskSS;
|
|
||||||
taskSS.precision(10);
|
|
||||||
taskSS << "{id = 'FireAtPoint', lat = " << aimLat << ", lng = " << aimLng << ", alt = " << aimAlt << ", radius = 0.001 }";
|
|
||||||
|
|
||||||
taskString += ". Aiming altitude " + to_string((int)round((aimAlt - position.alt) / 0.3048)) + "ft AGL";
|
|
||||||
Command* command = dynamic_cast<Command*>(new SetTask(groupName, taskSS.str(), [this]() { this->setHasTaskAssigned(true); }));
|
|
||||||
scheduler->appendCommand(command);
|
|
||||||
shellsFiredAtTasking = totalShellsFired;
|
|
||||||
setHasTask(true);
|
|
||||||
setTargetPosition(Coords(aimLat, aimLng, target->getPosition().alt));
|
|
||||||
nextTaskingMilliseconds = timeNow + static_cast<unsigned long>(2 * aimTime * 1000);
|
|
||||||
}
|
|
||||||
else if (distance < aimMethodRange) {
|
|
||||||
taskString += ". Range is less than aim method range (" + to_string((int)round(aimMethodRange / 0.3048)) + "ft), using AIM method.";
|
|
||||||
|
|
||||||
/* If the unit is closer than the aim method range, use the aim method range */
|
|
||||||
string aimMethodTask = aimAtPoint(Coords(aimLat, aimLng, aimAlt));
|
|
||||||
taskString += aimMethodTask;
|
|
||||||
|
|
||||||
setTargetPosition(Coords(aimLat, aimLng, target->getPosition().alt));
|
|
||||||
nextTaskingMilliseconds = timeNow + static_cast<unsigned long>(2 * aimTime * 1000);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
taskString += ". Target is not in range of weapon, waking up unit to get ready for tasking.";
|
|
||||||
|
|
||||||
/* Else just wake the unit up with an impossible command */
|
|
||||||
std::ostringstream taskSS;
|
|
||||||
taskSS.precision(10);
|
|
||||||
taskSS << "{id = 'FireAtPoint', lat = " << 0 << ", lng = " << 0 << ", alt = " << 0 << ", radius = 0.001, expendQty = " << 0 << " }";
|
|
||||||
Command* command = dynamic_cast<Command*>(new SetTask(groupName, taskSS.str(), [this]() { this->setHasTaskAssigned(true); }));
|
|
||||||
scheduler->appendCommand(command);
|
|
||||||
shellsFiredAtTasking = totalShellsFired;
|
|
||||||
setHasTask(true);
|
|
||||||
setTargetPosition(Coords(NULL));
|
|
||||||
|
|
||||||
/* Don't wait too long before checking again */
|
|
||||||
nextTaskingMilliseconds = timeNow + static_cast<unsigned long>(5 * 1000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
missOnPurposeTarget = target;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
taskString += "Missing on purpose. No target in range.";
|
|
||||||
if (getHasTask())
|
if (getHasTask())
|
||||||
resetTask();
|
resetTask();
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
double distance = 0;
|
||||||
|
unsigned char unitCoalition = coalition == 0 ? getOperateAs() : coalition;
|
||||||
|
unsigned char targetCoalition = unitCoalition == 2 ? 1 : 2;
|
||||||
|
|
||||||
|
/* Get all the units in range and select one at random */
|
||||||
|
double range = max(max(engagementRange, aimMethodRange), acquisitionRange);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only do if we have a valid target close enough for AAA */
|
||||||
|
if (target != nullptr) {
|
||||||
|
taskString += "Missing on purpose. Valid target at range: " + to_string((int)round(distance)) + "m";
|
||||||
|
|
||||||
|
// Very simplified algorithm ignoring drag
|
||||||
|
double correctedAimTime = aimTime + distance / muzzleVelocity;
|
||||||
|
|
||||||
|
/* If the target is in targeting range and we are in highest precision mode, target it */
|
||||||
|
if (distance < targetingRange && shotsScatter == ShotsScatter::LOW) {
|
||||||
|
taskString += ". Range is less than targeting range (" + to_string((int)round(targetingRange)) + "m) and scatter is LOW, aiming at target.";
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
shellsFiredAtTasking = totalShellsFired;
|
||||||
|
setHasTask(true);
|
||||||
|
|
||||||
|
nextTaskingMilliseconds = timeNow + static_cast<unsigned long>(2 * aimTime * 1000);
|
||||||
|
}
|
||||||
|
/* Else, do miss on purpose */
|
||||||
|
else {
|
||||||
|
/* Compute where the target will be in aimTime seconds. */
|
||||||
|
double aimDistance = target->getHorizontalVelocity() * correctedAimTime;
|
||||||
|
double aimLat = 0;
|
||||||
|
double aimLng = 0;
|
||||||
|
Geodesic::WGS84().Direct(target->getPosition().lat, target->getPosition().lng, target->getTrack() * 57.29577, aimDistance, aimLat, aimLng); /* TODO make util to convert degrees and radians function */
|
||||||
|
double aimAlt = target->getPosition().alt + target->getVerticalVelocity();
|
||||||
|
|
||||||
|
if (flak) {
|
||||||
|
aimLat += RANDOM_MINUS_ONE_TO_ONE * (1 + (ShotsScatter::LOW - shotsScatter)) * 0.01;
|
||||||
|
aimLng += RANDOM_MINUS_ONE_TO_ONE * (1 + (ShotsScatter::LOW - shotsScatter)) * 0.01;
|
||||||
|
aimAlt += RANDOM_MINUS_ONE_TO_ONE * (1 + (ShotsScatter::LOW - shotsScatter)) * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send the command */
|
||||||
|
if (distance < engagementRange) {
|
||||||
|
taskString += ". Range is less than engagement range (" + to_string((int)round(engagementRange)) + "m), using FIRE AT POINT method";
|
||||||
|
|
||||||
|
/* If the unit is closer than the engagement range, use the fire at point method */
|
||||||
|
std::ostringstream taskSS;
|
||||||
|
taskSS.precision(10);
|
||||||
|
taskSS << "{id = 'FireAtPoint', lat = " << aimLat << ", lng = " << aimLng << ", alt = " << aimAlt << ", radius = 0.001 }";
|
||||||
|
|
||||||
|
taskString += ". Aiming altitude " + to_string((int)round((aimAlt - position.alt) / 0.3048)) + "ft AGL";
|
||||||
|
Command* command = dynamic_cast<Command*>(new SetTask(groupName, taskSS.str(), [this]() { this->setHasTaskAssigned(true); }));
|
||||||
|
scheduler->appendCommand(command);
|
||||||
|
shellsFiredAtTasking = totalShellsFired;
|
||||||
|
setHasTask(true);
|
||||||
|
setTargetPosition(Coords(aimLat, aimLng, target->getPosition().alt));
|
||||||
|
nextTaskingMilliseconds = timeNow + static_cast<unsigned long>(2 * aimTime * 1000);
|
||||||
|
}
|
||||||
|
else if (distance < aimMethodRange) {
|
||||||
|
taskString += ". Range is less than aim method range (" + to_string((int)round(aimMethodRange / 0.3048)) + "ft), using AIM method.";
|
||||||
|
|
||||||
|
/* If the unit is closer than the aim method range, use the aim method range */
|
||||||
|
string aimMethodTask = aimAtPoint(Coords(aimLat, aimLng, aimAlt));
|
||||||
|
taskString += aimMethodTask;
|
||||||
|
|
||||||
|
setTargetPosition(Coords(aimLat, aimLng, target->getPosition().alt));
|
||||||
|
nextTaskingMilliseconds = timeNow + static_cast<unsigned long>(2 * aimTime * 1000);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
taskString += ". Target is not in range of weapon, waking up unit to get ready for tasking.";
|
||||||
|
|
||||||
|
/* Else just wake the unit up with an impossible command */
|
||||||
|
std::ostringstream taskSS;
|
||||||
|
taskSS.precision(10);
|
||||||
|
taskSS << "{id = 'FireAtPoint', lat = " << 0 << ", lng = " << 0 << ", alt = " << 0 << ", radius = 0.001, expendQty = " << 0 << " }";
|
||||||
|
Command* command = dynamic_cast<Command*>(new SetTask(groupName, taskSS.str(), [this]() { this->setHasTaskAssigned(true); }));
|
||||||
|
scheduler->appendCommand(command);
|
||||||
|
shellsFiredAtTasking = totalShellsFired;
|
||||||
|
setHasTask(true);
|
||||||
|
setTargetPosition(Coords(NULL));
|
||||||
|
|
||||||
|
/* Don't wait too long before checking again */
|
||||||
|
nextTaskingMilliseconds = timeNow + static_cast<unsigned long>(5 * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
missOnPurposeTarget = target;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
taskString += "Missing on purpose. No target in range.";
|
||||||
|
setTargetPosition(Coords(NULL));
|
||||||
|
if (getHasTask())
|
||||||
|
resetTask();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* If no valid target was detected */
|
/* If no valid target was detected */
|
||||||
|
|||||||
@@ -8625,9 +8625,9 @@
|
|||||||
"abilities": "AA",
|
"abilities": "AA",
|
||||||
"canTargetPoint": false,
|
"canTargetPoint": false,
|
||||||
"canRearm": false,
|
"canRearm": false,
|
||||||
"muzzleVelocity": 700,
|
"muzzleVelocity": 600,
|
||||||
"aimTime": 50,
|
"aimTime": 50,
|
||||||
"shotsToFire": 1,
|
"shotsToFire": 10,
|
||||||
"barrelHeight": 5,
|
"barrelHeight": 5,
|
||||||
"cost": null,
|
"cost": null,
|
||||||
"markerFile": "groundunit-aaa",
|
"markerFile": "groundunit-aaa",
|
||||||
@@ -8635,7 +8635,7 @@
|
|||||||
"targetingRange": 100,
|
"targetingRange": 100,
|
||||||
"aimMethodRange": 100,
|
"aimMethodRange": 100,
|
||||||
"shotsBaseInterval": 5,
|
"shotsBaseInterval": 5,
|
||||||
"shotsBaseScatter": 5,
|
"shotsBaseScatter": 15,
|
||||||
"alertnessTimeConstant": 5,
|
"alertnessTimeConstant": 5,
|
||||||
"flak": true
|
"flak": true
|
||||||
},
|
},
|
||||||
@@ -9514,20 +9514,20 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"acquisitionRange": 10000,
|
"acquisitionRange": 10000,
|
||||||
"engagementRange": 3000,
|
"engagementRange": 9000,
|
||||||
"description": "ZSU-57-2. Tracked self propelled optically guided AA gun. 2 x 57 mm auto cannon.",
|
"description": "ZSU-57-2. Tracked self propelled optically guided AA gun. 2 x 57 mm auto cannon.",
|
||||||
"abilities": "Combined arms, AA",
|
"abilities": "Combined arms, AA",
|
||||||
"canTargetPoint": true,
|
"canTargetPoint": true,
|
||||||
"canRearm": false,
|
"canRearm": false,
|
||||||
"muzzleVelocity": 1200,
|
"muzzleVelocity": 1000,
|
||||||
"barrelHeight": 3,
|
"barrelHeight": 3,
|
||||||
"aimTime": 10,
|
"aimTime": 15,
|
||||||
"shotsToFire": 5,
|
"shotsToFire": 5,
|
||||||
"cost": null,
|
"cost": null,
|
||||||
"tags": "CA",
|
"tags": "CA",
|
||||||
"markerFile": "groundunit-aaa",
|
"markerFile": "groundunit-aaa",
|
||||||
"canAAA": true,
|
"canAAA": true,
|
||||||
"aimMethodRange": 9000,
|
"aimMethodRange": 100,
|
||||||
"targetingRange": 100,
|
"targetingRange": 100,
|
||||||
"shotsBaseInterval": 5,
|
"shotsBaseInterval": 5,
|
||||||
"shotsBaseScatter": 5,
|
"shotsBaseScatter": 5,
|
||||||
@@ -10636,14 +10636,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"aimTime": 50,
|
"aimTime": 50,
|
||||||
"shotsToFire": 1,
|
"shotsToFire": 5,
|
||||||
"acquisitionRange": 15000,
|
"acquisitionRange": 15000,
|
||||||
"engagementRange": 12000,
|
"engagementRange": 12000,
|
||||||
"description": "The flak 88. Fixed anti aircraft gun famously also used as an anti-tank gun. 88mm flak gun.",
|
"description": "The flak 88. Fixed anti aircraft gun famously also used as an anti-tank gun. 88mm flak gun.",
|
||||||
"abilities": "AA",
|
"abilities": "AA",
|
||||||
"canTargetPoint": true,
|
"canTargetPoint": true,
|
||||||
"canRearm": false,
|
"canRearm": false,
|
||||||
"muzzleVelocity": 700,
|
"muzzleVelocity": 880,
|
||||||
"barrelHeight": 2.1,
|
"barrelHeight": 2.1,
|
||||||
"cost": 40000,
|
"cost": 40000,
|
||||||
"markerFile": "groundunit-aaa",
|
"markerFile": "groundunit-aaa",
|
||||||
|
|||||||
Reference in New Issue
Block a user