Added back attack and movement functions, missionData is now part of units

This commit is contained in:
Pax1601 2023-01-17 22:17:20 +01:00
parent 280799b27a
commit b77f271183
18 changed files with 388 additions and 225 deletions

File diff suppressed because one or more lines are too long

View File

@ -5,9 +5,8 @@
font-size: 12px;
position: fixed;
transition: bottom 0.2s;
border-radius: 5px;
border-radius: 2px;
text-shadow: 1px 1px #000, -1px -1px #000, 1px -1px #000, -1px 1px #000;
box-shadow: 0px 0px 10px #000000AA;
font-family: "Lucida Console", "Courier New", monospace !important;
}

View File

@ -11,6 +11,7 @@
--text-color:white;
--blue-coalition-color: #2196F3;
--red-coalition-color: #f32121;
--neutral-coalition-color: #AAAAAA;
--active-coalition-color: var(--blue-coalition-color);
--highlight-color: #FFFFFFAA;
}

View File

@ -1,4 +1,4 @@
import { setActiveCoalition } from "..";
import { getActiveCoalition, setActiveCoalition } from "..";
import { deg2rad } from "../other/utils";
export class SelectionWheel
@ -57,6 +57,22 @@ export class SelectionWheel
}
button.appendChild(image);
}
/* Hide the coalition switch if required */
var switchContainer = <HTMLElement> this.#container.querySelector("#coalition-switch-container");
if (showCoalition == false)
{
switchContainer.style.display = "none";
document.documentElement.style.setProperty('--active-coalition-color', getComputedStyle(this.#container).getPropertyValue("--neutral-coalition-color"));
}
else
{
switchContainer.style.display = "block";
if (getActiveCoalition() == "blue")
document.documentElement.style.setProperty('--active-coalition-color', getComputedStyle(this.#container).getPropertyValue("--blue-coalition-color"));
else
document.documentElement.style.setProperty('--active-coalition-color', getComputedStyle(this.#container).getPropertyValue("--red-coalition-color"));
}
}
}

View File

@ -1,4 +1,5 @@
import * as L from 'leaflet'
import { getUnitsManager } from '..';
import { ConvertDDToDMS } from '../other/utils';
/* Edit here to change server address */
@ -24,7 +25,6 @@ export function getDataFromDCS(callback: CallableFunction)
export function addDestination(ID: number, path: any)
{
// TODO move in dedicated file
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
@ -89,34 +89,34 @@ export function spawnAircraft(type: string, latlng: L.LatLng, coalition: string,
export function attackUnit(ID: number, targetID: number)
{
//var xhr = new XMLHttpRequest();
//xhr.open("PUT", RESTaddress);
//xhr.setRequestHeader("Content-Type", "application/json");
//xhr.onreadystatechange = () => {
// if (xhr.readyState === 4) {
// console.log("Unit " + unitsManager.getUnitByID(ID).unitName + " attack " + unitsManager.getUnitByID(targetID).unitName );
// }
//};
//
//var command = {"ID": ID, "targetID": targetID};
//var data = {"attackUnit": command}
//
//xhr.send(JSON.stringify(data));
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
console.log("Unit " + getUnitsManager().getUnitByID(ID).unitName + " attack " + getUnitsManager().getUnitByID(targetID).unitName );
}
};
var command = {"ID": ID, "targetID": targetID};
var data = {"attackUnit": command}
xhr.send(JSON.stringify(data));
}
export function cloneUnit(ID: number)
{
//var xhr = new XMLHttpRequest();
//xhr.open("PUT", RESTaddress);
//xhr.setRequestHeader("Content-Type", "application/json");
//xhr.onreadystatechange = () => {
// if (xhr.readyState === 4) {
// console.log("Unit " + unitsManager.getUnitByID(ID).unitName + " cloned");
// }
//};
//
//var command = {"ID": ID};
//var data = {"cloneUnit": command}
//
//xhr.send(JSON.stringify(data));
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
console.log("Unit " + getUnitsManager().getUnitByID(ID).unitName + " cloned");
}
};
var command = {"ID": ID};
var data = {"cloneUnit": command}
xhr.send(JSON.stringify(data));
}

View File

@ -26,6 +26,8 @@ function setup()
scenarioDropdown = new Dropdown("scenario-dropdown", ["Caucasus", "Syria", "Nevada", "Marianas", "South Atlantic", "The channel"], () => {});
mapSourceDropdown = new Dropdown("map-source-dropdown", map.getLayers(), (option: string) => map.setLayer(option));
activeCoalition = "blue";
/* Main update rate = 250ms is minimum time, equal to server update time. */
setInterval(() => getDataFromDCS(update), 250);
}

View File

@ -158,9 +158,9 @@ export class Map extends L.Map
{
if (!e.originalEvent.ctrlKey)
{
//unitsManager.clearDestinations();
getUnitsManager().clearDestinations();
}
//unitsManager.addDestination(e.latlng)
getUnitsManager().addDestination(e.latlng)
}
}

