Basic scenic and miss on purpose AAA modes

This commit is contained in:
Pax1601 2023-10-01 19:15:56 +02:00
parent d209f98265
commit 5035c408e7
10 changed files with 226 additions and 6 deletions

View File

@ -329,6 +329,18 @@ export class ServerManager {
this.PUT(data, callback);
}
scenicAAA(ID: number, callback: CallableFunction = () => {}) {
var command = { "ID": ID }
var data = { "scenicAAA": command }
this.PUT(data, callback);
}
missOnPurpose(ID: number, callback: CallableFunction = () => {}) {
var command = { "ID": ID }
var data = { "missOnPurpose": command }
this.PUT(data, callback);
}
setAdvacedOptions(ID: number, isTanker: boolean, isAWACS: boolean, TACAN: TACAN, radio: Radio, generalSettings: GeneralSettings, callback: CallableFunction = () => {}) {
var command = {
"ID": ID,

View File

@ -739,6 +739,15 @@ export class Unit extends CustomMarker {
});
}
scenicAAA() {
getApp().getServerManager().scenicAAA(this.ID);
}
missOnPurpose() {
getApp().getServerManager().missOnPurpose(this.ID);
}
/***********************************************/
onAdd(map: Map): this {
super.onAdd(map);
@ -802,7 +811,7 @@ export class Unit extends CustomMarker {
if (selectedUnits.length > 0 && !(selectedUnits.length == 1 && (selectedUnits.includes(this)))) {
options["attack"] = { text: "Attack", tooltip: "Attack the unit using A/A or A/G weapons" };
if (getApp().getUnitsManager().getSelectedUnitsCategories().length == 1 && getApp().getUnitsManager().getSelectedUnitsCategories()[0] === "Aircraft")
options["follow"] = { text: "Follow", tooltip: "Follow the unit at a user defined distance and position" };;
options["follow"] = { text: "Follow", tooltip: "Follow the unit at a user defined distance and position" };
}
else if ((selectedUnits.length > 0 && (selectedUnits.includes(this))) || selectedUnits.length == 0) {
if (this.getCategory() == "Aircraft") {
@ -813,6 +822,11 @@ export class Unit extends CustomMarker {
if (selectedUnitTypes.length === 1 && ["NavyUnit", "GroundUnit"].includes(selectedUnitTypes[0]) && getApp().getUnitsManager().getSelectedUnitsVariable((unit: Unit) => {return unit.getCoalition()}) !== undefined)
options["group"] = { text: "Create group", tooltip: "Create a group from the selected units." };
if (selectedUnitTypes.length === 1 && ["GroundUnit"].includes(selectedUnitTypes[0]) && selectedUnits.every((unit: Unit) => { return ["AAA"].includes(unit.getType())})) {
options["scenic-aaa"] = { text: "Scenic AAA", tooltip: "Shoot AAA in the air without aiming at any target" };
options["miss-aaa"] = { text: "Miss on purpose AAA", tooltip: "Shoot AAA towards the closes enemy, but don't aim precisely" };
}
if (Object.keys(options).length > 0) {
getApp().getMap().showUnitContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng);
getApp().getMap().getUnitContextMenu().setOptions(options, (option: string) => {
@ -831,6 +845,10 @@ export class Unit extends CustomMarker {
getApp().getUnitsManager().selectedUnitsRefuel();
else if (action === "group")
getApp().getUnitsManager().selectedUnitsCreateGroup();
else if (action === "scenic-aaa")
getApp().getUnitsManager().selectedUnitsScenicAAA();
else if (action === "miss-aaa")
getApp().getUnitsManager().selectedUnitsMissOnPurpose();
else if (action === "follow")
this.#showFollowOptions(e);
}

View File

@ -625,6 +625,26 @@ export class UnitsManager {
});
this.#showActionMessage(selectedUnits, `unit simulating fire fight`);
}
/** Instruct units to enter into scenic AAA mode. Units will shoot in the air without aiming
*
*/
selectedUnitsScenicAAA() {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
for (let idx in selectedUnits) {
selectedUnits[idx].scenicAAA();
}
}
/** Instruct units to enter into miss on purpose mode. Units will aim to the nearest enemy unit but not precisely.
*
*/
selectedUnitsMissOnPurpose() {
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
for (let idx in selectedUnits) {
selectedUnits[idx].missOnPurpose();
}
}
/*********************** Control operations on selected units ************************/
/** See getUnitsCategories for more info

View File

@ -1,6 +1,6 @@
local version = "v0.4.4-alpha"
local debug = true -- True enables debug printing using DCS messages
local debug = false -- True enables debug printing using DCS messages
-- .dll related variables
Olympus.OlympusDLL = nil

View File

@ -65,7 +65,9 @@ namespace State
CARPET_BOMB,
BOMB_BUILDING,
FIRE_AT_AREA,
SIMULATE_FIRE_FIGHT
SIMULATE_FIRE_FIGHT,
SCENIC_AAA,
MISS_ON_PURPOSE
};
};

View File

@ -186,6 +186,7 @@ protected:
/********** Other **********/
unsigned int taskCheckCounter = 0;
unsigned int internalCounter = 0;
bool hasTaskAssigned = false;
double initialFuel = 0;
map<unsigned char, unsigned long long> updateTimeMap;

View File

@ -23,7 +23,8 @@ public:
void deleteUnit(unsigned int ID, bool explosion, bool immediate);
void acquireControl(unsigned int ID);
void loadDatabases();
Unit* getClosestUnit(Unit* unit, unsigned char coalition, vector<string> categories, double &distance);
private:
map<unsigned int, Unit*> units;
json::value missionDB;

View File

@ -73,6 +73,12 @@ void GroundUnit::setState(unsigned char newState)
setTargetPosition(Coords(NULL));
break;
}
case State::SCENIC_AAA: {
break;
}
case State::MISS_ON_PURPOSE: {
break;
}
default:
break;
}
@ -99,6 +105,16 @@ void GroundUnit::setState(unsigned char newState)
resetActiveDestination();
break;
}
case State::SCENIC_AAA: {
clearActivePath();
resetActiveDestination();
break;
}
case State::MISS_ON_PURPOSE: {
clearActivePath();
resetActiveDestination();
break;
}
default:
break;
}
@ -156,11 +172,13 @@ void GroundUnit::AIloop()
scheduler->appendCommand(command);
setHasTask(true);
}
break;
}
case State::SIMULATE_FIRE_FIGHT: {
setTask("Simulating fire fight");
if (!getHasTask() || (((double)(rand()) / (double)(RAND_MAX)) < 0.002)) {
if (!getHasTask() || internalCounter == 0) {
double dist;
double bearing1;
double bearing2;
@ -175,7 +193,6 @@ void GroundUnit::AIloop()
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();
log(to_string(barrelHeight) + " " + to_string(muzzleVelocity));
}
}
@ -193,6 +210,93 @@ void GroundUnit::AIloop()
scheduler->appendCommand(command);
setHasTask(true);
}
if (internalCounter == 0)
internalCounter = 20 * scheduler->getFrameRate(); /* 20 seconds */
internalCounter--;
break;
}
case State::SCENIC_AAA: {
setTask("Scenic AAA");
if (!getHasTask() || internalCounter == 0) {
double r = 15; /* m */
double barrelElevation = 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);
std::ostringstream taskSS;
taskSS.precision(10);
taskSS << "{id = 'FireAtPoint', lat = " << lat << ", lng = " << lng << ", alt = " << position.alt + barrelElevation << ", radius = 0.001}";
Command* command = dynamic_cast<Command*>(new SetTask(groupName, taskSS.str(), [this]() { this->setHasTaskAssigned(true); }));
scheduler->appendCommand(command);
setHasTask(true);
}
if (internalCounter == 0)
internalCounter = 20 * scheduler->getFrameRate(); /* 20 seconds */
internalCounter--;
break;
}
case State::MISS_ON_PURPOSE: {
setTask("Missing on purpose");
/* Only run this when the internal counter reaches 0 to avoid excessive computations when no nearby target */
if (internalCounter == 0) {
double distance = 0;
Unit* target = unitsManager->getClosestUnit(this, 1, { "Aircraft", "Helicopter" }, distance); /* Red, TODO make assignable */
/* Only do if we have a valid target close enough for AAA */
if (target != nullptr && distance < 10000 /* m */) {
double aimTime = 10; /* s TODO: add to database */
/* Compute where the target will be in aimTime seconds. We don't consider vertical velocity atm, since after all we are not really tring to hit */
double aimDistance = target->getSpeed() * aimTime;
double aimLat = 0;
double aimLng = 0;
Geodesic::WGS84().Direct(target->getPosition().lat, target->getPosition().lng, target->getHeading(), aimDistance, aimLat, aimLng);
/* Compute distance to the aim point */
double dist;
double bearing1;
double bearing2;
Geodesic::WGS84().Inverse(position.lat, position.lng, aimLat, aimLng, dist, bearing1, bearing2);
/* Default gun values */
double barrelHeight = 1.0; /* m */
double muzzleVelocity = 860; /* m/s */
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();
}
}
/* Send the command */
std::ostringstream taskSS;
taskSS.precision(10);
taskSS << "{id = 'FireAtPoint', lat = " << aimLat << ", lng = " << aimLng << ", alt = " << target->getPosition().alt + 0.5 * 9.81 * (dist * dist) / (muzzleVelocity * muzzleVelocity) << ", radius = 0.001}";
Command* command = dynamic_cast<Command*>(new SetTask(groupName, taskSS.str(), [this]() { this->setHasTaskAssigned(true); }));
scheduler->appendCommand(command);
setHasTask(true);
}
else {
if (getHasTask())
resetTask();
}
}
if (internalCounter == 0)
internalCounter = 5 * scheduler->getFrameRate();
internalCounter--;
break;
}
default:
break;

