Reworked unit as as state machine, added formations, added measuring on map, added copy-paste

This commit is contained in:
Pax1601
2023-02-01 21:38:36 +01:00
parent bb7259d908
commit 3c1db67733
74 changed files with 4838 additions and 907 deletions

View File

@@ -2,7 +2,7 @@ import { Marker, LatLng, Polyline, Icon } from 'leaflet';
import { ConvertDDToDMS } from '../other/utils';
import { getMap, getUnitsManager, getVisibilitySettings } from '..';
import { UnitMarker, MarkerOptions, AircraftMarker, HelicopterMarker, GroundUnitMarker, NavyUnitMarker, WeaponMarker } from './unitmarker';
import { addDestination, attackUnit, changeAltitude, changeSpeed } from '../dcs/dcs';
import { addDestination, attackUnit, changeAltitude, changeSpeed, createFormation as setLeader } from '../dcs/dcs';
var pathIcon = new Icon({
iconUrl: 'images/marker-icon.png',
@@ -12,9 +12,6 @@ var pathIcon = new Icon({
export class Unit {
ID: number = -1;
leader: boolean = false;
wingman: boolean = false;
wingmen: Unit[] = [];
formation: string = "";
name: string = "";
unitName: string = "";
@@ -33,7 +30,13 @@ export class Unit {
activePath: any = null;
ammo: any = null;
targets: any = null;
hasTask: boolean = false;
isLeader: boolean = false;
isWingman: boolean = false;
leaderID: number = 0;
wingmen: Unit[] = [];
wingmenIDs: number[] = [];
#selectable: boolean;
#selected: boolean = false;
#preventClick: boolean = false;
@@ -61,42 +64,36 @@ export class Unit {
this.#marker = marker;
this.#marker.on('click', (e) => this.#onClick(e));
this.#marker.on('dblclick', (e) => this.#onDoubleClick(e));
this.#marker.on('contextmenu', (e) => this.#onContextMenu(e));
this.#pathPolyline = new Polyline([], { color: '#2d3e50', weight: 3, opacity: 0.5, smoothFactor: 1 });
this.#pathPolyline.addTo(getMap());
this.#targetsPolylines = [];
}
update(response: JSON) {
update(response: any) {
for (let entry in response) {
// @ts-ignore
// @ts-ignore TODO handle better
this[entry] = response[entry];
}
// TODO handle better
if (response['activePath'] == undefined)
this.activePath = null
/* Dead units can't be selected */
this.setSelected(this.getSelected() && this.alive)
this.#updateMarker();
this.#clearTargets();
if (this.getSelected())
if (this.getSelected() && this.activePath != null)
{
this.#drawPath();
this.#drawTargets();
}
else
this.#clearPath();
/*
this.wingmen = [];
if (response["wingmenIDs"] != null)
{
for (let ID of response["wingmenIDs"])
{
this.wingmen.push(unitsManager.getUnitByID(ID));
}
}
*/
this.#clearPath();
}
setSelected(selected: boolean) {
@@ -105,6 +102,7 @@ export class Unit {
this.#selected = selected;
this.#marker.setSelected(selected);
getUnitsManager().onUnitSelection();
}
}
@@ -140,6 +138,28 @@ export class Unit {
return false;
}
getLeader() {
return getUnitsManager().getUnitByID(this.leaderID);
}
getFormation() {
return [<Unit>this].concat(this.getWingmen())
}
getWingmen() {
var wingmen: Unit[] = [];
if (this.wingmenIDs != null)
{
for (let ID of this.wingmenIDs)
{
var unit = getUnitsManager().getUnitByID(ID)
if (unit)
wingmen.push(unit);
}
}
return wingmen;
}
#onClick(e: any) {
this.#timer = setTimeout(() => {
if (!this.#preventClick) {
@@ -157,22 +177,21 @@ export class Unit {
#onDoubleClick(e: any) {
clearTimeout(this.#timer);
this.#preventClick = true;
}
#onContextMenu(e: any) {
var options = [
'Attack',
'Follow'
]
//if (!this.leader && !this.wingman) {
// options.push({ 'tooltip': 'Create formation', 'src': 'formation.png', 'callback': () => { getMap().hideSelectionWheel(); /*unitsManager.createFormation(this.ID);*/ } });
//}
getMap().showSelectionScroll(e.originalEvent, options, (action: string) => this.#executeAction(action));
getMap().showSelectionScroll(e.originalEvent, "Action: " + this.unitName, options, (action: string) => this.#executeAction(action));
}
#executeAction(action: string) {
getMap().hideSelectionScroll();
if (action === "Attack")
getUnitsManager().attackUnit(this.ID);
getUnitsManager().selectedUnitsAttackUnit(this.ID);
}
#updateMarker() {
@@ -318,25 +337,13 @@ export class Unit {
xhr.send(JSON.stringify(data));
}
setLeader(wingmenIDs)
{
// TODO move in dedicated file
var xhr = new XMLHttpRequest();
xhr.open("PUT", RESTaddress);
xhr.setRequestHeader("Content-Type", "application/json");
xhr.onreadystatechange = () => {
if (xhr.readyState === 4) {
console.log(this.unitName + " created formation with: " + wingmenIDs);
}
};
var command = {"ID": this.ID, "wingmenIDs": wingmenIDs}
var data = {"setLeader": command}
xhr.send(JSON.stringify(data));
}
*/
setLeader(isLeader: boolean, wingmenIDs: number[] = [])
{
setLeader(this.ID, isLeader, wingmenIDs);
}
}
export class AirUnit extends Unit {

View File

@@ -1,6 +1,7 @@
import { LatLng, LatLngBounds } from "leaflet";
import { getMap, getUnitInfoPanel } from "..";
import { getMap, getUnitControlPanel, getUnitInfoPanel } from "..";
import { Unit, GroundUnit } from "./unit";
import { cloneUnit } from "../dcs/dcs";
export class UnitsManager {
#units: { [ID: number]: Unit };
@@ -9,6 +10,13 @@ export class UnitsManager {
constructor() {
this.#units = {};
this.#copiedUnits = [];
document.addEventListener('copy', () => this.copyUnits());
document.addEventListener('paste', () => this.pasteUnits());
}
getUnits() {
return this.#units;
}
addUnit(ID: number, data: any) {
@@ -27,7 +35,10 @@ export class UnitsManager {
}
getUnitByID(ID: number) {
return this.#units[ID];
if (ID in this.#units)
return this.#units[ID];
else
return null;
}
removeUnit(ID: number) {
@@ -40,6 +51,13 @@ export class UnitsManager {
}
}
selectUnit(ID: number, deselectAllUnits: boolean = true)
{
if (deselectAllUnits)
this.deselectAllUnits();
this.#units[ID]?.setSelected(true);
}
update(data: any) {
for (let ID in data["units"]) {
/* Create the unit if missing from the local array, then update the data. Drawing is handled by leaflet. */
@@ -49,6 +67,7 @@ export class UnitsManager {
this.#units[parseInt(ID)].update(data["units"][ID]);
}
/* Update the unit info panel */
if (this.getSelectedUnits().length == 1) {
getUnitInfoPanel().show();
getUnitInfoPanel().update(this.getSelectedUnits()[0]);
@@ -67,6 +86,8 @@ export class UnitsManager {
getMap().setState("IDLE");
//unitControlPanel.setEnabled(false);
}
this.#updateUnitControlPanel();
}
selectFromBounds(bounds: LatLngBounds)
@@ -92,7 +113,35 @@ export class UnitsManager {
return selectedUnits;
}
addDestination(latlng: L.LatLng) {
getSelectedLeaders() {
var leaders: Unit[] = [];
for (let idx in this.getSelectedUnits())
{
var unit = this.getSelectedUnits()[idx];
if (unit.isLeader)
leaders.push(unit);
else if (unit.isWingman)
{
var leader = unit.getLeader();
if (leader && !leaders.includes(leader))
leaders.push(leader);
}
}
return leaders;
}
getSelectedSingletons() {
var singletons: Unit[] = [];
for (let idx in this.getSelectedUnits())
{
var unit = this.getSelectedUnits()[idx];
if (!unit.isLeader && !unit.isWingman)
singletons.push(unit);
}
return singletons;
}
selectedUnitsAddDestination(latlng: L.LatLng) {
var selectedUnits = this.getSelectedUnits();
for (let idx in selectedUnits) {
var commandedUnit = selectedUnits[idx];
@@ -104,7 +153,7 @@ export class UnitsManager {
}
}
clearDestinations() {
selectedUnitsClearDestinations() {
var selectedUnits = this.getSelectedUnits();
for (let idx in selectedUnits) {
var commandedUnit = selectedUnits[idx];
@@ -116,6 +165,11 @@ export class UnitsManager {
}
}
selectedUnitsLandAt(latlng: LatLng)
{
}
selectedUnitsChangeSpeed(speedChange: string)
{
var selectedUnits = this.getSelectedUnits();
@@ -134,33 +188,21 @@ export class UnitsManager {
}
}
// handleKeyEvent(e)
// {
// if (e.originalEvent.code === 'KeyC' && e.originalEvent.ctrlKey)
// {
// this.copyUnits();
// }
// else if (e.originalEvent.code === 'KeyV' && e.originalEvent.ctrlKey)
// {
// this.pasteUnits();
// }
// }
copyUnits()
{
this.#copiedUnits = this.getSelectedUnits();
}
// copyUnits()
// {
// this.#copiedUnits = this.getSelectedUnits();
// }
pasteUnits()
{
for (let idx in this.#copiedUnits)
{
var unit = this.#copiedUnits[idx];
cloneUnit(unit.ID);
}
}
// pasteUnits()
// {
// for (let idx in this.#copiedUnits)
// {
// var unit = this.#copiedUnits[idx];
// cloneUnit(unit.ID);
// }
// }
attackUnit(ID: number) {
selectedUnitsAttackUnit(ID: number) {
var selectedUnits = this.getSelectedUnits();
for (let idx in selectedUnits) {
/* If a unit is a wingman, send the command to its leader */
@@ -173,59 +215,70 @@ export class UnitsManager {
}
}
// createFormation(ID)
// {
// var selectedUnits = this.getSelectedUnits();
// var wingmenIDs = [];
// for (let idx in selectedUnits)
// {
// if (selectedUnits[idx].wingman)
// {
// showMessage(selectedUnits[idx].unitName + " is already in a formation.");
// return;
// }
// else if (selectedUnits[idx].leader)
// {
// showMessage(selectedUnits[idx].unitName + " is already in a formation.");
// return;
// }
// else
// {
// /* TODO
// if (selectedUnits[idx].category !== this.getUnitByID(ID).category)
// {
// showMessage("All units must be of the same category to create a formation.");
// }
// */
// if (selectedUnits[idx].ID != ID)
// {
// wingmenIDs.push(selectedUnits[idx].ID);
// }
// }
// }
// if (wingmenIDs.length > 0)
// {
// this.getUnitByID(ID).setLeader(wingmenIDs);
// }
// else
// {
// showMessage("At least 2 units must be selected to create a formation.");
// }
// }
selectedUnitsCreateFormation(ID: number | null = null)
{
var selectedUnits = this.getSelectedUnits();
if (selectedUnits.length >= 2)
{
if (ID == null)
ID = selectedUnits[0].ID
var wingmenIDs = [];
for (let idx in selectedUnits)
{
if (selectedUnits[idx].isWingman)
{
console.log(selectedUnits[idx].unitName + " is already in a formation.");
return;
}
else if (selectedUnits[idx].isLeader)
{
console.log(selectedUnits[idx].unitName + " is already in a formation.");
return;
}
else
{
/* TODO
if (selectedUnits[idx].category !== this.getUnitByID(ID).category)
{
showMessage("All units must be of the same category to create a formation.");
}
*/
if (selectedUnits[idx].ID != ID)
{
wingmenIDs.push(selectedUnits[idx].ID);
}
}
}
if (wingmenIDs.length > 0)
{
this.getUnitByID(ID)?.setLeader(true, wingmenIDs);
}
else
{
console.log("At least 2 units must be selected to create a formation.");
}
}
setTimeout(() => this.#updateUnitControlPanel(), 1000); // TODO find better method, may fail
}
// getLeader(ID)
// {
// for (let idx in this.#units)
// {
// var unit = this.#units[idx];
// if (unit.leader)
// {
// if (unit.wingmen.includes(this.getUnitByID(ID)))
// {
// return unit;
// }
// }
// }
// showMessage("Error: no leader found for this unit")
// }
selectedUnitsUndoFormation(ID: number | null = null)
{
for (let leader of this.getSelectedLeaders())
{
leader.setLeader(false);
}
setTimeout(() => this.#updateUnitControlPanel(), 1000); // TODO find better method, may fail
}
#updateUnitControlPanel() {
/* Update the unit control panel */
if (this.getSelectedUnits().length > 0) {
getUnitControlPanel().show();
getUnitControlPanel().update(this.getSelectedLeaders().concat(this.getSelectedSingletons()));
}
else {
getUnitControlPanel().hide();
}
}
}