Added more functions to edit Coalition Areas

This commit is contained in:
Pax1601
2023-06-18 19:55:01 +02:00
parent ad3b1cb167
commit f9f02c3eb0
18 changed files with 3660 additions and 159 deletions

3362
client/package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,6 +10,7 @@
"watch": "watchify .\\src\\index.ts --debug -o .\\public\\javascripts\\bundle.js -t [ babelify --global true --presets [ @babel/preset-env ] --extensions '.js'] -p [ tsify --noImplicitAny ]" "watch": "watchify .\\src\\index.ts --debug -o .\\public\\javascripts\\bundle.js -t [ babelify --global true --presets [ @babel/preset-env ] --extensions '.js'] -p [ tsify --noImplicitAny ]"
}, },
"dependencies": { "dependencies": {
"@turf/turf": "^6.5.0",
"@types/formatcoords": "^1.1.0", "@types/formatcoords": "^1.1.0",
"@types/geojson": "^7946.0.10", "@types/geojson": "^7946.0.10",
"@types/leaflet": "^1.9.0", "@types/leaflet": "^1.9.0",

View File

@@ -1,11 +1,11 @@
import { getUnitsManager } from "..";
import { CoalitionArea } from "../map/coalitionarea"; import { CoalitionArea } from "../map/coalitionarea";
import { groundUnitsDatabase } from "../units/groundunitsdatabase";
import { ContextMenu } from "./contextmenu"; import { ContextMenu } from "./contextmenu";
import { Dropdown } from "./dropdown"; import { Dropdown } from "./dropdown";
import { Slider } from "./slider"; import { Slider } from "./slider";
import { Switch } from "./switch"; import { Switch } from "./switch";
const unitRole = ["AAA", "MANPADS", "Short range SAM", "Long range SAM", "Radar"]; const unitRole = ["AAA", "MANPADS", "SAM Sites", "Radar"];
export class CoalitionAreaContextMenu extends ContextMenu { export class CoalitionAreaContextMenu extends ContextMenu {
#coalitionSwitch: Switch; #coalitionSwitch: Switch;
@@ -30,6 +30,22 @@ export class CoalitionAreaContextMenu extends ContextMenu {
this.showSubMenu(e.detail.type); this.showSubMenu(e.detail.type);
}); });
document.addEventListener("contextMenuCreateIads", (e: any) => {
const values: {[key: string]: boolean} = {};
const element = this.#iadsRoleDropdown.getOptionElements();
for (let idx = 0; idx < element.length; idx++) {
const option = element.item(idx) as HTMLElement;
const key = option.querySelector("span")?.innerText;
const value = option.querySelector("input")?.checked;
if (key !== undefined && value !== undefined)
values[key] = value;
}
const area = this.getCoalitionArea();
if (area)
getUnitsManager().createIADS(area, values, this.#iadsDensitySlider.getValue());
})
/* Create the checkboxes to select the unit roles */ /* Create the checkboxes to select the unit roles */
this.#iadsRoleDropdown.setOptionsElements(unitRole.map((unitRole: string) => { this.#iadsRoleDropdown.setOptionsElements(unitRole.map((unitRole: string) => {
var div = document.createElement("div"); var div = document.createElement("div");
@@ -38,6 +54,7 @@ export class CoalitionAreaContextMenu extends ContextMenu {
label.title = `Add ${unitRole}s to the IADS`; label.title = `Add ${unitRole}s to the IADS`;
var input = document.createElement("input"); var input = document.createElement("input");
input.type = "checkbox"; input.type = "checkbox";
input.checked = true;
var span = document.createElement("span"); var span = document.createElement("span");
span.innerText = unitRole; span.innerText = unitRole;
label.appendChild(input); label.appendChild(input);
@@ -46,8 +63,6 @@ export class CoalitionAreaContextMenu extends ContextMenu {
return div as HTMLElement; return div as HTMLElement;
})); }));
this.hide(); this.hide();
} }

View File

