From cf86c4ade9d0ee8d6963a28644720b846298fa2d Mon Sep 17 00:00:00 2001 From: Pax1601 Date: Tue, 18 Mar 2025 12:53:22 +0100 Subject: [PATCH] fix: minor improvements to miss on purpose mode --- backend/core/src/groundunit.cpp | 443 +++++++++++++----------- databases/units/groundunitdatabase.json | 18 +- 2 files changed, 242 insertions(+), 219 deletions(-) diff --git a/backend/core/src/groundunit.cpp b/backend/core/src/groundunit.cpp index ab0ccead..74d6272d 100644 --- a/backend/core/src/groundunit.cpp +++ b/backend/core/src/groundunit.cpp @@ -108,12 +108,14 @@ void GroundUnit::setState(unsigned char newState) /************ Perform any action required when ENTERING a state ************/ switch (newState) { case State::IDLE: { + setTask("Idle"); setEnableTaskCheckFailed(false); clearActivePath(); resetActiveDestination(); break; } case State::REACH_DESTINATION: { + setTask("Reaching destination"); setEnableTaskCheckFailed(true); resetActiveDestination(); break; @@ -125,6 +127,7 @@ void GroundUnit::setState(unsigned char newState) break; } case State::FIRE_AT_AREA: { + setTask("Firing at area"); setTargetPosition(currentTargetPosition); setEnableTaskCheckFailed(true); clearActivePath(); @@ -132,6 +135,7 @@ void GroundUnit::setState(unsigned char newState) break; } case State::SIMULATE_FIRE_FIGHT: { + setTask("Simulating fire fight"); setTargetPosition(currentTargetPosition); setEnableTaskCheckFailed(false); clearActivePath(); @@ -139,12 +143,14 @@ void GroundUnit::setState(unsigned char newState) break; } case State::SCENIC_AAA: { + setTask("Scenic AAA"); setEnableTaskCheckFailed(false); clearActivePath(); resetActiveDestination(); break; } case State::MISS_ON_PURPOSE: { + setTask("Miss on purpose"); setEnableTaskCheckFailed(false); clearActivePath(); resetActiveDestination(); @@ -156,6 +162,7 @@ void GroundUnit::setState(unsigned char newState) setHasTask(false); resetTaskFailedCounter(); + nextTaskingMilliseconds = 0; log(unitName + " setting state from " + to_string(state) + " to " + to_string(newState)); state = newState; @@ -183,14 +190,12 @@ void GroundUnit::AIloop() switch (state) { case State::IDLE: { - setTask("Idle"); if (getHasTask()) resetTask(); + break; } case State::REACH_DESTINATION: { - setTask("Reaching destination"); - string enrouteTask = ""; bool looping = false; @@ -237,8 +242,6 @@ void GroundUnit::AIloop() break; } case State::FIRE_AT_AREA: { - setTask("Firing at area"); - if (!getHasTask()) { std::ostringstream taskSS; taskSS.precision(10); @@ -258,55 +261,59 @@ void GroundUnit::AIloop() case State::SIMULATE_FIRE_FIGHT: { string taskString = ""; - if ( - (totalShellsFired - shellsFiredAtTasking >= shotsToFire || timeNow >= nextTaskingMilliseconds) && - targetPosition != Coords(NULL) && - scheduler->getLoad() < 30 - ) { - /* Get the distance and bearing to the target */ - 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 ((totalShellsFired - shellsFiredAtTasking >= shotsToFire || timeNow >= nextTaskingMilliseconds) && targetPosition != Coords(NULL)) { + if (scheduler->getLoad() > 100) { + taskString = "Excessive load, skipping tasking of unit"; + setTargetPosition(Coords(NULL)); + if (getHasTask()) + resetTask(); } - - /* 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(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; - } + /* Get the distance and bearing to the target */ + Coords scatteredTargetPosition = targetPosition; + double distance; + 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 */ - nextTaskingMilliseconds = timeNow + static_cast(2 * aimTime * 1000); + /* 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(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(2 * aimTime * 1000); + } } if (targetPosition == Coords(NULL)) @@ -327,63 +334,71 @@ void GroundUnit::AIloop() string taskString = ""; /* Only perform scenic functions when the scheduler is "free" */ - if ((totalShellsFired - shellsFiredAtTasking >= shotsToFire || timeNow >= nextTaskingMilliseconds) && - scheduler->getLoad() < 30) { - double distance = 0; - unsigned char unitCoalition = coalition == 0 ? getOperateAs() : coalition; - unsigned char targetCoalition = unitCoalition == 2 ? 1 : 2; - 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(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(2 * aimTime * 1000); + if (totalShellsFired - shellsFiredAtTasking >= shotsToFire || timeNow >= nextTaskingMilliseconds) { + if (scheduler->getLoad() > 100) { + taskString = "Excessive load, skipping tasking of unit"; + setTargetPosition(Coords(NULL)); + if (getHasTask()) + resetTask(); } else { - if (target == nullptr) - taskString += "Scenic AAA. No valid target."; - else - taskString += "Scenic AAA. Target outside max range: " + to_string((int)round(distance)) + "m."; + double distance = 0; + unsigned char unitCoalition = coalition == 0 ? getOperateAs() : coalition; + unsigned char targetCoalition = unitCoalition == 2 ? 1 : 2; + 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(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(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) { /* 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 */ - if ((totalShellsFired - shellsFiredAtTasking >= shotsToFire || timeNow >= nextTaskingMilliseconds) && - scheduler->getLoad() < 30) { - 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 targets = unitsManager->getUnitsInRange(this, targetCoalition, { "Aircraft", "Helicopter" }, range); - - Unit* target = nullptr; - unsigned int index = static_cast((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(new SetTask(groupName, taskSS.str(), [this]() { this->setHasTaskAssigned(true); })); - scheduler->appendCommand(command); - shellsFiredAtTasking = totalShellsFired; - setHasTask(true); - - nextTaskingMilliseconds = timeNow + static_cast(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(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(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(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(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(5 * 1000); - } - } - missOnPurposeTarget = target; - } - else { - taskString += "Missing on purpose. No target in range."; + if (totalShellsFired - shellsFiredAtTasking >= shotsToFire || timeNow >= nextTaskingMilliseconds) { + if (scheduler->getLoad() > 100) { + taskString = "Excessive load, skipping tasking of unit"; + setTargetPosition(Coords(NULL)); if (getHasTask()) 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 targets = unitsManager->getUnitsInRange(this, targetCoalition, { "Aircraft", "Helicopter" }, range); + + Unit* target = nullptr; + unsigned int index = static_cast((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(new SetTask(groupName, taskSS.str(), [this]() { this->setHasTaskAssigned(true); })); + scheduler->appendCommand(command); + shellsFiredAtTasking = totalShellsFired; + setHasTask(true); + + nextTaskingMilliseconds = timeNow + static_cast(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(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(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(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(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(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 */ diff --git a/databases/units/groundunitdatabase.json b/databases/units/groundunitdatabase.json index 90e54b8b..23a6a6c1 100644 --- a/databases/units/groundunitdatabase.json +++ b/databases/units/groundunitdatabase.json @@ -8625,9 +8625,9 @@ "abilities": "AA", "canTargetPoint": false, "canRearm": false, - "muzzleVelocity": 700, + "muzzleVelocity": 600, "aimTime": 50, - "shotsToFire": 1, + "shotsToFire": 10, "barrelHeight": 5, "cost": null, "markerFile": "groundunit-aaa", @@ -8635,7 +8635,7 @@ "targetingRange": 100, "aimMethodRange": 100, "shotsBaseInterval": 5, - "shotsBaseScatter": 5, + "shotsBaseScatter": 15, "alertnessTimeConstant": 5, "flak": true }, @@ -9514,20 +9514,20 @@ } }, "acquisitionRange": 10000, - "engagementRange": 3000, + "engagementRange": 9000, "description": "ZSU-57-2. Tracked self propelled optically guided AA gun. 2 x 57 mm auto cannon.", "abilities": "Combined arms, AA", "canTargetPoint": true, "canRearm": false, - "muzzleVelocity": 1200, + "muzzleVelocity": 1000, "barrelHeight": 3, - "aimTime": 10, + "aimTime": 15, "shotsToFire": 5, "cost": null, "tags": "CA", "markerFile": "groundunit-aaa", "canAAA": true, - "aimMethodRange": 9000, + "aimMethodRange": 100, "targetingRange": 100, "shotsBaseInterval": 5, "shotsBaseScatter": 5, @@ -10636,14 +10636,14 @@ } }, "aimTime": 50, - "shotsToFire": 1, + "shotsToFire": 5, "acquisitionRange": 15000, "engagementRange": 12000, "description": "The flak 88. Fixed anti aircraft gun famously also used as an anti-tank gun. 88mm flak gun.", "abilities": "AA", "canTargetPoint": true, "canRearm": false, - "muzzleVelocity": 700, + "muzzleVelocity": 880, "barrelHeight": 2.1, "cost": 40000, "markerFile": "groundunit-aaa",