Added effect of vertical velocity and of scatter and intensity

This commit is contained in:
Pax1601 2023-10-30 15:56:10 +01:00
parent c0f3f3a40a
commit 11a5fec195
11 changed files with 116 additions and 36 deletions

View File

@ -189,6 +189,8 @@ export enum DataIndexes {
hasTask,
position,
speed,
horizontalVelocity,
verticalVelocity,
heading,
isActiveTanker,
isActiveAWACS,

View File

@ -152,6 +152,8 @@ export interface UnitData {
hasTask: boolean;
position: LatLng;
speed: number;
horizontalVelocity: number;
verticalVelocity: number;
heading: number;
isActiveTanker: boolean;
isActiveAWACS: boolean;

View File

@ -34,6 +34,8 @@ export class Unit extends CustomMarker {
#hasTask: boolean = false;
#position: LatLng = new LatLng(0, 0, 0);
#speed: number = 0;
#horizontalVelocity: number = 0;
#verticalVelocity: number = 0;
#heading: number = 0;
#isActiveTanker: boolean = false;
#isActiveAWACS: boolean = false;
@ -111,6 +113,8 @@ export class Unit extends CustomMarker {
getHasTask() { return this.#hasTask };
getPosition() { return this.#position };
getSpeed() { return this.#speed };
getHorizontalVelocity() { return this.#horizontalVelocity };
getVerticalVelocity() { return this.#verticalVelocity };
getHeading() { return this.#heading };
getIsActiveTanker() { return this.#isActiveTanker };
getIsActiveAWACS() { return this.#isActiveAWACS };
@ -224,6 +228,8 @@ export class Unit extends CustomMarker {
case DataIndexes.hasTask: this.#hasTask = dataExtractor.extractBool(); break;
case DataIndexes.position: this.#position = dataExtractor.extractLatLng(); updateMarker = true; break;
case DataIndexes.speed: this.#speed = dataExtractor.extractFloat64(); updateMarker = true; break;
case DataIndexes.horizontalVelocity: this.#horizontalVelocity = dataExtractor.extractFloat64(); break;
case DataIndexes.verticalVelocity: this.#verticalVelocity = dataExtractor.extractFloat64(); break;
case DataIndexes.heading: this.#heading = dataExtractor.extractFloat64(); updateMarker = true; break;
case DataIndexes.isActiveTanker: this.#isActiveTanker = dataExtractor.extractBool(); break;
case DataIndexes.isActiveAWACS: this.#isActiveAWACS = dataExtractor.extractBool(); break;
@ -290,6 +296,8 @@ export class Unit extends CustomMarker {
hasTask: this.#hasTask,
position: this.#position,
speed: this.#speed,
horizontalVelocity: this.#horizontalVelocity,
verticalVelocity: this.#verticalVelocity,
heading: this.#heading,
isActiveTanker: this.#isActiveTanker,
isActiveAWACS: this.#isActiveAWACS,

View File

@ -888,6 +888,7 @@ function Olympus.setUnitsData(arg, time)
local lat, lng, alt = coord.LOtoLL(unit:getPoint())
local position = unit:getPosition()
local heading = math.atan2( position.x.z, position.x.x )
local velocity = unit:getVelocity();
-- Fill the data table
table["name"] = unit:getTypeName()
@ -896,7 +897,9 @@ function Olympus.setUnitsData(arg, time)
table["position"]["lat"] = lat
table["position"]["lng"] = lng
table["position"]["alt"] = alt
table["speed"] = mist.vec.mag(unit:getVelocity())
table["speed"] = mist.vec.mag(velocity)
table["horizontalVelocity"] = math.sqrt(velocity.x * velocity.x + velocity.z * velocity.z)
table["verticalVelocity"] = velocity.y
table["heading"] = heading
table["isAlive"] = unit:isExist() and unit:isActive() and unit:getLife() >= 1

View File

@ -19,6 +19,8 @@ namespace DataIndex {
hasTask,
position,
speed,
horizontalVelocity,
verticalVelocity,
heading,
isActiveTanker,
isActiveAWACS,

View File

@ -55,6 +55,8 @@ public:
bool checkTaskFailed();
void resetTaskFailedCounter();
void setHasTaskAssigned(bool newHasTaskAssigned);
void setEnableTaskCheckFailed(bool newEnableTaskCheckFailed) { enableTaskFailedCheck = newEnableTaskCheckFailed; }
bool getEnableTaskCheckFailed() { return enableTaskFailedCheck; }
void triggerUpdate(unsigned char datumIndex);
@ -73,9 +75,11 @@ public:
virtual void setGroupName(string newValue) { updateValue(groupName, newValue, DataIndex::groupName); }
virtual void setState(unsigned char newValue) { updateValue(state, newValue, DataIndex::state); };
virtual void setTask(string newValue) { updateValue(task, newValue, DataIndex::task); }
virtual void setHasTask(bool newValue) { updateValue(hasTask, newValue, DataIndex::hasTask); }
virtual void setHasTask(bool newValue);
virtual void setPosition(Coords newValue) { updateValue(position, newValue, DataIndex::position); }
virtual void setSpeed(double newValue) { updateValue(speed, newValue, DataIndex::speed); }
virtual void setHorizontalVelocity(double newValue) { updateValue(horizontalVelocity, newValue, DataIndex::horizontalVelocity); }
virtual void setVerticalVelocity(double newValue) { updateValue(verticalVelocity, newValue, DataIndex::verticalVelocity); }
virtual void setHeading(double newValue) { updateValue(heading, newValue, DataIndex::heading); }
virtual void setIsActiveTanker(bool newValue);
virtual void setIsActiveAWACS(bool newValue);
@ -119,6 +123,8 @@ public:
virtual bool getHasTask() { return hasTask; }
virtual Coords getPosition() { return position; }
virtual double getSpeed() { return speed; }
virtual double getHorizontalVelocity() { return horizontalVelocity; }
virtual double getVerticalVelocity() { return verticalVelocity; }
virtual double getHeading() { return heading; }
virtual bool getIsActiveTanker() { return isActiveTanker; }
virtual bool getIsActiveAWACS() { return isActiveAWACS; }
@ -164,6 +170,8 @@ protected:
bool hasTask = false;
Coords position = Coords(NULL);
double speed = NULL;
double horizontalVelocity = NULL;
double verticalVelocity = NULL;
double heading = NULL;
bool isActiveTanker = false;
bool isActiveAWACS = false;
@ -200,6 +208,7 @@ protected:
double initialFuel = 0;
map<unsigned char, unsigned long long> updateTimeMap;
unsigned long long lastLoopTime = 0;
bool enableTaskFailedCheck = false;
/********** Private methods **********/
virtual void AIloop() = 0;

View File

@ -80,15 +80,18 @@ void AirUnit::setState(unsigned char newState)
/************ Perform any action required when ENTERING a state ************/
switch (newState) {
case State::IDLE: {
setEnableTaskCheckFailed(false);
clearActivePath();
resetActiveDestination();
break;
}
case State::REACH_DESTINATION: {
setEnableTaskCheckFailed(true);
resetActiveDestination();
break;
}
case State::ATTACK: {
setEnableTaskCheckFailed(true);
if (isTargetAlive()) {
Unit* target = unitsManager->getUnit(targetID);
Coords targetPosition = Coords(target->getPosition().lat, target->getPosition().lng, 0);
@ -99,36 +102,43 @@ void AirUnit::setState(unsigned char newState)
break;
}
case State::FOLLOW: {
setEnableTaskCheckFailed(true);
clearActivePath();
resetActiveDestination();
break;
}
case State::LAND: {
setEnableTaskCheckFailed(false);
resetActiveDestination();
break;
}
case State::REFUEL: {
setEnableTaskCheckFailed(true);
initialFuel = fuel;
clearActivePath();
resetActiveDestination();
break;
}
case State::BOMB_POINT: {
setEnableTaskCheckFailed(true);
clearActivePath();
resetActiveDestination();
break;
}
case State::CARPET_BOMB: {
setEnableTaskCheckFailed(true);
clearActivePath();
resetActiveDestination();
break;
}
case State::BOMB_BUILDING: {
setEnableTaskCheckFailed(true);
clearActivePath();
resetActiveDestination();
break;
}
case State::LAND_AT_POINT: {
setEnableTaskCheckFailed(true);
resetActiveDestination();
break;
}

View File

@ -13,6 +13,9 @@ extern Scheduler* scheduler;
extern UnitsManager* unitsManager;
json::value GroundUnit::database = json::value();
#define RANDOM_ZERO_TO_ONE (double)(rand()) / (double)(RAND_MAX)
#define RANDOM_MINUS_ONE_TO_ONE (((double)(rand()) / (double)(RAND_MAX) - 0.5) * 2)
void GroundUnit::loadDatabase(string path) {
char* buf = nullptr;
size_t sz = 0;
@ -89,30 +92,36 @@ void GroundUnit::setState(unsigned char newState)
/************ Perform any action required when ENTERING a state ************/
switch (newState) {
case State::IDLE: {
setEnableTaskCheckFailed(false);
clearActivePath();
resetActiveDestination();
break;
}
case State::REACH_DESTINATION: {
setEnableTaskCheckFailed(true);
resetActiveDestination();
break;
}
case State::FIRE_AT_AREA: {
setEnableTaskCheckFailed(true);
clearActivePath();
resetActiveDestination();
break;
}
case State::SIMULATE_FIRE_FIGHT: {
setEnableTaskCheckFailed(true);
clearActivePath();
resetActiveDestination();
break;
}
case State::SCENIC_AAA: {
setEnableTaskCheckFailed(false);
clearActivePath();
resetActiveDestination();
break;
}
case State::MISS_ON_PURPOSE: {
setEnableTaskCheckFailed(false);
clearActivePath();
resetActiveDestination();
break;
@ -214,7 +223,7 @@ void GroundUnit::AIloop()
}
if (internalCounter == 0)
internalCounter = 20 / 0.05; // TODO make defined constant
internalCounter = 20 / FRAMERATE_TIME_INTERVAL;
internalCounter--;
break;
@ -247,7 +256,7 @@ void GroundUnit::AIloop()
}
if (internalCounter == 0)
internalCounter = 20 / 0.05;
internalCounter = 20 / FRAMERATE_TIME_INTERVAL;
internalCounter--;
break;
@ -261,48 +270,58 @@ void GroundUnit::AIloop()
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 = 5; /* degs */
double engagementRange = 10000; /* 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();
}
/* Only do if we have a valid target close enough for AAA */
if (target != nullptr && distance < 10000 /* m */) {
/* Default gun values */
double barrelHeight = 1.0; /* m */
double muzzleVelocity = 860; /* m/s */
double aimTime = 10; /* s */
unsigned int shotsToFire = 10;
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();
}
/* Approximate the flight time */
if (muzzleVelocity != 0)
aimTime += distance / muzzleVelocity;
internalCounter = (aimTime + 2) / 0.05; // TODO fix me you fucking monster
internalCounter = (aimTime + (3 - shotsIntensity) * shotsBaseInterval + 2) / FRAMERATE_TIME_INTERVAL;
/* 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;
/* 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;
/* Compute distance to the aim point */ // TODO: why am I here?
double dist;
double bearing1;
double bearing2;
Geodesic::WGS84().Inverse(position.lat, position.lng, aimLat, aimLng, dist, bearing1, bearing2);
/* 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;
/* Send the command */
std::ostringstream taskSS;
taskSS.precision(10);
taskSS << "{id = 'FireAtPoint', lat = " << aimLat << ", lng = " << aimLng << ", alt = " << target->getPosition().alt << ", radius = 0.001, expendQty = " << shotsToFire << " }";
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);
@ -315,8 +334,16 @@ void GroundUnit::AIloop()
}
}
if (internalCounter == 0)
internalCounter = 5 / 0.05;
/* 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;
}
internalCounter--;
break;

View File

@ -81,21 +81,25 @@ void NavyUnit::setState(unsigned char newState)
/************ Perform any action required when ENTERING a state ************/
switch (newState) {
case State::IDLE: {
setEnableTaskCheckFailed(false);
clearActivePath();
resetActiveDestination();
break;
}
case State::REACH_DESTINATION: {
setEnableTaskCheckFailed(true);
resetActiveDestination();
break;
}
case State::FIRE_AT_AREA: {
setEnableTaskCheckFailed(true);
clearActivePath();
resetActiveDestination();
resetTask();
break;
}
case State::SIMULATE_FIRE_FIGHT: {
setEnableTaskCheckFailed(true);
clearActivePath();
resetActiveDestination();
resetTask();

View File

@ -69,6 +69,12 @@ void Unit::update(json::value json, double dt)
if (json.has_number_field(L"speed"))
setSpeed(json[L"speed"].as_number().to_double());
if (json.has_number_field(L"horizontalVelocity"))
setHorizontalVelocity(json[L"horizontalVelocity"].as_number().to_double());
if (json.has_number_field(L"verticalVelocity"))
setVerticalVelocity(json[L"verticalVelocity"].as_number().to_double());
if (json.has_boolean_field(L"isAlive"))
setAlive(json[L"isAlive"].as_bool());
@ -146,7 +152,7 @@ void Unit::runAILoop() {
/* If the unit is alive, controlled, is the leader of the group and it is not a human, run the AI Loop that performs the requested commands and instructions (moving, attacking, etc) */
if (getAlive() && getControlled() && !getHuman() && getIsLeader()) {
if (checkTaskFailed() && state != State::IDLE && state != State::LAND) {
if (getEnableTaskCheckFailed() && checkTaskFailed()) {
log(unitName + " has no task, switching to IDLE state");
setState(State::IDLE);
}
@ -249,6 +255,8 @@ void Unit::getData(stringstream& ss, unsigned long long time)
case DataIndex::hasTask: appendNumeric(ss, datumIndex, hasTask); break;
case DataIndex::position: appendNumeric(ss, datumIndex, position); break;
case DataIndex::speed: appendNumeric(ss, datumIndex, speed); break;
case DataIndex::horizontalVelocity: appendNumeric(ss, datumIndex, horizontalVelocity); break;
case DataIndex::verticalVelocity: appendNumeric(ss, datumIndex, verticalVelocity); break;
case DataIndex::heading: appendNumeric(ss, datumIndex, heading); break;
case DataIndex::isActiveTanker: appendNumeric(ss, datumIndex, isActiveTanker); break;
case DataIndex::isActiveAWACS: appendNumeric(ss, datumIndex, isActiveAWACS); break;
@ -734,6 +742,10 @@ bool Unit::updateActivePath(bool looping)
}
}
void Unit::setHasTask(bool newValue) {
updateValue(hasTask, newValue, DataIndex::hasTask);
}
bool Unit::checkTaskFailed()
{
if (getHasTask())

View File

@ -172,19 +172,20 @@ Unit* UnitsManager::getClosestUnit(Unit* unit, unsigned char coalition, vector<s
double bearing1;
double bearing2;
Geodesic::WGS84().Inverse(unit->getPosition().lat, unit->getPosition().lng, p.second->getPosition().lat, p.second->getPosition().lng, dist, bearing1, bearing2);
double altDelta = unit->getPosition().alt - p.second->getPosition().alt;
/* If the closest unit has not been assigned yet, assign it to this unit */
if (closestUnit == nullptr)
{
closestUnit = p.second;
distance = dist;
distance = sqrt(dist * dist + altDelta * altDelta);
}
else {
/* Check if the unit is closer than the one already selected */
if (dist < distance) {
closestUnit = p.second;
distance = dist;
distance = sqrt(dist * dist + altDelta * altDelta);
}
}
}