@@ -55,6 +55,10 @@ export class Dropdown {
this.#options.replaceChildren(...optionsElements); this.#options.replaceChildren(...optionsElements);
} }
getOptionElements() {
return this.#options.children;
}
selectText(text: string) { selectText(text: string) {
const index = [].slice.call(this.#options.children).findIndex((opt: Element) => opt.querySelector("button")?.innerText === text); const index = [].slice.call(this.#options.children).findIndex((opt: Element) => opt.querySelector("button")?.innerText === text);
if (index > -1) { if (index > -1) {

View File

@@ -14,9 +14,10 @@ export interface SpawnOptions {
type: string; type: string;
latlng: LatLng; latlng: LatLng;
coalition: string; coalition: string;
loadout: string | null; loadout?: string | null;
airbaseName: string | null; airbaseName?: string | null;
altitude: number | null; altitude?: number | null;
immediate?: boolean;
} }
export class MapContextMenu extends ContextMenu { export class MapContextMenu extends ContextMenu {

View File

@@ -223,6 +223,10 @@ export function getUnitsManager() {
return unitsManager; return unitsManager;
} }
export function getMissionHandler() {
return missionHandler;
}
export function getUnitInfoPanel() { export function getUnitInfoPanel() {
return unitInfoPanel; return unitInfoPanel;
} }

View File

@@ -1,20 +1,105 @@
import { LatLngExpression, Polygon, PolylineOptions } from "leaflet"; import { LatLng, LatLngExpression, Polygon, PolylineOptions } from "leaflet";
import { getMap } from ".."; import { getMap } from "..";
import { DRAW_POLYGON } from "./map";
import { CoalitionAreaHandle } from "./coalitionareahandle";
export class CoalitionArea extends Polygon { export class CoalitionArea extends Polygon {
#coalition: string = "blue";
#selected: boolean = true;
#editing: boolean = true;
#handles: CoalitionAreaHandle[] = [];
constructor(latlngs: LatLngExpression[] | LatLngExpression[][] | LatLngExpression[][][], options?: PolylineOptions) { constructor(latlngs: LatLngExpression[] | LatLngExpression[][] | LatLngExpression[][][], options?: PolylineOptions) {
if (options === undefined) if (options === undefined)
options = {}; options = {};
options.bubblingMouseEvents = false; options.bubblingMouseEvents = false;
super(latlngs, options); super(latlngs, options);
this.on("click", (e: any) => {
if (!this.getSelected()) {
this.setSelected(true);
getMap().setState(DRAW_POLYGON);
}
else if (this.getEditing()) {
this.addLatLng(e.latlng);
this.addTemporaryLatLng(e.latlng);
}
});
this.on("contextmenu", (e: any) => { this.on("contextmenu", (e: any) => {
getMap().showCoalitionAreaContextMenu(e, this); if (!this.#editing)
}) getMap().showCoalitionAreaContextMenu(e, this);
else
this.setEditing(false);
});
this.#setColors();
} }
setCoalition(coalition: string) { setCoalition(coalition: string) {
this.setStyle({color: coalition, fillColor: coalition}); this.#coalition = coalition;
this.#setColors();
}
getCoalition() {
return this.#coalition;
}
setSelected(selected: boolean) {
this.#selected = selected;
this.#setColors();
this.#setHandles();
if (!selected)
this.setEditing(false);
}
getSelected() {
return this.#selected;
}
setEditing(editing: boolean) {
this.#editing = editing;
this.#setHandles();
}
getEditing() {
return this.#editing;
}
addTemporaryLatLng(latlng: LatLng) {
this.addLatLng(latlng);
}
moveTemporaryLatLng(latlng: LatLng) {
var latlngs = this.getLatLngs()[0] as LatLng[];
latlngs[latlngs.length - 1] = latlng;
this.setLatLngs(latlngs);
}
addLatLng(latlng: LatLngExpression | LatLngExpression[], latlngs?: LatLng[] | undefined): this {
super.addLatLng(latlng, latlngs)
this.#setHandles();
return this;
}
#setColors() {
this.setStyle({color: this.getSelected()? "white": this.#coalition, fillColor: this.#coalition});
}
#setHandles() {
this.#handles.forEach((handle: CoalitionAreaHandle) => handle.removeFrom(getMap()));
this.#handles = [];
var latlngs = this.getLatLngs()[0] as LatLng[];
latlngs.forEach((latlng: LatLng, idx: number) => {
const handle = new CoalitionAreaHandle(latlng);
handle.addTo(getMap());
handle.on("dragend", (e: any) => {
var latlngs = this.getLatLngs()[0] as LatLng[];
latlngs[idx] = e.latlng;
this.setLatLngs(latlngs);
});
this.#handles.push(handle);
});
} }
} }

View File

@@ -0,0 +1,19 @@
import { DivIcon, LatLng } from "leaflet";
import { CustomMarker } from "./custommarker";
export class CoalitionAreaHandle extends CustomMarker {
constructor(latlng: LatLng) {
super(latlng, {interactive: true, draggable: true});
}
createIcon() {
this.setIcon(new DivIcon({
iconSize: [52, 52],
iconAnchor: [26, 26],
className: "leaflet-target-marker",
}));
var el = document.createElement("div");
el.classList.add("ol-target-icon");
this.getElement()?.appendChild(el);
}
}

View File

@@ -55,7 +55,7 @@ export class Map extends L.Map {
#destinationGroupRotation: number = 0; #destinationGroupRotation: number = 0;
#computeDestinationRotation: boolean = false; #computeDestinationRotation: boolean = false;
#destinationRotationCenter: L.LatLng | null = null; #destinationRotationCenter: L.LatLng | null = null;
#polygons: CoalitionArea[] = []; #coalitionAreas: CoalitionArea[] = [];
#mapContextMenu: MapContextMenu = new MapContextMenu("map-contextmenu"); #mapContextMenu: MapContextMenu = new MapContextMenu("map-contextmenu");
#unitContextMenu: UnitContextMenu = new UnitContextMenu("unit-contextmenu"); #unitContextMenu: UnitContextMenu = new UnitContextMenu("unit-contextmenu");
@@ -175,16 +175,19 @@ export class Map extends L.Map {
if (this.#state === IDLE) { if (this.#state === IDLE) {
this.#resetDestinationMarkers(); this.#resetDestinationMarkers();
this.#resetTargetMarker(); this.#resetTargetMarker();
this.#deselectCoalitionAreas();
this.#showCursor(); this.#showCursor();
} }
else if (this.#state === MOVE_UNIT) { else if (this.#state === MOVE_UNIT) {
this.#resetTargetMarker(); this.#resetTargetMarker();
this.#deselectCoalitionAreas();
this.#createDestinationMarkers(); this.#createDestinationMarkers();
if (this.#destinationPreviewMarkers.length > 0) if (this.#destinationPreviewMarkers.length > 0)
this.#hideCursor(); this.#hideCursor();
} }
else if ([BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) { else if ([BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) {
this.#resetDestinationMarkers(); this.#resetDestinationMarkers();
this.#deselectCoalitionAreas();
this.#createTargetMarker(); this.#createTargetMarker();
this.#hideCursor(); this.#hideCursor();
} }
@@ -193,8 +196,8 @@ export class Map extends L.Map {
this.#resetTargetMarker(); this.#resetTargetMarker();
this.#showCursor(); this.#showCursor();
//@ts-ignore draggable option added by plugin //@ts-ignore draggable option added by plugin
this.#polygons.push(new CoalitionArea([])); this.#coalitionAreas.push(new CoalitionArea([]));
this.#polygons[this.#polygons.length - 1].addTo(this); this.#coalitionAreas[this.#coalitionAreas.length - 1].addTo(this);
} }
document.dispatchEvent(new CustomEvent("mapStateChanged")); document.dispatchEvent(new CustomEvent("mapStateChanged"));
} }
@@ -394,6 +397,10 @@ export class Map extends L.Map {
} }
} }
getSelectedCoalitionArea() {
return this.#coalitionAreas.find((area: CoalitionArea) => {return area.getSelected()});
}
/* Event handlers */ /* Event handlers */
#onClick(e: any) { #onClick(e: any) {
if (!this.#preventLeftClick) { if (!this.#preventLeftClick) {
@@ -402,7 +409,14 @@ export class Map extends L.Map {
} }
else if (this.#state === DRAW_POLYGON) { else if (this.#state === DRAW_POLYGON) {
this.#polygons[this.#polygons.length - 1].addLatLng(e.latlng); /* This gets only called to create the first point of the area. All other points are added by the area itself */
if (this.getSelectedCoalitionArea()?.getEditing()) {
this.getSelectedCoalitionArea()?.addLatLng(e.latlng);
this.getSelectedCoalitionArea()?.addTemporaryLatLng(e.latlng);
}
else {
this.getSelectedCoalitionArea()?.setSelected(false);
}
} }
else { else {
this.setState(IDLE); this.setState(IDLE);
@@ -448,17 +462,6 @@ export class Map extends L.Map {
} }
} }
#executeAction(e: any, action: string) {
if (action === "bomb")
getUnitsManager().selectedUnitsBombPoint(this.getMouseCoordinates());
else if (action === "carpet-bomb")
getUnitsManager().selectedUnitsCarpetBomb(this.getMouseCoordinates());
else if (action === "building-bomb")
getUnitsManager().selectedUnitsBombBuilding(this.getMouseCoordinates());
else if (action === "fire-at-area")
getUnitsManager().selectedUnitsFireAtArea(this.getMouseCoordinates());
}
#onSelectionEnd(e: any) { #onSelectionEnd(e: any) {
clearTimeout(this.#leftClickTimer); clearTimeout(this.#leftClickTimer);
this.#preventLeftClick = true; this.#preventLeftClick = true;
@@ -497,6 +500,10 @@ export class Map extends L.Map {
else if ([BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) { else if ([BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) {
this.#targetMarker.setLatLng(this.getMouseCoordinates()); this.#targetMarker.setLatLng(this.getMouseCoordinates());
} }
else if (this.#state === DRAW_POLYGON) {
if (this.getSelectedCoalitionArea()?.getEditing())
this.getSelectedCoalitionArea()?.moveTemporaryLatLng(e.latlng);
}
} }
#onZoom(e: any) { #onZoom(e: any) {
@@ -569,6 +576,10 @@ export class Map extends L.Map {
this.removeLayer(this.#targetMarker); this.removeLayer(this.#targetMarker);
} }
#deselectCoalitionAreas() {
this.getSelectedCoalitionArea()?.setSelected(false);
}
#showCursor() { #showCursor() {
document.getElementById(this.#ID)?.classList.remove("hidden-cursor"); document.getElementById(this.#ID)?.classList.remove("hidden-cursor");
} }

