Add basic visibility functions and ability to spawn multiple units in group

This commit is contained in:
Pax1601
2023-07-07 17:26:41 +02:00
parent 30568e54f7
commit 327d5c74d9
26 changed files with 589 additions and 348 deletions

View File

@@ -17,7 +17,8 @@ interface CustomEventMap {
"groupCreation": CustomEvent<Unit[]>,
"groupDeletion": CustomEvent<Unit[]>,
"mapStateChanged": CustomEvent<string>,
"mapContextMenu": CustomEvent<>
"mapContextMenu": CustomEvent<>,
"visibilityModeChanged": CustomEvent<string>,
}
declare global {

View File

@@ -1,8 +1,3 @@
interface UnitsData {
units: string,
sessionHash: string
}
interface AirbasesData {
airbases: {[key: string]: any},
}

View File

@@ -8,7 +8,8 @@ interface UnitIconOptions {
showShortLabel: boolean,
showFuel: boolean,
showAmmo: boolean,
showSummary: boolean,
showSummary: boolean,
showCallsign: boolean,
rotateToHeading: boolean
}

View File

@@ -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"];

View File

@@ -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();

View File

@@ -184,12 +184,12 @@ function setupEvents() {
const form = document.querySelector("#splash-content")?.querySelector("#authentication-form");
const username = (<HTMLInputElement>(form?.querySelector("#username"))).value;
const password = (<HTMLInputElement>(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;
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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)

View File

@@ -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);

View File

@@ -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());

View File

@@ -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
};
}

View File

@@ -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" ) {