mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
Merge branch 'main' into 412-improve-importexport
This commit is contained in:
60
client/src/unit/contextaction.ts
Normal file
60
client/src/unit/contextaction.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { Unit } from "./unit";
|
||||
|
||||
export interface ContextActionOptions {
|
||||
isScenic?: boolean
|
||||
}
|
||||
|
||||
export class ContextAction {
|
||||
#id: string = "";
|
||||
#label: string = "";
|
||||
#description: string = "";
|
||||
#callback: CallableFunction | null = null;
|
||||
#units: Unit[] = [];
|
||||
#hideContextAfterExecution: boolean = true
|
||||
#options: ContextActionOptions;
|
||||
|
||||
constructor(id: string, label: string, description: string, callback: CallableFunction, hideContextAfterExecution: boolean = true, options: ContextActionOptions) {
|
||||
this.#id = id;
|
||||
this.#label = label;
|
||||
this.#description = description;
|
||||
this.#callback = callback;
|
||||
this.#hideContextAfterExecution = hideContextAfterExecution;
|
||||
this.#options = {
|
||||
"isScenic": false,
|
||||
...options
|
||||
}
|
||||
}
|
||||
|
||||
addUnit(unit: Unit) {
|
||||
this.#units.push(unit);
|
||||
}
|
||||
|
||||
getId() {
|
||||
return this.#id;
|
||||
}
|
||||
|
||||
getLabel() {
|
||||
return this.#label;
|
||||
}
|
||||
|
||||
getOptions() {
|
||||
return this.#options;
|
||||
}
|
||||
|
||||
getDescription() {
|
||||
return this.#description;
|
||||
}
|
||||
|
||||
getCallback() {
|
||||
return this.#callback;
|
||||
}
|
||||
|
||||
executeCallback() {
|
||||
if (this.#callback)
|
||||
this.#callback(this.#units);
|
||||
}
|
||||
|
||||
getHideContextAfterExecution() {
|
||||
return this.#hideContextAfterExecution;
|
||||
}
|
||||
}
|
||||
25
client/src/unit/contextactionset.ts
Normal file
25
client/src/unit/contextactionset.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { ContextAction, ContextActionOptions } from "./contextaction";
|
||||
import { Unit } from "./unit";
|
||||
|
||||
export class ContextActionSet {
|
||||
#contextActions: {[key: string]: ContextAction} = {};
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
addContextAction(unit: Unit, id: string, label: string, description: string, callback: CallableFunction, hideContextAfterExecution: boolean = true, options?:ContextActionOptions) {
|
||||
options = options || {};
|
||||
|
||||
if (!(id in this.#contextActions)) {
|
||||
this.#contextActions[id] = new ContextAction(id, label, description, callback, hideContextAfterExecution, options);
|
||||
}
|
||||
this.#contextActions[id].addUnit(unit);
|
||||
}
|
||||
|
||||
getContextActions() {
|
||||
return this.#contextActions;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
import { getApp } from "../..";
|
||||
import { GAME_MASTER } from "../../constants/constants";
|
||||
import { UnitBlueprint } from "../../interfaces";
|
||||
import { UnitDatabase } from "./unitdatabase"
|
||||
|
||||
export class AircraftDatabase extends UnitDatabase {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { getApp } from "../..";
|
||||
import { GAME_MASTER } from "../../constants/constants";
|
||||
import { UnitBlueprint } from "../../interfaces";
|
||||
|
||||
export class UnitDatabase {
|
||||
export abstract class UnitDatabase {
|
||||
blueprints: { [key: string]: UnitBlueprint } = {};
|
||||
#url: string;
|
||||
|
||||
@@ -31,9 +31,7 @@ export class UnitDatabase {
|
||||
}
|
||||
}
|
||||
|
||||
getCategory() {
|
||||
return "";
|
||||
}
|
||||
abstract getCategory(): string;
|
||||
|
||||
/* Gets a specific blueprint by name */
|
||||
getByName(name: string) {
|
||||
@@ -240,4 +238,15 @@ export class UnitDatabase {
|
||||
getSpawnPointsByName(name: string) {
|
||||
return Infinity;
|
||||
}
|
||||
|
||||
getUnkownUnit(name: string): UnitBlueprint {
|
||||
return {
|
||||
name: name,
|
||||
enabled: true,
|
||||
coalition: 'neutral',
|
||||
era: 'N/A',
|
||||
label: name,
|
||||
shortLabel: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
import { Marker, LatLng, Polyline, Icon, DivIcon, CircleMarker, Map, Point, Circle } from 'leaflet';
|
||||
import { Marker, LatLng, Polyline, Icon, DivIcon, CircleMarker, Map, Point } from 'leaflet';
|
||||
import { getApp } from '..';
|
||||
import { enumToCoalition, enumToEmissioNCountermeasure, getMarkerCategoryByName, enumToROE, enumToReactionToThreat, enumToState, getUnitDatabaseByCategory, mToFt, msToKnots, rad2deg, bearing, deg2rad, ftToM, getGroundElevation, coalitionToEnum, nmToFt, nmToM } from '../other/utils';
|
||||
import { enumToCoalition, enumToEmissioNCountermeasure, enumToROE, enumToReactionToThreat, enumToState, getUnitDatabaseByCategory, mToFt, msToKnots, rad2deg, bearing, deg2rad, ftToM, getGroundElevation, coalitionToEnum, nmToFt, nmToM } from '../other/utils';
|
||||
import { CustomMarker } from '../map/markers/custommarker';
|
||||
import { SVGInjector } from '@tanem/svg-injector';
|
||||
import { UnitDatabase } from './databases/unitdatabase';
|
||||
import { TargetMarker } from '../map/markers/targetmarker';
|
||||
import { DLINK, DataIndexes, GAME_MASTER, HIDE_GROUP_MEMBERS, IDLE, IRST, MOVE_UNIT, OPTIC, RADAR, ROEs, RWR, SHOW_UNIT_CONTACTS, SHOW_UNITS_ENGAGEMENT_RINGS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, VISUAL, emissionsCountermeasures, reactionsToThreat, states, SHOW_UNITS_ACQUISITION_RINGS, HIDE_UNITS_SHORT_RANGE_RINGS, FILL_SELECTED_RING, GROUPING_ZOOM_TRANSITION, GROUND_UNIT_AIR_DEFENCE_REGEX } from '../constants/constants';
|
||||
import { DLINK, DataIndexes, GAME_MASTER, HIDE_GROUP_MEMBERS, IDLE, IRST, MOVE_UNIT, OPTIC, RADAR, ROEs, RWR, SHOW_UNIT_CONTACTS, SHOW_UNITS_ENGAGEMENT_RINGS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, VISUAL, emissionsCountermeasures, reactionsToThreat, states, SHOW_UNITS_ACQUISITION_RINGS, HIDE_UNITS_SHORT_RANGE_RINGS, FILL_SELECTED_RING, GROUPING_ZOOM_TRANSITION, MAX_SHOTS_SCATTER, SHOTS_SCATTER_DEGREES, GROUND_UNIT_AIR_DEFENCE_REGEX } from '../constants/constants';
|
||||
import { DataExtractor } from '../server/dataextractor';
|
||||
import { groundUnitDatabase } from './databases/groundunitdatabase';
|
||||
import { navyUnitDatabase } from './databases/navyunitdatabase';
|
||||
@@ -13,6 +13,8 @@ import { Weapon } from '../weapon/weapon';
|
||||
import { Ammo, Contact, GeneralSettings, LoadoutBlueprint, ObjectIconOptions, Offset, Radio, TACAN, UnitData } from '../interfaces';
|
||||
import { RangeCircle } from "../map/rangecircle";
|
||||
import { Group } from './group';
|
||||
import { ContextActionSet } from './contextactionset';
|
||||
import * as turf from "@turf/turf";
|
||||
|
||||
var pathIcon = new Icon({
|
||||
iconUrl: '/resources/theme/images/markers/marker-icon.png',
|
||||
@@ -190,15 +192,17 @@ export abstract class Unit extends CustomMarker {
|
||||
|
||||
/* Deselect units if they are hidden */
|
||||
document.addEventListener("toggleCoalitionVisibility", (ev: CustomEventInit) => {
|
||||
window.setTimeout(() => { this.setSelected(this.getSelected() && !this.getHidden()) }, 300);
|
||||
this.#updateMarker();
|
||||
this.setSelected(this.getSelected() && !this.getHidden());
|
||||
});
|
||||
|
||||
document.addEventListener("toggleUnitVisibility", (ev: CustomEventInit) => {
|
||||
window.setTimeout(() => { this.setSelected(this.getSelected() && !this.getHidden()) }, 300);
|
||||
document.addEventListener("toggleMarkerVisibility", (ev: CustomEventInit) => {
|
||||
this.#updateMarker();
|
||||
this.setSelected(this.getSelected() && !this.getHidden());
|
||||
});
|
||||
|
||||
/* Update the marker when the visibility options change */
|
||||
document.addEventListener("mapVisibilityOptionsChanged", (ev: CustomEventInit) => {
|
||||
/* Update the marker when the options change */
|
||||
document.addEventListener("mapOptionsChanged", (ev: CustomEventInit) => {
|
||||
this.#updateMarker();
|
||||
|
||||
/* Circles don't like to be updated when the map is zooming */
|
||||
@@ -212,7 +216,7 @@ export abstract class Unit extends CustomMarker {
|
||||
});
|
||||
}
|
||||
|
||||
/********************** Abstract methods *************************/
|
||||
/********************** Abstract methods *************************/
|
||||
/** Get the unit category string
|
||||
*
|
||||
* @returns string The unit category
|
||||
@@ -228,9 +232,20 @@ export abstract class Unit extends CustomMarker {
|
||||
|
||||
/** Get the actions that this unit can perform
|
||||
*
|
||||
* @returns Object containing the available actions
|
||||
*/
|
||||
abstract getActions(): {[key: string]: { text: string, tooltip: string, type: string}};
|
||||
abstract appendContextActions(contextActionSet: ContextActionSet, targetUnit: Unit | null, targetPosition: LatLng | null): void;
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns string containing the marker category
|
||||
*/
|
||||
abstract getMarkerCategory(): string;
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns string containing the default marker
|
||||
*/
|
||||
abstract getDefaultMarker(): string;
|
||||
|
||||
/** Get the category but for display use - for the user. (i.e. has spaces in it)
|
||||
*
|
||||
@@ -248,7 +263,7 @@ export abstract class Unit extends CustomMarker {
|
||||
setData(dataExtractor: DataExtractor) {
|
||||
/* This variable controls if the marker must be updated. This is not always true since not all variables have an effect on the marker */
|
||||
var updateMarker = !getApp().getMap().hasLayer(this);
|
||||
|
||||
|
||||
var oldIsLeader = this.#isLeader;
|
||||
var datumIndex = 0;
|
||||
while (datumIndex != DataIndexes.endOfData) {
|
||||
@@ -320,7 +335,7 @@ export abstract class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
/* If the unit is selected or if the view is centered on this unit, sent the update signal so that other elements like the UnitControlPanel can be updated. */
|
||||
if (this.getSelected() || getApp().getMap().getCenterUnit() === this)
|
||||
if (this.getSelected() || getApp().getMap().getCenteredOnUnit() === this)
|
||||
document.dispatchEvent(new CustomEvent("unitUpdated", { detail: this }));
|
||||
}
|
||||
|
||||
@@ -379,14 +394,6 @@ export abstract class Unit extends CustomMarker {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns string containing the marker category
|
||||
*/
|
||||
getMarkerCategory(): string {
|
||||
return getMarkerCategoryByName(this.getName());
|
||||
}
|
||||
|
||||
/** Get a database of information also in this unit's category
|
||||
*
|
||||
* @returns UnitDatabase
|
||||
@@ -421,7 +428,7 @@ export abstract class Unit extends CustomMarker {
|
||||
else {
|
||||
this.#clearContacts();
|
||||
this.#clearPath();
|
||||
this.#clearTarget();
|
||||
this.#clearTargetPosition();
|
||||
}
|
||||
|
||||
/* When the group leader is selected, if grouping is active, all the other group members are also selected */
|
||||
@@ -456,7 +463,7 @@ export abstract class Unit extends CustomMarker {
|
||||
return this.#selected;
|
||||
}
|
||||
|
||||
/** Set the number of the hotgroup to which the unit belongs
|
||||
/** Set the number of the hotgroup to which the unit belongss
|
||||
*
|
||||
* @param hotgroup (number)
|
||||
*/
|
||||
@@ -498,7 +505,7 @@ export abstract class Unit extends CustomMarker {
|
||||
* @returns Unit[]
|
||||
*/
|
||||
getGroupMembers() {
|
||||
if (this.#group !== null)
|
||||
if (this.#group !== null)
|
||||
return this.#group.getMembers().filter((unit: Unit) => { return unit != this; })
|
||||
return [];
|
||||
}
|
||||
@@ -508,7 +515,7 @@ export abstract class Unit extends CustomMarker {
|
||||
* @returns Unit The leader of the group
|
||||
*/
|
||||
getGroupLeader() {
|
||||
if (this.#group !== null)
|
||||
if (this.#group !== null)
|
||||
return this.#group.getLeader();
|
||||
return null;
|
||||
}
|
||||
@@ -530,7 +537,7 @@ export abstract class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
getDatabaseEntry() {
|
||||
return this.getDatabase()?.getByName(this.#name);
|
||||
return this.getDatabase()?.getByName(this.#name) ?? this.getDatabase()?.getUnkownUnit(this.getName());
|
||||
}
|
||||
|
||||
getGroup() {
|
||||
@@ -572,7 +579,7 @@ export abstract class Unit extends CustomMarker {
|
||||
|
||||
var iconOptions = this.getIconOptions();
|
||||
|
||||
/* Generate and append elements depending on active options */
|
||||
/* Generate and append elements depending on active options */
|
||||
/* Velocity vector */
|
||||
if (iconOptions.showVvi) {
|
||||
var vvi = document.createElement("div");
|
||||
@@ -600,7 +607,7 @@ export abstract class Unit extends CustomMarker {
|
||||
/* If a unit does not belong to the commanded coalition or it is not visually detected, show it with the generic aircraft square */
|
||||
var marker;
|
||||
if (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC].includes(value)))
|
||||
marker = this.getDatabaseEntry()?.markerFile ?? this.getMarkerCategory();
|
||||
marker = this.getDatabaseEntry()?.markerFile ?? this.getDefaultMarker();
|
||||
else
|
||||
marker = "aircraft";
|
||||
img.src = `/resources/theme/images/units/${marker}.svg`;
|
||||
@@ -690,13 +697,11 @@ export abstract class Unit extends CustomMarker {
|
||||
/* Hide the unit if it does not belong to the commanded coalition and it is not detected by a method that can pinpoint its location (RWR does not count) */
|
||||
(!this.belongsToCommandedCoalition() && (this.#detectionMethods.length == 0 || (this.#detectionMethods.length == 1 && this.#detectionMethods[0] === RWR))) ||
|
||||
/* Hide the unit if grouping is activated, the unit is not the group leader, it is not selected, and the zoom is higher than the grouping threshold */
|
||||
(getApp().getMap().getVisibilityOptions()[HIDE_GROUP_MEMBERS] && !this.#isLeader && this.getCategory() == "GroundUnit" && getApp().getMap().getZoom() < GROUPING_ZOOM_TRANSITION &&
|
||||
(this.belongsToCommandedCoalition() || (!this.belongsToCommandedCoalition() && this.#detectionMethods.length == 0)))) &&
|
||||
!(this.getSelected()
|
||||
);
|
||||
(getApp().getMap().getVisibilityOptions()[HIDE_GROUP_MEMBERS] && !this.#isLeader && !this.getSelected() && this.getCategory() == "GroundUnit" && getApp().getMap().getZoom() < GROUPING_ZOOM_TRANSITION &&
|
||||
(this.belongsToCommandedCoalition() || (!this.belongsToCommandedCoalition() && this.#detectionMethods.length == 0))));
|
||||
|
||||
/* Force dead units to be hidden */
|
||||
this.setHidden(hidden || !this.#alive);
|
||||
this.setHidden(hidden || !this.getAlive());
|
||||
}
|
||||
|
||||
setHidden(hidden: boolean) {
|
||||
@@ -776,15 +781,11 @@ export abstract class Unit extends CustomMarker {
|
||||
return this.getDatabaseEntry()?.canRearm === true;
|
||||
}
|
||||
|
||||
canLandAtPoint() {
|
||||
return this.getCategory() === "Helicopter";
|
||||
}
|
||||
|
||||
canAAA() {
|
||||
return this.getDatabaseEntry()?.canAAA === true;
|
||||
}
|
||||
|
||||
indirectFire() {
|
||||
isIndirectFire() {
|
||||
return this.getDatabaseEntry()?.indirectFire === true;
|
||||
}
|
||||
|
||||
@@ -941,6 +942,7 @@ export abstract class Unit extends CustomMarker {
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: Remove coalition
|
||||
scenicAAA() {
|
||||
var coalition = "neutral";
|
||||
if (this.getCoalition() === "red")
|
||||
@@ -950,6 +952,7 @@ export abstract class Unit extends CustomMarker {
|
||||
getApp().getServerManager().scenicAAA(this.ID, coalition);
|
||||
}
|
||||
|
||||
// TODO: Remove coalition
|
||||
missOnPurpose() {
|
||||
var coalition = "neutral";
|
||||
if (this.getCoalition() === "red")
|
||||
@@ -973,24 +976,6 @@ export abstract class Unit extends CustomMarker {
|
||||
getApp().getServerManager().setShotsIntensity(this.ID, shotsIntensity);
|
||||
}
|
||||
|
||||
/***********************************************/
|
||||
executeAction(e: any, action: string) {
|
||||
if (action === "center-map")
|
||||
getApp().getMap().centerOnUnit(this.ID);
|
||||
if (action === "attack")
|
||||
getApp().getUnitsManager().selectedUnitsAttackUnit(this.ID);
|
||||
else if (action === "refuel")
|
||||
getApp().getUnitsManager().selectedUnitsRefuel();
|
||||
else if (action === "group-ground" || action === "group-navy")
|
||||
getApp().getUnitsManager().selectedUnitsCreateGroup();
|
||||
else if (action === "scenic-aaa")
|
||||
getApp().getUnitsManager().selectedUnitsScenicAAA();
|
||||
else if (action === "miss-aaa")
|
||||
getApp().getUnitsManager().selectedUnitsMissOnPurpose();
|
||||
else if (action === "follow")
|
||||
this.#showFollowOptions(e);
|
||||
}
|
||||
|
||||
/***********************************************/
|
||||
onAdd(map: Map): this {
|
||||
super.onAdd(map);
|
||||
@@ -1001,6 +986,56 @@ export abstract class Unit extends CustomMarker {
|
||||
this.#redrawMarker();
|
||||
}
|
||||
|
||||
showFollowOptions(units: Unit[]) {
|
||||
var contextActionSet = new ContextActionSet();
|
||||
|
||||
contextActionSet.addContextAction(this, 'trail', "Trail", "Follow unit in trail formation", () => this.applyFollowOptions('trail', units));
|
||||
contextActionSet.addContextAction(this, 'echelon-lh', "Echelon (LH)", "Follow unit in echelon left formation", () => this.applyFollowOptions('echelon-lh', units));
|
||||
contextActionSet.addContextAction(this, 'echelon-rh', "Echelon (RH)", "Follow unit in echelon right formation", () => this.applyFollowOptions('echelon-rh', units));
|
||||
contextActionSet.addContextAction(this, 'line-abreast-lh', "Line abreast (LH)", "Follow unit in line abreast left formation", () => this.applyFollowOptions('line-abreast-lh', units));
|
||||
contextActionSet.addContextAction(this, 'line-abreast-rh', "Line abreast (RH)", "Follow unit in line abreast right formation", () => this.applyFollowOptions('line-abreast-rh', units));
|
||||
contextActionSet.addContextAction(this, 'front', "Front", "Fly in front of unit", () => this.applyFollowOptions('front', units));
|
||||
contextActionSet.addContextAction(this, 'diamond', "Diamond", "Follow unit in diamond formation", () => this.applyFollowOptions('diamond', units));
|
||||
contextActionSet.addContextAction(this, 'custom', "Custom", "Set a custom formation position", () => this.applyFollowOptions('custom', units));
|
||||
|
||||
getApp().getMap().getUnitContextMenu().setContextActions(contextActionSet);
|
||||
getApp().getMap().showUnitContextMenu();
|
||||
}
|
||||
|
||||
applyFollowOptions(formation: string, units: Unit[]) {
|
||||
if (formation === "custom") {
|
||||
document.getElementById("custom-formation-dialog")?.classList.remove("hide");
|
||||
document.addEventListener("applyCustomFormation", () => {
|
||||
var dialog = document.getElementById("custom-formation-dialog");
|
||||
if (dialog) {
|
||||
dialog.classList.add("hide");
|
||||
var clock = 1;
|
||||
while (clock < 8) {
|
||||
if ((<HTMLInputElement>dialog.querySelector(`#formation-${clock}`)).checked)
|
||||
break
|
||||
clock++;
|
||||
}
|
||||
var angleDeg = 360 - (clock - 1) * 45;
|
||||
var angleRad = deg2rad(angleDeg);
|
||||
var distance = ftToM(parseInt((<HTMLInputElement>dialog.querySelector(`#distance`)?.querySelector("input")).value));
|
||||
var upDown = ftToM(parseInt((<HTMLInputElement>dialog.querySelector(`#up-down`)?.querySelector("input")).value));
|
||||
|
||||
// X: front-rear, positive front
|
||||
// Y: top-bottom, positive top
|
||||
// Z: left-right, positive right
|
||||
var x = distance * Math.cos(angleRad);
|
||||
var y = upDown;
|
||||
var z = distance * Math.sin(angleRad);
|
||||
|
||||
getApp().getUnitsManager().followUnit(this.ID, { "x": x, "y": y, "z": z }, undefined, units);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
getApp().getUnitsManager().followUnit(this.ID, undefined, formation, units);
|
||||
}
|
||||
}
|
||||
|
||||
/***********************************************/
|
||||
#onClick(e: any) {
|
||||
/* Exit if we were waiting for a doubleclick */
|
||||
@@ -1039,102 +1074,20 @@ export abstract class Unit extends CustomMarker {
|
||||
});
|
||||
}
|
||||
|
||||
getActionOptions() {
|
||||
var options: { [key: string]: { text: string, tooltip: string, type: string } } | null = null;
|
||||
#onContextMenu(e: any) {
|
||||
var contextActionSet = new ContextActionSet();
|
||||
|
||||
var units = getApp().getUnitsManager().getSelectedUnits();
|
||||
units.push(this);
|
||||
if (!units.includes(this))
|
||||
units.push(this);
|
||||
|
||||
/* Keep only the common "or" options or any "and" option */
|
||||
units.forEach((unit: Unit) => {
|
||||
var unitOptions = unit.getActions();
|
||||
if (options === null) {
|
||||
options = unitOptions;
|
||||
} else {
|
||||
/* Options of "or" type get shown if any one unit has it*/
|
||||
for (let optionKey in unitOptions) {
|
||||
if (unitOptions[optionKey].type == "or") {
|
||||
options[optionKey] = unitOptions[optionKey];
|
||||
}
|
||||
}
|
||||
unit.appendContextActions(contextActionSet, this, null);
|
||||
})
|
||||
|
||||
/* Options of "and" type get shown if ALL units have it */
|
||||
for (let optionKey in options) {
|
||||
if (!(optionKey in unitOptions)) {
|
||||
delete options[optionKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return options ?? {};
|
||||
}
|
||||
|
||||
#onContextMenu(e: any) {
|
||||
var options = this.getActionOptions();
|
||||
|
||||
if (Object.keys(options).length > 0) {
|
||||
if (Object.keys(contextActionSet.getContextActions()).length > 0) {
|
||||
getApp().getMap().showUnitContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng);
|
||||
getApp().getMap().getUnitContextMenu().setOptions(options, (option: string) => {
|
||||
getApp().getMap().hideUnitContextMenu();
|
||||
this.executeAction(e, option);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#showFollowOptions(e: any) {
|
||||
var options: { [key: string]: { text: string, tooltip: string } } = {};
|
||||
|
||||
options = {
|
||||
'trail': { text: "Trail", tooltip: "Follow unit in trail formation" },
|
||||
'echelon-lh': { text: "Echelon (LH)", tooltip: "Follow unit in echelon left formation" },
|
||||
'echelon-rh': { text: "Echelon (RH)", tooltip: "Follow unit in echelon right formation" },
|
||||
'line-abreast-lh': { text: "Line abreast (LH)", tooltip: "Follow unit in line abreast left formation" },
|
||||
'line-abreast-rh': { text: "Line abreast (RH)", tooltip: "Follow unit in line abreast right formation" },
|
||||
'front': { text: "Front", tooltip: "Fly in front of unit" },
|
||||
'diamond': { text: "Diamond", tooltip: "Follow unit in diamond formation" },
|
||||
'custom': { text: "Custom", tooltip: "Set a custom formation position" },
|
||||
}
|
||||
|
||||
getApp().getMap().getUnitContextMenu().setOptions(options, (option: string) => {
|
||||
getApp().getMap().hideUnitContextMenu();
|
||||
this.#applyFollowOptions(option);
|
||||
});
|
||||
|
||||
getApp().getMap().showUnitContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng);
|
||||
}
|
||||
|
||||
#applyFollowOptions(action: string) {
|
||||
if (action === "custom") {
|
||||
document.getElementById("custom-formation-dialog")?.classList.remove("hide");
|
||||
document.addEventListener("applyCustomFormation", () => {
|
||||
var dialog = document.getElementById("custom-formation-dialog");
|
||||
if (dialog) {
|
||||
dialog.classList.add("hide");
|
||||
var clock = 1;
|
||||
while (clock < 8) {
|
||||
if ((<HTMLInputElement>dialog.querySelector(`#formation-${clock}`)).checked)
|
||||
break
|
||||
clock++;
|
||||
}
|
||||
var angleDeg = 360 - (clock - 1) * 45;
|
||||
var angleRad = deg2rad(angleDeg);
|
||||
var distance = ftToM(parseInt((<HTMLInputElement>dialog.querySelector(`#distance`)?.querySelector("input")).value));
|
||||
var upDown = ftToM(parseInt((<HTMLInputElement>dialog.querySelector(`#up-down`)?.querySelector("input")).value));
|
||||
|
||||
// X: front-rear, positive front
|
||||
// Y: top-bottom, positive top
|
||||
// Z: left-right, positive right
|
||||
var x = distance * Math.cos(angleRad);
|
||||
var y = upDown;
|
||||
var z = distance * Math.sin(angleRad);
|
||||
|
||||
getApp().getUnitsManager().selectedUnitsFollowUnit(this.ID, { "x": x, "y": y, "z": z });
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
getApp().getUnitsManager().selectedUnitsFollowUnit(this.ID, undefined, action);
|
||||
getApp().getMap().getUnitContextMenu().setContextActions(contextActionSet);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1313,11 +1266,13 @@ export abstract class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
#clearPath() {
|
||||
for (let WP in this.#pathMarkers) {
|
||||
getApp().getMap().removeLayer(this.#pathMarkers[WP]);
|
||||
if (this.#pathPolyline.getLatLngs().length != 0) {
|
||||
for (let WP in this.#pathMarkers) {
|
||||
getApp().getMap().removeLayer(this.#pathMarkers[WP]);
|
||||
}
|
||||
this.#pathMarkers = [];
|
||||
this.#pathPolyline.setLatLngs([]);
|
||||
}
|
||||
this.#pathMarkers = [];
|
||||
this.#pathPolyline.setLatLngs([]);
|
||||
}
|
||||
|
||||
#drawContacts() {
|
||||
@@ -1471,7 +1426,7 @@ export abstract class Unit extends CustomMarker {
|
||||
}
|
||||
}
|
||||
else
|
||||
this.#clearTarget();
|
||||
this.#clearTargetPosition();
|
||||
}
|
||||
|
||||
#drawTargetPosition(targetPosition: LatLng) {
|
||||
@@ -1480,10 +1435,25 @@ export abstract class Unit extends CustomMarker {
|
||||
if (!getApp().getMap().hasLayer(this.#targetPositionPolyline))
|
||||
this.#targetPositionPolyline.addTo(getApp().getMap());
|
||||
this.#targetPositionMarker.setLatLng(new LatLng(targetPosition.lat, targetPosition.lng));
|
||||
this.#targetPositionPolyline.setLatLngs([new LatLng(this.#position.lat, this.#position.lng), new LatLng(targetPosition.lat, targetPosition.lng)])
|
||||
|
||||
if (this.getState() === 'simulate-fire-fight' && this.getShotsScatter() != MAX_SHOTS_SCATTER) {
|
||||
let turfUnitPosition = turf.point([this.getPosition().lng, this.getPosition().lat]);
|
||||
let turfTargetPosition = turf.point([targetPosition.lng, targetPosition.lat]);
|
||||
|
||||
let bearing = turf.bearing(turfUnitPosition, turfTargetPosition);
|
||||
let scatterDistance = turf.distance(turfUnitPosition, turfTargetPosition) * Math.tan((MAX_SHOTS_SCATTER - this.getShotsScatter()) * deg2rad(SHOTS_SCATTER_DEGREES));
|
||||
let destination1 = turf.destination(turfTargetPosition, scatterDistance, bearing + 90);
|
||||
let destination2 = turf.destination(turfTargetPosition, scatterDistance, bearing - 90);
|
||||
|
||||
this.#targetPositionPolyline.setStyle({dashArray: "4, 8"});
|
||||
this.#targetPositionPolyline.setLatLngs([new LatLng(destination1.geometry.coordinates[1], destination1.geometry.coordinates[0]), new LatLng(this.#position.lat, this.#position.lng), new LatLng(destination2.geometry.coordinates[1], destination2.geometry.coordinates[0])])
|
||||
} else {
|
||||
this.#targetPositionPolyline.setStyle({dashArray: ""});
|
||||
this.#targetPositionPolyline.setLatLngs([new LatLng(this.#position.lat, this.#position.lng), new LatLng(targetPosition.lat, targetPosition.lng)])
|
||||
}
|
||||
}
|
||||
|
||||
#clearTarget() {
|
||||
#clearTargetPosition() {
|
||||
if (getApp().getMap().hasLayer(this.#targetPositionMarker))
|
||||
this.#targetPositionMarker.removeFrom(getApp().getMap());
|
||||
|
||||
@@ -1492,7 +1462,7 @@ export abstract class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
#onZoom(e: any) {
|
||||
if (this.checkZoomRedraw())
|
||||
if (this.checkZoomRedraw())
|
||||
this.#redrawMarker();
|
||||
this.#updateMarker();
|
||||
}
|
||||
@@ -1516,35 +1486,24 @@ export abstract class AirUnit extends Unit {
|
||||
};
|
||||
}
|
||||
|
||||
getActions() {
|
||||
var options: { [key: string]: { text: string, tooltip: string, type: string } } = {};
|
||||
|
||||
/* Options if this unit is not selected */
|
||||
if (!this.getSelected()) {
|
||||
/* Someone else is selected */
|
||||
if (getApp().getUnitsManager().getSelectedUnits().length > 0) {
|
||||
options["attack"] = { text: "Attack", tooltip: "Attack the unit using A/A or A/G weapons", type: "or" };
|
||||
options["follow"] = { text: "Follow", tooltip: "Follow the unit at a user defined distance and position", type: "or" };
|
||||
} else {
|
||||
options["center-map"] = { text: "Center map", tooltip: "Center the map on the unit and follow it", type: "and" };
|
||||
appendContextActions(contextActionSet: ContextActionSet, targetUnit: Unit | null, targetPosition: LatLng | null) {
|
||||
if (targetUnit !== null) {
|
||||
if (targetUnit != this) {
|
||||
contextActionSet.addContextAction(this, "attack", "Attack unit", "Attack the unit using A/A or A/G weapons", (units: Unit[]) => { getApp().getUnitsManager().attackUnit(targetUnit.ID, units) });
|
||||
contextActionSet.addContextAction(this, "follow", "Follow unit", "Follow this unit in formation", (units: Unit[]) => { targetUnit.showFollowOptions(units); }, false); // Don't hide the context menu after the execution (to show the follow options)
|
||||
}
|
||||
if (targetUnit.getSelected()) {
|
||||
contextActionSet.addContextAction(this, "refuel", "Refuel", "Refuel units at the nearest AAR Tanker. If no tanker is available the unit will RTB", (units: Unit[]) => { getApp().getUnitsManager().refuel(units) });
|
||||
}
|
||||
if (getApp().getUnitsManager().getSelectedUnits().length == 1 && targetUnit === this) {
|
||||
contextActionSet.addContextAction(this, "center-map", "Center map", "Center the map on the unit and follow it", () => { getApp().getMap().centerOnUnit(this.ID); });
|
||||
}
|
||||
}
|
||||
/* Options if this unit is selected*/
|
||||
else if (this.getSelected()) {
|
||||
/* This is the only selected unit */
|
||||
if (getApp().getUnitsManager().getSelectedUnits().length == 1) {
|
||||
options["center-map"] = { text: "Center map", tooltip: "Center the map on the unit and follow it", type: "and" };
|
||||
} else {
|
||||
options["follow"] = { text: "Follow", tooltip: "Follow the unit at a user defined distance and position", type: "or" };
|
||||
}
|
||||
|
||||
options["refuel"] = { text: "Air to air refuel", tooltip: "Refuel units at the nearest AAR Tanker. If no tanker is available the unit will RTB.", type: "and" }; // TODO Add some way of knowing which aircraft can AAR
|
||||
if (targetPosition !== null) {
|
||||
contextActionSet.addContextAction(this, "bomb", "Precision bombing", "Precision bombing of a specific point", (units: Unit[]) => { getApp().getUnitsManager().bombPoint(targetPosition, units) });
|
||||
contextActionSet.addContextAction(this, "carpet-bomb", "Carpet bombing", "Carpet bombing close to a point", (units: Unit[]) => { getApp().getUnitsManager().carpetBomb(targetPosition, units) });
|
||||
}
|
||||
/* All other options */
|
||||
else {
|
||||
/* Provision */
|
||||
}
|
||||
return options;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1556,6 +1515,22 @@ export class Aircraft extends AirUnit {
|
||||
getCategory() {
|
||||
return "Aircraft";
|
||||
}
|
||||
|
||||
appendContextActions(contextActionSet: ContextActionSet, targetUnit: Unit | null, targetPosition: LatLng | null) {
|
||||
super.appendContextActions(contextActionSet, targetUnit, targetPosition);
|
||||
|
||||
if (targetPosition === null && this.getSelected()) {
|
||||
contextActionSet.addContextAction(this, "refuel", "Refuel", "Refuel units at the nearest AAR Tanker. If no tanker is available the unit will RTB", (units: Unit[]) => { getApp().getUnitsManager().refuel(units) });
|
||||
}
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
return "aircraft";
|
||||
}
|
||||
|
||||
getDefaultMarker() {
|
||||
return "aircraft";
|
||||
}
|
||||
}
|
||||
|
||||
export class Helicopter extends AirUnit {
|
||||
@@ -1566,6 +1541,21 @@ export class Helicopter extends AirUnit {
|
||||
getCategory() {
|
||||
return "Helicopter";
|
||||
}
|
||||
|
||||
appendContextActions(contextActionSet: ContextActionSet, targetUnit: Unit | null, targetPosition: LatLng | null) {
|
||||
super.appendContextActions(contextActionSet, targetUnit, targetPosition);
|
||||
|
||||
if (targetPosition !== null)
|
||||
contextActionSet.addContextAction(this, "land-at-point", "Land here", "land at this precise location", (units: Unit[]) => { getApp().getUnitsManager().landAtPoint(targetPosition, units) });
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
return "helicopter";
|
||||
}
|
||||
|
||||
getDefaultMarker() {
|
||||
return "helicopter";
|
||||
}
|
||||
}
|
||||
|
||||
export class GroundUnit extends Unit {
|
||||
@@ -1590,37 +1580,35 @@ export class GroundUnit extends Unit {
|
||||
};
|
||||
}
|
||||
|
||||
getActions() {
|
||||
var options: { [key: string]: { text: string, tooltip: string, type: string } } = {};
|
||||
appendContextActions(contextActionSet: ContextActionSet, targetUnit: Unit | null, targetPosition: LatLng | null) {
|
||||
contextActionSet.addContextAction(this, "group-ground", "Group ground units", "Create a group of ground units", (units: Unit[]) => { getApp().getUnitsManager().createGroup(units) });
|
||||
|
||||
/* Options if this unit is not selected */
|
||||
if (!this.getSelected()) {
|
||||
/* Someone else is selected */
|
||||
if (getApp().getUnitsManager().getSelectedUnits().length > 0) {
|
||||
options["attack"] = { text: "Attack", tooltip: "Attack the unit using A/A or A/G weapons", type: "or" };
|
||||
} else {
|
||||
options["center-map"] = { text: "Center map", tooltip: "Center the map on the unit and follow it", type: "and" };
|
||||
}
|
||||
}
|
||||
/* Options if this unit is selected*/
|
||||
else if (this.getSelected()) {
|
||||
/* This is the only selected unit */
|
||||
if (getApp().getUnitsManager().getSelectedUnits().length == 1) {
|
||||
options["center-map"] = { text: "Center map", tooltip: "Center the map on the unit and follow it", type: "and" };
|
||||
} else {
|
||||
options["group-ground"] = { text: "Create group", tooltip: "Create a group from the selected units", type: "and" };
|
||||
if (targetUnit !== null) {
|
||||
if (targetUnit != this) {
|
||||
contextActionSet.addContextAction(this, "attack", "Attack unit", "Attack the unit using A/A or A/G weapons", (units: Unit[]) => { getApp().getUnitsManager().attackUnit(targetUnit.ID, units) });
|
||||
}
|
||||
|
||||
if (this.canAAA()) {
|
||||
options["scenic-aaa"] = { text: "Scenic AAA", tooltip: "Shoot AAA in the air without aiming at any target, when a enemy unit gets close enough. WARNING: works correctly only on neutral units, blue or red units will aim", type: "and" };
|
||||
options["miss-aaa"] = { text: "Miss on purpose AAA", tooltip: "Shoot AAA towards the closest enemy unit, but don't aim precisely. WARNING: works correctly only on neutral units, blue or red units will aim", type: "and" };
|
||||
if (getApp().getUnitsManager().getSelectedUnits().length == 1 && targetUnit === this) {
|
||||
contextActionSet.addContextAction(this, "center-map", "Center map", "Center the map on the unit and follow it", () => { getApp().getMap().centerOnUnit(this.ID); });
|
||||
}
|
||||
}
|
||||
|
||||
if (targetPosition !== null) {
|
||||
if (this.canTargetPoint()) {
|
||||
contextActionSet.addContextAction(this, "fire-at-area", "Fire at area", "Fire at a specific area on the ground", (units: Unit[]) => { getApp().getUnitsManager().fireAtArea(targetPosition, units) });
|
||||
contextActionSet.addContextAction(this, "simulate-fire-fight", "Simulate fire fight", "Simulate a fire fight by shooting randomly in a certain large area.\nWARNING: works correctly only on neutral units, blue or red units will aim", (units: Unit[]) => { getApp().getUnitsManager().simulateFireFight(targetPosition, units) });
|
||||
}
|
||||
}
|
||||
/* All other options */
|
||||
else {
|
||||
/* Provision */
|
||||
if (this.canAAA()) {
|
||||
contextActionSet.addContextAction(this, "scenic-aaa", "Scenic AAA", "Shoot AAA in the air without aiming at any target, when a enemy unit gets close enough.\nWARNING: works correctly only on neutral units, blue or red units will aim", (units: Unit[]) => { getApp().getUnitsManager().scenicAAA(units) }, undefined, {
|
||||
"isScenic": true
|
||||
});
|
||||
contextActionSet.addContextAction(this, "miss-aaa", "Miss on purpose", "Shoot AAA towards the closest enemy unit, but don't aim precisely.\nWARNING: works correctly only on neutral units, blue or red units will aim", (units: Unit[]) => { getApp().getUnitsManager().missOnPurpose(units) }, undefined, {
|
||||
"isScenic": true
|
||||
});
|
||||
}
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
getCategory() {
|
||||
@@ -1645,9 +1633,9 @@ export class GroundUnit extends Unit {
|
||||
unitWhenGrouped = (member !== null ? member?.getDatabaseEntry()?.unitWhenGrouped : unitWhenGrouped);
|
||||
}
|
||||
if (unitWhenGrouped)
|
||||
return this.getDatabase()?.getByName(unitWhenGrouped);
|
||||
return this.getDatabase()?.getByName(unitWhenGrouped) ?? this.getDatabase()?.getUnkownUnit(this.getName());
|
||||
else
|
||||
return this.getDatabase()?.getByName(this.getName());
|
||||
return this.getDatabase()?.getByName(this.getName()) ?? this.getDatabase()?.getUnkownUnit(this.getName());
|
||||
}
|
||||
|
||||
/* When we zoom past the grouping limit, grouping is enabled and the unit is a leader, we redraw the unit to apply any possible grouped marker */
|
||||
@@ -1656,6 +1644,17 @@ export class GroundUnit extends Unit {
|
||||
(getApp().getMap().getZoom() >= GROUPING_ZOOM_TRANSITION && getApp().getMap().getPreviousZoom() < GROUPING_ZOOM_TRANSITION ||
|
||||
getApp().getMap().getZoom() < GROUPING_ZOOM_TRANSITION && getApp().getMap().getPreviousZoom() >= GROUPING_ZOOM_TRANSITION))
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
if (/\bAAA|SAM\b/.test(this.getType()) || /\bmanpad|stinger\b/i.test(this.getType()))
|
||||
return "groundunit-sam";
|
||||
else
|
||||
return "groundunit";
|
||||
}
|
||||
|
||||
getDefaultMarker() {
|
||||
return "groundunit";
|
||||
}
|
||||
}
|
||||
|
||||
export class NavyUnit extends Unit {
|
||||
@@ -1680,36 +1679,21 @@ export class NavyUnit extends Unit {
|
||||
};
|
||||
}
|
||||
|
||||
getActions() {
|
||||
var options: { [key: string]: { text: string, tooltip: string, type: string } } = {};
|
||||
appendContextActions(contextActionSet: ContextActionSet, targetUnit: Unit | null, targetPosition: LatLng | null) {
|
||||
contextActionSet.addContextAction(this, "group-navy", "Group navy units", "Create a group of navy units", (units: Unit[]) => { getApp().getUnitsManager().createGroup(units) });
|
||||
|
||||
/* Options if this unit is not selected */
|
||||
if (!this.getSelected()) {
|
||||
/* Someone else is selected */
|
||||
if (getApp().getUnitsManager().getSelectedUnits().length > 0) {
|
||||
options["attack"] = { text: "Attack", tooltip: "Attack the unit using A/A or A/G weapons", type: "or" };
|
||||
} else {
|
||||
options["center-map"] = { text: "Center map", tooltip: "Center the map on the unit and follow it", type: "and" };
|
||||
if (targetUnit !== null) {
|
||||
if (targetUnit != this) {
|
||||
contextActionSet.addContextAction(this, "attack", "Attack unit", "Attack the unit using A/A or A/G weapons", (units: Unit[]) => { getApp().getUnitsManager().attackUnit(targetUnit.ID, units) });
|
||||
}
|
||||
if (getApp().getUnitsManager().getSelectedUnits().length == 1 && targetUnit === this) {
|
||||
contextActionSet.addContextAction(this, "center-map", "Center map", "Center the map on the unit and follow it", () => { getApp().getMap().centerOnUnit(this.ID); });
|
||||
}
|
||||
}
|
||||
/* Options if this unit is selected */
|
||||
else if (this.getSelected()) {
|
||||
/* This is the only selected unit */
|
||||
if (getApp().getUnitsManager().getSelectedUnits().length == 1) {
|
||||
options["center-map"] = { text: "Center map", tooltip: "Center the map on the unit and follow it", type: "and" };
|
||||
} else {
|
||||
options["group-navy"] = { text: "Create group", tooltip: "Create a group from the selected units", type: "and" };
|
||||
}
|
||||
}
|
||||
/* All other options */
|
||||
else {
|
||||
/* Provision */
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
return "navyunit";
|
||||
if (targetPosition !== null) {
|
||||
contextActionSet.addContextAction(this, "fire-at-area", "Fire at area", "Fire at a specific area on the ground", (units: Unit[]) => { getApp().getUnitsManager().fireAtArea(targetPosition, units) });
|
||||
}
|
||||
}
|
||||
|
||||
getCategory() {
|
||||
@@ -1720,4 +1704,12 @@ export class NavyUnit extends Unit {
|
||||
var blueprint = navyUnitDatabase.getByName(this.getName());
|
||||
return blueprint?.type ? blueprint.type : "";
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
return "navyunit";
|
||||
}
|
||||
|
||||
getDefaultMarker() {
|
||||
return "navyunit";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ import { CoalitionArea } from "../map/coalitionarea/coalitionarea";
|
||||
import { groundUnitDatabase } from "./databases/groundunitdatabase";
|
||||
import { DELETE_CYCLE_TIME, DELETE_SLOW_THRESHOLD, DataIndexes, GAME_MASTER, IADSDensities, IDLE, MOVE_UNIT } from "../constants/constants";
|
||||
import { DataExtractor } from "../server/dataextractor";
|
||||
import { citiesDatabase } from "./citiesDatabase";
|
||||
import { citiesDatabase } from "./databases/citiesdatabase";
|
||||
import { aircraftDatabase } from "./databases/aircraftdatabase";
|
||||
import { helicopterDatabase } from "./databases/helicopterdatabase";
|
||||
import { navyUnitDatabase } from "./databases/navyunitdatabase";
|
||||
@@ -40,15 +40,15 @@ export class UnitsManager {
|
||||
|
||||
document.addEventListener('commandModeOptionsChanged', () => { Object.values(this.#units).forEach((unit: Unit) => unit.updateVisibility()) });
|
||||
document.addEventListener('contactsUpdated', (e: CustomEvent) => { this.#requestDetectionUpdate = true });
|
||||
document.addEventListener('copy', () => this.selectedUnitsCopy());
|
||||
document.addEventListener('deleteSelectedUnits', () => this.selectedUnitsDelete());
|
||||
document.addEventListener('explodeSelectedUnits', (e: any) => this.selectedUnitsDelete(true, e.detail.type));
|
||||
document.addEventListener('copy', () => this.copy());
|
||||
document.addEventListener('deleteSelectedUnits', () => this.delete());
|
||||
document.addEventListener('explodeSelectedUnits', (e: any) => this.delete(true, e.detail.type));
|
||||
document.addEventListener('exportToFile', () => this.exportToFile());
|
||||
document.addEventListener('importFromFile', () => this.importFromFile());
|
||||
document.addEventListener('keyup', (event) => this.#onKeyUp(event));
|
||||
document.addEventListener('paste', () => this.pasteUnits());
|
||||
document.addEventListener('selectedUnitsChangeAltitude', (e: any) => { this.selectedUnitsChangeAltitude(e.detail.type) });
|
||||
document.addEventListener('selectedUnitsChangeSpeed', (e: any) => { this.selectedUnitsChangeSpeed(e.detail.type) });
|
||||
document.addEventListener('paste', () => this.paste());
|
||||
document.addEventListener('selectedUnitsChangeAltitude', (e: any) => { this.changeAltitude(e.detail.type) });
|
||||
document.addEventListener('selectedUnitsChangeSpeed', (e: any) => { this.changeSpeed(e.detail.type) });
|
||||
document.addEventListener('unitDeselection', (e: CustomEvent) => this.#onUnitDeselection(e.detail));
|
||||
document.addEventListener('unitSelection', (e: CustomEvent) => this.#onUnitSelection(e.detail));
|
||||
document.addEventListener("toggleMarkerProtection", (ev: CustomEventInit) => { this.#showNumberOfSelectedProtectedUnits() });
|
||||
@@ -134,8 +134,8 @@ export class UnitsManager {
|
||||
this.#units[ID]?.setData(dataExtractor);
|
||||
}
|
||||
|
||||
/* Update the unit groups */
|
||||
for (let ID in this.#units) {
|
||||
/* Update the unit groups */
|
||||
for (let ID in this.#units) {
|
||||
const unit = this.#units[ID];
|
||||
const groupName = unit.getGroupName();
|
||||
|
||||
@@ -145,7 +145,7 @@ export class UnitsManager {
|
||||
this.#groups[groupName] = new Group(groupName);
|
||||
|
||||
/* If the unit was not assigned to a group yet, assign it */
|
||||
if (unit.getGroup() === null)
|
||||
if (unit.getGroup() === null)
|
||||
this.#groups[groupName].addMember(unit);
|
||||
}
|
||||
}
|
||||
@@ -264,6 +264,10 @@ export class UnitsManager {
|
||||
if (options.showProtectionReminder === true && numProtectedUnits > selectedUnits.length && selectedUnits.length === 0) {
|
||||
const messageText = (numProtectedUnits === 1) ? `Unit is protected` : `All selected units are protected`;
|
||||
(getApp().getPopupsManager().get("infoPopup") as Popup).setText(messageText);
|
||||
// Cheap way for now until we use more locks
|
||||
let lock = <HTMLElement>document.querySelector("#unit-visibility-control button.lock");
|
||||
lock.classList.add("prompt");
|
||||
setTimeout(() => lock.classList.remove("prompt"), 4000);
|
||||
}
|
||||
|
||||
if (options.onlyOnePerGroup) {
|
||||
@@ -321,11 +325,13 @@ export class UnitsManager {
|
||||
getUnitsVariable(variableGetter: CallableFunction, units: Unit[]) {
|
||||
if (units.length == 0)
|
||||
return undefined;
|
||||
return units.map((unit: Unit) => {
|
||||
return variableGetter(unit);
|
||||
})?.reduce((a: any, b: any) => {
|
||||
return a === b ? a : undefined
|
||||
|
||||
var value: any = variableGetter(units[0]);
|
||||
units.forEach((unit: Unit) => {
|
||||
if (variableGetter(unit) !== value)
|
||||
value = undefined;
|
||||
});
|
||||
return value;
|
||||
};
|
||||
|
||||
/** For a given unit, it returns if and how it is being detected by other units. NOTE: this function will return how a unit is being detected, i.e. how other units are detecting it. It will not return
|
||||
@@ -353,23 +359,23 @@ export class UnitsManager {
|
||||
* @param latlng Position of the new destination
|
||||
* @param mantainRelativePosition If true, the selected units will mantain their relative positions when reaching the target. This is useful to maintain a formation for groun/navy units
|
||||
* @param rotation Rotation in radians by which the formation will be rigidly rotated. E.g. a ( V ) formation will look like this ( < ) if rotated pi/4 radians (90 degrees)
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsAddDestination(latlng: L.LatLng, mantainRelativePosition: boolean, rotation: number) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
addDestination(latlng: L.LatLng, mantainRelativePosition: boolean, rotation: number, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (selectedUnits.length === 0)
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
/* Compute the destination for each unit. If mantainRelativePosition is true, compute the destination so to hold the relative positions */
|
||||
var unitDestinations: { [key: number]: LatLng } = {};
|
||||
if (mantainRelativePosition)
|
||||
unitDestinations = this.selectedUnitsComputeGroupDestination(latlng, rotation);
|
||||
unitDestinations = this.computeGroupDestination(latlng, rotation);
|
||||
else
|
||||
selectedUnits.forEach((unit: Unit) => { unitDestinations[unit.ID] = latlng; });
|
||||
|
||||
for (let idx in selectedUnits) {
|
||||
const unit = selectedUnits[idx];
|
||||
units.forEach((unit: Unit) => { unitDestinations[unit.ID] = latlng; });
|
||||
|
||||
units.forEach((unit: Unit) => {
|
||||
/* If a unit is following another unit, and that unit is also selected, send the command to the followed ("leader") unit */
|
||||
if (unit.getState() === "follow") {
|
||||
const leader = this.getUnitByID(unit.getLeaderID())
|
||||
@@ -382,18 +388,22 @@ export class UnitsManager {
|
||||
if (unit.ID in unitDestinations)
|
||||
unit.addDestination(unitDestinations[unit.ID]);
|
||||
}
|
||||
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, " new destination added");
|
||||
});
|
||||
this.#showActionMessage(units, " new destination added");
|
||||
}
|
||||
|
||||
/** Clear the destinations of all the selected units
|
||||
*
|
||||
*/
|
||||
selectedUnitsClearDestinations() {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: false });
|
||||
for (let idx in selectedUnits) {
|
||||
const unit = selectedUnits[idx];
|
||||
clearDestinations(units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: false });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
for (let idx in units) {
|
||||
const unit = units[idx];
|
||||
if (unit.getState() === "follow") {
|
||||
const leader = this.getUnitByID(unit.getLeaderID())
|
||||
if (leader && leader.getSelected())
|
||||
@@ -409,179 +419,239 @@ export class UnitsManager {
|
||||
/** Instruct all the selected units to land at a specific location
|
||||
*
|
||||
* @param latlng Location where to land at
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsLandAt(latlng: LatLng) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].landAt(latlng);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, " landing");
|
||||
landAt(latlng: LatLng, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
units.forEach((unit: Unit) => unit.landAt(latlng));
|
||||
|
||||
this.#showActionMessage(units, " landing");
|
||||
}
|
||||
|
||||
/** Instruct all the selected units to change their speed
|
||||
*
|
||||
* @param speedChange Speed change, either "stop", "slow", or "fast". The specific value depends on the unit category
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsChangeSpeed(speedChange: string) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].changeSpeed(speedChange);
|
||||
}
|
||||
changeSpeed(speedChange: string, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
units.forEach((unit: Unit) => unit.changeSpeed(speedChange));
|
||||
}
|
||||
|
||||
/** Instruct all the selected units to change their altitude
|
||||
*
|
||||
* @param altitudeChange Altitude change, either "climb" or "descend". The specific value depends on the unit category
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsChangeAltitude(altitudeChange: string) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].changeAltitude(altitudeChange);
|
||||
}
|
||||
changeAltitude(altitudeChange: string, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
units.forEach((unit: Unit) => unit.changeAltitude(altitudeChange));
|
||||
}
|
||||
|
||||
/** Set a specific speed to all the selected units
|
||||
*
|
||||
* @param speed Value to set, in m/s
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsSetSpeed(speed: number) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].setSpeed(speed);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `setting speed to ${msToKnots(speed)} kts`);
|
||||
setSpeed(speed: number, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
units.forEach((unit: Unit) => unit.setSpeed(speed));
|
||||
this.#showActionMessage(units, `setting speed to ${msToKnots(speed)} kts`);
|
||||
}
|
||||
|
||||
/** Set a specific speed type to all the selected units
|
||||
*
|
||||
* @param speedType Value to set, either "CAS" or "GS". If "CAS" is selected, the unit will try to maintain the selected Calibrated Air Speed, but DCS will still only maintain a Ground Speed value so errors may arise depending on wind.
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsSetSpeedType(speedType: string) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].setSpeedType(speedType);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `setting speed type to ${speedType}`);
|
||||
setSpeedType(speedType: string, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
units.forEach((unit: Unit) => unit.setSpeedType(speedType));
|
||||
this.#showActionMessage(units, `setting speed type to ${speedType}`);
|
||||
}
|
||||
|
||||
/** Set a specific altitude to all the selected units
|
||||
*
|
||||
* @param altitude Value to set, in m
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsSetAltitude(altitude: number) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].setAltitude(altitude);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `setting altitude to ${mToFt(altitude)} ft`);
|
||||
setAltitude(altitude: number, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
units.forEach((unit: Unit) => unit.setAltitude(altitude));
|
||||
this.#showActionMessage(units, `setting altitude to ${mToFt(altitude)} ft`);
|
||||
}
|
||||
|
||||
/** Set a specific altitude type to all the selected units
|
||||
*
|
||||
* @param altitudeType Value to set, either "ASL" or "AGL". If "AGL" is selected, the unit will try to maintain the selected Above Ground Level altitude. Due to a DCS bug, this will only be true at the final position.
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsSetAltitudeType(altitudeType: string) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].setAltitudeType(altitudeType);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `setting altitude type to ${altitudeType}`);
|
||||
setAltitudeType(altitudeType: string, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
units.forEach((unit: Unit) => unit.setAltitudeType(altitudeType));
|
||||
this.#showActionMessage(units, `setting altitude type to ${altitudeType}`);
|
||||
}
|
||||
|
||||
/** Set a specific ROE to all the selected units
|
||||
*
|
||||
* @param ROE Value to set, see constants for acceptable values
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsSetROE(ROE: string) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].setROE(ROE);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `ROE set to ${ROE}`);
|
||||
setROE(ROE: string, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
units.forEach((unit: Unit) => unit.setROE(ROE));
|
||||
this.#showActionMessage(units, `ROE set to ${ROE}`);
|
||||
}
|
||||
|
||||
/** Set a specific reaction to threat to all the selected units
|
||||
*
|
||||
* @param reactionToThreat Value to set, see constants for acceptable values
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsSetReactionToThreat(reactionToThreat: string) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].setReactionToThreat(reactionToThreat);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `reaction to threat set to ${reactionToThreat}`);
|
||||
setReactionToThreat(reactionToThreat: string, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
units.forEach((unit: Unit) => unit.setReactionToThreat(reactionToThreat));
|
||||
this.#showActionMessage(units, `reaction to threat set to ${reactionToThreat}`);
|
||||
}
|
||||
|
||||
/** Set a specific emissions & countermeasures to all the selected units
|
||||
*
|
||||
* @param emissionCountermeasure Value to set, see constants for acceptable values
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsSetEmissionsCountermeasures(emissionCountermeasure: string) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].setEmissionsCountermeasures(emissionCountermeasure);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `emissions & countermeasures set to ${emissionCountermeasure}`);
|
||||
setEmissionsCountermeasures(emissionCountermeasure: string, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
units.forEach((unit: Unit) => unit.setEmissionsCountermeasures(emissionCountermeasure));
|
||||
this.#showActionMessage(units, `emissions & countermeasures set to ${emissionCountermeasure}`);
|
||||
}
|
||||
|
||||
/** Turn selected units on or off, only works on ground and navy units
|
||||
*
|
||||
* @param onOff If true, the unit will be turned on
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsSetOnOff(onOff: boolean) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].setOnOff(onOff);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `unit active set to ${onOff}`);
|
||||
setOnOff(onOff: boolean, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
units.forEach((unit: Unit) => unit.setOnOff(onOff));
|
||||
this.#showActionMessage(units, `unit active set to ${onOff}`);
|
||||
}
|
||||
|
||||
/** Instruct the selected units to follow roads, only works on ground units
|
||||
*
|
||||
* @param followRoads If true, units will follow roads
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsSetFollowRoads(followRoads: boolean) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].setFollowRoads(followRoads);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `follow roads set to ${followRoads}`);
|
||||
setFollowRoads(followRoads: boolean, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
units.forEach((unit: Unit) => unit.setFollowRoads(followRoads));
|
||||
this.#showActionMessage(units, `follow roads set to ${followRoads}`);
|
||||
}
|
||||
|
||||
/** Instruct selected units to operate as a certain coalition
|
||||
*
|
||||
* @param operateAsBool If true, units will operate as blue
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsSetOperateAs(operateAsBool: boolean) {
|
||||
setOperateAs(operateAsBool: boolean, units: Unit[] | null = null) {
|
||||
var operateAs = operateAsBool ? "blue" : "red";
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].setOperateAs(operateAs);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `operate as set to ${operateAs}`);
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
units.forEach((unit: Unit) => unit.setOperateAs(operateAs));
|
||||
this.#showActionMessage(units, `operate as set to ${operateAs}`);
|
||||
}
|
||||
|
||||
/** Instruct units to attack a specific unit
|
||||
*
|
||||
* @param ID ID of the unit to attack
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsAttackUnit(ID: number) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].attackUnit(ID);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `attacking unit ${this.getUnitByID(ID)?.getUnitName()}`);
|
||||
attackUnit(ID: number, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
units.forEach((unit: Unit) => unit.attackUnit(ID));
|
||||
this.#showActionMessage(units, `attacking unit ${this.getUnitByID(ID)?.getUnitName()}`);
|
||||
}
|
||||
|
||||
/** Instruct units to refuel at the nearest tanker, if possible. Else units will RTB
|
||||
*
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsRefuel() {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].refuel();
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `sent to nearest tanker`);
|
||||
refuel(units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
units.forEach((unit: Unit) => unit.refuel());
|
||||
this.#showActionMessage(units, `sent to nearest tanker`);
|
||||
}
|
||||
|
||||
/** Instruct the selected units to follow another unit in a formation. Only works for aircrafts and helicopters.
|
||||
@@ -589,8 +659,15 @@ export class UnitsManager {
|
||||
* @param ID ID of the unit to follow
|
||||
* @param offset Optional parameter, defines a static offset. X: front-rear, positive front, Y: top-bottom, positive top, Z: left-right, positive right
|
||||
* @param formation Optional parameter, defines a predefined formation type. Values are: "trail", "echelon-lh", "echelon-rh", "line-abreast-lh", "line-abreast-rh", "front", "diamond"
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsFollowUnit(ID: number, offset?: { "x": number, "y": number, "z": number }, formation?: string) {
|
||||
followUnit(ID: number, offset?: { "x": number, "y": number, "z": number }, formation?: string, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
if (offset == undefined) {
|
||||
/* Simple formations with fixed offsets */
|
||||
offset = { "x": 0, "y": 0, "z": 0 };
|
||||
@@ -603,16 +680,11 @@ export class UnitsManager {
|
||||
else offset = undefined;
|
||||
}
|
||||
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (selectedUnits.length === 0)
|
||||
return;
|
||||
|
||||
var count = 1;
|
||||
var xr = 0; var yr = 1; var zr = -1;
|
||||
var layer = 1;
|
||||
for (let idx in selectedUnits) {
|
||||
var unit = selectedUnits[idx];
|
||||
for (let idx in units) {
|
||||
var unit = units[idx];
|
||||
if (unit.ID !== ID) {
|
||||
if (offset != undefined)
|
||||
/* Offset is set, apply it */
|
||||
@@ -634,51 +706,69 @@ export class UnitsManager {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `following unit ${this.getUnitByID(ID)?.getUnitName()}`);
|
||||
this.#showActionMessage(units, `following unit ${this.getUnitByID(ID)?.getUnitName()}`);
|
||||
}
|
||||
|
||||
/** Instruct the selected units to perform precision bombing of specific coordinates
|
||||
*
|
||||
* @param latlng Location to bomb
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsBombPoint(latlng: LatLng) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].bombPoint(latlng);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `unit bombing point`);
|
||||
bombPoint(latlng: LatLng, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
units.forEach((unit: Unit) => unit.bombPoint(latlng));
|
||||
this.#showActionMessage(units, `unit bombing point`);
|
||||
}
|
||||
|
||||
/** Instruct the selected units to perform carpet bombing of specific coordinates
|
||||
*
|
||||
* @param latlng Location to bomb
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsCarpetBomb(latlng: LatLng) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].carpetBomb(latlng);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `unit carpet bombing point`);
|
||||
carpetBomb(latlng: LatLng, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
units.forEach((unit: Unit) => unit.carpetBomb(latlng));
|
||||
this.#showActionMessage(units, `unit carpet bombing point`);
|
||||
}
|
||||
|
||||
/** Instruct the selected units to fire at specific coordinates
|
||||
*
|
||||
* @param latlng Location to fire at
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsFireAtArea(latlng: LatLng) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].fireAtArea(latlng);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `unit firing at area`);
|
||||
fireAtArea(latlng: LatLng, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
units.forEach((unit: Unit) => unit.fireAtArea(latlng));
|
||||
this.#showActionMessage(units, `unit firing at area`);
|
||||
}
|
||||
|
||||
/** Instruct the selected units to simulate a fire fight at specific coordinates
|
||||
*
|
||||
* @param latlng Location to fire at
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsSimulateFireFight(latlng: LatLng) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
simulateFireFight(latlng: LatLng, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
getGroundElevation(latlng, (response: string) => {
|
||||
var groundElevation: number | null = null;
|
||||
try {
|
||||
@@ -686,70 +776,86 @@ export class UnitsManager {
|
||||
} catch {
|
||||
console.warn("Simulate fire fight: could not retrieve ground elevation")
|
||||
}
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].simulateFireFight(latlng, groundElevation);
|
||||
}
|
||||
units?.forEach((unit: Unit) => unit.simulateFireFight(latlng, groundElevation));
|
||||
});
|
||||
this.#showActionMessage(selectedUnits, `unit simulating fire fight`);
|
||||
this.#showActionMessage(units, `unit simulating fire fight`);
|
||||
}
|
||||
|
||||
/** Instruct units to enter into scenic AAA mode. Units will shoot in the air without aiming
|
||||
*
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsScenicAAA() {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].scenicAAA();
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `unit set to perform scenic AAA`);
|
||||
scenicAAA(units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
units.forEach((unit: Unit) => unit.scenicAAA());
|
||||
this.#showActionMessage(units, `unit set to perform scenic AAA`);
|
||||
}
|
||||
|
||||
/** Instruct units to enter into miss on purpose mode. Units will aim to the nearest enemy unit but not precisely.
|
||||
*
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsMissOnPurpose() {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].missOnPurpose();
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `unit set to perform miss-on-purpose AAA`);
|
||||
missOnPurpose(units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
units.forEach((unit: Unit) => unit.missOnPurpose());
|
||||
this.#showActionMessage(units, `unit set to perform miss-on-purpose AAA`);
|
||||
}
|
||||
|
||||
/** Instruct units to land at specific point
|
||||
*
|
||||
* @param latlng Point where to land
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsLandAtPoint(latlng: LatLng) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
landAtPoint(latlng: LatLng, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].landAtPoint(latlng);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `unit landing at point`);
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
units.forEach((unit: Unit) => unit.landAtPoint(latlng));
|
||||
this.#showActionMessage(units, `unit landing at point`);
|
||||
}
|
||||
|
||||
/** Set a specific shots scatter to all the selected units
|
||||
*
|
||||
* @param shotsScatter Value to set
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsSetShotsScatter(shotsScatter: number) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].setShotsScatter(shotsScatter);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `shots scatter set to ${shotsScatter}`);
|
||||
setShotsScatter(shotsScatter: number, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
units.forEach((unit: Unit) => unit.setShotsScatter(shotsScatter));
|
||||
this.#showActionMessage(units, `shots scatter set to ${shotsScatter}`);
|
||||
}
|
||||
|
||||
/** Set a specific shots intensity to all the selected units
|
||||
*
|
||||
* @param shotsScatter Value to set
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsSetShotsIntensity(shotsIntensity: number) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].setShotsIntensity(shotsIntensity);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `shots intensity set to ${shotsIntensity}`);
|
||||
setShotsIntensity(shotsIntensity: number, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
units.forEach((unit: Unit) => unit.setShotsIntensity(shotsIntensity));
|
||||
this.#showActionMessage(units, `shots intensity set to ${shotsIntensity}`);
|
||||
}
|
||||
|
||||
/*********************** Control operations on selected units ************************/
|
||||
@@ -773,15 +879,18 @@ export class UnitsManager {
|
||||
/** Groups the selected units in a single (DCS) group, if all the units have the same category
|
||||
*
|
||||
*/
|
||||
selectedUnitsCreateGroup() {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: false, showProtectionReminder: true });
|
||||
if (this.getUnitsCategories(selectedUnits).length == 1) {
|
||||
var units: { ID: number, location: LatLng }[] = [];
|
||||
for (let idx in selectedUnits) {
|
||||
var unit = selectedUnits[idx];
|
||||
units.push({ ID: unit.ID, location: unit.getPosition() });
|
||||
}
|
||||
getApp().getServerManager().cloneUnits(units, true, 0 /* No spawn points, we delete the original units */);
|
||||
createGroup(units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: false, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
if (this.getUnitsCategories(units).length == 1) {
|
||||
var unitsData: { ID: number, location: LatLng }[] = [];
|
||||
units.forEach((unit: Unit) => unitsData.push({ ID: unit.ID, location: unit.getPosition() }));
|
||||
getApp().getServerManager().cloneUnits(unitsData, true, 0 /* No spawn points, we delete the original units */);
|
||||
this.#showActionMessage(units, `created a group`);
|
||||
} else {
|
||||
(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`Groups can only be created from units of the same category`);
|
||||
}
|
||||
@@ -790,33 +899,40 @@ export class UnitsManager {
|
||||
/** Set the hotgroup for the selected units. It will be the only hotgroup of the unit
|
||||
*
|
||||
* @param hotgroup Hotgroup number
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsSetHotgroup(hotgroup: number) {
|
||||
setHotgroup(hotgroup: number, units: Unit[] | null = null) {
|
||||
this.getUnitsByHotgroup(hotgroup).forEach((unit: Unit) => unit.setHotgroup(null));
|
||||
this.selectedUnitsAddToHotgroup(hotgroup);
|
||||
this.addToHotgroup(hotgroup);
|
||||
}
|
||||
|
||||
/** Add the selected units to a hotgroup. Units can be in multiple hotgroups at the same type
|
||||
*
|
||||
* @param hotgroup Hotgroup number
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
selectedUnitsAddToHotgroup(hotgroup: number) {
|
||||
var selectedUnits = this.getSelectedUnits();
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].setHotgroup(hotgroup);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `added to hotgroup ${hotgroup}`);
|
||||
addToHotgroup(hotgroup: number, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits();
|
||||
units.forEach((unit: Unit) => unit.setHotgroup(hotgroup));
|
||||
this.#showActionMessage(units, `added to hotgroup ${hotgroup}`);
|
||||
(getApp().getPanelsManager().get("hotgroup") as HotgroupPanel).refreshHotgroups();
|
||||
}
|
||||
|
||||
/** Delete the selected units
|
||||
*
|
||||
* @param explosion If true, the unit will be deleted using an explosion
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
* @returns
|
||||
*/
|
||||
selectedUnitsDelete(explosion: boolean = false, explosionType: string = "") {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeProtected: true }); /* Can be applied to humans too */
|
||||
const selectionContainsAHuman = selectedUnits.some((unit: Unit) => {
|
||||
delete(explosion: boolean = false, explosionType: string = "", units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeProtected: true, showProtectionReminder: true }); /* Can be applied to humans too */
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
const selectionContainsAHuman = units.some((unit: Unit) => {
|
||||
return unit.getHuman() === true;
|
||||
});
|
||||
|
||||
@@ -825,14 +941,12 @@ export class UnitsManager {
|
||||
}
|
||||
|
||||
const doDelete = (explosion = false, explosionType = "", immediate = false) => {
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].delete(explosion, explosionType, immediate);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `deleted`);
|
||||
units?.forEach((unit: Unit) => unit.delete(explosion, explosionType, immediate));
|
||||
this.#showActionMessage(units as Unit[], `deleted`);
|
||||
}
|
||||
|
||||
if (selectedUnits.length >= DELETE_SLOW_THRESHOLD)
|
||||
this.#showSlowDeleteDialog(selectedUnits).then((action: any) => {
|
||||
if (units.length >= DELETE_SLOW_THRESHOLD)
|
||||
this.#showSlowDeleteDialog(units).then((action: any) => {
|
||||
if (action === "delete-slow")
|
||||
doDelete(explosion, explosionType, false);
|
||||
else if (action === "delete-immediate")
|
||||
@@ -847,21 +961,28 @@ export class UnitsManager {
|
||||
*
|
||||
* @param latlng Center of the group after the translation
|
||||
* @param rotation Rotation of the group, in radians
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
* @returns Array of positions for each unit, in order
|
||||
*/
|
||||
selectedUnitsComputeGroupDestination(latlng: LatLng, rotation: number) {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true });
|
||||
computeGroupDestination(latlng: LatLng, rotation: number, units: Unit[] | null = null) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return {};
|
||||
|
||||
/* Compute the center of the group */
|
||||
var len = units.length;
|
||||
var center = { x: 0, y: 0 };
|
||||
selectedUnits.forEach((unit: Unit) => {
|
||||
units.forEach((unit: Unit) => {
|
||||
var mercator = latLngToMercator(unit.getPosition().lat, unit.getPosition().lng);
|
||||
center.x += mercator.x / selectedUnits.length;
|
||||
center.y += mercator.y / selectedUnits.length;
|
||||
center.x += mercator.x / len;
|
||||
center.y += mercator.y / len;
|
||||
});
|
||||
|
||||
/* Compute the distances from the center of the group */
|
||||
var unitDestinations: { [key: number]: LatLng } = {};
|
||||
selectedUnits.forEach((unit: Unit) => {
|
||||
units.forEach((unit: Unit) => {
|
||||
var mercator = latLngToMercator(unit.getPosition().lat, unit.getPosition().lng);
|
||||
var distancesFromCenter = { dx: mercator.x - center.x, dy: mercator.y - center.y };
|
||||
|
||||
@@ -883,9 +1004,18 @@ export class UnitsManager {
|
||||
/** Copy the selected units and store their properties in memory
|
||||
*
|
||||
*/
|
||||
selectedUnitsCopy() {
|
||||
copy(units: Unit[] | null = null) {
|
||||
if ( !getApp().getContextManager().getCurrentContext().getAllowUnitCopying() )
|
||||
return;
|
||||
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true });
|
||||
|
||||
if (units.length === 0)
|
||||
return;
|
||||
|
||||
/* A JSON is used to deepcopy the units, creating a "snapshot" of their properties at the time of the copy */
|
||||
this.#copiedUnits = JSON.parse(JSON.stringify(this.getSelectedUnits().map((unit: Unit) => { return unit.getData() }))); /* Can be applied to humans too */
|
||||
this.#copiedUnits = JSON.parse(JSON.stringify(units.map((unit: Unit) => { return unit.getData() }))); /* Can be applied to humans too */
|
||||
(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`${this.#copiedUnits.length} units copied`);
|
||||
}
|
||||
|
||||
@@ -894,7 +1024,10 @@ export class UnitsManager {
|
||||
*
|
||||
* @returns True if units were pasted successfully
|
||||
*/
|
||||
pasteUnits() {
|
||||
paste() {
|
||||
if ( !getApp().getContextManager().getCurrentContext().getAllowUnitPasting() )
|
||||
return;
|
||||
|
||||
let spawnPoints = 0;
|
||||
|
||||
/* If spawns are restricted, check that the user has the necessary spawn points */
|
||||
@@ -1081,7 +1214,7 @@ export class UnitsManager {
|
||||
#onKeyUp(event: KeyboardEvent) {
|
||||
if (!keyEventWasInInput(event)) {
|
||||
if (event.key === "Delete")
|
||||
this.selectedUnitsDelete();
|
||||
this.delete();
|
||||
else if (event.key === "a" && event.ctrlKey)
|
||||
Object.values(this.getUnits()).filter((unit: Unit) => { return !unit.getHidden() }).forEach((unit: Unit) => unit.setSelected(true));
|
||||
}
|
||||
@@ -1130,9 +1263,9 @@ export class UnitsManager {
|
||||
(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`${units[0].getUnitName()} and ${units.length - 1} other units ${message}`);
|
||||
}
|
||||
|
||||
#showSlowDeleteDialog(selectedUnits: Unit[]) {
|
||||
#showSlowDeleteDialog(units: Unit[]) {
|
||||
let button: HTMLButtonElement | null = null;
|
||||
const deletionTime = Math.round(selectedUnits.length * DELETE_CYCLE_TIME).toString();
|
||||
const deletionTime = Math.round(units.length * DELETE_CYCLE_TIME).toString();
|
||||
const dialog = this.#slowDeleteDialog;
|
||||
const element = dialog.getElement();
|
||||
const listener = (ev: MouseEvent) => {
|
||||
@@ -1140,7 +1273,7 @@ export class UnitsManager {
|
||||
button = ev.target;
|
||||
}
|
||||
|
||||
element.querySelectorAll(".deletion-count").forEach(el => el.innerHTML = selectedUnits.length.toString());
|
||||
element.querySelectorAll(".deletion-count").forEach(el => el.innerHTML = units.length.toString());
|
||||
element.querySelectorAll(".deletion-time").forEach(el => el.innerHTML = deletionTime);
|
||||
dialog.show();
|
||||
|
||||
@@ -1160,9 +1293,9 @@ export class UnitsManager {
|
||||
|
||||
#showNumberOfSelectedProtectedUnits() {
|
||||
const map = getApp().getMap();
|
||||
const selectedUnits = this.getSelectedUnits();
|
||||
const numSelectedUnits = selectedUnits.length;
|
||||
const numProtectedUnits = selectedUnits.filter((unit: Unit) => map.unitIsProtected(unit)).length;
|
||||
const units = this.getSelectedUnits();
|
||||
const numSelectedUnits = units.length;
|
||||
const numProtectedUnits = units.filter((unit: Unit) => map.getIsUnitProtected(unit)).length;
|
||||
|
||||
if (numProtectedUnits === 1 && numSelectedUnits === numProtectedUnits)
|
||||
(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`Notice: unit is protected`);
|
||||
@@ -1172,6 +1305,6 @@ export class UnitsManager {
|
||||
}
|
||||
|
||||
#unitIsProtected(unit: Unit) {
|
||||
return getApp().getMap().unitIsProtected(unit)
|
||||
return getApp().getMap().getIsUnitProtected(unit)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user