View File

@@ -3,50 +3,41 @@ import { getInfoPopup, getMap } from "..";
import { Airbase } from "./airbase"; import { Airbase } from "./airbase";
import { Bullseye } from "./bullseye"; import { Bullseye } from "./bullseye";
export class MissionHandler export class MissionHandler {
{ #bullseyes: { [name: string]: Bullseye } = {};
#bullseyes : {[name: string]: Bullseye} = {}; #airbases: { [name: string]: Airbase } = {};
#airbases : {[name: string]: Airbase} = {}; #theatre: string = "";
#theatre : string = "";
#airbaseData : { [name: string]: object } = {}; #airbaseData: { [name: string]: object } = {};
// Time // Time
#date : any; #date: any;
#elapsedTime : any; #elapsedTime: any;
#startTime : any; #startTime: any;
#time : any; #time: any;
#updateTime : any; #updateTime: any;
constructor() constructor() {
{
} }
update(data: BullseyesData | AirbasesData | any) update(data: BullseyesData | AirbasesData | any) {
{ if ("bullseyes" in data) {
if ("bullseyes" in data) for (let idx in data.bullseyes) {
{
for (let idx in data.bullseyes)
{
const bullseye = data.bullseyes[idx]; const bullseye = data.bullseyes[idx];
if (!(idx in this.#bullseyes)) if (!(idx in this.#bullseyes))
this.#bullseyes[idx] = new Bullseye([0, 0]).addTo(getMap()); this.#bullseyes[idx] = new Bullseye([0, 0]).addTo(getMap());
if (bullseye.latitude && bullseye.longitude && bullseye.coalition) if (bullseye.latitude && bullseye.longitude && bullseye.coalition) {
{ this.#bullseyes[idx].setLatLng(new LatLng(bullseye.latitude, bullseye.longitude));
this.#bullseyes[idx].setLatLng(new LatLng(bullseye.latitude, bullseye.longitude));
this.#bullseyes[idx].setCoalition(bullseye.coalition); this.#bullseyes[idx].setCoalition(bullseye.coalition);
} }
} }
} }
if ("mission" in data) {
if ("mission" in data) if (data.mission != null && data.mission.theatre != this.#theatre) {
{
if (data.mission != null && data.mission.theatre != this.#theatre)
{
this.#theatre = data.mission.theatre; this.#theatre = data.mission.theatre;
getMap().setTheatre(this.#theatre); getMap().setTheatre(this.#theatre);
@@ -55,30 +46,18 @@ export class MissionHandler
} }
if ("airbases" in data) if ("airbases" in data) {
{ for (let idx in data.airbases) {
/*
console.log( Object.values( data.airbases ).sort( ( a:any, b:any ) => {
const aVal = a.callsign.toLowerCase();
const bVal = b.callsign.toLowerCase();
return aVal > bVal ? 1 : -1;
}) );
//*/
for (let idx in data.airbases)
{
var airbase = data.airbases[idx] var airbase = data.airbases[idx]
if (this.#airbases[idx] === undefined && airbase.callsign != '') if (this.#airbases[idx] === undefined && airbase.callsign != '') {
{
this.#airbases[idx] = new Airbase({ this.#airbases[idx] = new Airbase({
position: new LatLng(airbase.latitude, airbase.longitude), position: new LatLng(airbase.latitude, airbase.longitude),
name: airbase.callsign name: airbase.callsign
}).addTo(getMap()); }).addTo(getMap());
this.#airbases[idx].on('contextmenu', (e) => this.#onAirbaseClick(e)); this.#airbases[idx].on('contextmenu', (e) => this.#onAirbaseClick(e));
} }
if (this.#airbases[idx] != undefined && airbase.latitude && airbase.longitude && airbase.coalition) if (this.#airbases[idx] != undefined && airbase.latitude && airbase.longitude && airbase.coalition) {
{
this.#airbases[idx].setLatLng(new LatLng(airbase.latitude, airbase.longitude)); this.#airbases[idx].setLatLng(new LatLng(airbase.latitude, airbase.longitude));
this.#airbases[idx].setCoalition(airbase.coalition); this.#airbases[idx].setCoalition(airbase.coalition);
} }
@@ -87,83 +66,68 @@ export class MissionHandler
} }
} }
if ("mission" in data && data.mission != null) if ("mission" in data && data.mission != null) {
{ if (data.mission != null && data.mission.theatre != this.#theatre) {
if (data.mission != null && data.mission.theatre != this.#theatre)
{
this.#theatre = data.mission.theatre; this.#theatre = data.mission.theatre;
getMap().setTheatre(this.#theatre); getMap().setTheatre(this.#theatre);
getInfoPopup().setText("Map set to " + this.#theatre); getInfoPopup().setText("Map set to " + this.#theatre);
} }
if ( "date" in data.mission ) { if ("date" in data.mission)
this.#date = data.mission.date; this.#date = data.mission.date;
} if ("elapsedTime" in data.mission)
if ( "elapsedTime" in data.mission ) {
this.#elapsedTime = data.mission.elapsedTime; this.#elapsedTime = data.mission.elapsedTime;
} if ("startTime" in data.mission)
if ( "startTime" in data.mission ) {
this.#startTime = data.mission.startTime; this.#startTime = data.mission.startTime;
} if ("time" in data.mission)
if ( "time" in data.mission ) {
this.#time = data.mission.time; this.#time = data.mission.time;
}
} }
if ("time" in data)
if ( "time" in data ) {
this.#updateTime = data.time; this.#updateTime = data.time;
}
} }
getBullseyes() getBullseyes() {
{
return this.#bullseyes; return this.#bullseyes;
} }
getAirbases() {
return this.#airbases;
}
getDate() { getDate() {
return this.#date; return this.#date;
} }
getNowDate() { getNowDate() {
const date = this.getDate(); const date = this.getDate();
const time = this.getTime(); const time = this.getTime();
if ( !date ) { if (!date) {
return new Date(); return new Date();
} }
let year = date.Year; let year = date.Year;
let month = date.Month - 1; let month = date.Month - 1;
if ( month < 0 ) { if (month < 0) {
month = 11; month = 11;
year--; year--;
} }
return new Date( year, month, date.Day, time.h, time.m, time.s ); return new Date(year, month, date.Day, time.h, time.m, time.s);
} }
getTime() { getTime() {
return this.#time; return this.#time;
} }
getUpdateTime() { getUpdateTime() {
return this.#updateTime; return this.#updateTime;
} }
#onAirbaseClick(e: any) #onAirbaseClick(e: any) {
{
getMap().showAirbaseContextMenu(e, e.sourceTarget); getMap().showAirbaseContextMenu(e, e.sourceTarget);
} }
} }

View File

@@ -1,3 +1,7 @@
import { LatLng, Point, Polygon } from "leaflet";
import * as turf from "@turf/turf";
import { UnitDatabase } from "../units/unitdatabase";
export function bearing(lat1: number, lon1: number, lat2: number, lon2: number) { export function bearing(lat1: number, lon1: number, lat2: number, lon2: number) {
const φ1 = deg2rad(lat1); // φ, λ in radians const φ1 = deg2rad(lat1); // φ, λ in radians
const φ2 = deg2rad(lat2); const φ2 = deg2rad(lat2);
@@ -184,4 +188,35 @@ export function mToNm(m: number) {
export function nmToFt(nm: number) { export function nmToFt(nm: number) {
return nm * 6076.12; return nm * 6076.12;
}
export function randomPointInPoly(polygon: Polygon): LatLng {
var bounds = polygon.getBounds();
var x_min = bounds.getEast();
var x_max = bounds.getWest();
var y_min = bounds.getSouth();
var y_max = bounds.getNorth();
var lat = y_min + (Math.random() * (y_max - y_min));
var lng = x_min + (Math.random() * (x_max - x_min));
var poly = polygon.toGeoJSON();
var inside = turf.inside(turf.point([lng, lat]), poly);
if (inside) {
return new LatLng(lat, lng);
} else {
return randomPointInPoly(polygon);
}
}
export function polygonArea(polygon: Polygon) {
var poly = polygon.toGeoJSON();
return turf.area(poly);
}
export function randomUnitBlueprintByRole(unitDatabse: UnitDatabase, role: string) {
const unitBlueprints = unitDatabse.getByRole(role);
var index = Math.floor(Math.random() * unitBlueprints.length);
return unitBlueprints[index];
} }

View File

@@ -134,13 +134,13 @@ export function spawnExplosion(intensity: number, latlng: LatLng) {
} }
export function spawnGroundUnit(spawnOptions: SpawnOptions) { export function spawnGroundUnit(spawnOptions: SpawnOptions) {
var command = { "type": spawnOptions.type, "location": spawnOptions.latlng, "coalition": spawnOptions.coalition }; var command = { "type": spawnOptions.type, "location": spawnOptions.latlng, "coalition": spawnOptions.coalition, "immediate": spawnOptions.immediate? true: false };
var data = { "spawnGround": command } var data = { "spawnGround": command }
POST(data, () => { }); POST(data, () => { });
} }
export function spawnAircraft(spawnOptions: SpawnOptions) { export function spawnAircraft(spawnOptions: SpawnOptions) {
var command = { "type": spawnOptions.type, "location": spawnOptions.latlng, "coalition": spawnOptions.coalition, "altitude": spawnOptions.altitude, "payloadName": spawnOptions.loadout != null ? spawnOptions.loadout : "", "airbaseName": spawnOptions.airbaseName != null ? spawnOptions.airbaseName : "" }; var command = { "type": spawnOptions.type, "location": spawnOptions.latlng, "coalition": spawnOptions.coalition, "altitude": spawnOptions.altitude, "payloadName": spawnOptions.loadout != null ? spawnOptions.loadout : "", "airbaseName": spawnOptions.airbaseName != null ? spawnOptions.airbaseName : "", "immediate": spawnOptions.immediate? true: false };
var data = { "spawnAir": command } var data = { "spawnAir": command }
POST(data, () => { }); POST(data, () => { });
} }

View File

@@ -14,7 +14,7 @@ export class GroundUnitsDatabase extends UnitDatabase {
"items": [ "items": [
], ],
"roles": [ "roles": [
"SAM Site" "SAM Sites"
], ],
"code": "", "code": "",
"name": "Default" "name": "Default"
@@ -31,7 +31,7 @@ export class GroundUnitsDatabase extends UnitDatabase {
"fuel": 1, "fuel": 1,
"items": [], "items": [],
"roles": [ "roles": [
"SAM Site" "SAM Sites"
], ],
"code": "", "code": "",
"name": "Default" "name": "Default"
@@ -48,7 +48,7 @@ export class GroundUnitsDatabase extends UnitDatabase {
"fuel": 1, "fuel": 1,
"items": [], "items": [],
"roles": [ "roles": [
"SAM Site" "SAM Sites"
], ],
"code": "", "code": "",
"name": "Default" "name": "Default"
@@ -65,7 +65,7 @@ export class GroundUnitsDatabase extends UnitDatabase {
"fuel": 1, "fuel": 1,
"items": [], "items": [],
"roles": [ "roles": [
"SAM Site" "SAM Sites"
], ],
"code": "", "code": "",
"name": "Default" "name": "Default"
@@ -82,7 +82,7 @@ export class GroundUnitsDatabase extends UnitDatabase {
"fuel": 1, "fuel": 1,
"items": [], "items": [],
"roles": [ "roles": [
"SAM Site" "SAM Sites"
], ],
"code": "", "code": "",
"name": "Default" "name": "Default"
@@ -99,7 +99,7 @@ export class GroundUnitsDatabase extends UnitDatabase {
"fuel": 1, "fuel": 1,
"items": [], "items": [],
"roles": [ "roles": [
"SAM Site" "SAM Sites"
], ],
"code": "", "code": "",
"name": "Default" "name": "Default"
@@ -116,7 +116,7 @@ export class GroundUnitsDatabase extends UnitDatabase {
"fuel": 1, "fuel": 1,
"items": [], "items": [],
"roles": [ "roles": [
"SAM Site" "SAM Sites"
], ],
"code": "", "code": "",
"name": "Default" "name": "Default"
@@ -1544,7 +1544,7 @@ export class GroundUnitsDatabase extends UnitDatabase {
} }
], ],
"roles": [ "roles": [
"SAM" "MANPADS"
], ],
"code": "", "code": "",
"name": "Default" "name": "Default"
@@ -1590,7 +1590,7 @@ export class GroundUnitsDatabase extends UnitDatabase {
} }
], ],
"roles": [ "roles": [
"SAM" "MANPADS"
], ],
"code": "", "code": "",
"name": "Default" "name": "Default"
@@ -1613,7 +1613,7 @@ export class GroundUnitsDatabase extends UnitDatabase {
} }
], ],
"roles": [ "roles": [
"SAM" "MANPADS"
], ],
"code": "", "code": "",
"name": "Default" "name": "Default"
@@ -1927,7 +1927,7 @@ export class GroundUnitsDatabase extends UnitDatabase {
} }
], ],
"roles": [ "roles": [
"SAM" "MANPADS"
], ],
"code": "", "code": "",
"name": "Default" "name": "Default"
@@ -1973,7 +1973,7 @@ export class GroundUnitsDatabase extends UnitDatabase {
} }
], ],
"roles": [ "roles": [
"SAM" "MANPADS"
], ],
"code": "", "code": "",
"name": "Default" "name": "Default"