View File

@ -2,8 +2,7 @@ import { Marker, LatLng, Polyline } from 'leaflet';
import { ConvertDDToDMS } from '../other/utils';
import { getMap, getUnitsManager } from '..';
import { UnitMarker, MarkerOptions } from './unitmarker';
import { addDestination } from '../dcs/dcs';
//import { attackUnit } from 'DCS/DCSCommands.js'
import { addDestination, attackUnit } from '../dcs/dcs';
export class Unit
{
@ -86,7 +85,7 @@ export class Unit
/* The marker is set by the inherited class */
this.#marker = marker;
this.#marker.on('click', (e) => this.#onClick(e));
//this.#marker.on('dblclick', (e) => this.#onDoubleClick(e));
this.#marker.on('dblclick', (e) => this.#onDoubleClick(e));
this.#selected = false;
this.#preventClick = false;
@ -142,7 +141,7 @@ export class Unit
setSelected(selected: boolean)
{
// Only alive units can be selected. Some units are not selectable (weapons)
/* Only alive units can be selected. Some units are not selectable (weapons) */
if ((this.alive || !selected) && this.#selectable && this.#selected != selected)
{
this.#selected = selected;
@ -168,7 +167,7 @@ export class Unit
{
path = {"1": latlng};
}
addDestination
addDestination(this.ID, path);
}
clearDestinations()
@ -193,6 +192,25 @@ export class Unit
}, 200);
}
#onDoubleClick(e: any)
{
clearTimeout(this.#timer);
this.#preventClick = true;
var options = [
{'tooltip': 'Attack', 'src': 'attack.png', 'callback': () => {getMap().hideSelectionWheel(); getUnitsManager().attackUnit(this.ID);}},
{'tooltip': 'Go to tanker', 'src': 'tanker.png', 'callback': () => {getMap().hideSelectionWheel(); /*showMessage("Function not implemented yet");*/}},
{'tooltip': 'RTB', 'src': 'rtb.png', 'callback': () => {getMap().hideSelectionWheel(); /*showMessage("Function not implemented yet");*/}}
]
if (!this.leader && !this.wingman)
{
options.push({'tooltip': 'Create formation', 'src': 'formation.png', 'callback': () => {getMap().hideSelectionWheel(); /*unitsManager.createFormation(this.ID);*/}});
}
getMap().showSelectionWheel(e.originalEvent, options, false);
}
#updateMarker()
{
/* Add the marker if not present */
@ -217,21 +235,21 @@ export class Unit
var _points = [];
_points.push(new LatLng(this.latitude, this.longitude));
// Add markers if missing
/* Add markers if missing */
while (this.#pathMarkers.length < Object.keys(this.activePath).length)
{
var marker = new Marker([0, 0]).addTo(getMap());
this.#pathMarkers.push(marker);
}
// Remove markers if too many
/* Remove markers if too many */
while (this.#pathMarkers.length > Object.keys(this.activePath).length)
{
getMap().removeLayer(this.#pathMarkers[this.#pathMarkers.length - 1]);
this.#pathMarkers.splice(this.#pathMarkers.length - 1, 1)
}
// Update the position of the existing markers (to avoid creating markers uselessly)
/* Update the position of the existing markers (to avoid creating markers uselessly) */
for (let WP in this.activePath)
{
var destination = this.activePath[WP];
@ -252,69 +270,7 @@ export class Unit
this.#pathPolyline.setLatLngs([]);
}
/*
#onDoubleClick(e)
{
clearTimeout(this.#timer);
this.#preventClick = true;
var options = [
{'tooltip': 'Attack', 'src': 'attack.png', 'callback': () => {map.removeSelectionWheel(); unitsManager.attackUnit(this.ID);}},
{'tooltip': 'Go to tanker', 'src': 'tanker.png', 'callback': () => {map.removeSelectionWheel(); showMessage("Function not implemented yet");}},
{'tooltip': 'RTB', 'src': 'rtb.png', 'callback': () => {map.removeSelectionWheel(); showMessage("Function not implemented yet");}}
]
if (!this.leader && !this.wingman)
{
options.push({'tooltip': 'Create formation', 'src': 'formation.png', 'callback': () => {map.removeSelectionWheel(); unitsManager.createFormation(this.ID);}});
}
map.showSelectionWheel(e, options, false);
}
drawPath()
{
var _points = [];
_points.push(new LatLng(this.latitude, this.longitude));
// Add markers if missing
while (this.#pathMarkers.length < Object.keys(this.activePath).length)
{
var marker = new Marker([0, 0]).addTo(map.getMap());
this.#pathMarkers.push(marker);
}
// Remove markers if too many
while (this.#pathMarkers.length > Object.keys(this.activePath).length)
{
map.getMap().removeLayer(this.#pathMarkers[this.#pathMarkers.length - 1]);
this.#pathMarkers.splice(this.#pathMarkers.length - 1, 1)
}
// Update the position of the existing markers (to avoid creating markers uselessly)
for (let WP in this.activePath)
{
var destination = this.activePath[WP];
this.#pathMarkers[parseInt(WP) - 1].setLatLng([destination.lat, destination.lng]);
_points.push(new LatLng(destination.lat, destination.lng));
this.#pathPolyline.setLatLngs(_points);
}
}
clearPath()
{
for (let WP in this.#pathMarkers)
{
map.getMap().removeLayer(this.#pathMarkers[WP]);
}
this.#pathMarkers = [];
this.#pathPolyline.setLatLngs([]);
}
drawTargets()
{
for (let typeIndex in this.missionData['targets'])
@ -359,8 +315,9 @@ export class Unit
map.getMap().removeLayer(this.#targetsPolylines[index])
}
}
*/
attackUnit(targetID)
attackUnit(targetID: number)
{
// Call DCS attackUnit function
if (this.ID != targetID)
@ -373,6 +330,7 @@ export class Unit
}
}
/*
changeSpeed(speedChange)
{
// TODO move in dedicated file

View File

@ -1,4 +1,4 @@
import { getUnitInfoPanel } from "..";
import { getMap, getUnitInfoPanel } from "..";
import { Unit, GroundUnit } from "./unit";
export class UnitsManager
@ -14,7 +14,7 @@ export class UnitsManager
addUnit(ID: number, data: any)
{
// The name of the unit category is exactly the same as the constructor name
/* The name of the unit category is exactly the same as the constructor name */
var constructor = Unit.getConstructor(data.category);
if (constructor != undefined)
{
@ -51,7 +51,7 @@ export class UnitsManager
{
for (let ID in data["units"])
{
// Create the unit if missing from the local array, then update the data. Drawing is handled by leaflet.
/* Create the unit if missing from the local array, then update the data. Drawing is handled by leaflet. */
if (!(ID in this.#units))
{
this.addUnit(parseInt(ID), data["units"][ID]);
@ -72,16 +72,16 @@ export class UnitsManager
onUnitSelection()
{
//if (this.getSelectedUnits().length > 0)
//{
// map.setState("MOVE_UNIT");
// unitControlPanel.setEnabled(true);
//}
//else
//{
// map.setState("IDLE");
// unitControlPanel.setEnabled(false);
//}
if (this.getSelectedUnits().length > 0)
{
getMap().setState("MOVE_UNIT");
//unitControlPanel.setEnabled(true);
}
else
{
getMap().setState("IDLE");
//unitControlPanel.setEnabled(false);
}
}
// selectFromBounds(bounds)
@ -110,33 +110,33 @@ export class UnitsManager
return selectedUnits;
}
// addDestination(latlng)
// {
// var selectedUnits = this.getSelectedUnits();
// for (let idx in selectedUnits)
// {
// var commandedUnit = selectedUnits[idx];
// if (selectedUnits[idx].wingman)
// {
// commandedUnit = this.getLeader(selectedUnits[idx].ID);
// }
// commandedUnit.addDestination(latlng);
// }
// }
addDestination(latlng: L.LatLng)
{
var selectedUnits = this.getSelectedUnits();
for (let idx in selectedUnits)
{
var commandedUnit = selectedUnits[idx];
//if (selectedUnits[idx].wingman)
//{
// commandedUnit = this.getLeader(selectedUnits[idx].ID);
//}
commandedUnit.addDestination(latlng);
}
}
// clearDestinations()
// {
// var selectedUnits = this.getSelectedUnits();
// for (let idx in selectedUnits)
// {
// var commandedUnit = selectedUnits[idx];
// if (selectedUnits[idx].wingman)
// {
// commandedUnit = this.getLeader(selectedUnits[idx].ID);
// }
// commandedUnit.clearDestinations();
// }
// }
clearDestinations()
{
var selectedUnits = this.getSelectedUnits();
for (let idx in selectedUnits)
{
var commandedUnit = selectedUnits[idx];
//if (selectedUnits[idx].wingman)
//{
// commandedUnit = this.getLeader(selectedUnits[idx].ID);
//}
commandedUnit.clearDestinations();
}
}
// selectedUnitsMove()
// {
@ -187,20 +187,20 @@ export class UnitsManager
// }
// }
// attackUnit(ID)
// {
// var selectedUnits = this.getSelectedUnits();
// for (let idx in selectedUnits)
// {
// // If a unit is a wingman, send the command to its leader
// var commandedUnit = selectedUnits[idx];
// if (selectedUnits[idx].wingman)
// {
// commandedUnit = this.getLeader(selectedUnits[idx].ID);
// }
// commandedUnit.attackUnit(ID);
// }
// }
attackUnit(ID: number)
{
var selectedUnits = this.getSelectedUnits();
for (let idx in selectedUnits)
{
/* If a unit is a wingman, send the command to its leader */
var commandedUnit = selectedUnits[idx];
//if (selectedUnits[idx].wingman)
//{
// commandedUnit = this.getLeader(selectedUnits[idx].ID);
//}
commandedUnit.attackUnit(ID);
}
}
// createFormation(ID)
// {

View File

@ -1,7 +1,7 @@
<div class="olympus-selection-wheel" id="selection-wheel">
<div class="olympus-wheel">
</div>
<label>
<label id="coalition-switch-container">
<input type="checkbox" id="coalition-switch"> <span class="olympus-selection-wheel-slider olympus-selection-wheel-switch"></span>
</label>
</div>

View File

@ -13,7 +13,8 @@ public:
Unit(json::value json, int ID);
~Unit();
void update(json::value json);
void updateExportData(json::value json);
void updateMissionData(json::value json);
json::value json();
void setPath(list<Coords> path);
@ -65,7 +66,6 @@ protected:
double heading = NULL;
double speed = NULL;
json::value flags = json::value::null();
Coords oldPosition = Coords(0); // Used to approximate speed
int targetID = NULL;
bool holding = false;
bool looping = false;
@ -77,9 +77,13 @@ protected:
vector<Unit*> wingmen;
double targetSpeed = 0;
double targetAltitude = 0;
double fuel = 0;
json::value ammo;
json::value targets;
list<Coords> activePath;
Coords activeDestination = Coords(0);
Coords oldPosition = Coords(0); // Used to approximate speed
virtual void AIloop();

View File

@ -11,8 +11,8 @@ public:
~UnitsFactory();
Unit* getUnit(int ID);
void getMissionDB(lua_State* L);
void update(lua_State* L);
void updateExportData(lua_State* L);
void updateMissionData(json::value missionData);
void updateAnswer(json::value& answer);
private:

View File

@ -25,7 +25,7 @@ Unit::~Unit()
}
void Unit::update(json::value json)
void Unit::updateExportData(json::value json)
{
/* Lock for thread safety */
lock_guard<mutex> guard(mutexLock);
@ -77,6 +77,16 @@ void Unit::update(json::value json)
}
}
void Unit::updateMissionData(json::value json)
{
if (json.has_number_field(L"fuel"))
fuel = json[L"fuel"].as_number().to_int32();
if (json.has_object_field(L"ammo"))
ammo = json[L"ammo"];
if (json.has_object_field(L"targets"))
targets = json[L"targets"];
}
void Unit::setPath(list<Coords> path)
{
activePath = path;
@ -187,6 +197,9 @@ json::value Unit::json()
json[L"leader"] = leader;
json[L"wingman"] = wingman;
json[L"formation"] = json::value::string(formation);
json[L"fuel"] = fuel;
json[L"ammo"] = ammo;
json[L"targets"] = targets;
int i = 0;
for (auto itr = wingmen.begin(); itr != wingmen.end(); itr++)

View File

@ -11,7 +11,6 @@ auto before = std::chrono::system_clock::now();
UnitsFactory* unitsFactory = nullptr;
Server* server = nullptr;
Scheduler* scheduler = nullptr;
json::value missionData;
/* Called when DCS simulation stops. All singleton instances are deleted. */
extern "C" DllExport int coreDeinit(lua_State* L)
@ -48,7 +47,7 @@ extern "C" DllExport int coreFrame(lua_State* L)
{
if (unitsFactory != nullptr)
{
unitsFactory->update(L);
unitsFactory->updateExportData(L);
}
// TODO allow for different intervals
@ -65,7 +64,10 @@ extern "C" DllExport int coreMissionData(lua_State * L)
{
lua_getglobal(L, "Olympus");
lua_getfield(L, -1, "missionData");
missionData = luaTableToJSON(L, -1);
json::value missionData = luaTableToJSON(L, -1);
if (missionData.has_object_field(L"unitsData"))
unitsFactory->updateMissionData(missionData[L"unitsData"]);
return(0);
}

View File

@ -9,7 +9,6 @@
extern UnitsFactory* unitsFactory;
extern Scheduler* scheduler;
extern json::value missionData;
void handle_eptr(std::exception_ptr eptr)
{
@ -58,7 +57,6 @@ void Server::handle_get(http_request request)
std::exception_ptr eptr;
try {
unitsFactory->updateAnswer(answer);
answer[L"missionData"] = missionData;
response.set_body(answer);
}
catch (...) {

View File

@ -26,11 +26,11 @@ Unit* UnitsFactory::getUnit(int ID)
}
}
void UnitsFactory::update(lua_State* L)
void UnitsFactory::updateExportData(lua_State* L)
{
map<int, json::value> unitJSONs = getAllUnits(L);
/* Update all units, create them if needed */
/* Update all units, create them if needed TODO: move code to get constructor in dedicated function */
for (auto const& p : unitJSONs)
{
int ID = p.first;
@ -74,7 +74,7 @@ void UnitsFactory::update(lua_State* L)
/* Update the unit if present*/
if (units.count(ID) != 0)
{
units[ID]->update(p.second);
units[ID]->updateExportData(p.second);
}
}
@ -88,6 +88,21 @@ void UnitsFactory::update(lua_State* L)
}
}
void UnitsFactory::updateMissionData(json::value missionData)
{
/* Update all units */
for (auto const& p : units)
{
int ID = p.first;
log(to_string(ID));
if (missionData.has_field(to_wstring(ID)))
{
log("mix");
p.second->updateMissionData(missionData[to_wstring(ID)]);
}
}
}
void UnitsFactory::updateAnswer(json::value& answer)
{
// TODO THREAT SAFEY!
@ -100,3 +115,4 @@ void UnitsFactory::updateAnswer(json::value& answer)
answer[L"units"] = unitsJson;
}

View File

@ -4,7 +4,7 @@
void DllExport stackUpdate(lua_State* L, int& stackDepth, int initialStack = 0);
void DllExport stackPop(lua_State* L, int popDepth = 1);
void DllExport stackClean(lua_State* L, int stackDepth);
json::value DllExport luaTableToJSON(lua_State* L, int index);
json::value DllExport luaTableToJSON(lua_State* L, int index, bool logKeys = false);
#define STACK_UPDATE stackUpdate(L, stackDepth, initialStack);
#define STACK_INIT int stackDepth = 0; int initialStack = 0; stackUpdate(L, initialStack);

View File

@ -18,7 +18,7 @@ void stackClean(lua_State* L, int stackDepth)
lua_pop(L, stackDepth);
}
json::value luaTableToJSON(lua_State* L, int index)
json::value luaTableToJSON(lua_State* L, int index, bool logKeys)
{
auto json = json::value::object();
@ -32,9 +32,13 @@ json::value luaTableToJSON(lua_State* L, int index)
{
lua_pushvalue(L, -2);
const char* key = lua_tostring(L, -1);
if (logKeys)
{
log(key);
}
if (lua_istable(L, -2))
{
json[to_wstring(key)] = luaTableToJSON(L, -2);
json[to_wstring(key)] = luaTableToJSON(L, -2, logKeys);
}
else if (lua_isnumber(L, -2))
{
@ -44,7 +48,7 @@ json::value luaTableToJSON(lua_State* L, int index)
{
json[to_wstring(key)] = json::value::boolean(lua_toboolean(L, -2));
}
else if (lua_isstring(L, -2)) // Keep last, only checks if it can be stringified (not if it actually IS a string)
else if (lua_isstring(L, -2)) // Keep last, lua_isstring only checks if it can be stringified (not if it actually IS a string)
{
json[to_wstring(key)] = json::value::string(to_wstring(lua_tostring(L, -2)));
}