diff --git a/frontend/react/src/audio/audiounitpipeline.ts b/frontend/react/src/audio/audiounitpipeline.ts index 5702b99e..3954a3a0 100644 --- a/frontend/react/src/audio/audiounitpipeline.ts +++ b/frontend/react/src/audio/audiounitpipeline.ts @@ -80,7 +80,9 @@ export class AudioUnitPipeline { /* Don't bother updating parameters if the client is too far away */ if (this.#distance < this.#maxDistance) { /* Compute a new gain decreasing with distance. */ - let newGain = 1.0 - Math.pow(this.#distance / this.#maxDistance, 1); // Arbitrary + //let newGain = 1.0 - Math.pow(this.#distance / this.#maxDistance, 1); // Arbitrary + + let newGain = Math.min( 1, 20 / this.#distance ) /* Set the values of the main gain node and the multitap gain node, used for reverb effect */ this.#gainNode.gain.setValueAtTime(newGain, getApp().getAudioManager().getAudioContext().currentTime); diff --git a/frontend/react/src/constants/constants.ts b/frontend/react/src/constants/constants.ts index dac06c3a..6a5bafb5 100644 --- a/frontend/react/src/constants/constants.ts +++ b/frontend/react/src/constants/constants.ts @@ -1,6 +1,29 @@ import { LatLng, LatLngBounds } from "leaflet"; import { MapOptions } from "../types/types"; import { CommandModeOptions } from "../interfaces"; +import { ContextAction } from "../unit/contextaction"; +import { + faExplosion, + faHand, + faLocationCrosshairs, + faLocationDot, + faMapLocation, + faPeopleGroup, + faPlaneArrival, + faRoute, + faTrash, + faXmarksLines, +} from "@fortawesome/free-solid-svg-icons"; +import { Unit } from "../unit/unit"; +import { getApp } from "../olympusapp"; +import { + olButtonsContextAttack, + olButtonsContextFollow, + olButtonsContextLandAtPoint, + olButtonsContextRefuel, + olButtonsContextSimulateFireFight, +} from "../ui/components/olicons"; +import { FormationCreationRequestEvent, UnitExplosionRequestEvent } from "../events"; export const UNITS_URI = "units"; export const WEAPONS_URI = "weapons"; @@ -308,7 +331,7 @@ export const MAP_OPTIONS_DEFAULTS = { fillSelectedRing: false, showMinimap: false, protectDCSUnits: true, - keepRelativePositions: true + keepRelativePositions: true, } as MapOptions; export const MAP_HIDDEN_TYPES_DEFAULTS = { @@ -400,6 +423,12 @@ export enum AudioMessageType { settings, } +export enum ContextActionTarget { + NONE, + UNIT, + POINT, +} + export enum ContextActionType { NO_COLOR, MOVE, @@ -408,6 +437,238 @@ export enum ContextActionType { ENGAGE, DELETE, } + export const CONTEXT_ACTION_COLORS = [null, "white", "green", "purple", "blue", "red"]; +export namespace ContextActions { + export const STOP = new ContextAction( + "stop", + "Stop unit", + "Stops the unit", + faHand, + ContextActionTarget.NONE, + (units: Unit[], _1, _2) => { + getApp().getUnitsManager().stop(units); + }, + { + executeImmediately: true, + type: ContextActionType.MOVE, + hotkey: "KeyZ", + } + ); + export const MOVE = new ContextAction( + "move", + "Set destination", + "Click on the map to move the units there", + faLocationDot, + ContextActionTarget.POINT, + (units: Unit[], _, targetPosition, originalEvent) => { + if (!originalEvent?.ctrlKey) getApp().getUnitsManager().clearDestinations(units); + if (targetPosition) + getApp() + .getUnitsManager() + .addDestination(targetPosition, getApp().getMap().getOptions().keepRelativePositions, getApp().getMap().getDestinationRotation(), units); + }, + { type: ContextActionType.MOVE, hotkey: "KeyX" } + ); + + export const PATH = new ContextAction( + "path", + "Create route", + "Click on the map to add a destination to the path", + faRoute, + ContextActionTarget.POINT, + (units: Unit[], _, targetPosition) => { + if (targetPosition) + getApp() + .getUnitsManager() + .addDestination(targetPosition, getApp().getMap().getOptions().keepRelativePositions, getApp().getMap().getDestinationRotation(), units); + }, + { type: ContextActionType.MOVE, hotkey: "KeyC" } + ); + + export const DELETE = new ContextAction( + "delete", + "Delete unit", + "Deletes the unit", + faTrash, + ContextActionTarget.NONE, + (units: Unit[], _1, _2) => { + getApp().getUnitsManager().delete(false); + }, + { + executeImmediately: true, + type: ContextActionType.DELETE, + } + ); + + export const EXPLODE = new ContextAction( + "explode", + "Explode unit", + "Explodes the unit", + faExplosion, + ContextActionTarget.NONE, + (units: Unit[], _1, _2) => { + getApp().setState(OlympusState.UNIT_CONTROL, UnitControlSubState.UNIT_EXPLOSION_MENU); + UnitExplosionRequestEvent.dispatch(units); + }, + { + executeImmediately: true, + type: ContextActionType.DELETE, + } + ); + + export const CENTER_MAP = new ContextAction( + "center-map", + "Center map", + "Center the map on the unit and follow it", + faMapLocation, + ContextActionTarget.NONE, + (units: Unit[]) => { + getApp().getMap().centerOnUnit(units[0]); + }, + { executeImmediately: true, type: ContextActionType.OTHER } + ); + + export const REFUEL = new ContextAction( + "refuel", + "Refuel at tanker", + "Refuel units at the nearest AAR Tanker. If no tanker is available the unit will RTB", + olButtonsContextRefuel, + ContextActionTarget.NONE, + (units: Unit[]) => { + getApp().getUnitsManager().refuel(units); + }, + { executeImmediately: true, type: ContextActionType.ADMIN } + ); + + export const FOLLOW = new ContextAction( + "follow", + "Follow unit", + "Click on a unit to follow it in formation", + olButtonsContextFollow, + ContextActionTarget.UNIT, + (units: Unit[], targetUnit: Unit | null, _) => { + if (targetUnit) { + getApp().setState(OlympusState.UNIT_CONTROL, UnitControlSubState.FORMATION); + FormationCreationRequestEvent.dispatch( + targetUnit, + units.filter((unit) => unit !== targetUnit) + ); + } + }, + { type: ContextActionType.ADMIN } + ); + + export const BOMB = new ContextAction( + "bomb", + "Precision bomb location", + "Click on a point to execute a precision bombing attack", + faLocationCrosshairs, + ContextActionTarget.POINT, + (units: Unit[], _, targetPosition: LatLng | null) => { + if (targetPosition) + getApp() + .getUnitsManager() + .bombPoint(targetPosition, getApp().getMap().getOptions().keepRelativePositions, getApp().getMap().getDestinationRotation(), units); + }, + { type: ContextActionType.ENGAGE } + ); + + export const CARPET_BOMB = new ContextAction( + "carpet-bomb", + "Carpet bomb location", + "Click on a point to execute a carpet bombing attack", + faXmarksLines, + ContextActionTarget.POINT, + (units: Unit[], _, targetPosition: LatLng | null) => { + if (targetPosition) + getApp() + .getUnitsManager() + .carpetBomb(targetPosition, getApp().getMap().getOptions().keepRelativePositions, getApp().getMap().getDestinationRotation(), units); + }, + { type: ContextActionType.ENGAGE } + ); + + export const LAND = new ContextAction( + "land", + "Land", + "Click on a point to land at the nearest airbase", + faPlaneArrival, + ContextActionTarget.POINT, + (units: Unit[], _, targetPosition: LatLng | null) => { + if (targetPosition) getApp().getUnitsManager().landAt(targetPosition, units); + }, + { type: ContextActionType.ADMIN } + ); + + export const LAND_AT_POINT = new ContextAction( + "land-at-point", + "Land at location", + "Click on a point to land there", + olButtonsContextLandAtPoint, + ContextActionTarget.POINT, + (units: Unit[], _, targetPosition: LatLng | null) => { + if (targetPosition) + getApp() + .getUnitsManager() + .landAtPoint(targetPosition, getApp().getMap().getOptions().keepRelativePositions, getApp().getMap().getDestinationRotation(), units); + }, + { type: ContextActionType.ADMIN } + ); + + export const GROUP = new ContextAction( + "group-ground", + "Group ground units", + "Create a group of ground units", + faPeopleGroup, + ContextActionTarget.NONE, + (units: Unit[], _1, _2) => { + getApp().getUnitsManager().createGroup(units); + }, + { executeImmediately: true, type: ContextActionType.OTHER } + ); + + export const ATTACK = new ContextAction( + "attack", + "Attack unit", + "Click on a unit to attack it", + olButtonsContextAttack, + ContextActionTarget.UNIT, + (units: Unit[], targetUnit: Unit | null, _) => { + if (targetUnit) getApp().getUnitsManager().attackUnit(targetUnit.ID, units); + }, + { type: ContextActionType.ENGAGE } + ); + + export const FIRE_AT_AREA = new ContextAction( + "fire-at-area", + "Fire at area", + "Click on a point to precisely fire at it (if possible)", + faLocationCrosshairs, + ContextActionTarget.POINT, + (units: Unit[], _, targetPosition: LatLng | null) => { + if (targetPosition) + getApp() + .getUnitsManager() + .fireAtArea(targetPosition, getApp().getMap().getOptions().keepRelativePositions, getApp().getMap().getDestinationRotation(), units); + }, + { type: ContextActionType.ENGAGE } + ); + + export const SIMULATE_FIRE_FIGHT = new ContextAction( + "simulate-fire-fight", + "Simulate fire fight", + "Simulate a fire fight by shooting randomly in a certain large area. WARNING: works correctly only on neutral units, blue or red units will aim", + olButtonsContextSimulateFireFight, + ContextActionTarget.POINT, + (units: Unit[], _, targetPosition: LatLng | null) => { + if (targetPosition) + getApp() + .getUnitsManager() + .simulateFireFight(targetPosition, getApp().getMap().getOptions().keepRelativePositions, getApp().getMap().getDestinationRotation(), units); + }, + { type: ContextActionType.ADMIN } + ); +} diff --git a/frontend/react/src/map/map.ts b/frontend/react/src/map/map.ts index 9de812ff..eaca1940 100644 --- a/frontend/react/src/map/map.ts +++ b/frontend/react/src/map/map.ts @@ -20,6 +20,8 @@ import { DrawSubState, JTACSubState, UnitControlSubState, + ContextActionTarget, + ContextActionType, } from "../constants/constants"; import { CoalitionPolygon } from "./coalitionarea/coalitionpolygon"; import { MapHiddenTypes, MapOptions } from "../types/types"; @@ -931,7 +933,7 @@ export class Map extends L.Map { return; } - if (e.originalEvent.button === 2) this.#isRotatingDestination = true; + if (this.#contextAction?.getTarget() === ContextActionTarget.POINT && e.originalEvent.button === 2) this.#isRotatingDestination = true; this.scrollWheelZoom.disable(); this.#shortPressTimer = window.setTimeout(() => { @@ -1100,10 +1102,12 @@ export class Map extends L.Map { else document.dispatchEvent(new CustomEvent("forceboxselect", { detail: e.originalEvent })); } else if (getApp().getState() === OlympusState.UNIT_CONTROL) { if (e.originalEvent.button === 2) { - getApp().setState(OlympusState.UNIT_CONTROL, UnitControlSubState.MAP_CONTEXT_MENU); - MapContextMenuRequestEvent.dispatch(pressLocation); + if (!getApp().getMap().getContextAction()) { + getApp().setState(OlympusState.UNIT_CONTROL, UnitControlSubState.MAP_CONTEXT_MENU); + MapContextMenuRequestEvent.dispatch(pressLocation); + } } else { - if (this.#contextAction?.getTarget() === "position") { + if (this.#contextAction?.getTarget() === ContextActionTarget.POINT) { this.dragging.disable(); this.#isRotatingDestination = true; } else { @@ -1259,9 +1263,9 @@ export class Map extends L.Map { }); selectedUnits.forEach((unit) => { - if (["move", "path", "land-at-point"].includes(this.#contextAction?.getId() ?? "")) { + if (this.#contextAction?.getOptions().type === ContextActionType.MOVE) { this.#destinationPreviewMarkers[unit.ID] = new TemporaryUnitMarker(new L.LatLng(0, 0), unit.getName(), unit.getCoalition()); - } else if (this.#contextAction?.getTarget() === "position" && this.#contextAction?.getId() !== "land") { + } else if (this.#contextAction?.getTarget() === ContextActionTarget.POINT) { this.#destinationPreviewMarkers[unit.ID] = new TargetMarker(new L.LatLng(0, 0)); } this.#destinationPreviewMarkers[unit.ID]?.addTo(this); diff --git a/frontend/react/src/shortcut/shortcutmanager.ts b/frontend/react/src/shortcut/shortcutmanager.ts index 16046c58..07c882fb 100644 --- a/frontend/react/src/shortcut/shortcutmanager.ts +++ b/frontend/react/src/shortcut/shortcutmanager.ts @@ -1,3 +1,4 @@ +import { ContextActions, OlympusState } from "../constants/constants"; import { ShortcutKeyboardOptions, ShortcutMouseOptions } from "../interfaces"; import { getApp } from "../olympusapp"; import { ShortcutKeyboard, ShortcutMouse } from "./shortcut"; @@ -117,6 +118,26 @@ export class ShortcutManager { shiftKey: false, }); + for (let contextActionName in ContextActions) { + if (ContextActions[contextActionName].getOptions().hotkey) { + this.addKeyboardShortcut(`${contextActionName}Hotkey`, { + code: ContextActions[contextActionName].getOptions().hotkey, + shiftKey: true, + callback: () => { + const contextActionSet = getApp().getMap().getContextActionSet(); + if ( + getApp().getState() === OlympusState.UNIT_CONTROL && + contextActionSet && + ContextActions[contextActionName].getId() in contextActionSet.getContextActions() + ) { + if (ContextActions[contextActionName].getOptions().executeImmediately) ContextActions[contextActionName].executeCallback(); + else getApp().getMap().setContextAction(ContextActions[contextActionName]); + } + }, + }); + } + } + let PTTKeys = ["KeyZ", "KeyX", "KeyC", "KeyV", "KeyB", "KeyN", "KeyM", "KeyK", "KeyL"]; PTTKeys.forEach((key, idx) => { this.addKeyboardShortcut(`PTT${idx}Active`, { diff --git a/frontend/react/src/ui/contextmenus/mapcontextmenu.tsx b/frontend/react/src/ui/contextmenus/mapcontextmenu.tsx index 1be88d06..407b8b37 100644 --- a/frontend/react/src/ui/contextmenus/mapcontextmenu.tsx +++ b/frontend/react/src/ui/contextmenus/mapcontextmenu.tsx @@ -1,7 +1,7 @@ import React, { useEffect, useRef, useState } from "react"; import { Unit } from "../../unit/unit"; import { ContextAction } from "../../unit/contextaction"; -import { CONTEXT_ACTION_COLORS, NO_SUBSTATE, OlympusState, OlympusSubState, UnitControlSubState } from "../../constants/constants"; +import { CONTEXT_ACTION_COLORS, ContextActionTarget, NO_SUBSTATE, OlympusState, OlympusSubState, UnitControlSubState } from "../../constants/constants"; import { OlDropdownItem } from "../components/oldropdown"; import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; import { LatLng } from "leaflet"; @@ -69,9 +69,9 @@ export function MapContextMenu(props: {}) { }); let reorderedActions: ContextAction[] = contextActionSet - ? Object.values(contextActionSet.getContextActions(appSubState === UnitControlSubState.MAP_CONTEXT_MENU ? "position" : "unit")).sort( - (a: ContextAction, b: ContextAction) => (a.getOptions().type < b.getOptions().type ? -1 : 1) - ) + ? Object.values( + contextActionSet.getContextActions(appSubState === UnitControlSubState.MAP_CONTEXT_MENU ? ContextActionTarget.POINT : ContextActionTarget.UNIT) + ).sort((a: ContextAction, b: ContextAction) => (a.getOptions().type < b.getOptions().type ? -1 : 1)) : []; return ( @@ -107,13 +107,13 @@ export function MapContextMenu(props: {}) { if (contextActionIt.getOptions().executeImmediately) { contextActionIt.executeCallback(null, null); } else { - if (appSubState === UnitControlSubState.MAP_CONTEXT_MENU ) { + if (appSubState === UnitControlSubState.MAP_CONTEXT_MENU) { contextActionIt.executeCallback(null, latLng); } else if (unit !== null) { contextActionIt.executeCallback(unit, null); } } - getApp().setState(OlympusState.UNIT_CONTROL) + getApp().setState(OlympusState.UNIT_CONTROL); }} > diff --git a/frontend/react/src/ui/panels/unitcontrolbar.tsx b/frontend/react/src/ui/panels/unitcontrolbar.tsx index 27d2b399..f35ab42b 100644 --- a/frontend/react/src/ui/panels/unitcontrolbar.tsx +++ b/frontend/react/src/ui/panels/unitcontrolbar.tsx @@ -66,20 +66,30 @@ export function UnitControlBar(props: {}) {
onScroll(ev.target)} ref={scrollRef}> {reorderedActions.map((contextActionIt: ContextAction) => { return ( - { - if (contextActionIt.getOptions().executeImmediately) { - contextActionIt.executeCallback(null, null); - } else { - contextActionIt !== contextAction ? getApp().getMap().setContextAction(contextActionIt) : getApp().getMap().setContextAction(null); - } - }} - /> +
+ { + if (contextActionIt.getOptions().executeImmediately) { + contextActionIt.executeCallback(null, null); + } else { + contextActionIt !== contextAction ? getApp().getMap().setContextAction(contextActionIt) : getApp().getMap().setContextAction(null); + } + }} + /> +
+ {(contextActionIt.getOptions().hotkey ?? "").replace("Key", "")} +
+
); })}
@@ -120,7 +130,7 @@ export function UnitControlBar(props: {}) { )} - {*/} + {*/} )} diff --git a/frontend/react/src/unit/contextaction.ts b/frontend/react/src/unit/contextaction.ts index 636bbd4f..53ba0823 100644 --- a/frontend/react/src/unit/contextaction.ts +++ b/frontend/react/src/unit/contextaction.ts @@ -1,11 +1,12 @@ import { IconDefinition } from "@fortawesome/fontawesome-svg-core"; import { Unit } from "./unit"; import { LatLng } from "leaflet"; -import { ContextActionType } from "../constants/constants"; +import { ContextActionTarget, ContextActionType } from "../constants/constants"; export interface ContextActionOptions { executeImmediately?: boolean; type: ContextActionType; + hotkey?: string; } export type ContextActionCallback = (units: Unit[], targetUnit: Unit | null, targetPosition: LatLng | null, originalEvent?: MouseEvent) => void; @@ -18,9 +19,9 @@ export class ContextAction { #units: Unit[] = []; #icon: IconDefinition; #options: ContextActionOptions; - #target: "unit" | "position" | null = null; + #target: ContextActionTarget; - constructor(id: string, label: string, description: string, icon: IconDefinition, target: "unit" | "position" | null, callback: ContextActionCallback, options: ContextActionOptions) { + constructor(id: string, label: string, description: string, icon: IconDefinition, target: ContextActionTarget, callback: ContextActionCallback, options: ContextActionOptions) { this.#id = id; this.#label = label; this.#description = description; @@ -33,8 +34,8 @@ export class ContextAction { }; } - addUnit(unit: Unit) { - this.#units.push(unit); + setUnits(units: Unit[]) { + this.#units = units; } getId() { diff --git a/frontend/react/src/unit/contextactionset.ts b/frontend/react/src/unit/contextactionset.ts index 91eff67a..21fe4c4c 100644 --- a/frontend/react/src/unit/contextactionset.ts +++ b/frontend/react/src/unit/contextactionset.ts @@ -1,30 +1,26 @@ +import { ContextActionTarget } from "../constants/constants"; import { ContextAction, ContextActionCallback, ContextActionOptions } from "./contextaction"; import { Unit } from "./unit"; -import { IconDefinition } from "@fortawesome/fontawesome-svg-core"; export class ContextActionSet { #contextActions: { [key: string]: ContextAction } = {}; #defaultContextAction: ContextAction | null = null; + #units: { [key: string]: Unit[] } = {}; addContextAction( unit: Unit, - id: string, - label: string, - description: string, - icon: IconDefinition, - target: "unit" | "position" | null, - callback: ContextActionCallback, - options?: ContextActionOptions + contextAction: ContextAction ) { - options = options || {}; - - if (!(id in this.#contextActions)) { - this.#contextActions[id] = new ContextAction(id, label, description, icon, target, callback, options); + this.#contextActions[contextAction.getId()] = contextAction; + if (!(contextAction.getId() in this.#units)) { + this.#units[contextAction.getId()] = [] + this.#contextActions[contextAction.getId()].setUnits(this.#units[contextAction.getId()]); } - this.#contextActions[id].addUnit(unit); + + this.#units[contextAction.getId()].push(unit) } - getContextActions(targetFilter?: string) { + getContextActions(targetFilter?: ContextActionTarget) { if (targetFilter) { var filteredContextActionSet = new ContextActionSet(); Object.keys(this.#contextActions).forEach((key) => { @@ -36,17 +32,10 @@ export class ContextActionSet { addDefaultContextAction( unit: Unit, - id: string, - label: string, - description: string, - icon: IconDefinition, - target: "unit" | "position" | null, - callback: ContextActionCallback, - options?: ContextActionOptions + contextAction: ContextAction ) { - options = options || {}; - if (this.#defaultContextAction === null) this.#defaultContextAction = new ContextAction(id, label, description, icon, target, callback, options); - this.#defaultContextAction.addUnit(unit); + if (this.#defaultContextAction === null) this.#defaultContextAction = contextAction; + //this.#defaultContextAction.addUnit(unit); } getDefaultContextAction() { diff --git a/frontend/react/src/unit/unit.ts b/frontend/react/src/unit/unit.ts index e0898c2e..1bf5ba6c 100644 --- a/frontend/react/src/unit/unit.ts +++ b/frontend/react/src/unit/unit.ts @@ -39,6 +39,7 @@ import { JTACSubState, UnitControlSubState, ContextActionType, + ContextActions, } from "../constants/constants"; import { DataExtractor } from "../server/dataextractor"; import { Weapon } from "../weapon/weapon"; @@ -80,6 +81,7 @@ import { UnitSelectedEvent, UnitUpdatedEvent, } from "../events"; +import { ContextAction } from "./contextaction"; var pathIcon = new Icon({ iconUrl: "/vite/images/markers/marker-icon.png", @@ -838,102 +840,13 @@ export abstract class Unit extends CustomMarker { * */ appendContextActions(contextActionSet: ContextActionSet) { - contextActionSet.addContextAction( - this, - "stop", - "Stop unit", - "Stops the unit", - faHand, - null, - (units: Unit[], _1, _2) => { - getApp().getUnitsManager().stop(units); - }, - { - executeImmediately: true, - type: ContextActionType.MOVE, - } - ); + contextActionSet.addContextAction(this, ContextActions.STOP); + contextActionSet.addContextAction(this, ContextActions.MOVE); + contextActionSet.addContextAction(this, ContextActions.PATH); + contextActionSet.addContextAction(this, ContextActions.DELETE); + contextActionSet.addContextAction(this, ContextActions.EXPLODE); - contextActionSet.addContextAction( - this, - "move", - "Set destination", - "Click on the map to move the units there", - faLocationDot, - "position", - (units: Unit[], _, targetPosition, originalEvent) => { - if (!originalEvent?.ctrlKey) getApp().getUnitsManager().clearDestinations(units); - if (targetPosition) - getApp() - .getUnitsManager() - .addDestination(targetPosition, getApp().getMap().getOptions().keepRelativePositions, getApp().getMap().getDestinationRotation(), units); - }, - { type: ContextActionType.MOVE } - ); - - contextActionSet.addContextAction( - this, - "path", - "Create route", - "Click on the map to add a destination to the path", - faRoute, - "position", - (units: Unit[], _, targetPosition) => { - if (targetPosition) - getApp() - .getUnitsManager() - .addDestination(targetPosition, getApp().getMap().getOptions().keepRelativePositions, getApp().getMap().getDestinationRotation(), units); - }, - { type: ContextActionType.MOVE } - ); - - contextActionSet.addContextAction( - this, - "delete", - "Delete unit", - "Deletes the unit", - faTrash, - null, - (units: Unit[], _1, _2) => { - getApp().getUnitsManager().delete(false); - }, - { - executeImmediately: true, - type: ContextActionType.DELETE, - } - ); - - contextActionSet.addContextAction( - this, - "explode", - "Explode unit", - "Explodes the unit", - faExplosion, - null, - (units: Unit[], _1, _2) => { - getApp().setState(OlympusState.UNIT_CONTROL, UnitControlSubState.UNIT_EXPLOSION_MENU); - UnitExplosionRequestEvent.dispatch(units); - }, - { - executeImmediately: true, - type: ContextActionType.DELETE, - } - ); - - contextActionSet.addDefaultContextAction( - this, - "default", - "Set destination", - "", - faRoute, - null, - (units: Unit[], targetUnit, targetPosition, originalEvent) => { - if (targetPosition) { - if (!originalEvent?.ctrlKey) getApp().getUnitsManager().clearDestinations(units); - getApp().getUnitsManager().addDestination(targetPosition, false, 0, units); - } - } - ); + contextActionSet.addDefaultContextAction(this, ContextActions.MOVE); } drawLines() { @@ -1452,7 +1365,7 @@ export abstract class Unit extends CustomMarker { #onLongPress(e: any) { console.log(`Long press on ${this.getUnitName()}`); - if (e.originalEvent.button === 2) { + if (e.originalEvent.button === 2 && !getApp().getMap().getContextAction()) { getApp().setState(OlympusState.UNIT_CONTROL, UnitControlSubState.UNIT_CONTEXT_MENU); UnitContextMenuRequestEvent.dispatch(this); } @@ -1869,111 +1782,20 @@ export abstract class AirUnit extends Unit { super.appendContextActions(contextActionSet); /* Context actions to be executed immediately */ - contextActionSet.addContextAction( - this, - "refuel", - "Refuel at tanker", - "Refuel units at the nearest AAR Tanker. If no tanker is available the unit will RTB", - olButtonsContextRefuel, - null, - (units: Unit[]) => { - getApp().getUnitsManager().refuel(units); - }, - { executeImmediately: true, type: ContextActionType.ADMIN } - ); - contextActionSet.addContextAction( - this, - "center-map", - "Center map", - "Center the map on the unit and follow it", - faMapLocation, - null, - (units: Unit[]) => { - getApp().getMap().centerOnUnit(units[0]); - }, - { executeImmediately: true, type: ContextActionType.OTHER } - ); + contextActionSet.addContextAction(this, ContextActions.REFUEL); + contextActionSet.addContextAction(this, ContextActions.CENTER_MAP); /* Context actions that require a target unit */ - contextActionSet.addContextAction( - this, - "attack", - "Attack unit", - "Click on a unit to attack it using A/A or A/G weapons", - olButtonsContextAttack, - "unit", - (units: Unit[], targetUnit: Unit | null, _) => { - if (targetUnit) getApp().getUnitsManager().attackUnit(targetUnit.ID, units); - }, - { type: ContextActionType.ENGAGE } - ); - - contextActionSet.addContextAction( - this, - "follow", - "Follow unit", - "Click on a unit to follow it in formation", - olButtonsContextFollow, - "unit", - (units: Unit[], targetUnit: Unit | null, _) => { - if (targetUnit) { - getApp().setState(OlympusState.UNIT_CONTROL, UnitControlSubState.FORMATION); - FormationCreationRequestEvent.dispatch( - targetUnit, - units.filter((unit) => unit !== targetUnit) - ); - } - }, - { type: ContextActionType.ADMIN } - ); + contextActionSet.addContextAction(this, ContextActions.ATTACK); + contextActionSet.addContextAction(this, ContextActions.FOLLOW); if (this.canTargetPoint()) { /* Context actions that require a target position */ - contextActionSet.addContextAction( - this, - "bomb", - "Precision bomb location", - "Click on a point to execute a precision bombing attack", - faLocationCrosshairs, - "position", - (units: Unit[], _, targetPosition: LatLng | null) => { - if (targetPosition) - getApp() - .getUnitsManager() - .bombPoint(targetPosition, getApp().getMap().getOptions().keepRelativePositions, getApp().getMap().getDestinationRotation(), units); - }, - { type: ContextActionType.ENGAGE } - ); - - contextActionSet.addContextAction( - this, - "carpet-bomb", - "Carpet bomb location", - "Click on a point to execute a carpet bombing attack", - faXmarksLines, - "position", - (units: Unit[], _, targetPosition: LatLng | null) => { - if (targetPosition) - getApp() - .getUnitsManager() - .carpetBomb(targetPosition, getApp().getMap().getOptions().keepRelativePositions, getApp().getMap().getDestinationRotation(), units); - }, - { type: ContextActionType.ENGAGE } - ); + contextActionSet.addContextAction(this, ContextActions.BOMB); + contextActionSet.addContextAction(this, ContextActions.CARPET_BOMB); } - contextActionSet.addContextAction( - this, - "land", - "Land", - "Click on a point to land at the nearest airbase", - faPlaneArrival, - "position", - (units: Unit[], _, targetPosition: LatLng | null) => { - if (targetPosition) getApp().getUnitsManager().landAt(targetPosition, units); - }, - { type: ContextActionType.ADMIN } - ); + contextActionSet.addContextAction(this, ContextActions.LAND); } } @@ -2010,21 +1832,7 @@ export class Helicopter extends AirUnit { appendContextActions(contextActionSet: ContextActionSet) { super.appendContextActions(contextActionSet); - contextActionSet.addContextAction( - this, - "land-at-point", - "Land at location", - "Click on a point to land there", - olButtonsContextLandAtPoint, - "position", - (units: Unit[], _, targetPosition: LatLng | null) => { - if (targetPosition) - getApp() - .getUnitsManager() - .landAtPoint(targetPosition, getApp().getMap().getOptions().keepRelativePositions, getApp().getMap().getDestinationRotation(), units); - }, - { type: ContextActionType.ADMIN } - ); + contextActionSet.addContextAction(this, ContextActions.LAND_AT_POINT); } getMarkerCategory() { @@ -2062,77 +1870,16 @@ export class GroundUnit extends Unit { super.appendContextActions(contextActionSet); /* Context actions to be executed immediately */ - contextActionSet.addContextAction( - this, - "group-ground", - "Group ground units", - "Create a group of ground units", - faPeopleGroup, - null, - (units: Unit[], _1, _2) => { - getApp().getUnitsManager().createGroup(units); - }, - { executeImmediately: true, type: ContextActionType.OTHER } - ); - contextActionSet.addContextAction( - this, - "center-map", - "Center map", - "Center the map on the unit and follow it", - faMapLocation, - null, - (units: Unit[]) => { - getApp().getMap().centerOnUnit(units[0]); - }, - { executeImmediately: true, type: ContextActionType.OTHER } - ); + contextActionSet.addContextAction(this, ContextActions.GROUP); + contextActionSet.addContextAction(this, ContextActions.CENTER_MAP); /* Context actions that require a target unit */ - contextActionSet.addContextAction( - this, - "attack", - "Attack unit", - "Click on a unit to attack it", - olButtonsContextAttack, - "unit", - (units: Unit[], targetUnit: Unit | null, _) => { - if (targetUnit) getApp().getUnitsManager().attackUnit(targetUnit.ID, units); - }, - { type: ContextActionType.ENGAGE } - ); + contextActionSet.addContextAction(this, ContextActions.ATTACK); /* Context actions that require a target position */ if (this.canTargetPoint()) { - contextActionSet.addContextAction( - this, - "fire-at-area", - "Fire at area", - "Click on a point to precisely fire at it (if possible)", - faLocationCrosshairs, - "position", - (units: Unit[], _, targetPosition: LatLng | null) => { - if (targetPosition) - getApp() - .getUnitsManager() - .fireAtArea(targetPosition, getApp().getMap().getOptions().keepRelativePositions, getApp().getMap().getDestinationRotation(), units); - }, - { type: ContextActionType.ENGAGE } - ); - contextActionSet.addContextAction( - this, - "simulate-fire-fight", - "Simulate fire fight", - "Simulate a fire fight by shooting randomly in a certain large area. WARNING: works correctly only on neutral units, blue or red units will aim", - olButtonsContextSimulateFireFight, - "position", - (units: Unit[], _, targetPosition: LatLng | null) => { - if (targetPosition) - getApp() - .getUnitsManager() - .simulateFireFight(targetPosition, getApp().getMap().getOptions().keepRelativePositions, getApp().getMap().getDestinationRotation(), units); - }, - { type: ContextActionType.ADMIN } - ); + contextActionSet.addContextAction(this, ContextActions.FIRE_AT_AREA); + contextActionSet.addContextAction(this, ContextActions.SIMULATE_FIRE_FIGHT); } } @@ -2192,61 +1939,14 @@ export class NavyUnit extends Unit { super.appendContextActions(contextActionSet); /* Context actions to be executed immediately */ - contextActionSet.addContextAction( - this, - "group-navy", - "Group navy units", - "Create a group of navy units", - faQuestionCircle, - null, - (units: Unit[], _1, _2) => { - getApp().getUnitsManager().createGroup(units); - }, - { executeImmediately: true, type: ContextActionType.OTHER } - ); - contextActionSet.addContextAction( - this, - "center-map", - "Center map", - "Center the map on the unit and follow it", - faMapLocation, - null, - (units: Unit[]) => { - getApp().getMap().centerOnUnit(units[0]); - }, - { executeImmediately: true, type: ContextActionType.OTHER } - ); + contextActionSet.addContextAction(this, ContextActions.GROUP); + contextActionSet.addContextAction(this, ContextActions.CENTER_MAP); /* Context actions that require a target unit */ - contextActionSet.addContextAction( - this, - "attack", - "Attack unit", - "Click on a unit to attack it", - olButtonsContextAttack, - "unit", - (units: Unit[], targetUnit: Unit | null, _) => { - if (targetUnit) getApp().getUnitsManager().attackUnit(targetUnit.ID, units); - }, - { type: ContextActionType.ENGAGE } - ); + contextActionSet.addContextAction(this, ContextActions.ATTACK); /* Context actions that require a target position */ - contextActionSet.addContextAction( - this, - "fire-at-area", - "Fire at area", - "Click on a point to precisely fire at it (if possible)", - faLocationCrosshairs, - "position", - (units: Unit[], _, targetPosition: LatLng | null) => { - if (targetPosition) - getApp() - .getUnitsManager() - .fireAtArea(targetPosition, getApp().getMap().getOptions().keepRelativePositions, getApp().getMap().getDestinationRotation(), units); - }, - { type: ContextActionType.ENGAGE } - ); + contextActionSet.addContextAction(this, ContextActions.FIRE_AT_AREA); } getCategory() {