View File

@@ -1,9 +1,12 @@
import { LatLng, LatLngBounds } from "leaflet"; import { LatLng, LatLngBounds } from "leaflet";
import { getHotgroupPanel, getInfoPopup, getMap, getUnitDataTable } from ".."; import { getHotgroupPanel, getInfoPopup, getMap, getMissionHandler, getUnitDataTable } from "..";
import { Unit } from "./unit"; import { Unit } from "./unit";
import { cloneUnit } from "../server/server"; import { cloneUnit, spawnGroundUnit } from "../server/server";
import { deg2rad, keyEventWasInInput, latLngToMercator, mToFt, mercatorToLatLng, msToKnots } from "../other/utils"; import { deg2rad, keyEventWasInInput, latLngToMercator, mToFt, mercatorToLatLng, msToKnots, polygonArea, randomPointInPoly, randomUnitBlueprintByRole } from "../other/utils";
import { IDLE, MOVE_UNIT } from "../map/map"; import { IDLE, MOVE_UNIT } from "../map/map";
import { CoalitionArea } from "../map/coalitionarea";
import { Airbase } from "../missionhandler/airbase";
import { groundUnitsDatabase } from "./groundunitsdatabase";
export class UnitsManager { export class UnitsManager {
#units: { [ID: number]: Unit }; #units: { [ID: number]: Unit };
@@ -499,6 +502,35 @@ export class UnitsManager {
} }
} }
createIADS(coalitionArea: CoalitionArea, options: {[key: string]: boolean}, density: number) {
const activeRoles = Object.keys(options).filter((key: string) => { return options[key]; });
const airbases = getMissionHandler().getAirbases();
const pointsNumber = polygonArea(coalitionArea) / 1e7 * density / 100;
for (let i = 0; i < pointsNumber; i++) {
const latlng = randomPointInPoly(coalitionArea);
var minDistance: number = Infinity;
var maxDistance: number = 0;
Object.values(airbases).forEach((airbase: Airbase) => {
var distance = airbase.getLatLng().distanceTo(latlng);
if (distance < minDistance) minDistance = distance;
if (distance > maxDistance) maxDistance = distance;
});
const probability = Math.pow(1 - minDistance / 50e3, 5);
if (Math.random() < probability){
const role = activeRoles[Math.floor(Math.random() * activeRoles.length)];
const unitBlueprint = randomUnitBlueprintByRole(groundUnitsDatabase, role);
spawnGroundUnit({
role: role,
latlng: latlng,
type: unitBlueprint.name,
coalition: coalitionArea.getCoalition(),
immediate: true
})
}
}
}
/***********************************************/ /***********************************************/
#onKeyUp(event: KeyboardEvent) { #onKeyUp(event: KeyboardEvent) {
if (!keyEventWasInInput(event) && event.key === "Delete" ) { if (!keyEventWasInInput(event) && event.key === "Delete" ) {

View File

@@ -112,7 +112,7 @@
<div id="area-coalition-label" data-active-coalition="blue"></div> <div id="area-coalition-label" data-active-coalition="blue"></div>
<div id="upper-bar" class="ol-panel"> <div id="upper-bar" class="ol-panel">
<div id="coalition-area-switch" class="ol-switch ol-coalition-switch"></div> <div id="coalition-area-switch" class="ol-switch ol-coalition-switch"></div>
<button data-active-coalition="blue" id="iads-button" title="Create Integrated Air Defens System" data-on-click="coalitionAreaContextMenuShow" <button data-active-coalition="blue" id="iads-button" title="Create Integrated Air Defense System" data-on-click="coalitionAreaContextMenuShow"
data-on-click-params='{ "type": "iads" }' class="ol-contexmenu-button"></button> data-on-click-params='{ "type": "iads" }' class="ol-contexmenu-button"></button>
</div> </div>
<div id="iads-menu" class="ol-panel ol-contexmenu-panel hide"> <div id="iads-menu" class="ol-panel ol-contexmenu-panel hide">
@@ -123,11 +123,12 @@
</div> </div>
</div> </div>
<!--
<div class="ol-select-container"> <div class="ol-select-container">
<div id="iads-period-options" class="ol-select"> <div id="iads-period-options" class="ol-select">
<div class="ol-select-value">Units period</div> <div class="ol-select-value">Units period</div>
<div class="ol-select-options"> <div class="ol-select-options">
<!-- This is where all the iads unit period buttons will be shown--> <!-- This is where all the iads unit period buttons will be shown--><!--
</div> </div>
</div> </div>
</div> </div>
@@ -138,6 +139,7 @@
Coalition specific units Coalition specific units
</label> </label>
</div> </div>
-->
<div id="iads-density-slider" class="ol-slider-container"> <div id="iads-density-slider" class="ol-slider-container">
<dl class="ol-data-grid"> <dl class="ol-data-grid">
@@ -145,6 +147,6 @@
</dl> </dl>
<input type="range" min="0" max="100" value="0" class="ol-slider"> <input type="range" min="0" max="100" value="0" class="ol-slider">
</div> </div>
<button class="create-iads-button" title="" data-active-coalition="blue" data-on-click="contextMenuCreateIads" disabled>Add units to IADS</button> <button class="create-iads-button" title="" data-active-coalition="blue" data-on-click="contextMenuCreateIads">Add units to IADS</button>
</div> </div>
</div> </div>

View File

@@ -1,6 +1,6 @@
local version = "v0.3.0-alpha" local version = "v0.3.0-alpha"
local debug = false local debug = true
Olympus.unitCounter = 1 Olympus.unitCounter = 1
Olympus.payloadRegistry = {} Olympus.payloadRegistry = {}

View File

@@ -145,37 +145,40 @@ private:
class SpawnGroundUnit : public Command class SpawnGroundUnit : public Command
{ {
public: public:
SpawnGroundUnit(wstring coalition, wstring unitType, Coords location) : SpawnGroundUnit(wstring coalition, wstring unitType, Coords location, bool immediate) :
coalition(coalition), coalition(coalition),
unitType(unitType), unitType(unitType),
location(location) location(location),
immediate(immediate)
{ {
priority = CommandPriority::LOW; priority = CommandPriority::LOW;
}; };
virtual wstring getString(lua_State* L); virtual wstring getString(lua_State* L);
virtual int getLoad() { return 100; } virtual int getLoad() { return 100 * !immediate; }
private: private:
const wstring coalition; const wstring coalition;
const wstring unitType; const wstring unitType;
const Coords location; const Coords location;
const bool immediate;
}; };
/* Spawn air unit command */ /* Spawn air unit command */
class SpawnAircraft : public Command class SpawnAircraft : public Command
{ {
public: public:
SpawnAircraft(wstring coalition, wstring unitType, Coords location, wstring payloadName, wstring airbaseName) : SpawnAircraft(wstring coalition, wstring unitType, Coords location, wstring payloadName, wstring airbaseName, bool immediate) :
coalition(coalition), coalition(coalition),
unitType(unitType), unitType(unitType),
location(location), location(location),
payloadName(payloadName), payloadName(payloadName),
airbaseName(airbaseName) airbaseName(airbaseName),
immediate(immediate)
{ {
priority = CommandPriority::LOW; priority = CommandPriority::LOW;
}; };
virtual wstring getString(lua_State* L); virtual wstring getString(lua_State* L);
virtual int getLoad() { return 100; } virtual int getLoad() { return 100 * !immediate; }
private: private:
const wstring coalition; const wstring coalition;
@@ -183,6 +186,7 @@ private:
const Coords location; const Coords location;
const wstring payloadName; const wstring payloadName;
const wstring airbaseName; const wstring airbaseName;
const bool immediate;
}; };
/* Clone unit command */ /* Clone unit command */
@@ -232,7 +236,7 @@ public:
priority = CommandPriority::MEDIUM; priority = CommandPriority::MEDIUM;
}; };
virtual wstring getString(lua_State* L); virtual wstring getString(lua_State* L);
virtual int getLoad() { return 10; } virtual int getLoad() { return 2; }
private: private:
const wstring groupName; const wstring groupName;
@@ -249,7 +253,7 @@ public:
priority = CommandPriority::HIGH; priority = CommandPriority::HIGH;
}; };
virtual wstring getString(lua_State* L); virtual wstring getString(lua_State* L);
virtual int getLoad() { return 10; } virtual int getLoad() { return 2; }
private: private:
const wstring groupName; const wstring groupName;
@@ -266,7 +270,7 @@ public:
priority = CommandPriority::HIGH; priority = CommandPriority::HIGH;
}; };
virtual wstring getString(lua_State* L); virtual wstring getString(lua_State* L);
virtual int getLoad() { return 10; } virtual int getLoad() { return 2; }
private: private:
const wstring groupName; const wstring groupName;
@@ -297,7 +301,7 @@ public:
priority = CommandPriority::HIGH; priority = CommandPriority::HIGH;
}; };
virtual wstring getString(lua_State* L); virtual wstring getString(lua_State* L);
virtual int getLoad() { return 10; } virtual int getLoad() { return 2; }
private: private:
const wstring groupName; const wstring groupName;
@@ -318,7 +322,7 @@ public:
priority = CommandPriority::HIGH; priority = CommandPriority::HIGH;
}; };
virtual wstring getString(lua_State* L); virtual wstring getString(lua_State* L);
virtual int getLoad() { return 10; } virtual int getLoad() { return 2; }
private: private:
const wstring groupName; const wstring groupName;

