mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
Merge branch 'main' into 328-add-simple-miss-on-purpose-mode-for-aaa
This commit is contained in:
commit
a0ac9eb285
31
client/@types/olympus/index.d.ts
vendored
31
client/@types/olympus/index.d.ts
vendored
@ -588,11 +588,11 @@ declare module "interfaces" {
|
||||
name?: string;
|
||||
shiftKey?: boolean;
|
||||
}
|
||||
export interface KeyboardShortcutOptions extends ShortcutOptions {
|
||||
export interface ShortcutKeyboardOptions extends ShortcutOptions {
|
||||
code: string;
|
||||
event?: "keydown" | "keyup";
|
||||
}
|
||||
export interface MouseShortcutOptions extends ShortcutOptions {
|
||||
export interface ShortcutMouseOptions extends ShortcutOptions {
|
||||
button: number;
|
||||
event: "mousedown" | "mouseup";
|
||||
}
|
||||
@ -1085,7 +1085,7 @@ declare module "unit/unit" {
|
||||
carpetBomb(latlng: LatLng): void;
|
||||
bombBuilding(latlng: LatLng): void;
|
||||
fireAtArea(latlng: LatLng): void;
|
||||
simulateFireFight(latlng: LatLng, groundElevation: number | null): void;
|
||||
simulateFireFight(latlng: LatLng, targetGroundElevation: number | null): void;
|
||||
/***********************************************/
|
||||
onAdd(map: Map): this;
|
||||
}
|
||||
@ -1493,26 +1493,28 @@ declare module "plugin/pluginmanager" {
|
||||
}
|
||||
}
|
||||
declare module "shortcut/shortcut" {
|
||||
import { KeyboardShortcutOptions, MouseShortcutOptions, ShortcutOptions } from "interfaces";
|
||||
import { ShortcutKeyboardOptions, ShortcutMouseOptions, ShortcutOptions } from "interfaces";
|
||||
export abstract class Shortcut {
|
||||
#private;
|
||||
constructor(config: ShortcutOptions);
|
||||
getConfig(): ShortcutOptions;
|
||||
}
|
||||
export class ShortcutKeyboard extends Shortcut {
|
||||
constructor(config: KeyboardShortcutOptions);
|
||||
constructor(config: ShortcutKeyboardOptions);
|
||||
}
|
||||
export class ShortcutMouse extends Shortcut {
|
||||
constructor(config: MouseShortcutOptions);
|
||||
constructor(config: ShortcutMouseOptions);
|
||||
}
|
||||
}
|
||||
declare module "shortcut/shortcutmanager" {
|
||||
import { ShortcutKeyboardOptions, ShortcutMouseOptions } from "interfaces";
|
||||
import { Manager } from "other/manager";
|
||||
import { Shortcut } from "shortcut/shortcut";
|
||||
export class ShortcutManager extends Manager {
|
||||
#private;
|
||||
constructor();
|
||||
add(name: string, shortcut: Shortcut): this;
|
||||
add(name: string, shortcut: any): this;
|
||||
addKeyboardShortcut(name: string, shortcutKeyboardOptions: ShortcutKeyboardOptions): this;
|
||||
addMouseShortcut(name: string, shortcutMouseOptions: ShortcutMouseOptions): this;
|
||||
getKeysBeingHeld(): string[];
|
||||
keyComboMatches(combo: string[]): boolean;
|
||||
onKeyDown(callback: CallableFunction): void;
|
||||
@ -1967,6 +1969,19 @@ declare module "server/servermanager" {
|
||||
getPaused(): boolean;
|
||||
}
|
||||
}
|
||||
declare module "panels/unitlistpanel" {
|
||||
import { OlympusApp } from "olympusapp";
|
||||
import { Panel } from "panels/panel";
|
||||
export class UnitListPanel extends Panel {
|
||||
#private;
|
||||
constructor(olympusApp: OlympusApp, panelElement: string, contentElement: string);
|
||||
doUpdate(): void;
|
||||
getContentElement(): HTMLElement;
|
||||
startUpdates(): void;
|
||||
stopUpdates(): void;
|
||||
toggle(): void;
|
||||
}
|
||||
}
|
||||
declare module "olympusapp" {
|
||||
import { Map } from "map/map";
|
||||
import { MissionManager } from "mission/missionmanager";
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
import { OlympusPlugin } from "interfaces";
|
||||
|
||||
const SHOW_CONTROL_TIPS = "Show control tips"
|
||||
|
||||
export class ControlTipsPlugin implements OlympusPlugin {
|
||||
@ -41,7 +43,7 @@ export class ControlTipsPlugin implements OlympusPlugin {
|
||||
this.#updateTips();
|
||||
});
|
||||
|
||||
document.addEventListener("unitDeselection", (ev: CustomEvent) => {
|
||||
document.addEventListener("unitDeselection", (ev: CustomEventInit ) => {
|
||||
this.#updateTips();
|
||||
});
|
||||
|
||||
@ -55,7 +57,7 @@ export class ControlTipsPlugin implements OlympusPlugin {
|
||||
this.#updateTips();
|
||||
});
|
||||
|
||||
document.addEventListener("unitSelection", (ev: CustomEvent) => {
|
||||
document.addEventListener("unitSelection", (ev: CustomEventInit ) => {
|
||||
this.#updateTips();
|
||||
});
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
@import url("panels/unitcontrol.css");
|
||||
@import url("panels/unitinfo.css");
|
||||
@import url("panels/logpanel.css");
|
||||
@import url("panels/unitlist.css");
|
||||
|
||||
@import url("other/contextmenus.css");
|
||||
@import url("other/popup.css");
|
||||
|
||||
@ -16,4 +16,25 @@
|
||||
|
||||
#connection-status-panel[data-is-connected] dd::after {
|
||||
background: var(--accent-green);
|
||||
}
|
||||
|
||||
#connection-status-panel[data-is-paused] dt::before {
|
||||
content: "Server paused";
|
||||
}
|
||||
|
||||
#connection-status-panel[data-is-paused] dd {
|
||||
animation: pulse 1s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
#connection-status-panel[data-is-paused] dd::after {
|
||||
background: var(--accent-amber);
|
||||
}
|
||||
@ -248,6 +248,7 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
#advanced-settings-dialog:not([data-show-tanker]) #tanker-checkbox,
|
||||
#advanced-settings-dialog:not([data-show-AWACS]) #AWACS-checkbox,
|
||||
#advanced-settings-dialog:not([data-show-TACAN]) #TACAN-options,
|
||||
#advanced-settings-dialog:not([data-show-radio]) #radio-options {
|
||||
#advanced-settings-dialog:not([data-show-radio]) #radio-options,
|
||||
#advanced-settings-dialog:not([data-show-air-unit-checkboxes]) .air-unit-checkbox {
|
||||
display: none;
|
||||
}
|
||||
92
client/public/stylesheets/panels/unitlist.css
Normal file
92
client/public/stylesheets/panels/unitlist.css
Normal file
@ -0,0 +1,92 @@
|
||||
#unit-list-panel {
|
||||
bottom:20px;
|
||||
display:flex;
|
||||
flex-direction: column;
|
||||
justify-self:center;
|
||||
position: absolute;
|
||||
z-index:999;
|
||||
}
|
||||
|
||||
#unit-list-panel h3 {
|
||||
margin-bottom:4px;
|
||||
}
|
||||
|
||||
#unit-list-panel-content {
|
||||
display:flex;
|
||||
flex-flow: column nowrap;
|
||||
max-height: 200px;
|
||||
row-gap: 4px;
|
||||
}
|
||||
|
||||
.unit-list-unit {
|
||||
border-radius: var( --border-radius-sm );
|
||||
column-gap: 2px;
|
||||
display:flex;
|
||||
flex-flow: row nowrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
|
||||
.unit-list-unit:nth-of-type(even) {
|
||||
background:#ffffff10;
|
||||
overflow:visible;
|
||||
}
|
||||
|
||||
.unit-list-unit.headers {
|
||||
margin-bottom:3px;
|
||||
margin-right:10px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.unit-list-unit.headers [data-sort-field] {
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.unit-list-unit.headers > * {
|
||||
background-color: var( --background-grey );
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.unit-list-unit > * {
|
||||
font-size:13px;
|
||||
overflow: hidden;
|
||||
padding:2px;
|
||||
width:80px;
|
||||
}
|
||||
|
||||
.unit-list-unit :first-child {
|
||||
overflow-x: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
width:150px;
|
||||
}
|
||||
|
||||
.unit-list-unit :first-child:hover {
|
||||
overflow:visible;
|
||||
}
|
||||
|
||||
.unit-list-unit :first-child:hover span {
|
||||
position:relative;
|
||||
z-index:9999;
|
||||
}
|
||||
|
||||
.unit-list-unit :first-child:hover span:hover {
|
||||
background-color: white;
|
||||
color: var( --background-steel );
|
||||
}
|
||||
|
||||
.unit-list-unit :nth-child(2) {
|
||||
width:120px;
|
||||
}
|
||||
|
||||
.unit-list-unit > [data-unit-id] {
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
#unit-list-panel-content > * {
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
#unit-list-panel-content > .unit-list-unit:hover {
|
||||
background-color: var( --background-grey );
|
||||
}
|
||||
@ -1054,7 +1054,9 @@ nav.ol-panel> :last-child {
|
||||
}
|
||||
|
||||
.ol-coalitionarea-handle-icon,
|
||||
.ol-coalitionarea-middle-handle-icon {
|
||||
.ol-coalitionarea-middle-handle-icon,
|
||||
.ol-destination-preview-icon,
|
||||
.ol-destination-preview-handle-icon {
|
||||
pointer-events: none;
|
||||
z-index: 9999;
|
||||
border-radius: 999px;
|
||||
@ -1072,6 +1074,13 @@ nav.ol-panel> :last-child {
|
||||
height: 16px;
|
||||
}
|
||||
|
||||
.ol-destination-preview-handle-icon {
|
||||
background-color: #247be2;
|
||||
border: 2px solid white;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
dl.ol-data-grid {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
--unit-background-red: #FF5858;
|
||||
|
||||
/*** UI Colours **/
|
||||
--accent-amber: #ffd828;
|
||||
--accent-green: #8bff63;
|
||||
--accent-light-blue: #5ca7ff;
|
||||
--transparent-accent-light-blue: rgba(92, 167, 255, .33);
|
||||
|
||||
@ -259,12 +259,12 @@ export interface ShortcutOptions {
|
||||
shiftKey?: boolean;
|
||||
}
|
||||
|
||||
export interface KeyboardShortcutOptions extends ShortcutOptions {
|
||||
export interface ShortcutKeyboardOptions extends ShortcutOptions {
|
||||
code: string;
|
||||
event?: "keydown" | "keyup";
|
||||
}
|
||||
|
||||
export interface MouseShortcutOptions extends ShortcutOptions {
|
||||
export interface ShortcutMouseOptions extends ShortcutOptions {
|
||||
button: number;
|
||||
event: "mousedown" | "mouseup";
|
||||
}
|
||||
|
||||
@ -21,6 +21,7 @@ import { AirbaseSpawnContextMenu } from "../contextmenus/airbasespawnmenu";
|
||||
import { Popup } from "../popups/popup";
|
||||
import { GestureHandling } from "leaflet-gesture-handling";
|
||||
import { TouchBoxSelect } from "./touchboxselect";
|
||||
import { DestinationPreviewHandle } from "./markers/destinationpreviewHandle";
|
||||
|
||||
var hasTouchScreen = false;
|
||||
if ("maxTouchPoints" in navigator)
|
||||
@ -67,6 +68,8 @@ export class Map extends L.Map {
|
||||
#targetCursor: TargetMarker = new TargetMarker(new L.LatLng(0, 0), { interactive: false });
|
||||
#destinationPreviewCursors: DestinationPreviewMarker[] = [];
|
||||
#drawingCursor: DrawingCursor = new DrawingCursor();
|
||||
#destinationPreviewHandle: DestinationPreviewHandle = new DestinationPreviewHandle(new L.LatLng(0, 0));
|
||||
#destinationPreviewHandleLine: L.Polyline = new L.Polyline([], { color: "#000000", weight: 3, opacity: 0.5, smoothFactor: 1, dashArray: "4, 8" });
|
||||
#longPressHandled: boolean = false;
|
||||
#longPressTimer: number = 0;
|
||||
|
||||
@ -133,6 +136,7 @@ export class Map extends L.Map {
|
||||
this.on("click", (e: any) => this.#onClick(e));
|
||||
this.on("dblclick", (e: any) => this.#onDoubleClick(e));
|
||||
this.on("zoomstart", (e: any) => this.#onZoomStart(e));
|
||||
this.on("zoom", (e: any) => this.#onZoom(e));
|
||||
this.on("zoomend", (e: any) => this.#onZoomEnd(e));
|
||||
this.on("drag", (e: any) => this.centerOnUnit(null));
|
||||
this.on("contextmenu", (e: any) => this.#onContextMenu(e));
|
||||
@ -141,6 +145,7 @@ export class Map extends L.Map {
|
||||
this.on('mousedown', (e: any) => this.#onMouseDown(e));
|
||||
this.on('mouseup', (e: any) => this.#onMouseUp(e));
|
||||
this.on('mousemove', (e: any) => this.#onMouseMove(e));
|
||||
this.on('drag', (e: any) => this.#onMouseMove(e));
|
||||
this.on('keydown', (e: any) => this.#onKeyDown(e));
|
||||
this.on('keyup', (e: any) => this.#onKeyUp(e));
|
||||
|
||||
@ -523,36 +528,38 @@ export class Map extends L.Map {
|
||||
}
|
||||
|
||||
this.hideMapContextMenu();
|
||||
if (this.#state === IDLE) {
|
||||
if (this.#state == IDLE) {
|
||||
this.showMapContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng);
|
||||
var clickedCoalitionArea = null;
|
||||
if (!this.#shiftKey) {
|
||||
if (this.#state === IDLE) {
|
||||
if (this.#state == IDLE) {
|
||||
this.showMapContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng);
|
||||
var clickedCoalitionArea = null;
|
||||
|
||||
/* Coalition areas are ordered in the #coalitionAreas array according to their zindex. Select the upper one */
|
||||
for (let coalitionArea of this.#coalitionAreas) {
|
||||
if (coalitionArea.getBounds().contains(e.latlng)) {
|
||||
if (coalitionArea.getSelected())
|
||||
clickedCoalitionArea = coalitionArea;
|
||||
else
|
||||
this.getMapContextMenu().setCoalitionArea(coalitionArea);
|
||||
/* Coalition areas are ordered in the #coalitionAreas array according to their zindex. Select the upper one */
|
||||
for (let coalitionArea of this.#coalitionAreas) {
|
||||
if (coalitionArea.getBounds().contains(e.latlng)) {
|
||||
if (coalitionArea.getSelected())
|
||||
clickedCoalitionArea = coalitionArea;
|
||||
else
|
||||
this.getMapContextMenu().setCoalitionArea(coalitionArea);
|
||||
}
|
||||
}
|
||||
if (clickedCoalitionArea)
|
||||
this.showCoalitionAreaContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng, clickedCoalitionArea);
|
||||
}
|
||||
if (clickedCoalitionArea)
|
||||
this.showCoalitionAreaContextMenu(e.originalEvent.x, e.originalEvent.y, e.latlng, clickedCoalitionArea);
|
||||
}
|
||||
}
|
||||
else if (this.#state === MOVE_UNIT) {
|
||||
if (!e.originalEvent.ctrlKey) {
|
||||
getApp().getUnitsManager().selectedUnitsClearDestinations();
|
||||
else if (this.#state === MOVE_UNIT) {
|
||||
if (!e.originalEvent.ctrlKey) {
|
||||
getApp().getUnitsManager().selectedUnitsClearDestinations();
|
||||
}
|
||||
getApp().getUnitsManager().selectedUnitsAddDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : e.latlng, this.#shiftKey, this.#destinationGroupRotation)
|
||||
|
||||
this.#destinationGroupRotation = 0;
|
||||
this.#destinationRotationCenter = null;
|
||||
this.#computeDestinationRotation = false;
|
||||
}
|
||||
else {
|
||||
this.setState(IDLE);
|
||||
}
|
||||
getApp().getUnitsManager().selectedUnitsAddDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : e.latlng, this.#shiftKey, this.#destinationGroupRotation)
|
||||
|
||||
this.#destinationGroupRotation = 0;
|
||||
this.#destinationRotationCenter = null;
|
||||
this.#computeDestinationRotation = false;
|
||||
}
|
||||
else {
|
||||
this.setState(IDLE);
|
||||
}
|
||||
}
|
||||
|
||||
@ -586,7 +593,7 @@ export class Map extends L.Map {
|
||||
}
|
||||
|
||||
this.#longPressTimer = window.setTimeout(() => {
|
||||
if (e.originalEvent.button != 2 || e.originalEvent.ctrlKey)
|
||||
if (e.originalEvent.button != 2 || e.originalEvent.ctrlKey || e.originalEvent.shiftKey)
|
||||
return;
|
||||
|
||||
this.hideMapContextMenu();
|
||||
@ -684,6 +691,11 @@ export class Map extends L.Map {
|
||||
this.#isZooming = true;
|
||||
}
|
||||
|
||||
#onZoom(e: any) {
|
||||
if (this.#centerUnit != null)
|
||||
this.#panToUnit(this.#centerUnit);
|
||||
}
|
||||
|
||||
#onZoomEnd(e: any) {
|
||||
this.#isZooming = false;
|
||||
}
|
||||
@ -726,7 +738,7 @@ export class Map extends L.Map {
|
||||
|
||||
#showDestinationCursors() {
|
||||
const singleCursor = !this.#shiftKey;
|
||||
const selectedUnitsCount = getApp().getUnitsManager().getSelectedUnits({ excludeHumans: false, onlyOnePerGroup: true }).length;
|
||||
const selectedUnitsCount = getApp().getUnitsManager().getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }).length;
|
||||
if (selectedUnitsCount > 0) {
|
||||
if (singleCursor && this.#destinationPreviewCursors.length != 1) {
|
||||
this.#hideDestinationCursors();
|
||||
@ -740,6 +752,9 @@ export class Map extends L.Map {
|
||||
this.#destinationPreviewCursors.splice(0, 1);
|
||||
}
|
||||
|
||||
this.#destinationPreviewHandleLine.addTo(this);
|
||||
this.#destinationPreviewHandle.addTo(this);
|
||||
|
||||
while (this.#destinationPreviewCursors.length < selectedUnitsCount) {
|
||||
var cursor = new DestinationPreviewMarker(this.getMouseCoordinates(), { interactive: false });
|
||||
cursor.addTo(this);
|
||||
@ -761,6 +776,9 @@ export class Map extends L.Map {
|
||||
this.#destinationPreviewCursors[idx].setLatLng(this.#shiftKey ? latlng : this.getMouseCoordinates());
|
||||
})
|
||||
};
|
||||
|
||||
this.#destinationPreviewHandleLine.setLatLngs([groupLatLng, this.getMouseCoordinates()]);
|
||||
this.#destinationPreviewHandle.setLatLng(this.getMouseCoordinates());
|
||||
}
|
||||
|
||||
#hideDestinationCursors() {
|
||||
@ -770,22 +788,15 @@ export class Map extends L.Map {
|
||||
})
|
||||
this.#destinationPreviewCursors = [];
|
||||
|
||||
this.#destinationPreviewHandleLine.removeFrom(this);
|
||||
this.#destinationPreviewHandle.removeFrom(this);
|
||||
|
||||
/* Reset the variables used to compute the rotation of the group cursors */
|
||||
this.#destinationGroupRotation = 0;
|
||||
this.#computeDestinationRotation = false;
|
||||
this.#destinationRotationCenter = null;
|
||||
}
|
||||
|
||||
#showTargetCursor() {
|
||||
this.#hideTargetCursor();
|
||||
this.#targetCursor.addTo(this);
|
||||
}
|
||||
|
||||
#hideTargetCursor() {
|
||||
this.#targetCursor.setLatLng(new L.LatLng(0, 0));
|
||||
this.removeLayer(this.#targetCursor);
|
||||
}
|
||||
|
||||
#showDrawingCursor() {
|
||||
this.#hideDefaultCursor();
|
||||
if (!this.hasLayer(this.#drawingCursor))
|
||||
@ -803,7 +814,6 @@ export class Map extends L.Map {
|
||||
if (this.#ctrlKey || this.#selecting) {
|
||||
/* Hide all non default cursors */
|
||||
this.#hideDestinationCursors();
|
||||
this.#hideTargetCursor();
|
||||
this.#hideDrawingCursor();
|
||||
|
||||
this.#showDefaultCursor();
|
||||
@ -811,13 +821,11 @@ export class Map extends L.Map {
|
||||
/* Hide all the unnecessary cursors depending on the active state */
|
||||
if (this.#state !== IDLE) this.#hideDefaultCursor();
|
||||
if (this.#state !== MOVE_UNIT) this.#hideDestinationCursors();
|
||||
//if (![BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) this.#hideTargetCursor();
|
||||
if (this.#state !== COALITIONAREA_DRAW_POLYGON) this.#hideDrawingCursor();
|
||||
|
||||
/* Show the active cursor depending on the active state */
|
||||
if (this.#state === IDLE) this.#showDefaultCursor();
|
||||
else if (this.#state === MOVE_UNIT) this.#showDestinationCursors();
|
||||
//else if ([BOMBING, CARPET_BOMBING, FIRE_AT_AREA].includes(this.#state)) this.#showTargetCursor();
|
||||
else if (this.#state === COALITIONAREA_DRAW_POLYGON) this.#showDrawingCursor();
|
||||
}
|
||||
}
|
||||
|
||||
19
client/src/map/markers/destinationpreviewHandle.ts
Normal file
19
client/src/map/markers/destinationpreviewHandle.ts
Normal file
@ -0,0 +1,19 @@
|
||||
import { DivIcon, LatLng } from "leaflet";
|
||||
import { CustomMarker } from "../markers/custommarker";
|
||||
|
||||
export class DestinationPreviewHandle extends CustomMarker {
|
||||
constructor(latlng: LatLng) {
|
||||
super(latlng, {interactive: true, draggable: true});
|
||||
}
|
||||
|
||||
createIcon() {
|
||||
this.setIcon(new DivIcon({
|
||||
iconSize: [18, 18],
|
||||
iconAnchor: [9, 9],
|
||||
className: "leaflet-destination-preview-handle-marker",
|
||||
}));
|
||||
var el = document.createElement("div");
|
||||
el.classList.add("ol-destination-preview-handle-icon");
|
||||
this.getElement()?.appendChild(el);
|
||||
}
|
||||
}
|
||||
@ -36,8 +36,16 @@ export class MissionManager {
|
||||
}
|
||||
|
||||
updateBullseyes(data: BullseyesData) {
|
||||
const commandMode = getApp().getMissionManager().getCommandModeOptions().commandMode;
|
||||
for (let idx in data.bullseyes) {
|
||||
const bullseye = data.bullseyes[idx];
|
||||
|
||||
// Prevent Red and Blue coalitions seeing each other's bulleye(s)
|
||||
if ((bullseye.coalition === "red" && commandMode === BLUE_COMMANDER)
|
||||
|| (bullseye.coalition === "blue" && commandMode === RED_COMMANDER)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!(idx in this.#bullseyes))
|
||||
this.#bullseyes[idx] = new Bullseye([0, 0]).addTo(getApp().getMap());
|
||||
|
||||
|
||||
@ -25,6 +25,7 @@ import { helicopterDatabase } from "./unit/databases/helicopterdatabase";
|
||||
import { groundUnitDatabase } from "./unit/databases/groundunitdatabase";
|
||||
import { navyUnitDatabase } from "./unit/databases/navyunitdatabase";
|
||||
import { ConfigurationOptions } from "./interfaces";
|
||||
import { UnitListPanel } from "./panels/unitlistpanel";
|
||||
|
||||
export class OlympusApp {
|
||||
/* Global data */
|
||||
@ -181,6 +182,7 @@ export class OlympusApp {
|
||||
.add("serverStatus", new ServerStatusPanel("server-status-panel"))
|
||||
.add("unitControl", new UnitControlPanel("unit-control-panel"))
|
||||
.add("unitInfo", new UnitInfoPanel("unit-info-panel"))
|
||||
.add("unitList", new UnitListPanel("unit-list-panel", "unit-list-panel-content"))
|
||||
|
||||
// Popups
|
||||
this.getPopupsManager().add("infoPopup", new Popup("info-popup"));
|
||||
@ -196,8 +198,8 @@ export class OlympusApp {
|
||||
if (config && config.address != undefined && config.port != undefined) {
|
||||
const address = config.address;
|
||||
const port = config.port;
|
||||
if (typeof address === 'string' && typeof port == 'number') {
|
||||
this.getServerManager().setAddress(address == "*" ? window.location.hostname : address, port);
|
||||
if (typeof address === 'string' && typeof port == 'number') {
|
||||
this.getServerManager().setAddress(address == "*" ? window.location.hostname : address, port);
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -236,22 +238,38 @@ export class OlympusApp {
|
||||
});
|
||||
|
||||
const shortcutManager = this.getShortcutManager();
|
||||
shortcutManager.add("toggleDemo", new ShortcutKeyboard({
|
||||
shortcutManager.addKeyboardShortcut("toggleDemo", {
|
||||
"callback": () => {
|
||||
this.getServerManager().toggleDemoEnabled();
|
||||
},
|
||||
"code": "KeyT"
|
||||
})).add("togglePause", new ShortcutKeyboard({
|
||||
}).addKeyboardShortcut("togglePause", {
|
||||
"altKey": false,
|
||||
"callback": () => {
|
||||
this.getServerManager().setPaused(!this.getServerManager().getPaused());
|
||||
},
|
||||
"code": "Space",
|
||||
"ctrlKey": false
|
||||
}));
|
||||
}).addKeyboardShortcut("deselectAll", {
|
||||
"callback": (ev: KeyboardEvent) => {
|
||||
this.getUnitsManager().deselectAllUnits();
|
||||
},
|
||||
"code": "Escape"
|
||||
}).addKeyboardShortcut("toggleUnitLabels", {
|
||||
"altKey": false,
|
||||
"callback": () => {
|
||||
const chk = document.querySelector(`label[title="Show unit labels"] input[type="checkbox"]`);
|
||||
if (chk instanceof HTMLElement) {
|
||||
chk.click();
|
||||
}
|
||||
},
|
||||
"code": "KeyL",
|
||||
"ctrlKey": false,
|
||||
"shiftKey": false
|
||||
});
|
||||
|
||||
["KeyW", "KeyA", "KeyS", "KeyD", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].forEach(code => {
|
||||
shortcutManager.add(`pan${code}keydown`, new ShortcutKeyboard({
|
||||
shortcutManager.addKeyboardShortcut(`pan${code}keydown`, {
|
||||
"altKey": false,
|
||||
"callback": (ev: KeyboardEvent) => {
|
||||
this.getMap().handleMapPanning(ev);
|
||||
@ -259,30 +277,42 @@ export class OlympusApp {
|
||||
"code": code,
|
||||
"ctrlKey": false,
|
||||
"event": "keydown"
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
["KeyW", "KeyA", "KeyS", "KeyD", "ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown"].forEach(code => {
|
||||
shortcutManager.add(`pan${code}keyup`, new ShortcutKeyboard({
|
||||
shortcutManager.addKeyboardShortcut(`pan${code}keyup`, {
|
||||
"callback": (ev: KeyboardEvent) => {
|
||||
this.getMap().handleMapPanning(ev);
|
||||
},
|
||||
"code": code
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
["Digit1", "Digit2", "Digit3", "Digit4", "Digit5", "Digit6", "Digit7", "Digit8", "Digit9"].forEach(code => {
|
||||
shortcutManager.add(`hotgroup${code}`, new ShortcutKeyboard({
|
||||
const digits = ["Digit1", "Digit2", "Digit3", "Digit4", "Digit5", "Digit6", "Digit7", "Digit8", "Digit9"];
|
||||
|
||||
digits.forEach(code => {
|
||||
shortcutManager.addKeyboardShortcut(`hotgroup${code}`, {
|
||||
"altKey": false,
|
||||
"callback": (ev: KeyboardEvent) => {
|
||||
if (ev.ctrlKey && ev.shiftKey)
|
||||
this.getUnitsManager().selectedUnitsAddToHotgroup(parseInt(ev.code.substring(5)));
|
||||
else if (ev.ctrlKey && !ev.shiftKey)
|
||||
this.getUnitsManager().selectedUnitsSetHotgroup(parseInt(ev.code.substring(5)));
|
||||
else if (!ev.ctrlKey && ev.shiftKey)
|
||||
this.getUnitsManager().selectUnitsByHotgroup(parseInt(ev.code.substring(5)), false);
|
||||
else
|
||||
this.getUnitsManager().selectUnitsByHotgroup(parseInt(ev.code.substring(5)));
|
||||
},
|
||||
"code": code
|
||||
}));
|
||||
});
|
||||
});
|
||||
|
||||
// Stop hotgroup controls sending the browser to another tab
|
||||
digits.forEach(code => {
|
||||
document.addEventListener("keydown", (ev: KeyboardEvent) => {
|
||||
if (ev.code === code && ev.ctrlKey === true && ev.altKey === false && ev.shiftKey === false) {
|
||||
ev.preventDefault();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// TODO: move from here in dedicated class
|
||||
|
||||
@ -1,12 +1,27 @@
|
||||
import { Panel } from "./panel";
|
||||
|
||||
export class ConnectionStatusPanel extends Panel {
|
||||
|
||||
constructor(ID: string) {
|
||||
super( ID );
|
||||
}
|
||||
|
||||
|
||||
update(connected: boolean) {
|
||||
this.getElement().toggleAttribute( "data-is-connected", connected );
|
||||
|
||||
showDisconnected() {
|
||||
this.getElement().toggleAttribute( "data-is-connected", false );
|
||||
this.getElement().toggleAttribute( "data-is-paused", false );
|
||||
}
|
||||
|
||||
|
||||
showConnected() {
|
||||
this.getElement().toggleAttribute( "data-is-connected", true );
|
||||
this.getElement().toggleAttribute( "data-is-paused", false );
|
||||
}
|
||||
|
||||
|
||||
showServerPaused() {
|
||||
this.getElement().toggleAttribute( "data-is-connected", false );
|
||||
this.getElement().toggleAttribute( "data-is-paused", true );
|
||||
}
|
||||
|
||||
}
|
||||
@ -26,6 +26,7 @@ export class MouseInfoPanel extends Panel {
|
||||
getApp().getMap()?.on("click", (e: any) => this.#onMapClick(e));
|
||||
getApp().getMap()?.on('zoom', (e: any) => this.#onZoom(e));
|
||||
getApp().getMap()?.on('mousemove', (e: any) => this.#onMouseMove(e));
|
||||
getApp().getMap()?.on('drag', (e: any) => this.#onMouseMove(e));
|
||||
|
||||
document.addEventListener('unitsSelection', (e: CustomEvent<Unit[]>) => this.#update());
|
||||
document.addEventListener('clearSelection', () => this.#update());
|
||||
@ -107,14 +108,15 @@ export class MouseInfoPanel extends Panel {
|
||||
|
||||
#drawMeasureLine() {
|
||||
var mouseLatLng = getApp().getMap().containerPointToLatLng(getApp().getMap().getMousePosition());
|
||||
const mousePosition = getApp().getMap().getMousePosition();
|
||||
if (this.#measurePoint != null) {
|
||||
var points = [this.#measurePoint, mouseLatLng];
|
||||
this.#measureLine.setLatLngs(points);
|
||||
var dist = distance(this.#measurePoint.lat, this.#measurePoint.lng, mouseLatLng.lat, mouseLatLng.lng);
|
||||
var bear = bearing(this.#measurePoint.lat, this.#measurePoint.lng, mouseLatLng.lat, mouseLatLng.lng);
|
||||
var startXY = getApp().getMap().latLngToContainerPoint(this.#measurePoint);
|
||||
var dx = (getApp().getMap().getMousePosition().x - startXY.x);
|
||||
var dy = (getApp().getMap().getMousePosition().y - startXY.y);
|
||||
var dx = mousePosition.x - startXY.x;
|
||||
var dy = mousePosition.y - startXY.y;
|
||||
|
||||
var angle = Math.atan2(dy, dx);
|
||||
if (angle > Math.PI / 2)
|
||||
@ -133,8 +135,8 @@ export class MouseInfoPanel extends Panel {
|
||||
let data = [`${bng}°`, `${str} ${unit}`];
|
||||
|
||||
this.#measureBox.innerText = data.join(" / ");
|
||||
this.#measureBox.style.left = (getApp().getMap().getMousePosition().x + startXY.x) / 2 - this.#measureBox.offsetWidth / 2 + "px";
|
||||
this.#measureBox.style.top = (getApp().getMap().getMousePosition().y + startXY.y) / 2 - this.#measureBox.offsetHeight / 2 + "px";
|
||||
this.#measureBox.style.left = (mousePosition.x + startXY.x) / 2 - this.#measureBox.offsetWidth / 2 + "px";
|
||||
this.#measureBox.style.top = (mousePosition.y + startXY.y) / 2 - this.#measureBox.offsetHeight / 2 + "px";
|
||||
this.#measureBox.style.rotate = angle + "rad";
|
||||
}
|
||||
}
|
||||
|
||||
@ -295,7 +295,7 @@ export class UnitControlPanel extends Panel {
|
||||
const TACANCallsignInput = this.#advancedSettingsDialog.querySelector("#tacan-callsign")?.querySelector("input") as HTMLInputElement;
|
||||
const radioMhzInput = this.#advancedSettingsDialog.querySelector("#radio-mhz")?.querySelector("input") as HTMLInputElement;
|
||||
const radioCallsignNumberInput = this.#advancedSettingsDialog.querySelector("#radio-callsign-number")?.querySelector("input") as HTMLInputElement;
|
||||
|
||||
|
||||
const unit = units[0];
|
||||
const roles = aircraftDatabase.getByName(unit.getName())?.loadouts?.map((loadout) => {return loadout.roles})
|
||||
const tanker = roles != undefined && Array.prototype.concat.apply([], roles)?.includes("Refueling");
|
||||
@ -305,6 +305,7 @@ export class UnitControlPanel extends Panel {
|
||||
|
||||
/* Activate the correct options depending on unit type */
|
||||
this.#advancedSettingsDialog.toggleAttribute("data-show-settings", !tanker && !AWACS);
|
||||
this.#advancedSettingsDialog.toggleAttribute("data-show-air-unit-checkboxes", ["Aircraft", "Helicopter"].includes(units[0].getCategory()));
|
||||
this.#advancedSettingsDialog.toggleAttribute("data-show-tasking", tanker || AWACS);
|
||||
this.#advancedSettingsDialog.toggleAttribute("data-show-tanker", tanker);
|
||||
this.#advancedSettingsDialog.toggleAttribute("data-show-AWACS", AWACS);
|
||||
|
||||
191
client/src/panels/unitlistpanel.ts
Normal file
191
client/src/panels/unitlistpanel.ts
Normal file
@ -0,0 +1,191 @@
|
||||
import { getApp } from "..";
|
||||
import { ShortcutKeyboard } from "../shortcut/shortcut";
|
||||
import { Unit } from "../unit/unit";
|
||||
import { Panel } from "./panel";
|
||||
|
||||
export class UnitListPanel extends Panel {
|
||||
#contentElement: HTMLElement;
|
||||
#currentSortAlgorithm: string = "unitName";
|
||||
#currentSortDirection: string = "ASC";
|
||||
#units: Unit[] = [];
|
||||
#unitNameCache: {[key:string]: string} = {};
|
||||
#updatesInterval!: ReturnType<typeof setInterval>;
|
||||
|
||||
constructor(panelElement: string, contentElement: string) {
|
||||
super(panelElement);
|
||||
const getElement = document.getElementById(contentElement);
|
||||
|
||||
if (getElement instanceof HTMLElement)
|
||||
this.#contentElement = getElement;
|
||||
else
|
||||
throw new Error(`UnitList: unable to find element "${contentElement}".`);
|
||||
|
||||
// Add the header click listener and sorting
|
||||
[].slice.call(this.getElement().querySelectorAll(".headers > *")).forEach((header: HTMLElement) => {
|
||||
header.addEventListener("click", (ev: MouseEvent) => {
|
||||
const el = ev.target;
|
||||
if (el instanceof HTMLElement) {
|
||||
const newSort = el.getAttribute("data-sort-field") || "unitName";
|
||||
|
||||
if (this.#currentSortAlgorithm === newSort)
|
||||
this.#currentSortDirection = (this.#currentSortDirection === "ASC") ? "DESC" : "ASC";
|
||||
else
|
||||
this.#currentSortDirection = "ASC";
|
||||
|
||||
this.#currentSortAlgorithm = newSort;
|
||||
|
||||
this.doUpdate();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Dynamically listen for clicks in order to do stuff with units
|
||||
this.getElement().addEventListener("click", (ev: MouseEvent) => {
|
||||
const t = ev.target;
|
||||
|
||||
if (t instanceof HTMLElement) {
|
||||
const el = t.closest( "[data-unit-id]");
|
||||
|
||||
if (el instanceof HTMLElement) {
|
||||
let id: number = Number(el.getAttribute("data-unit-id") || 0);
|
||||
getApp().getUnitsManager().selectUnit(id);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
new ShortcutKeyboard({
|
||||
"callback": () => { this.toggle() },
|
||||
"code": "KeyU"
|
||||
});
|
||||
|
||||
this.startUpdates();
|
||||
}
|
||||
|
||||
doUpdate() {
|
||||
if (!this.getVisible())
|
||||
return;
|
||||
|
||||
this.#contentElement.innerHTML = "";
|
||||
|
||||
this.#units = Object.values(getApp().getUnitsManager().getUnits());
|
||||
|
||||
if (this.#currentSortAlgorithm === "coalition")
|
||||
this.#sortUnitsByCoalition();
|
||||
|
||||
if (this.#currentSortAlgorithm === "name")
|
||||
this.#sortUnitsByName();
|
||||
|
||||
if (this.#currentSortAlgorithm === "unitName")
|
||||
this.#sortUnitsByUnitName();
|
||||
|
||||
Object.values(this.#units).forEach((unit: Unit) => {
|
||||
// Exclude dead units
|
||||
if (!unit.getAlive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const name = unit.getName();
|
||||
|
||||
if ( this.#unitNameCache.hasOwnProperty( name ) === false ) {
|
||||
this.#unitNameCache[name] = unit.getDatabase()?.getByName(unit.getName())?.label || unit.getName();
|
||||
}
|
||||
|
||||
this.#contentElement.innerHTML += `
|
||||
<div class="unit-list-unit" data-unit-id="${unit.ID}" data-value-unitName="${unit.getUnitName()}" data-value-name="${unit.getName()}" data-value-coalition="${unit.getCoalition()}" data-value-human="${unit.getHuman() ? "Human" : "AI"}">
|
||||
<div><span>${unit.getUnitName()}</span></div>
|
||||
<div>${this.#unitNameCache[name]}</div>
|
||||
<div>${unit.getCategory()}</div>
|
||||
<div>${unit.getCoalition()}</div>
|
||||
<div>${unit.getHuman() ? "Human" : "AI"}</div>
|
||||
</div>`;
|
||||
});
|
||||
}
|
||||
|
||||
getContentElement() {
|
||||
return this.#contentElement;
|
||||
}
|
||||
|
||||
#sortStringsCompare(str1: string, str2: string) {
|
||||
if (str1 > str2) {
|
||||
return (this.#currentSortDirection === "ASC") ? 1 : -1;
|
||||
} else if (str1 < str2) {
|
||||
return (this.#currentSortDirection === "ASC") ? -1 : 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#sortUnitsByUnitName() {
|
||||
this.#units.sort((unit1: Unit, unit2: Unit) => {
|
||||
|
||||
const str1 = unit1.getUnitName().toLowerCase();
|
||||
const str2 = unit2.getUnitName().toLowerCase();
|
||||
|
||||
return this.#sortStringsCompare(str1, str2);
|
||||
});
|
||||
}
|
||||
|
||||
#sortUnitsByCategory() {
|
||||
this.#units.sort((unit1: Unit, unit2: Unit) => {
|
||||
let str1 = unit1.getCategory();
|
||||
let str2 = unit2.getCategory();
|
||||
|
||||
let cmp = this.#sortStringsCompare(str1, str2);
|
||||
|
||||
if (cmp !== 0)
|
||||
return cmp;
|
||||
|
||||
str1 = unit1.getUnitName().toLowerCase();
|
||||
str2 = unit2.getUnitName().toLowerCase();
|
||||
|
||||
return this.#sortStringsCompare(str1, str2);
|
||||
});
|
||||
}
|
||||
|
||||
#sortUnitsByCoalition() {
|
||||
this.#units.sort((unit1: Unit, unit2: Unit) => {
|
||||
let str1 = unit1.getCoalition();
|
||||
let str2 = unit2.getCoalition();
|
||||
|
||||
let cmp = this.#sortStringsCompare(str1, str2);
|
||||
|
||||
if (cmp !== 0)
|
||||
return cmp;
|
||||
|
||||
str1 = unit1.getUnitName().toLowerCase();
|
||||
str2 = unit2.getUnitName().toLowerCase();
|
||||
|
||||
return this.#sortStringsCompare(str1, str2);
|
||||
});
|
||||
}
|
||||
|
||||
#sortUnitsByName() {
|
||||
this.#units.sort((unit1: Unit, unit2: Unit) => {
|
||||
const str1 = unit1.getName().toLowerCase();
|
||||
const str2 = unit2.getName().toLowerCase();
|
||||
return this.#sortStringsCompare(str1, str2);
|
||||
});
|
||||
}
|
||||
|
||||
startUpdates() {
|
||||
this.doUpdate();
|
||||
|
||||
this.#updatesInterval = setInterval(() => {
|
||||
this.doUpdate();
|
||||
}, 4000);
|
||||
}
|
||||
|
||||
stopUpdates() {
|
||||
clearInterval(this.#updatesInterval);
|
||||
}
|
||||
|
||||
toggle() {
|
||||
if (this.getVisible())
|
||||
this.stopUpdates();
|
||||
else
|
||||
this.startUpdates();
|
||||
|
||||
super.toggle();
|
||||
}
|
||||
}
|
||||
@ -17,6 +17,8 @@ export class ServerManager {
|
||||
#sessionHash: string | null = null;
|
||||
#lastUpdateTimes: {[key: string]: number} = {}
|
||||
#demoEnabled = false;
|
||||
#previousMissionElapsedTime:number = 0; // Track if mission elapsed time is increasing (i.e. is the server paused)
|
||||
#serverIsPaused: boolean = false;
|
||||
|
||||
constructor() {
|
||||
this.#lastUpdateTimes[UNITS_URI] = Date.now();
|
||||
@ -445,9 +447,25 @@ export class ServerManager {
|
||||
var time = getApp().getUnitsManager()?.update(buffer);
|
||||
return time;
|
||||
}, true);
|
||||
(getApp().getPanelsManager().get("connectionStatus") as ConnectionStatusPanel).update(this.getConnected());
|
||||
|
||||
const elapsedMissionTime = getApp().getMissionManager().getDateAndTime().elapsedTime;
|
||||
this.#serverIsPaused = ( elapsedMissionTime === this.#previousMissionElapsedTime );
|
||||
this.#previousMissionElapsedTime = elapsedMissionTime;
|
||||
|
||||
const csp = (getApp().getPanelsManager().get("connectionStatus") as ConnectionStatusPanel);
|
||||
|
||||
if ( this.getConnected() ) {
|
||||
if ( this.getServerIsPaused() ) {
|
||||
csp.showServerPaused();
|
||||
} else {
|
||||
csp.showConnected();
|
||||
}
|
||||
} else {
|
||||
csp.showDisconnected();
|
||||
}
|
||||
|
||||
}
|
||||
}, 5000);
|
||||
}, ( this.getServerIsPaused() ? 500 : 5000 ));
|
||||
|
||||
window.setInterval(() => {
|
||||
if (!this.getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
|
||||
@ -521,4 +539,8 @@ export class ServerManager {
|
||||
getPaused() {
|
||||
return this.#paused;
|
||||
}
|
||||
|
||||
getServerIsPaused() {
|
||||
return this.#serverIsPaused;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { KeyboardShortcutOptions, MouseShortcutOptions, ShortcutOptions } from "../interfaces";
|
||||
import { ShortcutKeyboardOptions, ShortcutMouseOptions, ShortcutOptions } from "../interfaces";
|
||||
import { keyEventWasInInput } from "../other/utils";
|
||||
|
||||
export abstract class Shortcut {
|
||||
@ -14,7 +14,7 @@ export abstract class Shortcut {
|
||||
}
|
||||
|
||||
export class ShortcutKeyboard extends Shortcut {
|
||||
constructor(config: KeyboardShortcutOptions) {
|
||||
constructor(config: ShortcutKeyboardOptions) {
|
||||
config.event = config.event || "keyup";
|
||||
super(config);
|
||||
|
||||
@ -37,7 +37,7 @@ export class ShortcutKeyboard extends Shortcut {
|
||||
}
|
||||
|
||||
export class ShortcutMouse extends Shortcut {
|
||||
constructor(config: MouseShortcutOptions) {
|
||||
constructor(config: ShortcutMouseOptions) {
|
||||
super(config);
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,6 @@
|
||||
import { ShortcutKeyboardOptions, ShortcutMouseOptions } from "../interfaces";
|
||||
import { Manager } from "../other/manager";
|
||||
import { Shortcut } from "./shortcut";
|
||||
import { ShortcutKeyboard, ShortcutMouse } from "./shortcut";
|
||||
|
||||
export class ShortcutManager extends Manager {
|
||||
|
||||
@ -25,8 +26,18 @@ export class ShortcutManager extends Manager {
|
||||
|
||||
}
|
||||
|
||||
add(name: string, shortcut: Shortcut) {
|
||||
super.add(name, shortcut);
|
||||
add( name: string, shortcut:any ) {
|
||||
console.error( "ShortcutManager:add() cannot be used. Use addKeyboardShortcut or addMouseShortcut." );
|
||||
return this;
|
||||
}
|
||||
|
||||
addKeyboardShortcut( name:string, shortcutKeyboardOptions:ShortcutKeyboardOptions ) {
|
||||
super.add( name, new ShortcutKeyboard( shortcutKeyboardOptions ) );
|
||||
return this;
|
||||
}
|
||||
|
||||
addMouseShortcut( name:string, shortcutMouseOptions:ShortcutMouseOptions ) {
|
||||
super.add( name, new ShortcutMouse( shortcutMouseOptions ) );
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@ -201,8 +201,12 @@ export class UnitsManager {
|
||||
*
|
||||
* @param hotgroup The hotgroup number
|
||||
*/
|
||||
selectUnitsByHotgroup(hotgroup: number) {
|
||||
this.deselectAllUnits();
|
||||
selectUnitsByHotgroup(hotgroup: number, deselectAllUnits: boolean = true ) {
|
||||
|
||||
if ( deselectAllUnits ) {
|
||||
this.deselectAllUnits();
|
||||
}
|
||||
|
||||
this.getUnitsByHotgroup(hotgroup).forEach((unit: Unit) => unit.setSelected(true))
|
||||
}
|
||||
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
<%- include('panels/serverstatus.ejs') %>
|
||||
<%- include('panels/hotgroup.ejs') %>
|
||||
<%- include('panels/logpanel.ejs') %>
|
||||
<%- include('panels/unitlist.ejs') %>
|
||||
|
||||
<!-- Context menus -->
|
||||
<%- include('contextmenus/airbase.ejs') %>
|
||||
|
||||
@ -38,28 +38,28 @@
|
||||
<hr>
|
||||
</div>
|
||||
<div id="general-settings-grid">
|
||||
<div id="prohibit-jettison-checkbox" class="ol-checkbox">
|
||||
<div id="prohibit-jettison-checkbox" class="ol-checkbox air-unit-checkbox">
|
||||
<label title="The unit will not jettison external stores">
|
||||
<input type="checkbox"/>
|
||||
Prohibit jettison
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="prohibit-afterburner-checkbox" class="ol-checkbox">
|
||||
<div id="prohibit-afterburner-checkbox" class="ol-checkbox air-unit-checkbox">
|
||||
<label title="The unit will not engage the afterburner">
|
||||
<input type="checkbox" />
|
||||
Prohibit afterburner
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="prohibit-AA-checkbox" class="ol-checkbox">
|
||||
<div id="prohibit-AA-checkbox" class="ol-checkbox air-unit-checkbox">
|
||||
<label title="The unit will not engage airborne targets">
|
||||
<input type="checkbox" />
|
||||
Prohibit A/A
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="prohibit-AG-checkbox" class="ol-checkbox">
|
||||
<div id="prohibit-AG-checkbox" class="ol-checkbox air-unit-checkbox">
|
||||
<label title="The unit will not engage ground targets">
|
||||
<input type="checkbox" />
|
||||
Prohibit A/G
|
||||
|
||||
11
client/views/panels/unitlist.ejs
Normal file
11
client/views/panels/unitlist.ejs
Normal file
@ -0,0 +1,11 @@
|
||||
<div id="unit-list-panel" class="ol-panel">
|
||||
<h3>Unit List</h3>
|
||||
<div class="unit-list-unit headers">
|
||||
<div data-sort-field="unitName">Name</div>
|
||||
<div data-sort-field="name">Vehicle</div>
|
||||
<div data-sort-field="category">Category</div>
|
||||
<div data-sort-field="coalition">Coalition</div>
|
||||
<div>Human/AI</div>
|
||||
</div>
|
||||
<div id="unit-list-panel-content" class="ol-scrollable"></div>
|
||||
</div>
|
||||
Loading…
x
Reference in New Issue
Block a user