From 327d5c74d983f9370af30149fec0c8f1c20d28fb Mon Sep 17 00:00:00 2001 From: Pax1601 Date: Fri, 7 Jul 2023 17:26:41 +0200 Subject: [PATCH 1/5] Add basic visibility functions and ability to spawn multiple units in group --- client/app.js | 13 +- client/demo.js | 80 ++++++- client/public/stylesheets/markers/units.css | 4 +- client/public/stylesheets/olympus.css | 19 +- client/src/@types/dom.d.ts | 3 +- client/src/@types/server.d.ts | 5 - client/src/@types/unit.d.ts | 3 +- client/src/constants/constants.ts | 14 +- client/src/controls/mapcontextmenu.ts | 25 +- client/src/index.ts | 8 +- client/src/map/map.ts | 6 +- client/src/map/temporaryunitmarker.ts | 19 +- client/src/missionhandler/missionhandler.ts | 5 +- client/src/other/utils.ts | 10 + client/src/server/server.ts | 60 ++--- client/src/units/unit.ts | 115 ++++++--- client/src/units/unitsmanager.ts | 88 ++++++- client/views/other/dialogs.ejs | 2 +- client/views/panels/navbar.ejs | 8 + olympus.json | 4 +- scripts/OlympusCommand.lua | 252 ++++++++------------ src/core/include/commands.h | 28 +-- src/core/include/server.h | 8 +- src/core/src/commands.cpp | 58 +++-- src/core/src/scheduler.cpp | 51 ++-- src/core/src/server.cpp | 49 +++- 26 files changed, 589 insertions(+), 348 deletions(-) diff --git a/client/app.js b/client/app.js index 85d5c31d..651c2b73 100644 --- a/client/app.js +++ b/client/app.js @@ -3,7 +3,6 @@ var path = require('path'); var cookieParser = require('cookie-parser'); var logger = require('morgan'); var fs = require('fs'); -var basicAuth = require('express-basic-auth') var atcRouter = require('./routes/api/atc'); var airbasesRouter = require('./routes/api/airbases'); @@ -37,15 +36,7 @@ if (config["server"] != undefined) module.exports = app; const DemoDataGenerator = require('./demo.js'); -var demoDataGenerator = new DemoDataGenerator(10); -app.get('/demo/units', (req, res) => demoDataGenerator.units(req, res)); -app.get('/demo/logs', (req, res) => demoDataGenerator.logs(req, res)); -app.get('/demo/bullseyes', (req, res) => demoDataGenerator.bullseyes(req, res)); -app.get('/demo/airbases', (req, res) => demoDataGenerator.airbases(req, res)); -app.get('/demo/mission', (req, res) => demoDataGenerator.mission(req, res)); - -app.use('/demo', basicAuth({ - users: { 'admin': 'socks' } -})) +var demoDataGenerator = new DemoDataGenerator(app); + diff --git a/client/demo.js b/client/demo.js index be34fce8..8c515a85 100644 --- a/client/demo.js +++ b/client/demo.js @@ -1,7 +1,8 @@ +var basicAuth = require('express-basic-auth') var enc = new TextEncoder(); const DEMO_UNIT_DATA = { - ["1"]:{ alive: true, human: false, controlled: true, coalition: 2, country: 0, name: "KC-135", unitName: "Cool guy 1-1", groupName: "Cool group 1", state: 3, task: "Being cool!", + ["1"]:{ category: "Aircraft", alive: true, human: false, controlled: true, coalition: 2, country: 0, name: "KC-135", unitName: "Cool guy 1-1 who also has a very long name", groupName: "Cool group 1", state: 3, task: "Being cool!", hasTask: true, position: { lat: 37, lng: -116, alt: 1000 }, speed: 200, heading: 45, isTanker: true, isAWACS: false, onOff: true, followRoads: false, fuel: 50, desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0, formationOffset: { x: 0, y: 0, z: 0 }, @@ -14,15 +15,15 @@ const DEMO_UNIT_DATA = { radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 }, generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false }, ammo: [{ quantity: 2, name: "A cool missile", guidance: 0, category: 0, missileCategory: 0 } ], - contacts: [], - activePath: [ {lat: 38, lng: -115, alt: 0}, {lat: 38, lng: -114, alt: 0} ] + contacts: [{ID: 2, detectionMethod: 1}], + activePath: [{lat: 38, lng: -115, alt: 0}, {lat: 38, lng: -114, alt: 0}] }, - ["2"]:{ alive: true, human: false, controlled: false, coalition: 1, country: 0, name: "KC-135", unitName: "Cool guy 1-2", groupName: "Cool group 2", state: 1, task: "Being cool", - hasTask: false, position: { lat: 36.9, lng: -116, alt: 1000 }, speed: 200, heading: 0, isTanker: false, isAWACS: false, onOff: true, followRoads: false, fuel: 50, + ["2"]:{ category: "Aircraft", alive: true, human: false, controlled: false, coalition: 1, country: 0, name: "FA-18C_hornet", unitName: "Cool guy 1-2", groupName: "Cool group 2", state: 1, task: "Being cool", + hasTask: false, position: { lat: 36.9, lng: -116, alt: 1000 }, speed: 200, heading: 315 * Math.PI / 180, isTanker: false, isAWACS: false, onOff: true, followRoads: false, fuel: 50, desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0, formationOffset: { x: 0, y: 0, z: 0 }, targetID: 0, - targetPosition: { lat: 38, lng: -117, alt: 1000 }, + targetPosition: { lat: 0, lng: 0, alt: 0 }, ROE: 2, reactionToThreat: 1, emissionsCountermeasures: 1, @@ -30,15 +31,57 @@ const DEMO_UNIT_DATA = { radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 }, generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false }, ammo: [{ quantity: 2, name: "A cool missile", guidance: 0, category: 0, missileCategory: 0 } ], - contacts: [{ID: 1, detectionMethod: 4}], - activePath: [ {lat: 38, lng: -115, alt: 0}, {lat: 38, lng: -114, alt: 0} ] + contacts: [{ID: 1, detectionMethod: 16}], + activePath: [ ] + }, ["3"]:{ category: "GroundUnit", alive: true, human: false, controlled: false, coalition: 1, country: 0, name: "M-60", unitName: "Cool guy 1-3", groupName: "Cool group 3", state: 1, task: "Being cool", + hasTask: false, position: { lat: 37.1, lng: -116, alt: 1000 }, speed: 200, heading: 315 * Math.PI / 180, isTanker: false, isAWACS: false, onOff: true, followRoads: false, fuel: 50, + desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0, + formationOffset: { x: 0, y: 0, z: 0 }, + targetID: 0, + targetPosition: { lat: 0, lng: 0, alt: 0 }, + ROE: 2, + reactionToThreat: 1, + emissionsCountermeasures: 1, + TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 }, + radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 }, + generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false }, + ammo: [{ quantity: 2, name: "A cool missile", guidance: 0, category: 0, missileCategory: 0 } ], + contacts: [{ID: 1, detectionMethod: 16}], + activePath: [ ] + }, ["4"]:{ category: "GroundUnit", alive: true, human: false, controlled: false, coalition: 1, country: 0, name: "M-60", unitName: "Cool guy 1-4", groupName: "Cool group 3", state: 1, task: "Being cool", + hasTask: false, position: { lat: 37.1, lng: -116.1, alt: 1000 }, speed: 200, heading: 315 * Math.PI / 180, isTanker: false, isAWACS: false, onOff: true, followRoads: false, fuel: 50, + desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0, + formationOffset: { x: 0, y: 0, z: 0 }, + targetID: 0, + targetPosition: { lat: 0, lng: 0, alt: 0 }, + ROE: 2, + reactionToThreat: 1, + emissionsCountermeasures: 1, + TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 }, + radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 }, + generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false }, + ammo: [{ quantity: 2, name: "A cool missile", guidance: 0, category: 0, missileCategory: 0 } ], + contacts: [{ID: 1, detectionMethod: 16}], + activePath: [ ] } } class DemoDataGenerator { - constructor() + constructor(app) { - + app.get('/demo/units', (req, res) => this.units(req, res)); + app.get('/demo/logs', (req, res) => this.logs(req, res)); + app.get('/demo/bullseyes', (req, res) => this.bullseyes(req, res)); + app.get('/demo/airbases', (req, res) => this.airbases(req, res)); + app.get('/demo/mission', (req, res) => this.mission(req, res)); + + app.use('/demo', basicAuth({ + users: { + 'admin': 'socks', + 'blue': 'bluesocks', + 'red': 'redsocks' + }, + })) } units(req, res){ @@ -48,7 +91,7 @@ class DemoDataGenerator { for (let idx in DEMO_UNIT_DATA) { const unit = DEMO_UNIT_DATA[idx]; array = this.concat(array, this.uint32ToByteArray(idx)); - array = this.appendString(array, "Aircraft", 1); + array = this.appendString(array, unit.category, 1); array = this.appendUint8(array, unit.alive, 2); array = this.appendUint8(array, unit.human, 3); array = this.appendUint8(array, unit.controlled, 4); @@ -302,6 +345,21 @@ class DemoDataGenerator { mission(req, res){ var ret = {mission: {theatre: "Nevada"}}; ret.time = Date.now(); + var auth = req.get("Authorization"); + if (auth) { + var username = atob(auth.replace("Basic ", "")).split(":")[0]; + switch (username) { + case "admin": + ret.mission.visibilityMode = "Game master"; + break + case "blue": + ret.mission.visibilityMode = "Blue commander"; + break; + case "red": + ret.mission.visibilityMode = "Red commander"; + break; + } + } res.send(JSON.stringify(ret)); } diff --git a/client/public/stylesheets/markers/units.css b/client/public/stylesheets/markers/units.css index 62ca82b6..4cbdcc90 100644 --- a/client/public/stylesheets/markers/units.css +++ b/client/public/stylesheets/markers/units.css @@ -150,7 +150,7 @@ 1px -1px 0 #000, -1px 1px 0 #000, 1px 1px 0 #000; - translate: -60px 0; + right: 100%; width: fit-content; } @@ -171,7 +171,7 @@ width: 80px; } -[data-object|="unit"] .unit-summary .unit-callsign:hover { +[data-object|="unit"]:hover .unit-summary .unit-callsign{ direction: rtl; overflow: visible; } diff --git a/client/public/stylesheets/olympus.css b/client/public/stylesheets/olympus.css index 480d5ab7..58b587cd 100644 --- a/client/public/stylesheets/olympus.css +++ b/client/public/stylesheets/olympus.css @@ -812,16 +812,16 @@ nav.ol-panel> :last-child { font-weight: bold; } -#connection-status { +#login-status { margin-bottom: 5px; } -#connection-status[data-status="connecting"]::before { +#login-status[data-status="connecting"]::before { animation: blinker 1s linear infinite; content: "Connecting..."; } -#connection-status[data-status="failed"]::before { +#login-status[data-status="failed"]::before { color: var(--primary-red); content: "Incorrect username/password!"; } @@ -865,6 +865,19 @@ nav.ol-panel> :last-child { translate: 0% -300%; } +#visibiliy-mode { + font-size: 14px; + font-weight: bolder; +} + +#visibiliy-mode[data-mode="Blue commander"] { + color: var(--primary-blue); +} + +#visibiliy-mode[data-mode="Red commander"] { + color: var(--primary-red); +} + .ol-destination-preview-icon { background-image: url("/resources/theme/images/markers/move.svg"); height: 52px; diff --git a/client/src/@types/dom.d.ts b/client/src/@types/dom.d.ts index 3feba2b0..9d260bb8 100644 --- a/client/src/@types/dom.d.ts +++ b/client/src/@types/dom.d.ts @@ -17,7 +17,8 @@ interface CustomEventMap { "groupCreation": CustomEvent, "groupDeletion": CustomEvent, "mapStateChanged": CustomEvent, - "mapContextMenu": CustomEvent<> + "mapContextMenu": CustomEvent<>, + "visibilityModeChanged": CustomEvent, } declare global { diff --git a/client/src/@types/server.d.ts b/client/src/@types/server.d.ts index 23920339..1f17211d 100644 --- a/client/src/@types/server.d.ts +++ b/client/src/@types/server.d.ts @@ -1,8 +1,3 @@ -interface UnitsData { - units: string, - sessionHash: string -} - interface AirbasesData { airbases: {[key: string]: any}, } diff --git a/client/src/@types/unit.d.ts b/client/src/@types/unit.d.ts index ec6aaa39..4026b637 100644 --- a/client/src/@types/unit.d.ts +++ b/client/src/@types/unit.d.ts @@ -8,7 +8,8 @@ interface UnitIconOptions { showShortLabel: boolean, showFuel: boolean, showAmmo: boolean, - showSummary: boolean, + showSummary: boolean, + showCallsign: boolean, rotateToHeading: boolean } diff --git a/client/src/constants/constants.ts b/client/src/constants/constants.ts index aa9c1189..b0c300f1 100644 --- a/client/src/constants/constants.ts +++ b/client/src/constants/constants.ts @@ -1,4 +1,16 @@ -import { LatLng, LatLngBounds, TileLayer, tileLayer } from "leaflet"; +import { LatLng, LatLngBounds } from "leaflet"; + +export const HIDE_ALL = "Hide all"; +export const GAME_MASTER = "Game master"; +export const BLUE_COMMANDER = "Blue commander"; +export const RED_COMMANDER = "Red commander"; + +export const VISUAL = 1; +export const OPTIC = 2; +export const RADAR = 4; +export const IRST = 8; +export const RWR = 16; +export const DLINK = 32; export const states: string[] = ["none", "idle", "reach-destination", "attack", "follow", "land", "refuel", "AWACS", "tanker", "bomb-point", "carpet-bomb", "bomb-building", "fire-at-area"]; export const ROEs: string[] = ["free", "designated", "return", "hold"]; diff --git a/client/src/controls/mapcontextmenu.ts b/client/src/controls/mapcontextmenu.ts index c0cdcdf9..8db22841 100644 --- a/client/src/controls/mapcontextmenu.ts +++ b/client/src/controls/mapcontextmenu.ts @@ -1,6 +1,6 @@ import { LatLng } from "leaflet"; import { getActiveCoalition, getMap, setActiveCoalition } from ".."; -import { spawnAircraft, spawnExplosion, spawnGroundUnit, spawnSmoke } from "../server/server"; +import { spawnAircrafts, spawnExplosion, spawnGroundUnits, spawnSmoke } from "../server/server"; import { aircraftDatabase } from "../units/aircraftdatabase"; import { groundUnitsDatabase } from "../units/groundunitsdatabase"; import { ContextMenu } from "./contextmenu"; @@ -9,17 +9,6 @@ import { Switch } from "./switch"; import { Slider } from "./slider"; import { ftToM } from "../other/utils"; -export interface SpawnOptions { - role: string; - name: string; - latlng: LatLng; - coalition: string; - loadout?: string | null; - airbaseName?: string | null; - altitude?: number | null; - immediate?: boolean; -} - export class MapContextMenu extends ContextMenu { #coalitionSwitch: Switch; #aircraftRoleDropdown: Dropdown; @@ -28,7 +17,7 @@ export class MapContextMenu extends ContextMenu { #aircrafSpawnAltitudeSlider: Slider; #groundUnitRoleDropdown: Dropdown; #groundUnitTypeDropdown: Dropdown; - #spawnOptions: SpawnOptions = { role: "", name: "", latlng: new LatLng(0, 0), loadout: null, coalition: "blue", airbaseName: null, altitude: ftToM(20000) }; + #spawnOptions = { role: "", name: "", latlng: new LatLng(0, 0), coalition: "blue", loadout: "", airbaseName: "", altitude: ftToM(20000) }; constructor(id: string) { super(id); @@ -57,8 +46,8 @@ export class MapContextMenu extends ContextMenu { this.hide(); this.#spawnOptions.coalition = getActiveCoalition(); if (this.#spawnOptions) { - getMap().addTemporaryMarker(this.#spawnOptions); - spawnAircraft(this.#spawnOptions); + getMap().addTemporaryMarker(this.#spawnOptions.latlng, this.#spawnOptions.name, getActiveCoalition()); + spawnAircrafts([{unitName: this.#spawnOptions.name, latlng: this.#spawnOptions.latlng, loadout: this.#spawnOptions.loadout}], getActiveCoalition(), this.#spawnOptions.airbaseName, false); } }); @@ -66,8 +55,8 @@ export class MapContextMenu extends ContextMenu { this.hide(); this.#spawnOptions.coalition = getActiveCoalition(); if (this.#spawnOptions) { - getMap().addTemporaryMarker(this.#spawnOptions); - spawnGroundUnit(this.#spawnOptions); + getMap().addTemporaryMarker(this.#spawnOptions.latlng, this.#spawnOptions.name, getActiveCoalition()); + spawnGroundUnits([{unitName: this.#spawnOptions.name, latlng: this.#spawnOptions.latlng}], getActiveCoalition(), false); } }); @@ -86,7 +75,7 @@ export class MapContextMenu extends ContextMenu { } show(x: number, y: number, latlng: LatLng) { - this.#spawnOptions.airbaseName = null; + this.#spawnOptions.airbaseName = ""; super.show(x, y, latlng); this.#spawnOptions.latlng = latlng; this.showUpperBar(); diff --git a/client/src/index.ts b/client/src/index.ts index 933551cb..b86827f7 100644 --- a/client/src/index.ts +++ b/client/src/index.ts @@ -184,12 +184,12 @@ function setupEvents() { const form = document.querySelector("#splash-content")?.querySelector("#authentication-form"); const username = ((form?.querySelector("#username"))).value; const password = ((form?.querySelector("#password"))).value; - setCredentials(username, btoa("admin" + ":" + password)); + setCredentials(username, password); /* Start periodically requesting updates */ startUpdate(); - setConnectionStatus("connecting"); + setLoginStatus("connecting"); }) document.addEventListener("reloadPage", () => { @@ -259,8 +259,8 @@ export function getActiveCoalition() { return activeCoalition; } -export function setConnectionStatus(status: string) { - const el = document.querySelector("#connection-status") as HTMLElement; +export function setLoginStatus(status: string) { + const el = document.querySelector("#login-status") as HTMLElement; if (el) el.dataset["status"] = status; } diff --git a/client/src/map/map.ts b/client/src/map/map.ts index 18c74d99..06cc58c2 100644 --- a/client/src/map/map.ts +++ b/client/src/map/map.ts @@ -1,7 +1,7 @@ import * as L from "leaflet" import { getUnitsManager } from ".."; import { BoxSelect } from "./boxselect"; -import { MapContextMenu, SpawnOptions } from "../controls/mapcontextmenu"; +import { MapContextMenu } from "../controls/mapcontextmenu"; import { UnitContextMenu } from "../controls/unitcontextmenu"; import { AirbaseContextMenu } from "../controls/airbasecontextmenu"; import { Dropdown } from "../controls/dropdown"; @@ -390,8 +390,8 @@ export class Map extends L.Map { } } - addTemporaryMarker(spawnOptions: SpawnOptions) { - var marker = new TemporaryUnitMarker(spawnOptions); + addTemporaryMarker(latlng: L.LatLng, name: string, coalition: string) { + var marker = new TemporaryUnitMarker(latlng, name, coalition); marker.addTo(this); this.#temporaryMarkers.push(marker); } diff --git a/client/src/map/temporaryunitmarker.ts b/client/src/map/temporaryunitmarker.ts index 7c266e0e..81136e6a 100644 --- a/client/src/map/temporaryunitmarker.ts +++ b/client/src/map/temporaryunitmarker.ts @@ -1,19 +1,20 @@ import { CustomMarker } from "./custommarker"; -import { SpawnOptions } from "../controls/mapcontextmenu"; -import { DivIcon } from "leaflet"; +import { DivIcon, LatLng } from "leaflet"; import { SVGInjector } from "@tanem/svg-injector"; import { getMarkerCategoryByName, getUnitDatabaseByCategory } from "../other/utils"; export class TemporaryUnitMarker extends CustomMarker { - #spawnOptions: SpawnOptions; + #name: string; + #coalition: string; - constructor(spawnOptions: SpawnOptions) { - super(spawnOptions.latlng, {interactive: false}); - this.#spawnOptions = spawnOptions; + constructor(latlng: LatLng, name: string, coalition: string) { + super(latlng, {interactive: false}); + this.#name = name; + this.#coalition = coalition; } createIcon() { - const category = getMarkerCategoryByName(this.#spawnOptions.name); + const category = getMarkerCategoryByName(this.#name); /* Set the icon */ var icon = new DivIcon({ @@ -26,7 +27,7 @@ export class TemporaryUnitMarker extends CustomMarker { var el = document.createElement("div"); el.classList.add("unit"); el.setAttribute("data-object", `unit-${category}`); - el.setAttribute("data-coalition", this.#spawnOptions.coalition); + el.setAttribute("data-coalition", this.#coalition); // Main icon var unitIcon = document.createElement("div"); @@ -42,7 +43,7 @@ export class TemporaryUnitMarker extends CustomMarker { if (category == "aircraft" || category == "helicopter") { var shortLabel = document.createElement("div"); shortLabel.classList.add("unit-short-label"); - shortLabel.innerText = getUnitDatabaseByCategory(category)?.getByName(this.#spawnOptions.name)?.shortLabel || ""; + shortLabel.innerText = getUnitDatabaseByCategory(category)?.getByName(this.#name)?.shortLabel || ""; el.append(shortLabel); } diff --git a/client/src/missionhandler/missionhandler.ts b/client/src/missionhandler/missionhandler.ts index 0c81884b..cac749f1 100644 --- a/client/src/missionhandler/missionhandler.ts +++ b/client/src/missionhandler/missionhandler.ts @@ -1,5 +1,5 @@ import { LatLng } from "leaflet"; -import { getInfoPopup, getMap } from ".."; +import { getInfoPopup, getMap, getUnitsManager } from ".."; import { Airbase } from "./airbase"; import { Bullseye } from "./bullseye"; @@ -72,6 +72,9 @@ export class MissionHandler { getInfoPopup().setText("Map set to " + this.#theatre); } + if ("visibilityMode" in data.mission) + getUnitsManager().setVisibilityMode(data.mission.visibilityMode); + if ("date" in data.mission) this.#date = data.mission.date; if ("elapsedTime" in data.mission) diff --git a/client/src/other/utils.ts b/client/src/other/utils.ts index 39f95722..0fd69911 100644 --- a/client/src/other/utils.ts +++ b/client/src/other/utils.ts @@ -35,6 +35,16 @@ export function distance(lat1: number, lon1: number, lat2: number, lon2: number) return d; } +export function coordinatesFromBearingAndDistance(lat1: number, lon1: number, brng: number, dist: number) { + const R = 6371e3; // metres + const φ1 = deg2rad(lat1); // φ, λ in radians + const λ1 = deg2rad(lon1); + const φ2 = Math.asin( Math.sin(φ1)*Math.cos(dist/R) + Math.cos(φ1)*Math.sin(dist/R)*Math.cos(brng) ); + const λ2 = λ1 + Math.atan2(Math.sin(brng)*Math.sin(dist/R)*Math.cos(φ1), Math.cos(dist/R)-Math.sin(φ1)*Math.sin(φ2)); + + return {lat: rad2deg(φ2), lng: rad2deg(λ2)}; +} + export function ConvertDDToDMS(D: number, lng: boolean) { var dir = D < 0 ? (lng ? "W" : "S") : lng ? "E" : "N"; var deg = 0 | (D < 0 ? (D = -D) : D); diff --git a/client/src/server/server.ts b/client/src/server/server.ts index 533053fe..fefee15f 100644 --- a/client/src/server/server.ts +++ b/client/src/server/server.ts @@ -1,6 +1,5 @@ import { LatLng } from 'leaflet'; -import { getConnectionStatusPanel, getInfoPopup, getMissionData, getUnitDataTable, getUnitsManager, setConnectionStatus } from '..'; -import { SpawnOptions } from '../controls/mapcontextmenu'; +import { getConnectionStatusPanel, getInfoPopup, getMissionData, getUnitDataTable, getUnitsManager, setLoginStatus } from '..'; import { GeneralSettings, Radio, TACAN } from '../@types/unit'; import { ROEs, emissionsCountermeasures, reactionsToThreat } from '../constants/constants'; @@ -16,7 +15,7 @@ const BULLSEYE_URI = "bullseyes"; const MISSION_URI = "mission"; var username = ""; -var credentials = ""; +var password = ""; var sessionHash: string | null = null; var lastUpdateTime = 0; @@ -26,12 +25,12 @@ export function toggleDemoEnabled() { demoEnabled = !demoEnabled; } -export function setCredentials(newUsername: string, newCredentials: string) { +export function setCredentials(newUsername: string, newPassword: string) { username = newUsername; - credentials = newCredentials; + password = newPassword; } -export function GET(callback: CallableFunction, uri: string, options?: { time?: number }) { +export function GET(callback: CallableFunction, uri: string, options?: { time?: number }, responseType?: string) { var xmlHttp = new XMLHttpRequest(); /* Assemble the request options string */ @@ -39,26 +38,31 @@ export function GET(callback: CallableFunction, uri: string, options?: { time?: if (options?.time != undefined) optionsString = `time=${options.time}`; + /* On the connection */ xmlHttp.open("GET", `${demoEnabled ? DEMO_ADDRESS : REST_ADDRESS}/${uri}${optionsString ? `?${optionsString}` : ''}`, true); - if (credentials) - xmlHttp.setRequestHeader("Authorization", "Basic " + credentials); - if (uri === UNITS_URI) - xmlHttp.responseType = "arraybuffer"; + /* If provided, set the credentials */ + if (username && password) + xmlHttp.setRequestHeader("Authorization", "Basic " + btoa(`${username}:${password}`)); + + /* If specified, set the response type */ + if (responseType) + xmlHttp.responseType = responseType as XMLHttpRequestResponseType; xmlHttp.onload = function (e) { if (xmlHttp.status == 200) { + /* Success */ setConnected(true); if (xmlHttp.responseType == 'arraybuffer') callback(xmlHttp.response); - else { - var data = JSON.parse(xmlHttp.responseText); - callback(data); - } + else + callback(JSON.parse(xmlHttp.responseText)); } else if (xmlHttp.status == 401) { + /* Bad credentials */ console.error("Incorrect username/password"); - setConnectionStatus("failed"); + setLoginStatus("failed"); } else { + /* Failure, probably disconnected */ setConnected(false); } }; @@ -73,8 +77,8 @@ export function POST(request: object, callback: CallableFunction) { var xmlHttp = new XMLHttpRequest(); xmlHttp.open("PUT", demoEnabled ? DEMO_ADDRESS : REST_ADDRESS); xmlHttp.setRequestHeader("Content-Type", "application/json"); - if (credentials) - xmlHttp.setRequestHeader("Authorization", "Basic " + credentials); + if (username && password) + xmlHttp.setRequestHeader("Authorization", "Basic " + btoa(`${username}:${password}`)); xmlHttp.onreadystatechange = () => { callback(); }; @@ -120,7 +124,7 @@ export function getMission(callback: CallableFunction) { } export function getUnits(callback: CallableFunction, refresh: boolean = false) { - GET(callback, `${UNITS_URI}`, { time: refresh ? 0 : lastUpdateTime }); + GET(callback, `${UNITS_URI}`, { time: refresh ? 0 : lastUpdateTime }, 'arraybuffer'); } export function addDestination(ID: number, path: any) { @@ -141,15 +145,15 @@ export function spawnExplosion(intensity: number, latlng: LatLng) { POST(data, () => { }); } -export function spawnGroundUnit(spawnOptions: SpawnOptions) { - var command = { "type": spawnOptions.name, "location": spawnOptions.latlng, "coalition": spawnOptions.coalition, "immediate": spawnOptions.immediate? true: false }; - var data = { "spawnGround": command } +export function spawnGroundUnits(units: any, coalition: string, immediate: boolean) { + var command = { "units": units, "coalition": coalition, "immediate": immediate }; + var data = { "spawnGroundUnits": command } POST(data, () => { }); } -export function spawnAircraft(spawnOptions: SpawnOptions) { - var command = { "type": spawnOptions.name, "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 } +export function spawnAircrafts(units: any, coalition: string, airbaseName: string, immediate: boolean) { + var command = { "units": units, "coalition": coalition, "airbaseName": airbaseName, "immediate": immediate }; + var data = { "spawnAircrafts": command } POST(data, () => { }); } @@ -319,11 +323,11 @@ export function startUpdate() { export function requestUpdate() { /* Main update rate = 250ms is minimum time, equal to server update time. */ - getUnits((buffer: ArrayBuffer) => { - if (!getPaused()) { + if (!getPaused()) { + getUnits((buffer: ArrayBuffer) => { getUnitsManager()?.update(buffer); - } - }, false); + }, false); + } window.setTimeout(() => requestUpdate(), getConnected() ? 250 : 1000); getConnectionStatusPanel()?.update(getConnected()); diff --git a/client/src/units/unit.ts b/client/src/units/unit.ts index 05b8772a..ee7be24f 100644 --- a/client/src/units/unit.ts +++ b/client/src/units/unit.ts @@ -1,12 +1,12 @@ -import { Marker, LatLng, Polyline, Icon, DivIcon, CircleMarker, Map } from 'leaflet'; +import { Marker, LatLng, Polyline, Icon, DivIcon, CircleMarker, Map, Point } from 'leaflet'; import { getMap, getUnitsManager } from '..'; -import { enumToCoalition, enumToEmissioNCountermeasure, getMarkerCategoryByName, enumToROE, enumToReactionToThreat, enumToState, getUnitDatabaseByCategory, mToFt, msToKnots, rad2deg } from '../other/utils'; +import { enumToCoalition, enumToEmissioNCountermeasure, getMarkerCategoryByName, enumToROE, enumToReactionToThreat, enumToState, getUnitDatabaseByCategory, mToFt, msToKnots, rad2deg, bearing, coordinatesFromBearingAndDistance, deg2rad } from '../other/utils'; import { addDestination, attackUnit, changeAltitude, changeSpeed, createFormation as setLeader, deleteUnit, getUnits, landAt, setAltitude, setReactionToThreat, setROE, setSpeed, refuel, setAdvacedOptions, followUnit, setEmissionsCountermeasures, setSpeedType, setAltitudeType, setOnOff, setFollowRoads, bombPoint, carpetBomb, bombBuilding, fireAtArea } from '../server/server'; import { CustomMarker } from '../map/custommarker'; import { SVGInjector } from '@tanem/svg-injector'; import { UnitDatabase } from './unitdatabase'; import { TargetMarker } from '../map/targetmarker'; -import { BOMBING, CARPET_BOMBING, DataIndexes, FIRE_AT_AREA, IDLE, MOVE_UNIT, ROEs, emissionsCountermeasures, reactionsToThreat, states } from '../constants/constants'; +import { BLUE_COMMANDER, BOMBING, CARPET_BOMBING, DLINK, DataIndexes, FIRE_AT_AREA, HIDE_ALL, IDLE, IRST, MOVE_UNIT, OPTIC, RADAR, RED_COMMANDER, ROEs, RWR, VISUAL, emissionsCountermeasures, reactionsToThreat, states } from '../constants/constants'; import { Ammo, Contact, GeneralSettings, Offset, Radio, TACAN, UnitIconOptions } from '../@types/unit'; import { DataExtractor } from './dataextractor'; @@ -88,6 +88,7 @@ export class Unit extends CustomMarker { #targetPositionPolyline: Polyline; #timer: number = 0; #hotgroup: number | null = null; + #detectionMethods: number[] = []; getAlive() {return this.#alive}; getHuman() {return this.#human}; @@ -296,7 +297,8 @@ export class Unit extends CustomMarker { showShortLabel: false, showFuel: false, showAmmo: false, - showSummary: false, + showSummary: true, + showCallsign: true, rotateToHeading: false } } @@ -309,7 +311,7 @@ export class Unit extends CustomMarker { setSelected(selected: boolean) { /* Only alive units can be selected. Some units are not selectable (weapons) */ - if ((this.#alive || !selected) && this.getSelectable() && this.getSelected() != selected) { + if ((this.#alive || !selected) && this.getSelectable() && this.getSelected() != selected && this.belongsToCommandedCoalition()) { this.#selected = selected; this.getElement()?.querySelector(`[data-object|="unit"]`)?.toggleAttribute("data-is-selected", selected); if (selected) { @@ -362,6 +364,16 @@ export class Unit extends CustomMarker { return Object.values(getUnitsManager().getUnits()).filter((unit: Unit) => { return unit != this && unit.#groupName === this.#groupName; }); } + belongsToCommandedCoalition() { + if (getUnitsManager().getVisibilityMode() === HIDE_ALL) + return false; + if (getUnitsManager().getVisibilityMode() === BLUE_COMMANDER && this.#coalition !== "blue") + return false; + if (getUnitsManager().getVisibilityMode() === RED_COMMANDER && this.#coalition !== "red") + return false; + return true; + } + /********************** Icon *************************/ createIcon(): void { /* Set the icon */ @@ -453,7 +465,7 @@ export class Unit extends CustomMarker { altitude.classList.add("unit-altitude"); var speed = document.createElement("div"); speed.classList.add("unit-speed"); - summary.appendChild(callsign); + if (this.getIconOptions().showCallsign) summary.appendChild(callsign); summary.appendChild(altitude); summary.appendChild(speed); el.appendChild(summary); @@ -468,12 +480,17 @@ export class Unit extends CustomMarker { const hiddenUnits = getUnitsManager().getHiddenTypes(); if (this.#human && hiddenUnits.includes("human")) hidden = true; - else if (this.#controlled == false && hiddenUnits.includes("dcs")) + if (this.#controlled == false && hiddenUnits.includes("dcs")) hidden = true; - else if (hiddenUnits.includes(this.getMarkerCategory())) + if (hiddenUnits.includes(this.getMarkerCategory())) hidden = true; - else if (hiddenUnits.includes(this.#coalition)) + if (hiddenUnits.includes(this.#coalition)) hidden = true; + if (getUnitsManager().getVisibilityMode() === HIDE_ALL) + hidden = true; + if (!this.belongsToCommandedCoalition() && this.#detectionMethods.length == 0) { + hidden = true; + } this.setHidden(hidden || !this.#alive); } @@ -495,6 +512,22 @@ export class Unit extends CustomMarker { return this.#hidden; } + setDetectionMethods(newDetectionMethods: number[]) { + if (!this.belongsToCommandedCoalition()) { + /* Check if the detection methods of this unit have changed */ + if (this.#detectionMethods.length !== newDetectionMethods.length || this.getDetectionMethods().some(value => !newDetectionMethods.includes(value))) { + /* Force a redraw of the unit to reflect the new status of the detection methods */ + this.setHidden(true); + this.#detectionMethods = newDetectionMethods; + this.updateVisibility(); + } + } + } + + getDetectionMethods() { + return this.#detectionMethods; + } + getLeader() { return getUnitsManager().getUnitByID(this.#leaderID); } @@ -912,19 +945,28 @@ export class Unit extends CustomMarker { var contactData = this.#contacts[index]; var contact = getUnitsManager().getUnitByID(contactData.ID) if (contact != null) { - var startLatLng = new LatLng(this.#position.lat, this.#position.lng) - var endLatLng = new LatLng(contact.#position.lat, contact.#position.lng) + var startLatLng = new LatLng(this.#position.lat, this.#position.lng); + var endLatLng: LatLng; + if (contactData.detectionMethod === RWR) { + var bearingToContact = bearing(this.#position.lat, this.#position.lng, contact.#position.lat, contact.#position.lng); + var startXY = getMap().latLngToContainerPoint(startLatLng); + var endX = startXY.x + 80 * Math.sin(deg2rad(bearingToContact)); + var endY = startXY.y - 80 * Math.cos(deg2rad(bearingToContact)); + endLatLng = getMap().containerPointToLatLng(new Point(endX, endY)); + } + else + endLatLng = new LatLng(contact.#position.lat, contact.#position.lng); var color; - if (contactData.detectionMethod === 1) + if (contactData.detectionMethod === VISUAL || contactData.detectionMethod === OPTIC) color = "#FF00FF"; - else if (contactData.detectionMethod === 4) + else if (contactData.detectionMethod === RADAR || contactData.detectionMethod === IRST) color = "#FFFF00"; - else if (contactData.detectionMethod === 16) + else if (contactData.detectionMethod === RWR) color = "#00FF00"; else color = "#FFFFFF"; - var contactPolyline = new Polyline([startLatLng, endLatLng], { color: color, weight: 3, opacity: 0.4, smoothFactor: 1, dashArray: "4, 8" }); + var contactPolyline = new Polyline([startLatLng, endLatLng], { color: color, weight: 3, opacity: 1, smoothFactor: 1, dashArray: "4, 8" }); contactPolyline.addTo(getMap()); this.#contactsPolylines.push(contactPolyline) } @@ -941,10 +983,11 @@ export class Unit extends CustomMarker { if (this.#targetPosition.lat != 0 && this.#targetPosition.lng != 0) { this.#drawtargetPosition(this.#targetPosition); } - else if (this.#targetID != 0 && getUnitsManager().getUnitByID(this.#targetID)) { - const position = getUnitsManager().getUnitByID(this.#targetID)?.getPosition(); - if (position) - this.#drawtargetPosition(position); + else if (this.#targetID != 0) { + const target = getUnitsManager().getUnitByID(this.#targetID); + if (target && getUnitsManager().getUnitDetectedMethods(target).some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))) { + this.#drawtargetPosition(target.getPosition()); + } } else this.#clearTarget(); @@ -971,14 +1014,15 @@ export class Unit extends CustomMarker { export class AirUnit extends Unit { getIconOptions() { return { - showState: true, - showVvi: true, - showHotgroup: true, - showUnitIcon: true, - showShortLabel: true, - showFuel: true, - showAmmo: true, - showSummary: true, + showState: this.belongsToCommandedCoalition(), + showVvi: (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))), + showHotgroup: this.belongsToCommandedCoalition(), + showUnitIcon: (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))), + showShortLabel: (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC].includes(value))), + showFuel: this.belongsToCommandedCoalition(), + showAmmo: this.belongsToCommandedCoalition(), + showSummary: (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))), + showCallsign: this.belongsToCommandedCoalition(), rotateToHeading: false }; } @@ -1011,14 +1055,15 @@ export class GroundUnit extends Unit { getIconOptions() { return { - showState: true, + showState: this.belongsToCommandedCoalition(), showVvi: false, - showHotgroup: true, - showUnitIcon: true, + showHotgroup: this.belongsToCommandedCoalition(), + showUnitIcon: (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))), showShortLabel: false, showFuel: false, showAmmo: false, showSummary: false, + showCallsign: this.belongsToCommandedCoalition(), rotateToHeading: false }; } @@ -1035,14 +1080,15 @@ export class NavyUnit extends Unit { getIconOptions() { return { - showState: true, + showState: this.belongsToCommandedCoalition(), showVvi: false, showHotgroup: true, - showUnitIcon: true, - showShortLabel: true, + showUnitIcon: (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))), + showShortLabel: false, showFuel: false, showAmmo: false, showSummary: false, + showCallsign: this.belongsToCommandedCoalition(), rotateToHeading: false }; } @@ -1063,11 +1109,12 @@ export class Weapon extends Unit { showState: false, showVvi: false, showHotgroup: false, - showUnitIcon: true, + showUnitIcon: this.belongsToCommandedCoalition(), showShortLabel: false, showFuel: false, showAmmo: false, showSummary: false, + showCallsign: false, rotateToHeading: true }; } diff --git a/client/src/units/unitsmanager.ts b/client/src/units/unitsmanager.ts index 5d222291..8980cefe 100644 --- a/client/src/units/unitsmanager.ts +++ b/client/src/units/unitsmanager.ts @@ -1,13 +1,14 @@ import { LatLng, LatLngBounds } from "leaflet"; import { getHotgroupPanel, getInfoPopup, getMap, getMissionHandler } from ".."; import { Unit } from "./unit"; -import { cloneUnit, setLastUpdateTime, spawnGroundUnit } from "../server/server"; +import { cloneUnit, setLastUpdateTime, spawnGroundUnits } from "../server/server"; import { deg2rad, keyEventWasInInput, latLngToMercator, mToFt, mercatorToLatLng, msToKnots, polygonArea, randomPointInPoly, randomUnitBlueprintByRole } from "../other/utils"; import { CoalitionArea } from "../map/coalitionarea"; import { Airbase } from "../missionhandler/airbase"; import { groundUnitsDatabase } from "./groundunitsdatabase"; -import { DataIndexes, IADSRoles, IDLE, MOVE_UNIT } from "../constants/constants"; +import { DataIndexes, HIDE_ALL, IADSRoles, IDLE, MOVE_UNIT } from "../constants/constants"; import { DataExtractor } from "./dataextractor"; +import { Contact } from "../@types/unit"; export class UnitsManager { #units: { [ID: number]: Unit }; @@ -15,6 +16,7 @@ export class UnitsManager { #selectionEventDisabled: boolean = false; #pasteDisabled: boolean = false; #hiddenTypes: string[] = []; + #visibilityMode: string = HIDE_ALL; constructor() { this.#units = {}; @@ -27,6 +29,8 @@ export class UnitsManager { document.addEventListener('deleteSelectedUnits', () => this.selectedUnitsDelete()); document.addEventListener('explodeSelectedUnits', () => this.selectedUnitsDelete(true)); document.addEventListener('keyup', (event) => this.#onKeyUp(event)); + document.addEventListener('exportToFile', () => this.exportToFile()); + document.addEventListener('importFromFile', () => this.importFromFile()); } getSelectableAircraft() { @@ -87,6 +91,12 @@ export class UnitsManager { this.#units[ID]?.setData(dataExtractor); } + for (let ID in this.#units) { + var unit = this.#units[ID]; + if (!unit.belongsToCommandedCoalition()) + unit.setDetectionMethods(this.getUnitDetectedMethods(unit)); + } + setLastUpdateTime(updateTime); } @@ -104,6 +114,24 @@ export class UnitsManager { return this.#hiddenTypes; } + setVisibilityMode(newVisibilityMode: string) { + if (newVisibilityMode !== this.#visibilityMode) { + document.dispatchEvent(new CustomEvent("visibilityModeChanged", { detail: this })); + const el = document.getElementById("visibiliy-mode"); + if (el) { + el.dataset.mode = newVisibilityMode; + el.textContent = newVisibilityMode.toUpperCase(); + } + this.#visibilityMode = newVisibilityMode; + for (let ID in this.#units) + this.#units[ID].updateVisibility(); + } + } + + getVisibilityMode() { + return this.#visibilityMode; + } + selectUnit(ID: number, deselectAllUnits: boolean = true) { if (deselectAllUnits) this.getSelectedUnits().filter((unit: Unit) => unit.ID !== ID).forEach((unit: Unit) => unit.setSelected(false)); @@ -479,6 +507,20 @@ export class UnitsManager { this.#showActionMessage(selectedUnits, `unit bombing point`); } + getUnitDetectedMethods(unit: Unit) { + var detectionMethods: number[] = []; + for (let idx in this.#units) { + if (this.#units[idx].getCoalition() !== "neutral" && this.#units[idx].getCoalition() != unit.getCoalition()) + { + this.#units[idx].getContacts().forEach((contact: Contact) => { + if (contact.ID == unit.ID && !detectionMethods.includes(contact.detectionMethod)) + detectionMethods.push(contact.detectionMethod); + }); + } + } + return detectionMethods; + } + /***********************************************/ copyUnits() { this.#copiedUnits = this.getSelectedUnits(); /* Can be applied to humans too */ @@ -516,13 +558,49 @@ export class UnitsManager { const probability = Math.pow(1 - minDistance / 50e3, 5) * IADSRoles[role]; if (Math.random() < probability){ const unitBlueprint = randomUnitBlueprintByRole(groundUnitsDatabase, role); - const spawnOptions = {role: role, latlng: latlng, name: unitBlueprint.name, coalition: coalitionArea.getCoalition(), immediate: true}; - spawnGroundUnit(spawnOptions); - getMap().addTemporaryMarker(spawnOptions); + spawnGroundUnits([{unitName: unitBlueprint.name, latlng: latlng}], coalitionArea.getCoalition(), true); + getMap().addTemporaryMarker(latlng, unitBlueprint.name, coalitionArea.getCoalition()); } } } + exportToFile() { + var unitsToExport: {[key: string]: any} = {}; + for (let ID in this.#units) { + var unit = this.#units[ID]; + if (!["Aircraft", "Helicopter"].includes(unit.getCategory())) { + if (unit.getGroupName() in unitsToExport) + unitsToExport[unit.getGroupName()].push(unit.getData()); + else + unitsToExport[unit.getGroupName()] = [unit.getData()]; + } + } + var a = document.createElement("a"); + var file = new Blob([JSON.stringify(unitsToExport)], {type: 'text/plain'}); + a.href = URL.createObjectURL(file); + a.download = 'export.json'; + a.click(); + } + + importFromFile() { + var input = document.createElement("input"); + input.type = "file"; + input.addEventListener("change", (e: any) => { + var file = e.target.files[0]; + if (!file) { + return; + } + var reader = new FileReader(); + reader.onload = function(e: any) { + var contents = e.target.result; + var groups = JSON.parse(contents); + + }; + reader.readAsText(file); + }) + input.click(); + } + /***********************************************/ #onKeyUp(event: KeyboardEvent) { if (!keyEventWasInInput(event) && event.key === "Delete" ) { diff --git a/client/views/other/dialogs.ejs b/client/views/other/dialogs.ejs index bbf5badb..49e25a05 100644 --- a/client/views/other/dialogs.ejs +++ b/client/views/other/dialogs.ejs @@ -12,7 +12,7 @@ -

+

+ +
ArcGIS Satellite diff --git a/olympus.json b/olympus.json index 3618c236..683f600f 100644 --- a/olympus.json +++ b/olympus.json @@ -4,6 +4,8 @@ "port": 30000 }, "authentication": { - "password": "password" + "gameMasterPassword": "password", + "blueCommanderPassword": "bluepassword", + "redCommanderPassword": "redpassword" } } diff --git a/scripts/OlympusCommand.lua b/scripts/OlympusCommand.lua index 7f086d6e..6ec47dae 100644 --- a/scripts/OlympusCommand.lua +++ b/scripts/OlympusCommand.lua @@ -303,105 +303,105 @@ function Olympus.explosion(intensity, lat, lng) trigger.action.explosion(mist.utils.makeVec3GL(coord.LLtoLO(lat, lng, 0)), intensity) end --- Spawns a single ground unit -function Olympus.spawnGroundUnit(coalition, unitType, lat, lng) - Olympus.debug("Olympus.spawnGroundUnit " .. coalition .. " " .. unitType .. " (" .. lat .. ", " .. lng ..")", 2) - local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(lat, lng, 0)) +-- Spawns a new unit or group +function Olympus.spawnUnits(spawnTable) + Olympus.debug("Olympus.spawnUnits " .. serializeTable(spawnTable), 2) local unitTable = {} + local route = {} + local category = nil - if Olympus.hasKey(templates, unitType) then - for idx, value in pairs(templates[unitType].units) do - unitTable[#unitTable + 1] = { - ["type"] = value.name, - ["x"] = spawnLocation.x + value.dx, - ["y"] = spawnLocation.z + value.dy, - ["playerCanDrive"] = true, - ["heading"] = 0, - ["skill"] = "High" - } - end - else - unitTable = - { - [1] = - { - ["type"] = unitType, - ["x"] = spawnLocation.x, - ["y"] = spawnLocation.z, - ["playerCanDrive"] = true, - ["heading"] = 0, - ["skill"] = "High" - }, - } + if spawnTable.category == 'Aircraft' then + unitTable = Olympus.generateAirUnitsTable(spawnTable.units) + route = Olympus.generateAirUnitsRoute(spawnTable) + category = 'airplane' + elseif spawnTable.category == 'GroundUnit' then + unitTable = Olympus.generateGroundUnitsTable(spawnTable.units) + category = 'vehicle' end - local countryID = Olympus.getCountryIDByCoalition(coalition) - + local countryID = Olympus.getCountryIDByCoalition(spawnTable.coalition) local vars = { units = unitTable, country = countryID, - category = 'vehicle', - name = "Ground-" .. Olympus.unitCounter, + category = category, + route = route, + name = "Olympus-" .. Olympus.unitCounter, } mist.dynAdd(vars) + Olympus.unitCounter = Olympus.unitCounter + 1 - Olympus.debug("Olympus.spawnGround completed succesfully", 2) -end + Olympus.debug("Olympus.spawnUnits completed succesfully", 2) +end --- Spawns a single aircraft. Spawn options are: --- payloadName: a string, one of the names defined in unitPayloads.lua. Must be compatible with the unitType --- airbaseName: a string, if present the aircraft will spawn on the ground of the selected airbase --- payload: a table, if present the unit will receive this specific payload. Overrides payloadName -function Olympus.spawnAircraft(coalition, unitType, lat, lng, alt, spawnOptions) - local payloadName = spawnOptions["payloadName"] - local airbaseName = spawnOptions["airbaseName"] - local payload = spawnOptions["payload"] - - Olympus.debug("Olympus.spawnAircraft " .. coalition .. " " .. unitType .. " (" .. lat .. ", " .. lng ..", " .. alt .. ")", 2) - local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(lat, lng, 0)) - - if payload == nil then - if payloadName and payloadName ~= "" and Olympus.unitPayloads[unitType][payloadName] then - payload = Olympus.unitPayloads[unitType][payloadName] +-- Generates ground units table, either single or from template +function Olympus.generateGroundUnitsTable(units) + for idx, unit in pairs(units) do + local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(unit.lat, unit.lng, 0)) + local unitTable = {} + if Olympus.hasKey(templates, unit.unitType) then + for idx, value in pairs(templates[unit.unitType].units) do + unitTable[#unitTable + 1] = { + ["type"] = value.name, + ["x"] = spawnLocation.x + value.dx, + ["y"] = spawnLocation.z + value.dy, + ["playerCanDrive"] = true, + ["heading"] = 0, + ["skill"] = "High" + } + end else - payload = {} + unitTable[#unitTable + 1] = + { + ["type"] = unit.unitType, + ["x"] = unit.x, + ["y"] = unit.z, + ["playerCanDrive"] = true, + ["heading"] = 0, + ["skill"] = "High" + } end end - - local countryID = Olympus.getCountryIDByCoalition(coalition) - local unitTable = - { - [1] = + return unitTable +end + +-- Generates unit table for a air unit. +function Olympus.generateAirUnitsTable(units) + local unitTable = {} + for idx, unit in pairs(units) do + local payloadName = unit.payloadName -- payloadName: a string, one of the names defined in unitPayloads.lua. Must be compatible with the unitType + local payload = unit.payload -- payload: a table, if present the unit will receive this specific payload. Overrides payloadName + + if payload == nil then + if payloadName and payloadName ~= "" and Olympus.unitPayloads[unit.unitType][payloadName] then + payload = Olympus.unitPayloads[unit.unitType][payloadName] + else + payload = {} + end + end + + local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(unit.lat, unit.lng, 0)) + unitTable[#unitTable + 1] = { - ["type"] = unitType, + ["type"] = unit.unitType, ["x"] = spawnLocation.x, ["y"] = spawnLocation.z, - ["alt"] = alt, - ["alt_type"] = "BARO", + ["alt"] = unit.alt, + ["alt_type"] = "BARO", ["skill"] = "Excellent", - ["payload"] = - { - ["pylons"] = payload, - ["fuel"] = 999999, - ["flare"] = 60, - ["ammo_type"] = 1, - ["chaff"] = 60, - ["gun"] = 100, - }, + ["payload"] = { ["pylons"] = payload, ["fuel"] = 999999, ["flare"] = 60, ["ammo_type"] = 1, ["chaff"] = 60, ["gun"] = 100, }, ["heading"] = 0, - ["callsign"] = - { - [1] = 1, - [2] = 1, - [3] = 1, - ["name"] = "Olympus" .. Olympus.unitCounter, - }, - ["name"] = "Olympus-" .. Olympus.unitCounter - }, - } + ["callsign"] = { [1] = 1, [2] = 1, [3] = 1, ["name"] = "Olympus" .. Olympus.unitCounter.. "-" .. #unitTable + 1 }, + ["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitTable + 1 + } + end + return unitTable + +function Olympus.generateAirUnitsRoute(spawnTable) + local airbaseName = spawnTable.airbaseName -- airbaseName: a string, if present the aircraft will spawn on the ground of the selected airbase + local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(spawnTable.lat, spawnTable.lng, 0)) -- If a airbase is provided the first waypoint is set as a From runway takeoff. local route = {} @@ -416,10 +416,9 @@ function Olympus.spawnAircraft(coalition, unitType, lat, lng, alt, spawnOptions) [1] = { ["action"] = "From Parking Area Hot", - ["task"] = - { - ["id"] = "ComboTask", - ["params"] = {["tasks"] = {},}, + ["tasks"] = { + [1] = {["number"] = 1, ["auto"] = true, ["id"] = "WrappedAction", ["enabled"] = true, ["params"] = {["action"] = {["id"] = "EPLRS", ["params"] = {["value"] = true}, }, }, }, + [2] = {["number"] = 2, ["auto"] = false, ["id"] = "Orbit", ["enabled"] = true, ["params"] = {["pattern"] = "Circle"}, }, }, ["type"] = "TakeOffParkingHot", ["ETA"] = 0, @@ -442,69 +441,18 @@ function Olympus.spawnAircraft(coalition, unitType, lat, lng, alt, spawnOptions) { ["alt"] = alt, ["alt_type"] = "BARO", - ["task"] = - { - ["id"] = "ComboTask", - ["params"] = - { - ["tasks"] = - { - [1] = - { - ["number"] = 1, - ["auto"] = true, - ["id"] = "WrappedAction", - ["enabled"] = true, - ["params"] = - { - ["action"] = - { - ["id"] = "EPLRS", - ["params"] = - { - ["value"] = true - }, - }, - }, - }, - [2] = - { - ["number"] = 2, - ["auto"] = false, - ["id"] = "Orbit", - ["enabled"] = true, - ["params"] = - { - ["pattern"] = "Circle" - }, - }, - }, - }, - }, + ["tasks"] = { + [1] = {["number"] = 1, ["auto"] = true, ["id"] = "WrappedAction", ["enabled"] = true, ["params"] = {["action"] = {["id"] = "EPLRS", ["params"] = {["value"] = true}, }, }, }, + [2] = {["number"] = 2, ["auto"] = false, ["id"] = "Orbit", ["enabled"] = true, ["params"] = {["pattern"] = "Circle"}, }, + }, ["type"] = "Turning Point", ["x"] = spawnLocation.x, ["y"] = spawnLocation.z, - }, -- end of [1] - }, -- end of ["points"] - } -- end of ["route"] + }, + }, + } end - - local vars = - { - units = unitTable, - country = countryID, - category = 'airplane', - name = "Olympus-" .. Olympus.unitCounter, - route = route, - task = 'CAP', - } - - local newGroup = mist.dynAdd(vars) - - -- Save the payload to be reused in case the unit is cloned. TODO: save by ID not by name (it works but I like consistency) - Olympus.payloadRegistry[vars.name] = payload - Olympus.unitCounter = Olympus.unitCounter + 1 - Olympus.debug("Olympus.spawnAir completed successfully", 2) + return route end -- Clones a unit by ID. Will clone the unit with the same original payload as the source unit. TODO: only works on Olympus unit not ME units. @@ -513,15 +461,21 @@ function Olympus.clone(ID, lat, lng, category) local unit = Olympus.getUnitByID(ID) if unit then local coalition = Olympus.getCoalitionByCoalitionID(unit:getCoalition()) - - if category == "Aircraft" then - local spawnOptions = { - payload = Olympus.payloadRegistry[unit:getName()] + -- TODO: understand category in this script + local spawnTable = { + coalition = coalition, + category = category, + units = { + [1] = { + lat = lat, + lng = lng, + alt = unit:getPoint().y, + unitType = unit:getTypeName(), + payload = Olympus.payloadRegistry[unit:getName()] + } } - Olympus.spawnAircraft(coalition, unit:getTypeName(), lat, lng, unit:getPoint().y, spawnOptions) - elseif category == "GroundUnit" then - Olympus.spawnGroundUnit(coalition, unit:getTypeName(), lat, lng) - end + } + Olympus.spawnUnits(spawnTable) end Olympus.debug("Olympus.clone completed successfully", 2) end @@ -765,4 +719,4 @@ end timer.scheduleFunction(Olympus.setMissionData, {}, timer.getTime() + 1) -Olympus.notify("OlympusCommand script " .. version .. " loaded successfully", 2, true) \ No newline at end of file +Olympus.notify("OlympusCommand script " .. version .. " loaded successfully", 2, true) diff --git a/src/core/include/commands.h b/src/core/include/commands.h index 5c1769e0..91d42162 100644 --- a/src/core/include/commands.h +++ b/src/core/include/commands.h @@ -151,13 +151,13 @@ private: }; /* Spawn ground unit command */ -class SpawnGroundUnit : public Command +class SpawnGroundUnits : public Command { public: - SpawnGroundUnit(string coalition, string unitType, Coords location, bool immediate) : + SpawnGroundUnits(string coalition, vector unitTypes, vector locations, bool immediate) : coalition(coalition), - unitType(unitType), - location(location), + unitTypes(unitTypes), + locations(locations), immediate(immediate) { priority = immediate? CommandPriority::IMMEDIATE: CommandPriority::LOW; @@ -167,20 +167,20 @@ public: private: const string coalition; - const string unitType; - const Coords location; + const vector unitTypes; + const vector locations; const bool immediate; }; /* Spawn air unit command */ -class SpawnAircraft : public Command +class SpawnAircrafts : public Command { public: - SpawnAircraft(string coalition, string unitType, Coords location, string payloadName, string airbaseName, bool immediate) : + SpawnAircrafts(string coalition, vector unitTypes, vector locations, vector payloadNames, string airbaseName, bool immediate) : coalition(coalition), - unitType(unitType), - location(location), - payloadName(payloadName), + unitTypes(unitTypes), + locations(locations), + payloadNames(payloadNames), airbaseName(airbaseName), immediate(immediate) { @@ -191,9 +191,9 @@ public: private: const string coalition; - const string unitType; - const Coords location; - const string payloadName; + const vector unitTypes; + const vector locations; + const vector payloadNames; const string airbaseName; const bool immediate; }; diff --git a/src/core/include/server.h b/src/core/include/server.h index 6880f7b4..2bcddddd 100644 --- a/src/core/include/server.h +++ b/src/core/include/server.h @@ -24,10 +24,16 @@ private: void handle_request(http_request request, function action); void handle_put(http_request request); + string extractPassword(http_request& request); + void task(); atomic runListener; - string password = ""; + string gameMasterPassword = ""; + string blueCommanderPassword = ""; + string redCommanderPassword = ""; + string atcPassword = ""; + string observerPassword = ""; }; diff --git a/src/core/src/commands.cpp b/src/core/src/commands.cpp index fbd6da60..450158a0 100644 --- a/src/core/src/commands.cpp +++ b/src/core/src/commands.cpp @@ -37,38 +37,52 @@ string Smoke::getString(lua_State* L) return commandSS.str(); } -/* Spawn ground command */ -string SpawnGroundUnit::getString(lua_State* L) +/* Spawn ground units command */ +string SpawnGroundUnits::getString(lua_State* L) { + if (unitTypes.size() != locations.size()) return ""; + + std::ostringstream unitsSS; + unitsSS.precision(10); + for (int i = 0; i < unitTypes.size(); i++) { + unitsSS << "[" << i + 1 << "] = {" + << "unitType = " << "\"" << unitTypes[i] << "\"" << ", " + << "lat = " << locations[i].lat << ", " + << "lng = " << locations[i].lng << "}"; + } + std::ostringstream commandSS; commandSS.precision(10); - commandSS << "Olympus.spawnGroundUnit, " - << "\"" << coalition << "\"" << ", " - << "\"" << unitType << "\"" << ", " - << location.lat << ", " - << location.lng; + commandSS << "Olympus.spawnUnits, {" + << "category = " << "\"" << "GroundUnit" << "\"" << ", " + << "coalition = " << "\"" << coalition << "\"" << ", " + << "units = " << "\"" << unitsSS.str() << "\"" << "}"; return commandSS.str(); } -/* Spawn air command */ -string SpawnAircraft::getString(lua_State* L) +/* Spawn aircrafts command */ +string SpawnAircrafts::getString(lua_State* L) { - std::ostringstream optionsSS; - optionsSS.precision(10); - optionsSS << "{" - << "payloadName = \"" << payloadName << "\", " - << "airbaseName = \"" << airbaseName << "\", " - << "}"; + if (unitTypes.size() != locations.size() || unitTypes.size() != payloadNames.size()) return ""; + + std::ostringstream unitsSS; + unitsSS.precision(10); + for (int i = 0; i < unitTypes.size(); i++) { + unitsSS << "[" << i + 1 << "] = {" + << "unitType = " << "\"" << unitTypes[i] << "\"" << ", " + << "lat = " << locations[i].lat << ", " + << "lng = " << locations[i].lng << ", " + << "alt = " << locations[i].alt << ", " + << "payloadName = \"" << payloadNames[i] << "\", " << "}"; + } std::ostringstream commandSS; commandSS.precision(10); - commandSS << "Olympus.spawnAircraft, " - << "\"" << coalition << "\"" << ", " - << "\"" << unitType << "\"" << ", " - << location.lat << ", " - << location.lng << ", " - << location.alt << ", " - << optionsSS.str(); + commandSS << "Olympus.spawnUnits, {" + << "category = " << "\"" << "Aircraft" << "\"" << ", " + << "coalition = " << "\"" << coalition << "\"" << ", " + << "airbaseName = \"" << airbaseName << "\", " + << "units = " << "\"" << unitsSS.str() << "\"" << "}"; return commandSS.str(); } diff --git a/src/core/src/scheduler.cpp b/src/core/src/scheduler.cpp index 2346c1ea..799faea0 100644 --- a/src/core/src/scheduler.cpp +++ b/src/core/src/scheduler.cpp @@ -92,30 +92,49 @@ void Scheduler::handleRequest(string key, json::value value) Coords loc; loc.lat = lat; loc.lng = lng; command = dynamic_cast(new Smoke(color, loc)); } - else if (key.compare("spawnGround") == 0) + else if (key.compare("spawnGroundUnits") == 0) { bool immediate = value[L"immediate"].as_bool(); string coalition = to_string(value[L"coalition"]); - string type = to_string(value[L"type"]); - double lat = value[L"location"][L"lat"].as_double(); - double lng = value[L"location"][L"lng"].as_double(); - log("Spawning " + coalition + " ground unit of type " + type + " at (" + to_string(lat) + ", " + to_string(lng) + ")"); - Coords loc; loc.lat = lat; loc.lng = lng; - command = dynamic_cast(new SpawnGroundUnit(coalition, type, loc, immediate)); + + vector unitTypes; + vector locations; + for (auto unit : value[L"units"].as_array()) { + string unitType = to_string(unit[L"type"]); + double lat = unit[L"location"][L"lat"].as_double(); + double lng = unit[L"location"][L"lng"].as_double(); + Coords location; location.lat = lat; location.lng = lng; + log("Spawning " + coalition + " ground unit of type " + unitType + " at (" + to_string(lat) + ", " + to_string(lng) + ")"); + unitTypes.push_back(unitType); + locations.push_back(location); + } + + command = dynamic_cast(new SpawnGroundUnits(coalition, unitTypes, locations, immediate)); } - else if (key.compare("spawnAir") == 0) + else if (key.compare("spawnAircrafts") == 0) { bool immediate = value[L"immediate"].as_bool(); string coalition = to_string(value[L"coalition"]); - string type = to_string(value[L"type"]); - double lat = value[L"location"][L"lat"].as_double(); - double lng = value[L"location"][L"lng"].as_double(); - double altitude = value[L"altitude"].as_double(); - Coords loc; loc.lat = lat; loc.lng = lng; loc.alt = altitude; - string payloadName = to_string(value[L"payloadName"]); string airbaseName = to_string(value[L"airbaseName"]); - log("Spawning " + coalition + " air unit of type " + type + " with payload " + payloadName + " at (" + to_string(lat) + ", " + to_string(lng) + " " + airbaseName + ")"); - command = dynamic_cast(new SpawnAircraft(coalition, type, loc, payloadName, airbaseName, immediate)); + + vector unitTypes; + vector locations; + vector payloadNames; + for (auto unit : value[L"units"].as_array()) { + string unitType = to_string(unit[L"type"]); + double lat = unit[L"location"][L"lat"].as_double(); + double lng = unit[L"location"][L"lng"].as_double(); + double alt = value[L"altitude"].as_double(); + Coords location; location.lat = lat; location.lng = lng; location.alt = alt; + string payloadName = to_string(value[L"payloadName"]); + + log("Spawning " + coalition + " air unit unit of type " + unitType + " at (" + to_string(lat) + ", " + to_string(lng) + ")"); + unitTypes.push_back(unitType); + locations.push_back(location); + payloadNames.push_back(payloadName); + } + + command = dynamic_cast(new SpawnAircrafts(coalition, unitTypes, locations, payloadNames, airbaseName, immediate)); } else if (key.compare("attackUnit") == 0) { diff --git a/src/core/src/server.cpp b/src/core/src/server.cpp index 580b24d7..7a27bcec 100644 --- a/src/core/src/server.cpp +++ b/src/core/src/server.cpp @@ -72,8 +72,9 @@ void Server::handle_get(http_request request) milliseconds ms = duration_cast(system_clock::now().time_since_epoch()); http_response response(status_codes::OK); - string authorization = to_base64("admin:" + password); - if (password.length() == 0 || (request.headers().has(L"Authorization") && request.headers().find(L"Authorization")->second.compare(L"Basic " + to_wstring(authorization)) == 0)) + + string password = extractPassword(request); + if (password.compare(gameMasterPassword) == 0 || password.compare(blueCommanderPassword) == 0 || password.compare(redCommanderPassword) == 0) { std::exception_ptr eptr; try { @@ -114,9 +115,15 @@ void Server::handle_get(http_request request) answer[L"airbases"] = airbases; else if (URI.compare(BULLSEYE_URI) == 0) answer[L"bullseyes"] = bullseyes; - else if (URI.compare(MISSION_URI) == 0) + else if (URI.compare(MISSION_URI) == 0) { + if (password.compare(gameMasterPassword) == 0) + mission[L"visibilityMode"] = json::value(L"Game master"); + else if (password.compare(blueCommanderPassword) == 0) + mission[L"visibilityMode"] = json::value(L"Blue commander"); + else if (password.compare(redCommanderPassword) == 0) + mission[L"visibilityMode"] = json::value(L"Red commander"); answer[L"mission"] = mission; - + } answer[L"time"] = json::value::string(to_wstring(ms.count())); answer[L"sessionHash"] = json::value::string(to_wstring(sessionHash)); @@ -144,8 +151,10 @@ void Server::handle_get(http_request request) void Server::handle_request(http_request request, function action) { http_response response(status_codes::OK); - string authorization = to_base64("admin:" + password); - if (password.length() == 0 || (request.headers().has(L"Authorization") && request.headers().find(L"Authorization")->second.compare(L"Basic " + to_wstring(authorization)) == 0)) + + //TODO: limit what a user can do depending on the password + string password = extractPassword(request); + if (password.compare(gameMasterPassword) == 0 || password.compare(blueCommanderPassword) == 0 || password.compare(redCommanderPassword) == 0) { auto answer = json::value::object(); request.extract_json().then([&answer, &action](pplx::task task) @@ -200,6 +209,30 @@ void Server::handle_put(http_request request) }); } +string Server::extractPassword(http_request& request) { + if (request.headers().has(L"Authorization")) { + string authorization = to_string(request.headers().find(L"Authorization")->second); + string s = "Basic "; + string::size_type i = authorization.find(s); + + if (i != std::string::npos) + authorization.erase(i, s.length()); + else + return ""; + + string decoded = from_base64(authorization); + i = authorization.find(":"); + if (i != std::string::npos) + decoded.erase(0, i); + else + return ""; + + return decoded; + } + else + return ""; +} + void Server::task() { string address = REST_ADDRESS; @@ -225,7 +258,9 @@ void Server::task() if (config.is_object() && config.has_object_field(L"authentication") && config[L"authentication"].has_string_field(L"password")) { - password = to_string(config[L"authentication"][L"password"]); + gameMasterPassword = to_string(config[L"authentication"][L"gameMasterPassword"]); + blueCommanderPassword = to_string(config[L"authentication"][L"blueCommanderPassword"]); + redCommanderPassword = to_string(config[L"authentication"][L"redCommanderPassword"]); } else log("Error reading configuration file. No password set."); From a0763a6450695eac70e611edfc59973d29111494 Mon Sep 17 00:00:00 2001 From: Pax1601 Date: Sun, 9 Jul 2023 18:48:21 +0200 Subject: [PATCH 2/5] Minor tweaks and fixes --- .../public/stylesheets/other/contextmenus.css | 11 ++++ client/src/controls/airbasecontextmenu.ts | 3 +- .../src/controls/coalitionareacontextmenu.ts | 19 ++++-- client/src/controls/mapcontextmenu.ts | 66 +++++++++++++++++-- client/src/index.ts | 17 ++++- client/src/map/coalitionarea.ts | 9 ++- client/src/other/utils.ts | 2 +- client/src/panels/unitcontrolpanel.ts | 2 +- client/src/server/server.ts | 5 +- client/src/units/unit.ts | 27 +++++--- client/src/units/unitsmanager.ts | 33 ++++++---- client/views/other/contextmenus.ejs | 20 +++++- scripts/OlympusCommand.lua | 31 ++++----- src/core/include/commands.h | 6 +- src/core/src/commands.cpp | 10 +-- src/core/src/scheduler.cpp | 14 ++-- src/core/src/server.cpp | 15 ++--- src/core/src/unit.cpp | 6 +- src/core/src/unitsmanager.cpp | 9 ++- 19 files changed, 216 insertions(+), 89 deletions(-) diff --git a/client/public/stylesheets/other/contextmenus.css b/client/public/stylesheets/other/contextmenus.css index 99e8b35c..91a50f7f 100644 --- a/client/public/stylesheets/other/contextmenus.css +++ b/client/public/stylesheets/other/contextmenus.css @@ -197,6 +197,17 @@ padding: 0px 10px; } +.contextmenu-options-container { + display: flex; + align-items: center; + justify-content: space-between; + padding-left: 10px; +} + +.contextmenu-options-container>*:nth-child(2) { + width: 120px; +} + /* Unit context menu */ #unit-contextmenu { display: flex; diff --git a/client/src/controls/airbasecontextmenu.ts b/client/src/controls/airbasecontextmenu.ts index 5ee53166..c53bfb9a 100644 --- a/client/src/controls/airbasecontextmenu.ts +++ b/client/src/controls/airbasecontextmenu.ts @@ -50,7 +50,7 @@ export class AirbaseContextMenu extends ContextMenu { } setCoalition(coalition: string) { - (this.getContainer()?.querySelector("#spawn-airbase-aircraft-button")).dataset.activeCoalition = coalition; + (this.getContainer()?.querySelector("#spawn-airbase-aircraft-button")).dataset.coalition = coalition; } enableLandButton(enableLandButton: boolean) { @@ -62,6 +62,7 @@ export class AirbaseContextMenu extends ContextMenu { setActiveCoalition(this.#airbase.getCoalition()); getMap().showMapContextMenu({ originalEvent: { x: this.getX(), y: this.getY(), latlng: this.getLatLng() } }); getMap().getMapContextMenu().hideUpperBar(); + getMap().getMapContextMenu().hideAltitudeSlider(); getMap().getMapContextMenu().showSubMenu("aircraft"); getMap().getMapContextMenu().setAirbaseName(this.#airbase.getName()); getMap().getMapContextMenu().setLatLng(this.#airbase.getLatLng()); diff --git a/client/src/controls/coalitionareacontextmenu.ts b/client/src/controls/coalitionareacontextmenu.ts index dcb9bc23..5abdd79a 100644 --- a/client/src/controls/coalitionareacontextmenu.ts +++ b/client/src/controls/coalitionareacontextmenu.ts @@ -1,5 +1,6 @@ +import { LatLng } from "leaflet"; import { getMap, getUnitsManager } from ".."; -import { IADSRoles } from "../constants/constants"; +import { GAME_MASTER, IADSRoles } from "../constants/constants"; import { CoalitionArea } from "../map/coalitionarea"; import { ContextMenu } from "./contextmenu"; import { Dropdown } from "./dropdown"; @@ -75,6 +76,12 @@ export class CoalitionAreaContextMenu extends ContextMenu { this.hide(); } + show(x: number, y: number, latlng: LatLng) { + super.show(x, y, latlng); + if (getUnitsManager().getCommandMode() !== GAME_MASTER) + this.#coalitionSwitch.hide() + } + showSubMenu(type: string) { this.getContainer()?.querySelector("#iads-menu")?.classList.toggle("hide", type !== "iads"); this.getContainer()?.querySelector("#iads-button")?.classList.toggle("is-open", type === "iads"); @@ -104,9 +111,11 @@ export class CoalitionAreaContextMenu extends ContextMenu { } #onSwitchClick(value: boolean) { - this.getCoalitionArea()?.setCoalition(value ? "red" : "blue"); - this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { - element.setAttribute("data-coalition", this.getCoalitionArea()?.getCoalition()) - }); + if (getUnitsManager().getCommandMode() == GAME_MASTER) { + this.getCoalitionArea()?.setCoalition(value ? "red" : "blue"); + this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { + element.setAttribute("data-coalition", this.getCoalitionArea()?.getCoalition()) + }); + } } } \ No newline at end of file diff --git a/client/src/controls/mapcontextmenu.ts b/client/src/controls/mapcontextmenu.ts index 8db22841..0976f983 100644 --- a/client/src/controls/mapcontextmenu.ts +++ b/client/src/controls/mapcontextmenu.ts @@ -1,5 +1,5 @@ import { LatLng } from "leaflet"; -import { getActiveCoalition, getMap, setActiveCoalition } from ".."; +import { getActiveCoalition, getMap, getUnitsManager, setActiveCoalition } from ".."; import { spawnAircrafts, spawnExplosion, spawnGroundUnits, spawnSmoke } from "../server/server"; import { aircraftDatabase } from "../units/aircraftdatabase"; import { groundUnitsDatabase } from "../units/groundunitsdatabase"; @@ -8,16 +8,19 @@ import { Dropdown } from "./dropdown"; import { Switch } from "./switch"; import { Slider } from "./slider"; import { ftToM } from "../other/utils"; +import { GAME_MASTER } from "../constants/constants"; export class MapContextMenu extends ContextMenu { #coalitionSwitch: Switch; #aircraftRoleDropdown: Dropdown; #aircraftTypeDropdown: Dropdown; + #aircraftCountDropdown: Dropdown; #aircraftLoadoutDropdown: Dropdown; #aircrafSpawnAltitudeSlider: Slider; #groundUnitRoleDropdown: Dropdown; #groundUnitTypeDropdown: Dropdown; - #spawnOptions = { role: "", name: "", latlng: new LatLng(0, 0), coalition: "blue", loadout: "", airbaseName: "", altitude: ftToM(20000) }; + #groundCountDropdown: Dropdown; + #spawnOptions = { role: "", name: "", latlng: new LatLng(0, 0), coalition: "blue", loadout: "", airbaseName: "", altitude: ftToM(20000), count: 1 }; constructor(id: string) { super(id); @@ -27,6 +30,9 @@ export class MapContextMenu extends ContextMenu { this.#coalitionSwitch.getContainer()?.addEventListener("contextmenu", (e) => this.#onSwitchRightClick(e)); this.#aircraftRoleDropdown = new Dropdown("aircraft-role-options", (role: string) => this.#setAircraftRole(role)); this.#aircraftTypeDropdown = new Dropdown("aircraft-type-options", (type: string) => this.#setAircraftType(type)); + this.#aircraftCountDropdown = new Dropdown("aircraft-count-options", (type: string) => this.#setAircraftCount(type)); + this.#aircraftCountDropdown.setOptions(["1", "2", "3", "4"]); + this.#aircraftCountDropdown.setValue("1"); this.#aircraftLoadoutDropdown = new Dropdown("loadout-options", (loadout: string) => this.#setAircraftLoadout(loadout)); this.#aircrafSpawnAltitudeSlider = new Slider("aircraft-spawn-altitude-slider", 0, 50000, "ft", (value: number) => {this.#spawnOptions.altitude = ftToM(value);}); this.#aircrafSpawnAltitudeSlider.setIncrement(500); @@ -34,6 +40,11 @@ export class MapContextMenu extends ContextMenu { this.#aircrafSpawnAltitudeSlider.setActive(true); this.#groundUnitRoleDropdown = new Dropdown("ground-unit-role-options", (role: string) => this.#setGroundUnitRole(role)); this.#groundUnitTypeDropdown = new Dropdown("ground-unit-type-options", (type: string) => this.#setGroundUnitType(type)); + this.#groundCountDropdown = new Dropdown("ground-count-options", (type: string) => this.#setGroundCount(type)); + var groundCount = []; + for (let i = 1; i <= 10; i++) groundCount.push(String(i)); + this.#groundCountDropdown.setOptions(groundCount); + this.#groundCountDropdown.setValue("1"); document.addEventListener("mapContextMenuShow", (e: any) => { if (this.getVisibleSubMenu() !== e.detail.type) @@ -47,7 +58,12 @@ export class MapContextMenu extends ContextMenu { this.#spawnOptions.coalition = getActiveCoalition(); if (this.#spawnOptions) { getMap().addTemporaryMarker(this.#spawnOptions.latlng, this.#spawnOptions.name, getActiveCoalition()); - spawnAircrafts([{unitName: this.#spawnOptions.name, latlng: this.#spawnOptions.latlng, loadout: this.#spawnOptions.loadout}], getActiveCoalition(), this.#spawnOptions.airbaseName, false); + var unitTable = {unitType: this.#spawnOptions.name, location: this.#spawnOptions.latlng, altitude: this.#spawnOptions.altitude, loadout: this.#spawnOptions.loadout}; + var units = []; + for (let i = 1; i < parseInt(this.#aircraftCountDropdown.getValue()) + 1; i++) { + units.push(unitTable); + } + spawnAircrafts(units, getActiveCoalition(), this.#spawnOptions.airbaseName, false); } }); @@ -56,7 +72,13 @@ export class MapContextMenu extends ContextMenu { this.#spawnOptions.coalition = getActiveCoalition(); if (this.#spawnOptions) { getMap().addTemporaryMarker(this.#spawnOptions.latlng, this.#spawnOptions.name, getActiveCoalition()); - spawnGroundUnits([{unitName: this.#spawnOptions.name, latlng: this.#spawnOptions.latlng}], getActiveCoalition(), false); + var unitTable = {unitType: this.#spawnOptions.name, location: this.#spawnOptions.latlng}; + var units = []; + for (let i = 1; i < parseInt(this.#groundCountDropdown.getValue()) + 1; i++) { + units.push(JSON.parse(JSON.stringify(unitTable))); + unitTable.location.lat += 0.0001; + } + spawnGroundUnits(units, getActiveCoalition(), false); } }); @@ -70,7 +92,6 @@ export class MapContextMenu extends ContextMenu { spawnExplosion(e.detail.strength, this.getLatLng()); }); - this.hide(); } @@ -79,6 +100,19 @@ export class MapContextMenu extends ContextMenu { super.show(x, y, latlng); this.#spawnOptions.latlng = latlng; this.showUpperBar(); + + this.showAltitudeSlider(); + + this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { element.setAttribute("data-coalition", getActiveCoalition()) }); + if (getActiveCoalition() == "blue") + this.#coalitionSwitch.setValue(false); + else if (getActiveCoalition() == "red") + this.#coalitionSwitch.setValue(true); + else + this.#coalitionSwitch.setValue(undefined); + + if (getUnitsManager().getCommandMode() !== GAME_MASTER) + this.#coalitionSwitch.hide() } showSubMenu(type: string) { @@ -95,6 +129,8 @@ export class MapContextMenu extends ContextMenu { this.#resetAircraftType(); this.#resetGroundUnitRole(); this.#resetGroundUnitType(); + this.#aircraftCountDropdown.setValue("1"); + this.#groundCountDropdown.setValue("1"); this.clip(); this.setVisibleSubMenu(type); @@ -119,7 +155,6 @@ export class MapContextMenu extends ContextMenu { this.setVisibleSubMenu(null); } - showUpperBar() { this.getContainer()?.querySelector("#upper-bar")?.classList.toggle("hide", false); } @@ -128,6 +163,14 @@ export class MapContextMenu extends ContextMenu { this.getContainer()?.querySelector("#upper-bar")?.classList.toggle("hide", true); } + showAltitudeSlider() { + this.getContainer()?.querySelector("#aircraft-spawn-altitude-slider")?.classList.toggle("hide", false); + } + + hideAltitudeSlider() { + this.getContainer()?.querySelector("#aircraft-spawn-altitude-slider")?.classList.toggle("hide", true); + } + setAirbaseName(airbaseName: string) { this.#spawnOptions.airbaseName = airbaseName; } @@ -144,6 +187,7 @@ export class MapContextMenu extends ContextMenu { #onSwitchRightClick(e: any) { this.#coalitionSwitch.setValue(undefined); setActiveCoalition("neutral"); + this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { element.setAttribute("data-coalition", getActiveCoalition()) }); } /********* Aircraft spawn menu *********/ @@ -178,6 +222,11 @@ export class MapContextMenu extends ContextMenu { this.clip(); } + #setAircraftCount(count: string) { + this.#spawnOptions.count = parseInt(count); + this.clip(); + } + #resetAircraftType() { (this.getContainer()?.querySelector("#aircraft-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true; (this.getContainer()?.querySelector("#loadout-list")).replaceChildren(); @@ -236,6 +285,11 @@ export class MapContextMenu extends ContextMenu { this.clip(); } + #setGroundCount(count: string) { + this.#spawnOptions.count = parseInt(count); + this.clip(); + } + #resetGroundUnitType() { (this.getContainer()?.querySelector("#ground-unit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true; this.clip(); diff --git a/client/src/index.ts b/client/src/index.ts index b86827f7..47742664 100644 --- a/client/src/index.ts +++ b/client/src/index.ts @@ -16,6 +16,7 @@ import { Popup } from "./popups/popup"; import { Dropdown } from "./controls/dropdown"; import { HotgroupPanel } from "./panels/hotgrouppanel"; import { SVGInjector } from "@tanem/svg-injector"; +import { BLUE_COMMANDER, GAME_MASTER, RED_COMMANDER } from "./constants/constants"; var map: Map; @@ -44,8 +45,8 @@ function setup() { featureSwitches = new FeatureSwitches(); /* Initialize base functionalitites */ - map = new Map('map-container'); unitsManager = new UnitsManager(); + map = new Map('map-container'); missionHandler = new MissionHandler(); /* Panels */ @@ -252,11 +253,21 @@ export function getHotgroupPanel() { } export function setActiveCoalition(newActiveCoalition: string) { - activeCoalition = newActiveCoalition; + if (getUnitsManager().getCommandMode() == GAME_MASTER) + activeCoalition = newActiveCoalition; } export function getActiveCoalition() { - return activeCoalition; + if (getUnitsManager().getCommandMode() == GAME_MASTER) + return activeCoalition; + else { + if (getUnitsManager().getCommandMode() == BLUE_COMMANDER) + return "blue"; + else if (getUnitsManager().getCommandMode() == RED_COMMANDER) + return "red"; + else + return "neutral"; + } } export function setLoginStatus(status: string) { diff --git a/client/src/map/coalitionarea.ts b/client/src/map/coalitionarea.ts index 2e2ab0fe..27397061 100644 --- a/client/src/map/coalitionarea.ts +++ b/client/src/map/coalitionarea.ts @@ -1,7 +1,8 @@ import { DomUtil, LatLng, LatLngExpression, Map, Point, Polygon, PolylineOptions } from "leaflet"; -import { getMap } from ".."; +import { getMap, getUnitsManager } from ".."; import { CoalitionAreaHandle } from "./coalitionareahandle"; import { CoalitionAreaMiddleHandle } from "./coalitionareamiddlehandle"; +import { BLUE_COMMANDER, RED_COMMANDER } from "../constants/constants"; export class CoalitionArea extends Polygon { #coalition: string = "blue"; @@ -19,7 +20,11 @@ export class CoalitionArea extends Polygon { super(latlngs, options); this.#setColors(); this.#registerCallbacks(); - + + if (getUnitsManager().getCommandMode() == BLUE_COMMANDER) + this.setCoalition("blue"); + else if (getUnitsManager().getCommandMode() == RED_COMMANDER) + this.setCoalition("red"); } setCoalition(coalition: string) { diff --git a/client/src/other/utils.ts b/client/src/other/utils.ts index 0fd69911..b8882a7c 100644 --- a/client/src/other/utils.ts +++ b/client/src/other/utils.ts @@ -247,7 +247,7 @@ export function getMarkerCategoryByName(name: string) { return (role?.includes("SAM")) ? "groundunit-sam" : "groundunit-other"; } else - return ""; // TODO add other unit types + return "groundunit-other"; // TODO add other unit types } export function getUnitDatabaseByCategory(category: string) { diff --git a/client/src/panels/unitcontrolpanel.ts b/client/src/panels/unitcontrolpanel.ts index 1abf45ef..582f7ddd 100644 --- a/client/src/panels/unitcontrolpanel.ts +++ b/client/src/panels/unitcontrolpanel.ts @@ -36,7 +36,7 @@ export class UnitControlPanel extends Panel { /* Option buttons */ // Reversing the ROEs so that the least "aggressive" option is always on the left this.#optionButtons["ROE"] = ROEs.slice(0).reverse().map((option: string, index: number) => { - return this.#createOptionButton(option, `roe/${option.toLowerCase()}.svg`, ROEDescriptions[index], () => { getUnitsManager().selectedUnitsSetROE(option); }); + return this.#createOptionButton(option, `roe/${option.toLowerCase()}.svg`, ROEDescriptions.slice(0).reverse()[index], () => { getUnitsManager().selectedUnitsSetROE(option); }); }); this.#optionButtons["reactionToThreat"] = reactionsToThreat.map((option: string, index: number) => { diff --git a/client/src/server/server.ts b/client/src/server/server.ts index fefee15f..d77df67d 100644 --- a/client/src/server/server.ts +++ b/client/src/server/server.ts @@ -324,9 +324,7 @@ export function startUpdate() { export function requestUpdate() { /* Main update rate = 250ms is minimum time, equal to server update time. */ if (!getPaused()) { - getUnits((buffer: ArrayBuffer) => { - getUnitsManager()?.update(buffer); - }, false); + getUnits((buffer: ArrayBuffer) => { getUnitsManager()?.update(buffer); }, false); } window.setTimeout(() => requestUpdate(), getConnected() ? 250 : 1000); @@ -335,7 +333,6 @@ export function requestUpdate() { export function requestRefresh() { /* Main refresh rate = 5000ms. */ - if (!getPaused()) { getAirbases((data: AirbasesData) => getMissionData()?.update(data)); getBullseye((data: BullseyesData) => getMissionData()?.update(data)); diff --git a/client/src/units/unit.ts b/client/src/units/unit.ts index ee7be24f..14e6f44e 100644 --- a/client/src/units/unit.ts +++ b/client/src/units/unit.ts @@ -6,7 +6,7 @@ import { CustomMarker } from '../map/custommarker'; import { SVGInjector } from '@tanem/svg-injector'; import { UnitDatabase } from './unitdatabase'; import { TargetMarker } from '../map/targetmarker'; -import { BLUE_COMMANDER, BOMBING, CARPET_BOMBING, DLINK, DataIndexes, FIRE_AT_AREA, HIDE_ALL, IDLE, IRST, MOVE_UNIT, OPTIC, RADAR, RED_COMMANDER, ROEs, RWR, VISUAL, emissionsCountermeasures, reactionsToThreat, states } from '../constants/constants'; +import { BLUE_COMMANDER, BOMBING, CARPET_BOMBING, DLINK, DataIndexes, FIRE_AT_AREA, GAME_MASTER, HIDE_ALL, IDLE, IRST, MOVE_UNIT, OPTIC, RADAR, RED_COMMANDER, ROEs, RWR, VISUAL, emissionsCountermeasures, reactionsToThreat, states } from '../constants/constants'; import { Ammo, Contact, GeneralSettings, Offset, Radio, TACAN, UnitIconOptions } from '../@types/unit'; import { DataExtractor } from './dataextractor'; @@ -223,6 +223,10 @@ export class Unit extends CustomMarker { if (updateMarker) this.#updateMarker(); + document.dispatchEvent(new CustomEvent("unitUpdated", { detail: this })); + } + + drawLines() { // TODO dont delete the polylines of the detected units this.#clearContacts(); if (this.getSelected()) { @@ -234,8 +238,6 @@ export class Unit extends CustomMarker { this.#clearPath(); this.#clearTarget(); } - - document.dispatchEvent(new CustomEvent("unitUpdated", { detail: this })); } getData() { @@ -365,11 +367,11 @@ export class Unit extends CustomMarker { } belongsToCommandedCoalition() { - if (getUnitsManager().getVisibilityMode() === HIDE_ALL) + if (getUnitsManager().getCommandMode() === HIDE_ALL) return false; - if (getUnitsManager().getVisibilityMode() === BLUE_COMMANDER && this.#coalition !== "blue") + if (getUnitsManager().getCommandMode() === BLUE_COMMANDER && this.#coalition !== "blue") return false; - if (getUnitsManager().getVisibilityMode() === RED_COMMANDER && this.#coalition !== "red") + if (getUnitsManager().getCommandMode() === RED_COMMANDER && this.#coalition !== "red") return false; return true; } @@ -486,7 +488,7 @@ export class Unit extends CustomMarker { hidden = true; if (hiddenUnits.includes(this.#coalition)) hidden = true; - if (getUnitsManager().getVisibilityMode() === HIDE_ALL) + if (getUnitsManager().getCommandMode() === HIDE_ALL) hidden = true; if (!this.belongsToCommandedCoalition() && this.#detectionMethods.length == 0) { hidden = true; @@ -791,7 +793,8 @@ export class Unit extends CustomMarker { this.updateVisibility(); /* Draw the minimap marker */ - if (this.#alive) { + var drawMiniMapMarker = (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))); + if (this.#alive && drawMiniMapMarker) { if (this.#miniMapMarker == null) { this.#miniMapMarker = new CircleMarker(new LatLng(this.#position.lat, this.#position.lng), { radius: 0.5 }); if (this.#coalition == "neutral") @@ -985,7 +988,7 @@ export class Unit extends CustomMarker { } else if (this.#targetID != 0) { const target = getUnitsManager().getUnitByID(this.#targetID); - if (target && getUnitsManager().getUnitDetectedMethods(target).some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))) { + if (target && (getUnitsManager().getCommandMode() == GAME_MASTER || (this.belongsToCommandedCoalition() && getUnitsManager().getUnitDetectedMethods(target).some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))))) { this.#drawtargetPosition(target.getPosition()); } } @@ -1093,6 +1096,10 @@ export class NavyUnit extends Unit { }; } + getMarkerCategory() { + return "navyunit"; + } + getCategory() { return "NavyUnit"; } @@ -1109,7 +1116,7 @@ export class Weapon extends Unit { showState: false, showVvi: false, showHotgroup: false, - showUnitIcon: this.belongsToCommandedCoalition(), + showUnitIcon: true, showShortLabel: false, showFuel: false, showAmmo: false, diff --git a/client/src/units/unitsmanager.ts b/client/src/units/unitsmanager.ts index 8980cefe..355753bb 100644 --- a/client/src/units/unitsmanager.ts +++ b/client/src/units/unitsmanager.ts @@ -16,7 +16,7 @@ export class UnitsManager { #selectionEventDisabled: boolean = false; #pasteDisabled: boolean = false; #hiddenTypes: string[] = []; - #visibilityMode: string = HIDE_ALL; + #commandMode: string = HIDE_ALL; constructor() { this.#units = {}; @@ -75,7 +75,7 @@ export class UnitsManager { update(buffer: ArrayBuffer) { var dataExtractor = new DataExtractor(buffer); var updateTime = Number(dataExtractor.extractUInt64()); - + var requestRefresh = false; while (dataExtractor.getSeekPosition() < buffer.byteLength) { const ID = dataExtractor.extractUInt32(); if (!(ID in this.#units)) { @@ -85,7 +85,7 @@ export class UnitsManager { this.addUnit(ID, category); } else { - // TODO request a refresh since we must have missed some packets + requestRefresh = true; } } this.#units[ID]?.setData(dataExtractor); @@ -98,6 +98,10 @@ export class UnitsManager { } setLastUpdateTime(updateTime); + + for (let ID in this.#units) { + this.#units[ID].drawLines(); + }; } setHiddenType(key: string, value: boolean) { @@ -115,21 +119,21 @@ export class UnitsManager { } setVisibilityMode(newVisibilityMode: string) { - if (newVisibilityMode !== this.#visibilityMode) { + if (newVisibilityMode !== this.#commandMode) { document.dispatchEvent(new CustomEvent("visibilityModeChanged", { detail: this })); const el = document.getElementById("visibiliy-mode"); if (el) { el.dataset.mode = newVisibilityMode; el.textContent = newVisibilityMode.toUpperCase(); } - this.#visibilityMode = newVisibilityMode; + this.#commandMode = newVisibilityMode; for (let ID in this.#units) this.#units[ID].updateVisibility(); } } - getVisibilityMode() { - return this.#visibilityMode; + getCommandMode() { + return this.#commandMode; } selectUnit(ID: number, deselectAllUnits: boolean = true) { @@ -558,7 +562,7 @@ export class UnitsManager { const probability = Math.pow(1 - minDistance / 50e3, 5) * IADSRoles[role]; if (Math.random() < probability){ const unitBlueprint = randomUnitBlueprintByRole(groundUnitsDatabase, role); - spawnGroundUnits([{unitName: unitBlueprint.name, latlng: latlng}], coalitionArea.getCoalition(), true); + spawnGroundUnits([{unitType: unitBlueprint.name, location: latlng}], coalitionArea.getCoalition(), true); getMap().addTemporaryMarker(latlng, unitBlueprint.name, coalitionArea.getCoalition()); } } @@ -569,10 +573,12 @@ export class UnitsManager { for (let ID in this.#units) { var unit = this.#units[ID]; if (!["Aircraft", "Helicopter"].includes(unit.getCategory())) { + var data: any = unit.getData(); + data.category = unit.getCategory(); if (unit.getGroupName() in unitsToExport) - unitsToExport[unit.getGroupName()].push(unit.getData()); + unitsToExport[unit.getGroupName()].push(data); else - unitsToExport[unit.getGroupName()] = [unit.getData()]; + unitsToExport[unit.getGroupName()] = [data]; } } var a = document.createElement("a"); @@ -594,7 +600,12 @@ export class UnitsManager { reader.onload = function(e: any) { var contents = e.target.result; var groups = JSON.parse(contents); - + for (let groupName in groups) { + if (groupName !== "" && groups[groupName].length > 0 && groups[groupName].every((unit: any) => {return unit.category == "GroundUnit";})) { + var units = groups[groupName].map((unit: any) => {return {unitType: unit.name, location: unit.position}}); + spawnGroundUnits(units, groups[groupName][0].coalition, true); + } + } }; reader.readAsText(file); }) diff --git a/client/views/other/contextmenus.ejs b/client/views/other/contextmenus.ejs index eed3f719..ee41d2e4 100644 --- a/client/views/other/contextmenus.ejs +++ b/client/views/other/contextmenus.ejs @@ -38,6 +38,15 @@
+
+
Group members
+
+
+
+ +
+
+
Spawn altitude @@ -74,6 +83,15 @@
+
+
Group members
+
+
+
+ +
+
+
@@ -104,7 +122,7 @@

Parking available:

- +
diff --git a/scripts/OlympusCommand.lua b/scripts/OlympusCommand.lua index 6ec47dae..1c3ecd59 100644 --- a/scripts/OlympusCommand.lua +++ b/scripts/OlympusCommand.lua @@ -1,6 +1,6 @@ local version = "v0.3.0-alpha" -local debug = FALSE +local debug = false Olympus.unitCounter = 1 Olympus.payloadRegistry = {} @@ -305,10 +305,10 @@ end -- Spawns a new unit or group function Olympus.spawnUnits(spawnTable) - Olympus.debug("Olympus.spawnUnits " .. serializeTable(spawnTable), 2) + Olympus.debug("Olympus.spawnUnits " .. Olympus.serializeTable(spawnTable), 2) - local unitTable = {} - local route = {} + local unitTable = nil + local route = nil local category = nil if spawnTable.category == 'Aircraft' then @@ -328,6 +328,7 @@ function Olympus.spawnUnits(spawnTable) category = category, route = route, name = "Olympus-" .. Olympus.unitCounter, + task = 'CAP' } mist.dynAdd(vars) @@ -337,16 +338,16 @@ end -- Generates ground units table, either single or from template function Olympus.generateGroundUnitsTable(units) + local unitTable = {} for idx, unit in pairs(units) do local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(unit.lat, unit.lng, 0)) - local unitTable = {} if Olympus.hasKey(templates, unit.unitType) then for idx, value in pairs(templates[unit.unitType].units) do - unitTable[#unitTable + 1] = { + unitTable[#unitTable + 1] = + { ["type"] = value.name, ["x"] = spawnLocation.x + value.dx, ["y"] = spawnLocation.z + value.dy, - ["playerCanDrive"] = true, ["heading"] = 0, ["skill"] = "High" } @@ -355,9 +356,8 @@ function Olympus.generateGroundUnitsTable(units) unitTable[#unitTable + 1] = { ["type"] = unit.unitType, - ["x"] = unit.x, - ["y"] = unit.z, - ["playerCanDrive"] = true, + ["x"] = spawnLocation.x, + ["y"] = spawnLocation.z, ["heading"] = 0, ["skill"] = "High" } @@ -371,12 +371,12 @@ end function Olympus.generateAirUnitsTable(units) local unitTable = {} for idx, unit in pairs(units) do - local payloadName = unit.payloadName -- payloadName: a string, one of the names defined in unitPayloads.lua. Must be compatible with the unitType - local payload = unit.payload -- payload: a table, if present the unit will receive this specific payload. Overrides payloadName + local loadout = unit.loadout -- loadout: a string, one of the names defined in unitPayloads.lua. Must be compatible with the unitType + local payload = unit.payload -- payload: a table, if present the unit will receive this specific payload. Overrides loadout if payload == nil then - if payloadName and payloadName ~= "" and Olympus.unitPayloads[unit.unitType][payloadName] then - payload = Olympus.unitPayloads[unit.unitType][payloadName] + if loadout and loadout ~= "" and Olympus.unitPayloads[unit.unitType][loadout] then + payload = Olympus.unitPayloads[unit.unitType][loadout] else payload = {} end @@ -398,10 +398,11 @@ function Olympus.generateAirUnitsTable(units) } end return unitTable +end function Olympus.generateAirUnitsRoute(spawnTable) local airbaseName = spawnTable.airbaseName -- airbaseName: a string, if present the aircraft will spawn on the ground of the selected airbase - local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(spawnTable.lat, spawnTable.lng, 0)) + local spawnLocation = mist.utils.makeVec3GL(coord.LLtoLO(spawnTable.units[1].lat, spawnTable.units[1].lng, 0)) -- If a airbase is provided the first waypoint is set as a From runway takeoff. local route = {} diff --git a/src/core/include/commands.h b/src/core/include/commands.h index 91d42162..bd819edb 100644 --- a/src/core/include/commands.h +++ b/src/core/include/commands.h @@ -176,11 +176,11 @@ private: class SpawnAircrafts : public Command { public: - SpawnAircrafts(string coalition, vector unitTypes, vector locations, vector payloadNames, string airbaseName, bool immediate) : + SpawnAircrafts(string coalition, vector unitTypes, vector locations, vector loadouts, string airbaseName, bool immediate) : coalition(coalition), unitTypes(unitTypes), locations(locations), - payloadNames(payloadNames), + loadouts(loadouts), airbaseName(airbaseName), immediate(immediate) { @@ -193,7 +193,7 @@ private: const string coalition; const vector unitTypes; const vector locations; - const vector payloadNames; + const vector loadouts; const string airbaseName; const bool immediate; }; diff --git a/src/core/src/commands.cpp b/src/core/src/commands.cpp index 450158a0..2d9bbb9a 100644 --- a/src/core/src/commands.cpp +++ b/src/core/src/commands.cpp @@ -48,7 +48,7 @@ string SpawnGroundUnits::getString(lua_State* L) unitsSS << "[" << i + 1 << "] = {" << "unitType = " << "\"" << unitTypes[i] << "\"" << ", " << "lat = " << locations[i].lat << ", " - << "lng = " << locations[i].lng << "}"; + << "lng = " << locations[i].lng << "},"; } std::ostringstream commandSS; @@ -56,14 +56,14 @@ string SpawnGroundUnits::getString(lua_State* L) commandSS << "Olympus.spawnUnits, {" << "category = " << "\"" << "GroundUnit" << "\"" << ", " << "coalition = " << "\"" << coalition << "\"" << ", " - << "units = " << "\"" << unitsSS.str() << "\"" << "}"; + << "units = " << "{" << unitsSS.str() << "}" << "}"; return commandSS.str(); } /* Spawn aircrafts command */ string SpawnAircrafts::getString(lua_State* L) { - if (unitTypes.size() != locations.size() || unitTypes.size() != payloadNames.size()) return ""; + if (unitTypes.size() != locations.size() || unitTypes.size() != loadouts.size()) return ""; std::ostringstream unitsSS; unitsSS.precision(10); @@ -73,7 +73,7 @@ string SpawnAircrafts::getString(lua_State* L) << "lat = " << locations[i].lat << ", " << "lng = " << locations[i].lng << ", " << "alt = " << locations[i].alt << ", " - << "payloadName = \"" << payloadNames[i] << "\", " << "}"; + << "loadout = \"" << loadouts[i] << "\"" << "},"; } std::ostringstream commandSS; @@ -82,7 +82,7 @@ string SpawnAircrafts::getString(lua_State* L) << "category = " << "\"" << "Aircraft" << "\"" << ", " << "coalition = " << "\"" << coalition << "\"" << ", " << "airbaseName = \"" << airbaseName << "\", " - << "units = " << "\"" << unitsSS.str() << "\"" << "}"; + << "units = " << "{" << unitsSS.str() << "}" << "}"; return commandSS.str(); } diff --git a/src/core/src/scheduler.cpp b/src/core/src/scheduler.cpp index 799faea0..739a21eb 100644 --- a/src/core/src/scheduler.cpp +++ b/src/core/src/scheduler.cpp @@ -100,7 +100,7 @@ void Scheduler::handleRequest(string key, json::value value) vector unitTypes; vector locations; for (auto unit : value[L"units"].as_array()) { - string unitType = to_string(unit[L"type"]); + string unitType = to_string(unit[L"unitType"]); double lat = unit[L"location"][L"lat"].as_double(); double lng = unit[L"location"][L"lng"].as_double(); Coords location; location.lat = lat; location.lng = lng; @@ -119,22 +119,22 @@ void Scheduler::handleRequest(string key, json::value value) vector unitTypes; vector locations; - vector payloadNames; + vector loadouts; for (auto unit : value[L"units"].as_array()) { - string unitType = to_string(unit[L"type"]); + string unitType = to_string(unit[L"unitType"]); double lat = unit[L"location"][L"lat"].as_double(); double lng = unit[L"location"][L"lng"].as_double(); - double alt = value[L"altitude"].as_double(); + double alt = unit[L"altitude"].as_double(); Coords location; location.lat = lat; location.lng = lng; location.alt = alt; - string payloadName = to_string(value[L"payloadName"]); + string loadout = to_string(unit[L"loadout"]); log("Spawning " + coalition + " air unit unit of type " + unitType + " at (" + to_string(lat) + ", " + to_string(lng) + ")"); unitTypes.push_back(unitType); locations.push_back(location); - payloadNames.push_back(payloadName); + loadouts.push_back(loadout); } - command = dynamic_cast(new SpawnAircrafts(coalition, unitTypes, locations, payloadNames, airbaseName, immediate)); + command = dynamic_cast(new SpawnAircrafts(coalition, unitTypes, locations, loadouts, airbaseName, immediate)); } else if (key.compare("attackUnit") == 0) { diff --git a/src/core/src/server.cpp b/src/core/src/server.cpp index 7a27bcec..1c300f38 100644 --- a/src/core/src/server.cpp +++ b/src/core/src/server.cpp @@ -221,9 +221,9 @@ string Server::extractPassword(http_request& request) { return ""; string decoded = from_base64(authorization); - i = authorization.find(":"); - if (i != std::string::npos) - decoded.erase(0, i); + i = decoded.find(":"); + if (i != string::npos && i+1 < decoded.length()) + decoded.erase(0, i+1); else return ""; @@ -255,12 +255,11 @@ void Server::task() else log("Error reading configuration file. Starting server on " + address); - if (config.is_object() && config.has_object_field(L"authentication") && - config[L"authentication"].has_string_field(L"password")) + if (config.is_object() && config.has_object_field(L"authentication")) { - gameMasterPassword = to_string(config[L"authentication"][L"gameMasterPassword"]); - blueCommanderPassword = to_string(config[L"authentication"][L"blueCommanderPassword"]); - redCommanderPassword = to_string(config[L"authentication"][L"redCommanderPassword"]); + if (config[L"authentication"].has_string_field(L"gameMasterPassword")) gameMasterPassword = to_string(config[L"authentication"][L"gameMasterPassword"]); + if (config[L"authentication"].has_string_field(L"blueCommanderPassword")) blueCommanderPassword = to_string(config[L"authentication"][L"blueCommanderPassword"]); + if (config[L"authentication"].has_string_field(L"redCommanderPassword")) redCommanderPassword = to_string(config[L"authentication"][L"redCommanderPassword"]); } else log("Error reading configuration file. No password set."); diff --git a/src/core/src/unit.cpp b/src/core/src/unit.cpp index dd912cf7..74b810cd 100644 --- a/src/core/src/unit.cpp +++ b/src/core/src/unit.cpp @@ -84,7 +84,7 @@ void Unit::runAILoop() { const bool isUnitLeaderOfAGroupWithOtherUnits = unitsManager->isUnitInGroup(this) && unitsManager->isUnitGroupLeader(this); if (!(isUnitAlive || isUnitLeaderOfAGroupWithOtherUnits)) return; - if (checkTaskFailed() && state != State::IDLE && State::LAND) + if (checkTaskFailed() && state != State::IDLE && state != State::LAND) setState(State::IDLE); AIloop(); @@ -188,10 +188,10 @@ bool Unit::hasFreshData(unsigned long long time) { void Unit::getData(stringstream& ss, unsigned long long time) { Unit* leader = this; - if (unitsManager->isUnitInGroup(this) && !unitsManager->isUnitGroupLeader(this)) + if (unitsManager->isUnitInGroup(this) && !unitsManager->isUnitGroupLeader(this)) leader = unitsManager->getGroupLeader(this); - if (!leader->hasFreshData(time)) return; + if (leader == nullptr || (!leader->hasFreshData(time) && !hasFreshData(time))) return; const unsigned char endOfData = DataIndex::endOfData; ss.write((const char*)&ID, sizeof(ID)); diff --git a/src/core/src/unitsmanager.cpp b/src/core/src/unitsmanager.cpp index 3c96ef9c..d3b181e0 100644 --- a/src/core/src/unitsmanager.cpp +++ b/src/core/src/unitsmanager.cpp @@ -39,6 +39,7 @@ bool UnitsManager::isUnitInGroup(Unit* unit) { if (unit != nullptr) { string groupName = unit->getGroupName(); + if (groupName.length() == 0) return false; for (auto const& p : units) { if (p.second->getGroupName().compare(groupName) == 0 && p.second != unit) @@ -50,8 +51,10 @@ bool UnitsManager::isUnitInGroup(Unit* unit) bool UnitsManager::isUnitGroupLeader(Unit* unit) { - if (unit != nullptr) - return unit == getGroupLeader(unit); + if (unit != nullptr) { + Unit* leader = getGroupLeader(unit); + return leader == nullptr? false: unit == getGroupLeader(unit); + } else return false; } @@ -61,7 +64,7 @@ Unit* UnitsManager::getGroupLeader(Unit* unit) { if (unit != nullptr) { string groupName = unit->getGroupName(); - + if (groupName.length() == 0) return nullptr; /* Find the first unit that has the same groupName */ for (auto const& p : units) { From 13e3b93e939f70888558d611334e2a03b6327b71 Mon Sep 17 00:00:00 2001 From: Pax1601 Date: Mon, 10 Jul 2023 16:58:54 +0200 Subject: [PATCH 3/5] More tweaks on frontend --- client/demo.js | 2 +- client/public/stylesheets/markers/units.css | 44 +- .../public/stylesheets/other/contextmenus.css | 72 +- .../olympus/images/buttons/other/back.svg | 10 +- .../olympus/images/buttons/other/delete.svg | 8 +- .../olympus/images/buttons/other/edit.svg | 1 + .../olympus/images/buttons/spawn/aircraft.svg | 50 +- .../images/buttons/spawn/explosion.svg | 6 +- .../olympus/images/buttons/spawn/ground.svg | 83 - .../images/buttons/spawn/groundunit.svg | 89 + .../images/buttons/spawn/helicopter.svg | 59 + .../olympus/images/buttons/spawn/navyunit.svg | 59 + .../olympus/images/buttons/spawn/sam.svg | 40 +- .../olympus/images/buttons/spawn/smoke.svg | 43 +- .../olympus/images/units/helicopter.svg | 17 + client/public/themes/olympus/theme.css | 27 +- client/src/@types/unitdatabase.d.ts | 6 +- client/src/constants/constants.ts | 1 - client/src/controls/airbasecontextmenu.ts | 2 +- client/src/controls/mapcontextmenu.ts | 348 +- client/src/index.ts | 4 +- client/src/map/coalitionarea.ts | 14 +- client/src/map/map.ts | 63 +- client/src/missionhandler/missionhandler.ts | 2 +- client/src/other/utils.ts | 10 +- client/src/panels/unitcontrolpanel.ts | 2 +- client/src/server/server.ts | 18 +- client/src/units/aircraftdatabase.ts | 4 +- client/src/units/groundunitdatabase.ts | 1535 +++++++ client/src/units/groundunitsdatabase.ts | 3671 ----------------- client/src/units/navalunitdatabase.ts | 1711 -------- client/src/units/navyunitdatabase.ts | 946 +++++ client/src/units/unit.ts | 15 +- client/src/units/unitdatabase.ts | 59 +- client/src/units/unitsmanager.ts | 4 +- client/views/other/contextmenus.ejs | 147 +- client/views/panels/navbar.ejs | 3 - 37 files changed, 3321 insertions(+), 5854 deletions(-) create mode 100644 client/public/themes/olympus/images/buttons/other/edit.svg delete mode 100644 client/public/themes/olympus/images/buttons/spawn/ground.svg create mode 100644 client/public/themes/olympus/images/buttons/spawn/groundunit.svg create mode 100644 client/public/themes/olympus/images/buttons/spawn/helicopter.svg create mode 100644 client/public/themes/olympus/images/buttons/spawn/navyunit.svg create mode 100644 client/public/themes/olympus/images/units/helicopter.svg create mode 100644 client/src/units/groundunitdatabase.ts delete mode 100644 client/src/units/groundunitsdatabase.ts delete mode 100644 client/src/units/navalunitdatabase.ts create mode 100644 client/src/units/navyunitdatabase.ts diff --git a/client/demo.js b/client/demo.js index 8c515a85..ca78a542 100644 --- a/client/demo.js +++ b/client/demo.js @@ -48,7 +48,7 @@ const DEMO_UNIT_DATA = { ammo: [{ quantity: 2, name: "A cool missile", guidance: 0, category: 0, missileCategory: 0 } ], contacts: [{ID: 1, detectionMethod: 16}], activePath: [ ] - }, ["4"]:{ category: "GroundUnit", alive: true, human: false, controlled: false, coalition: 1, country: 0, name: "M-60", unitName: "Cool guy 1-4", groupName: "Cool group 3", state: 1, task: "Being cool", + }, ["4"]:{ category: "Helicopter", alive: true, human: false, controlled: false, coalition: 1, country: 0, name: "AH-64D_BLK_II", unitName: "Cool guy 1-4", groupName: "Cool group 3", state: 1, task: "Being cool", hasTask: false, position: { lat: 37.1, lng: -116.1, alt: 1000 }, speed: 200, heading: 315 * Math.PI / 180, isTanker: false, isAWACS: false, onOff: true, followRoads: false, fuel: 50, desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0, formationOffset: { x: 0, y: 0, z: 0 }, diff --git a/client/public/stylesheets/markers/units.css b/client/public/stylesheets/markers/units.css index 4cbdcc90..d4f88333 100644 --- a/client/public/stylesheets/markers/units.css +++ b/client/public/stylesheets/markers/units.css @@ -14,11 +14,11 @@ background: var(--secondary-gunmetal-grey); display: flex; justify-self: center; - padding-bottom: calc((var(--unit-aircraft-width) / 2) + var(--unit-stroke-width)); + padding-bottom: calc((var(--unit-width) / 2) + var(--unit-stroke-width)); position: absolute; transform-origin: bottom; translate: 0 -50%; - width: var(--unit-aircraft-vvi-width); + width: var(--unit-vvi-width); } .unit-hotgroup { @@ -100,13 +100,13 @@ /*** Fuel indicator ***/ [data-object|="unit"] .unit-fuel { background: white; - border: var(--unit-aircraft-fuel-border-width) solid var(--secondary-dark-steel); + border: var(--unit-fuel-border-width) solid var(--secondary-dark-steel); border-radius: var(--border-radius-sm); display: none; - height: var(--unit-aircraft-fuel-height); + height: var(--unit-fuel-height); position: absolute; - translate: var(--unit-aircraft-fuel-x) var(--unit-aircraft-fuel-y); - width: var(--unit-aircraft-fuel-width); + translate: var(--unit-fuel-x) var(--unit-fuel-y); + width: var(--unit-fuel-width); } [data-object|="unit"] .unit-fuel-level { @@ -117,19 +117,19 @@ /*** Ammo indicator ***/ [data-object|="unit"] .unit-ammo { - column-gap: var(--unit-aircraft-ammo-spacing); + column-gap: var(--unit-ammo-spacing); display: none; height: fit-content; position: absolute; - translate: var(--unit-aircraft-ammo-x) var(--unit-aircraft-ammo-y); + translate: var(--unit-ammo-x) var(--unit-ammo-y); width: fit-content; } [data-object|="unit"] .unit-ammo>* { background-color: white; - border: var(--unit-aircraft-ammo-border-width) solid var(--secondary-dark-steel); + border: var(--unit-ammo-border-width) solid var(--secondary-dark-steel); border-radius: 50%; - padding: var(--unit-aircraft-ammo-radius); + padding: var(--unit-ammo-radius); } /*** Unit summary ***/ @@ -292,24 +292,24 @@ } /*** Dead unit ***/ -[data-object|="unit-aircraft"][data-is-dead] .unit-selected-spotlight, -[data-object|="unit-aircraft"][data-is-dead] .unit-short-label, -[data-object|="unit-aircraft"][data-is-dead] .unit-vvi, -[data-object|="unit-aircraft"][data-is-dead] .unit-hotgroup, -[data-object|="unit-aircraft"][data-is-dead] .unit-hotgroup-id, -[data-object|="unit-aircraft"][data-is-dead] .unit-state, -[data-object|="unit-aircraft"][data-is-dead] .unit-fuel, -[data-object|="unit-aircraft"][data-is-dead] .unit-ammo, -[data-object|="unit-aircraft"][data-is-dead]:hover .unit-fuel, -[data-object|="unit-aircraft"][data-is-dead]:hover .unit-ammo { +[data-object|="unit"][data-is-dead] .unit-selected-spotlight, +[data-object|="unit"][data-is-dead] .unit-short-label, +[data-object|="unit"][data-is-dead] .unit-vvi, +[data-object|="unit"][data-is-dead] .unit-hotgroup, +[data-object|="unit"][data-is-dead] .unit-hotgroup-id, +[data-object|="unit"][data-is-dead] .unit-state, +[data-object|="unit"][data-is-dead] .unit-fuel, +[data-object|="unit"][data-is-dead] .unit-ammo, +[data-object|="unit"][data-is-dead]:hover .unit-fuel, +[data-object|="unit"][data-is-dead]:hover .unit-ammo { display: none; } -[data-object|="unit-aircraft"][data-is-dead] .unit-summary>* { +[data-object|="unit"][data-is-dead] .unit-summary>* { display: none; } -[data-object|="unit-aircraft"][data-is-dead] .unit-summary .unit-callsign { +[data-object|="unit"][data-is-dead] .unit-summary .unit-callsign { display: block; } diff --git a/client/public/stylesheets/other/contextmenus.css b/client/public/stylesheets/other/contextmenus.css index 91a50f7f..6b691787 100644 --- a/client/public/stylesheets/other/contextmenus.css +++ b/client/public/stylesheets/other/contextmenus.css @@ -8,11 +8,10 @@ z-index: 9999; } -#aircraft-spawn-menu { - height: fit-content; -} - -#ground-unit-spawn-menu { +#aircraft-spawn-menu, +#helicopter-spawn-menu, +#groundunit-spawn-menu, +#navyunit-spawn-menu { height: fit-content; } @@ -35,35 +34,27 @@ width: 50px; } -#aircraft-spawn-menu .ol-select.is-open .ol-select-options { +#aircraft-spawn-menu .ol-select.is-open .ol-select-options, +#helicopter-spawn-menu .ol-select.is-open .ol-select-options { max-height: 300px; } -#aircraft-spawn-menu>button, -#ground-unit-spawn-menu>button, -#iads-menu>button { +.deploy-unit-button { text-align: center; width: 100%; } -#aircraft-spawn-button { - background-image: url("/resources/theme/images/buttons/spawn/aircraft.svg"); - background-size: 48px; +.upper-bar svg>* { + fill: white; } -#ground-ol-contexmenu-button { - background-image: url("/resources/theme/images/buttons/spawn/ground.svg"); - background-size: 48px; +.upper-bar svg { + width: 22px; + margin: 0px 5px; } -#smoke-spawn-button { - background-image: url("/resources/theme/images/buttons/spawn/smoke.svg"); - background-size: 48px; -} - -#explosion-spawn-button { - background-image: url("/resources/theme/images/buttons/spawn/explosion.svg"); - background-size: 48px; +.upper-bar button:nth-child(2) { + margin-left: auto; } [data-coalition="blue"]#active-coalition-label, @@ -117,7 +108,8 @@ content: "Create neutral unit"; } -#loadout-preview { +#aircraft-loadout-preview, +#helicopter-loadout-preview { align-content: space-between; align-items: center; column-gap: 20px; @@ -126,14 +118,16 @@ width: 100%; } -#loadout-list { +#aircaft-loadout-list, +#helicopter-loadout-list { align-content: center; display: flex; flex-direction: column; height: 100%; } -#unit-image { +#aircraft-unit-image, +#helicopter-unit-image { filter: invert(100%); height: 100px; margin-bottom: 10px; @@ -186,14 +180,16 @@ background-color: orange; } -#aircraft-spawn-menu .ol-slider-value { +#aircraft-spawn-menu .ol-slider-value, +#helicopter-spawn-menu .ol-slider-value { color: var(--accent-light-blue); cursor: pointer; font-size: 14px; font-weight: bold; } -#aircraft-spawn-altitude-slider { +#aircraft-spawn-altitude-slider, +#helicopter-spawn-altitude-slider { padding: 0px 10px; } @@ -371,26 +367,6 @@ width: 50px; } -#iads-button { - background-image: url("/resources/theme/images/buttons/spawn/sam.svg"); - background-size: 48px; -} - -#cap-button { - background-image: url("/resources/theme/images/buttons/spawn/aircraft.svg"); - background-size: 48px; -} - -#coalitionarea-back-button { - background-image: url("/resources/theme/images/buttons/other/back.svg"); - background-size: 48px; -} - -#coalitionarea-delete-button { - background-image: url("/resources/theme/images/buttons/other/delete.svg"); - background-size: 48px; -} - #coalition-area-contextmenu .ol-checkbox { align-self: flex-start; } diff --git a/client/public/themes/olympus/images/buttons/other/back.svg b/client/public/themes/olympus/images/buttons/other/back.svg index 52c98f94..0b86d7f1 100644 --- a/client/public/themes/olympus/images/buttons/other/back.svg +++ b/client/public/themes/olympus/images/buttons/other/back.svg @@ -23,9 +23,9 @@ inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" showgrid="false" - inkscape:zoom="26.15625" - inkscape:cx="20.587814" - inkscape:cy="20.109916" + inkscape:zoom="13.078125" + inkscape:cx="19.918757" + inkscape:cy="17.663082" inkscape:window-width="1920" inkscape:window-height="1017" inkscape:window-x="1912" @@ -34,8 +34,8 @@ inkscape:current-layer="svg4" /> diff --git a/client/public/themes/olympus/images/buttons/other/delete.svg b/client/public/themes/olympus/images/buttons/other/delete.svg index c290353d..68a91a1a 100644 --- a/client/public/themes/olympus/images/buttons/other/delete.svg +++ b/client/public/themes/olympus/images/buttons/other/delete.svg @@ -24,8 +24,8 @@ inkscape:deskcolor="#d1d1d1" showgrid="false" inkscape:zoom="13.078125" - inkscape:cx="27.794504" - inkscape:cy="19.192354" + inkscape:cx="27.870968" + inkscape:cy="20.415771" inkscape:window-width="1920" inkscape:window-height="1017" inkscape:window-x="1912" @@ -34,7 +34,7 @@ inkscape:current-layer="svg1940" /> + style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.0589579;stroke-opacity:1" /> diff --git a/client/public/themes/olympus/images/buttons/other/edit.svg b/client/public/themes/olympus/images/buttons/other/edit.svg new file mode 100644 index 00000000..a690992f --- /dev/null +++ b/client/public/themes/olympus/images/buttons/other/edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/client/public/themes/olympus/images/buttons/spawn/aircraft.svg b/client/public/themes/olympus/images/buttons/spawn/aircraft.svg index beb45a25..50b02231 100644 --- a/client/public/themes/olympus/images/buttons/spawn/aircraft.svg +++ b/client/public/themes/olympus/images/buttons/spawn/aircraft.svg @@ -1,20 +1,19 @@ + sodipodi:docname="aircraft.svg" + inkscape:version="1.2.2 (732a01da63, 2022-12-09)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/"> @@ -44,33 +43,18 @@ id="namedview10" showgrid="false" inkscape:zoom="18.782524" - inkscape:cx="26.073424" - inkscape:cy="15.446316" + inkscape:cx="26.114701" + inkscape:cy="15.493125" inkscape:window-x="1912" inkscape:window-y="-8" inkscape:window-maximized="1" - inkscape:current-layer="svg8" /> - + inkscape:current-layer="svg8" + inkscape:showpageshadow="2" + inkscape:pagecheckerboard="0" + inkscape:deskcolor="#d1d1d1" /> - + style="fill-opacity:1;stroke-width:1.81764" /> diff --git a/client/public/themes/olympus/images/buttons/spawn/explosion.svg b/client/public/themes/olympus/images/buttons/spawn/explosion.svg index 192784b0..b3803f61 100644 --- a/client/public/themes/olympus/images/buttons/spawn/explosion.svg +++ b/client/public/themes/olympus/images/buttons/spawn/explosion.svg @@ -24,7 +24,7 @@ inkscape:deskcolor="#d1d1d1" showgrid="false" inkscape:zoom="13.078125" - inkscape:cx="19.53644" + inkscape:cx="18.389486" inkscape:cy="12.157706" inkscape:window-width="1920" inkscape:window-height="1017" @@ -34,9 +34,9 @@ inkscape:current-layer="svg4" /> + style="fill-opacity:1;stroke-width:0.0537019" /> diff --git a/client/public/themes/olympus/images/buttons/spawn/ground.svg b/client/public/themes/olympus/images/buttons/spawn/ground.svg deleted file mode 100644 index f0d8bf1c..00000000 --- a/client/public/themes/olympus/images/buttons/spawn/ground.svg +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - diff --git a/client/public/themes/olympus/images/buttons/spawn/groundunit.svg b/client/public/themes/olympus/images/buttons/spawn/groundunit.svg new file mode 100644 index 00000000..28c22079 --- /dev/null +++ b/client/public/themes/olympus/images/buttons/spawn/groundunit.svg @@ -0,0 +1,89 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/client/public/themes/olympus/images/buttons/spawn/helicopter.svg b/client/public/themes/olympus/images/buttons/spawn/helicopter.svg new file mode 100644 index 00000000..585c9678 --- /dev/null +++ b/client/public/themes/olympus/images/buttons/spawn/helicopter.svg @@ -0,0 +1,59 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/client/public/themes/olympus/images/buttons/spawn/navyunit.svg b/client/public/themes/olympus/images/buttons/spawn/navyunit.svg new file mode 100644 index 00000000..b5d83ba2 --- /dev/null +++ b/client/public/themes/olympus/images/buttons/spawn/navyunit.svg @@ -0,0 +1,59 @@ + + + + + + image/svg+xml + + + + + + + + + diff --git a/client/public/themes/olympus/images/buttons/spawn/sam.svg b/client/public/themes/olympus/images/buttons/spawn/sam.svg index 0109de6b..bf91d6f5 100644 --- a/client/public/themes/olympus/images/buttons/spawn/sam.svg +++ b/client/public/themes/olympus/images/buttons/spawn/sam.svg @@ -1,9 +1,7 @@ - - + transform="matrix(1.6146667,0,0,1.6146667,-9.4139752,-8.9208162)" + style="stroke:none"> + style="stroke:none" /> + style="stroke:none" /> + transform="matrix(1.6146667,0,0,1.6146667,-9.4139752,-8.9208162)" + style="stroke:none"> + style="stroke:none" /> @@ -69,7 +45,6 @@ stroke-width="0" width="19" height="19" - fill="#ffffff" transform="translate(6.5,6.5)" id="rect7265" x="0" @@ -81,7 +56,6 @@ stroke-width="0" width="12.7515" height="12.7515" - fill="#ffffff" transform="rotate(-175.986,10.642671,7.6933413)" id="rect7268" x="0" diff --git a/client/public/themes/olympus/images/buttons/spawn/smoke.svg b/client/public/themes/olympus/images/buttons/spawn/smoke.svg index 468c0c95..b4ba491d 100644 --- a/client/public/themes/olympus/images/buttons/spawn/smoke.svg +++ b/client/public/themes/olympus/images/buttons/spawn/smoke.svg @@ -1,20 +1,19 @@ + sodipodi:docname="smoke.svg" + inkscape:version="1.2.2 (732a01da63, 2022-12-09)" + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" + xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" + xmlns="http://www.w3.org/2000/svg" + xmlns:svg="http://www.w3.org/2000/svg" + xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:cc="http://creativecommons.org/ns#" + xmlns:dc="http://purl.org/dc/elements/1.1/"> @@ -23,7 +22,7 @@ image/svg+xml - + @@ -42,25 +41,19 @@ id="namedview17" showgrid="false" inkscape:zoom="18.782524" - inkscape:cx="15.515904" - inkscape:cy="14.452888" + inkscape:cx="15.572987" + inkscape:cy="14.481547" inkscape:window-x="1912" inkscape:window-y="-8" inkscape:window-maximized="1" - inkscape:current-layer="svg15" /> + inkscape:current-layer="svg15" + inkscape:showpageshadow="2" + inkscape:pagecheckerboard="0" + inkscape:deskcolor="#d1d1d1" /> - + + + diff --git a/client/public/themes/olympus/theme.css b/client/public/themes/olympus/theme.css index 57f6bb3f..f816c274 100644 --- a/client/public/themes/olympus/theme.css +++ b/client/public/themes/olympus/theme.css @@ -65,18 +65,17 @@ --unit-width: 50px; /*** Air units ***/ - --unit-aircraft-ammo-gap: calc(2px + var(--unit-stroke-width)); - --unit-aircraft-ammo-border-radius: 50%; - --unit-aircraft-ammo-border-width: 2px; - --unit-aircraft-ammo-radius: 2px; - --unit-aircraft-ammo-spacing: 2px; - --unit-aircraft-ammo-x: 0px; - --unit-aircraft-ammo-y: 30px; - --unit-aircraft-fuel-border-width: 2px; - --unit-aircraft-fuel-height: 6px; - --unit-aircraft-fuel-width: 36px; - --unit-aircraft-fuel-x: 0px; - --unit-aircraft-fuel-y: 22px; - --unit-aircraft-height: 28px; - --unit-aircraft-vvi-width: 4px; + --unit-ammo-gap: calc(2px + var(--unit-stroke-width)); + --unit-ammo-border-radius: 50%; + --unit-ammo-border-width: 2px; + --unit-ammo-radius: 2px; + --unit-ammo-spacing: 2px; + --unit-ammo-x: 0px; + --unit-ammo-y: 30px; + --unit-fuel-border-width: 2px; + --unit-fuel-height: 6px; + --unit-fuel-width: 36px; + --unit-fuel-x: 0px; + --unit-fuel-y: 22px; + --unit-vvi-width: 4px; } \ No newline at end of file diff --git a/client/src/@types/unitdatabase.d.ts b/client/src/@types/unitdatabase.d.ts index 469a5fac..f76a38a5 100644 --- a/client/src/@types/unitdatabase.d.ts +++ b/client/src/@types/unitdatabase.d.ts @@ -15,10 +15,10 @@ interface LoadoutBlueprint { interface UnitBlueprint { name: string; era?: string[]; - type?: string; label: string; shortLabel: string; + type?: string; range?: string; - loadouts: LoadoutBlueprint[]; - filename: string; + loadouts?: LoadoutBlueprint[]; + filename?: string; } diff --git a/client/src/constants/constants.ts b/client/src/constants/constants.ts index b0c300f1..0584f11c 100644 --- a/client/src/constants/constants.ts +++ b/client/src/constants/constants.ts @@ -139,7 +139,6 @@ export const BOMBING = "Bombing"; export const CARPET_BOMBING = "Carpet bombing"; export const FIRE_AT_AREA = "Fire at area"; export const COALITIONAREA_DRAW_POLYGON = "Draw Coalition Area"; -export const COALITIONAREA_INTERACT = "Interact with Coalition Areas" export const visibilityControls: string[] = ["human", "dcs", "aircraft", "groundunit-sam", "groundunit-other", "navyunit", "airbase"]; export const visibilityControlsTootlips: string[] = ["Toggle human players visibility", "Toggle DCS controlled units visibility", "Toggle aircrafts visibility", "Toggle SAM units visibility", "Toggle ground units (not SAM) visibility", "Toggle navy units visibility", "Toggle airbases visibility"]; diff --git a/client/src/controls/airbasecontextmenu.ts b/client/src/controls/airbasecontextmenu.ts index c53bfb9a..7b9559d0 100644 --- a/client/src/controls/airbasecontextmenu.ts +++ b/client/src/controls/airbasecontextmenu.ts @@ -60,7 +60,7 @@ export class AirbaseContextMenu extends ContextMenu { showSpawnMenu() { if (this.#airbase != null) { setActiveCoalition(this.#airbase.getCoalition()); - getMap().showMapContextMenu({ originalEvent: { x: this.getX(), y: this.getY(), latlng: this.getLatLng() } }); + getMap().showMapContextMenu(this.getX(), this.getY(), this.getLatLng()); getMap().getMapContextMenu().hideUpperBar(); getMap().getMapContextMenu().hideAltitudeSlider(); getMap().getMapContextMenu().showSubMenu("aircraft"); diff --git a/client/src/controls/mapcontextmenu.ts b/client/src/controls/mapcontextmenu.ts index 0976f983..a87c66d2 100644 --- a/client/src/controls/mapcontextmenu.ts +++ b/client/src/controls/mapcontextmenu.ts @@ -1,26 +1,38 @@ import { LatLng } from "leaflet"; import { getActiveCoalition, getMap, getUnitsManager, setActiveCoalition } from ".."; -import { spawnAircrafts, spawnExplosion, spawnGroundUnits, spawnSmoke } from "../server/server"; +import { spawnAircrafts, spawnExplosion, spawnGroundUnits, spawnHelicopters, spawnNavyUnits, spawnSmoke } from "../server/server"; import { aircraftDatabase } from "../units/aircraftdatabase"; -import { groundUnitsDatabase } from "../units/groundunitsdatabase"; +import { groundUnitDatabase } from "../units/groundunitdatabase"; +import { helicopterDatabase } from "../units/helicopterdatabase"; import { ContextMenu } from "./contextmenu"; import { Dropdown } from "./dropdown"; import { Switch } from "./switch"; import { Slider } from "./slider"; import { ftToM } from "../other/utils"; import { GAME_MASTER } from "../constants/constants"; +import { navyUnitDatabase } from "../units/navyunitdatabase"; +import { CoalitionArea } from "../map/coalitionarea"; export class MapContextMenu extends ContextMenu { #coalitionSwitch: Switch; #aircraftRoleDropdown: Dropdown; - #aircraftTypeDropdown: Dropdown; + #aircraftNameDropdown: Dropdown; #aircraftCountDropdown: Dropdown; #aircraftLoadoutDropdown: Dropdown; - #aircrafSpawnAltitudeSlider: Slider; - #groundUnitRoleDropdown: Dropdown; + #aircraftSpawnAltitudeSlider: Slider; + #helicopterRoleDropdown: Dropdown; + #helicopterNameDropdown: Dropdown; + #helicopterCountDropdown: Dropdown; + #helicopterLoadoutDropdown: Dropdown; + #helicopterSpawnAltitudeSlider: Slider; #groundUnitTypeDropdown: Dropdown; - #groundCountDropdown: Dropdown; + #groundUnitNameDropdown: Dropdown; + #groundUnitCountDropdown: Dropdown; + #navyUnitTypeDropdown: Dropdown; + #navyUnitNameDropdown: Dropdown; + #navyUnitCountDropdown: Dropdown; #spawnOptions = { role: "", name: "", latlng: new LatLng(0, 0), coalition: "blue", loadout: "", airbaseName: "", altitude: ftToM(20000), count: 1 }; + #coalitionArea: CoalitionArea | null = null; constructor(id: string) { super(id); @@ -28,23 +40,47 @@ export class MapContextMenu extends ContextMenu { this.#coalitionSwitch = new Switch("coalition-switch", (value: boolean) => this.#onSwitchClick(value)); this.#coalitionSwitch.setValue(false); this.#coalitionSwitch.getContainer()?.addEventListener("contextmenu", (e) => this.#onSwitchRightClick(e)); + + /* Aircraft menu */ this.#aircraftRoleDropdown = new Dropdown("aircraft-role-options", (role: string) => this.#setAircraftRole(role)); - this.#aircraftTypeDropdown = new Dropdown("aircraft-type-options", (type: string) => this.#setAircraftType(type)); + this.#aircraftNameDropdown = new Dropdown("aircraft-type-options", (type: string) => this.#setAircraftName(type)); this.#aircraftCountDropdown = new Dropdown("aircraft-count-options", (type: string) => this.#setAircraftCount(type)); this.#aircraftCountDropdown.setOptions(["1", "2", "3", "4"]); this.#aircraftCountDropdown.setValue("1"); - this.#aircraftLoadoutDropdown = new Dropdown("loadout-options", (loadout: string) => this.#setAircraftLoadout(loadout)); - this.#aircrafSpawnAltitudeSlider = new Slider("aircraft-spawn-altitude-slider", 0, 50000, "ft", (value: number) => {this.#spawnOptions.altitude = ftToM(value);}); - this.#aircrafSpawnAltitudeSlider.setIncrement(500); - this.#aircrafSpawnAltitudeSlider.setValue(20000); - this.#aircrafSpawnAltitudeSlider.setActive(true); - this.#groundUnitRoleDropdown = new Dropdown("ground-unit-role-options", (role: string) => this.#setGroundUnitRole(role)); - this.#groundUnitTypeDropdown = new Dropdown("ground-unit-type-options", (type: string) => this.#setGroundUnitType(type)); - this.#groundCountDropdown = new Dropdown("ground-count-options", (type: string) => this.#setGroundCount(type)); - var groundCount = []; - for (let i = 1; i <= 10; i++) groundCount.push(String(i)); - this.#groundCountDropdown.setOptions(groundCount); - this.#groundCountDropdown.setValue("1"); + this.#aircraftLoadoutDropdown = new Dropdown("aircraft-loadout-options", (loadout: string) => this.#setAircraftLoadout(loadout)); + this.#aircraftSpawnAltitudeSlider = new Slider("aircraft-spawn-altitude-slider", 0, 50000, "ft", (value: number) => {this.#spawnOptions.altitude = ftToM(value);}); + this.#aircraftSpawnAltitudeSlider.setIncrement(500); + this.#aircraftSpawnAltitudeSlider.setValue(20000); + this.#aircraftSpawnAltitudeSlider.setActive(true); + + /* Helicopter menu */ + this.#helicopterRoleDropdown = new Dropdown("helicopter-role-options", (role: string) => this.#setHelicopterRole(role)); + this.#helicopterNameDropdown = new Dropdown("helicopter-type-options", (type: string) => this.#setHelicopterName(type)); + this.#helicopterCountDropdown = new Dropdown("helicopter-count-options", (type: string) => this.#setHelicopterCount(type)); + this.#helicopterCountDropdown.setOptions(["1", "2", "3", "4"]); + this.#helicopterCountDropdown.setValue("1"); + this.#helicopterLoadoutDropdown = new Dropdown("helicopter-loadout-options", (loadout: string) => this.#setHelicopterLoadout(loadout)); + this.#helicopterSpawnAltitudeSlider = new Slider("helicopter-spawn-altitude-slider", 0, 10000, "ft", (value: number) => {this.#spawnOptions.altitude = ftToM(value);}); + this.#helicopterSpawnAltitudeSlider.setIncrement(50); + this.#helicopterSpawnAltitudeSlider.setValue(5000); + this.#helicopterSpawnAltitudeSlider.setActive(true); + + var count = []; + for (let i = 1; i < 10; i++) count.push(String(i)); + + /* Ground unit menu */ + this.#groundUnitTypeDropdown = new Dropdown("groundunit-type-options", (type: string) => this.#setGroundUnitType(type)); + this.#groundUnitNameDropdown = new Dropdown("groundunit-name-options", (name: string) => this.#setGroundUnitName(name)); + this.#groundUnitCountDropdown = new Dropdown("groundunit-count-options", (count: string) => this.#setGroundUnitCount(count)); + this.#groundUnitCountDropdown.setOptions(count); + this.#groundUnitCountDropdown.setValue("1"); + + /* Navy unit menu */ + this.#navyUnitTypeDropdown = new Dropdown("navyunit-type-options", (type: string) => this.#setNavyUnitType(type)); + this.#navyUnitNameDropdown = new Dropdown("navyunit-name-options", (name: string) => this.#setNavyUnitName(name)); + this.#navyUnitCountDropdown = new Dropdown("navyunit-count-options", (count: string) => this.#setNavyUnitCount(count)); + this.#navyUnitCountDropdown.setOptions(count); + this.#navyUnitCountDropdown.setValue("1"); document.addEventListener("mapContextMenuShow", (e: any) => { if (this.getVisibleSubMenu() !== e.detail.type) @@ -67,6 +103,20 @@ export class MapContextMenu extends ContextMenu { } }); + document.addEventListener("contextMenuDeployHelicopter", () => { + this.hide(); + this.#spawnOptions.coalition = getActiveCoalition(); + if (this.#spawnOptions) { + getMap().addTemporaryMarker(this.#spawnOptions.latlng, this.#spawnOptions.name, getActiveCoalition()); + var unitTable = {unitType: this.#spawnOptions.name, location: this.#spawnOptions.latlng, altitude: this.#spawnOptions.altitude, loadout: this.#spawnOptions.loadout}; + var units = []; + for (let i = 1; i < parseInt(this.#helicopterCountDropdown.getValue()) + 1; i++) { + units.push(unitTable); + } + spawnHelicopters(units, getActiveCoalition(), this.#spawnOptions.airbaseName, false); + } + }); + document.addEventListener("contextMenuDeployGroundUnit", () => { this.hide(); this.#spawnOptions.coalition = getActiveCoalition(); @@ -74,7 +124,7 @@ export class MapContextMenu extends ContextMenu { getMap().addTemporaryMarker(this.#spawnOptions.latlng, this.#spawnOptions.name, getActiveCoalition()); var unitTable = {unitType: this.#spawnOptions.name, location: this.#spawnOptions.latlng}; var units = []; - for (let i = 1; i < parseInt(this.#groundCountDropdown.getValue()) + 1; i++) { + for (let i = 1; i < parseInt(this.#groundUnitCountDropdown.getValue()) + 1; i++) { units.push(JSON.parse(JSON.stringify(unitTable))); unitTable.location.lat += 0.0001; } @@ -82,6 +132,21 @@ export class MapContextMenu extends ContextMenu { } }); + document.addEventListener("contextMenuDeployNavyUnits", () => { + this.hide(); + this.#spawnOptions.coalition = getActiveCoalition(); + if (this.#spawnOptions) { + getMap().addTemporaryMarker(this.#spawnOptions.latlng, this.#spawnOptions.name, getActiveCoalition()); + var unitTable = {unitType: this.#spawnOptions.name, location: this.#spawnOptions.latlng}; + var units = []; + for (let i = 1; i < parseInt(this.#navyUnitCountDropdown.getValue()) + 1; i++) { + units.push(JSON.parse(JSON.stringify(unitTable))); + unitTable.location.lat += 0.0001; + } + spawnNavyUnits(units, getActiveCoalition(), false); + } + }); + document.addEventListener("contextMenuDeploySmoke", (e: any) => { this.hide(); spawnSmoke(e.detail.color, this.getLatLng()); @@ -91,6 +156,14 @@ export class MapContextMenu extends ContextMenu { this.hide(); spawnExplosion(e.detail.strength, this.getLatLng()); }); + + document.addEventListener("editCoalitionArea", (e: any) => { + this.hide(); + if (this.#coalitionArea) { + getMap().deselectAllCoalitionAreas(); + this.#coalitionArea.setSelected(true); + } + }); this.hide(); } @@ -113,24 +186,35 @@ export class MapContextMenu extends ContextMenu { if (getUnitsManager().getCommandMode() !== GAME_MASTER) this.#coalitionSwitch.hide() + + this.getContainer()?.querySelector("#coalition-area-button")?.classList.toggle("hide", true); } showSubMenu(type: string) { this.getContainer()?.querySelector("#aircraft-spawn-menu")?.classList.toggle("hide", type !== "aircraft"); this.getContainer()?.querySelector("#aircraft-spawn-button")?.classList.toggle("is-open", type === "aircraft"); - this.getContainer()?.querySelector("#ground-unit-spawn-menu")?.classList.toggle("hide", type !== "ground-unit"); - this.getContainer()?.querySelector("#ground-ol-contexmenu-button")?.classList.toggle("is-open", type === "ground-unit"); + this.getContainer()?.querySelector("#helicopter-spawn-menu")?.classList.toggle("hide", type !== "helicopter"); + this.getContainer()?.querySelector("#helicopter-spawn-button")?.classList.toggle("is-open", type === "helicopter"); + this.getContainer()?.querySelector("#groundunit-spawn-menu")?.classList.toggle("hide", type !== "groundunit"); + this.getContainer()?.querySelector("#groundunit-spawn-button")?.classList.toggle("is-open", type === "groundunit"); + this.getContainer()?.querySelector("#navyunit-spawn-menu")?.classList.toggle("hide", type !== "navyunit"); + this.getContainer()?.querySelector("#navyunit-spawn-button")?.classList.toggle("is-open", type === "navyunit"); this.getContainer()?.querySelector("#smoke-spawn-menu")?.classList.toggle("hide", type !== "smoke"); this.getContainer()?.querySelector("#smoke-spawn-button")?.classList.toggle("is-open", type === "smoke"); this.getContainer()?.querySelector("#explosion-menu")?.classList.toggle("hide", type !== "explosion"); this.getContainer()?.querySelector("#explosion-spawn-button")?.classList.toggle("is-open", type === "explosion"); this.#resetAircraftRole(); - this.#resetAircraftType(); - this.#resetGroundUnitRole(); + this.#resetAircraftName(); + this.#resetHelicopterRole(); + this.#resetHelicopterName(); this.#resetGroundUnitType(); + this.#resetGroundUnitName(); + this.#resetNavyUnitType(); + this.#resetNavyUnitName(); this.#aircraftCountDropdown.setValue("1"); - this.#groundCountDropdown.setValue("1"); + this.#helicopterCountDropdown.setValue("1"); + this.#groundUnitCountDropdown.setValue("1"); this.clip(); this.setVisibleSubMenu(type); @@ -139,28 +223,38 @@ export class MapContextMenu extends ContextMenu { hideSubMenus() { this.getContainer()?.querySelector("#aircraft-spawn-menu")?.classList.toggle("hide", true); this.getContainer()?.querySelector("#aircraft-spawn-button")?.classList.toggle("is-open", false); - this.getContainer()?.querySelector("#ground-unit-spawn-menu")?.classList.toggle("hide", true); - this.getContainer()?.querySelector("#ground-ol-contexmenu-button")?.classList.toggle("is-open", false); + this.getContainer()?.querySelector("#helicopter-spawn-menu")?.classList.toggle("hide", true); + this.getContainer()?.querySelector("#helicopter-spawn-button")?.classList.toggle("is-open", false); + this.getContainer()?.querySelector("#groundunit-spawn-menu")?.classList.toggle("hide", true); + this.getContainer()?.querySelector("#groundunit-spawn-button")?.classList.toggle("is-open", false); + this.getContainer()?.querySelector("#navyunit-spawn-menu")?.classList.toggle("hide", true); + this.getContainer()?.querySelector("#navyunit-spawn-button")?.classList.toggle("is-open", false); this.getContainer()?.querySelector("#smoke-spawn-menu")?.classList.toggle("hide", true); this.getContainer()?.querySelector("#smoke-spawn-button")?.classList.toggle("is-open", false); this.getContainer()?.querySelector("#explosion-menu")?.classList.toggle("hide", true); this.getContainer()?.querySelector("#explosion-spawn-button")?.classList.toggle("is-open", false); this.#resetAircraftRole(); - this.#resetAircraftType(); - this.#resetGroundUnitRole(); + this.#resetAircraftName(); + this.#resetHelicopterRole(); + this.#resetHelicopterName(); + this.#resetHelicopterRole(); + this.#resetHelicopterName(); this.#resetGroundUnitType(); + this.#resetGroundUnitName(); + this.#resetNavyUnitType(); + this.#resetNavyUnitName(); this.clip(); this.setVisibleSubMenu(null); } showUpperBar() { - this.getContainer()?.querySelector("#upper-bar")?.classList.toggle("hide", false); + this.getContainer()?.querySelector(".upper-bar")?.classList.toggle("hide", false); } hideUpperBar() { - this.getContainer()?.querySelector("#upper-bar")?.classList.toggle("hide", true); + this.getContainer()?.querySelector(".upper-bar")?.classList.toggle("hide", true); } showAltitudeSlider() { @@ -179,6 +273,11 @@ export class MapContextMenu extends ContextMenu { this.#spawnOptions.latlng = latlng; } + setCoalitionArea(coalitionArea: CoalitionArea) { + this.#coalitionArea = coalitionArea; + this.getContainer()?.querySelector("#coalition-area-button")?.classList.toggle("hide", false); + } + #onSwitchClick(value: boolean) { value? setActiveCoalition("red"): setActiveCoalition("blue"); this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => { element.setAttribute("data-coalition", getActiveCoalition()) }); @@ -193,45 +292,45 @@ export class MapContextMenu extends ContextMenu { /********* Aircraft spawn menu *********/ #setAircraftRole(role: string) { this.#spawnOptions.role = role; - this.#resetAircraftType(); - this.#aircraftTypeDropdown.setOptions(aircraftDatabase.getByRole(role).map((blueprint) => { return blueprint.label })); - this.#aircraftTypeDropdown.selectValue(0); + this.#resetAircraftName(); + this.#aircraftNameDropdown.setOptions(aircraftDatabase.getByRole(role).map((blueprint) => { return blueprint.label })); + this.#aircraftNameDropdown.selectValue(0); this.clip(); } #resetAircraftRole() { (this.getContainer()?.querySelector("#aircraft-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true; - (this.getContainer()?.querySelector("#loadout-list")).replaceChildren(); + (this.getContainer()?.querySelector("#aircraft-loadout-list")).replaceChildren(); this.#aircraftRoleDropdown.reset(); - this.#aircraftTypeDropdown.reset(); + this.#aircraftNameDropdown.reset(); this.#aircraftRoleDropdown.setOptions(aircraftDatabase.getRoles()); this.clip(); } - #setAircraftType(label: string) { - this.#resetAircraftType(); - var type = aircraftDatabase.getByLabel(label)?.name || null; - if (type != null) { - this.#spawnOptions.name = type; - this.#aircraftLoadoutDropdown.setOptions(aircraftDatabase.getLoadoutNamesByRole(type, this.#spawnOptions.role)); + #setAircraftName(label: string) { + this.#resetAircraftName(); + var name = aircraftDatabase.getByLabel(label)?.name || null; + if (name != null) { + this.#spawnOptions.name = name; + this.#aircraftLoadoutDropdown.setOptions(aircraftDatabase.getLoadoutNamesByRole(name, this.#spawnOptions.role)); this.#aircraftLoadoutDropdown.selectValue(0); - var image = (this.getContainer()?.querySelector("#unit-image")); + var image = (this.getContainer()?.querySelector("#aircraft-unit-image")); image.src = `images/units/${aircraftDatabase.getByLabel(label)?.filename}`; image.classList.toggle("hide", false); } this.clip(); } - #setAircraftCount(count: string) { - this.#spawnOptions.count = parseInt(count); + #resetAircraftName() { + (this.getContainer()?.querySelector("#aircraft-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true; + (this.getContainer()?.querySelector("#aircraft-loadout-list")).replaceChildren(); + this.#aircraftLoadoutDropdown.reset(); + (this.getContainer()?.querySelector("#aircraft-unit-image")).classList.toggle("hide", true); this.clip(); } - #resetAircraftType() { - (this.getContainer()?.querySelector("#aircraft-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true; - (this.getContainer()?.querySelector("#loadout-list")).replaceChildren(); - this.#aircraftLoadoutDropdown.reset(); - (this.getContainer()?.querySelector("#unit-image")).classList.toggle("hide", true); + #setAircraftCount(count: string) { + this.#spawnOptions.count = parseInt(count); this.clip(); } @@ -242,7 +341,7 @@ export class MapContextMenu extends ContextMenu { (this.getContainer()?.querySelector("#aircraft-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = false; var items = loadout.items.map((item: any) => { return `${item.quantity}x ${item.name}`; }); items.length == 0 ? items.push("Empty loadout") : ""; - (this.getContainer()?.querySelector("#loadout-list")).replaceChildren( + (this.getContainer()?.querySelector("#aircraft-loadout-list")).replaceChildren( ...items.map((item: any) => { var div = document.createElement('div'); div.innerText = item; @@ -253,45 +352,146 @@ export class MapContextMenu extends ContextMenu { this.clip(); } - /********* Ground unit spawn menu *********/ - #setGroundUnitRole(role: string) { + /********* Helicopter spawn menu *********/ + #setHelicopterRole(role: string) { this.#spawnOptions.role = role; - this.#resetGroundUnitType(); - - const types = groundUnitsDatabase.getByRole(role).map((blueprint) => { return blueprint.label }); - this.#groundUnitTypeDropdown.setOptions(types); - this.#groundUnitTypeDropdown.selectValue(0); + this.#resetHelicopterName(); + this.#helicopterNameDropdown.setOptions(helicopterDatabase.getByRole(role).map((blueprint) => { return blueprint.label })); + this.#helicopterNameDropdown.selectValue(0); this.clip(); } - #resetGroundUnitRole() { - (this.getContainer()?.querySelector("#ground-unit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true; - (this.getContainer()?.querySelector("#loadout-list")).replaceChildren(); - this.#groundUnitRoleDropdown.reset(); - this.#groundUnitTypeDropdown.reset(); - - const roles = groundUnitsDatabase.getRoles(); - this.#groundUnitRoleDropdown.setOptions(roles); + #resetHelicopterRole() { + (this.getContainer()?.querySelector("#helicopter-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true; + (this.getContainer()?.querySelector("#helicopter-loadout-list")).replaceChildren(); + this.#helicopterRoleDropdown.reset(); + this.#helicopterNameDropdown.reset(); + this.#helicopterRoleDropdown.setOptions(helicopterDatabase.getRoles()); this.clip(); } - #setGroundUnitType(label: string) { - this.#resetGroundUnitType(); - var type = groundUnitsDatabase.getByLabel(label)?.name || null; - if (type != null) { - this.#spawnOptions.name = type; - (this.getContainer()?.querySelector("#ground-unit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = false; + #setHelicopterName(label: string) { + this.#resetHelicopterName(); + var name = helicopterDatabase.getByLabel(label)?.name || null; + if (name != null) { + this.#spawnOptions.name = name; + this.#helicopterLoadoutDropdown.setOptions(helicopterDatabase.getLoadoutNamesByRole(name, this.#spawnOptions.role)); + this.#helicopterLoadoutDropdown.selectValue(0); + var image = (this.getContainer()?.querySelector("#helicopter-unit-image")); + image.src = `images/units/${helicopterDatabase.getByLabel(label)?.filename}`; + image.classList.toggle("hide", false); } this.clip(); } - #setGroundCount(count: string) { + #resetHelicopterName() { + (this.getContainer()?.querySelector("#helicopter-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true; + (this.getContainer()?.querySelector("#helicopter-loadout-list")).replaceChildren(); + this.#helicopterLoadoutDropdown.reset(); + (this.getContainer()?.querySelector("#helicopter-unit-image")).classList.toggle("hide", true); + this.clip(); + } + + #setHelicopterCount(count: string) { this.#spawnOptions.count = parseInt(count); this.clip(); } + #setHelicopterLoadout(loadoutName: string) { + var loadout = helicopterDatabase.getLoadoutByName(this.#spawnOptions.name, loadoutName); + if (loadout) { + this.#spawnOptions.loadout = loadout.code; + (this.getContainer()?.querySelector("#helicopter-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = false; + var items = loadout.items.map((item: any) => { return `${item.quantity}x ${item.name}`; }); + items.length == 0 ? items.push("Empty loadout") : ""; + (this.getContainer()?.querySelector("#helicopter-loadout-list")).replaceChildren( + ...items.map((item: any) => { + var div = document.createElement('div'); + div.innerText = item; + return div; + }) + ) + } + this.clip(); + } + + /********* Groundunit spawn menu *********/ + #setGroundUnitType(role: string) { + this.#resetGroundUnitName(); + + const types = groundUnitDatabase.getByType(role).map((blueprint) => { return blueprint.label }); + this.#groundUnitNameDropdown.setOptions(types); + this.#groundUnitNameDropdown.selectValue(0); + this.clip(); + } + #resetGroundUnitType() { - (this.getContainer()?.querySelector("#ground-unit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true; + (this.getContainer()?.querySelector("#groundunit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true; + this.#groundUnitTypeDropdown.reset(); + this.#groundUnitNameDropdown.reset(); + + const roles = groundUnitDatabase.getTypes(); + this.#groundUnitTypeDropdown.setOptions(roles); + this.clip(); + } + + #setGroundUnitName(label: string) { + this.#resetGroundUnitName(); + var type = groundUnitDatabase.getByLabel(label)?.name || null; + if (type != null) { + this.#spawnOptions.name = type; + (this.getContainer()?.querySelector("#groundunit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = false; + } + this.clip(); + } + + #resetGroundUnitName() { + (this.getContainer()?.querySelector("#groundunit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true; + this.clip(); + } + + #setGroundUnitCount(count: string) { + this.#spawnOptions.count = parseInt(count); + this.clip(); + } + + /********* Navyunit spawn menu *********/ + #setNavyUnitType(role: string) { + this.#resetNavyUnitName(); + + const types = navyUnitDatabase.getByType(role).map((blueprint) => { return blueprint.label }); + this.#navyUnitNameDropdown.setOptions(types); + this.#navyUnitNameDropdown.selectValue(0); + this.clip(); + } + + #resetNavyUnitType() { + (this.getContainer()?.querySelector("#navyunit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true; + this.#navyUnitTypeDropdown.reset(); + this.#navyUnitNameDropdown.reset(); + + const roles = navyUnitDatabase.getTypes(); + this.#navyUnitTypeDropdown.setOptions(roles); + this.clip(); + } + + #setNavyUnitName(label: string) { + this.#resetNavyUnitName(); + var type = navyUnitDatabase.getByLabel(label)?.name || null; + if (type != null) { + this.#spawnOptions.name = type; + (this.getContainer()?.querySelector("#navyunit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = false; + } + this.clip(); + } + + #resetNavyUnitName() { + (this.getContainer()?.querySelector("#navyunit-spawn-menu")?.querySelector(".deploy-unit-button")).disabled = true; + this.clip(); + } + + #setNavyUnitCount(count: string) { + this.#spawnOptions.count = parseInt(count); this.clip(); } } \ No newline at end of file diff --git a/client/src/index.ts b/client/src/index.ts index 47742664..1e07d6d7 100644 --- a/client/src/index.ts +++ b/client/src/index.ts @@ -17,6 +17,7 @@ import { Dropdown } from "./controls/dropdown"; import { HotgroupPanel } from "./panels/hotgrouppanel"; import { SVGInjector } from "@tanem/svg-injector"; import { BLUE_COMMANDER, GAME_MASTER, RED_COMMANDER } from "./constants/constants"; +import { NavyUnitDatabase, navyUnitDatabase } from "./units/navyunitdatabase"; var map: Map; @@ -280,4 +281,5 @@ export function getInfoPopup() { return infoPopup; } -window.onload = setup; \ No newline at end of file +window.onload = setup; + diff --git a/client/src/map/coalitionarea.ts b/client/src/map/coalitionarea.ts index 27397061..cd45557b 100644 --- a/client/src/map/coalitionarea.ts +++ b/client/src/map/coalitionarea.ts @@ -17,6 +17,8 @@ export class CoalitionArea extends Polygon { options = {}; options.bubblingMouseEvents = false; + options.interactive = false; + super(latlngs, options); this.#setColors(); this.#registerCallbacks(); @@ -40,6 +42,7 @@ export class CoalitionArea extends Polygon { this.#selected = selected; this.#setColors(); this.#setHandles(); + this.setOpacity(selected? 1: 0.5); if (!this.getSelected() && this.getEditing()) { /* Remove the vertex we were working on */ var latlngs = this.getLatLngs()[0] as LatLng[]; @@ -67,16 +70,6 @@ export class CoalitionArea extends Polygon { return this.#editing; } - setInteractive(interactive: boolean) { - this.setOpacity(interactive? 1: 0.5); - this.options.interactive = interactive; - - if (interactive) - DomUtil.addClass(this.getElement() as HTMLElement, 'leaflet-interactive'); - else - DomUtil.removeClass(this.getElement() as HTMLElement, 'leaflet-interactive'); - } - addTemporaryLatLng(latlng: LatLng) { this.#activeIndex++; var latlngs = this.getLatLngs()[0] as LatLng[]; @@ -161,7 +154,6 @@ export class CoalitionArea extends Polygon { if (!this.getEditing()) { getMap().deselectAllCoalitionAreas(); this.setSelected(true); - getMap().showCoalitionAreaContextMenu(e, this); } else this.setEditing(false); diff --git a/client/src/map/map.ts b/client/src/map/map.ts index 06cc58c2..c3ff9608 100644 --- a/client/src/map/map.ts +++ b/client/src/map/map.ts @@ -12,7 +12,7 @@ import { DestinationPreviewMarker } from "./destinationpreviewmarker"; import { TemporaryUnitMarker } from "./temporaryunitmarker"; import { ClickableMiniMap } from "./clickableminimap"; import { SVGInjector } from '@tanem/svg-injector' -import { layers as mapLayers, mapBounds, minimapBoundaries, IDLE, COALITIONAREA_DRAW_POLYGON, visibilityControls, visibilityControlsTootlips, FIRE_AT_AREA, MOVE_UNIT, CARPET_BOMBING, BOMBING, COALITIONAREA_INTERACT } from "../constants/constants"; +import { layers as mapLayers, mapBounds, minimapBoundaries, IDLE, COALITIONAREA_DRAW_POLYGON, visibilityControls, visibilityControlsTootlips, FIRE_AT_AREA, MOVE_UNIT, CARPET_BOMBING, BOMBING } from "../constants/constants"; import { TargetMarker } from "./targetmarker"; import { CoalitionArea } from "./coalitionarea"; import { CoalitionAreaContextMenu } from "../controls/coalitionareacontextmenu"; @@ -116,16 +116,7 @@ export class Map extends L.Map { getUnitsManager().setHiddenType(ev.detail.type, !el?.classList.contains("off")); Object.values(getUnitsManager().getUnits()).forEach((unit: Unit) => unit.updateVisibility()); }); - - document.addEventListener("toggleCoalitionAreaInteraction", (ev: CustomEventInit) => { - const el = ev.detail._element; - /* Add listener to set the button to off if the state changes */ - document.addEventListener("mapStateChanged", () => el?.classList.toggle("off", !(this.getState() === COALITIONAREA_INTERACT))); - if (this.getState() !== COALITIONAREA_INTERACT) - this.setState(COALITIONAREA_INTERACT); - else - this.setState(IDLE); - }); + document.addEventListener("toggleCoalitionAreaDraw", (ev: CustomEventInit) => { const el = ev.detail._element; @@ -186,22 +177,12 @@ export class Map extends L.Map { this.#updateCursor(); /* Operations to perform if you are NOT in a state */ - if (this.#state !== COALITIONAREA_INTERACT) { - this.#coalitionAreas.forEach((coalitionArea: CoalitionArea) => { - coalitionArea.setInteractive(false); - }); - } if (this.#state !== COALITIONAREA_DRAW_POLYGON) { this.#deselectCoalitionAreas(); } /* Operations to perform if you ARE in a state */ - if (this.#state === COALITIONAREA_INTERACT) { - this.#coalitionAreas.forEach((coalitionArea: CoalitionArea) => { - coalitionArea.setInteractive(true); - }); - } - else if (this.#state === COALITIONAREA_DRAW_POLYGON) { + if (this.#state === COALITIONAREA_DRAW_POLYGON) { this.#coalitionAreas.push(new CoalitionArea([])); this.#coalitionAreas[this.#coalitionAreas.length - 1].addTo(this); } @@ -231,11 +212,9 @@ export class Map extends L.Map { this.hideCoalitionAreaContextMenu(); } - showMapContextMenu(e: any) { + showMapContextMenu(x: number, y: number, latlng: L.LatLng) { this.hideAllContextMenus(); - var x = e.originalEvent.x; - var y = e.originalEvent.y; - this.#mapContextMenu.show(x, y, e.latlng); + this.#mapContextMenu.show(x, y, latlng); document.dispatchEvent(new CustomEvent("mapContextMenu")); } @@ -248,11 +227,9 @@ export class Map extends L.Map { return this.#mapContextMenu; } - showUnitContextMenu(e: any) { + showUnitContextMenu(x: number, y: number, latlng: L.LatLng) { this.hideAllContextMenus(); - var x = e.originalEvent.x; - var y = e.originalEvent.y; - this.#unitContextMenu.show(x, y, e.latlng); + this.#unitContextMenu.show(x, y, latlng); } getUnitContextMenu() { @@ -263,11 +240,9 @@ export class Map extends L.Map { this.#unitContextMenu.hide(); } - showAirbaseContextMenu(e: any, airbase: Airbase) { + showAirbaseContextMenu(x: number, y: number, latlng: L.LatLng, airbase: Airbase) { this.hideAllContextMenus(); - var x = e.originalEvent.x; - var y = e.originalEvent.y; - this.#airbaseContextMenu.show(x, y, e.latlng); + this.#airbaseContextMenu.show(x, y, latlng); this.#airbaseContextMenu.setAirbase(airbase); } @@ -279,11 +254,9 @@ export class Map extends L.Map { this.#airbaseContextMenu.hide(); } - showCoalitionAreaContextMenu(e: any, coalitionArea: CoalitionArea) { + showCoalitionAreaContextMenu(x: number, y: number, latlng: L.LatLng, coalitionArea: CoalitionArea) { this.hideAllContextMenus(); - var x = e.originalEvent.x; - var y = e.originalEvent.y; - this.#coalitionAreaContextMenu.show(x, y, e.latlng); + this.#coalitionAreaContextMenu.show(x, y, latlng); this.#coalitionAreaContextMenu.setCoalitionArea(coalitionArea); } @@ -449,7 +422,15 @@ export class Map extends L.Map { this.hideMapContextMenu(); if (this.#state === IDLE) { if (this.#state == IDLE) { - this.showMapContextMenu(e); + this.showMapContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng); + for (let coalitionArea of this.#coalitionAreas) { + if (coalitionArea.getBounds().contains(e.latlng)) { + if (coalitionArea.getSelected()) + this.showCoalitionAreaContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng, coalitionArea); + else + this.getMapContextMenu().setCoalitionArea(coalitionArea); + } + } } } else if (this.#state === MOVE_UNIT) { @@ -673,13 +654,13 @@ export class Map extends L.Map { this.#showDefaultCursor(); } else { /* Hide all the unnecessary cursors depending on the active state */ - if (this.#state !== IDLE && this.#state !== COALITIONAREA_INTERACT) this.#hideDefaultCursor(); + if (this.#state !== IDLE) this.#hideDefaultCursor(); if (this.#state !== MOVE_UNIT) this.#hideDestinationCursors(); if (![BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) this.#hideTargetCursor(); if (this.#state !== COALITIONAREA_DRAW_POLYGON) this.#hideDrawingCursor(); /* Show the active cursor depending on the active state */ - if (this.#state === IDLE || this.#state === COALITIONAREA_INTERACT) this.#showDefaultCursor(); + if (this.#state === IDLE) this.#showDefaultCursor(); else if (this.#state === MOVE_UNIT) this.#showDestinationCursors(); else if ([BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) this.#showTargetCursor(); else if (this.#state === COALITIONAREA_DRAW_POLYGON) this.#showDrawingCursor(); diff --git a/client/src/missionhandler/missionhandler.ts b/client/src/missionhandler/missionhandler.ts index cac749f1..48fd98d5 100644 --- a/client/src/missionhandler/missionhandler.ts +++ b/client/src/missionhandler/missionhandler.ts @@ -130,6 +130,6 @@ export class MissionHandler { } #onAirbaseClick(e: any) { - getMap().showAirbaseContextMenu(e, e.sourceTarget); + getMap().showAirbaseContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng, e.sourceTarget); } } \ No newline at end of file diff --git a/client/src/other/utils.ts b/client/src/other/utils.ts index b8882a7c..2a50102b 100644 --- a/client/src/other/utils.ts +++ b/client/src/other/utils.ts @@ -3,7 +3,7 @@ import * as turf from "@turf/turf"; import { UnitDatabase } from "../units/unitdatabase"; import { aircraftDatabase } from "../units/aircraftdatabase"; import { helicopterDatabase } from "../units/helicopterdatabase"; -import { groundUnitsDatabase } from "../units/groundunitsdatabase"; +import { groundUnitDatabase } from "../units/groundunitdatabase"; import { Buffer } from "buffer"; import { ROEs, emissionsCountermeasures, reactionsToThreat, states } from "../constants/constants"; @@ -241,10 +241,10 @@ export function getMarkerCategoryByName(name: string) { return "aircraft"; else if (helicopterDatabase.getByName(name) != null) return "helicopter"; - else if (groundUnitsDatabase.getByName(name) != null){ + else if (groundUnitDatabase.getByName(name) != null){ // TODO this is very messy - var role = groundUnitsDatabase.getByName(name)?.loadouts[0].roles[0]; - return (role?.includes("SAM")) ? "groundunit-sam" : "groundunit-other"; + var type = groundUnitDatabase.getByName(name)?.type; + return (type?.includes("SAM")) ? "groundunit-sam" : "groundunit-other"; } else return "groundunit-other"; // TODO add other unit types @@ -256,7 +256,7 @@ export function getUnitDatabaseByCategory(category: string) { else if (category == "helicopter") return helicopterDatabase; else if (category.includes("groundunit")) - return groundUnitsDatabase; + return groundUnitDatabase; else return null; } diff --git a/client/src/panels/unitcontrolpanel.ts b/client/src/panels/unitcontrolpanel.ts index 582f7ddd..d25d7404 100644 --- a/client/src/panels/unitcontrolpanel.ts +++ b/client/src/panels/unitcontrolpanel.ts @@ -209,7 +209,7 @@ export class UnitControlPanel extends Panel { const radioCallsignNumberInput = this.#advancedSettingsDialog.querySelector("#radio-callsign-number")?.querySelector("input") as HTMLInputElement; const unit = units[0]; - const roles = aircraftDatabase.getByName(unit.getName())?.loadouts.map((loadout) => {return loadout.roles}) + const roles = aircraftDatabase.getByName(unit.getName())?.loadouts?.map((loadout) => {return loadout.roles}) const tanker = roles != undefined && Array.prototype.concat.apply([], roles)?.includes("Tanker"); const AWACS = roles != undefined && Array.prototype.concat.apply([], roles)?.includes("AWACS"); const radioMHz = Math.floor(unit.getRadio().frequency / 1000000); diff --git a/client/src/server/server.ts b/client/src/server/server.ts index d77df67d..52f64b87 100644 --- a/client/src/server/server.ts +++ b/client/src/server/server.ts @@ -145,15 +145,27 @@ export function spawnExplosion(intensity: number, latlng: LatLng) { POST(data, () => { }); } +export function spawnAircrafts(units: any, coalition: string, airbaseName: string, immediate: boolean) { + var command = { "units": units, "coalition": coalition, "airbaseName": airbaseName, "immediate": immediate }; + var data = { "spawnAircrafts": command } + POST(data, () => { }); +} + +export function spawnHelicopters(units: any, coalition: string, airbaseName: string, immediate: boolean) { + var command = { "units": units, "coalition": coalition, "airbaseName": airbaseName, "immediate": immediate }; + var data = { "spawnHelicopter": command } + POST(data, () => { }); +} + export function spawnGroundUnits(units: any, coalition: string, immediate: boolean) { var command = { "units": units, "coalition": coalition, "immediate": immediate }; var data = { "spawnGroundUnits": command } POST(data, () => { }); } -export function spawnAircrafts(units: any, coalition: string, airbaseName: string, immediate: boolean) { - var command = { "units": units, "coalition": coalition, "airbaseName": airbaseName, "immediate": immediate }; - var data = { "spawnAircrafts": command } +export function spawnNavyUnits(units: any, coalition: string, immediate: boolean) { + var command = { "units": units, "coalition": coalition, "immediate": immediate }; + var data = { "spawnNavyUnits": command } POST(data, () => { }); } diff --git a/client/src/units/aircraftdatabase.ts b/client/src/units/aircraftdatabase.ts index 6092ed81..a798d841 100644 --- a/client/src/units/aircraftdatabase.ts +++ b/client/src/units/aircraftdatabase.ts @@ -2123,7 +2123,7 @@ export class AircraftDatabase extends UnitDatabase { "AWACS" ], "code": "", - "name": "Blue Naval AWACS" + "name": "Blue Navy AWACS" } ], "filename": "e-2.png" @@ -3484,7 +3484,7 @@ export class AircraftDatabase extends UnitDatabase { }, "Su-33": { "name": "Su-33", - "label": "Su-33 Naval Flanker", + "label": "Su-33 Navy Flanker", "era": ["Late Cold War", "Modern"], "shortLabel": "33", "loadouts": [ diff --git a/client/src/units/groundunitdatabase.ts b/client/src/units/groundunitdatabase.ts new file mode 100644 index 00000000..a52f58fc --- /dev/null +++ b/client/src/units/groundunitdatabase.ts @@ -0,0 +1,1535 @@ +import { UnitDatabase } from "./unitdatabase" + +export class GroundUnitDatabase extends UnitDatabase { + constructor() { + super(); + this.blueprints = { + "SA-2 SAM Battery": { + "name": "SA-2 SAM Battery", + "era": [ + "Early Cold War" + ], + "label": "SA-2 SAM Battery", + "shortLabel": "SA-2 SAM Battery", + "range": "Long", + "filename": "", + "type": "SAM Sites" + }, + "SA-3 SAM Battery": { + "name": "SA-3 SAM Battery", + "era": [ + "Early Cold War" + ], + "label": "SA-3 SAM Battery", + "shortLabel": "SA-3 SAM Battery", + "range": "Medium", + "filename": "", + "type": "SAM Sites" + }, + "SA-6 SAM Battery": { + "name": "SA-6 SAM Battery", + "era": [ + "Mid Cold War" + ], + "label": "SA-6 SAM Battery", + "shortLabel": "SA-6 SAM Battery", + "range": "Medium", + "filename": "", + "type": "SAM Sites" + }, + "SA-10 SAM Battery": { + "name": "SA-10 SAM Battery", + "era": [ + "Late Cold War" + ], + "label": "SA-10 SAM Battery", + "shortLabel": "SA-10 SAM Battery", + "range": "Long", + "filename": "", + "type": "SAM Sites" + }, + "SA-11 SAM Battery": { + "name": "SA-11 SAM Battery", + "era": [ + "Late Cold War" + ], + "label": "SA-11 SAM Battery", + "shortLabel": "SA-11 SAM Battery", + "range": "Medium", + "filename": "", + "type": "SAM Sites" + }, + "Patriot site": { + "name": "Patriot site", + "era": [ + "Late Cold War" + ], + "label": "Patriot site", + "shortLabel": "Patriot site", + "range": "Long", + "filename": "", + "type": "SAM Sites" + }, + "Hawk SAM Battery": { + "name": "Hawk SAM Battery", + "era": [ + "Early Cold War" + ], + "label": "Hawk SAM Battery", + "shortLabel": "Hawk SAM Battery", + "range": "Medium", + "filename": "", + "type": "SAM Sites" + }, + "2B11 mortar": { + "name": "2B11 mortar", + "era": [ + "Late Cold War" + ], + "label": "2B11 mortar", + "shortLabel": "2B11 mortar", + "filename": "", + "type": "Gun Artillery" + }, + "SAU Gvozdika": { + "name": "SAU Gvozdika", + "era": [ + "Mid Cold War" + ], + "label": "SAU Gvozdika", + "shortLabel": "SAU Gvozdika", + "filename": "", + "type": "Gun Artillery" + }, + "SAU Msta": { + "name": "SAU Msta", + "era": [ + "Late Cold War" + ], + "label": "SAU Msta", + "shortLabel": "SAU Msta", + "filename": "", + "type": "Gun Artillery" + }, + "SAU Akatsia": { + "name": "SAU Akatsia", + "era": [ + "Mid Cold War" + ], + "label": "SAU Akatsia", + "shortLabel": "SAU Akatsia", + "filename": "", + "type": "Gun Artillery" + }, + "SAU 2-C9": { + "name": "SAU 2-C9", + "era": [ + "Mid Cold War" + ], + "label": "SAU Nona", + "shortLabel": "SAU Nona", + "filename": "", + "type": "Gun Artillery" + }, + "M-109": { + "name": "M-109", + "era": [ + "Early Cold War" + ], + "label": "M-109 Paladin", + "shortLabel": "M-109", + "filename": "", + "type": "Gun Artillery" + }, + "AAV7": { + "name": "AAV7", + "era": [ + "Mid Cold War" + ], + "label": "AAV7", + "shortLabel": "AAV7", + "filename": "", + "type": "APC" + }, + "BMD-1": { + "name": "BMD-1", + "era": [ + "Mid Cold War" + ], + "label": "BMD-1", + "shortLabel": "BMD-1", + "filename": "", + "type": "IFV" + }, + "BMP-1": { + "name": "BMP-1", + "era": [ + "Mid Cold War" + ], + "label": "BMP-1", + "shortLabel": "BMP-1", + "filename": "", + "type": "IFV" + }, + "BMP-2": { + "name": "BMP-2", + "era": [ + "Mid Cold War" + ], + "label": "BMP-2", + "shortLabel": "BMP-2", + "filename": "", + "type": "IFV" + }, + "BMP-3": { + "name": "BMP-3", + "era": [ + "Late Cold War" + ], + "label": "BMP-3", + "shortLabel": "BMP-3", + "filename": "", + "type": "IFV" + }, + "Boman": { + "name": "Boman", + "era": [ + "Late Cold War" + ], + "label": "Grad Fire Direction Manager", + "shortLabel": "Boman", + "filename": "", + "type": "Reconnaissance" + }, + "BRDM-2": { + "name": "BRDM-2", + "era": [ + "Early Cold War" + ], + "label": "BRDM-2", + "shortLabel": "BRDM-2", + "filename": "", + "type": "Reconnaissance" + }, + "BTR-80": { + "name": "BTR-80", + "era": [ + "Late Cold War" + ], + "label": "BTR-80", + "shortLabel": "BTR-80", + "filename": "", + "type": "APC" + }, + "BTR_D": { + "name": "BTR_D", + "era": [ + "Mid Cold War" + ], + "label": "BTR_D", + "shortLabel": "BTR_D", + "filename": "", + "type": "APC" + }, + "Bunker": { + "name": "Bunker", + "label": "Bunker", + "shortLabel": "Bunker", + "filename": "", + "type": "Static" + }, + "Cobra": { + "name": "Cobra", + "era": [ + "Modern" + ], + "label": "Otokar Cobra", + "shortLabel": "Cobra", + "filename": "", + "type": "Reconnaissance" + }, + "LAV-25": { + "name": "LAV-25", + "era": [ + "Late Cold War" + ], + "label": "LAV-25", + "shortLabel": "LAV-25", + "filename": "", + "type": "IFV" + }, + "M1043 HMMWV Armament": { + "name": "M1043 HMMWV Armament", + "era": [ + "Late Cold War" + ], + "label": "HMMWV M2 Browning", + "shortLabel": "HMMWV M2", + "filename": "", + "type": "Reconnaissance" + }, + "M1045 HMMWV TOW": { + "name": "M1045 HMMWV TOW", + "era": [ + "Late Cold War" + ], + "label": "HMMWV TOW", + "shortLabel": "HMMWV TOW", + "filename": "", + "type": "Reconnaissance" + }, + "M1126 Stryker ICV": { + "name": "M1126 Stryker ICV", + "era": [ + "Modern" + ], + "label": "Stryker MG", + "shortLabel": "Stryker MG", + "filename": "", + "type": "APC" + }, + "M-113": { + "name": "M-113", + "era": [ + "Early Cold War" + ], + "label": "M-113", + "shortLabel": "M-113", + "filename": "", + "type": "APC" + }, + "M1134 Stryker ATGM": { + "name": "M1134 Stryker ATGM", + "era": [ + "Modern" + ], + "label": "Stryker ATGM", + "shortLabel": "Stryker ATGM", + "filename": "", + "type": "IFV" + }, + "M-2 Bradley": { + "name": "M-2 Bradley", + "era": [ + "Late Cold War" + ], + "label": "M-2A2 Bradley", + "shortLabel": "M-2 Bradley", + "filename": "", + "type": "IFV" + }, + "Marder": { + "name": "Marder", + "era": [ + "Late Cold War" + ], + "label": "Marder", + "shortLabel": "Marder", + "filename": "", + "type": "IFV" + }, + "MCV-80": { + "name": "MCV-80", + "era": [ + "Late Cold War" + ], + "label": "Warrior IFV", + "shortLabel": "Warrior", + "filename": "", + "type": "IFV" + }, + "MTLB": { + "name": "MTLB", + "era": [ + "Mid Cold War" + ], + "label": "MT-LB", + "shortLabel": "MT-LB", + "filename": "", + "type": "APC" + }, + "Paratrooper RPG-16": { + "name": "Paratrooper RPG-16", + "era": [ + "Modern" + ], + "label": "Paratrooper RPG-16", + "shortLabel": "Paratrooper RPG-16", + "filename": "", + "type": "Infantry" + }, + "Paratrooper AKS-74": { + "name": "Paratrooper AKS-74", + "era": [ + "Modern" + ], + "label": "Paratrooper AKS-74", + "shortLabel": "Paratrooper AKS-74", + "filename": "", + "type": "Infantry" + }, + "Sandbox": { + "name": "Sandbox", + "label": "Sandbox", + "shortLabel": "Sandbox", + "filename": "", + "type": "Static" + }, + "Soldier AK": { + "name": "Soldier AK", + "era": [ + "Early Cold War" + ], + "label": "Soldier AK", + "shortLabel": "Soldier AK", + "filename": "", + "type": "Infantry" + }, + "Infantry AK": { + "name": "Infantry AK", + "era": [ + "Mid Cold War" + ], + "label": "Infantry AK", + "shortLabel": "Infantry AK", + "filename": "", + "type": "Infantry" + }, + "Soldier M249": { + "name": "Soldier M249", + "era": [ + "Late Cold War" + ], + "label": "Soldier M249", + "shortLabel": "Soldier M249", + "filename": "", + "type": "Infantry" + }, + "Soldier M4": { + "name": "Soldier M4", + "era": [ + "Mid Cold War" + ], + "label": "Soldier M4", + "shortLabel": "Soldier M4", + "filename": "", + "type": "Infantry" + }, + "Soldier M4 GRG": { + "name": "Soldier M4 GRG", + "era": [ + "Mid Cold War" + ], + "label": "Soldier M4 GRG", + "shortLabel": "Soldier M4 GRG", + "filename": "", + "type": "Infantry" + }, + "Soldier RPG": { + "name": "Soldier RPG", + "era": [ + "Mid Cold War" + ], + "label": "Soldier RPG", + "shortLabel": "Soldier RPG", + "filename": "", + "type": "Infantry" + }, + "TPZ": { + "name": "TPZ", + "era": [ + "Late Cold War" + ], + "label": "TPz Fuchs", + "shortLabel": "TPz Fuchs", + "filename": "", + "type": "APC" + }, + "Grad-URAL": { + "name": "Grad-URAL", + "era": [ + "Mid Cold War" + ], + "label": "Grad", + "shortLabel": "Grad", + "filename": "", + "type": "Rocket Artillery" + }, + "Uragan_BM-27": { + "name": "Uragan_BM-27", + "era": [ + "Late Cold War" + ], + "label": "Uragan", + "shortLabel": "Uragan", + "filename": "", + "type": "Rocket Artillery" + }, + "Smerch": { + "name": "Smerch", + "era": [ + "Late Cold War" + ], + "label": "Smerch", + "shortLabel": "Smerch", + "filename": "", + "type": "Rocket Artillery" + }, + "MLRS": { + "name": "MLRS", + "era": [ + "Late Cold War" + ], + "label": "M270", + "shortLabel": "M270", + "filename": "", + "type": "Rocket Artillery" + }, + "2S6 Tunguska": { + "name": "2S6 Tunguska", + "era": [ + "Late Cold War" + ], + "label": "SA-19 Tunguska", + "shortLabel": "SA-19", + "range": "Short", + "filename": "", + "type": "AAA/SAM" + }, + "Kub 2P25 ln": { + "name": "Kub 2P25 ln", + "era": [ + "Late Cold War" + ], + "label": "SA-6 Kub 2P25 ln", + "shortLabel": "Kub 2P25 ln", + "range": "Medium", + "filename": "", + "type": "SAM" + }, + "5p73 s-125 ln": { + "name": "5p73 s-125 ln", + "era": [ + "Early Cold War" + ], + "label": "SA-3 5p73 s-125 ln", + "shortLabel": "5p73 s-125 ln", + "range": "Medium", + "filename": "", + "type": "SAM" + }, + "S-300PS 5P85C ln": { + "name": "S-300PS 5P85C ln", + "era": [ + "Late Cold War" + ], + "label": "SA-10 S-300PS 5P85C ln", + "shortLabel": "S-300PS 5P85C ln", + "range": "Long", + "filename": "", + "type": "SAM" + }, + "S-300PS 5P85D ln": { + "name": "S-300PS 5P85D ln", + "era": [ + "Late Cold War" + ], + "label": "SA-10 S-300PS 5P85D ln", + "shortLabel": "S-300PS 5P85D ln", + "range": "Long", + "filename": "", + "type": "SAM" + }, + "SA-11 Buk LN 9A310M1": { + "name": "SA-11 Buk LN 9A310M1", + "era": [ + "Late Cold War" + ], + "label": "SA-11 Buk LN 9A310M1", + "shortLabel": "SA-11 Buk LN 9A310M1", + "range": "Medium", + "filename": "", + "type": "SAM" + }, + "Osa 9A33 ln": { + "name": "Osa 9A33 ln", + "era": [ + "Mid Cold War" + ], + "label": "SA-8 Osa 9A33 ln", + "shortLabel": "Osa 9A33 ln", + "range": "Short", + "filename": "", + "type": "SAM" + }, + "Tor 9A331": { + "name": "Tor 9A331", + "era": [ + "Late Cold War" + ], + "label": "SA-15 Tor 9A331", + "shortLabel": "Tor 9A331", + "range": "Medium", + "filename": "", + "type": "SAM" + }, + "Strela-10M3": { + "name": "Strela-10M3", + "era": [ + "Late Cold War" + ], + "label": "SA-13 Strela-10M3", + "shortLabel": "Strela-10M3", + "range": "Short", + "filename": "", + "type": "SAM" + }, + "Strela-1 9P31": { + "name": "Strela-1 9P31", + "era": [ + "Late Cold War" + ], + "label": "SA-9 Strela-1 9P31", + "shortLabel": "Strela-1 9P31", + "range": "Short", + "filename": "", + "type": "SAM" + }, + "SA-11 Buk CC 9S470M1": { + "name": "SA-11 Buk CC 9S470M1", + "era": [ + "Late Cold War" + ], + "label": "SA-11 Buk CC 9S470M1", + "shortLabel": "SA-11 Buk CC 9S470M1", + "range": "Medium", + "filename": "", + "type": "SAM" + }, + "SA-8 Osa LD 9T217": { + "name": "SA-8 Osa LD 9T217", + "era": [ + "Late Cold War" + ], + "label": "SA-8 Osa LD 9T217", + "shortLabel": "SA-8 Osa LD 9T217", + "range": "Short", + "filename": "", + "type": "SAM" + }, + "Patriot AMG": { + "name": "Patriot AMG", + "era": [ + "Modern" + ], + "label": "Patriot AMG", + "shortLabel": "Patriot AMG", + "range": "Long", + "filename": "", + "type": "SAM" + }, + "Patriot ECS": { + "name": "Patriot ECS", + "era": [ + "Modern" + ], + "label": "Patriot ECS", + "shortLabel": "Patriot ECS", + "range": "Long", + "filename": "", + "type": "SAM" + }, + "Gepard": { + "name": "Gepard", + "era": [ + "Late Cold War", + "Modern" + ], + "label": "Gepard", + "shortLabel": "Gepard", + "filename": "", + "type": "AAA" + }, + "Hawk pcp": { + "name": "Hawk pcp", + "era": [ + "Late Cold War" + ], + "label": "Hawk pcp", + "shortLabel": "Hawk pcp", + "range": "Medium", + "filename": "", + "type": "SAM" + }, + "SA-18 Igla manpad": { + "name": "SA-18 Igla manpad", + "era": [ + "Late Cold War" + ], + "label": "SA-18 Igla manpad", + "shortLabel": "SA-18 Igla manpad", + "range": "Short", + "filename": "", + "type": "MANPADS" + }, + "Igla manpad INS": { + "name": "Igla manpad INS", + "era": [ + "Late Cold War" + ], + "label": "SA-18 Igla manpad INS", + "shortLabel": "Igla manpad INS", + "range": "Short", + "filename": "", + "type": "MANPADS" + }, + "SA-18 Igla-S manpad": { + "name": "SA-18 Igla-S manpad", + "era": [ + "Late Cold War" + ], + "label": "SA-18 Igla-S manpad", + "shortLabel": "SA-18 Igla-S manpad", + "range": "Short", + "filename": "", + "type": "MANPADS" + }, + "Vulcan": { + "name": "Vulcan", + "era": [ + "Late Cold War" + ], + "label": "Vulcan", + "shortLabel": "Vulcan", + "filename": "", + "type": "AAA" + }, + "Hawk ln": { + "name": "Hawk ln", + "era": [ + "Late Cold War" + ], + "label": "Hawk ln", + "shortLabel": "Hawk ln", + "filename": "", + "type": "SAM" + }, + "M48 Chaparral": { + "name": "M48 Chaparral", + "era": [ + "Late Cold War" + ], + "label": "M48 Chaparral", + "shortLabel": "M48 Chaparral", + "filename": "", + "type": "SAM" + }, + "M6 Linebacker": { + "name": "M6 Linebacker", + "era": [ + "Late Cold War" + ], + "label": "M6 Linebacker", + "shortLabel": "M6 Linebacker", + "filename": "", + "type": "SAM" + }, + "Patriot ln": { + "name": "Patriot ln", + "era": [ + "Late Cold War" + ], + "label": "Patriot ln", + "shortLabel": "Patriot ln", + "range": "Long", + "filename": "", + "type": "SAM" + }, + "M1097 Avenger": { + "name": "M1097 Avenger", + "era": [ + "Modern" + ], + "label": "M1097 Avenger", + "shortLabel": "M1097 Avenger", + "filename": "", + "type": "SAM" + }, + "Patriot EPP": { + "name": "Patriot EPP", + "era": [ + "Late Cold War" + ], + "label": "Patriot EPP", + "shortLabel": "Patriot EPP", + "range": "Long", + "filename": "", + "type": "SAM" + }, + "Patriot cp": { + "name": "Patriot cp", + "era": [ + "Late Cold War" + ], + "label": "Patriot cp", + "shortLabel": "Patriot cp", + "range": "Long", + "filename": "", + "type": "SAM" + }, + "Roland ADS": { + "name": "Roland ADS", + "era": [ + "Late Cold War" + ], + "label": "Roland ADS", + "shortLabel": "Roland ADS", + "filename": "", + "type": "SAM" + }, + "S-300PS 54K6 cp": { + "name": "S-300PS 54K6 cp", + "era": [ + "Late Cold War" + ], + "label": "SA-10 S-300PS 54K6 cp", + "shortLabel": "S-300PS 54K6 cp", + "range": "Long", + "filename": "", + "type": "SAM" + }, + "Stinger manpad GRG": { + "name": "Stinger manpad GRG", + "era": [ + "Late Cold War" + ], + "label": "Stinger manpad GRG", + "shortLabel": "Stinger manpad GRG", + "range": "Short", + "filename": "", + "type": "SAM" + }, + "Stinger manpad dsr": { + "name": "Stinger manpad dsr", + "era": [ + "Late Cold War" + ], + "label": "Stinger manpad dsr", + "shortLabel": "Stinger manpad dsr", + "range": "Short", + "filename": "", + "type": "MANPADS" + }, + "Stinger comm dsr": { + "name": "Stinger comm dsr", + "era": [ + "Late Cold War" + ], + "label": "Stinger comm dsr", + "shortLabel": "Stinger comm dsr", + "range": "Short", + "filename": "", + "type": "SAM" + }, + "Stinger manpad": { + "name": "Stinger manpad", + "era": [ + "Late Cold War" + ], + "label": "Stinger manpad", + "shortLabel": "Stinger manpad", + "range": "Short", + "filename": "", + "type": "MANPADS" + }, + "Stinger comm": { + "name": "Stinger comm", + "era": [ + "Late Cold War" + ], + "label": "Stinger comm", + "shortLabel": "Stinger comm", + "range": "Short", + "filename": "", + "type": "SAM" + }, + "ZSU-23-4 Shilka": { + "name": "ZSU-23-4 Shilka", + "era": [ + "Late Cold War" + ], + "label": "ZSU-23-4 Shilka", + "shortLabel": "ZSU-23-4 Shilka", + "filename": "", + "type": "AAA" + }, + "ZU-23 Emplacement Closed": { + "name": "ZU-23 Emplacement Closed", + "era": [ + "Late Cold War" + ], + "label": "ZU-23 Emplacement Closed", + "shortLabel": "ZU-23 Emplacement Closed", + "filename": "", + "type": "AAA" + }, + "ZU-23 Emplacement": { + "name": "ZU-23 Emplacement", + "era": [ + "Late Cold War" + ], + "label": "ZU-23 Emplacement", + "shortLabel": "ZU-23 Emplacement", + "filename": "", + "type": "AAA" + }, + "ZU-23 Closed Insurgent": { + "name": "ZU-23 Closed Insurgent", + "era": [ + "Late Cold War" + ], + "label": "ZU-23 Closed Insurgent", + "shortLabel": "ZU-23 Closed Insurgent", + "filename": "", + "type": "AAA" + }, + "Ural-375 ZU-23 Insurgent": { + "name": "Ural-375 ZU-23 Insurgent", + "era": [ + "Late Cold War" + ], + "label": "Ural-375 ZU-23 Insurgent", + "shortLabel": "Ural-375 ZU-23 Insurgent", + "filename": "", + "type": "AAA" + }, + "ZU-23 Insurgent": { + "name": "ZU-23 Insurgent", + "era": [ + "Late Cold War" + ], + "label": "ZU-23 Insurgent", + "shortLabel": "ZU-23 Insurgent", + "filename": "", + "type": "AAA" + }, + "Ural-375 ZU-23": { + "name": "Ural-375 ZU-23", + "era": [ + "Late Cold War" + ], + "label": "Ural-375 ZU-23", + "shortLabel": "Ural-375 ZU-23", + "filename": "", + "type": "AAA" + }, + "1L13 EWR": { + "name": "1L13 EWR", + "era": [ + "Late Cold War" + ], + "label": "1L13 EWR", + "shortLabel": "1L13 EWR", + "filename": "", + "type": "Radar" + }, + "Kub 1S91 str": { + "name": "Kub 1S91 str", + "era": [ + "Mid Cold War" + ], + "label": "SA-6 Kub 1S91 str", + "shortLabel": "Kub 1S91 str", + "range": "Medium", + "filename": "", + "type": "SAM" + }, + "S-300PS 40B6M tr": { + "name": "S-300PS 40B6M tr", + "era": [ + "Late Cold War" + ], + "label": "SA-10 S-300PS 40B6M tr", + "shortLabel": "S-300PS 40B6M tr", + "range": "Long", + "filename": "", + "type": "SAM" + }, + "S-300PS 40B6MD sr": { + "name": "S-300PS 40B6MD sr", + "era": [ + "Late Cold War" + ], + "label": "SA-10 S-300PS 40B6MD sr", + "shortLabel": "S-300PS 40B6MD sr", + "range": "Long", + "filename": "", + "type": "SAM" + }, + "55G6 EWR": { + "name": "55G6 EWR", + "era": [ + "Early Cold War" + ], + "label": "55G6 EWR", + "shortLabel": "55G6 EWR", + "filename": "", + "type": "Radar" + }, + "S-300PS 64H6E sr": { + "name": "S-300PS 64H6E sr", + "era": [ + "Late Cold War" + ], + "label": "SA-10 S-300PS 64H6E sr", + "shortLabel": "S-300PS 64H6E sr", + "range": "Long", + "filename": "", + "type": "SAM" + }, + "SA-11 Buk SR 9S18M1": { + "name": "SA-11 Buk SR 9S18M1", + "era": [ + "Mid Cold War" + ], + "label": "SA-11 Buk SR 9S18M1", + "shortLabel": "SA-11 Buk SR 9S18M1", + "range": "Long", + "filename": "", + "type": "SAM" + }, + "Dog Ear radar": { + "name": "Dog Ear radar", + "era": [ + "Mid Cold War" + ], + "label": "Dog Ear radar", + "shortLabel": "Dog Ear radar", + "filename": "", + "type": "SAM" + }, + "Hawk tr": { + "name": "Hawk tr", + "era": [ + "Early Cold War" + ], + "label": "Hawk tr", + "shortLabel": "Hawk tr", + "range": "Medium", + "filename": "", + "type": "SAM" + }, + "Hawk sr": { + "name": "Hawk sr", + "era": [ + "Early Cold War" + ], + "label": "Hawk sr", + "shortLabel": "Hawk sr", + "range": "Long", + "filename": "", + "type": "SAM" + }, + "Patriot str": { + "name": "Patriot str", + "era": [ + "Late Cold War" + ], + "label": "Patriot str", + "shortLabel": "Patriot str", + "range": "Medium", + "filename": "", + "type": "SAM" + }, + "Hawk cwar": { + "name": "Hawk cwar", + "era": [ + "Early Cold War" + ], + "label": "Hawk cwar", + "shortLabel": "Hawk cwar", + "range": "Long", + "filename": "", + "type": "SAM" + }, + "p-19 s-125 sr": { + "name": "p-19 s-125 sr", + "era": [ + "Mid Cold War" + ], + "label": "SA-3 p-19 s-125 sr", + "shortLabel": "p-19 s-125 sr", + "filename": "", + "type": "SAM" + }, + "Roland Radar": { + "name": "Roland Radar", + "era": [ + "Mid Cold War" + ], + "label": "Roland Radar", + "shortLabel": "Roland Radar", + "filename": "", + "type": "SAM" + }, + "snr s-125 tr": { + "name": "snr s-125 tr", + "era": [ + "Early Cold War" + ], + "label": "SA-3 snr s-125 tr", + "shortLabel": "snr s-125 tr", + "range": "Medium", + "filename": "", + "type": "SAM" + }, + "house1arm": { + "name": "house1arm", + "label": "house1arm", + "shortLabel": "house1arm", + "filename": "", + "type": "Structure" + }, + "house2arm": { + "name": "house2arm", + "label": "house2arm", + "shortLabel": "house2arm", + "filename": "", + "type": "Structure" + }, + "outpost_road": { + "name": "outpost_road", + "label": "outpost_road", + "shortLabel": "outpost_road", + "filename": "", + "type": "Structure" + }, + "outpost": { + "name": "outpost", + "label": "outpost", + "shortLabel": "outpost", + "filename": "", + "type": "Structure" + }, + "houseA_arm": { + "name": "houseA_arm", + "label": "houseA_arm", + "shortLabel": "houseA_arm", + "filename": "", + "type": "Structure" + }, + "Challenger2": { + "name": "Challenger2", + "era": [ + "Modern" + ], + "label": "Challenger2", + "shortLabel": "Challenger2", + "filename": "", + "type": "Tank" + }, + "Leclerc": { + "name": "Leclerc", + "era": [ + "Modern" + ], + "label": "Leclerc", + "shortLabel": "Leclerc", + "filename": "", + "type": "Tank" + }, + "Leopard1A3": { + "name": "Leopard1A3", + "era": [ + "Mid Cold War" + ], + "label": "Leopard1A3", + "shortLabel": "Leopard1A3", + "filename": "", + "type": "Tank" + }, + "Leopard-2": { + "name": "Leopard-2", + "era": [ + "Late Cold War" + ], + "label": "Leopard-2", + "shortLabel": "Leopard-2", + "filename": "", + "type": "Tank" + }, + "M-60": { + "name": "M-60", + "era": [ + "Early Cold War" + ], + "label": "M-60", + "shortLabel": "M-60", + "filename": "", + "type": "Tank" + }, + "M1128 Stryker MGS": { + "name": "M1128 Stryker MGS", + "era": [ + "Modern" + ], + "label": "M1128 Stryker MGS", + "shortLabel": "M1128 Stryker MGS", + "filename": "", + "type": "SPG" + }, + "M-1 Abrams": { + "name": "M-1 Abrams", + "era": [ + "Late Cold War" + ], + "label": "M-1 Abrams", + "shortLabel": "M-1 Abrams", + "filename": "", + "type": "Tank" + }, + "T-55": { + "name": "T-55", + "era": [ + "Early Cold War" + ], + "label": "T-55", + "shortLabel": "T-55", + "filename": "", + "type": "Tank" + }, + "T-72B": { + "name": "T-72B", + "era": [ + "Mid Cold War" + ], + "label": "T-72B", + "shortLabel": "T-72B", + "filename": "", + "type": "Tank" + }, + "T-80UD": { + "name": "T-80UD", + "era": [ + "Mid Cold War" + ], + "label": "T-80UD", + "shortLabel": "T-80UD", + "filename": "", + "type": "Tank" + }, + "T-90": { + "name": "T-90", + "era": [ + "Late Cold War" + ], + "label": "T-90", + "shortLabel": "T-90", + "filename": "", + "type": "Tank" + }, + "Ural-4320 APA-5D": { + "name": "Ural-4320 APA-5D", + "era": [ + "Early Cold War" + ], + "label": "Ural-4320 APA-5D", + "shortLabel": "Ural-4320 APA-5D", + "filename": "", + "type": "Unarmed" + }, + "ATMZ-5": { + "name": "ATMZ-5", + "era": [ + "Early Cold War" + ], + "label": "ATMZ-5", + "shortLabel": "ATMZ-5", + "filename": "", + "type": "Unarmed" + }, + "ATZ-10": { + "name": "ATZ-10", + "era": [ + "Early Cold War" + ], + "label": "ATZ-10", + "shortLabel": "ATZ-10", + "filename": "", + "type": "Unarmed" + }, + "GAZ-3307": { + "name": "GAZ-3307", + "era": [ + "Early Cold War" + ], + "label": "GAZ-3307", + "shortLabel": "GAZ-3307", + "filename": "", + "type": "Unarmed" + }, + "GAZ-3308": { + "name": "GAZ-3308", + "era": [ + "Early Cold War" + ], + "label": "GAZ-3308", + "shortLabel": "GAZ-3308", + "filename": "", + "type": "Unarmed" + }, + "GAZ-66": { + "name": "GAZ-66", + "era": [ + "Early Cold War" + ], + "label": "GAZ-66", + "shortLabel": "GAZ-66", + "filename": "", + "type": "Unarmed" + }, + "M978 HEMTT Tanker": { + "name": "M978 HEMTT Tanker", + "era": [ + "Mid Cold War" + ], + "label": "M978 HEMTT Tanker", + "shortLabel": "M978 HEMTT Tanker", + "filename": "", + "type": "Unarmed" + }, + "HEMTT TFFT": { + "name": "HEMTT TFFT", + "era": [ + "Late Cold War" + ], + "label": "HEMTT TFFT", + "shortLabel": "HEMTT TFFT", + "filename": "", + "type": "Unarmed" + }, + "IKARUS Bus": { + "name": "IKARUS Bus", + "era": [ + "Mid Cold War" + ], + "label": "IKARUS Bus", + "shortLabel": "IKARUS Bus", + "filename": "", + "type": "Unarmed" + }, + "KAMAZ Truck": { + "name": "KAMAZ Truck", + "era": [ + "Mid Cold War" + ], + "label": "KAMAZ Truck", + "shortLabel": "KAMAZ Truck", + "filename": "", + "type": "Unarmed" + }, + "LAZ Bus": { + "name": "LAZ Bus", + "era": [ + "Early Cold War" + ], + "label": "LAZ Bus", + "shortLabel": "LAZ Bus", + "filename": "", + "type": "Unarmed" + }, + "Hummer": { + "name": "Hummer", + "era": [ + "Mid Cold War" + ], + "label": "Hummer", + "shortLabel": "Hummer", + "filename": "", + "type": "Unarmed" + }, + "M 818": { + "name": "M 818", + "era": [ + "Early Cold War" + ], + "label": "M 818", + "shortLabel": "M 818", + "filename": "", + "type": "Unarmed" + }, + "MAZ-6303": { + "name": "MAZ-6303", + "era": [ + "Mid Cold War" + ], + "label": "MAZ-6303", + "shortLabel": "MAZ-6303", + "filename": "", + "type": "Unarmed" + }, + "Predator GCS": { + "name": "Predator GCS", + "era": [ + "Late Cold War" + ], + "label": "Predator GCS", + "shortLabel": "Predator GCS", + "filename": "", + "type": "Unarmed" + }, + "Predator TrojanSpirit": { + "name": "Predator TrojanSpirit", + "era": [ + "Late Cold War" + ], + "label": "Predator TrojanSpirit", + "shortLabel": "Predator TrojanSpirit", + "filename": "", + "type": "Unarmed" + }, + "Suidae": { + "name": "Suidae", + "era": [ + "Late Cold War" + ], + "label": "Suidae", + "shortLabel": "Suidae", + "filename": "", + "type": "Unarmed" + }, + "Tigr_233036": { + "name": "Tigr_233036", + "era": [ + "Late Cold War" + ], + "label": "Tigr_233036", + "shortLabel": "Tigr_233036", + "filename": "", + "type": "Unarmed" + }, + "Trolley bus": { + "name": "Trolley bus", + "era": [ + "Late Cold War" + ], + "label": "Trolley bus", + "shortLabel": "Trolley bus", + "filename": "", + "type": "Unarmed" + }, + "UAZ-469": { + "name": "UAZ-469", + "era": [ + "Mid Cold War" + ], + "label": "UAZ-469", + "shortLabel": "UAZ-469", + "filename": "", + "type": "Unarmed" + }, + "Ural ATsP-6": { + "name": "Ural ATsP-6", + "era": [ + "Mid Cold War" + ], + "label": "Ural ATsP-6", + "shortLabel": "Ural ATsP-6", + "filename": "", + "type": "Unarmed" + }, + "Ural-375 PBU": { + "name": "Ural-375 PBU", + "era": [ + "Mid Cold War" + ], + "label": "Ural-375 PBU", + "shortLabel": "Ural-375 PBU", + "filename": "", + "type": "Unarmed" + }, + "Ural-375": { + "name": "Ural-375", + "era": [ + "Mid Cold War" + ], + "label": "Ural-375", + "shortLabel": "Ural-375", + "filename": "", + "type": "Unarmed" + }, + "Ural-4320-31": { + "name": "Ural-4320-31", + "era": [ + "Late Cold War" + ], + "label": "Ural-4320-31", + "shortLabel": "Ural-4320-31", + "filename": "", + "type": "Unarmed" + }, + "Ural-4320T": { + "name": "Ural-4320T", + "era": [ + "Late Cold War" + ], + "label": "Ural-4320T", + "shortLabel": "Ural-4320T", + "filename": "", + "type": "Unarmed" + }, + "VAZ Car": { + "name": "VAZ Car", + "era": [ + "Early Cold War" + ], + "label": "VAZ Car", + "shortLabel": "VAZ Car", + "filename": "", + "type": "Unarmed" + }, + "ZiL-131 APA-80": { + "name": "ZiL-131 APA-80", + "era": [ + "Early Cold War" + ], + "label": "ZiL-131 APA-80", + "shortLabel": "ZiL-131 APA-80", + "filename": "", + "type": "Unarmed" + }, + "SKP-11": { + "name": "SKP-11", + "era": [ + "Early Cold War" + ], + "label": "SKP-11", + "shortLabel": "SKP-11", + "filename": "", + "type": "Unarmed" + }, + "ZIL-131 KUNG": { + "name": "ZIL-131 KUNG", + "era": [ + "Early Cold War" + ], + "label": "ZIL-131 KUNG", + "shortLabel": "ZIL-131 KUNG", + "filename": "", + "type": "Unarmed" + }, + "ZIL-4331": { + "name": "ZIL-4331", + "era": [ + "Early Cold War" + ], + "label": "ZIL-4331", + "shortLabel": "ZIL-4331", + "filename": "", + "type": "Unarmed" + } + } + } +} + +export var groundUnitDatabase = new GroundUnitDatabase(); diff --git a/client/src/units/groundunitsdatabase.ts b/client/src/units/groundunitsdatabase.ts deleted file mode 100644 index 3aacebf9..00000000 --- a/client/src/units/groundunitsdatabase.ts +++ /dev/null @@ -1,3671 +0,0 @@ -import { UnitDatabase } from "./unitdatabase" - -export class GroundUnitsDatabase extends UnitDatabase { - constructor() { - super(); - this.blueprints = { - "SA-2 SAM Battery": { - "name": "SA-2 SAM Battery", - "era": ["Early Cold War"], - "label": "SA-2 SAM Battery", - "shortLabel": "SA-2 SAM Battery", - "range": "Long", - "loadouts": [ - { - "fuel": 1, - "items": [ - ], - "roles": [ - "SAM Sites" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "SA-3 SAM Battery": { - "name": "SA-3 SAM Battery", - "era": ["Early Cold War"], - "label": "SA-3 SAM Battery", - "shortLabel": "SA-3 SAM Battery", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "SAM Sites" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "SA-6 SAM Battery": { - "name": "SA-6 SAM Battery", - "era": ["Mid Cold War"], - "label": "SA-6 SAM Battery", - "shortLabel": "SA-6 SAM Battery", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "SAM Sites" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "SA-10 SAM Battery": { - "name": "SA-10 SAM Battery", - "era": ["Late Cold War"], - "label": "SA-10 SAM Battery", - "shortLabel": "SA-10 SAM Battery", - "range": "Long", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "SAM Sites" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "SA-11 SAM Battery": { - "name": "SA-11 SAM Battery", - "era": ["Late Cold War"], - "label": "SA-11 SAM Battery", - "shortLabel": "SA-11 SAM Battery", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "SAM Sites" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Patriot site": { - "name": "Patriot site", - "era": ["Late Cold War"], - "label": "Patriot site", - "shortLabel": "Patriot site", - "range": "Long", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "SAM Sites" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Hawk SAM Battery": { - "name": "Hawk SAM Battery", - "era": ["Early Cold War"], - "label": "Hawk SAM Battery", - "shortLabel": "Hawk SAM Battery", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "SAM Sites" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "2B11 mortar": { - "name": "2B11 mortar", - "era": ["Late Cold War"], - "label": "2B11 mortar", - "shortLabel": "2B11 mortar", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "120mm Mortar Tube", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - - ], - "roles": [ - "Gun Artillery" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "SAU Gvozdika": { - "name": "SAU Gvozdika", - "era": ["Mid Cold War"], - "label": "SAU Gvozdika", - "shortLabel": "SAU Gvozdika", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "122mm Howitzer", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - } - - ], - "roles": [ - "Gun Artillery" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "SAU Msta": { - "name": "SAU Msta", - "era": ["Late Cold War"], - "label": "SAU Msta", - "shortLabel": "SAU Msta", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "152mm Howitzer", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - } - ], - "roles": [ - "Gun Artillery" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "SAU Akatsia": { - "name": "SAU Akatsia", - "era": ["Mid Cold War"], - "label": "SAU Akatsia", - "shortLabel": "SAU Akatsia", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "152mm Howitzer", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - } - ], - "roles": [ - "Gun Artillery" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "SAU 2-C9": { - "name": "SAU 2-C9", - "era": ["Mid Cold War"], - "label": "SAU Nona", - "shortLabel": "SAU Nona", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "120mm Mortar", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "Gun Artillery" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "M-109": { - "name": "M-109", - "era": ["Early Cold War"], - "label": "M-109 Paladin", - "shortLabel": "M-109", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "155mm Howitzer", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - } - ], - "roles": [ - "Gun Artillery" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "AAV7": { - "name": "AAV7", - "era": ["Mid Cold War"], - "label": "AAV7", - "shortLabel": "AAV7", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "12.7mm M2 HMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "APC" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "BMD-1": { - "name": "BMD-1", - "era": ["Mid Cold War"], - "label": "BMD-1", - "shortLabel": "BMD-1", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "73mm Gun", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "7.62mm PKT GPMG", - "quantity": 3, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "AT-3 Sagger ATGM", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - } - ], - "roles": [ - "IFV" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "BMP-1": { - "name": "BMP-1", - "era": ["Mid Cold War"], - "label": "BMP-1", - "shortLabel": "BMP-1", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "73mm Gun", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "7.62mm PKT GPMG", - "quantity": 3, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "AT-3 Sagger ATGM", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - } - ], - "roles": [ - "IFV" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "BMP-2": { - "name": "BMP-2", - "era": ["Mid Cold War"], - "label": "BMP-2", - "shortLabel": "BMP-2", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "30mm Autocannon", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "7.62mm PKT GPMG", - "quantity": 3, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "AT-5 Konkurs ATGM", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - } - ], - "roles": [ - "IFV" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "BMP-3": { - "name": "BMP-3", - "era": ["Late Cold War"], - "label": "BMP-3", - "shortLabel": "BMP-3", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "100mm Gun", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - }, - { - "name": "30mm Autocannon", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "7.62mm PKT GPMG", - "quantity": 3, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "AT-10 Stabber ATGM", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - } - ], - "roles": [ - "IFV" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Boman": { - "name": "Boman", - "era": ["Late Cold War"], - "label": "Grad Fire Direction Manager", - "shortLabel": "Boman", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "7.62mm PKMB GPMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "RPG-7", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - } - ], - "roles": [ - "Reconnaissance" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "BRDM-2": { - "name": "BRDM-2", - "era": ["Early Cold War"], - "label": "BRDM-2", - "shortLabel": "BRDM-2", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "14.5mm KPVT HMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "7.62mm PKT GPMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "Reconnaissance" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "BTR-80": { - "name": "BTR-80", - "era": ["Late Cold War"], - "label": "BTR-80", - "shortLabel": "BTR-80", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "14.5mm KPVT HMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "7.62mm PKT GPMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "APC" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "BTR_D": { - "name": "BTR_D", - "era": ["Mid Cold War"], - "label": "BTR_D", - "shortLabel": "BTR_D", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "7.62mm PKT GPMG", - "quantity": 2, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "AT-5 Konkurs ATGM", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - } - ], - "roles": [ - "APC" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Bunker": { - "name": "Bunker", - "label": "Bunker", - "shortLabel": "Bunker", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Static" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Cobra": { - "name": "Cobra", - "era": ["Modern"], - "label": "Otokar Cobra", - "shortLabel": "Cobra", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "12.7mm HMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "Reconnaissance" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "LAV-25": { - "name": "LAV-25", - "era": ["Late Cold War"], - "label": "LAV-25", - "shortLabel": "LAV-25", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "25mm M242 Autocannon", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "7.62mm M240 GPMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "IFV" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "M1043 HMMWV Armament": { - "name": "M1043 HMMWV Armament", - "era": ["Late Cold War"], - "label": "HMMWV M2 Browning", - "shortLabel": "HMMWV M2", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "12.7mm M2 HMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "Reconnaissance" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "M1045 HMMWV TOW": { - "name": "M1045 HMMWV TOW", - "era": ["Late Cold War"], - "label": "HMMWV TOW", - "shortLabel": "HMMWV TOW", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "BGM-71 TOW ATGM", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - } - ], - "roles": [ - "Reconnaissance" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "M1126 Stryker ICV": { - "name": "M1126 Stryker ICV", - "era": ["Modern"], - "label": "Stryker MG", - "shortLabel": "Stryker MG", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "12.7mm M2 HMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "APC" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "M-113": { - "name": "M-113", - "era": ["Early Cold War"], - "label": "M-113", - "shortLabel": "M-113", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "12.7mm M2 HMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "APC" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "M1134 Stryker ATGM": { - "name": "M1134 Stryker ATGM", - "era": ["Modern"], - "label": "Stryker ATGM", - "shortLabel": "Stryker ATGM", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "12.7mm M2 HMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "BGM-71 TOW", - "quantity": 2, - "effectiveAgainst": "Surface (Hard)" - } - ], - "roles": [ - "IFV" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "M-2 Bradley": { - "name": "M-2 Bradley", - "era": ["Late Cold War"], - "label": "M-2A2 Bradley", - "shortLabel": "M-2 Bradley", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "25mm M242 Autocannon", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "BGM-71 TOW", - "quantity": 2, - "effectiveAgainst": "Surface (Hard)" - }, - { - "name": "7.62mm M240 GPMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "IFV" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Marder": { - "name": "Marder", - "era": ["Late Cold War"], - "label": "Marder", - "shortLabel": "Marder", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "20mm MK 20 Rh 202 Autocannon", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "7.62mm MG3 GPMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "IFV" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "MCV-80": { - "name": "MCV-80", - "era": ["Late Cold War"], - "label": "Warrior IFV", - "shortLabel": "Warrior", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "30mm L21A1 Autocannon", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "7.62mm L94A1 GPMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "IFV" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "MTLB": { - "name": "MTLB", - "era": ["Mid Cold War"], - "label": "MT-LB", - "shortLabel": "MT-LB", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "7.62mm PKT GPMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "APC" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Paratrooper RPG-16": { - "name": "Paratrooper RPG-16", - "era": ["Modern"], - "label": "Paratrooper RPG-16", - "shortLabel": "Paratrooper RPG-16", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "RPG-16", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - } - ], - "roles": [ - "Infantry" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Paratrooper AKS-74": { - "name": "Paratrooper AKS-74", - "era": ["Modern"], - "label": "Paratrooper AKS-74", - "shortLabel": "Paratrooper AKS-74", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "5.45mm AKS-74", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "Infantry" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Sandbox": { - "name": "Sandbox", - "label": "Sandbox", - "shortLabel": "Sandbox", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Static" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Soldier AK": { - "name": "Soldier AK", - "era": ["Early Cold War"], - "label": "Soldier AK", - "shortLabel": "Soldier AK", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "5.45mm AK-74", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "Infantry" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Infantry AK": { - "name": "Infantry AK", - "era": ["Mid Cold War"], - "label": "Infantry AK", - "shortLabel": "Infantry AK", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "5.45mm AK-74", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "Infantry" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Soldier M249": { - "name": "Soldier M249", - "era": ["Late Cold War"], - "label": "Soldier M249", - "shortLabel": "Soldier M249", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "5.56mm M249 SAW", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "Infantry" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Soldier M4": { - "name": "Soldier M4", - "era": ["Mid Cold War"], - "label": "Soldier M4", - "shortLabel": "Soldier M4", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "5.56mm M4", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "Infantry" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Soldier M4 GRG": { - "name": "Soldier M4 GRG", - "era": ["Mid Cold War"], - "label": "Soldier M4 GRG", - "shortLabel": "Soldier M4 GRG", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "5.56mm M4", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "Infantry" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Soldier RPG": { - "name": "Soldier RPG", - "era": ["Mid Cold War"], - "label": "Soldier RPG", - "shortLabel": "Soldier RPG", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "RPG-16", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - } - ], - "roles": [ - "Infantry" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "TPZ": { - "name": "TPZ", - "era": ["Late Cold War"], - "label": "TPz Fuchs", - "shortLabel": "TPz Fuchs", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "7.62mm M3 GPMG", - "quantity": 1, - "effectiveAgainst": "Surface (soft)" - } - ], - "roles": [ - "APC" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Grad-URAL": { - "name": "Grad-URAL", - "era": ["Mid Cold War"], - "label": "Grad", - "shortLabel": "Grad", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "122mm Grad 9M21 Rocket", - "quantity": 40, - "effectiveAgainst": "Surface (Hard)" - } - ], - "roles": [ - "Rocket Artillery" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Uragan_BM-27": { - "name": "Uragan_BM-27", - "era": ["Late Cold War"], - "label": "Uragan", - "shortLabel": "Uragan", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "220mm Uragan 9M27 Rocket", - "quantity": 16, - "effectiveAgainst": "Surface (Hard)" - } - ], - "roles": [ - "Rocket Artillery" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Smerch": { - "name": "Smerch", - "era": ["Late Cold War"], - "label": "Smerch", - "shortLabel": "Smerch", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "300mm Smerch 9M55 Rocket", - "quantity": 12, - "effectiveAgainst": "Surface (Hard)" - } - ], - "roles": [ - "Rocket Artillery" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "MLRS": { - "name": "MLRS", - "era": ["Late Cold War"], - "label": "M270", - "shortLabel": "M270", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "227mm with 644 DPICM Submunitions", - "quantity": 12, - "effectiveAgainst": "Surface (Hard)" - } - ], - "roles": [ - "Rocket Artillery" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "2S6 Tunguska": { - "name": "2S6 Tunguska", - "era": ["Late Cold War"], - "label": "SA-19 Tunguska", - "shortLabel": "SA-19", - "range": "Short", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Twin Barrel 30mm 2A38M Autocannons", - "quantity": 2, - "effectiveAgainst": "Surface (Soft), Aircraft" - }, - { - "name": "9M311 SAM (Radio Command Guidance)", - "quantity": 4, - "effectiveAgainst": "Surface (Soft), Aircraft" - } - ], - "roles": [ - "AAA/SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Kub 2P25 ln": { - "name": "Kub 2P25 ln", - "era": ["Late Cold War"], - "label": "SA-6 Kub 2P25 ln", - "shortLabel": "Kub 2P25 ln", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "3M9M SAM (SARH)", - "quantity": 3, - "effectiveAgainst": "Aircraft" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "5p73 s-125 ln": { - "name": "5p73 s-125 ln", - "era": ["Early Cold War"], - "label": "SA-3 5p73 s-125 ln", - "shortLabel": "5p73 s-125 ln", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "SA-3 3M9M SAM (RF CLOS)", - "quantity": 3, - "effectiveAgainst": "Aircraft" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "S-300PS 5P85C ln": { - "name": "S-300PS 5P85C ln", - "era": ["Late Cold War"], - "label": "SA-10 S-300PS 5P85C ln", - "shortLabel": "S-300PS 5P85C ln", - "range": "Long", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "48N6 SAM (SARH)", - "quantity": 2, - "effectiveAgainst": "Aircraft" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "S-300PS 5P85D ln": { - "name": "S-300PS 5P85D ln", - "era": ["Late Cold War"], - "label": "SA-10 S-300PS 5P85D ln", - "shortLabel": "S-300PS 5P85D ln", - "range": "Long", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "48N6 SAM (SARH)", - "quantity": 4, - "effectiveAgainst": "Aircraft" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "SA-11 Buk LN 9A310M1": { - "name": "SA-11 Buk LN 9A310M1", - "era": ["Late Cold War"], - "label": "SA-11 Buk LN 9A310M1", - "shortLabel": "SA-11 Buk LN 9A310M1", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "9M38M1 SAM (SARH)", - "quantity": 4, - "effectiveAgainst": "Aircraft" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Osa 9A33 ln": { - "name": "Osa 9A33 ln", - "era": ["Mid Cold War"], - "label": "SA-8 Osa 9A33 ln", - "shortLabel": "Osa 9A33 ln", - "range": "Short", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "9M33 SAM (SARH)", - "quantity": 4, - "effectiveAgainst": "Aircraft" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Tor 9A331": { - "name": "Tor 9A331", - "era": ["Late Cold War"], - "label": "SA-15 Tor 9A331", - "shortLabel": "Tor 9A331", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "9M330 SAM (Radio Command Guidance)", - "quantity": 8, - "effectiveAgainst": "Aircraft" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Strela-10M3": { - "name": "Strela-10M3", - "era": ["Late Cold War"], - "label": "SA-13 Strela-10M3", - "shortLabel": "Strela-10M3", - "range": "Short", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "9M333 SAM (IR)", - "quantity": 4, - "effectiveAgainst": "Aircraft" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Strela-1 9P31": { - "name": "Strela-1 9P31", - "era": ["Late Cold War"], - "label": "SA-9 Strela-1 9P31", - "shortLabel": "Strela-1 9P31", - "range": "Short", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "9M31 SAM (IR)", - "quantity": 4, - "effectiveAgainst": "Aircraft" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "SA-11 Buk CC 9S470M1": { - "name": "SA-11 Buk CC 9S470M1", - "era": ["Late Cold War"], - "label": "SA-11 Buk CC 9S470M1", - "shortLabel": "SA-11 Buk CC 9S470M1", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Command Post", - "quantity": 1, - "effectiveAgainst": "None" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "SA-8 Osa LD 9T217": { - "name": "SA-8 Osa LD 9T217", - "era": ["Late Cold War"], - "label": "SA-8 Osa LD 9T217", - "shortLabel": "SA-8 Osa LD 9T217", - "range": "Short", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Transloader", - "quantity": 1, - "effectiveAgainst": "None" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Patriot AMG": { - "name": "Patriot AMG", - "era": ["Modern"], - "label": "Patriot AMG", - "shortLabel": "Patriot AMG", - "range": "Long", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Antenna Mast Group", - "quantity": 1, - "effectiveAgainst": "None" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Patriot ECS": { - "name": "Patriot ECS", - "era": ["Modern"], - "label": "Patriot ECS", - "shortLabel": "Patriot ECS", - "range": "Long", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Engagement Control Station", - "quantity": 1, - "effectiveAgainst": "None" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Gepard": { - "name": "Gepard", - "era": ["Late Cold War", "Modern"], - "label": "Gepard", - "shortLabel": "Gepard", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "35mm KDA Autocannon", - "quantity": 2, - "effectiveAgainst": "Surface (Soft), Aircraft" - } - ], - "roles": [ - "AAA" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Hawk pcp": { - "name": "Hawk pcp", - "era": ["Late Cold War"], - "label": "Hawk pcp", - "shortLabel": "Hawk pcp", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Command Post", - "quantity": 1, - "effectiveAgainst": "None" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "SA-18 Igla manpad": { - "name": "SA-18 Igla manpad", - "era": ["Late Cold War"], - "label": "SA-18 Igla manpad", - "shortLabel": "SA-18 Igla manpad", - "range": "Short", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "9K38 SAM (IR)", - "quantity": 1, - "effectiveAgainst": "Aircraft" - } - ], - "roles": [ - "MANPADS" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Igla manpad INS": { - "name": "Igla manpad INS", - "era": ["Late Cold War"], - "label": "SA-18 Igla manpad INS", - "shortLabel": "Igla manpad INS", - "range": "Short", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "9K38 SAM (IR)", - "quantity": 1, - "effectiveAgainst": "Aircraft" - } - ], - "roles": [ - "MANPADS" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "SA-18 Igla-S manpad": { - "name": "SA-18 Igla-S manpad", - "era": ["Late Cold War"], - "label": "SA-18 Igla-S manpad", - "shortLabel": "SA-18 Igla-S manpad", - "range": "Short", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "9K338 SAM (IR)", - "quantity": 1, - "effectiveAgainst": "Aircraft" - } - ], - "roles": [ - "MANPADS" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Vulcan": { - "name": "Vulcan", - "era": ["Late Cold War"], - "label": "Vulcan", - "shortLabel": "Vulcan", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "M168 20mm Vulcan", - "quantity": 1, - "effectiveAgainst": "Aircraft" - } - ], - "roles": [ - "AAA" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Hawk ln": { - "name": "Hawk ln", - "era": ["Late Cold War"], - "label": "Hawk ln", - "shortLabel": "Hawk ln", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "MIM 23B SAM (SARH)", - "quantity": 3, - "effectiveAgainst": "Aircraft" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "M48 Chaparral": { - "name": "M48 Chaparral", - "era": ["Late Cold War"], - "label": "M48 Chaparral", - "shortLabel": "M48 Chaparral", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "MIM-72G SAM (IR)", - "quantity": 4, - "effectiveAgainst": "Aircraft" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "M6 Linebacker": { - "name": "M6 Linebacker", - "era": ["Late Cold War"], - "label": "M6 Linebacker", - "shortLabel": "M6 Linebacker", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "M242 25mm Autocannon", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "7.62mm M240C GPMG", - "quantity": 4, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "Stinger SAM (IR)", - "quantity": 4, - "effectiveAgainst": "Aircraft" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Patriot ln": { - "name": "Patriot ln", - "era": ["Late Cold War"], - "label": "Patriot ln", - "shortLabel": "Patriot ln", - "range": "Long", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "MIM-104 SAM (SARH)", - "quantity": 4, - "effectiveAgainst": "Aircraft" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "M1097 Avenger": { - "name": "M1097 Avenger", - "era": ["Modern"], - "label": "M1097 Avenger", - "shortLabel": "M1097 Avenger", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "12.7mm M2 HMG", - "quantity": 2, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "Stinger SAM (IR)", - "quantity": 4, - "effectiveAgainst": "Aircraft" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Patriot EPP": { - "name": "Patriot EPP", - "era": ["Late Cold War"], - "label": "Patriot EPP", - "shortLabel": "Patriot EPP", - "range": "Long", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Diesel-Electric Generator", - "quantity": 1, - "effectiveAgainst": "None" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Patriot cp": { - "name": "Patriot cp", - "era": ["Late Cold War"], - "label": "Patriot cp", - "shortLabel": "Patriot cp", - "range": "Long", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Command Post", - "quantity": 1, - "effectiveAgainst": "None" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Roland ADS": { - "name": "Roland ADS", - "era": ["Late Cold War"], - "label": "Roland ADS", - "shortLabel": "Roland ADS", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "MIM-115 SAM (Radio Command Guidance)", - "quantity": 2, - "effectiveAgainst": "Aircraft" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "S-300PS 54K6 cp": { - "name": "S-300PS 54K6 cp", - "era": ["Late Cold War"], - "label": "SA-10 S-300PS 54K6 cp", - "shortLabel": "S-300PS 54K6 cp", - "range": "Long", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Command Post", - "quantity": 1, - "effectiveAgainst": "None" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Stinger manpad GRG": { - "name": "Stinger manpad GRG", - "era": ["Late Cold War"], - "label": "Stinger manpad GRG", - "shortLabel": "Stinger manpad GRG", - "range": "Short", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Stinger SAM (IR)", - "quantity": 1, - "effectiveAgainst": "Aircraft" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Stinger manpad dsr": { - "name": "Stinger manpad dsr", - "era": ["Late Cold War"], - "label": "Stinger manpad dsr", - "shortLabel": "Stinger manpad dsr", - "range": "Short", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Stinger SAM (IR)", - "quantity": 1, - "effectiveAgainst": "Aircraft" - } - ], - "roles": [ - "MANPADS" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Stinger comm dsr": { - "name": "Stinger comm dsr", - "era": ["Late Cold War"], - "label": "Stinger comm dsr", - "shortLabel": "Stinger comm dsr", - "range": "Short", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Commander", - "quantity": 1, - "effectiveAgainst": "None" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Stinger manpad": { - "name": "Stinger manpad", - "era": ["Late Cold War"], - "label": "Stinger manpad", - "shortLabel": "Stinger manpad", - "range": "Short", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Stinger SAM (IR)", - "quantity": 1, - "effectiveAgainst": "Aircraft" - } - ], - "roles": [ - "MANPADS" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Stinger comm": { - "name": "Stinger comm", - "era": ["Late Cold War"], - "label": "Stinger comm", - "shortLabel": "Stinger comm", - "range": "Short", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Commander", - "quantity": 1, - "effectiveAgainst": "None" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "ZSU-23-4 Shilka": { - "name": "ZSU-23-4 Shilka", - "era": ["Late Cold War"], - "label": "ZSU-23-4 Shilka", - "shortLabel": "ZSU-23-4 Shilka", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "23mm AZP-23M Autocannon", - "quantity": 4, - "effectiveAgainst": "Surface (Soft), Aircraft" - } - ], - "roles": [ - "AAA" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "ZU-23 Emplacement Closed": { - "name": "ZU-23 Emplacement Closed", - "era": ["Late Cold War"], - "label": "ZU-23 Emplacement Closed", - "shortLabel": "ZU-23 Emplacement Closed", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "23mm 2A14 Autocannon", - "quantity": 4, - "effectiveAgainst": "Surface (Soft), Aircraft" - } - ], - "roles": [ - "AAA" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "ZU-23 Emplacement": { - "name": "ZU-23 Emplacement", - "era": ["Late Cold War"], - "label": "ZU-23 Emplacement", - "shortLabel": "ZU-23 Emplacement", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "23mm 2A14 Autocannon", - "quantity": 4, - "effectiveAgainst": "Surface (Soft), Aircraft" - } - ], - "roles": [ - "AAA" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "ZU-23 Closed Insurgent": { - "name": "ZU-23 Closed Insurgent", - "era": ["Late Cold War"], - "label": "ZU-23 Closed Insurgent", - "shortLabel": "ZU-23 Closed Insurgent", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "23mm 2A14 Autocannon", - "quantity": 4, - "effectiveAgainst": "Surface (Soft), Aircraft" - } - ], - "roles": [ - "AAA" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Ural-375 ZU-23 Insurgent": { - "name": "Ural-375 ZU-23 Insurgent", - "era": ["Late Cold War"], - "label": "Ural-375 ZU-23 Insurgent", - "shortLabel": "Ural-375 ZU-23 Insurgent", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "23mm 2A14 Autocannon", - "quantity": 4, - "effectiveAgainst": "Surface (Soft), Aircraft" - } - ], - "roles": [ - "AAA" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "ZU-23 Insurgent": { - "name": "ZU-23 Insurgent", - "era": ["Late Cold War"], - "label": "ZU-23 Insurgent", - "shortLabel": "ZU-23 Insurgent", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "23mm 2A14 Autocannon", - "quantity": 4, - "effectiveAgainst": "Surface (Soft), Aircraft" - } - ], - "roles": [ - "AAA" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Ural-375 ZU-23": { - "name": "Ural-375 ZU-23", - "era": ["Late Cold War"], - "label": "Ural-375 ZU-23", - "shortLabel": "Ural-375 ZU-23", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "23mm 2A14 Autocannon", - "quantity": 4, - "effectiveAgainst": "Surface (Soft), Aircraft" - } - ], - "roles": [ - "AAA" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "1L13 EWR": { - "name": "1L13 EWR", - "era": ["Late Cold War"], - "label": "1L13 EWR", - "shortLabel": "1L13 EWR", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Early Warning Radar", - "quantity": 1, - "effectiveAgainst": "None" - } - ], - "roles": [ - "Radar" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Kub 1S91 str": { - "name": "Kub 1S91 str", - "era": ["Mid Cold War"], - "label": "SA-6 Kub 1S91 str", - "shortLabel": "Kub 1S91 str", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Search and Track Radar", - "quantity": 1, - "effectiveAgainst": "None" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "S-300PS 40B6M tr": { - "name": "S-300PS 40B6M tr", - "era": ["Late Cold War"], - "label": "SA-10 S-300PS 40B6M tr", - "shortLabel": "S-300PS 40B6M tr", - "range": "Long", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Track Radar", - "quantity": 1, - "effectiveAgainst": "None" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "S-300PS 40B6MD sr": { - "name": "S-300PS 40B6MD sr", - "era": ["Late Cold War"], - "label": "SA-10 S-300PS 40B6MD sr", - "shortLabel": "S-300PS 40B6MD sr", - "range": "Long", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Search Radar", - "quantity": 1, - "effectiveAgainst": "None" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "55G6 EWR": { - "name": "55G6 EWR", - "era": ["Early Cold War"], - "label": "55G6 EWR", - "shortLabel": "55G6 EWR", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Early Warning Radar", - "quantity": 1, - "effectiveAgainst": "None" - } - ], - "roles": [ - "Radar" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "S-300PS 64H6E sr": { - "name": "S-300PS 64H6E sr", - "era": ["Late Cold War"], - "label": "SA-10 S-300PS 64H6E sr", - "shortLabel": "S-300PS 64H6E sr", - "range": "Long", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Search Radar", - "quantity": 1, - "effectiveAgainst": "None" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "SA-11 Buk SR 9S18M1": { - "name": "SA-11 Buk SR 9S18M1", - "era": ["Mid Cold War"], - "label": "SA-11 Buk SR 9S18M1", - "shortLabel": "SA-11 Buk SR 9S18M1", - "range": "Long", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Search Radar", - "quantity": 1, - "effectiveAgainst": "None" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Dog Ear radar": { - "name": "Dog Ear radar", - "era": ["Mid Cold War"], - "label": "Dog Ear radar", - "shortLabel": "Dog Ear radar", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Search Radar", - "quantity": 1, - "effectiveAgainst": "None" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Hawk tr": { - "name": "Hawk tr", - "era": ["Early Cold War"], - "label": "Hawk tr", - "shortLabel": "Hawk tr", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Track Radar", - "quantity": 1, - "effectiveAgainst": "None" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Hawk sr": { - "name": "Hawk sr", - "era": ["Early Cold War"], - "label": "Hawk sr", - "shortLabel": "Hawk sr", - "range": "Long", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Search Radar", - "quantity": 1, - "effectiveAgainst": "None" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Patriot str": { - "name": "Patriot str", - "era": ["Late Cold War"], - "label": "Patriot str", - "shortLabel": "Patriot str", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Search and Track Radar", - "quantity": 1, - "effectiveAgainst": "None" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Hawk cwar": { - "name": "Hawk cwar", - "era": ["Early Cold War"], - "label": "Hawk cwar", - "shortLabel": "Hawk cwar", - "range": "Long", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Search and Track Radar", - "quantity": 1, - "effectiveAgainst": "None" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "p-19 s-125 sr": { - "name": "p-19 s-125 sr", - "era": ["Mid Cold War"], - "label": "SA-3 p-19 s-125 sr", - "shortLabel": "p-19 s-125 sr", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Search Radar", - "quantity": 1, - "effectiveAgainst": "None" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Roland Radar": { - "name": "Roland Radar", - "era": ["Mid Cold War"], - "label": "Roland Radar", - "shortLabel": "Roland Radar", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Search Radar", - "quantity": 1, - "effectiveAgainst": "None" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "snr s-125 tr": { - "name": "snr s-125 tr", - "era": ["Early Cold War"], - "label": "SA-3 snr s-125 tr", - "shortLabel": "snr s-125 tr", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "Track Radar", - "quantity": 1, - "effectiveAgainst": "None" - } - ], - "roles": [ - "SAM" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "house1arm": { - "name": "house1arm", - "label": "house1arm", - "shortLabel": "house1arm", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Structure" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "house2arm": { - "name": "house2arm", - "label": "house2arm", - "shortLabel": "house2arm", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Structure" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "outpost_road": { - "name": "outpost_road", - "label": "outpost_road", - "shortLabel": "outpost_road", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Structure" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "outpost": { - "name": "outpost", - "label": "outpost", - "shortLabel": "outpost", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Structure" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "houseA_arm": { - "name": "houseA_arm", - "label": "houseA_arm", - "shortLabel": "houseA_arm", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Structure" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Challenger2": { - "name": "Challenger2", - "era": ["Modern"], - "label": "Challenger2", - "shortLabel": "Challenger2", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "120mm L30A1 Gun", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - }, - { - "name": "7.62mm L94A1 GPMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "7.62mm L37A2 GPMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "Tank" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Leclerc": { - "name": "Leclerc", - "era": ["Modern"], - "label": "Leclerc", - "shortLabel": "Leclerc", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "120mm F1 Gun", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - }, - { - "name": "12.7mm M2HB HMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "Tank" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Leopard1A3": { - "name": "Leopard1A3", - "era": ["Mid Cold War"], - "label": "Leopard1A3", - "shortLabel": "Leopard1A3", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "105mm L7A3 Gun", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - }, - { - "name": "7.62mm MG3 GPMG", - "quantity": 2, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "Tank" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Leopard-2": { - "name": "Leopard-2", - "era": ["Late Cold War"], - "label": "Leopard-2", - "shortLabel": "Leopard-2", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "120mm Rh L/44 Gun", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - }, - { - "name": "7.62mm MG3 GPMG", - "quantity": 2, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "Tank" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "M-60": { - "name": "M-60", - "era": ["Early Cold War"], - "label": "M-60", - "shortLabel": "M-60", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "105mm M68 Gun", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - }, - { - "name": "7.62mm M73 GPMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "12.7mm M85 HMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "Tank" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "M1128 Stryker MGS": { - "name": "M1128 Stryker MGS", - "era": ["Modern"], - "label": "M1128 Stryker MGS", - "shortLabel": "M1128 Stryker MGS", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "105mm M68 Gun", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - }, - { - "name": "7.62mm M240 GPMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "SPG" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "M-1 Abrams": { - "name": "M-1 Abrams", - "era": ["Late Cold War"], - "label": "M-1 Abrams", - "shortLabel": "M-1 Abrams", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "120mm Rh L/44 Gun", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - }, - { - "name": "7.62mm M240 GPMG", - "quantity": 2, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "12.7mm M2 HMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "Tank" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "T-55": { - "name": "T-55", - "era": ["Early Cold War"], - "label": "T-55", - "shortLabel": "T-55", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "100mm D-10T Gun", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - }, - { - "name": "7.62mm SGMT GPMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "12.7mm DShK HMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "Tank" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "T-72B": { - "name": "T-72B", - "era": ["Mid Cold War"], - "label": "T-72B", - "shortLabel": "T-72B", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "125mm 2A46M Gun", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - }, - { - "name": "7.62mm PKT GPMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "12.7mm NSVT HMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "Tank" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "T-80UD": { - "name": "T-80UD", - "era": ["Mid Cold War"], - "label": "T-80UD", - "shortLabel": "T-80UD", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "125mm 2A46M Gun", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - }, - { - "name": "7.62mm PKT GPMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "12.7mm NSVT HMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - } - ], - "roles": [ - "Tank" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "T-90": { - "name": "T-90", - "era": ["Late Cold War"], - "label": "T-90", - "shortLabel": "T-90", - "loadouts": [ - { - "fuel": 1, - "items": [ - { - "name": "125mm 2A46M-5 Gun", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - }, - { - "name": "7.62mm PKT GPMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "12.7mm NSVT HMG", - "quantity": 1, - "effectiveAgainst": "Surface (Soft)" - }, - { - "name": "9K119M ATGM", - "quantity": 1, - "effectiveAgainst": "Surface (Hard)" - } - ], - "roles": [ - "Tank" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Ural-4320 APA-5D": { - "name": "Ural-4320 APA-5D", - "era": ["Early Cold War"], - "label": "Ural-4320 APA-5D", - "shortLabel": "Ural-4320 APA-5D", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "ATMZ-5": { - "name": "ATMZ-5", - "era": ["Early Cold War"], - "label": "ATMZ-5", - "shortLabel": "ATMZ-5", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "ATZ-10": { - "name": "ATZ-10", - "era": ["Early Cold War"], - "label": "ATZ-10", - "shortLabel": "ATZ-10", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "GAZ-3307": { - "name": "GAZ-3307", - "era": ["Early Cold War"], - "label": "GAZ-3307", - "shortLabel": "GAZ-3307", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "GAZ-3308": { - "name": "GAZ-3308", - "era": ["Early Cold War"], - "label": "GAZ-3308", - "shortLabel": "GAZ-3308", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "GAZ-66": { - "name": "GAZ-66", - "era": ["Early Cold War"], - "label": "GAZ-66", - "shortLabel": "GAZ-66", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "M978 HEMTT Tanker": { - "name": "M978 HEMTT Tanker", - "era": ["Mid Cold War"], - "label": "M978 HEMTT Tanker", - "shortLabel": "M978 HEMTT Tanker", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "HEMTT TFFT": { - "name": "HEMTT TFFT", - "era": ["Late Cold War"], - "label": "HEMTT TFFT", - "shortLabel": "HEMTT TFFT", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "IKARUS Bus": { - "name": "IKARUS Bus", - "era": ["Mid Cold War"], - "label": "IKARUS Bus", - "shortLabel": "IKARUS Bus", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "KAMAZ Truck": { - "name": "KAMAZ Truck", - "era": ["Mid Cold War"], - "label": "KAMAZ Truck", - "shortLabel": "KAMAZ Truck", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "LAZ Bus": { - "name": "LAZ Bus", - "era": ["Early Cold War"], - "label": "LAZ Bus", - "shortLabel": "LAZ Bus", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Hummer": { - "name": "Hummer", - "era": ["Mid Cold War"], - "label": "Hummer", - "shortLabel": "Hummer", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "M 818": { - "name": "M 818", - "era": ["Early Cold War"], - "label": "M 818", - "shortLabel": "M 818", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "MAZ-6303": { - "name": "MAZ-6303", - "era": ["Mid Cold War"], - "label": "MAZ-6303", - "shortLabel": "MAZ-6303", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Predator GCS": { - "name": "Predator GCS", - "era": ["Late Cold War"], - "label": "Predator GCS", - "shortLabel": "Predator GCS", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Predator TrojanSpirit": { - "name": "Predator TrojanSpirit", - "era": ["Late Cold War"], - "label": "Predator TrojanSpirit", - "shortLabel": "Predator TrojanSpirit", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Suidae": { - "name": "Suidae", - "era": ["Late Cold War"], - "label": "Suidae", - "shortLabel": "Suidae", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Tigr_233036": { - "name": "Tigr_233036", - "era": ["Late Cold War"], - "label": "Tigr_233036", - "shortLabel": "Tigr_233036", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Trolley bus": { - "name": "Trolley bus", - "era": ["Late Cold War"], - "label": "Trolley bus", - "shortLabel": "Trolley bus", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "UAZ-469": { - "name": "UAZ-469", - "era": ["Mid Cold War"], - "label": "UAZ-469", - "shortLabel": "UAZ-469", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Ural ATsP-6": { - "name": "Ural ATsP-6", - "era": ["Mid Cold War"], - "label": "Ural ATsP-6", - "shortLabel": "Ural ATsP-6", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Ural-375 PBU": { - "name": "Ural-375 PBU", - "era": ["Mid Cold War"], - "label": "Ural-375 PBU", - "shortLabel": "Ural-375 PBU", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Ural-375": { - "name": "Ural-375", - "era": ["Mid Cold War"], - "label": "Ural-375", - "shortLabel": "Ural-375", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Ural-4320-31": { - "name": "Ural-4320-31", - "era": ["Late Cold War"], - "label": "Ural-4320-31", - "shortLabel": "Ural-4320-31", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Ural-4320T": { - "name": "Ural-4320T", - "era": ["Late Cold War"], - "label": "Ural-4320T", - "shortLabel": "Ural-4320T", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "VAZ Car": { - "name": "VAZ Car", - "era": ["Early Cold War"], - "label": "VAZ Car", - "shortLabel": "VAZ Car", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "ZiL-131 APA-80": { - "name": "ZiL-131 APA-80", - "era": ["Early Cold War"], - "label": "ZiL-131 APA-80", - "shortLabel": "ZiL-131 APA-80", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "SKP-11": { - "name": "SKP-11", - "era": ["Early Cold War"], - "label": "SKP-11", - "shortLabel": "SKP-11", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "ZIL-131 KUNG": { - "name": "ZIL-131 KUNG", - "era": ["Early Cold War"], - "label": "ZIL-131 KUNG", - "shortLabel": "ZIL-131 KUNG", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "ZIL-4331": { - "name": "ZIL-4331", - "era": ["Early Cold War"], - "label": "ZIL-4331", - "shortLabel": "ZIL-4331", - "loadouts": [ - { - "fuel": 1, - "items": [ - - ], - "roles": [ - "Unarmed" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - } - } - } -} - -export var groundUnitsDatabase = new GroundUnitsDatabase(); diff --git a/client/src/units/navalunitdatabase.ts b/client/src/units/navalunitdatabase.ts deleted file mode 100644 index 2333c071..00000000 --- a/client/src/units/navalunitdatabase.ts +++ /dev/null @@ -1,1711 +0,0 @@ -import { UnitDatabase } from "./unitdatabase" - -export class NavalDatabase extends UnitDatabase { - constructor() { - super(); - this.blueprints = { - "052B DDG-168 Guangzhou": { - "name": "052B DDG-168 Guangzhou", - "type": "Modern", - "era": ["Mid Cold War"], - "label": "052B DDG-168 Guangzhou", - "shortLabel": "052B DDG-168 Guangzhou", - "range": "Short", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "052C DDG-171 Haikou": { - "name": "052C DDG-171 Haikou", - "type": "Destroyer", - "era": ["Modern"], - "label": "052C DDG-171 Haikou", - "shortLabel": "052C DDG-171 Haikou", - "range": "Short", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "054A FFG-538 Yantai": { - "name": "054A FFG-538 Yantai", - "type": "Frigate", - "era": ["Modern"], - "label": "054A FFG-538 Yantai", - "shortLabel": "054A FFG-538 Yantai", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Type 071": { - "name": "Type 071", - "type": "Transport", - "era": ["Modern"], - "label": "Type 071", - "shortLabel": "Type 071", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Type 093": { - "name": "Type 093", - "type": "Submarine", - "era": ["Modern"], - "label": "Type 093", - "shortLabel": "Type 093", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Akizuki": { - "name": "Akizuki", - "type": "Destroyer", - "era": ["WW2"], - "label": "Akizuki", - "shortLabel": "Akizuki", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "ARA Santa Fe S-21": { - "name": "ARA Santa Fe S-21", - "type": "Submarine", - "era": ["Early Cold War"], - "label": "ARA Santa Fe S-21", - "shortLabel": "ARA Santa Fe S-21", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "ARA Vienticinco de Mayo": { - "name": "ARA Vienticinco de Mayo", - "type": "Aircraft Carrier", - "era": ["Mid Cold War"], - "label": "ARA Vienticinco de Mayo", - "shortLabel": "ARA Vienticinco de Mayo", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Admiral Kuznetsov": { - "name": "Admiral Kuznetsov", - "type": "Aircraft Carrier", - "era": ["Late Cold War"], - "label": "Admiral Kuznetsov", - "shortLabel": "Admiral Kuznetsov", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Albatros (Grisha-5)": { - "name": "Albatros (Grisha-5)", - "type": "Aircraft Carrier", - "era": ["Early Cold War"], - "label": "Albatros (Grisha-5)", - "shortLabel": "Albatros (Grisha-5)", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Almirante Condell PFG-06": { - "name": "Almirante Condell PFG-06", - "type": "Frigate", - "era": ["Mid Cold War"], - "label": "Almirante Condell PFG-06", - "shortLabel": "Almirante Condell PFG-06", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Almirante lynch PFG-07": { - "name": "Almirante lynch PFG-07", - "type": "Frigate", - "era": ["Mid Cold War"], - "label": "Almirante lynch PFG-07", - "shortLabel": "Almirante lynch PFG-07", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Boat Armed Hi-Speed": { - "name": "Boat Armed Hi-Speed", - "type": "Fast Attack Craft", - "era": ["Mid Cold War"], - "label": "Boat Armed Hi-Speed", - "shortLabel": "Boat Armed Hi-Speed", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Boat LCVP Higgins": { - "name": "Boat LCVP Higgins", - "type": "Landing Craft", - "era": ["WW2"], - "label": "Boat LCVP Higgins", - "shortLabel": "Boat LCVP Higgins", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Boat Schnellboot type S130": { - "name": "Boat Schnellboot type S130", - "type": "Fast Attack Craft", - "era": ["WW2"], - "label": "Boat Schnellboot type S130", - "shortLabel": "Boat Schnellboot type S130", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Bulker Handy Wind": { - "name": "Bulker Handy Wind", - "type": "Cargoship", - "era": ["Late Cold War"], - "label": "Bulker Handy Wind", - "shortLabel": "Bulker Handy Wind", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "CV Admiral Kuznetsov(2017)": { - "name": "CV 1143.5 Admiral Kuznetsov(2017)", - "type": "Aircraft Carrier", - "era": ["Modern"], - "label": "CV Admiral Kuznetsov(2017)", - "shortLabel": "Admiral Kuznetsov(2017)", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "CV-59 Forrestal": { - "name": "CV-59 Forrestal", - "type": "Aircraft Carrier", - "era": ["Early Cold War"], - "label": "CV-59 Forrestal", - "shortLabel": "CV-59 Forrestal", - "range": "Short", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "CV6 USS Enterprise": { - "name": "CV6 USS Enterprise -The Grey Ghost-", - "type": "Aircraft Carrier", - "era": ["WW2"], - "label": "CV6 USS Enterprise Grey Ghost", - "shortLabel": "CV6 USS Enterprise", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "CVN-71 Theodore Roosevelt": { - "name": "CVN-71 Theodore Roosevelt", - "type": "Super Aircraft Carrier", - "era": ["Late Cold War"], - "label": "CVN-71 Theodore Roosevelt", - "shortLabel": "CVN-71 Theodore Roosevelt", - "range": "Short", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "CVN-72 Abraham Lincoln": { - "name": "CVN-72 Abraham Lincoln", - "type": "Super Aircraft Carrier", - "era": ["Late Cold War"], - "label": "CVN-72 Abraham Lincoln", - "shortLabel": "CVN-72 Abraham Lincoln", - "range": "Short", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "CVN-73 George Washington": { - "name": "CVN-73 George Washington", - "type": "Super Aircraft Carrier", - "era": ["Late Cold War"], - "label": "CVN-73 George Washington", - "shortLabel": "CVN-73 George Washington", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "CVN-74 John C. Stennis": { - "name": "CVN-74 John C. Stennis", - "type": "Aircraft Carrier", - "era": ["Late Cold War"], - "label": "CVN-74 John C. Stennis", - "shortLabel": "CVN-74 John C. Stennis", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "CVN-75 Harry S. Truman": { - "name": "CVN-75 Harry S. Truman", - "type": "Aircraft Carrier", - "era": ["Late Cold War"], - "label": "CVN-75 Harry S. Truman", - "shortLabel": "CVN-75 Harry S. Truman", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "HMS Leeds Castle (P-258)": { - "name": "Castle Class", - "type": "Patrol", - "era": ["Mid Cold War"], - "label": "HMS Leeds Castle (P-258)", - "shortLabel": "HMS Leeds Castle (P-258)", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "DDG Arleigh Burke lla": { - "name": "DDG Arleigh Burke lla", - "type": "Destroyer", - "era": ["Late Cold War"], - "label": "DDG Arleigh Burke lla", - "shortLabel": "DDG Arleigh Burke", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "DKM Admiral Hipper": { - "name": "DKM Admiral Hipper", - "type": "Cruiser", - "era": ["WW2"], - "label": "DKM Admiral Hipper", - "shortLabel": "DKM Admiral Hipper", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "DKM Admiral Scheer": { - "name": "DKM Admiral Scheer", - "type": "Cruiser", - "era": ["WW2"], - "label": "DKM Admiral Scheer", - "shortLabel": "DKM Admiral Scheer", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "DKM Blucher": { - "name": "DKM Blucher", - "type": "Battleship", - "era": ["WW2"], - "label": "DKM Blucher", - "shortLabel": "DKM Blucher", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "DKM Gneisenau": { - "name": "DKM Blucher", - "type": "Battleship", - "era": ["WW2"], - "label": "DKM Blucher", - "shortLabel": "DKM Blucher", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "DKM Prinz Eugen": { - "name": "DKM Prinz Eugen", - "type": "Cruiser", - "era": ["WW2"], - "label": "DKM Prinz Eugen", - "shortLabel": "DKM Prinz Eugen", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "DKM Scharnhorst": { - "name": "Scharnhorst", - "type": "Battleship", - "era": ["WW2"], - "label": "DKM Scharnhorst", - "shortLabel": "DKM Scharnhorst", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "DKM Tirpiz": { - "name": "DKM Tirpiz", - "type": "Battleship", - "era": ["WW2"], - "label": "DKM Tirpiz", - "shortLabel": "DKM Tirpiz", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "DKM Z39": { - "name": "DKM Z39", - "type": "Destroyer", - "era": ["WW2"], - "label": "DKM Z39", - "shortLabel": "DKM Z39", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Dry cargo ship Ivanov": { - "name": "Dry cargo ship Ivanov", - "type": "Cargoship", - "era": ["Late Cold War"], - "label": "Dry cargo ship Ivanov", - "shortLabel": "Dry cargo ship Ivanov", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Dry cargo ship Yakushev": { - "name": "Dry cargo ship Yakushev", - "type": "Cargoship", - "era": ["Late Cold War"], - "label": "Dry cargo ship Yakushev", - "shortLabel": "Dry cargo ship Yakushev", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Elnya tanker": { - "name": "Elnya tanker", - "type": "Tanker", - "era": ["Late Cold War"], - "label": "Elnya tanker", - "shortLabel": "Elnya tanker", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "FAC La Combattante lla": { - "name": "FAC La Combattante lla", - "type": "Fast Attack Craft", - "era": ["Mid Cold War"], - "label": "FAC La Combattante lla", - "shortLabel": "FAC La Combattante", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Fletcher-Class destroyer": { - "name": "Fletcher-Class destroyer", - "type": "Destroyer", - "era": ["WW2"], - "label": "Fletcher-Class destroyer", - "shortLabel": "Fletcher-Class destroyer", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "HMS Achilles (F12)": { - "name": "HMS Achilles (F12)", - "type": "Frigate", - "era": ["Mid Cold War"], - "label": "HMS Achilles (F12)", - "shortLabel": "HMS Achilles", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "HMS Andromeda (F57)": { - "name": "HMS Andromeda (F57)", - "type": "Frigate", - "era": ["Mid Cold War"], - "label": "HMS Andromeda (F57)", - "shortLabel": "HMS Andromeda", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "HMS Ariadne (F72)": { - "name": "HMS Ariadne (F72)", - "type": "Frigate", - "era": ["Mid Cold War"], - "label": "HMS Ariadne (F72)", - "shortLabel": "HMS Ariadne", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "HMS Invincible (R05)": { - "name": "HMS Invincible (R05)", - "type": "Aircraft Carrier", - "era": ["Mid Cold War"], - "label": "HMS Invincible (R05)", - "shortLabel": "HMS Invincible", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Harbor Tug": { - "name": "Harbor Tug", - "type": "Tug", - "era": ["Mid Cold War"], - "label": "Harbor Tug", - "shortLabel": "Harbor Tug", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Improved Kilo": { - "name": "Improved Kilo", - "type": "Submarine", - "era": ["Late Cold War"], - "label": "Project 636 Varshavyanka", - "shortLabel": "Varshavyanka", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Kilo": { - "name": "Kilo", - "type": "Submarine", - "era": ["Late Cold War"], - "label": "Project 636 Varshavyanka Basic", - "shortLabel": "Varshavyanka Basic", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "LHA-1 Tarawa": { - "name": "LHA-1 Tarawa", - "type": "Aircraft Carrier", - "era": ["Mid Cold War"], - "label": "LHA-1 Tarawa", - "shortLabel": "LHA-1 Tarawa", - "range": "Short", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "LS Ropucha": { - "name": "LS Ropucha", - "type": "Landing Craft", - "era": ["Mid Cold War"], - "label": "LS Ropucha", - "shortLabel": "LS Ropucha", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "LST Mk2": { - "name": "LST Mk2", - "type": "Transport", - "era": ["WW2"], - "label": "LST Mk2", - "shortLabel": "LST Mk2", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Molniya (Tarantul-3)": { - "name": "Molniya (Tarantul-3)", - "type": "Fast Attack Craft", - "era": ["Late Cold War"], - "label": "Molniya (Tarantul-3)", - "shortLabel": "Molniya (Tarantul-3)", - "range": "Short", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Moscow": { - "name": "Moscow", - "type": "Cruiser", - "era": ["Late Cold War"], - "label": "Moscow", - "shortLabel": "Moscow", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Neustrashimy": { - "name": "Neustrashimy", - "type": "Frigate", - "era": ["Late Cold War"], - "label": "Neustrashimy", - "shortLabel": "Neustrashimy", - "range": "Short", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Oliver H. Perry": { - "name": "Oliver H. Perry", - "type": "Frigate", - "era": ["Mid Cold War"], - "label": "Oliver H. Perry", - "shortLabel": "Oliver H. Perry", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Pyotr Velikiy": { - "name": "Pyotr Velikiy", - "type": "Cruiser", - "era": ["Late Cold War"], - "label": "Pyotr Velikiy", - "shortLabel": "Pyotr Velikiy", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Rezky (Krivak-2)": { - "name": "Rezky (Krivak-2)", - "type": "Frigate", - "era": ["Early Cold War"], - "label": "Rezky (Krivak-2)", - "shortLabel": "Rezky", - "range": "Short", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Supply Ship MV Tilde": { - "name": "Supply Ship MV Tilde", - "type": "Transport", - "era": ["Late Cold War"], - "label": "Supply Ship MV Tilde", - "shortLabel": "Supply Ship MV Tilde", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Tanker Seawise Giant": { - "name": "Tanker Seawise Giant", - "type": "Tanker", - "era": ["Late Cold War"], - "label": "Tanker Seawise Giant", - "shortLabel": "Tanker Seawise Giant", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Ticonderoga": { - "name": "Ticonderoga", - "type": "Cruiser", - "era": ["Late Cold War"], - "label": "Ticonderoga", - "shortLabel": "Ticonderoga", - "range": "Medium", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "U-boat VIIC U-flak": { - "name": "U-boat VIIC U-flak", - "type": "Submarine", - "era": ["WW2"], - "label": "U-boat VIIC U-flak", - "shortLabel": "U-boat VIIC U-flak", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "USS Bell DD-587": { - "name": "USS Bell DD-587", - "type": "Destroyer", - "era": ["WW2"], - "label": "USS Bell DD-587", - "shortLabel": "USS Bell DD-587", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "USS Cassin Young": { - "name": "USS Cassin Young", - "type": "Destroyer", - "era": ["WW2"], - "label": "USS Cassin Young", - "shortLabel": "USS Cassin Young", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "USS Cotten DD-669": { - "name": "USS Cotten DD-669", - "type": "Destroyer", - "era": ["WW2"], - "label": "USS Cotten DD-669", - "shortLabel": "USS Cotten", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "USS Enterprise": { - "name": "USS Enterprise", - "type": "Aircraft Carrier", - "era": ["WW2"], - "label": "CV-6 USS Enterprise", - "shortLabel": "USS Enterprise", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "USS Fletcher": { - "name": "USS Fletcher", - "type": "Destroyer", - "era": ["WW2"], - "label": "USS Fletcher", - "shortLabel": "USS Fletcher", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "USS Franklin -Big Ben-": { - "name": "USS Franklin -Big Ben-", - "type": "Aircraft Carrier", - "era": ["WW2"], - "label": "USS Franklin -Big Ben-", - "shortLabel": "USS Franklin", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "USS Gregory DD-802": { - "name": "USS Gregory DD-802", - "type": "Destroyer", - "era": ["WW2"], - "label": "USS Gregory DD-802", - "shortLabel": "USS Gregory", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "USS Hopewell DD-681": { - "name": "USS Hopewell DD-681", - "type": "Destroyer", - "era": ["WW2"], - "label": "USS Hopewell DD-681", - "shortLabel": "USS Hopewell", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "USS Hornet (CV-8)": { - "name": "USS Hornet (CV-8)", - "type": "Aircraft Carrier", - "era": ["WW2"], - "label": "USS Hornet (CV-8)", - "shortLabel": "USS Hornet (CV-8)", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "USS Illinois BB-65": { - "name": "USS Illinois BB-65", - "type": "Battleship", - "era": ["WW2"], - "label": "USS Illinois BB-65", - "shortLabel": "USS Illinois", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "USS Iowa": { - "name": "USS Iowa", - "type": "Battleship", - "era": ["WW2"], - "label": "USS Iowa", - "shortLabel": "USS Iowa", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "USS Johnson DD-557": { - "name": "USS Johnson DD-557", - "type": "Destoryer", - "era": ["WW2"], - "label": "USS Johnson DD-557", - "shortLabel": "USS Johnson", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "USS Kentucky BB-66": { - "name": "USS Kentucky BB-66", - "type": "Battleship", - "era": ["WW2"], - "label": "USS Kentucky BB-66", - "shortLabel": "USS Kentucky", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "USS La Vallette DD-448": { - "name": "USS La Vallette DD-448", - "type": "Destroyer", - "era": ["WW2"], - "label": "USS La Vallette DD-448", - "shortLabel": "USS La Vallette", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "USS Missouri": { - "name": "USS Missouri", - "type": "Battleship", - "era": ["WW2"], - "label": "USS Missouri", - "shortLabel": "USS Missouri", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "USS New Jersey": { - "name": "USS New Jersey", - "type": "Battleship", - "era": ["WW2"], - "label": "USS New Jersey", - "shortLabel": "USS New Jersey", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "USS Radford DD-446": { - "name": "USS Radford DD-446", - "type": "Destroyer", - "era": ["WW2"], - "label": "USS Radford DD-446", - "shortLabel": "USS Radford", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "USS Samuel Chase": { - "name": "USS Samuel Chase", - "type": "Transport", - "era": ["WW2"], - "label": "USS Samuel Chase", - "shortLabel": "USS Samuel Chase", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "USS Winsconsin": { - "name": "Winsconsin", - "type": "Battleship", - "era": ["WW2"], - "label": "USS Winsconsin", - "shortLabel": "USS Winsconsin", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "WW II USS Intrepid CV-11": { - "name": "WW II USS Intrepid CV-11", - "type": "Aircraft Carrier", - "era": ["WW2"], - "label": "WW II USS Intrepid CV-11", - "shortLabel": "USS Intrepid", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "WWII Japanese Battleship Musashi": { - "name": "WWII Japanese Battleship Musashi", - "type": "Battleship", - "era": ["WW2"], - "label": "WWII Japanese Battleship Musashi", - "shortLabel": "Battleship Musashi", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "WWII Japanese Battleship Yamato": { - "name": "WWII Japanese Battleship Yamato", - "type": "Battleship", - "era": ["WW2"], - "label": "WWII Japanese Battleship Yamato", - "shortLabel": "Battleship Yamato", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "WWII USS Yorktown CV-5": { - "name": "WWII USS Yorktown CV-5", - "type": "Aircraft Carrier", - "era": ["WW2"], - "label": "WWII USS Yorktown CV-5", - "shortLabel": "USS Yorktown", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "WWII USS Hornet CV-8": { - "name": "WWII USS Hornet CV-8", - "type": "Aircraft Carrier", - "era": ["WW2"], - "label": "WWII USS Hornet CV-8", - "shortLabel": "USS Hornet", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "ZUIKAKU": { - "name": "ZUIKAKU", - "type": "Aircraft Carrier", - "era": ["WW2"], - "label": "ZUIKAKU", - "shortLabel": "ZUIKAKU", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - "Zwezdny": { - "name": "Zwezdny", - "type": "Civilian Boat", - "era": ["Modern"], - "label": "Zwezdny", - "shortLabel": "Zwezdny", - "range": "", - "loadouts": [ - { - "fuel": 1, - "items": [], - "roles": [ - "Template" - ], - "code": "", - "name": "Default" - } - ], - "filename": "" - }, - } - } -} - -export var navalDatabase = new NavalDatabase(); diff --git a/client/src/units/navyunitdatabase.ts b/client/src/units/navyunitdatabase.ts new file mode 100644 index 00000000..b157f5d2 --- /dev/null +++ b/client/src/units/navyunitdatabase.ts @@ -0,0 +1,946 @@ +import { UnitDatabase } from "./unitdatabase" + +export class NavyUnitDatabase extends UnitDatabase { + constructor() { + super(); + this.blueprints = { + "052B DDG-168 Guangzhou": { + "name": "052B DDG-168 Guangzhou", + "type": "Destroyer", + "era": [ + "Modern" + ], + "label": "052B DDG-168 Guangzhou", + "shortLabel": "052B DDG-168 Guangzhou", + "range": "Short", + "filename": "" + }, + "052C DDG-171 Haikou": { + "name": "052C DDG-171 Haikou", + "type": "Destroyer", + "era": [ + "Modern" + ], + "label": "052C DDG-171 Haikou", + "shortLabel": "052C DDG-171 Haikou", + "range": "Short", + "filename": "" + }, + "054A FFG-538 Yantai": { + "name": "054A FFG-538 Yantai", + "type": "Frigate", + "era": [ + "Modern" + ], + "label": "054A FFG-538 Yantai", + "shortLabel": "054A FFG-538 Yantai", + "range": "Medium", + "filename": "" + }, + "Type 071": { + "name": "Type 071", + "type": "Transport", + "era": [ + "Modern" + ], + "label": "Type 071", + "shortLabel": "Type 071", + "range": "", + "filename": "" + }, + "Type 093": { + "name": "Type 093", + "type": "Submarine", + "era": [ + "Modern" + ], + "label": "Type 093", + "shortLabel": "Type 093", + "range": "", + "filename": "" + }, + "Akizuki": { + "name": "Akizuki", + "type": "Destroyer", + "era": [ + "WW2" + ], + "label": "Akizuki", + "shortLabel": "Akizuki", + "range": "", + "filename": "" + }, + "ARA Santa Fe S-21": { + "name": "ARA Santa Fe S-21", + "type": "Submarine", + "era": [ + "Early Cold War" + ], + "label": "ARA Santa Fe S-21", + "shortLabel": "ARA Santa Fe S-21", + "range": "", + "filename": "" + }, + "ARA Vienticinco de Mayo": { + "name": "ARA Vienticinco de Mayo", + "type": "Aircraft Carrier", + "era": [ + "Mid Cold War" + ], + "label": "ARA Vienticinco de Mayo", + "shortLabel": "ARA Vienticinco de Mayo", + "range": "", + "filename": "" + }, + "Admiral Kuznetsov": { + "name": "Admiral Kuznetsov", + "type": "Aircraft Carrier", + "era": [ + "Late Cold War" + ], + "label": "Admiral Kuznetsov", + "shortLabel": "Admiral Kuznetsov", + "range": "Medium", + "filename": "" + }, + "Albatros (Grisha-5)": { + "name": "Albatros (Grisha-5)", + "type": "Aircraft Carrier", + "era": [ + "Early Cold War" + ], + "label": "Albatros (Grisha-5)", + "shortLabel": "Albatros (Grisha-5)", + "range": "", + "filename": "" + }, + "Almirante Condell PFG-06": { + "name": "Almirante Condell PFG-06", + "type": "Frigate", + "era": [ + "Mid Cold War" + ], + "label": "Almirante Condell PFG-06", + "shortLabel": "Almirante Condell PFG-06", + "range": "", + "filename": "" + }, + "Almirante lynch PFG-07": { + "name": "Almirante lynch PFG-07", + "type": "Frigate", + "era": [ + "Mid Cold War" + ], + "label": "Almirante lynch PFG-07", + "shortLabel": "Almirante lynch PFG-07", + "range": "", + "filename": "" + }, + "Boat Armed Hi-Speed": { + "name": "Boat Armed Hi-Speed", + "type": "Fast Attack Craft", + "era": [ + "Mid Cold War" + ], + "label": "Boat Armed Hi-Speed", + "shortLabel": "Boat Armed Hi-Speed", + "range": "", + "filename": "" + }, + "Boat LCVP Higgins": { + "name": "Boat LCVP Higgins", + "type": "Landing Craft", + "era": [ + "WW2" + ], + "label": "Boat LCVP Higgins", + "shortLabel": "Boat LCVP Higgins", + "range": "", + "filename": "" + }, + "Boat Schnellboot type S130": { + "name": "Boat Schnellboot type S130", + "type": "Fast Attack Craft", + "era": [ + "WW2" + ], + "label": "Boat Schnellboot type S130", + "shortLabel": "Boat Schnellboot type S130", + "range": "", + "filename": "" + }, + "Bulker Handy Wind": { + "name": "Bulker Handy Wind", + "type": "Cargoship", + "era": [ + "Late Cold War" + ], + "label": "Bulker Handy Wind", + "shortLabel": "Bulker Handy Wind", + "range": "", + "filename": "" + }, + "CV Admiral Kuznetsov(2017)": { + "name": "CV 1143.5 Admiral Kuznetsov(2017)", + "type": "Aircraft Carrier", + "era": [ + "Modern" + ], + "label": "CV Admiral Kuznetsov(2017)", + "shortLabel": "Admiral Kuznetsov(2017)", + "range": "Medium", + "filename": "" + }, + "CV-59 Forrestal": { + "name": "CV-59 Forrestal", + "type": "Aircraft Carrier", + "era": [ + "Early Cold War" + ], + "label": "CV-59 Forrestal", + "shortLabel": "CV-59 Forrestal", + "range": "Short", + "filename": "" + }, + "CV6 USS Enterprise": { + "name": "CV6 USS Enterprise -The Grey Ghost-", + "type": "Aircraft Carrier", + "era": [ + "WW2" + ], + "label": "CV6 USS Enterprise Grey Ghost", + "shortLabel": "CV6 USS Enterprise", + "range": "", + "filename": "" + }, + "CVN-71 Theodore Roosevelt": { + "name": "CVN-71 Theodore Roosevelt", + "type": "Super Aircraft Carrier", + "era": [ + "Late Cold War" + ], + "label": "CVN-71 Theodore Roosevelt", + "shortLabel": "CVN-71 Theodore Roosevelt", + "range": "Short", + "filename": "" + }, + "CVN-72 Abraham Lincoln": { + "name": "CVN-72 Abraham Lincoln", + "type": "Super Aircraft Carrier", + "era": [ + "Late Cold War" + ], + "label": "CVN-72 Abraham Lincoln", + "shortLabel": "CVN-72 Abraham Lincoln", + "range": "Short", + "filename": "" + }, + "CVN-73 George Washington": { + "name": "CVN-73 George Washington", + "type": "Super Aircraft Carrier", + "era": [ + "Late Cold War" + ], + "label": "CVN-73 George Washington", + "shortLabel": "CVN-73 George Washington", + "range": "Medium", + "filename": "" + }, + "CVN-74 John C. Stennis": { + "name": "CVN-74 John C. Stennis", + "type": "Aircraft Carrier", + "era": [ + "Late Cold War" + ], + "label": "CVN-74 John C. Stennis", + "shortLabel": "CVN-74 John C. Stennis", + "range": "Medium", + "filename": "" + }, + "CVN-75 Harry S. Truman": { + "name": "CVN-75 Harry S. Truman", + "type": "Aircraft Carrier", + "era": [ + "Late Cold War" + ], + "label": "CVN-75 Harry S. Truman", + "shortLabel": "CVN-75 Harry S. Truman", + "range": "Medium", + "filename": "" + }, + "HMS Leeds Castle (P-258)": { + "name": "Castle Class", + "type": "Patrol", + "era": [ + "Mid Cold War" + ], + "label": "HMS Leeds Castle (P-258)", + "shortLabel": "HMS Leeds Castle (P-258)", + "range": "", + "filename": "" + }, + "DDG Arleigh Burke lla": { + "name": "DDG Arleigh Burke lla", + "type": "Destroyer", + "era": [ + "Late Cold War" + ], + "label": "DDG Arleigh Burke lla", + "shortLabel": "DDG Arleigh Burke", + "range": "Medium", + "filename": "" + }, + "DKM Admiral Hipper": { + "name": "DKM Admiral Hipper", + "type": "Cruiser", + "era": [ + "WW2" + ], + "label": "DKM Admiral Hipper", + "shortLabel": "DKM Admiral Hipper", + "range": "", + "filename": "" + }, + "DKM Admiral Scheer": { + "name": "DKM Admiral Scheer", + "type": "Cruiser", + "era": [ + "WW2" + ], + "label": "DKM Admiral Scheer", + "shortLabel": "DKM Admiral Scheer", + "range": "", + "filename": "" + }, + "DKM Blucher": { + "name": "DKM Blucher", + "type": "Battleship", + "era": [ + "WW2" + ], + "label": "DKM Blucher", + "shortLabel": "DKM Blucher", + "range": "", + "filename": "" + }, + "DKM Gneisenau": { + "name": "DKM Blucher", + "type": "Battleship", + "era": [ + "WW2" + ], + "label": "DKM Blucher", + "shortLabel": "DKM Blucher", + "range": "", + "filename": "" + }, + "DKM Prinz Eugen": { + "name": "DKM Prinz Eugen", + "type": "Cruiser", + "era": [ + "WW2" + ], + "label": "DKM Prinz Eugen", + "shortLabel": "DKM Prinz Eugen", + "range": "", + "filename": "" + }, + "DKM Scharnhorst": { + "name": "Scharnhorst", + "type": "Battleship", + "era": [ + "WW2" + ], + "label": "DKM Scharnhorst", + "shortLabel": "DKM Scharnhorst", + "range": "", + "filename": "" + }, + "DKM Tirpiz": { + "name": "DKM Tirpiz", + "type": "Battleship", + "era": [ + "WW2" + ], + "label": "DKM Tirpiz", + "shortLabel": "DKM Tirpiz", + "range": "", + "filename": "" + }, + "DKM Z39": { + "name": "DKM Z39", + "type": "Destroyer", + "era": [ + "WW2" + ], + "label": "DKM Z39", + "shortLabel": "DKM Z39", + "range": "", + "filename": "" + }, + "Dry cargo ship Ivanov": { + "name": "Dry cargo ship Ivanov", + "type": "Cargoship", + "era": [ + "Late Cold War" + ], + "label": "Dry cargo ship Ivanov", + "shortLabel": "Dry cargo ship Ivanov", + "range": "", + "filename": "" + }, + "Dry cargo ship Yakushev": { + "name": "Dry cargo ship Yakushev", + "type": "Cargoship", + "era": [ + "Late Cold War" + ], + "label": "Dry cargo ship Yakushev", + "shortLabel": "Dry cargo ship Yakushev", + "range": "", + "filename": "" + }, + "Elnya tanker": { + "name": "Elnya tanker", + "type": "Tanker", + "era": [ + "Late Cold War" + ], + "label": "Elnya tanker", + "shortLabel": "Elnya tanker", + "range": "", + "filename": "" + }, + "FAC La Combattante lla": { + "name": "FAC La Combattante lla", + "type": "Fast Attack Craft", + "era": [ + "Mid Cold War" + ], + "label": "FAC La Combattante lla", + "shortLabel": "FAC La Combattante", + "range": "", + "filename": "" + }, + "Fletcher-Class destroyer": { + "name": "Fletcher-Class destroyer", + "type": "Destroyer", + "era": [ + "WW2" + ], + "label": "Fletcher-Class destroyer", + "shortLabel": "Fletcher-Class destroyer", + "range": "", + "filename": "" + }, + "HMS Achilles (F12)": { + "name": "HMS Achilles (F12)", + "type": "Frigate", + "era": [ + "Mid Cold War" + ], + "label": "HMS Achilles (F12)", + "shortLabel": "HMS Achilles", + "range": "", + "filename": "" + }, + "HMS Andromeda (F57)": { + "name": "HMS Andromeda (F57)", + "type": "Frigate", + "era": [ + "Mid Cold War" + ], + "label": "HMS Andromeda (F57)", + "shortLabel": "HMS Andromeda", + "range": "", + "filename": "" + }, + "HMS Ariadne (F72)": { + "name": "HMS Ariadne (F72)", + "type": "Frigate", + "era": [ + "Mid Cold War" + ], + "label": "HMS Ariadne (F72)", + "shortLabel": "HMS Ariadne", + "range": "", + "filename": "" + }, + "HMS Invincible (R05)": { + "name": "HMS Invincible (R05)", + "type": "Aircraft Carrier", + "era": [ + "Mid Cold War" + ], + "label": "HMS Invincible (R05)", + "shortLabel": "HMS Invincible", + "range": "", + "filename": "" + }, + "Harbor Tug": { + "name": "Harbor Tug", + "type": "Tug", + "era": [ + "Mid Cold War" + ], + "label": "Harbor Tug", + "shortLabel": "Harbor Tug", + "range": "", + "filename": "" + }, + "Improved Kilo": { + "name": "Improved Kilo", + "type": "Submarine", + "era": [ + "Late Cold War" + ], + "label": "Project 636 Varshavyanka", + "shortLabel": "Varshavyanka", + "range": "Medium", + "filename": "" + }, + "Kilo": { + "name": "Kilo", + "type": "Submarine", + "era": [ + "Late Cold War" + ], + "label": "Project 636 Varshavyanka Basic", + "shortLabel": "Varshavyanka Basic", + "range": "Medium", + "filename": "" + }, + "LHA-1 Tarawa": { + "name": "LHA-1 Tarawa", + "type": "Aircraft Carrier", + "era": [ + "Mid Cold War" + ], + "label": "LHA-1 Tarawa", + "shortLabel": "LHA-1 Tarawa", + "range": "Short", + "filename": "" + }, + "LS Ropucha": { + "name": "LS Ropucha", + "type": "Landing Craft", + "era": [ + "Mid Cold War" + ], + "label": "LS Ropucha", + "shortLabel": "LS Ropucha", + "range": "", + "filename": "" + }, + "LST Mk2": { + "name": "LST Mk2", + "type": "Transport", + "era": [ + "WW2" + ], + "label": "LST Mk2", + "shortLabel": "LST Mk2", + "range": "", + "filename": "" + }, + "Molniya (Tarantul-3)": { + "name": "Molniya (Tarantul-3)", + "type": "Fast Attack Craft", + "era": [ + "Late Cold War" + ], + "label": "Molniya (Tarantul-3)", + "shortLabel": "Molniya (Tarantul-3)", + "range": "Short", + "filename": "" + }, + "Moscow": { + "name": "Moscow", + "type": "Cruiser", + "era": [ + "Late Cold War" + ], + "label": "Moscow", + "shortLabel": "Moscow", + "range": "Medium", + "filename": "" + }, + "Neustrashimy": { + "name": "Neustrashimy", + "type": "Frigate", + "era": [ + "Late Cold War" + ], + "label": "Neustrashimy", + "shortLabel": "Neustrashimy", + "range": "Short", + "filename": "" + }, + "Oliver H. Perry": { + "name": "Oliver H. Perry", + "type": "Frigate", + "era": [ + "Mid Cold War" + ], + "label": "Oliver H. Perry", + "shortLabel": "Oliver H. Perry", + "range": "Medium", + "filename": "" + }, + "Pyotr Velikiy": { + "name": "Pyotr Velikiy", + "type": "Cruiser", + "era": [ + "Late Cold War" + ], + "label": "Pyotr Velikiy", + "shortLabel": "Pyotr Velikiy", + "range": "Medium", + "filename": "" + }, + "Rezky (Krivak-2)": { + "name": "Rezky (Krivak-2)", + "type": "Frigate", + "era": [ + "Early Cold War" + ], + "label": "Rezky (Krivak-2)", + "shortLabel": "Rezky", + "range": "Short", + "filename": "" + }, + "Supply Ship MV Tilde": { + "name": "Supply Ship MV Tilde", + "type": "Transport", + "era": [ + "Late Cold War" + ], + "label": "Supply Ship MV Tilde", + "shortLabel": "Supply Ship MV Tilde", + "range": "", + "filename": "" + }, + "Tanker Seawise Giant": { + "name": "Tanker Seawise Giant", + "type": "Tanker", + "era": [ + "Late Cold War" + ], + "label": "Tanker Seawise Giant", + "shortLabel": "Tanker Seawise Giant", + "range": "", + "filename": "" + }, + "Ticonderoga": { + "name": "Ticonderoga", + "type": "Cruiser", + "era": [ + "Late Cold War" + ], + "label": "Ticonderoga", + "shortLabel": "Ticonderoga", + "range": "Medium", + "filename": "" + }, + "U-boat VIIC U-flak": { + "name": "U-boat VIIC U-flak", + "type": "Submarine", + "era": [ + "WW2" + ], + "label": "U-boat VIIC U-flak", + "shortLabel": "U-boat VIIC U-flak", + "range": "", + "filename": "" + }, + "USS Bell DD-587": { + "name": "USS Bell DD-587", + "type": "Destroyer", + "era": [ + "WW2" + ], + "label": "USS Bell DD-587", + "shortLabel": "USS Bell DD-587", + "range": "", + "filename": "" + }, + "USS Cassin Young": { + "name": "USS Cassin Young", + "type": "Destroyer", + "era": [ + "WW2" + ], + "label": "USS Cassin Young", + "shortLabel": "USS Cassin Young", + "range": "", + "filename": "" + }, + "USS Cotten DD-669": { + "name": "USS Cotten DD-669", + "type": "Destroyer", + "era": [ + "WW2" + ], + "label": "USS Cotten DD-669", + "shortLabel": "USS Cotten", + "range": "", + "filename": "" + }, + "USS Enterprise": { + "name": "USS Enterprise", + "type": "Aircraft Carrier", + "era": [ + "WW2" + ], + "label": "CV-6 USS Enterprise", + "shortLabel": "USS Enterprise", + "range": "", + "filename": "" + }, + "USS Fletcher": { + "name": "USS Fletcher", + "type": "Destroyer", + "era": [ + "WW2" + ], + "label": "USS Fletcher", + "shortLabel": "USS Fletcher", + "range": "", + "filename": "" + }, + "USS Franklin -Big Ben-": { + "name": "USS Franklin -Big Ben-", + "type": "Aircraft Carrier", + "era": [ + "WW2" + ], + "label": "USS Franklin -Big Ben-", + "shortLabel": "USS Franklin", + "range": "", + "filename": "" + }, + "USS Gregory DD-802": { + "name": "USS Gregory DD-802", + "type": "Destroyer", + "era": [ + "WW2" + ], + "label": "USS Gregory DD-802", + "shortLabel": "USS Gregory", + "range": "", + "filename": "" + }, + "USS Hopewell DD-681": { + "name": "USS Hopewell DD-681", + "type": "Destroyer", + "era": [ + "WW2" + ], + "label": "USS Hopewell DD-681", + "shortLabel": "USS Hopewell", + "range": "", + "filename": "" + }, + "USS Hornet (CV-8)": { + "name": "USS Hornet (CV-8)", + "type": "Aircraft Carrier", + "era": [ + "WW2" + ], + "label": "USS Hornet (CV-8)", + "shortLabel": "USS Hornet (CV-8)", + "range": "", + "filename": "" + }, + "USS Illinois BB-65": { + "name": "USS Illinois BB-65", + "type": "Battleship", + "era": [ + "WW2" + ], + "label": "USS Illinois BB-65", + "shortLabel": "USS Illinois", + "range": "", + "filename": "" + }, + "USS Iowa": { + "name": "USS Iowa", + "type": "Battleship", + "era": [ + "WW2" + ], + "label": "USS Iowa", + "shortLabel": "USS Iowa", + "range": "", + "filename": "" + }, + "USS Johnson DD-557": { + "name": "USS Johnson DD-557", + "type": "Destoryer", + "era": [ + "WW2" + ], + "label": "USS Johnson DD-557", + "shortLabel": "USS Johnson", + "range": "", + "filename": "" + }, + "USS Kentucky BB-66": { + "name": "USS Kentucky BB-66", + "type": "Battleship", + "era": [ + "WW2" + ], + "label": "USS Kentucky BB-66", + "shortLabel": "USS Kentucky", + "range": "", + "filename": "" + }, + "USS La Vallette DD-448": { + "name": "USS La Vallette DD-448", + "type": "Destroyer", + "era": [ + "WW2" + ], + "label": "USS La Vallette DD-448", + "shortLabel": "USS La Vallette", + "range": "", + "filename": "" + }, + "USS Missouri": { + "name": "USS Missouri", + "type": "Battleship", + "era": [ + "WW2" + ], + "label": "USS Missouri", + "shortLabel": "USS Missouri", + "range": "", + "filename": "" + }, + "USS New Jersey": { + "name": "USS New Jersey", + "type": "Battleship", + "era": [ + "WW2" + ], + "label": "USS New Jersey", + "shortLabel": "USS New Jersey", + "range": "", + "filename": "" + }, + "USS Radford DD-446": { + "name": "USS Radford DD-446", + "type": "Destroyer", + "era": [ + "WW2" + ], + "label": "USS Radford DD-446", + "shortLabel": "USS Radford", + "range": "", + "filename": "" + }, + "USS Samuel Chase": { + "name": "USS Samuel Chase", + "type": "Transport", + "era": [ + "WW2" + ], + "label": "USS Samuel Chase", + "shortLabel": "USS Samuel Chase", + "range": "", + "filename": "" + }, + "USS Winsconsin": { + "name": "Winsconsin", + "type": "Battleship", + "era": [ + "WW2" + ], + "label": "USS Winsconsin", + "shortLabel": "USS Winsconsin", + "range": "", + "filename": "" + }, + "WW II USS Intrepid CV-11": { + "name": "WW II USS Intrepid CV-11", + "type": "Aircraft Carrier", + "era": [ + "WW2" + ], + "label": "WW II USS Intrepid CV-11", + "shortLabel": "USS Intrepid", + "range": "", + "filename": "" + }, + "WWII Japanese Battleship Musashi": { + "name": "WWII Japanese Battleship Musashi", + "type": "Battleship", + "era": [ + "WW2" + ], + "label": "WWII Japanese Battleship Musashi", + "shortLabel": "Battleship Musashi", + "range": "", + "filename": "" + }, + "WWII Japanese Battleship Yamato": { + "name": "WWII Japanese Battleship Yamato", + "type": "Battleship", + "era": [ + "WW2" + ], + "label": "WWII Japanese Battleship Yamato", + "shortLabel": "Battleship Yamato", + "range": "", + "filename": "" + }, + "WWII USS Yorktown CV-5": { + "name": "WWII USS Yorktown CV-5", + "type": "Aircraft Carrier", + "era": [ + "WW2" + ], + "label": "WWII USS Yorktown CV-5", + "shortLabel": "USS Yorktown", + "range": "", + "filename": "" + }, + "WWII USS Hornet CV-8": { + "name": "WWII USS Hornet CV-8", + "type": "Aircraft Carrier", + "era": [ + "WW2" + ], + "label": "WWII USS Hornet CV-8", + "shortLabel": "USS Hornet", + "range": "", + "filename": "" + }, + "ZUIKAKU": { + "name": "ZUIKAKU", + "type": "Aircraft Carrier", + "era": [ + "WW2" + ], + "label": "ZUIKAKU", + "shortLabel": "ZUIKAKU", + "range": "", + "filename": "" + }, + "Zwezdny": { + "name": "Zwezdny", + "type": "Civilian Boat", + "era": [ + "Modern" + ], + "label": "Zwezdny", + "shortLabel": "Zwezdny", + "range": "", + "filename": "" + } + } + } +} + +export var navyUnitDatabase = new NavyUnitDatabase(); diff --git a/client/src/units/unit.ts b/client/src/units/unit.ts index 14e6f44e..ba412afd 100644 --- a/client/src/units/unit.ts +++ b/client/src/units/unit.ts @@ -538,9 +538,13 @@ export class Unit extends CustomMarker { if (typeof (roles) === "string") roles = [roles]; - return this.getDatabase()?.getByName(this.#name)?.loadouts.some((loadout: LoadoutBlueprint) => { - return (roles as string[]).some((role: string) => { return loadout.roles.includes(role) }); - }); + var loadouts = this.getDatabase()?.getByName(this.#name)?.loadouts; + if (loadouts) { + return loadouts.some((loadout: LoadoutBlueprint) => { + return (roles as string[]).some((role: string) => { return loadout.roles.includes(role) }); + }); + } else + return false; } /********************** Unit commands *************************/ @@ -731,7 +735,7 @@ export class Unit extends CustomMarker { } if (Object.keys(options).length > 0) { - getMap().showUnitContextMenu(e); + getMap().showUnitContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng); getMap().getUnitContextMenu().setOptions(options, (option: string) => { getMap().hideUnitContextMenu(); this.#executeAction(e, option); @@ -774,7 +778,8 @@ export class Unit extends CustomMarker { getMap().hideUnitContextMenu(); this.#applyFollowOptions(option); }); - getMap().showUnitContextMenu(e); + + getMap().showUnitContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng); } #applyFollowOptions(action: string) { diff --git a/client/src/units/unitdatabase.ts b/client/src/units/unitdatabase.ts index 24f5105e..fc1bf302 100644 --- a/client/src/units/unitdatabase.ts +++ b/client/src/units/unitdatabase.ts @@ -9,16 +9,30 @@ export class UnitDatabase { getRoles() { var roles: string[] = []; for (let unit in this.blueprints) { - for (let loadout of this.blueprints[unit].loadouts) { - for (let role of loadout.roles) { - if (role !== "" && !roles.includes(role)) - roles.push(role); + var loadouts = this.blueprints[unit].loadouts; + if (loadouts) { + for (let loadout of loadouts) { + for (let role of loadout.roles) { + if (role !== "" && !roles.includes(role)) + roles.push(role); + } } } } return roles; } + /* Returns a list of all possible types in a database */ + getTypes() { + var types: string[] = []; + for (let unit in this.blueprints) { + var type = this.blueprints[unit].type; + if (type && type !== "" && !types.includes(type)) + types.push(type); + } + return types; + } + /* Gets a specific blueprint by name */ getByName(name: string) { if (name in this.blueprints) @@ -35,7 +49,7 @@ export class UnitDatabase { return null; } - /* Gets a specific blueprint by range */ + /* Get all blueprints by range */ getByRange(range: string) { var unitswithrange = []; for (let unit in this.blueprints) { @@ -46,7 +60,7 @@ export class UnitDatabase { return unitswithrange; } - /* Gets a specific blueprint by type */ + /* Get all blueprints by type */ getByType(type: string) { var units = []; for (let unit in this.blueprints) { @@ -61,10 +75,13 @@ export class UnitDatabase { getByRole(role: string) { var units = []; for (let unit in this.blueprints) { - for (let loadout of this.blueprints[unit].loadouts) { - if (loadout.roles.includes(role) || loadout.roles.includes(role.toLowerCase())) { - units.push(this.blueprints[unit]) - break; + var loadouts = this.blueprints[unit].loadouts; + if (loadouts) { + for (let loadout of loadouts) { + if (loadout.roles.includes(role) || loadout.roles.includes(role.toLowerCase())) { + units.push(this.blueprints[unit]) + break; + } } } } @@ -73,20 +90,26 @@ export class UnitDatabase { /* Get the names of all the loadouts for a specific unit and for a specific role */ getLoadoutNamesByRole(name: string, role: string) { - var loadouts = []; - for (let loadout of this.blueprints[name].loadouts) { - if (loadout.roles.includes(role) || loadout.roles.includes("")) { - loadouts.push(loadout.name) + var loadoutsByRole = []; + var loadouts = this.blueprints[name].loadouts; + if (loadouts) { + for (let loadout of loadouts) { + if (loadout.roles.includes(role) || loadout.roles.includes("")) { + loadoutsByRole.push(loadout.name) + } } } - return loadouts; + return loadoutsByRole; } /* Get the loadout content from the unit name and loadout name */ getLoadoutByName(name: string, loadoutName: string) { - for (let loadout of this.blueprints[name].loadouts) { - if (loadout.name === loadoutName) - return loadout; + var loadouts = this.blueprints[name].loadouts; + if (loadouts) { + for (let loadout of loadouts) { + if (loadout.name === loadoutName) + return loadout; + } } return null; } diff --git a/client/src/units/unitsmanager.ts b/client/src/units/unitsmanager.ts index 355753bb..8f2fb8ad 100644 --- a/client/src/units/unitsmanager.ts +++ b/client/src/units/unitsmanager.ts @@ -5,7 +5,7 @@ import { cloneUnit, setLastUpdateTime, spawnGroundUnits } from "../server/server import { deg2rad, keyEventWasInInput, latLngToMercator, mToFt, mercatorToLatLng, msToKnots, polygonArea, randomPointInPoly, randomUnitBlueprintByRole } from "../other/utils"; import { CoalitionArea } from "../map/coalitionarea"; import { Airbase } from "../missionhandler/airbase"; -import { groundUnitsDatabase } from "./groundunitsdatabase"; +import { groundUnitDatabase } from "./groundunitdatabase"; import { DataIndexes, HIDE_ALL, IADSRoles, IDLE, MOVE_UNIT } from "../constants/constants"; import { DataExtractor } from "./dataextractor"; import { Contact } from "../@types/unit"; @@ -561,7 +561,7 @@ export class UnitsManager { const role = activeRoles[Math.floor(Math.random() * activeRoles.length)]; const probability = Math.pow(1 - minDistance / 50e3, 5) * IADSRoles[role]; if (Math.random() < probability){ - const unitBlueprint = randomUnitBlueprintByRole(groundUnitsDatabase, role); + const unitBlueprint = randomUnitBlueprintByRole(groundUnitDatabase, role); spawnGroundUnits([{unitType: unitBlueprint.name, location: latlng}], coalitionArea.getCoalition(), true); getMap().addTemporaryMarker(latlng, unitBlueprint.name, coalitionArea.getCoalition()); } diff --git a/client/views/other/contextmenus.ejs b/client/views/other/contextmenus.ejs index ee41d2e4..cc2fe2aa 100644 --- a/client/views/other/contextmenus.ejs +++ b/client/views/other/contextmenus.ejs @@ -1,15 +1,21 @@
-
+
- + data-on-click-params='{ "type": "aircraft" }' class="ol-contexmenu-button"> + + + + data-on-click-params='{ "type": "smoke" }' class="ol-contexmenu-button"> + data-on-click-params='{ "type": "explosion" }' class="ol-contexmenu-button"> +
@@ -22,7 +28,7 @@
-
Aircraft type
+
Aircraft name
Select role first
@@ -30,7 +36,7 @@
-
+
Loadout
Select type first
@@ -58,34 +64,88 @@
-
- -
+
+ +
-
+
-
-
Ground unit role
+
+
Helicopter role
- +
-
-
Ground unit type
+
+
Helicopter name
Select role first
- + +
+
+
+
+
+
Loadout
+
+
Select type first
+
Group members
-
+
+
+
+ +
+
+
+
+
+
Spawn altitude +
+
+
+
+
+ + +
+
+ +
+
+
+ +
+
+
+
+
Ground unit type
+
+ +
+
+
+
+
+
Ground unit name
+
+
Select role first
+ +
+
+
+
+
Group members
+
@@ -94,6 +154,35 @@
+
@@ -102,10 +191,10 @@
- - - - + + + +
@@ -128,16 +217,16 @@
-
+
- + data-on-click-params='{ "type": "iads" }' class="ol-contexmenu-button"> + + class="ol-contexmenu-button"> + class="ol-contexmenu-button">
diff --git a/client/views/panels/navbar.ejs b/client/views/panels/navbar.ejs index fb08ee90..e6108501 100644 --- a/client/views/panels/navbar.ejs +++ b/client/views/panels/navbar.ejs @@ -69,9 +69,6 @@
- From f379f6b998e9842aa4f098c8723bc3fc9c66c74e Mon Sep 17 00:00:00 2001 From: Pax1601 Date: Tue, 11 Jul 2023 17:30:05 +0200 Subject: [PATCH 4/5] Tweaks to IADS creation and menus --- client/demo.js | 2 +- client/public/stylesheets/olympus.css | 17 - .../public/stylesheets/other/contextmenus.css | 61 +- .../olympus/images/buttons/spawn/more.svg | 70 + client/src/@types/unitdatabase.d.ts | 2 +- client/src/constants/constants.ts | 3 +- client/src/controls/airbasecontextmenu.ts | 1 + .../src/controls/coalitionareacontextmenu.ts | 93 +- client/src/controls/mapcontextmenu.ts | 26 +- client/src/index.ts | 2 - client/src/map/coalitionarea.ts | 13 +- client/src/map/map.ts | 14 +- client/src/other/utils.ts | 52 +- client/src/units/citiesDatabase.ts | 7137 +++++++++++++++++ client/src/units/groundunitdatabase.ts | 7 + client/src/units/unit.ts | 4 +- client/src/units/unitdatabase.ts | 30 + client/src/units/unitsmanager.ts | 48 +- client/views/other/contextmenus.ejs | 47 +- 19 files changed, 7511 insertions(+), 118 deletions(-) create mode 100644 client/public/themes/olympus/images/buttons/spawn/more.svg create mode 100644 client/src/units/citiesDatabase.ts diff --git a/client/demo.js b/client/demo.js index ca78a542..d7fcebf0 100644 --- a/client/demo.js +++ b/client/demo.js @@ -343,7 +343,7 @@ class DemoDataGenerator { }; mission(req, res){ - var ret = {mission: {theatre: "Nevada"}}; + var ret = {mission: {theatre: "Syria"}}; ret.time = Date.now(); var auth = req.get("Authorization"); if (auth) { diff --git a/client/public/stylesheets/olympus.css b/client/public/stylesheets/olympus.css index 58b587cd..f35985db 100644 --- a/client/public/stylesheets/olympus.css +++ b/client/public/stylesheets/olympus.css @@ -1195,14 +1195,6 @@ input[type=number]::-webkit-outer-spin-button { background-color: var(--primary-neutral); } -.ol-context-menu>div:nth-child(2) { - align-items: center; - display: flex; - flex-direction: row; - justify-content: space-between; - padding-right: 0px; -} - .ol-context-menu>ul { max-height: 200px; overflow-x: hidden; @@ -1218,21 +1210,12 @@ input[type=number]::-webkit-outer-spin-button { margin: 0px; } -.ol-context-menu>div:nth-child(n+3) { - align-items: center; - display: flex; - flex-direction: column; - justify-content: space-between; - row-gap: 5px; -} - .ol-context-menu .ol-select-container { align-self: stretch; flex: 0 0 auto; width: 100%; } - .ol-contexmenu-button { border: none; border-radius: 0px; diff --git a/client/public/stylesheets/other/contextmenus.css b/client/public/stylesheets/other/contextmenus.css index 6b691787..7e812f74 100644 --- a/client/public/stylesheets/other/contextmenus.css +++ b/client/public/stylesheets/other/contextmenus.css @@ -4,10 +4,34 @@ height: fit-content; position: absolute; row-gap: 5px; - width: 280px; + width: 300px; z-index: 9999; } +#map-contextmenu>div:nth-child(2) { + align-items: center; + display: flex; + flex-direction: row; + justify-content: space-between; + padding-right: 0px; +} + +#map-contextmenu>div:nth-child(3) { + align-items: center; + display: flex; + flex-direction: row; + justify-content: space-between; + padding-right: 0px; +} + +#map-contextmenu>div:nth-child(n+4) { + align-items: center; + display: flex; + flex-direction: column; + justify-content: space-between; + row-gap: 5px; +} + #aircraft-spawn-menu, #helicopter-spawn-menu, #groundunit-spawn-menu, @@ -40,6 +64,7 @@ } .deploy-unit-button { + margin-top: 5px; text-align: center; width: 100%; } @@ -180,16 +205,14 @@ background-color: orange; } -#aircraft-spawn-menu .ol-slider-value, -#helicopter-spawn-menu .ol-slider-value { +.ol-context-menu .ol-slider-value { color: var(--accent-light-blue); cursor: pointer; font-size: 14px; font-weight: bold; } -#aircraft-spawn-altitude-slider, -#helicopter-spawn-altitude-slider { +.ol-context-menu .ol-slider-container { padding: 0px 10px; } @@ -357,7 +380,7 @@ height: fit-content; position: absolute; row-gap: 5px; - width: 250px; + width: 300px; z-index: 9999; } @@ -371,6 +394,10 @@ align-self: flex-start; } +#coalition-units-checkbox { + padding: 10px 10px; +} + #iads-menu .ol-select-options>* { padding-top: 8px; padding-bottom: 8px; @@ -391,3 +418,25 @@ #iads-menu { row-gap: 10px; } + +#coalition-area-contextmenu>div:nth-child(2) { + align-items: center; + display: flex; + flex-direction: row; + justify-content: space-between; + padding-right: 0px; +} + +#coalition-area-contextmenu>div:nth-child(n+3) { + align-items: center; + display: flex; + flex-direction: column; + justify-content: space-between; + row-gap: 5px; +} + +.create-iads-button { + margin-top: 5px; + text-align: center; + width: 100%; +} \ No newline at end of file diff --git a/client/public/themes/olympus/images/buttons/spawn/more.svg b/client/public/themes/olympus/images/buttons/spawn/more.svg new file mode 100644 index 00000000..abeb13eb --- /dev/null +++ b/client/public/themes/olympus/images/buttons/spawn/more.svg @@ -0,0 +1,70 @@ + + + + + + image/svg+xml + + + + + + + + + + diff --git a/client/src/@types/unitdatabase.d.ts b/client/src/@types/unitdatabase.d.ts index f76a38a5..5c2f801a 100644 --- a/client/src/@types/unitdatabase.d.ts +++ b/client/src/@types/unitdatabase.d.ts @@ -14,7 +14,7 @@ interface LoadoutBlueprint { interface UnitBlueprint { name: string; - era?: string[]; + era: string[]; label: string; shortLabel: string; type?: string; diff --git a/client/src/constants/constants.ts b/client/src/constants/constants.ts index 0584f11c..66053969 100644 --- a/client/src/constants/constants.ts +++ b/client/src/constants/constants.ts @@ -142,7 +142,8 @@ export const COALITIONAREA_DRAW_POLYGON = "Draw Coalition Area"; export const visibilityControls: string[] = ["human", "dcs", "aircraft", "groundunit-sam", "groundunit-other", "navyunit", "airbase"]; export const visibilityControlsTootlips: string[] = ["Toggle human players visibility", "Toggle DCS controlled units visibility", "Toggle aircrafts visibility", "Toggle SAM units visibility", "Toggle ground units (not SAM) visibility", "Toggle navy units visibility", "Toggle airbases visibility"]; -export const IADSRoles: {[key: string]: number}= {"AAA": 0.8, "MANPADS": 0.3, "SAM Sites": 0.1, "Radar": 0.05}; +export const IADSTypes = ["AAA", "MANPADS", "SAM Sites", "Radar"]; +export const IADSDensities: {[key: string]: number}= {"AAA": 0.8, "MANPADS": 0.3, "SAM Sites": 0.1, "Radar": 0.05}; export enum DataIndexes { startOfData = 0, diff --git a/client/src/controls/airbasecontextmenu.ts b/client/src/controls/airbasecontextmenu.ts index 7b9559d0..434217bc 100644 --- a/client/src/controls/airbasecontextmenu.ts +++ b/client/src/controls/airbasecontextmenu.ts @@ -62,6 +62,7 @@ export class AirbaseContextMenu extends ContextMenu { setActiveCoalition(this.#airbase.getCoalition()); getMap().showMapContextMenu(this.getX(), this.getY(), this.getLatLng()); getMap().getMapContextMenu().hideUpperBar(); + getMap().getMapContextMenu().hideLowerBar(); getMap().getMapContextMenu().hideAltitudeSlider(); getMap().getMapContextMenu().showSubMenu("aircraft"); getMap().getMapContextMenu().setAirbaseName(this.#airbase.getName()); diff --git a/client/src/controls/coalitionareacontextmenu.ts b/client/src/controls/coalitionareacontextmenu.ts index 5abdd79a..61e253f0 100644 --- a/client/src/controls/coalitionareacontextmenu.ts +++ b/client/src/controls/coalitionareacontextmenu.ts @@ -1,31 +1,38 @@ import { LatLng } from "leaflet"; import { getMap, getUnitsManager } from ".."; -import { GAME_MASTER, IADSRoles } from "../constants/constants"; +import { GAME_MASTER, IADSTypes } from "../constants/constants"; import { CoalitionArea } from "../map/coalitionarea"; import { ContextMenu } from "./contextmenu"; import { Dropdown } from "./dropdown"; import { Slider } from "./slider"; import { Switch } from "./switch"; +import { groundUnitDatabase } from "../units/groundunitdatabase"; export class CoalitionAreaContextMenu extends ContextMenu { #coalitionSwitch: Switch; #coalitionArea: CoalitionArea | null = null; #iadsDensitySlider: Slider; - #iadsRoleDropdown: Dropdown; - - //#iadsPeriodDropdown: Dropdown; + #iadsDistributionSlider: Slider; + #iadsTypesDropdown: Dropdown; + #iadsErasDropdown: Dropdown; + #iadsRangesDropdown: Dropdown; constructor(id: string) { super(id); this.#coalitionSwitch = new Switch("coalition-area-switch", (value: boolean) => this.#onSwitchClick(value)); this.#coalitionSwitch.setValue(false); - this.#iadsRoleDropdown = new Dropdown("iads-units-role-options", () => { }); - //this.#iadsPeriodDropdown = new Dropdown("iads-period-options", () => {}); + this.#iadsTypesDropdown = new Dropdown("iads-units-type-options", () => { }); + this.#iadsErasDropdown = new Dropdown("iads-era-options", () => {}); + this.#iadsRangesDropdown = new Dropdown("iads-range-options", () => {}); this.#iadsDensitySlider = new Slider("iads-density-slider", 5, 100, "%", (value: number) => { }); + this.#iadsDistributionSlider = new Slider("iads-distribution-slider", 5, 100, "%", (value: number) => { }); this.#iadsDensitySlider.setIncrement(5); this.#iadsDensitySlider.setValue(50); this.#iadsDensitySlider.setActive(true); + this.#iadsDistributionSlider.setIncrement(5); + this.#iadsDistributionSlider.setValue(50); + this.#iadsDistributionSlider.setActive(true); document.addEventListener("coalitionAreaContextMenuShow", (e: any) => { if (this.getVisibleSubMenu() !== e.detail.type) @@ -34,6 +41,12 @@ export class CoalitionAreaContextMenu extends ContextMenu { this.hideSubMenus(); }); + document.addEventListener("coalitionAreaBringToBack", (e: any) => { + if (this.#coalitionArea) + getMap().bringCoalitionAreaToBack(this.#coalitionArea); + getMap().hideCoalitionAreaContextMenu(); + }); + document.addEventListener("coalitionAreaDelete", (e: any) => { if (this.#coalitionArea) getMap().deleteCoalitionArea(this.#coalitionArea); @@ -41,36 +54,25 @@ export class CoalitionAreaContextMenu extends ContextMenu { }); 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()); + getUnitsManager().createIADS(area, this.#getCheckboxOptions(this.#iadsTypesDropdown), this.#getCheckboxOptions(this.#iadsErasDropdown), this.#getCheckboxOptions(this.#iadsRangesDropdown), this.#iadsDensitySlider.getValue(), this.#iadsDistributionSlider.getValue()); }) /* Create the checkboxes to select the unit roles */ - this.#iadsRoleDropdown.setOptionsElements(Object.keys(IADSRoles).map((unitRole: string) => { - var div = document.createElement("div"); - div.classList.add("ol-checkbox"); - var label = document.createElement("label"); - label.title = `Add ${unitRole}s to the IADS`; - var input = document.createElement("input"); - input.type = "checkbox"; - input.checked = true; - var span = document.createElement("span"); - span.innerText = unitRole; - label.appendChild(input); - label.appendChild(span); - div.appendChild(label); - return div as HTMLElement; + this.#iadsTypesDropdown.setOptionsElements(IADSTypes.map((role: string) => { + return this.#createCheckboxOption(role); + })); + + + /* Create the checkboxes to select the unit periods */ + this.#iadsErasDropdown.setOptionsElements(groundUnitDatabase.getEras().map((era: string) => { + return this.#createCheckboxOption(era); + })); + + /* Create the checkboxes to select the unit ranges */ + this.#iadsRangesDropdown.setOptionsElements(groundUnitDatabase.getRanges().map((range: string) => { + return this.#createCheckboxOption(range); })); this.hide(); @@ -118,4 +120,33 @@ export class CoalitionAreaContextMenu extends ContextMenu { }); } } + + #createCheckboxOption(text: string) { + var div = document.createElement("div"); + div.classList.add("ol-checkbox"); + var label = document.createElement("label"); + label.title = `Add ${text}s to the IADS`; + var input = document.createElement("input"); + input.type = "checkbox"; + input.checked = true; + var span = document.createElement("span"); + span.innerText = text; + label.appendChild(input); + label.appendChild(span); + div.appendChild(label); + return div as HTMLElement; + } + + #getCheckboxOptions(dropdown: Dropdown) { + var values: { [key: string]: boolean } = {}; + const element = dropdown.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; + } + return values; + } } \ No newline at end of file diff --git a/client/src/controls/mapcontextmenu.ts b/client/src/controls/mapcontextmenu.ts index a87c66d2..c90445e0 100644 --- a/client/src/controls/mapcontextmenu.ts +++ b/client/src/controls/mapcontextmenu.ts @@ -86,7 +86,7 @@ export class MapContextMenu extends ContextMenu { if (this.getVisibleSubMenu() !== e.detail.type) this.showSubMenu(e.detail.type); else - this.hideSubMenus(); + this.hideSubMenus(e.detail.type); }); document.addEventListener("contextMenuDeployAircraft", () => { @@ -191,6 +191,11 @@ export class MapContextMenu extends ContextMenu { } showSubMenu(type: string) { + if (type === "more") + this.getContainer()?.querySelector("#more-options-button-bar")?.classList.toggle("hide"); + else if (["aircraft", "groundunit"].includes(type)) + this.getContainer()?.querySelector("#more-options-button-bar")?.classList.toggle("hide", true); + this.getContainer()?.querySelector("#aircraft-spawn-menu")?.classList.toggle("hide", type !== "aircraft"); this.getContainer()?.querySelector("#aircraft-spawn-button")?.classList.toggle("is-open", type === "aircraft"); this.getContainer()?.querySelector("#helicopter-spawn-menu")?.classList.toggle("hide", type !== "helicopter"); @@ -220,7 +225,8 @@ export class MapContextMenu extends ContextMenu { this.setVisibleSubMenu(type); } - hideSubMenus() { + hideSubMenus(type: string) { + this.getContainer()?.querySelector("#more-options-button-bar")?.classList.toggle("hide", ["aircraft", "groundunit"].includes(type)); this.getContainer()?.querySelector("#aircraft-spawn-menu")?.classList.toggle("hide", true); this.getContainer()?.querySelector("#aircraft-spawn-button")?.classList.toggle("is-open", false); this.getContainer()?.querySelector("#helicopter-spawn-menu")?.classList.toggle("hide", true); @@ -257,6 +263,14 @@ export class MapContextMenu extends ContextMenu { this.getContainer()?.querySelector(".upper-bar")?.classList.toggle("hide", true); } + showLowerBar() { + this.getContainer()?.querySelector("#more-options-button-bar")?.classList.toggle("hide", false); + } + + hideLowerBar() { + this.getContainer()?.querySelector("#more-optionsbutton-bar")?.classList.toggle("hide", true); + } + showAltitudeSlider() { this.getContainer()?.querySelector("#aircraft-spawn-altitude-slider")?.classList.toggle("hide", false); } @@ -430,8 +444,8 @@ export class MapContextMenu extends ContextMenu { this.#groundUnitTypeDropdown.reset(); this.#groundUnitNameDropdown.reset(); - const roles = groundUnitDatabase.getTypes(); - this.#groundUnitTypeDropdown.setOptions(roles); + const types = groundUnitDatabase.getTypes(); + this.#groundUnitTypeDropdown.setOptions(types); this.clip(); } @@ -470,8 +484,8 @@ export class MapContextMenu extends ContextMenu { this.#navyUnitTypeDropdown.reset(); this.#navyUnitNameDropdown.reset(); - const roles = navyUnitDatabase.getTypes(); - this.#navyUnitTypeDropdown.setOptions(roles); + const types = navyUnitDatabase.getTypes(); + this.#navyUnitTypeDropdown.setOptions(types); this.clip(); } diff --git a/client/src/index.ts b/client/src/index.ts index 1e07d6d7..07787e68 100644 --- a/client/src/index.ts +++ b/client/src/index.ts @@ -17,7 +17,6 @@ import { Dropdown } from "./controls/dropdown"; import { HotgroupPanel } from "./panels/hotgrouppanel"; import { SVGInjector } from "@tanem/svg-injector"; import { BLUE_COMMANDER, GAME_MASTER, RED_COMMANDER } from "./constants/constants"; -import { NavyUnitDatabase, navyUnitDatabase } from "./units/navyunitdatabase"; var map: Map; @@ -206,7 +205,6 @@ function setupEvents() { else img.onload = () => SVGInjector(img); }) - } export function getMap() { diff --git a/client/src/map/coalitionarea.ts b/client/src/map/coalitionarea.ts index cd45557b..10efb786 100644 --- a/client/src/map/coalitionarea.ts +++ b/client/src/map/coalitionarea.ts @@ -27,6 +27,7 @@ export class CoalitionArea extends Polygon { this.setCoalition("blue"); else if (getUnitsManager().getCommandMode() == RED_COMMANDER) this.setCoalition("red"); + } setCoalition(coalition: string) { @@ -89,6 +90,12 @@ export class CoalitionArea extends Polygon { this.setStyle({opacity: opacity, fillOpacity: opacity * 0.25}); } + onRemove(map: Map): this { + super.onRemove(map); + this.#handles.concat(this.#middleHandles).forEach((handle: CoalitionAreaHandle | CoalitionAreaMiddleHandle) => handle.removeFrom(getMap())); + return this; + } + #setColors() { const coalitionColor = this.getCoalition() === "blue" ? "#247be2" : "#ff5858"; this.setStyle({ color: this.getSelected() ? "white" : coalitionColor, fillColor: coalitionColor }); @@ -159,10 +166,4 @@ export class CoalitionArea extends Polygon { this.setEditing(false); }); } - - onRemove(map: Map): this { - super.onRemove(map); - this.#handles.concat(this.#middleHandles).forEach((handle: CoalitionAreaHandle | CoalitionAreaMiddleHandle) => handle.removeFrom(getMap())); - return this; - } } \ No newline at end of file diff --git a/client/src/map/map.ts b/client/src/map/map.ts index c3ff9608..6c50858a 100644 --- a/client/src/map/map.ts +++ b/client/src/map/map.ts @@ -392,6 +392,12 @@ export class Map extends L.Map { return this.#coalitionAreas.find((area: CoalitionArea) => { return area.getSelected() }); } + bringCoalitionAreaToBack(coalitionArea: CoalitionArea) { + coalitionArea.bringToBack(); + this.#coalitionAreas.splice(this.#coalitionAreas.indexOf(coalitionArea), 1); + this.#coalitionAreas.unshift(coalitionArea); + } + /* Event handlers */ #onClick(e: any) { if (!this.#preventLeftClick) { @@ -423,14 +429,18 @@ export class Map extends L.Map { if (this.#state === IDLE) { if (this.#state == IDLE) { this.showMapContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng); + var clickedCoalitionArea = null; + /* Coalition areas are ordered in the #coalitionAreas array according to their zindex. Select the upper one */ for (let coalitionArea of this.#coalitionAreas) { if (coalitionArea.getBounds().contains(e.latlng)) { - if (coalitionArea.getSelected()) - this.showCoalitionAreaContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng, coalitionArea); + if (coalitionArea.getSelected()) + clickedCoalitionArea = coalitionArea; else this.getMapContextMenu().setCoalitionArea(coalitionArea); } } + if (clickedCoalitionArea) + this.showCoalitionAreaContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng, clickedCoalitionArea); } } else if (this.#state === MOVE_UNIT) { diff --git a/client/src/other/utils.ts b/client/src/other/utils.ts index 2a50102b..de64dab7 100644 --- a/client/src/other/utils.ts +++ b/client/src/other/utils.ts @@ -35,14 +35,14 @@ export function distance(lat1: number, lon1: number, lat2: number, lon2: number) return d; } -export function coordinatesFromBearingAndDistance(lat1: number, lon1: number, brng: number, dist: number) { +export function bearingAndDistanceToLatLng(lat: number, lon: number, brng: number, dist: number) { const R = 6371e3; // metres - const φ1 = deg2rad(lat1); // φ, λ in radians - const λ1 = deg2rad(lon1); + const φ1 = deg2rad(lat); // φ, λ in radians + const λ1 = deg2rad(lon); const φ2 = Math.asin( Math.sin(φ1)*Math.cos(dist/R) + Math.cos(φ1)*Math.sin(dist/R)*Math.cos(brng) ); const λ2 = λ1 + Math.atan2(Math.sin(brng)*Math.sin(dist/R)*Math.cos(φ1), Math.cos(dist/R)-Math.sin(φ1)*Math.sin(φ2)); - return {lat: rad2deg(φ2), lng: rad2deg(λ2)}; + return new LatLng(rad2deg(φ2), rad2deg(λ2)); } export function ConvertDDToDMS(D: number, lng: boolean) { @@ -205,6 +205,11 @@ export function nmToFt(nm: number) { return nm * 6076.12; } +export function polyContains(latlng: LatLng, polygon: Polygon) { + var poly = polygon.toGeoJSON(); + return turf.inside(turf.point([latlng.lng, latlng.lat]), poly); +} + export function randomPointInPoly(polygon: Polygon): LatLng { var bounds = polygon.getBounds(); var x_min = bounds.getEast(); @@ -226,12 +231,45 @@ export function randomPointInPoly(polygon: Polygon): LatLng { } export function polygonArea(polygon: Polygon) { - var poly = polygon.toGeoJSON(); + var poly = polygon.toGeoJSON(); return turf.area(poly); } -export function randomUnitBlueprintByRole(unitDatabse: UnitDatabase, role: string) { - const unitBlueprints = unitDatabse.getByRole(role); +export function randomUnitBlueprint(unitDatabase: UnitDatabase, options: {type?: string, role?: string, ranges?: string[], eras?: string[]} ) { + /* Start from all the unit blueprints in the database */ + var unitBlueprints = Object.values(unitDatabase.getBlueprints()); + + /* If a specific type or role is provided, use only the blueprints of that type or role */ + if (options.type && options.role) { + console.error("Can't create random unit if both type and role are provided. Either create by type or by role.") + return null; + } + + if (options.type) { + unitBlueprints = unitDatabase.getByType(options.type); + } + else if (options.role) { + unitBlueprints = unitDatabase.getByType(options.role); + } + + /* Keep only the units that have a range included in the requested values */ + if (options.ranges) { + unitBlueprints = unitBlueprints.filter((unitBlueprint: UnitBlueprint) => { + //@ts-ignore + return unitBlueprint.range? options.ranges.includes(unitBlueprint.range): true; + }); + } + + /* Keep only the units that have an era included in the requested values */ + if (options.eras) { + unitBlueprints = unitBlueprints.filter((unitBlueprint: UnitBlueprint) => { + //@ts-ignore + return options.eras.reduce((value, era) => { + return value? value: unitBlueprint.era.includes(era); + }, false); + }); + } + var index = Math.floor(Math.random() * unitBlueprints.length); return unitBlueprints[index]; } diff --git a/client/src/units/citiesDatabase.ts b/client/src/units/citiesDatabase.ts new file mode 100644 index 00000000..5979ff28 --- /dev/null +++ b/client/src/units/citiesDatabase.ts @@ -0,0 +1,7137 @@ +export var citiesDatabase: {lat: number, lng: number, pop: number}[] = [ + { + lat: 41.0136, + lng: 28.955, + pop: 16079000, + }, + { + lat: 39.93, + lng: 32.85, + pop: 5503985, + }, + { + lat: 31.9497, + lng: 35.9328, + pop: 4007526, + }, + { + lat: 25.2631, + lng: 55.2972, + pop: 3331420, + }, + { + lat: 29.3697, + lng: 47.9783, + pop: 3000000, + }, + { + lat: 40.1833, + lng: 29.05, + pop: 2901396, + }, + { + lat: 36.8874, + lng: 30.7075, + pop: 2426356, + }, + { + lat: 32.6447, + lng: 51.6675, + pop: 2219343, + }, + { + lat: 36.2333, + lng: -115.2654, + pop: 2150373, + }, + { + lat: 37.0628, + lng: 37.3792, + pop: 2028563, + }, + { + lat: 37.1583, + lng: 38.7917, + pop: 1985753, + }, + { + lat: 36.2, + lng: 37.16, + pop: 1916781, + }, + { + lat: 36.8, + lng: 34.6333, + pop: 1814468, + }, + { + lat: 37, + lng: 35.3213, + pop: 1765981, + }, + { + lat: 33.5131, + lng: 36.2919, + pop: 1754000, + }, + { + lat: 29.61, + lng: 52.5425, + pop: 1565572, + }, + { + lat: 24.4667, + lng: 54.3667, + pop: 1483000, + }, + { + lat: 23.6139, + lng: 58.5922, + pop: 1421409, + }, + { + lat: 32.08, + lng: 34.78, + pop: 1388400, + }, + { + lat: 41.2903, + lng: 36.3336, + pop: 1335716, + }, + { + lat: 30.515, + lng: 47.81, + pop: 1326564, + }, + { + lat: 31.3203, + lng: 48.6692, + pop: 1261042, + }, + { + lat: 25.3575, + lng: 55.3908, + pop: 1247749, + }, + { + lat: 41.0167, + lng: 39.55, + pop: 1215351, + }, + { + lat: 25.2867, + lng: 51.5333, + pop: 1186023, + }, + { + lat: 47.2333, + lng: 39.7, + pop: 1137704, + }, + { + lat: 41.7225, + lng: 44.7925, + pop: 1118035, + }, + { + lat: 40.1814, + lng: 44.5144, + pop: 1075800, + }, + { + lat: 46.4775, + lng: 30.7326, + pop: 1017699, + }, + { + lat: 47.0228, + lng: 28.8353, + pop: 702300, + }, + { + lat: 26.225, + lng: 50.5775, + pop: 436000, + }, + { + lat: 33.8869, + lng: 35.5131, + pop: 361366, + }, + { + lat: 35.1725, + lng: 33.365, + pop: 330000, + }, + { + lat: 15.2137, + lng: 145.7546, + pop: 2500, + }, + { + lat: 13.4745, + lng: 144.7504, + pop: 1051, + }, + { + lat: 45.0333, + lng: 38.9667, + pop: 948827, + }, + { + lat: 39.9244, + lng: 32.8856, + pop: 914501, + }, + { + lat: 41.047, + lng: 28.658, + pop: 891120, + }, + { + lat: 26.4333, + lng: 50.1, + pop: 903312, + }, + { + lat: 39.7767, + lng: 30.5206, + pop: 871187, + }, + { + lat: 36.9831, + lng: 35.3328, + pop: 792536, + }, + { + lat: 41.0344, + lng: 28.8564, + pop: 734369, + }, + { + lat: 34.7333, + lng: 36.7167, + pop: 775404, + }, + { + lat: 41, + lng: 28.8, + pop: 770317, + }, + { + lat: 39.9086, + lng: 41.2769, + pop: 767848, + }, + { + lat: 24.2075, + lng: 55.7447, + pop: 766936, + }, + { + lat: 30.2833, + lng: 57.0833, + pop: 738374, + }, + { + lat: 35.5167, + lng: 35.7833, + pop: 700000, + }, + { + lat: 35.1333, + lng: 36.75, + pop: 696863, + }, + { + lat: 40.8747, + lng: 29.235, + pop: 693599, + }, + { + lat: 25.3833, + lng: 49.5833, + pop: 660788, + }, + { + lat: 40.11, + lng: 29.0821, + pop: 643681, + }, + { + lat: 25.25, + lng: 51.4, + pop: 605712, + }, + { + lat: 32.8192, + lng: 34.9992, + pop: 600000, + }, + { + lat: 42.9833, + lng: 47.4833, + pop: 592976, + }, + { + lat: 35.3529, + lng: -119.0359, + pop: 590845, + }, + { + lat: 29.4964, + lng: 60.8628, + pop: 587730, + }, + { + lat: 32.3399, + lng: 36.2052, + pop: 580000, + }, + { + lat: 32.55, + lng: 35.85, + pop: 569068, + }, + { + lat: 34.4367, + lng: 35.8344, + pop: 530000, + }, + { + lat: 46.35, + lng: 48.035, + pop: 532504, + }, + { + lat: 31.8822, + lng: 54.3397, + pop: 529673, + }, + { + lat: 27.1833, + lng: 56.2667, + pop: 526648, + }, + { + lat: 26.556, + lng: 49.996, + pop: 524182, + }, + { + lat: 44.605, + lng: 33.5225, + pop: 522057, + }, + { + lat: 46.975, + lng: 31.995, + pop: 498748, + }, + { + lat: 32.3436, + lng: 62.1194, + pop: 500000, + }, + { + lat: 25.4136, + lng: 55.4456, + pop: 490035, + }, + { + lat: 32.0833, + lng: 36.1, + pop: 481300, + }, + { + lat: 40.8872, + lng: 29.19, + pop: 461155, + }, + { + lat: 41.0719, + lng: 28.9664, + pop: 437026, + }, + { + lat: 41.0339, + lng: 28.8903, + pop: 444561, + }, + { + lat: 45.05, + lng: 41.9833, + pop: 450680, + }, + { + lat: 47.0958, + lng: 37.5494, + pop: 449498, + }, + { + lat: 40.9792, + lng: 28.7214, + pop: 435625, + }, + { + lat: 37.5833, + lng: 36.9333, + pop: 443575, + }, + { + lat: 41.005, + lng: 39.7225, + pop: 426882, + }, + { + lat: 43.5853, + lng: 39.7203, + pop: 411524, + }, + { + lat: 36.9981, + lng: 35.3439, + pop: 407054, + }, + { + lat: 29.35, + lng: 47.6833, + pop: 393432, + }, + { + lat: 27, + lng: 49.6544, + pop: 392948, + }, + { + lat: 34.5277, + lng: -117.3536, + pop: 389060, + }, + { + lat: 23.5333, + lng: 58.3833, + pop: 383257, + }, + { + lat: 34.6935, + lng: -118.1753, + pop: 381732, + }, + { + lat: 36.2025, + lng: 36.1606, + pop: 377793, + }, + { + lat: 39.75, + lng: 37.0167, + pop: 377561, + }, + { + lat: 40.8, + lng: 29.4333, + pop: 371000, + }, + { + lat: 30.3392, + lng: 48.3042, + pop: 370180, + }, + { + lat: 30.3833, + lng: 47.7, + pop: 370000, + }, + { + lat: 40.7625, + lng: 29.9175, + pop: 363416, + }, + { + lat: 43.2167, + lng: 27.9167, + pop: 348668, + }, + { + lat: 44.9484, + lng: 34.1, + pop: 341799, + }, + { + lat: 41.1669, + lng: 29.0572, + pop: 342503, + }, + { + lat: 36.9165, + lng: 34.8951, + pop: 339676, + }, + { + lat: 40.9683, + lng: 29.2617, + pop: 327798, + }, + { + lat: 41.0011, + lng: 28.6419, + pop: 331525, + }, + { + lat: 40.6828, + lng: 46.3606, + pop: 331400, + }, + { + lat: 39.6333, + lng: 27.8833, + pop: 331788, + }, + { + lat: 41.0225, + lng: 28.8717, + pop: 289331, + }, + { + lat: 36.55, + lng: 32, + pop: 312319, + }, + { + lat: 36.0133, + lng: -115.0381, + pop: 311250, + }, + { + lat: 43.04, + lng: 44.6775, + pop: 306978, + }, + { + lat: 35.95, + lng: 39.01, + pop: 299824, + }, + { + lat: 36.5817, + lng: 36.165, + pop: 297943, + }, + { + lat: 25.4416, + lng: 49.6642, + pop: 298562, + }, + { + lat: 40.5455, + lng: 34.957, + pop: 294807, + }, + { + lat: 43.3125, + lng: 45.6986, + pop: 291687, + }, + { + lat: 46.6425, + lng: 32.625, + pop: 291428, + }, + { + lat: 32.0178, + lng: 36.0464, + pop: 280000, + }, + { + lat: 44.1667, + lng: 28.6333, + pop: 283872, + }, + { + lat: 41.15, + lng: 27.8, + pop: 279251, + }, + { + lat: 31.5831, + lng: 64.3692, + pop: 276831, + }, + { + lat: 34.4175, + lng: -118.4964, + pop: 275230, + }, + { + lat: 44.7167, + lng: 37.75, + pop: 273278, + }, + { + lat: 40.7833, + lng: 30.4, + pop: 271515, + }, + { + lat: 35.3333, + lng: 40.15, + pop: 271800, + }, + { + lat: 41.1856, + lng: 28.7406, + pop: 270549, + }, + { + lat: 43.4833, + lng: 43.6167, + pop: 265162, + }, + { + lat: 40.0806, + lng: 29.5097, + pop: 268155, + }, + { + lat: 32.3825, + lng: 48.4019, + pop: 264709, + }, + { + lat: 37.075, + lng: 36.25, + pop: 264373, + }, + { + lat: 36.2883, + lng: -115.0888, + pop: 259638, + }, + { + lat: 40.8161, + lng: 29.3006, + pop: 255468, + }, + { + lat: 31.95, + lng: 34.8, + pop: 249860, + }, + { + lat: 41.0369, + lng: 29.1786, + pop: 251937, + }, + { + lat: 47.2167, + lng: 38.9167, + pop: 250287, + }, + { + lat: 37.1939, + lng: 40.5861, + pop: 252656, + }, + { + lat: 45.4233, + lng: 28.0425, + pop: 249432, + }, + { + lat: 36.3276, + lng: -119.3269, + pop: 249804, + }, + { + lat: 41.02, + lng: 28.5775, + pop: 247736, + }, + { + lat: 32.7003, + lng: 51.5211, + pop: 247128, + }, + { + lat: 41.1342, + lng: 29.0922, + pop: 246700, + }, + { + lat: 32.0889, + lng: 34.8864, + pop: 236169, + }, + { + lat: 40.8417, + lng: 31.1583, + pop: 240633, + }, + { + lat: 30.4411, + lng: 47.9725, + pop: 240300, + }, + { + lat: 34.6747, + lng: 33.0442, + pop: 235056, + }, + { + lat: 23.6703, + lng: 58.1891, + pop: 237816, + }, + { + lat: 32.6347, + lng: 51.3653, + pop: 235281, + }, + { + lat: 36.7833, + lng: 31.4333, + pop: 230597, + }, + { + lat: 40.9833, + lng: 37.8833, + pop: 229214, + }, + { + lat: 28.9264, + lng: 50.8514, + pop: 223504, + }, + { + lat: 26.2833, + lng: 50.2, + pop: 219679, + }, + { + lat: 36.0091, + lng: -115.2278, + pop: 219566, + }, + { + lat: 32.3286, + lng: 34.8567, + pop: 217244, + }, + { + lat: 36.0952, + lng: -115.2636, + pop: 217441, + }, + { + lat: 23.6167, + lng: 58.5667, + pop: 214901, + }, + { + lat: 33.5606, + lng: 35.3758, + pop: 200000, + }, + { + lat: 33.8936, + lng: 35.5403, + pop: 150000, + }, + { + lat: 32.0833, + lng: 34.8333, + pop: 193774, + }, + { + lat: 40.7347, + lng: 31.6075, + pop: 205525, + }, + { + lat: 32.0167, + lng: 34.7667, + pop: 194300, + }, + { + lat: 32.8667, + lng: 59.2167, + pop: 203636, + }, + { + lat: 25.69, + lng: 51.51, + pop: 202031, + }, + { + lat: 40.3139, + lng: 36.5542, + pop: 201294, + }, + { + lat: 34.4285, + lng: -119.7202, + pop: 198240, + }, + { + lat: 36.1783, + lng: -115.0487, + pop: 196411, + }, + { + lat: 37.2306, + lng: 39.7653, + pop: 195910, + }, + { + lat: 37.1847, + lng: 38.7908, + pop: 195552, + }, + { + lat: 37.1819, + lng: 33.2181, + pop: 194018, + }, + { + lat: 39.8417, + lng: 33.5139, + pop: 193093, + }, + { + lat: 45, + lng: 41.1167, + pop: 190709, + }, + { + lat: 32.3311, + lng: 50.8594, + pop: 190441, + }, + { + lat: 36.0872, + lng: -115.1355, + pop: 189852, + }, + { + lat: 36.5117, + lng: 40.7422, + pop: 188160, + }, + { + lat: 37.05, + lng: 41.22, + pop: 184231, + }, + { + lat: 45.2692, + lng: 27.9575, + pop: 180302, + }, + { + lat: 41.2792, + lng: 31.4208, + pop: 175605, + }, + { + lat: 29.437, + lng: 55.6802, + pop: 175000, + }, + { + lat: 33.2708, + lng: 35.1961, + pop: 160000, + }, + { + lat: 32.8667, + lng: 51.5667, + pop: 173329, + }, + { + lat: 41.6458, + lng: 41.6417, + pop: 169095, + }, + { + lat: 32.07, + lng: 34.8236, + pop: 159200, + }, + { + lat: 23.8494, + lng: 57.4386, + pop: 170000, + }, + { + lat: 31.8667, + lng: 36, + pop: 169434, + }, + { + lat: 34.5944, + lng: -118.1057, + pop: 167987, + }, + { + lat: 41.2833, + lng: 28, + pop: 166789, + }, + { + lat: 40.7833, + lng: 29.7333, + pop: 165503, + }, + { + lat: 35.9333, + lng: 36.6333, + pop: 165000, + }, + { + lat: 29.1322, + lng: 48.1261, + pop: 164212, + }, + { + lat: 40.6667, + lng: 29.8333, + pop: 162584, + }, + { + lat: 30.5589, + lng: 49.1981, + pop: 162797, + }, + { + lat: 30.4067, + lng: 55.9939, + pop: 161909, + }, + { + lat: 25.3722, + lng: 51.2047, + pop: 161240, + }, + { + lat: 30.96, + lng: 61.86, + pop: 160902, + }, + { + lat: 37.0289, + lng: 35.8125, + pop: 160474, + }, + { + lat: 32.2222, + lng: 35.2611, + pop: 156906, + }, + { + lat: 39.7464, + lng: 39.4914, + pop: 157452, + }, + { + lat: 32.5589, + lng: 36.0147, + pop: 155693, + }, + { + lat: 34.447, + lng: 35.8178, + pop: 150000, + }, + { + lat: 29.2694, + lng: 51.22, + pop: 155567, + }, + { + lat: 41.6344, + lng: 32.3375, + pop: 155016, + }, + { + lat: 46.8489, + lng: 35.3675, + pop: 154992, + }, + { + lat: 40.35, + lng: 27.9667, + pop: 154359, + }, + { + lat: 26.65, + lng: 50.1667, + pop: 153933, + }, + { + lat: 24.362, + lng: 56.7344, + pop: 151349, + }, + { + lat: 45.3619, + lng: 36.4711, + pop: 149566, + }, + { + lat: 42.25, + lng: 42.7, + pop: 147900, + }, + { + lat: 39.7186, + lng: 43.0508, + pop: 149188, + }, + { + lat: 32.3325, + lng: 35.7517, + pop: 148870, + }, + { + lat: 40.65, + lng: 35.8331, + pop: 149084, + }, + { + lat: 41.3764, + lng: 33.7764, + pop: 148931, + }, + { + lat: 29.8742, + lng: 52.8025, + pop: 148858, + }, + { + lat: 26.0031, + lng: 63.0544, + pop: 147791, + }, + { + lat: 44.05, + lng: 43.0667, + pop: 145836, + }, + { + lat: 32.6189, + lng: 36.1021, + pop: 146481, + }, + { + lat: 40.6556, + lng: 29.275, + pop: 144407, + }, + { + lat: 37.5167, + lng: 34.05, + pop: 145389, + }, + { + lat: 43.25, + lng: 46.5833, + pop: 141259, + }, + { + lat: 33.4472, + lng: 36.3361, + pop: 136427, + }, + { + lat: 37.332, + lng: 42.187, + pop: 143124, + }, + { + lat: 37.01, + lng: 37.7972, + pop: 142389, + }, + { + lat: 44.6, + lng: 40.0833, + pop: 141970, + }, + { + lat: 41.0247, + lng: 40.5222, + pop: 141143, + }, + { + lat: 28.5, + lng: 53.5606, + pop: 141634, + }, + { + lat: 41.1992, + lng: 36.7275, + pop: 138840, + }, + { + lat: 32.0167, + lng: 34.75, + pop: 128800, + }, + { + lat: 41.0736, + lng: 28.2478, + pop: 137861, + }, + { + lat: 39.9208, + lng: 44.0444, + pop: 137613, + }, + { + lat: 46.8403, + lng: 29.6433, + pop: 133807, + }, + { + lat: 40.9153, + lng: 38.3894, + pop: 135920, + }, + { + lat: 40.0528, + lng: 47.4614, + pop: 136000, + }, + { + lat: 32.46, + lng: 48.3592, + pop: 135116, + }, + { + lat: 32.0089, + lng: 51.8667, + pop: 134952, + }, + { + lat: 31.0286, + lng: 61.5011, + pop: 134950, + }, + { + lat: 33.8, + lng: 35.6, + pop: 130000, + }, + { + lat: 31.8981, + lng: 34.8081, + pop: 132671, + }, + { + lat: 33.4833, + lng: 36.35, + pop: 114363, + }, + { + lat: 30.4397, + lng: 48.1664, + pop: 133097, + }, + { + lat: 41.1986, + lng: 32.6264, + pop: 131989, + }, + { + lat: 41.5472, + lng: 45.0111, + pop: 128680, + }, + { + lat: 43.9167, + lng: 42.7167, + pop: 128779, + }, + { + lat: 37.45, + lng: 35.8, + pop: 130495, + }, + { + lat: 28.6781, + lng: 57.7406, + pop: 130429, + }, + { + lat: 40.8265, + lng: 29.3745, + pop: 129655, + }, + { + lat: 47.1667, + lng: 39.7333, + pop: 126769, + }, + { + lat: 33.464, + lng: 36.3044, + pop: 101827, + }, + { + lat: 37.0758, + lng: -113.5752, + pop: 127890, + }, + { + lat: 42.8803, + lng: 47.6383, + pop: 123988, + }, + { + lat: 29.1061, + lng: 58.3569, + pop: 127396, + }, + { + lat: 41.1333, + lng: 37.2833, + pop: 126702, + }, + { + lat: 33.5711, + lng: 36.4011, + pop: 123494, + }, + { + lat: 40.7894, + lng: 43.8475, + pop: 121976, + }, + { + lat: 36.9167, + lng: 31.1, + pop: 124335, + }, + { + lat: 37.3697, + lng: 36.1, + pop: 124053, + }, + { + lat: 44.2222, + lng: 42.0575, + pop: 122395, + }, + { + lat: 42.05, + lng: 48.3, + pop: 123720, + }, + { + lat: 30.5958, + lng: 50.2417, + pop: 122604, + }, + { + lat: 13.4692, + lng: 144.7332, + pop: 122411, + }, + { + lat: 36.085, + lng: 35.9806, + pop: 121109, + }, + { + lat: 41.4564, + lng: 31.7986, + pop: 120395, + }, + { + lat: 23.3908, + lng: 57.4244, + pop: 120000, + }, + { + lat: 43.2167, + lng: 44.7667, + pop: 117936, + }, + { + lat: 36.3761, + lng: 33.9322, + pop: 119303, + }, + { + lat: 44.6333, + lng: 41.9333, + pop: 117446, + }, + { + lat: 36.8278, + lng: -119.683, + pop: 118488, + }, + { + lat: 40.7333, + lng: 29.9667, + pop: 118066, + }, + { + lat: 41.3333, + lng: 27.9667, + pop: 116882, + }, + { + lat: 33.9697, + lng: 35.6156, + pop: 102221, + }, + { + lat: 41.0333, + lng: 37.5, + pop: 116154, + }, + { + lat: 25.7667, + lng: 55.95, + pop: 115949, + }, + { + lat: 36.7167, + lng: 37.1167, + pop: 116034, + }, + { + lat: 26.475, + lng: 50.0417, + pop: 115000, + }, + { + lat: 40.6078, + lng: 43.0958, + pop: 115891, + }, + { + lat: 46.7556, + lng: 36.7889, + pop: 114205, + }, + { + lat: 44.0333, + lng: 42.85, + pop: 113056, + }, + { + lat: 22.5667, + lng: 58.1167, + pop: 115040, + }, + { + lat: 31.5231, + lng: 49.8861, + pop: 114343, + }, + { + lat: 27.2025, + lng: 60.6847, + pop: 113750, + }, + { + lat: 37.4167, + lng: 41.3697, + pop: 113367, + }, + { + lat: 36.7108, + lng: 38.9478, + pop: 113194, + }, + { + lat: 33.8333, + lng: 35.9167, + pop: 100000, + }, + { + lat: 24.1456, + lng: 49.0653, + pop: 111214, + }, + { + lat: 28.9383, + lng: 53.6483, + pop: 110825, + }, + { + lat: 35.0118, + lng: 37.0525, + pop: 110683, + }, + { + lat: 45.1939, + lng: 33.3681, + pop: 108248, + }, + { + lat: 30.6683, + lng: 51.5881, + pop: 108505, + }, + { + lat: 30.0342, + lng: 47.9294, + pop: 107620, + }, + { + lat: 32.1714, + lng: 34.9083, + pop: 100800, + }, + { + lat: 39.5736, + lng: -119.7161, + pop: 106900, + }, + { + lat: 36.0778, + lng: 37.3733, + pop: 106460, + }, + { + lat: 25.2919, + lng: 60.6431, + pop: 106739, + }, + { + lat: 40.77, + lng: 47.0489, + pop: 106100, + }, + { + lat: 37.075, + lng: 41.2153, + pop: 105856, + }, + { + lat: 39.8208, + lng: 34.8083, + pop: 105167, + }, + { + lat: 29.9758, + lng: 48.4722, + pop: 105080, + }, + { + lat: 36.9764, + lng: 38.4269, + pop: 104302, + }, + { + lat: 46.3167, + lng: 44.2667, + pop: 103535, + }, + { + lat: 32.0456, + lng: 48.8567, + pop: 101878, + }, + { + lat: 23.2325, + lng: 56.4973, + pop: 101640, + }, + { + lat: 31.9364, + lng: 49.3039, + pop: 100497, + }, + { + lat: 32.1653, + lng: 34.8458, + pop: 93989, + }, + { + lat: 40.5986, + lng: 33.6192, + pop: 96025, + }, + { + lat: 40.8128, + lng: 44.4883, + pop: 90525, + }, + { + lat: 25.1223, + lng: 56.3342, + pop: 93673, + }, + { + lat: 46.8333, + lng: 29.4833, + pop: 91882, + }, + { + lat: 43.5667, + lng: 27.8333, + pop: 90419, + }, + { + lat: 34.8833, + lng: 35.8833, + pop: 89457, + }, + { + lat: 32.0333, + lng: 35.7333, + pop: 88900, + }, + { + lat: 33.3833, + lng: 35.45, + pop: 80000, + }, + { + lat: 25.18, + lng: 51.61, + pop: 87970, + }, + { + lat: 33.45, + lng: 36.25, + pop: 84044, + }, + { + lat: 37.3131, + lng: 40.735, + pop: 86948, + }, + { + lat: 34.9167, + lng: 33.6333, + pop: 84900, + }, + { + lat: 35.8367, + lng: 38.5481, + pop: 84000, + }, + { + lat: 32.7019, + lng: 35.3033, + pop: 83400, + }, + { + lat: 40.1431, + lng: 29.9792, + pop: 81723, + }, + { + lat: 32.1833, + lng: 34.8667, + pop: 74000, + }, + { + lat: 26.25, + lng: 50.6167, + pop: 75000, + }, + { + lat: 31.9275, + lng: 34.8625, + pop: 75500, + }, + { + lat: 45.19, + lng: 28.8, + pop: 73707, + }, + { + lat: 39.9323, + lng: 48.9203, + pop: 70684, + }, + { + lat: 22.9333, + lng: 57.5333, + pop: 72076, + }, + { + lat: 32.0714, + lng: 34.81, + pop: 59518, + }, + { + lat: 33.2631, + lng: 35.2389, + pop: 61973, + }, + { + lat: 44.15, + lng: 43.4667, + pop: 67054, + }, + { + lat: 24.2592, + lng: 55.7839, + pop: 67963, + }, + { + lat: 41.2, + lng: 47.1667, + pop: 68360, + }, + { + lat: 42.8167, + lng: 47.1167, + pop: 65080, + }, + { + lat: 40.2597, + lng: 40.2278, + pop: 66633, + }, + { + lat: 34.7667, + lng: 32.4167, + pop: 63600, + }, + { + lat: 32.7125, + lng: 36.5667, + pop: 64730, + }, + { + lat: 43, + lng: 41.0167, + pop: 64441, + }, + { + lat: 46.6383, + lng: 27.7292, + pop: 63035, + }, + { + lat: 46.3017, + lng: 30.6569, + pop: 59800, + }, + { + lat: 26.219, + lng: 50.538, + pop: 44769, + }, + { + lat: 32.15, + lng: 34.8833, + pop: 56659, + }, + { + lat: 40.6172, + lng: 47.15, + pop: 59036, + }, + { + lat: 32.8, + lng: 35.1, + pop: 55464, + }, + { + lat: 32.0956, + lng: 34.9567, + pop: 56300, + }, + { + lat: 46.2167, + lng: 27.6667, + pop: 55837, + }, + { + lat: 33.4728, + lng: 36.3344, + pop: 50880, + }, + { + lat: 39.1511, + lng: -119.7476, + pop: 57957, + }, + { + lat: 32.5194, + lng: 35.1536, + pop: 55300, + }, + { + lat: 40.4597, + lng: 39.4778, + pop: 57269, + }, + { + lat: 26.1128, + lng: 50.5139, + pop: 52700, + }, + { + lat: 32.1903, + lng: 34.9686, + pop: 51683, + }, + { + lat: 31.9333, + lng: 34.8, + pop: 50200, + }, + { + lat: 33.5186, + lng: 35.3661, + pop: 50000, + }, + { + lat: 40.5, + lng: 44.7667, + pop: 52808, + }, + { + lat: 32.0636, + lng: 34.8553, + pop: 41900, + }, + { + lat: 33.5667, + lng: 36.3667, + pop: 45974, + }, + { + lat: 32.2723, + lng: 35.8914, + pop: 50745, + }, + { + lat: 41.9817, + lng: 44.1124, + pop: 48143, + }, + { + lat: 40.2739, + lng: 44.6256, + pop: 44400, + }, + { + lat: 32.0522, + lng: 34.9511, + pop: 46896, + }, + { + lat: 34.1236, + lng: 35.6511, + pop: 40000, + }, + { + lat: 32.15, + lng: 34.8333, + pop: 46700, + }, + { + lat: 26.1736, + lng: 50.5478, + pop: 40000, + }, + { + lat: 32.9136, + lng: 35.2961, + pop: 45300, + }, + { + lat: 31.8558, + lng: 34.73, + pop: 42314, + }, + { + lat: 36.1008, + lng: -115.0379, + pop: 45105, + }, + { + lat: 42.5083, + lng: 41.8667, + pop: 42998, + }, + { + lat: 32.8333, + lng: 35.0833, + pop: 39900, + }, + { + lat: 25.5533, + lng: 55.5475, + pop: 44411, + }, + { + lat: 32.0333, + lng: 34.85, + pop: 36706, + }, + { + lat: 32.8056, + lng: 35.1694, + pop: 41600, + }, + { + lat: 40.15, + lng: 44.04, + pop: 38635, + }, + { + lat: 41.1111, + lng: 42.7022, + pop: 42226, + }, + { + lat: 34.4, + lng: 35.9, + pop: 30000, + }, + { + lat: 35.125, + lng: 33.9417, + pop: 40920, + }, + { + lat: 40.8297, + lng: 46.0189, + pop: 40600, + }, + { + lat: 41.4708, + lng: 48.8097, + pop: 39900, + }, + { + lat: 40.3744, + lng: 47.1267, + pop: 38500, + }, + { + lat: 33.1256, + lng: 35.8239, + pop: 37022, + }, + { + lat: 40.6531, + lng: 47.7406, + pop: 36200, + }, + { + lat: 36.1365, + lng: -115.137, + pop: 36307, + }, + { + lat: 32.0333, + lng: 34.8833, + pop: 29146, + }, + { + lat: 41.0933, + lng: 45.3661, + pop: 35102, + }, + { + lat: 41.1833, + lng: 41.8181, + pop: 35081, + }, + { + lat: 32.0781, + lng: 34.8475, + pop: 25298, + }, + { + lat: 22.6833, + lng: 58.55, + pop: 35000, + }, + { + lat: 33.92, + lng: 35.67, + pop: 28000, + }, + { + lat: 25.2525, + lng: 51.5592, + pop: 29703, + }, + { + lat: 35.3403, + lng: 33.3192, + pop: 33207, + }, + { + lat: 33.1258, + lng: 35.4428, + pop: 30000, + }, + { + lat: 42.1167, + lng: 48.1833, + pop: 29716, + }, + { + lat: 41.6336, + lng: 46.6433, + pop: 31300, + }, + { + lat: 39.8697, + lng: 48.06, + pop: 31310, + }, + { + lat: 40.0128, + lng: 48.4789, + pop: 30918, + }, + { + lat: 47.2167, + lng: 27.8167, + pop: 30804, + }, + { + lat: 32.3171, + lng: 34.9358, + pop: 28025, + }, + { + lat: 33.5, + lng: 36.3667, + pop: 22535, + }, + { + lat: 45.9167, + lng: 28.1836, + pop: 30018, + }, + { + lat: 40.4097, + lng: 44.6431, + pop: 25039, + }, + { + lat: 40.65, + lng: 47.4761, + pop: 29600, + }, + { + lat: 35.3683, + lng: -118.9225, + pop: 29110, + }, + { + lat: 32.2322, + lng: 34.9483, + pop: 26200, + }, + { + lat: 26.12, + lng: 50.65, + pop: 20000, + }, + { + lat: 40.5905, + lng: 46.3239, + pop: 25758, + }, + { + lat: 34.0063, + lng: 36.2073, + pop: 24000, + }, + { + lat: 32.1151, + lng: 34.9751, + pop: 21848, + }, + { + lat: 33.8333, + lng: 35.5333, + pop: 9000, + }, + { + lat: 32.2822, + lng: 34.9833, + pop: 21451, + }, + { + lat: 39.9539, + lng: 44.5506, + pop: 21300, + }, + { + lat: 40.2975, + lng: 44.3617, + pop: 21600, + }, + { + lat: 40.8792, + lng: 45.1472, + pop: 20509, + }, + { + lat: 40.0433, + lng: 48.9356, + pop: 21504, + }, + { + lat: 47.3833, + lng: 28.8167, + pop: 21065, + }, + { + lat: 26.1497, + lng: 50.4653, + pop: 18000, + }, + { + lat: 26.2186, + lng: 50.4756, + pop: 18000, + }, + { + lat: 40.3589, + lng: 45.1267, + pop: 20765, + }, + { + lat: 41.3597, + lng: 48.5125, + pop: 20791, + }, + { + lat: 40.79, + lng: 48.1519, + pop: 20660, + }, + { + lat: 40.5692, + lng: 48.4008, + pop: 20200, + }, + { + lat: 41.1189, + lng: 45.4539, + pop: 20200, + }, + { + lat: 46.3167, + lng: 28.6667, + pop: 20113, + }, + { + lat: 34.2264, + lng: 35.66, + pop: 9613, + }, + { + lat: 33.4292, + lng: 36.3611, + pop: 12330, + }, + { + lat: 33.8219, + lng: 35.5875, + pop: 13000, + }, + { + lat: 41.9167, + lng: 45.4833, + pop: 19629, + }, + { + lat: 41.0761, + lng: 49.1139, + pop: 18655, + }, + { + lat: 34.7024, + lng: 33.0453, + pop: 14477, + }, + { + lat: 47.1333, + lng: 28.6167, + pop: 18376, + }, + { + lat: 39.9311, + lng: 48.3697, + pop: 17900, + }, + { + lat: 26.1008, + lng: 50.4878, + pop: 14800, + }, + { + lat: 26.1833, + lng: 56.25, + pop: 17777, + }, + { + lat: 41.4264, + lng: 48.4356, + pop: 16500, + }, + { + lat: 46.6333, + lng: 29.4, + pop: 15939, + }, + { + lat: 40.3383, + lng: 48.1608, + pop: 15385, + }, + { + lat: 26.2306, + lng: 50.5108, + pop: 12000, + }, + { + lat: 41.9406, + lng: 41.9906, + pop: 14785, + }, + { + lat: 36.6087, + lng: -119.5434, + pop: 14666, + }, + { + lat: 40.5183, + lng: 47.6542, + pop: 14273, + }, + { + lat: 40.3019, + lng: 44.5831, + pop: 10198, + }, + { + lat: 33.4517, + lng: 35.2908, + pop: 10965, + }, + { + lat: 41.6389, + lng: 42.9861, + pop: 14000, + }, + { + lat: 40.9922, + lng: 45.6289, + pop: 13700, + }, + { + lat: 42.0267, + lng: 35.1511, + pop: 13354, + }, + { + lat: 46.95, + lng: 28.7833, + pop: 12515, + }, + { + lat: 33.2814, + lng: 35.3964, + pop: 10000, + }, + { + lat: 46.8167, + lng: 28.5833, + pop: 12491, + }, + { + lat: 40.345, + lng: 46.9289, + pop: 12563, + }, + { + lat: 45.9, + lng: 28.6689, + pop: 12355, + }, + { + lat: 39.7756, + lng: 47.6186, + pop: 12263, + }, + { + lat: 46.5167, + lng: 28.7833, + pop: 11997, + }, + { + lat: 40.3147, + lng: 44.5936, + pop: 9513, + }, + { + lat: 41.4225, + lng: 46.9242, + pop: 11415, + }, + { + lat: 35.379, + lng: -118.9578, + pop: 11443, + }, + { + lat: 43.1667, + lng: 44.8, + pop: 10333, + }, + { + lat: 35.3832, + lng: -118.9743, + pop: 11025, + }, + { + lat: 46.8833, + lng: 29.2167, + pop: 10872, + }, + { + lat: 47.25, + lng: 28.3, + pop: 10808, + }, + { + lat: 40.5244, + lng: 46.1069, + pop: 10700, + }, + { + lat: 35.3636, + lng: -118.965, + pop: 10517, + }, + { + lat: 40.5067, + lng: 46.825, + pop: 10100, + }, + { + lat: 40.77, + lng: 46.4111, + pop: 10228, + }, + { + lat: 47.0833, + lng: 28.1833, + pop: 10063, + }, + { + lat: 36.6211, + lng: -119.3188, + pop: 9680, + }, + { + lat: 40.5656, + lng: 45.8161, + pop: 8657, + }, + { + lat: 35.3972, + lng: -118.9892, + pop: 8726, + }, + { + lat: 46.3336, + lng: 28.9614, + pop: 8471, + }, + { + lat: 40.2183, + lng: 47.7083, + pop: 8450, + }, + { + lat: 41.7258, + lng: 46.4083, + pop: 8134, + }, + { + lat: 41.8464, + lng: 44.7194, + pop: 7940, + }, + { + lat: 39.7611, + lng: 45.3333, + pop: 7633, + }, + { + lat: 46.4833, + lng: 28.25, + pop: 7443, + }, + { + lat: 40.6103, + lng: 46.7897, + pop: 7400, + }, + { + lat: 40.1067, + lng: 46.0383, + pop: 7246, + }, + { + lat: 46.5153, + lng: 29.6631, + pop: 7078, + }, + { + lat: 41.0708, + lng: 47.4583, + pop: 6876, + }, + { + lat: 47.2167, + lng: 29.1667, + pop: 6708, + }, + { + lat: 34.5506, + lng: 36.0781, + pop: 4730, + }, + { + lat: 39.7953, + lng: 47.1131, + pop: 5700, + }, + { + lat: 39.7583, + lng: 46.7483, + pop: 4446, + }, + { + lat: 40.5367, + lng: 48.9328, + pop: 3945, + }, + { + lat: 39.9833, + lng: 46.9167, + pop: 3770, + }, + { + lat: 46.2667, + lng: 28.2167, + pop: 3429, + }, + { + lat: 39.6408, + lng: 46.5469, + pop: 2190, + }, + { + lat: 39.7203, + lng: 44.8531, + pop: 2000, + }, + { + lat: 42.5194, + lng: 43.15, + pop: 2047, + }, + { + lat: 40.9078, + lng: 49.0733, + pop: 1600, + }, + { + lat: 39.9111, + lng: 46.7892, + pop: 1397, + }, + { + lat: 36.2692, + lng: 36.5672, + pop: 98534, + }, + { + lat: 34.598, + lng: -112.3185, + pop: 97901, + }, + { + lat: 41.2719, + lng: 36.3508, + pop: 97564, + }, + { + lat: 32.45, + lng: 34.9167, + pop: 95700, + }, + { + lat: 41.5722, + lng: 35.9147, + pop: 97452, + }, + { + lat: 30.3586, + lng: 50.7981, + pop: 96728, + }, + { + lat: 29.6194, + lng: 51.6542, + pop: 96683, + }, + { + lat: 36.3274, + lng: -119.6549, + pop: 95459, + }, + { + lat: 40.6667, + lng: 36.5667, + pop: 95361, + }, + { + lat: 37.025, + lng: 37.9769, + pop: 95149, + }, + { + lat: 45.1333, + lng: 42.0333, + pop: 93658, + }, + { + lat: 41.4267, + lng: 32.0758, + pop: 91569, + }, + { + lat: 31.9077, + lng: 35.0076, + pop: 90013, + }, + { + lat: 25.1264, + lng: 62.3225, + pop: 90762, + }, + { + lat: 36.4128, + lng: 35.8867, + pop: 90456, + }, + { + lat: 35.6386, + lng: 36.6717, + pop: 90000, + }, + { + lat: 40.6833, + lng: 30.6253, + pop: 89301, + }, + { + lat: 36.3225, + lng: 41.8642, + pop: 88023, + }, + { + lat: 36.8461, + lng: 40.0489, + pop: 87684, + }, + { + lat: 32.6539, + lng: 51.7553, + pop: 86063, + }, + { + lat: 46.7111, + lng: 38.2733, + pop: 85760, + }, + { + lat: 22.968, + lng: 57.298, + pop: 85000, + }, + { + lat: 47.1, + lng: 39.4167, + pop: 80721, + }, + { + lat: 44.8667, + lng: 37.3667, + pop: 81447, + }, + { + lat: 32.2444, + lng: 54.0186, + pop: 80712, + }, + { + lat: 23.3, + lng: 57.9833, + pop: 80538, + }, + { + lat: 40.39, + lng: 36.09, + pop: 79916, + }, + { + lat: 44.4994, + lng: 34.17, + pop: 79458, + }, + { + lat: 39.8144, + lng: 35.1903, + pop: 79314, + }, + { + lat: 32.4797, + lng: 51.7753, + pop: 79023, + }, + { + lat: 30.4356, + lng: 49.1056, + pop: 78353, + }, + { + lat: 36.5275, + lng: 37.9553, + pop: 78255, + }, + { + lat: 45.4333, + lng: 40.5667, + pop: 78149, + }, + { + lat: 44.575, + lng: 38.0725, + pop: 77212, + }, + { + lat: 32.1942, + lng: 48.2436, + pop: 77148, + }, + { + lat: 35.35, + lng: 35.9167, + pop: 75505, + }, + { + lat: 37.341, + lng: 41.894, + pop: 76523, + }, + { + lat: 31.9519, + lng: 34.8881, + pop: 75700, + }, + { + lat: 34.5352, + lng: -117.2109, + pop: 75311, + }, + { + lat: 32.3061, + lng: 54.0081, + pop: 75271, + }, + { + lat: 39.9078, + lng: 30.0367, + pop: 74441, + }, + { + lat: 31.28, + lng: 49.6036, + pop: 74285, + }, + { + lat: 44.2167, + lng: 43.1333, + pop: 74141, + }, + { + lat: 34.3688, + lng: 41.0945, + pop: 74100, + }, + { + lat: 37.3914, + lng: 36.8522, + pop: 73770, + }, + { + lat: 37.6764, + lng: 31.7261, + pop: 73768, + }, + { + lat: 29.5839, + lng: 50.5189, + pop: 73472, + }, + { + lat: 36.85, + lng: 31.05, + pop: 73260, + }, + { + lat: 41.1417, + lng: 28.4631, + pop: 72966, + }, + { + lat: 40.875, + lng: 35.4633, + pop: 71916, + }, + { + lat: 45.3517, + lng: 28.8364, + pop: 71411, + }, + { + lat: 23.5242, + lng: 58.4989, + pop: 70000, + }, + { + lat: 45.0489, + lng: 35.3792, + pop: 69145, + }, + { + lat: 29.0769, + lng: 48.0838, + pop: 68763, + }, + { + lat: 36.1995, + lng: -119.34, + pop: 68395, + }, + { + lat: 36.0643, + lng: -119.0338, + pop: 67887, + }, + { + lat: 37.025, + lng: 36.6345, + pop: 67674, + }, + { + lat: 30.7458, + lng: 49.7086, + pop: 67427, + }, + { + lat: 40.9333, + lng: 38.2333, + pop: 66736, + }, + { + lat: 37.575, + lng: 32.7747, + pop: 66794, + }, + { + lat: 45.25, + lng: 38.1167, + pop: 66285, + }, + { + lat: 36.0243, + lng: 32.8026, + pop: 65920, + }, + { + lat: 28.4167, + lng: 48.5, + pop: 65000, + }, + { + lat: 37.4592, + lng: 30.5953, + pop: 64943, + }, + { + lat: 37.4183, + lng: 31.8506, + pop: 64687, + }, + { + lat: 32.8889, + lng: 36.0431, + pop: 63676, + }, + { + lat: 33.6, + lng: 36.3, + pop: 63554, + }, + { + lat: 40.5833, + lng: 36.9667, + pop: 64119, + }, + { + lat: 30.8128, + lng: 56.5639, + pop: 63744, + }, + { + lat: 44.7833, + lng: 44.1667, + pop: 62495, + }, + { + lat: 23.32, + lng: 58.908, + pop: 63133, + }, + { + lat: 36.6333, + lng: 33.4333, + pop: 62853, + }, + { + lat: 32.3464, + lng: 51.5044, + pop: 62454, + }, + { + lat: 32.3117, + lng: 35.0272, + pop: 61941, + }, + { + lat: 44.1044, + lng: 39.0772, + pop: 62269, + }, + { + lat: 34.9167, + lng: 36.7333, + pop: 61176, + }, + { + lat: 37.3658, + lng: 40.2697, + pop: 61830, + }, + { + lat: 33.0058, + lng: 35.0989, + pop: 60000, + }, + { + lat: 35.0025, + lng: 40.5117, + pop: 60780, + }, + { + lat: 27.8389, + lng: 52.0619, + pop: 60187, + }, + { + lat: 32.6064, + lng: 35.2881, + pop: 60000, + }, + { + lat: 44.6333, + lng: 40.7333, + pop: 60164, + }, + { + lat: 43.1333, + lng: 45.55, + pop: 59954, + }, + { + lat: 27.3708, + lng: 62.3342, + pop: 60114, + }, + { + lat: 43.75, + lng: 44.0333, + pop: 57883, + }, + { + lat: 46.1833, + lng: 30.35, + pop: 57210, + }, + { + lat: 42.5633, + lng: 47.8636, + pop: 58690, + }, + { + lat: 40.1728, + lng: 44.2925, + pop: 57500, + }, + { + lat: 39.9319, + lng: 48.9203, + pop: 58253, + }, + { + lat: 45.8667, + lng: 40.1333, + pop: 57771, + }, + { + lat: 46.4833, + lng: 41.5333, + pop: 57622, + }, + { + lat: 44.9233, + lng: 37.9806, + pop: 57229, + }, + { + lat: 31.9678, + lng: 51.2894, + pop: 57071, + }, + { + lat: 30.795, + lng: 50.5644, + pop: 57036, + }, + { + lat: 29.1489, + lng: 48.1057, + pop: 56554, + }, + { + lat: 28.2211, + lng: 61.2158, + pop: 56584, + }, + { + lat: 34.5006, + lng: -114.3113, + pop: 56510, + }, + { + lat: 36.7994, + lng: 36.5178, + pop: 56409, + }, + { + lat: 31.2414, + lng: 48.6525, + pop: 56252, + }, + { + lat: 43.15, + lng: 45.9, + pop: 56226, + }, + { + lat: 32.3897, + lng: 51.3767, + pop: 55984, + }, + { + lat: 35.4389, + lng: 36.6511, + pop: 55843, + }, + { + lat: 40.3, + lng: 35.8833, + pop: 55673, + }, + { + lat: 41.2667, + lng: 27.9667, + pop: 54268, + }, + { + lat: 25.25, + lng: 51.3732, + pop: 54339, + }, + { + lat: 43.35, + lng: 46.1, + pop: 52908, + }, + { + lat: 27.95, + lng: 57.7, + pop: 52624, + }, + { + lat: 34.0167, + lng: 36.7167, + pop: 52502, + }, + { + lat: 41.2125, + lng: 36.4569, + pop: 52258, + }, + { + lat: 35.7662, + lng: -119.2635, + pop: 52206, + }, + { + lat: 44.7686, + lng: 39.8733, + pop: 52082, + }, + { + lat: 34.4536, + lng: 40.9367, + pop: 52020, + }, + { + lat: 45.6167, + lng: 38.9333, + pop: 51925, + }, + { + lat: 30.1164, + lng: 55.1186, + pop: 51620, + }, + { + lat: 33.7986, + lng: 35.825, + pop: 50000, + }, + { + lat: 31.5608, + lng: 48.1831, + pop: 51431, + }, + { + lat: 34.56, + lng: 38.2672, + pop: 51323, + }, + { + lat: 43.85, + lng: 46.7167, + pop: 49247, + }, + { + lat: 34.1794, + lng: 36.4208, + pop: 50000, + }, + { + lat: 39.8153, + lng: 46.7519, + pop: 49848, + }, + { + lat: 32.9278, + lng: 35.0817, + pop: 48900, + }, + { + lat: 37.7147, + lng: 33.5508, + pop: 49766, + }, + { + lat: 40.9333, + lng: 40.05, + pop: 49496, + }, + { + lat: 41.4411, + lng: 27.9216, + pop: 49106, + }, + { + lat: 40.161, + lng: 34.377, + pop: 49082, + }, + { + lat: 35.0183, + lng: 40.4533, + pop: 48922, + }, + { + lat: 41.4333, + lng: 31.75, + pop: 48381, + }, + { + lat: 30.6497, + lng: 48.6647, + pop: 48642, + }, + { + lat: 44.8667, + lng: 40.6167, + pop: 48439, + }, + { + lat: 34.9833, + lng: 36.0833, + pop: 47982, + }, + { + lat: 40.1703, + lng: 31.9211, + pop: 48274, + }, + { + lat: 36.3, + lng: 30.15, + pop: 48131, + }, + { + lat: 23.3103, + lng: 57.9455, + pop: 47718, + }, + { + lat: 37.1042, + lng: 40.93, + pop: 47580, + }, + { + lat: 33.9667, + lng: 36.6667, + pop: 47136, + }, + { + lat: 34.5119, + lng: 36.5764, + pop: 46772, + }, + { + lat: 40.8333, + lng: 35.65, + pop: 46608, + }, + { + lat: 34.8333, + lng: 36.7333, + pop: 45853, + }, + { + lat: 34.25, + lng: 35.65, + pop: 45000, + }, + { + lat: 31.6033, + lng: 55.4003, + pop: 45453, + }, + { + lat: 47.25, + lng: 39.8667, + pop: 45078, + }, + { + lat: 46.755, + lng: 33.375, + pop: 45069, + }, + { + lat: 34.5849, + lng: -112.4473, + pop: 45063, + }, + { + lat: 32.6153, + lng: 51.5556, + pop: 43183, + }, + { + lat: 36.891, + lng: 38.3536, + pop: 44821, + }, + { + lat: 35.8126, + lng: 36.3176, + pop: 44322, + }, + { + lat: 41.1833, + lng: 31.3833, + pop: 44286, + }, + { + lat: 32.7944, + lng: 35.5333, + pop: 44200, + }, + { + lat: 41.088, + lng: 40.7232, + pop: 44304, + }, + { + lat: 30.8989, + lng: 52.6867, + pop: 44341, + }, + { + lat: 41.4653, + lng: 34.7708, + pop: 44004, + }, + { + lat: 25.935, + lng: 49.6661, + pop: 44000, + }, + { + lat: 32.4711, + lng: 34.9675, + pop: 42100, + }, + { + lat: 32.8333, + lng: 35.0833, + pop: 42000, + }, + { + lat: 35.1822, + lng: 35.9403, + pop: 43151, + }, + { + lat: 33.35, + lng: 36.2333, + pop: 43456, + }, + { + lat: 25.3603, + lng: 60.3994, + pop: 43732, + }, + { + lat: 32.2667, + lng: 35.0103, + pop: 43100, + }, + { + lat: 40.945, + lng: 40.2644, + pop: 43499, + }, + { + lat: 24.75, + lng: 56.4667, + pop: 43312, + }, + { + lat: 36.6, + lng: 30.55, + pop: 43226, + }, + { + lat: 39.6658, + lng: 35.8836, + pop: 42919, + }, + { + lat: 40.3381, + lng: 42.5731, + pop: 42683, + }, + { + lat: 36.2235, + lng: -115.9974, + pop: 42471, + }, + { + lat: 43.7333, + lng: 44.7, + pop: 42155, + }, + { + lat: 42.15, + lng: 41.6667, + pop: 41465, + }, + { + lat: 45.4686, + lng: 39.4519, + pop: 42019, + }, + { + lat: 36.3481, + lng: 37.5308, + pop: 41786, + }, + { + lat: 36.75, + lng: 36.2167, + pop: 41409, + }, + { + lat: 43.3167, + lng: 44.9167, + pop: 41469, + }, + { + lat: 36.9533, + lng: 36.2033, + pop: 41368, + }, + { + lat: 32.8333, + lng: 35.0667, + pop: 39416, + }, + { + lat: 45.2667, + lng: 37.3667, + pop: 41133, + }, + { + lat: 37.2489, + lng: 37.8658, + pop: 41142, + }, + { + lat: 41.19, + lng: 40.9831, + pop: 41084, + }, + { + lat: 35.1205, + lng: -114.5461, + pop: 41064, + }, + { + lat: 32.7997, + lng: 51.6956, + pop: 40945, + }, + { + lat: 33.775, + lng: 35.9, + pop: 40000, + }, + { + lat: 26.9581, + lng: 56.2719, + pop: 40678, + }, + { + lat: 28.6489, + lng: 51.3794, + pop: 40722, + }, + { + lat: 25.3333, + lng: 56.35, + pop: 39515, + }, + { + lat: 31.5081, + lng: 50.8319, + pop: 40528, + }, + { + lat: 39.9458, + lng: 41.1053, + pop: 40350, + }, + { + lat: 41.0172, + lng: 34.0383, + pop: 40245, + }, + { + lat: 35.8636, + lng: 36.8006, + pop: 39901, + }, + { + lat: 40.9667, + lng: 35.6667, + pop: 40194, + }, + { + lat: 44.2503, + lng: 28.2614, + pop: 39780, + }, + { + lat: 43.6825, + lng: 43.5339, + pop: 38192, + }, + { + lat: 32.4611, + lng: 35.3, + pop: 39004, + }, + { + lat: 33.8067, + lng: 36.7403, + pop: 39903, + }, + { + lat: 26.5578, + lng: 54.0194, + pop: 39853, + }, + { + lat: 44.4667, + lng: 39.7333, + pop: 39762, + }, + { + lat: 40.9667, + lng: 39.9, + pop: 39624, + }, + { + lat: 31.9, + lng: 35.2, + pop: 38998, + }, + { + lat: 40.6603, + lng: 29.3236, + pop: 39110, + }, + { + lat: 44.8667, + lng: 38.15, + pop: 39374, + }, + { + lat: 37.1667, + lng: 42.1333, + pop: 39000, + }, + { + lat: 39.9139, + lng: 28.1603, + pop: 39058, + }, + { + lat: 41.0833, + lng: 31.1167, + pop: 38846, + }, + { + lat: 41.5097, + lng: 34.2142, + pop: 38849, + }, + { + lat: 45.7086, + lng: 34.3933, + pop: 38714, + }, + { + lat: 32.5553, + lng: 51.5097, + pop: 37740, + }, + { + lat: 45.3667, + lng: 41.7167, + pop: 38100, + }, + { + lat: 34.6894, + lng: 40.8308, + pop: 37935, + }, + { + lat: 43.2944, + lng: 45.8839, + pop: 37373, + }, + { + lat: 36, + lng: 36.6667, + pop: 37490, + }, + { + lat: 34.2283, + lng: 37.2406, + pop: 37820, + }, + { + lat: 25.0742, + lng: 56.3553, + pop: 37545, + }, + { + lat: 44.6333, + lng: 39.1333, + pop: 37475, + }, + { + lat: 43.5, + lng: 44.75, + pop: 37442, + }, + { + lat: 37.6942, + lng: 37.8614, + pop: 37323, + }, + { + lat: 35.0653, + lng: 36.3422, + pop: 37109, + }, + { + lat: 43.2167, + lng: 46.8667, + pop: 37171, + }, + { + lat: 34.5814, + lng: -117.4397, + pop: 37229, + }, + { + lat: 43.1833, + lng: 44.55, + pop: 37029, + }, + { + lat: 43.8172, + lng: 28.5828, + pop: 36364, + }, + { + lat: 36.5083, + lng: 36.8692, + pop: 36562, + }, + { + lat: 41.1764, + lng: 29.6128, + pop: 36516, + }, + { + lat: 33.0167, + lng: 35.2708, + pop: 36000, + }, + { + lat: 29.1267, + lng: 54.0422, + pop: 36410, + }, + { + lat: 32.6368, + lng: 35.99, + pop: 34948, + }, + { + lat: 46.8, + lng: 33.4667, + pop: 35900, + }, + { + lat: 36.1569, + lng: 37.7078, + pop: 35409, + }, + { + lat: 34.6683, + lng: 36.2597, + pop: 35445, + }, + { + lat: 32.9658, + lng: 35.4983, + pop: 35700, + }, + { + lat: 32.4583, + lng: 35.8583, + pop: 35085, + }, + { + lat: 45.35, + lng: 42.85, + pop: 35745, + }, + { + lat: 41.4833, + lng: 31.8333, + pop: 35323, + }, + { + lat: 39.6381, + lng: 34.4672, + pop: 35561, + }, + { + lat: 35.9025, + lng: 36.0606, + pop: 35460, + }, + { + lat: 33.5333, + lng: 36.2167, + pop: 33571, + }, + { + lat: 35.2609, + lng: 36.3822, + pop: 35000, + }, + { + lat: 22.9339, + lng: 57.775, + pop: 35173, + }, + { + lat: 32.8536, + lng: 35.1978, + pop: 34000, + }, + { + lat: 40.95, + lng: 39.9333, + pop: 34831, + }, + { + lat: 44.3211, + lng: 28.6133, + pop: 34398, + }, + { + lat: 40.95, + lng: 38.7333, + pop: 34592, + }, + { + lat: 44.4167, + lng: 43.9167, + pop: 34690, + }, + { + lat: 29.2331, + lng: 56.6022, + pop: 34517, + }, + { + lat: 27.6672, + lng: 54.1411, + pop: 34469, + }, + { + lat: 35.4293, + lng: -119.0306, + pop: 34350, + }, + { + lat: 45.3594, + lng: 40.7072, + pop: 34215, + }, + { + lat: 40.6422, + lng: 29.1203, + pop: 34076, + }, + { + lat: 37.6834, + lng: -113.0956, + pop: 34246, + }, + { + lat: 31.5775, + lng: 54.4369, + pop: 34237, + }, + { + lat: 36.4889, + lng: 36.1944, + pop: 33540, + }, + { + lat: 41.0656, + lng: 37.7714, + pop: 33253, + }, + { + lat: 37.5764, + lng: 36.3506, + pop: 33193, + }, + { + lat: 32.7356, + lng: 36.0669, + pop: 32236, + }, + { + lat: 37.4247, + lng: 37.6928, + pop: 32846, + }, + { + lat: 34.8167, + lng: 36.1167, + pop: 32213, + }, + { + lat: 36.965, + lng: 37.5092, + pop: 32653, + }, + { + lat: 29.7742, + lng: 52.7236, + pop: 32261, + }, + { + lat: 26.2667, + lng: 50.15, + pop: 32067, + }, + { + lat: 46.05, + lng: 38.1667, + pop: 32180, + }, + { + lat: 35.217, + lng: -114.0105, + pop: 32204, + }, + { + lat: 32.9667, + lng: 36.0667, + pop: 31683, + }, + { + lat: 46.63, + lng: 31.1, + pop: 32100, + }, + { + lat: 41.0056, + lng: 38.8167, + pop: 32008, + }, + { + lat: 32.8667, + lng: 35.3, + pop: 31100, + }, + { + lat: 36.5866, + lng: 37.0463, + pop: 31534, + }, + { + lat: 32.2714, + lng: 50.9775, + pop: 31739, + }, + { + lat: 29.205, + lng: 52.69, + pop: 31711, + }, + { + lat: 39.8642, + lng: 36.5983, + pop: 31748, + }, + { + lat: 33.9667, + lng: 36.0167, + pop: 30000, + }, + { + lat: 33, + lng: 36.1167, + pop: 31258, + }, + { + lat: 23.5889, + lng: 58.4083, + pop: 31409, + }, + { + lat: 27.8236, + lng: 52.3303, + pop: 31436, + }, + { + lat: 39.8503, + lng: 33.4536, + pop: 31308, + }, + { + lat: 36.4731, + lng: 41.6161, + pop: 31161, + }, + { + lat: 47.2514, + lng: 35.7058, + pop: 31016, + }, + { + lat: 43.55, + lng: 43.85, + pop: 30832, + }, + { + lat: 32.5767, + lng: 51.455, + pop: 30002, + }, + { + lat: 32.2553, + lng: 50.5711, + pop: 30504, + }, + { + lat: 45.1, + lng: 43.45, + pop: 30530, + }, + { + lat: 33.7436, + lng: 36.7012, + pop: 30450, + }, + { + lat: 33.8667, + lng: 35.5667, + pop: 30000, + }, + { + lat: 44.415, + lng: 27.8236, + pop: 30217, + }, + { + lat: 42.2257, + lng: 43.9701, + pop: 30432, + }, + { + lat: 26.5581, + lng: 54.8806, + pop: 30435, + }, + { + lat: 44.0872, + lng: 41.9733, + pop: 30369, + }, + { + lat: 35.6139, + lng: 36.5611, + pop: 30000, + }, + { + lat: 32.5025, + lng: 35.6922, + pop: 29590, + }, + { + lat: 40.75, + lng: 36.3167, + pop: 30123, + }, + { + lat: 27.76, + lng: 54.0072, + pop: 29987, + }, + { + lat: 44.6672, + lng: 34.3978, + pop: 29668, + }, + { + lat: 27.3423, + lng: 53.1768, + pop: 29380, + }, + { + lat: 36.8503, + lng: 40.0706, + pop: 29347, + }, + { + lat: 35.2897, + lng: 36.7433, + pop: 29100, + }, + { + lat: 23.6522, + lng: 53.6536, + pop: 29095, + }, + { + lat: 30.2364, + lng: 49.7119, + pop: 29015, + }, + { + lat: 36.1242, + lng: -115.3324, + pop: 28861, + }, + { + lat: 37.486, + lng: 37.297, + pop: 28582, + }, + { + lat: 47.2667, + lng: 29.1667, + pop: 28500, + }, + { + lat: 40.3748, + lng: 36.9031, + pop: 28413, + }, + { + lat: 32.6417, + lng: 35.9417, + pop: 27902, + }, + { + lat: 33.0381, + lng: 40.2844, + pop: 28400, + }, + { + lat: 40.6975, + lng: 29.5114, + pop: 28232, + }, + { + lat: 41.0475, + lng: 39.2798, + pop: 28209, + }, + { + lat: 23.3833, + lng: 57.8167, + pop: 28088, + }, + { + lat: 40.1836, + lng: 31.3506, + pop: 28091, + }, + { + lat: 41.1158, + lng: 45.4853, + pop: 28000, + }, + { + lat: 35.6308, + lng: -117.6622, + pop: 27989, + }, + { + lat: 37.1303, + lng: -113.4878, + pop: 27689, + }, + { + lat: 41.8111, + lng: 41.7753, + pop: 27546, + }, + { + lat: 31.8889, + lng: 35.1675, + pop: 26604, + }, + { + lat: 31.1267, + lng: 53.2592, + pop: 27524, + }, + { + lat: 40.9142, + lng: 40.1125, + pop: 27428, + }, + { + lat: 35.5938, + lng: -119.3671, + pop: 27505, + }, + { + lat: 44.7167, + lng: 43, + pop: 27471, + }, + { + lat: 36.4733, + lng: 37.0972, + pop: 27086, + }, + { + lat: 44.7528, + lng: 33.8608, + pop: 27351, + }, + { + lat: 37.5375, + lng: 40.8892, + pop: 27304, + }, + { + lat: 43.65, + lng: 44.0667, + pop: 27074, + }, + { + lat: 29.8519, + lng: 51.5869, + pop: 26918, + }, + { + lat: 30.6131, + lng: 53.1953, + pop: 26933, + }, + { + lat: 33.7389, + lng: 36.6, + pop: 26671, + }, + { + lat: 36.1, + lng: 32.9667, + pop: 26840, + }, + { + lat: 40.8761, + lng: 37.7406, + pop: 26737, + }, + { + lat: 45.5, + lng: 41.2333, + pop: 26761, + }, + { + lat: 46.6742, + lng: 28.0597, + pop: 26266, + }, + { + lat: 33.725, + lng: 36.0972, + pop: 26285, + }, + { + lat: 40.8167, + lng: 39.6167, + pop: 26626, + }, + { + lat: 33.0711, + lng: 36.1842, + pop: 26268, + }, + { + lat: 32.6714, + lng: 35.2406, + pop: 25600, + }, + { + lat: 26.5833, + lng: 49.9833, + pop: 25500, + }, + { + lat: 36.699, + lng: -119.5575, + pop: 26424, + }, + { + lat: 41.4944, + lng: 36.0789, + pop: 26337, + }, + { + lat: 43.2044, + lng: 46.0911, + pop: 25672, + }, + { + lat: 37.1886, + lng: 32.2456, + pop: 26287, + }, + { + lat: 31.4142, + lng: 51.5694, + pop: 26260, + }, + { + lat: 41.9975, + lng: 43.5986, + pop: 26135, + }, + { + lat: 43.95, + lng: 43.6333, + pop: 26106, + }, + { + lat: 39.645, + lng: 41.5083, + pop: 25969, + }, + { + lat: 40.9697, + lng: 27.9553, + pop: 25873, + }, + { + lat: 36.2472, + lng: 29.9828, + pop: 25893, + }, + { + lat: 41.61, + lng: 35.595, + pop: 25854, + }, + { + lat: 45.9675, + lng: 33.8003, + pop: 25769, + }, + { + lat: 36.1167, + lng: 36.1333, + pop: 25118, + }, + { + lat: 30.0542, + lng: 50.1639, + pop: 25730, + }, + { + lat: 32.7711, + lng: 35.0394, + pop: 23700, + }, + { + lat: 32.61, + lng: 35.6081, + pop: 25000, + }, + { + lat: 41.0547, + lng: 30.8503, + pop: 25497, + }, + { + lat: 41.3636, + lng: 41.6792, + pop: 25500, + }, + { + lat: 44.1394, + lng: 43.0169, + pop: 24919, + }, + { + lat: 42.1625, + lng: 42.3417, + pop: 25318, + }, + { + lat: 40.1658, + lng: 38.0942, + pop: 25404, + }, + { + lat: 33.6967, + lng: 36.3739, + pop: 25194, + }, + { + lat: 44.6706, + lng: 41.838, + pop: 25279, + }, + { + lat: 40.7928, + lng: 42.6086, + pop: 25187, + }, + { + lat: 36.5988, + lng: -119.4471, + pop: 25168, + }, + { + lat: 29.9275, + lng: 56.5722, + pop: 25152, + }, + { + lat: 34.8661, + lng: -117.0471, + pop: 25123, + }, + { + lat: 45.1336, + lng: 33.5772, + pop: 24282, + }, + { + lat: 30.8947, + lng: 49.4092, + pop: 25009, + }, + { + lat: 34.2919, + lng: 35.9546, + pop: 25000, + }, + { + lat: 32.4097, + lng: 35.2808, + pop: 24439, + }, + { + lat: 40.6303, + lng: 48.6414, + pop: 24681, + }, + { + lat: 46.6333, + lng: 32.5833, + pop: 24639, + }, + { + lat: 36.5625, + lng: 35.3803, + pop: 24559, + }, + { + lat: 46.85, + lng: 40.3167, + pop: 24561, + }, + { + lat: 40.0494, + lng: 43.6608, + pop: 24560, + }, + { + lat: 36.5715, + lng: -119.6143, + pop: 24545, + }, + { + lat: 40.555, + lng: 44.9536, + pop: 23200, + }, + { + lat: 44.7506, + lng: 44.9797, + pop: 24472, + }, + { + lat: 32.8022, + lng: 51.6211, + pop: 24433, + }, + { + lat: 35.2661, + lng: 36.7114, + pop: 24105, + }, + { + lat: 36.5453, + lng: -119.3987, + pop: 24383, + }, + { + lat: 32.2514, + lng: 48.8161, + pop: 24216, + }, + { + lat: 36.1333, + lng: 36.45, + pop: 23700, + }, + { + lat: 27.8417, + lng: 51.9394, + pop: 24083, + }, + { + lat: 32.5542, + lng: 51.525, + pop: 23203, + }, + { + lat: 34.2511, + lng: 36.0111, + pop: 24000, + }, + { + lat: 41.0096, + lng: 44.3841, + pop: 23782, + }, + { + lat: 41.3903, + lng: 41.4194, + pop: 23846, + }, + { + lat: 33.7075, + lng: 35.9039, + pop: 23000, + }, + { + lat: 41.95, + lng: 34.5833, + pop: 23720, + }, + { + lat: 44.1167, + lng: 42.9833, + pop: 22891, + }, + { + lat: 34.6931, + lng: 32.9992, + pop: 22369, + }, + { + lat: 39.9211, + lng: 40.6947, + pop: 23589, + }, + { + lat: 45.7167, + lng: 42.9, + pop: 23579, + }, + { + lat: 40.8006, + lng: 32.1986, + pop: 23547, + }, + { + lat: 32.7667, + lng: 34.9667, + pop: 22200, + }, + { + lat: 23.55, + lng: 56.25, + pop: 23466, + }, + { + lat: 40.7931, + lng: 37.0164, + pop: 23369, + }, + { + lat: 43.1939, + lng: 45.2833, + pop: 23282, + }, + { + lat: 33.4333, + lng: 36.0833, + pop: 22831, + }, + { + lat: 41.8922, + lng: 33.0044, + pop: 23329, + }, + { + lat: 31.4086, + lng: 48.7942, + pop: 23211, + }, + { + lat: 33.2075, + lng: 35.5697, + pop: 23076, + }, + { + lat: 37.5467, + lng: 34.4844, + pop: 23252, + }, + { + lat: 40.4633, + lng: 42.7858, + pop: 23231, + }, + { + lat: 28.9306, + lng: 51.0689, + pop: 23178, + }, + { + lat: 32.0167, + lng: 35.7667, + pop: 21908, + }, + { + lat: 34.3914, + lng: 36.3958, + pop: 22250, + }, + { + lat: 40.6867, + lng: 37.3992, + pop: 22783, + }, + { + lat: 43.7731, + lng: 41.9169, + pop: 21067, + }, + { + lat: 32.6267, + lng: 51.4392, + pop: 22693, + }, + { + lat: 29.0664, + lng: 58.4047, + pop: 22761, + }, + { + lat: 41.0494, + lng: 39.2353, + pop: 22630, + }, + { + lat: 31.2656, + lng: 56.8056, + pop: 22729, + }, + { + lat: 39.6104, + lng: -119.777, + pop: 22622, + }, + { + lat: 36.0841, + lng: -119.5613, + pop: 22616, + }, + { + lat: 44.4256, + lng: 39.5319, + pop: 22468, + }, + { + lat: 40.0633, + lng: 44.4408, + pop: 21376, + }, + { + lat: 35.95, + lng: 36.7, + pop: 21848, + }, + { + lat: 41.5861, + lng: 32.6406, + pop: 22333, + }, + { + lat: 36.2, + lng: 36.5167, + pop: 21934, + }, + { + lat: 39.5627, + lng: -119.1906, + pop: 22343, + }, + { + lat: 37.2, + lng: 36.5833, + pop: 22242, + }, + { + lat: 40.3756, + lng: 43.4125, + pop: 22282, + }, + { + lat: 31.485, + lng: 48.2686, + pop: 22057, + }, + { + lat: 37.3192, + lng: 37.5686, + pop: 22192, + }, + { + lat: 40.8667, + lng: 35.2167, + pop: 22179, + }, + { + lat: 33.6, + lng: 36.515, + pop: 20559, + }, + { + lat: 32.6594, + lng: 35.11, + pop: 21383, + }, + { + lat: 41.0036, + lng: 36.6319, + pop: 21847, + }, + { + lat: 39.895, + lng: 37.7531, + pop: 21753, + }, + { + lat: 41.3081, + lng: 32.1417, + pop: 21655, + }, + { + lat: 41.0736, + lng: 36.0403, + pop: 21692, + }, + { + lat: 30.4775, + lng: 54.2128, + pop: 21690, + }, + { + lat: 41.2, + lng: 32.3292, + pop: 21625, + }, + { + lat: 42.2689, + lng: 42.0678, + pop: 21596, + }, + { + lat: 36.3756, + lng: 36.9942, + pop: 21039, + }, + { + lat: 33.2539, + lng: 35.2717, + pop: 20000, + }, + { + lat: 32.2269, + lng: 50.7931, + pop: 21352, + }, + { + lat: 39.8728, + lng: 44.5192, + pop: 21311, + }, + { + lat: 36.5667, + lng: 36.1333, + pop: 20459, + }, + { + lat: 40.35, + lng: 30.0167, + pop: 20976, + }, + { + lat: 34.3722, + lng: 41.9875, + pop: 21000, + }, + { + lat: 31.8711, + lng: 35.4442, + pop: 20300, + }, + { + lat: 39.8303, + lng: 44.7025, + pop: 20800, + }, + { + lat: 42.1083, + lng: 43.0417, + pop: 20814, + }, + { + lat: 43.65, + lng: 43.55, + pop: 20718, + }, + { + lat: 28.6689, + lng: 59.0733, + pop: 20720, + }, + { + lat: 37.4278, + lng: 34.8711, + pop: 20683, + }, + { + lat: 41.3333, + lng: 41.3, + pop: 20565, + }, + { + lat: 34.8658, + lng: -118.2155, + pop: 20574, + }, + { + lat: 43.4, + lng: 42.9167, + pop: 20513, + }, + { + lat: 39.6333, + lng: 43.3778, + pop: 20450, + }, + { + lat: 40.7333, + lng: 38.4333, + pop: 20405, + }, + { + lat: 40.9, + lng: 31.05, + pop: 20266, + }, + { + lat: 32.7781, + lng: 51.6461, + pop: 20301, + }, + { + lat: 43.2419, + lng: 46, + pop: 20013, + }, + { + lat: 28.8714, + lng: 52.0917, + pop: 20320, + }, + { + lat: 41.4969, + lng: 44.8108, + pop: 20211, + }, + { + lat: 32.555, + lng: 51.5731, + pop: 19406, + }, + { + lat: 37.0669, + lng: 36.1464, + pop: 20127, + }, + { + lat: 34.2028, + lng: 35.6544, + pop: 20000, + }, + { + lat: 40.1894, + lng: 39.1267, + pop: 20084, + }, + { + lat: 40.6172, + lng: 43.9758, + pop: 19543, + }, + { + lat: 46.7333, + lng: 29.7, + pop: 20000, + }, + { + lat: 43.2242, + lng: 46.1942, + pop: 19727, + }, + { + lat: 43.0333, + lng: 44.2333, + pop: 20043, + }, + { + lat: 36.8032, + lng: -114.133, + pop: 20019, + }, + { + lat: 35.4794, + lng: -119.2013, + pop: 19897, + }, + { + lat: 30.895, + lng: 50.0931, + pop: 19857, + }, + { + lat: 32.5167, + lng: 36.4833, + pop: 19683, + }, + { + lat: 40.0731, + lng: 35.4947, + pop: 19786, + }, + { + lat: 41.4167, + lng: 35.05, + pop: 19650, + }, + { + lat: 34.4686, + lng: 41.9167, + pop: 19629, + }, + { + lat: 35.1944, + lng: -118.8306, + pop: 19568, + }, + { + lat: 43.4833, + lng: 44.1333, + pop: 19494, + }, + { + lat: 34.9478, + lng: 33.5881, + pop: 19199, + }, + { + lat: 40.8106, + lng: 41.5269, + pop: 19510, + }, + { + lat: 37.1487, + lng: -113.3517, + pop: 19501, + }, + { + lat: 43.1833, + lng: 44.3167, + pop: 19412, + }, + { + lat: 31.4619, + lng: 48.0739, + pop: 19481, + }, + { + lat: 36.7678, + lng: 35.7922, + pop: 18587, + }, + { + lat: 32.8454, + lng: 36.2499, + pop: 19158, + }, + { + lat: 27.8914, + lng: 53.4344, + pop: 19347, + }, + { + lat: 46.1667, + lng: 34.8, + pop: 19253, + }, + { + lat: 40.0186, + lng: 30.1814, + pop: 19244, + }, + { + lat: 27.5014, + lng: 52.5858, + pop: 18837, + }, + { + lat: 26.5992, + lng: 54.9361, + pop: 19213, + }, + { + lat: 32.0006, + lng: 54.2075, + pop: 19123, + }, + { + lat: 46.7, + lng: 41.7333, + pop: 19032, + }, + { + lat: 29.3606, + lng: 51.0683, + pop: 18913, + }, + { + lat: 28.9864, + lng: 51.0375, + pop: 18702, + }, + { + lat: 31.9436, + lng: 34.8392, + pop: 18401, + }, + { + lat: 35.3886, + lng: -119.2058, + pop: 18875, + }, + { + lat: 41.2422, + lng: 33.3283, + pop: 18863, + }, + { + lat: 31.9833, + lng: 35.7667, + pop: 17754, + }, + { + lat: 32.5, + lng: 34.9167, + pop: 17759, + }, + { + lat: 45.45, + lng: 29.2667, + pop: 18745, + }, + { + lat: 43.1642, + lng: 45.6228, + pop: 18534, + }, + { + lat: 40.8139, + lng: 32.8908, + pop: 18694, + }, + { + lat: 39.6731, + lng: 33.6136, + pop: 18139, + }, + { + lat: 34.4398, + lng: -117.5248, + pop: 18599, + }, + { + lat: 36.65, + lng: 36.2167, + pop: 17925, + }, + { + lat: 31.9408, + lng: 54.2736, + pop: 18309, + }, + { + lat: 31.9414, + lng: 54.2828, + pop: 18309, + }, + { + lat: 29.5, + lng: 53.3167, + pop: 18477, + }, + { + lat: 47.3907, + lng: 35.0027, + pop: 18468, + }, + { + lat: 31.7492, + lng: 54.2103, + pop: 18464, + }, + { + lat: 47.0333, + lng: 28.95, + pop: 17210, + }, + { + lat: 43.2081, + lng: 44.8186, + pop: 17734, + }, + { + lat: 32.5, + lng: 35.5, + pop: 18200, + }, + { + lat: 31.5419, + lng: 60.0364, + pop: 18304, + }, + { + lat: 40.5619, + lng: 42.3464, + pop: 18281, + }, + { + lat: 29.2736, + lng: 53.2203, + pop: 18187, + }, + { + lat: 33.6797, + lng: 35.5583, + pop: 17000, + }, + { + lat: 28.4758, + lng: 57.8481, + pop: 18185, + }, + { + lat: 43.1667, + lng: 46, + pop: 17970, + }, + { + lat: 33.6422, + lng: 36.2978, + pop: 17521, + }, + { + lat: 32.3836, + lng: 51.5147, + pop: 17966, + }, + { + lat: 35.25, + lng: 36.5833, + pop: 17578, + }, + { + lat: 36.1231, + lng: 37.3369, + pop: 17767, + }, + { + lat: 36.2583, + lng: 41.9431, + pop: 18000, + }, + { + lat: 34.4818, + lng: -118.6316, + pop: 18017, + }, + { + lat: 35.1, + lng: 33.3667, + pop: 16774, + }, + { + lat: 45.4575, + lng: 28.2711, + pop: 17736, + }, + { + lat: 26.2258, + lng: 60.2142, + pop: 17732, + }, + { + lat: 41.0314, + lng: 36.2683, + pop: 17628, + }, + { + lat: 34.9203, + lng: 40.5594, + pop: 17537, + }, + { + lat: 29.9125, + lng: 53.3086, + pop: 17642, + }, + { + lat: 35.3697, + lng: 36.38, + pop: 17313, + }, + { + lat: 41.2433, + lng: 42.3639, + pop: 17606, + }, + { + lat: 32.3781, + lng: 51.3181, + pop: 17335, + }, + { + lat: 44.1778, + lng: 43.5, + pop: 17451, + }, + { + lat: 46.1167, + lng: 32.9167, + pop: 17344, + }, + { + lat: 45.6855, + lng: 28.6134, + pop: 17400, + }, + { + lat: 35.3736, + lng: 36.6017, + pop: 17052, + }, + { + lat: 40.8483, + lng: 43.3317, + pop: 17373, + }, + { + lat: 30.0775, + lng: 53.1331, + pop: 17131, + }, + { + lat: 46.9753, + lng: 28.8194, + pop: 15934, + }, + { + lat: 40.07, + lng: 47.2047, + pop: 16998, + }, + { + lat: 40.2981, + lng: 41.6325, + pop: 17054, + }, + { + lat: 40.9833, + lng: 39.8, + pop: 15503, + }, + { + lat: 44.3381, + lng: 28.0336, + pop: 17022, + }, + { + lat: 41.1333, + lng: 41.0167, + pop: 16902, + }, + { + lat: 31.94, + lng: 51.6478, + pop: 16899, + }, + { + lat: 32.8542, + lng: 36.6292, + pop: 16745, + }, + { + lat: 40.7408, + lng: 44.8631, + pop: 16600, + }, + { + lat: 25.6439, + lng: 57.7744, + pop: 16860, + }, + { + lat: 46.0833, + lng: 40.8583, + pop: 16838, + }, + { + lat: 36.4575, + lng: 41.7061, + pop: 16798, + }, + { + lat: 44.8514, + lng: 34.9725, + pop: 16784, + }, + { + lat: 40.9, + lng: 38.4167, + pop: 16758, + }, + { + lat: 46.05, + lng: 28.8333, + pop: 16605, + }, + { + lat: 32.4725, + lng: 35.7928, + pop: 16000, + }, + { + lat: 32.7333, + lng: 36.2, + pop: 16240, + }, + { + lat: 37.5465, + lng: 35.3987, + pop: 16653, + }, + { + lat: 35.3208, + lng: 36.6225, + pop: 16267, + }, + { + lat: 41.0833, + lng: 39.3833, + pop: 16335, + }, + { + lat: 43.9333, + lng: 42.5167, + pop: 16512, + }, + { + lat: 40.646, + lng: 34.261, + pop: 16525, + }, + { + lat: 45.0544, + lng: 34.6022, + pop: 16428, + }, + { + lat: 32.8353, + lng: 35.9714, + pop: 15985, + }, + { + lat: 36.2333, + lng: 36.2167, + pop: 15692, + }, + { + lat: 32.3936, + lng: 51.3408, + pop: 16086, + }, + { + lat: 40.7475, + lng: 40.2419, + pop: 16213, + }, + { + lat: 39.9756, + lng: 41.8711, + pop: 16178, + }, + { + lat: 43.6003, + lng: 46.7789, + pop: 16165, + }, + { + lat: 34.8868, + lng: 38.8721, + pop: 16173, + }, + { + lat: 39.6568, + lng: -119.6694, + pop: 16131, + }, + { + lat: 40.4425, + lng: 47.6767, + pop: 16018, + }, + { + lat: 33.2692, + lng: 35.7706, + pop: 15973, + }, + { + lat: 41.9486, + lng: 34.3367, + pop: 16004, + }, + { + lat: 46.7006, + lng: 32.5478, + pop: 15984, + }, + { + lat: 27.7975, + lng: 53.685, + pop: 16000, + }, + { + lat: 46.1167, + lng: 48.0833, + pop: 15984, + }, + { + lat: 33.1047, + lng: 50.9589, + pop: 15828, + }, + { + lat: 37.0164, + lng: 41.9544, + pop: 15759, + }, + { + lat: 35.0004, + lng: -114.5748, + pop: 15872, + }, + { + lat: 43.4333, + lng: 28.3333, + pop: 15834, + }, + { + lat: 36.0561, + lng: 40.7303, + pop: 15806, + }, + { + lat: 31.4469, + lng: 49.5294, + pop: 15802, + }, + { + lat: 32.7025, + lng: 51.1536, + pop: 15673, + }, + { + lat: 35.9632, + lng: 38.0356, + pop: 15477, + }, + { + lat: 33.0528, + lng: 51.0825, + pop: 15550, + }, + { + lat: 33.4242, + lng: 36.2244, + pop: 13993, + }, + { + lat: 40.52, + lng: 35.2953, + pop: 15655, + }, + { + lat: 27.4064, + lng: 57.5014, + pop: 15634, + }, + { + lat: 34.7087, + lng: 33.0504, + pop: 14578, + }, + { + lat: 40.9667, + lng: 31.45, + pop: 15573, + }, + { + lat: 32.8019, + lng: 51.6636, + pop: 15524, + }, + { + lat: 30.8733, + lng: 55.2706, + pop: 15532, + }, + { + lat: 40.0172, + lng: 32.3483, + pop: 15540, + }, + { + lat: 36.3667, + lng: 36.2, + pop: 14751, + }, + { + lat: 31.4825, + lng: 48.8747, + pop: 15312, + }, + { + lat: 47.2848, + lng: 39.4823, + pop: 15334, + }, + { + lat: 40.879, + lng: 37.4532, + pop: 14954, + }, + { + lat: 40.8372, + lng: 44.2675, + pop: 15000, + }, + { + lat: 35.0333, + lng: 33.9833, + pop: 14963, + }, + { + lat: 30.7461, + lng: 50.7461, + pop: 15218, + }, + { + lat: 28.8833, + lng: 51.275, + pop: 15198, + }, + { + lat: 39.8033, + lng: 29.6178, + pop: 15181, + }, + { + lat: 46.5167, + lng: 32.5167, + pop: 15163, + }, + { + lat: 41.45, + lng: 45.1, + pop: 15100, + }, + { + lat: 34.2597, + lng: 36.4236, + pop: 15000, + }, + { + lat: 33.6333, + lng: 35.7833, + pop: 14728, + }, + { + lat: 32.4917, + lng: 36.7111, + pop: 15000, + }, + { + lat: 39.2592, + lng: -119.5653, + pop: 15036, + }, + { + lat: 32.5379, + lng: 34.9122, + pop: 13962, + }, + { + lat: 37.544, + lng: 41.72, + pop: 14976, + }, + { + lat: 35.9033, + lng: 36.7258, + pop: 14530, + }, + { + lat: 40.8736, + lng: 30.9508, + pop: 14895, + }, + { + lat: 36.6975, + lng: 38.9567, + pop: 14825, + }, + { + lat: 35.1578, + lng: -117.8721, + pop: 14914, + }, + { + lat: 35.8407, + lng: -114.9257, + pop: 14868, + }, + { + lat: 41.7494, + lng: 32.3864, + pop: 14776, + }, + { + lat: 43.17, + lng: 45.3711, + pop: 14720, + }, + { + lat: 42.4167, + lng: 27.7, + pop: 14789, + }, + { + lat: 45.85, + lng: 41.5167, + pop: 14761, + }, + { + lat: 40.9333, + lng: 38.1333, + pop: 14659, + }, + { + lat: 45.3694, + lng: 44.2281, + pop: 14722, + }, + { + lat: 46.6186, + lng: 31.5392, + pop: 14705, + }, + { + lat: 30.2625, + lng: 51.9833, + pop: 14633, + }, + { + lat: 33.6497, + lng: 35.4433, + pop: 12888, + }, + { + lat: 40.8856, + lng: 39.2922, + pop: 14592, + }, + { + lat: 26.9636, + lng: 56.0622, + pop: 14525, + }, + { + lat: 35.3753, + lng: 36.6872, + pop: 14307, + }, + { + lat: 33.7421, + lng: 36.6435, + pop: 14228, + }, + { + lat: 37.4025, + lng: 40.9561, + pop: 14233, + }, + { + lat: 43.2162, + lng: 46.0381, + pop: 14111, + }, + { + lat: 40.262, + lng: 36.313, + pop: 14335, + }, + { + lat: 39.71, + lng: 39.7017, + pop: 14390, + }, + { + lat: 46.1269, + lng: 30.385, + pop: 14321, + }, + { + lat: 37.4431, + lng: 36.0322, + pop: 14308, + }, + { + lat: 32.4467, + lng: 35.1703, + pop: 13640, + }, + { + lat: 35.2659, + lng: -118.9159, + pop: 14269, + }, + { + lat: 39.6936, + lng: 35.5111, + pop: 14198, + }, + { + lat: 34.7181, + lng: 33.0856, + pop: 13421, + }, + { + lat: 43.2036, + lng: 46.1322, + pop: 13824, + }, + { + lat: 41.05, + lng: 39.1333, + pop: 13955, + }, + { + lat: 42.65, + lng: 27.7333, + pop: 14146, + }, + { + lat: 44.27, + lng: 28.56, + pop: 13968, + }, + { + lat: 40.9, + lng: 30.4833, + pop: 13973, + }, + { + lat: 35.6781, + lng: -119.2413, + pop: 14085, + }, + { + lat: 25.7347, + lng: 51.5475, + pop: 13511, + }, + { + lat: 43.1636, + lng: 45.4725, + pop: 13836, + }, + { + lat: 36.255, + lng: 42.0164, + pop: 14000, + }, + { + lat: 41.0333, + lng: 37.1, + pop: 13922, + }, + { + lat: 27.4744, + lng: 52.6114, + pop: 13557, + }, + { + lat: 39.9106, + lng: 44.7278, + pop: 13600, + }, + { + lat: 46.6791, + lng: 32.7228, + pop: 12812, + }, + { + lat: 28.4061, + lng: 54.1881, + pop: 13809, + }, + { + lat: 36.3947, + lng: 36.6889, + pop: 13661, + }, + { + lat: 36.2828, + lng: 36.8519, + pop: 13525, + }, + { + lat: 40.5572, + lng: 39.2919, + pop: 13771, + }, + { + lat: 43.25, + lng: 46.1333, + pop: 13405, + }, + { + lat: 32.4167, + lng: 35.6833, + pop: 13056, + }, + { + lat: 47.0833, + lng: 39.5667, + pop: 13692, + }, + { + lat: 36.5833, + lng: 31.8833, + pop: 13563, + }, + { + lat: 34.8969, + lng: 36.1346, + pop: 13244, + }, + { + lat: 27.2856, + lng: 61.9964, + pop: 13580, + }, + { + lat: 32.8425, + lng: 36.34, + pop: 13315, + }, + { + lat: 32.9411, + lng: 50.1211, + pop: 13475, + }, + { + lat: 41.925, + lng: 44.4222, + pop: 13423, + }, + { + lat: 30.8939, + lng: 61.6803, + pop: 13357, + }, + { + lat: 33.93, + lng: 35.745, + pop: 12000, + }, + { + lat: 41.4647, + lng: 47.74, + pop: 13405, + }, + { + lat: 32.0078, + lng: 51.2156, + pop: 13317, + }, + { + lat: 35.41, + lng: 36.39, + pop: 12925, + }, + { + lat: 34.7833, + lng: 36.4333, + pop: 13020, + }, + { + lat: 35.1276, + lng: -118.4744, + pop: 13346, + }, + { + lat: 41.6667, + lng: 48.1333, + pop: 13232, + }, + { + lat: 36.5133, + lng: 41.9542, + pop: 13281, + }, + { + lat: 41.3228, + lng: 47.1133, + pop: 13260, + }, + { + lat: 32.7109, + lng: 36.0266, + pop: 12640, + }, + { + lat: 31.19, + lng: 50.4419, + pop: 13269, + }, + { + lat: 29.2911, + lng: 56.9131, + pop: 13263, + }, + { + lat: 40.9814, + lng: 47.8458, + pop: 13190, + }, + { + lat: 41.1333, + lng: 44.65, + pop: 13000, + }, + { + lat: 40.16, + lng: 47.1722, + pop: 13002, + }, + { + lat: 27.5236, + lng: 57.8811, + pop: 13169, + }, + { + lat: 46.3425, + lng: 30.5653, + pop: 13036, + }, + { + lat: 37.4711, + lng: 41.9139, + pop: 13091, + }, + { + lat: 37.4792, + lng: 40.4864, + pop: 13117, + }, + { + lat: 44.8832, + lng: 39.1902, + pop: 12745, + }, + { + lat: 45.3908, + lng: 47.3658, + pop: 13125, + }, + { + lat: 43.5167, + lng: 43.7, + pop: 12813, + }, + { + lat: 32.6428, + lng: 51.5, + pop: 11264, + }, + { + lat: 25.6208, + lng: 51.0819, + pop: 13085, + }, + { + lat: 37.0519, + lng: 31.7842, + pop: 13084, + }, + { + lat: 26.5758, + lng: 59.6397, + pop: 13070, + }, + { + lat: 32.5444, + lng: 50.7461, + pop: 12971, + }, + { + lat: 30.0547, + lng: 54.3717, + pop: 13032, + }, + { + lat: 45.9072, + lng: 43.3558, + pop: 12998, + }, + { + lat: 35.0611, + lng: 36.6972, + pop: 12194, + }, + { + lat: 34.5596, + lng: -117.9558, + pop: 12961, + }, + { + lat: 34.3436, + lng: 36.4756, + pop: 12000, + }, + { + lat: 32.4197, + lng: 52.6483, + pop: 12714, + }, + { + lat: 43.1878, + lng: 44.9036, + pop: 12734, + }, + { + lat: 42.2903, + lng: 43.2819, + pop: 12803, + }, + { + lat: 35.2645, + lng: -114.0091, + pop: 12858, + }, + { + lat: 34.7594, + lng: -112.412, + pop: 12854, + }, + { + lat: 43.2906, + lng: 45.3014, + pop: 12738, + }, + { + lat: 35.6342, + lng: 36.6322, + pop: 12276, + }, + { + lat: 45.1142, + lng: 34.0142, + pop: 12711, + }, + { + lat: 30.9817, + lng: 50.4233, + pop: 12772, + }, + { + lat: 43.4847, + lng: 44.5881, + pop: 12614, + }, + { + lat: 43.4333, + lng: 43.575, + pop: 10829, + }, + { + lat: 40.1806, + lng: 45.72, + pop: 12363, + }, + { + lat: 40.3053, + lng: 37.8306, + pop: 12637, + }, + { + lat: 36.6964, + lng: 32.6203, + pop: 12601, + }, + { + lat: 40.9667, + lng: 39.7333, + pop: 11077, + }, + { + lat: 44.1128, + lng: 28.5558, + pop: 12333, + }, + { + lat: 34.1542, + lng: 36.7442, + pop: 12508, + }, + { + lat: 44.165, + lng: 28.455, + pop: 12376, + }, + { + lat: 43.3458, + lng: 44.2028, + pop: 12501, + }, + { + lat: 43.2586, + lng: 45.5392, + pop: 12340, + }, + { + lat: 36.2082, + lng: -119.0897, + pop: 12551, + }, + { + lat: 32.3378, + lng: 51.1961, + pop: 12292, + }, + { + lat: 34.6097, + lng: -117.8339, + pop: 12497, + }, + { + lat: 34.2419, + lng: 35.9794, + pop: 12000, + }, + { + lat: 43.4269, + lng: 28.1617, + pop: 12429, + }, + { + lat: 39.9011, + lng: 38.7686, + pop: 12456, + }, + { + lat: 26.5583, + lng: 49.9503, + pop: 11460, + }, + { + lat: 43.2514, + lng: 45.9072, + pop: 12224, + }, + { + lat: 36.5244, + lng: -119.5602, + pop: 12413, + }, + { + lat: 32.3156, + lng: 50.6783, + pop: 12308, + }, + { + lat: 36.2936, + lng: 37.0444, + pop: 11918, + }, + { + lat: 40.14, + lng: 45.3064, + pop: 11987, + }, + { + lat: 43.3117, + lng: 45.1594, + pop: 12221, + }, + { + lat: 32.2854, + lng: 35.8113, + pop: 11586, + }, + { + lat: 43.1126, + lng: 45.7339, + pop: 12092, + }, + { + lat: 40.3167, + lng: 38.7667, + pop: 12250, + }, + { + lat: 42.665, + lng: 46.22, + pop: 12159, + }, + { + lat: 45.6833, + lng: 28.4028, + pop: 12185, + }, + { + lat: 27.1944, + lng: 60.4558, + pop: 12217, + }, + { + lat: 46.5331, + lng: 48.3456, + pop: 12214, + }, + { + lat: 34.5683, + lng: 36.2764, + pop: 12000, + }, + { + lat: 46.8333, + lng: 33.4167, + pop: 12123, + }, + { + lat: 32.6653, + lng: 35.7333, + pop: 11706, + }, + { + lat: 43.6756, + lng: 43.455, + pop: 12001, + }, + { + lat: 30.0042, + lng: 53.0067, + pop: 12000, + }, + { + lat: 41.8833, + lng: 34.9167, + pop: 12049, + }, + { + lat: 47.0708, + lng: 32.7997, + pop: 12045, + }, + { + lat: 31.9383, + lng: 51.0533, + pop: 11980, + }, + { + lat: 33.1839, + lng: 36.2264, + pop: 11802, + }, + { + lat: 40.9131, + lng: 37.5169, + pop: 11851, + }, + { + lat: 36.9883, + lng: 32.4569, + pop: 11970, + }, + { + lat: 36.6667, + lng: 34.4167, + pop: 11923, + }, + { + lat: 47.2717, + lng: 35.2248, + pop: 11949, + }, + { + lat: 40.9422, + lng: 39.1942, + pop: 11934, + }, + { + lat: 31.9911, + lng: 54.2322, + pop: 11691, + }, + { + lat: 42.4333, + lng: 47.3167, + pop: 11862, + }, + { + lat: 47.1708, + lng: 37.6954, + pop: 10350, + }, + { + lat: 46.2667, + lng: 30.4333, + pop: 11741, + }, + { + lat: 47.1333, + lng: 28.8667, + pop: 10669, + }, + { + lat: 33.2092, + lng: 35.2992, + pop: 10000, + }, + { + lat: 33.5581, + lng: 36.2222, + pop: 10045, + }, + { + lat: 43.6119, + lng: 43.3269, + pop: 11717, + }, + { + lat: 36.8175, + lng: 38.0111, + pop: 11570, + }, + { + lat: 38.8957, + lng: -119.7492, + pop: 11761, + }, + { + lat: 46.7353, + lng: 36.3473, + pop: 11679, + }, + { + lat: 32.4689, + lng: 51.5578, + pop: 10851, + }, + { + lat: 43.5667, + lng: 43.5833, + pop: 11575, + }, + { + lat: 26.2483, + lng: 60.7525, + pop: 11605, + }, + { + lat: 43.4911, + lng: 43.5528, + pop: 9669, + }, + { + lat: 46.6977, + lng: 35.1554, + pop: 11481, + }, + { + lat: 36.8833, + lng: 36.2333, + pop: 11187, + }, + { + lat: 46.9122, + lng: 28.8839, + pop: 10175, + }, + { + lat: 42.3264, + lng: 42.6006, + pop: 11281, + }, + { + lat: 36.6389, + lng: 32.8925, + pop: 11332, + }, + { + lat: 29.9789, + lng: 48.5206, + pop: 11173, + }, + { + lat: 30.0617, + lng: 48.4508, + pop: 11173, + }, + { + lat: 39.7667, + lng: 30.95, + pop: 11242, + }, + { + lat: 44.45, + lng: 42.5, + pop: 11215, + }, + { + lat: 46.1083, + lng: 28.5972, + pop: 11123, + }, + { + lat: 36.1417, + lng: 33.3178, + pop: 11088, + }, + { + lat: 34.2425, + lng: 37.0589, + pop: 11064, + }, + { + lat: 46.6833, + lng: 47.85, + pop: 11079, + }, + { + lat: 36.7667, + lng: 31.3889, + pop: 11000, + }, + { + lat: 47.0667, + lng: 28.6833, + pop: 10380, + }, + { + lat: 34.0833, + lng: 36.7667, + pop: 10984, + }, + { + lat: 44.4361, + lng: 34.1106, + pop: 10310, + }, + { + lat: 46.8678, + lng: 28.7689, + pop: 10907, + }, + { + lat: 45.5019, + lng: 32.7025, + pop: 11039, + }, + { + lat: 43.5261, + lng: 43.5594, + pop: 11004, + }, + { + lat: 36.1389, + lng: 36.83, + pop: 10657, + }, + { + lat: 41.6281, + lng: 48.6828, + pop: 10894, + }, + { + lat: 40.7444, + lng: 43.625, + pop: 10985, + }, + { + lat: 36.2167, + lng: 36.1667, + pop: 10354, + }, + { + lat: 33.4861, + lng: 36.6011, + pop: 10548, + }, + { + lat: 33.7333, + lng: 35.45, + pop: 10000, + }, + { + lat: 32.6872, + lng: 34.9383, + pop: 10639, + }, + { + lat: 37.3406, + lng: 40.8258, + pop: 10846, + }, + { + lat: 41.3, + lng: 27.95, + pop: 10601, + }, + { + lat: 44.5528, + lng: 34.2875, + pop: 9117, + }, + { + lat: 36.6667, + lng: 34.3833, + pop: 10907, + }, + { + lat: 32.2983, + lng: 48.4289, + pop: 10858, + }, + { + lat: 40.1428, + lng: 44.1164, + pop: 9870, + }, + { + lat: 41.3833, + lng: 27.9333, + pop: 10072, + }, + { + lat: 41.7333, + lng: 45.3333, + pop: 10871, + }, + { + lat: 40.9368, + lng: 45.8258, + pop: 10797, + }, + { + lat: 34.3, + lng: 35.8, + pop: 10000, + }, + { + lat: 36.7167, + lng: 36.2333, + pop: 10574, + }, + { + lat: 40.3217, + lng: 44.4814, + pop: 10656, + }, + { + lat: 35.683, + lng: 36.533, + pop: 10353, + }, + { + lat: 36.2333, + lng: 36.8167, + pop: 10394, + }, + { + lat: 32.6872, + lng: 36.3508, + pop: 10510, + }, + { + lat: 45.4978, + lng: 34.295, + pop: 10766, + }, + { + lat: 35.0211, + lng: 33.42, + pop: 10466, + }, + { + lat: 41.4783, + lng: 46.6175, + pop: 10700, + }, + { + lat: 30.3611, + lng: 51.1572, + pop: 10764, + }, + { + lat: 36.8667, + lng: 36.2, + pop: 10482, + }, + { + lat: 44.6667, + lng: 45.65, + pop: 10641, + }, + { + lat: 33.85, + lng: 35.6667, + pop: 10000, + }, + { + lat: 32.6322, + lng: 36.3386, + pop: 10466, + }, + { + lat: 31.4103, + lng: 56.2825, + pop: 10761, + }, + { + lat: 45.0448, + lng: 42.1104, + pop: 10695, + }, + { + lat: 31.6325, + lng: 49.8897, + pop: 10698, + }, + { + lat: 44.7444, + lng: 44.2031, + pop: 10721, + }, + { + lat: 29.2417, + lng: 57.3253, + pop: 10670, + }, + { + lat: 37.4944, + lng: 30.9817, + pop: 10707, + }, + { + lat: 40.3833, + lng: 35.5167, + pop: 10703, + }, + { + lat: 44.6142, + lng: 33.6083, + pop: 10196, + }, + { + lat: 46.3544, + lng: 34.3361, + pop: 10647, + }, + { + lat: 41.9747, + lng: 33.7608, + pop: 10594, + }, + { + lat: 33.3986, + lng: 36.4531, + pop: 10473, + }, + { + lat: 43.0878, + lng: 46.5631, + pop: 10532, + }, + { + lat: 40.2847, + lng: 30.3172, + pop: 10591, + }, + { + lat: 46.1958, + lng: 41.0778, + pop: 10593, + }, + { + lat: 36.0833, + lng: 36.5, + pop: 10296, + }, + { + lat: 29.5636, + lng: 51.3369, + pop: 10508, + }, + { + lat: 41.8389, + lng: 43.3792, + pop: 10546, + }, + { + lat: 40, + lng: 29.9, + pop: 10527, + }, + { + lat: 46.6167, + lng: 29.9167, + pop: 10436, + }, + { + lat: 32.3772, + lng: 51.1883, + pop: 10279, + }, + { + lat: 40.78, + lng: 43.1353, + pop: 10497, + }, + { + lat: 40.0986, + lng: 44.4681, + pop: 9550, + }, + { + lat: 36.834, + lng: 37.999, + pop: 10436, + }, + { + lat: 23.4675, + lng: 58.1061, + pop: 10396, + }, + { + lat: 36.305, + lng: -119.2083, + pop: 10441, + }, + { + lat: 30.1811, + lng: 56.8019, + pop: 10407, + }, + { + lat: 40.1167, + lng: 35.2667, + pop: 10407, + }, + { + lat: 46.7559, + lng: 33.4247, + pop: 10360, + }, + { + lat: 36.2941, + lng: -119.1459, + pop: 10349, + }, + { + lat: 44.1736, + lng: 28.4083, + pop: 10216, + }, + { + lat: 25.7089, + lng: 55.7972, + pop: 10190, + }, + { + lat: 36.1164, + lng: 36.5147, + pop: 10084, + }, + { + lat: 40.8333, + lng: 33.25, + pop: 10307, + }, + { + lat: 29.5978, + lng: 57.4386, + pop: 10286, + }, + { + lat: 27.4753, + lng: 59.4717, + pop: 10292, + }, + { + lat: 32.6828, + lng: 36.2233, + pop: 9784, + }, + { + lat: 40.9419, + lng: 45.7358, + pop: 10130, + }, + { + lat: 46.5036, + lng: 30.3244, + pop: 10148, + }, + { + lat: 39.7981, + lng: 42.6744, + pop: 10191, + }, + { + lat: 45.6333, + lng: 27.8, + pop: 10126, + }, + { + lat: 28.8678, + lng: 52.7533, + pop: 10120, + }, + { + lat: 46.9139, + lng: 28.9708, + pop: 9966, + }, + { + lat: 31.9989, + lng: 50.6617, + pop: 10113, + }, + { + lat: 26.2361, + lng: 61.3986, + pop: 10115, + }, + { + lat: 37.471, + lng: 42.317, + pop: 10094, + }, + { + lat: 43.1581, + lng: 44.1569, + pop: 10075, + }, + { + lat: 43.071, + lng: 46.6345, + pop: 10014, + }, + { + lat: 34.6527, + lng: -118.2163, + pop: 10079, + }, + { + lat: 46.0903, + lng: 47.7306, + pop: 10036, + }, + { + lat: 40.8808, + lng: 45.3917, + pop: 9864, + }, + { + lat: 39.75, + lng: 28.9167, + pop: 10042, + }, + { + lat: 35.1409, + lng: -118.4968, + pop: 10051, + }, + { + lat: 27.6594, + lng: 52.6575, + pop: 9982, + }, + { + lat: 43.2317, + lng: 45.5722, + pop: 9783, + }, + { + lat: 40.1331, + lng: 45.4367, + pop: 9880, + }, + { + lat: 36.9667, + lng: 35.05, + pop: 8689, + }, + { + lat: 27.1992, + lng: 54.3667, + pop: 9959, + }, + { + lat: 31.9336, + lng: 51.3306, + pop: 9923, + }, + { + lat: 33.0736, + lng: 50.1647, + pop: 9933, + }, + { + lat: 35.6169, + lng: 36.5953, + pop: 9595, + }, + { + lat: 43.1328, + lng: 45.7797, + pop: 9738, + }, + { + lat: 32.7039, + lng: 51.8381, + pop: 9712, + }, + { + lat: 47.2039, + lng: 30.9125, + pop: 9845, + }, + { + lat: 41.1289, + lng: 43.1328, + pop: 9833, + }, + { + lat: 42.3503, + lng: 42.9983, + pop: 9770, + }, + { + lat: 28.745, + lng: 53.8033, + pop: 9719, + }, + { + lat: 40.1333, + lng: 38.7333, + pop: 9759, + }, + { + lat: 47.0189, + lng: 34.9212, + pop: 9719, + }, + { + lat: 32.8658, + lng: 51.5972, + pop: 9690, + }, + { + lat: 36.8708, + lng: 39.025, + pop: 9653, + }, + { + lat: 40.7814, + lng: 43.8964, + pop: 9668, + }, + { + lat: 43.1797, + lng: 45.4081, + pop: 9584, + }, + { + lat: 45.1278, + lng: 39.5725, + pop: 9617, + }, + { + lat: 40.5194, + lng: 28.8281, + pop: 9625, + }, + { + lat: 44.6833, + lng: 27.9519, + pop: 9642, + }, + { + lat: 46.3667, + lng: 28.5167, + pop: 9562, + }, + { + lat: 46.6317, + lng: 32.4452, + pop: 9565, + }, + { + lat: 33.6878, + lng: 36.1008, + pop: 9371, + }, + { + lat: 44.8228, + lng: 44.6592, + pop: 9602, + }, + { + lat: 39.7175, + lng: 44.8764, + pop: 9306, + }, + { + lat: 46.3629, + lng: 33.5302, + pop: 9539, + }, + { + lat: 43.3725, + lng: 46.445, + pop: 9442, + }, + { + lat: 43.2, + lng: 45.7889, + pop: 9300, + }, + { + lat: 45.4464, + lng: 34.7344, + pop: 9460, + }, + { + lat: 43.9625, + lng: 42.9875, + pop: 9427, + }, + { + lat: 47.1503, + lng: 29.2925, + pop: 9381, + }, + { + lat: 40.21, + lng: 39.6511, + pop: 9387, + }, + { + lat: 29.0147, + lng: 61.45, + pop: 9359, + }, + { + lat: 28.0842, + lng: 54.0483, + pop: 9318, + }, + { + lat: 44.5167, + lng: 34.1833, + pop: 8571, + }, + { + lat: 47.0875, + lng: 28.8703, + pop: 8694, + }, + { + lat: 44.8981, + lng: 28.7419, + pop: 9213, + }, + { + lat: 46.2581, + lng: 33.2843, + pop: 9224, + }, + { + lat: 31.8719, + lng: 56.0239, + pop: 9232, + }, + { + lat: 29.885, + lng: 57.7306, + pop: 9205, + }, + { + lat: 47.25, + lng: 28.7667, + pop: 9122, + }, + { + lat: 40, + lng: 36.22, + pop: 9154, + }, + { + lat: 45.85, + lng: 28.6944, + pop: 9138, + }, + { + lat: 27.8722, + lng: 52.0289, + pop: 8753, + }, + { + lat: 35.5464, + lng: 36.6431, + pop: 8817, + }, + { + lat: 29.6, + lng: 55.5369, + pop: 9112, + }, + { + lat: 43.3186, + lng: 45.9878, + pop: 8972, + }, + { + lat: 35.2477, + lng: -116.6834, + pop: 9100, + }, + { + lat: 41.6222, + lng: 35.5314, + pop: 8864, + }, + { + lat: 46.7833, + lng: 29.6167, + pop: 9000, + }, + { + lat: 35.0025, + lng: 40.5117, + pop: 9000, + }, + { + lat: 39.4737, + lng: -118.7779, + pop: 9068, + }, + { + lat: 40.1, + lng: 31.6833, + pop: 9039, + }, + { + lat: 39.8056, + lng: 40.0364, + pop: 9032, + }, + { + lat: 40.75, + lng: 33.7667, + pop: 8981, + }, + { + lat: 42.3244, + lng: 42.4222, + pop: 8987, + }, + { + lat: 41.45, + lng: 44.5333, + pop: 8967, + }, + { + lat: 41.5, + lng: 31.8667, + pop: 8678, + }, + { + lat: 32.3828, + lng: 35.6619, + pop: 8647, + }, + { + lat: 44.2333, + lng: 42.0167, + pop: 8836, + }, + { + lat: 28.3106, + lng: 54.3347, + pop: 8927, + }, + { + lat: 40.1467, + lng: 45.2642, + pop: 8553, + }, + { + lat: 35.9969, + lng: 36.7867, + pop: 8540, + }, + { + lat: 46.1353, + lng: 41.9656, + pop: 8798, + }, + { + lat: 29.4769, + lng: 54.3314, + pop: 8799, + }, + { + lat: 37.1742, + lng: -113.6809, + pop: 8786, + }, + { + lat: 41.1244, + lng: 44.2819, + pop: 8700, + }, + { + lat: 40.283, + lng: 35.267, + pop: 8696, + }, + { + lat: 35.1268, + lng: -119.4243, + pop: 8730, + }, + { + lat: 32.5756, + lng: 59.7983, + pop: 8715, + }, + { + lat: 36.5433, + lng: -119.2914, + pop: 8701, + }, + { + lat: 37.1703, + lng: 34.6083, + pop: 8679, + }, + { + lat: 42.5658, + lng: 47.5631, + pop: 8627, + }, + { + lat: 40.1639, + lng: 39.8925, + pop: 8657, + }, + { + lat: 37.0728, + lng: 40.6519, + pop: 8551, + }, + { + lat: 44.7494, + lng: 43.4386, + pop: 8544, + }, + { + lat: 37.6494, + lng: 30.5339, + pop: 8537, + }, + { + lat: 40.1575, + lng: 33.7175, + pop: 8531, + }, + { + lat: 32.2631, + lng: 51.5622, + pop: 9924, + }, + { + lat: 32.0686, + lng: 61.8058, + pop: 10000, + }, + { + lat: 33.7711, + lng: 35.6858, + pop: 10000, + }, + { + lat: 30.4667, + lng: 53.45, + pop: 9776, + }, + { + lat: 36.5833, + lng: 31.8833, + pop: 9527, + }, + { + lat: 43.2167, + lng: 46.8667, + pop: 9458, + }, + { + lat: 40.9447, + lng: 47.9411, + pop: 9507, + }, + { + lat: 44.2659, + lng: 43.7562, + pop: 9516, + }, + { + lat: 30.4344, + lng: 63.3183, + pop: 9482, + }, + { + lat: 43.0997, + lng: 44.6317, + pop: 9217, + }, + { + lat: 32.1447, + lng: 48.3925, + pop: 9177, + }, + { + lat: 44.0817, + lng: 42.9606, + pop: 9079, + }, + { + lat: 43.42, + lng: 43.92, + pop: 9010, + }, + { + lat: 40.5808, + lng: 46.8503, + pop: 8830, + }, + { + lat: 32.2539, + lng: 50.5975, + pop: 8699, + }, + { + lat: 40.9533, + lng: 45.6792, + pop: 8702, + }, + { + lat: 43.3469, + lng: 44.6975, + pop: 8590, + }, + { + lat: 46.5467, + lng: 30.6306, + pop: 8558, + }, + { + lat: 43.1486, + lng: 44.7069, + pop: 8508, + }, + { + lat: 40.9053, + lng: 45.5564, + pop: 1155, + }, +] \ No newline at end of file diff --git a/client/src/units/groundunitdatabase.ts b/client/src/units/groundunitdatabase.ts index a52f58fc..b6e94fe6 100644 --- a/client/src/units/groundunitdatabase.ts +++ b/client/src/units/groundunitdatabase.ts @@ -233,6 +233,7 @@ export class GroundUnitDatabase extends UnitDatabase { }, "Bunker": { "name": "Bunker", + "era": ["Early Cold War", "Mid Cold War", "Late Cold War", "Modern"], "label": "Bunker", "shortLabel": "Bunker", "filename": "", @@ -370,6 +371,7 @@ export class GroundUnitDatabase extends UnitDatabase { }, "Sandbox": { "name": "Sandbox", + "era": ["Early Cold War", "Mid Cold War", "Late Cold War", "Modern"], "label": "Sandbox", "shortLabel": "Sandbox", "filename": "", @@ -1085,6 +1087,7 @@ export class GroundUnitDatabase extends UnitDatabase { }, "house1arm": { "name": "house1arm", + "era": ["Early Cold War", "Mid Cold War", "Late Cold War", "Modern"], "label": "house1arm", "shortLabel": "house1arm", "filename": "", @@ -1092,6 +1095,7 @@ export class GroundUnitDatabase extends UnitDatabase { }, "house2arm": { "name": "house2arm", + "era": ["Early Cold War", "Mid Cold War", "Late Cold War", "Modern"], "label": "house2arm", "shortLabel": "house2arm", "filename": "", @@ -1099,6 +1103,7 @@ export class GroundUnitDatabase extends UnitDatabase { }, "outpost_road": { "name": "outpost_road", + "era": ["Early Cold War", "Mid Cold War", "Late Cold War", "Modern"], "label": "outpost_road", "shortLabel": "outpost_road", "filename": "", @@ -1106,6 +1111,7 @@ export class GroundUnitDatabase extends UnitDatabase { }, "outpost": { "name": "outpost", + "era": ["Early Cold War", "Mid Cold War", "Late Cold War", "Modern"], "label": "outpost", "shortLabel": "outpost", "filename": "", @@ -1113,6 +1119,7 @@ export class GroundUnitDatabase extends UnitDatabase { }, "houseA_arm": { "name": "houseA_arm", + "era": ["Early Cold War", "Mid Cold War", "Late Cold War", "Modern"], "label": "houseA_arm", "shortLabel": "houseA_arm", "filename": "", diff --git a/client/src/units/unit.ts b/client/src/units/unit.ts index ba412afd..ca4d66a9 100644 --- a/client/src/units/unit.ts +++ b/client/src/units/unit.ts @@ -1,7 +1,7 @@ import { Marker, LatLng, Polyline, Icon, DivIcon, CircleMarker, Map, Point } from 'leaflet'; import { getMap, getUnitsManager } from '..'; -import { enumToCoalition, enumToEmissioNCountermeasure, getMarkerCategoryByName, enumToROE, enumToReactionToThreat, enumToState, getUnitDatabaseByCategory, mToFt, msToKnots, rad2deg, bearing, coordinatesFromBearingAndDistance, deg2rad } from '../other/utils'; -import { addDestination, attackUnit, changeAltitude, changeSpeed, createFormation as setLeader, deleteUnit, getUnits, landAt, setAltitude, setReactionToThreat, setROE, setSpeed, refuel, setAdvacedOptions, followUnit, setEmissionsCountermeasures, setSpeedType, setAltitudeType, setOnOff, setFollowRoads, bombPoint, carpetBomb, bombBuilding, fireAtArea } from '../server/server'; +import { enumToCoalition, enumToEmissioNCountermeasure, getMarkerCategoryByName, enumToROE, enumToReactionToThreat, enumToState, getUnitDatabaseByCategory, mToFt, msToKnots, rad2deg, bearing, deg2rad } from '../other/utils'; +import { addDestination, attackUnit, changeAltitude, changeSpeed, createFormation as setLeader, deleteUnit, landAt, setAltitude, setReactionToThreat, setROE, setSpeed, refuel, setAdvacedOptions, followUnit, setEmissionsCountermeasures, setSpeedType, setAltitudeType, setOnOff, setFollowRoads, bombPoint, carpetBomb, bombBuilding, fireAtArea } from '../server/server'; import { CustomMarker } from '../map/custommarker'; import { SVGInjector } from '@tanem/svg-injector'; import { UnitDatabase } from './unitdatabase'; diff --git a/client/src/units/unitdatabase.ts b/client/src/units/unitdatabase.ts index fc1bf302..68fc7120 100644 --- a/client/src/units/unitdatabase.ts +++ b/client/src/units/unitdatabase.ts @@ -5,6 +5,10 @@ export class UnitDatabase { } + getBlueprints() { + return this.blueprints; + } + /* Returns a list of all possible roles in a database */ getRoles() { var roles: string[] = []; @@ -33,6 +37,32 @@ export class UnitDatabase { return types; } + /* Returns a list of all possible periods in a database */ + getEras() { + var eras: string[] = []; + for (let unit in this.blueprints) { + var unitEras = this.blueprints[unit].era; + if (unitEras) { + for (let era of unitEras) { + if (era !== "" && !eras.includes(era)) + eras.push(era); + } + } + } + return eras; + } + + /* Returns a list of all possible ranges in a database */ + getRanges() { + var ranges: string[] = []; + for (let unit in this.blueprints) { + var range = this.blueprints[unit].range; + if (range && range !== "" && !ranges.includes(range)) + ranges.push(range); + } + return ranges; + } + /* Gets a specific blueprint by name */ getByName(name: string) { if (name in this.blueprints) diff --git a/client/src/units/unitsmanager.ts b/client/src/units/unitsmanager.ts index 8f2fb8ad..bd35acbf 100644 --- a/client/src/units/unitsmanager.ts +++ b/client/src/units/unitsmanager.ts @@ -2,13 +2,14 @@ import { LatLng, LatLngBounds } from "leaflet"; import { getHotgroupPanel, getInfoPopup, getMap, getMissionHandler } from ".."; import { Unit } from "./unit"; import { cloneUnit, setLastUpdateTime, spawnGroundUnits } from "../server/server"; -import { deg2rad, keyEventWasInInput, latLngToMercator, mToFt, mercatorToLatLng, msToKnots, polygonArea, randomPointInPoly, randomUnitBlueprintByRole } from "../other/utils"; +import { bearingAndDistanceToLatLng, deg2rad, keyEventWasInInput, latLngToMercator, mToFt, mercatorToLatLng, msToKnots, polyContains, polygonArea, randomPointInPoly, randomUnitBlueprint } from "../other/utils"; import { CoalitionArea } from "../map/coalitionarea"; import { Airbase } from "../missionhandler/airbase"; import { groundUnitDatabase } from "./groundunitdatabase"; -import { DataIndexes, HIDE_ALL, IADSRoles, IDLE, MOVE_UNIT } from "../constants/constants"; +import { DataIndexes, HIDE_ALL, IADSDensities, IDLE, MOVE_UNIT } from "../constants/constants"; import { DataExtractor } from "./dataextractor"; import { Contact } from "../@types/unit"; +import { citiesDatabase } from "./citiesDatabase"; export class UnitsManager { #units: { [ID: number]: Unit }; @@ -544,28 +545,31 @@ 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; - }); + createIADS(coalitionArea: CoalitionArea, types: {[key: string]: boolean}, eras: {[key: string]: boolean}, ranges: {[key: string]: boolean}, density: number, distribution: number) { + const activeTypes = Object.keys(types).filter((key: string) => { return types[key]; }); + const activeEras = Object.keys(eras).filter((key: string) => { return eras[key]; }); + const activeRanges = Object.keys(ranges).filter((key: string) => { return ranges[key]; }); - const role = activeRoles[Math.floor(Math.random() * activeRoles.length)]; - const probability = Math.pow(1 - minDistance / 50e3, 5) * IADSRoles[role]; - if (Math.random() < probability){ - const unitBlueprint = randomUnitBlueprintByRole(groundUnitDatabase, role); - spawnGroundUnits([{unitType: unitBlueprint.name, location: latlng}], coalitionArea.getCoalition(), true); - getMap().addTemporaryMarker(latlng, unitBlueprint.name, coalitionArea.getCoalition()); + citiesDatabase.forEach((city: {lat: number, lng: number, pop: number}) => { + if (polyContains(new LatLng(city.lat, city.lng), coalitionArea)) { + var pointsNumber = 2 + Math.pow(city.pop, 0.2) * density / 100; + for (let i = 0; i < pointsNumber; i++) { + var bearing = Math.random() * 360; + var distance = Math.random() * distribution * 100; + const latlng = bearingAndDistanceToLatLng(city.lat, city.lng, bearing, distance); + if (polyContains(latlng, coalitionArea)) { + const type = activeTypes[Math.floor(Math.random() * activeTypes.length)]; + if (Math.random() < IADSDensities[type]) { + const unitBlueprint = randomUnitBlueprint(groundUnitDatabase, {type: type, eras: activeEras, ranges: activeRanges}); + if (unitBlueprint) { + spawnGroundUnits([{unitType: unitBlueprint.name, location: latlng}], coalitionArea.getCoalition(), true); + getMap().addTemporaryMarker(latlng, unitBlueprint.name, coalitionArea.getCoalition()); + } + } + } + } } - } + }) } exportToFile() { diff --git a/client/views/other/contextmenus.ejs b/client/views/other/contextmenus.ejs index cc2fe2aa..6ea1c23c 100644 --- a/client/views/other/contextmenus.ejs +++ b/client/views/other/contextmenus.ejs @@ -1,21 +1,27 @@
-
+
- + +
+
+
+ + -
@@ -229,36 +235,49 @@ class="ol-contexmenu-button">
-
+
Unit types
- +
- -
- +
+
+
Units ranges
+
+ +
+
+
+ + + -->
IADS density
- + +
+
+
+
IADS distribution
+
+
From 5d29c8224435bf62cf6452f8856ca0cc11fbd7fc Mon Sep 17 00:00:00 2001 From: Pax1601 Date: Tue, 11 Jul 2023 18:56:37 +0200 Subject: [PATCH 5/5] Commented not implemented functions --- client/views/other/contextmenus.ejs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/client/views/other/contextmenus.ejs b/client/views/other/contextmenus.ejs index 6ea1c23c..197cbb63 100644 --- a/client/views/other/contextmenus.ejs +++ b/client/views/other/contextmenus.ejs @@ -4,8 +4,8 @@
- + +