View File

@ -390,6 +390,7 @@ void Scheduler::handleRequest(string key, json::value value, string username, js
Coords location; location.lat = lat; location.lng = lng;
cloneOptions.push_back({ ID, location });
log(username + " cloning unit with ID " + to_string(ID), true);
}
command = dynamic_cast<Command*>(new Clone(cloneOptions, deleteOriginal));
@ -575,6 +576,22 @@ void Scheduler::handleRequest(string key, json::value value, string username, js
unit->setTargetPosition(loc);
log(username + " tasked unit " + unit->getName() + " to simulate a fire fight", true);
}
else if (key.compare("scenicAAA") == 0)
{
unsigned int ID = value[L"ID"].as_integer();
unitsManager->acquireControl(ID);
Unit* unit = unitsManager->getGroupLeader(ID);
unit->setState(State::SCENIC_AAA);
log(username + " tasked unit " + unit->getName() + " to enter scenic AAA state", true);
}
else if (key.compare("missOnPurpose") == 0)
{
unsigned int ID = value[L"ID"].as_integer();
unitsManager->acquireControl(ID);
Unit* unit = unitsManager->getGroupLeader(ID);
unit->setState(State::MISS_ON_PURPOSE);
log(username + " tasked unit " + unit->getName() + " to enter Miss On Purpose state", true);
}
else if (key.compare("setCommandModeOptions") == 0) {
setCommandModeOptions(value);
log(username + " updated the Command Mode Options", true);

View File

@ -11,6 +11,9 @@
#include "scheduler.h"
#include "defines.h"
#include <GeographicLib/Geodesic.hpp>
using namespace GeographicLib;
#include "base64.hpp"
using namespace base64;
@ -148,6 +151,48 @@ void UnitsManager::deleteUnit(unsigned int ID, bool explosion, bool immediate)
}
}
Unit* UnitsManager::getClosestUnit(Unit* unit, unsigned char coalition, vector<string> categories, double &distance) {
Unit* closestUnit = nullptr;
distance = 0;
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 the closest unit has not been assigned yet, assign it to this unit */
if (closestUnit == nullptr)
{
closestUnit = p.second;
distance = dist;
}
else {
/* Check if the unit is closer than the one already selected */
if (dist < distance) {
closestUnit = p.second;
distance = dist;
}
}
}
}
return closestUnit;
}
void UnitsManager::acquireControl(unsigned int ID) {
Unit* leader = getGroupLeader(ID);
if (leader != nullptr) {