View File

@@ -92,16 +92,18 @@ void Scheduler::handleRequest(wstring key, json::value value)
} }
else if (key.compare(L"spawnGround") == 0) else if (key.compare(L"spawnGround") == 0)
{ {
bool immediate = value[L"immediate"].as_bool();
wstring coalition = value[L"coalition"].as_string(); wstring coalition = value[L"coalition"].as_string();
wstring type = value[L"type"].as_string(); wstring type = value[L"type"].as_string();
double lat = value[L"location"][L"lat"].as_double(); double lat = value[L"location"][L"lat"].as_double();
double lng = value[L"location"][L"lng"].as_double(); double lng = value[L"location"][L"lng"].as_double();
log(L"Spawning " + coalition + L" ground unit of type " + type + L" at (" + to_wstring(lat) + L", " + to_wstring(lng) + L")"); log(L"Spawning " + coalition + L" ground unit of type " + type + L" at (" + to_wstring(lat) + L", " + to_wstring(lng) + L")");
Coords loc; loc.lat = lat; loc.lng = lng; Coords loc; loc.lat = lat; loc.lng = lng;
command = dynamic_cast<Command*>(new SpawnGroundUnit(coalition, type, loc)); command = dynamic_cast<Command*>(new SpawnGroundUnit(coalition, type, loc, immediate));
} }
else if (key.compare(L"spawnAir") == 0) else if (key.compare(L"spawnAir") == 0)
{ {
bool immediate = value[L"immediate"].as_bool();
wstring coalition = value[L"coalition"].as_string(); wstring coalition = value[L"coalition"].as_string();
wstring type = value[L"type"].as_string(); wstring type = value[L"type"].as_string();
double lat = value[L"location"][L"lat"].as_double(); double lat = value[L"location"][L"lat"].as_double();
@@ -111,7 +113,7 @@ void Scheduler::handleRequest(wstring key, json::value value)
wstring payloadName = value[L"payloadName"].as_string(); wstring payloadName = value[L"payloadName"].as_string();
wstring airbaseName = value[L"airbaseName"].as_string(); wstring airbaseName = value[L"airbaseName"].as_string();
log(L"Spawning " + coalition + L" air unit of type " + type + L" with payload " + payloadName + L" at (" + to_wstring(lat) + L", " + to_wstring(lng) + L" " + airbaseName + L")"); log(L"Spawning " + coalition + L" air unit of type " + type + L" with payload " + payloadName + L" at (" + to_wstring(lat) + L", " + to_wstring(lng) + L" " + airbaseName + L")");
command = dynamic_cast<Command*>(new SpawnAircraft(coalition, type, loc, payloadName, airbaseName)); command = dynamic_cast<Command*>(new SpawnAircraft(coalition, type, loc, payloadName, airbaseName, immediate));
} }
else if (key.compare(L"attackUnit") == 0) else if (key.compare(L"attackUnit") == 0)
{ {