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

@ -83,6 +83,7 @@ declare module "controls/switch" {
}
declare module "constants/constants" {
import { LatLng, LatLngBounds } from "leaflet";
import { MapMarkerControl } from "map/map";
export const UNITS_URI = "units";
export const WEAPONS_URI = "weapons";
export const LOGS_URI = "logs";
@ -194,6 +195,7 @@ declare module "constants/constants" {
export const visibilityControls: string[];
export const visibilityControlsTypes: string[][];
export const visibilityControlsTooltips: string[];
export const MAP_MARKER_CONTROLS: MapMarkerControl[];
export const IADSTypes: string[];
export const IADSDensities: {
[key: string]: number;
@ -577,6 +579,8 @@ declare module "interfaces" {
acquisitionRange?: number;
engagementRange?: number;
targetingRange?: number;
aimMethodRange?: number;
alertnessTimeConstant?: number;
canTargetPoint?: boolean;
canRearm?: boolean;
canAAA?: boolean;
@ -621,6 +625,7 @@ declare module "interfaces" {
export interface ShortcutOptions {
altKey?: boolean;
callback: CallableFunction;
context?: string;
ctrlKey?: boolean;
name?: string;
shiftKey?: boolean;
@ -1121,6 +1126,7 @@ declare module "unit/unit" {
isInViewport(): boolean;
canTargetPoint(): boolean;
canRearm(): boolean;
canLandAtPoint(): boolean;
canAAA(): boolean;
indirectFire(): boolean;
isTanker(): boolean;
@ -1359,6 +1365,13 @@ declare module "contextmenus/airbasespawnmenu" {
setAirbase(airbase: Airbase): void;
}
}
declare module "context/context" {
export interface ContextInterface {
}
export class Context {
constructor(config: ContextInterface);
}
}
declare module "other/manager" {
export class Manager {
#private;
@ -1434,6 +1447,14 @@ declare module "map/map" {
import { CoalitionArea } from "map/coalitionarea/coalitionarea";
import { CoalitionAreaContextMenu } from "contextmenus/coalitionareacontextmenu";
import { AirbaseSpawnContextMenu } from "contextmenus/airbasespawnmenu";
export type MapMarkerControl = {
"image": string;
"isProtected"?: boolean;
"name": string;
"protectable"?: boolean;
"toggles": string[];
"tooltip": string;
};
export class Map extends L.Map {
#private;
/**
@ -1481,6 +1502,7 @@ declare module "map/map" {
getVisibilityOptions(): {
[key: string]: boolean;
};
unitIsProtected(unit: Unit): boolean;
}
}
declare module "mission/bullseye" {
@ -1749,7 +1771,9 @@ declare module "unit/unitsmanager" {
*/
getSelectedUnits(options?: {
excludeHumans?: boolean;
excludeProtected?: boolean;
onlyOnePerGroup?: boolean;
showProtectionReminder?: boolean;
}): Unit[];
/** Deselects all currently selected units
*
@ -2142,6 +2166,18 @@ declare module "panels/unitlistpanel" {
toggle(): void;
}
}
declare module "context/contextmanager" {
import { Manager } from "other/manager";
import { ContextInterface } from "context/context";
export class ContextManager extends Manager {
#private;
constructor();
add(name: string, contextConfig: ContextInterface): this;
currentContextIs(contextName: string): boolean;
getCurrentContext(): any;
setContext(contextName: string): false | undefined;
}
}
declare module "olympusapp" {
import { Map } from "map/map";
import { MissionManager } from "mission/missionmanager";
@ -2151,11 +2187,15 @@ declare module "olympusapp" {
import { WeaponsManager } from "weapon/weaponsmanager";
import { Manager } from "other/manager";
import { ServerManager } from "server/servermanager";
import { ContextManager } from "context/contextmanager";
import { Context } from "context/context";
export class OlympusApp {
#private;
constructor();
getDialogManager(): Manager;
getMap(): Map;
getCurrentContext(): Context;
getContextManager(): ContextManager;
getServerManager(): ServerManager;
getPanelsManager(): Manager;
getPopupsManager(): Manager;
@ -2207,3 +2247,11 @@ declare module "index" {
import { OlympusApp } from "olympusapp";
export function getApp(): OlympusApp;
}
declare module "context/contextmenumanager" {
import { ContextMenu } from "contextmenus/contextmenu";
import { Manager } from "other/manager";
export class ContextMenuManager extends Manager {
constructor();
add(name: string, contextMenu: ContextMenu): this;
}
}

View File

@ -37,12 +37,14 @@ export class GroundUnitEditor extends UnitEditor {
addStringInput(this.contentDiv2, "Acquisition range [m]", String(blueprint.acquisitionRange)?? "", "number", (value: string) => {blueprint.acquisitionRange = parseFloat(value); });
addStringInput(this.contentDiv2, "Engagement range [m]", String(blueprint.engagementRange)?? "", "number", (value: string) => {blueprint.engagementRange = parseFloat(value); });
addStringInput(this.contentDiv2, "Targeting range [m]", String(blueprint.targetingRange)?? "", "number", (value: string) => {blueprint.targetingRange = parseFloat(value); });
addStringInput(this.contentDiv2, "Aim method range [m]", String(blueprint.aimMethodRange)?? "", "number", (value: string) => {blueprint.aimMethodRange = parseFloat(value); });
addStringInput(this.contentDiv2, "Barrel height [m]", String(blueprint.barrelHeight)?? "", "number", (value: string) => {blueprint.barrelHeight = parseFloat(value); });
addStringInput(this.contentDiv2, "Muzzle velocity [m/s]", String(blueprint.muzzleVelocity)?? "", "number", (value: string) => {blueprint.muzzleVelocity = parseFloat(value); });
addStringInput(this.contentDiv2, "Aim time [s]", String(blueprint.aimTime)?? "", "number", (value: string) => {blueprint.aimTime = parseFloat(value); });
addStringInput(this.contentDiv2, "Burst quantity", String(blueprint.shotsToFire)?? "", "number", (value: string) => {blueprint.shotsToFire = Math.round(parseFloat(value)); });
addStringInput(this.contentDiv2, "Burst base interval [s]", String(blueprint.shotsBaseInterval)?? "", "number", (value: string) => {blueprint.shotsBaseInterval = Math.round(parseFloat(value)); });
addStringInput(this.contentDiv2, "Base scatter [°]", String(blueprint.shotsBaseScatter)?? "", "number", (value: string) => {blueprint.shotsBaseScatter = Math.round(parseFloat(value)); });
addStringInput(this.contentDiv2, "Shots to fire", String(blueprint.shotsToFire)?? "", "number", (value: string) => {blueprint.shotsToFire = Math.round(parseFloat(value)); });
addStringInput(this.contentDiv2, "Shots base interval [s]", String(blueprint.shotsBaseInterval)?? "", "number", (value: string) => {blueprint.shotsBaseInterval = Math.round(parseFloat(value)); });
addStringInput(this.contentDiv2, "Shots base scatter [°]", String(blueprint.shotsBaseScatter)?? "", "number", (value: string) => {blueprint.shotsBaseScatter = Math.round(parseFloat(value)); });
addStringInput(this.contentDiv2, "Alertness time constant [s]", String(blueprint.alertnessTimeConstant)?? "", "number", (value: string) => {blueprint.alertnessTimeConstant = Math.round(parseFloat(value)); });
addCheckboxInput(this.contentDiv2, "Can target point", blueprint.canTargetPoint ?? false, (value: boolean) => {blueprint.canTargetPoint = value;})
addCheckboxInput(this.contentDiv2, "Can rearm", blueprint.canRearm ?? false, (value: boolean) => {blueprint.canRearm = value;})
addCheckboxInput(this.contentDiv2, "Can operate as AAA", blueprint.canAAA ?? false, (value: boolean) => {blueprint.canAAA = value;})

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -221,6 +221,8 @@ export interface UnitBlueprint {
acquisitionRange?: number;
engagementRange?: number;
targetingRange?: number;
aimMethodRange?: number;
alertnessTimeConstant?: number;
canTargetPoint?: boolean;
canRearm?: boolean;
canAAA?: boolean;

View File

@ -653,6 +653,10 @@ export class Unit extends CustomMarker {
return this.getDatabase()?.getByName(this.#name)?.canRearm === true;
}
canLandAtPoint() {
return this.getCategory() === "Helicopter";
}
canAAA() {
return this.getDatabase()?.getByName(this.#name)?.canAAA === true;
}

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) {