Merge and fix
1
.gitignore
vendored
@ -14,3 +14,4 @@ node_modules
|
||||
/client/plugins/controltips/index.js
|
||||
hgt
|
||||
/client/public/databases/units/old
|
||||
/client/plugins/databasemanager/index.js
|
||||
|
||||
152
client/@types/olympus/index.d.ts
vendored
@ -254,6 +254,7 @@ declare module "constants/constants" {
|
||||
operateAs = 41,
|
||||
shotsScatter = 42,
|
||||
shotsIntensity = 43,
|
||||
health = 44,
|
||||
endOfData = 255
|
||||
}
|
||||
export const MGRS_PRECISION_10KM = 2;
|
||||
@ -313,7 +314,7 @@ declare module "controls/dropdown" {
|
||||
#private;
|
||||
constructor(ID: string | null, callback: CallableFunction, options?: string[] | null, defaultText?: string);
|
||||
getContainer(): HTMLElement;
|
||||
setOptions(optionsList: string[], sort?: "" | "string" | "number"): void;
|
||||
setOptions(optionsList: string[], sort?: "" | "string" | "number" | "string+number"): void;
|
||||
setOptionsElements(optionsElements: HTMLElement[]): void;
|
||||
getOptionElements(): HTMLCollection;
|
||||
addOptionElement(optionElement: HTMLElement): void;
|
||||
@ -449,6 +450,7 @@ declare module "interfaces" {
|
||||
export interface ObjectIconOptions {
|
||||
showState: boolean;
|
||||
showVvi: boolean;
|
||||
showHealth: boolean;
|
||||
showHotgroup: boolean;
|
||||
showUnitIcon: boolean;
|
||||
showShortLabel: boolean;
|
||||
@ -537,6 +539,7 @@ declare module "interfaces" {
|
||||
operateAs: string;
|
||||
shotsScatter: number;
|
||||
shotsIntensity: number;
|
||||
health: number;
|
||||
}
|
||||
export interface LoadoutItemBlueprint {
|
||||
name: string;
|
||||
@ -576,6 +579,7 @@ declare module "interfaces" {
|
||||
shotsBaseScatter?: number;
|
||||
description?: string;
|
||||
abilities?: string;
|
||||
tags?: string;
|
||||
acquisitionRange?: number;
|
||||
engagementRange?: number;
|
||||
targetingRange?: number;
|
||||
@ -585,6 +589,8 @@ declare module "interfaces" {
|
||||
canRearm?: boolean;
|
||||
canAAA?: boolean;
|
||||
indirectFire?: boolean;
|
||||
markerFile?: string;
|
||||
unitWhenGrouped?: string;
|
||||
}
|
||||
export interface UnitSpawnOptions {
|
||||
roleType: string;
|
||||
@ -768,7 +774,7 @@ declare module "other/utils" {
|
||||
ranges?: string[];
|
||||
eras?: string[];
|
||||
}): UnitBlueprint | null;
|
||||
export function getMarkerCategoryByName(name: string): "aircraft" | "helicopter" | "groundunit-sam" | "groundunit-other" | "groundunit-sam-radar" | "groundunit-sam-launcher" | "groundunit-ewr";
|
||||
export function getMarkerCategoryByName(name: string): "aircraft" | "helicopter" | "groundunit-other" | "navyunit" | "groundunit";
|
||||
export function getUnitDatabaseByCategory(category: string): import("unit/databases/aircraftdatabase").AircraftDatabase | import("unit/databases/helicopterdatabase").HelicopterDatabase | import("unit/databases/groundunitdatabase").GroundUnitDatabase | import("unit/databases/navyunitdatabase").NavyUnitDatabase | null;
|
||||
export function base64ToBytes(base64: string): ArrayBufferLike;
|
||||
export function enumToState(state: number): string;
|
||||
@ -1021,6 +1027,7 @@ declare module "weapon/weapon" {
|
||||
getIconOptions(): {
|
||||
showState: boolean;
|
||||
showVvi: boolean;
|
||||
showHealth: boolean;
|
||||
showHotgroup: boolean;
|
||||
showUnitIcon: boolean;
|
||||
showShortLabel: boolean;
|
||||
@ -1038,6 +1045,7 @@ declare module "weapon/weapon" {
|
||||
getIconOptions(): {
|
||||
showState: boolean;
|
||||
showVvi: boolean;
|
||||
showHealth: boolean;
|
||||
showHotgroup: boolean;
|
||||
showUnitIcon: boolean;
|
||||
showShortLabel: boolean;
|
||||
@ -1049,12 +1057,29 @@ declare module "weapon/weapon" {
|
||||
};
|
||||
}
|
||||
}
|
||||
declare module "map/rangecircle" {
|
||||
import { Circle } from 'leaflet';
|
||||
/**
|
||||
* This custom Circle object implements a faster render method for very big circles. When zoomed in, the default ctx.arc method
|
||||
* is very slow since the circle is huge. Also, when zoomed in most of the circle points will be outside the screen and not needed. This
|
||||
* simpler, faster renderer approximates the circle with line segements and only draws those currently visibile.
|
||||
* A more refined version using arcs could be implemented but this works good enough.
|
||||
*/
|
||||
export class RangeCircle extends Circle {
|
||||
_updatePath(): void;
|
||||
}
|
||||
}
|
||||
declare module "unit/unit" {
|
||||
import { LatLng, Map } from 'leaflet';
|
||||
import { CustomMarker } from "map/markers/custommarker";
|
||||
import { UnitDatabase } from "unit/databases/unitdatabase";
|
||||
import { DataExtractor } from "server/dataextractor";
|
||||
import { Ammo, Contact, GeneralSettings, ObjectIconOptions, Offset, Radio, TACAN, UnitData } from "interfaces";
|
||||
/**
|
||||
* Unit class which controls unit behaviour
|
||||
*
|
||||
* Just about everything is a unit - even missiles!
|
||||
*/
|
||||
export class Unit extends CustomMarker {
|
||||
#private;
|
||||
ID: number;
|
||||
@ -1074,8 +1099,8 @@ declare module "unit/unit" {
|
||||
getHorizontalVelocity(): number;
|
||||
getVerticalVelocity(): number;
|
||||
getHeading(): number;
|
||||
getIsActiveTanker(): boolean;
|
||||
getIsActiveAWACS(): boolean;
|
||||
getIsActiveTanker(): boolean;
|
||||
getOnOff(): boolean;
|
||||
getFollowRoads(): boolean;
|
||||
getFuel(): number;
|
||||
@ -1100,26 +1125,88 @@ declare module "unit/unit" {
|
||||
getOperateAs(): string;
|
||||
getShotsScatter(): number;
|
||||
getShotsIntensity(): number;
|
||||
getHealth(): number;
|
||||
static getConstructor(type: string): typeof GroundUnit | undefined;
|
||||
constructor(ID: number);
|
||||
getCategory(): string;
|
||||
/********************** Unit data *************************/
|
||||
setData(dataExtractor: DataExtractor): void;
|
||||
drawLines(): void;
|
||||
/** Get unit data collated into an object
|
||||
*
|
||||
* @returns object populated by unit information which can also be retrieved using getters
|
||||
*/
|
||||
getData(): UnitData;
|
||||
/**
|
||||
*
|
||||
* @returns string containing the marker category
|
||||
*/
|
||||
getMarkerCategory(): string;
|
||||
/** Get a database of information also in this unit's category
|
||||
*
|
||||
* @returns UnitDatabase
|
||||
*/
|
||||
getDatabase(): UnitDatabase | null;
|
||||
/** Get the icon options
|
||||
* Used to configure how the marker appears on the map
|
||||
*
|
||||
* @returns ObjectIconOptions
|
||||
*/
|
||||
getIconOptions(): ObjectIconOptions;
|
||||
/** Set the unit as alive or dead
|
||||
*
|
||||
* @param newAlive (boolean) true = alive, false = dead
|
||||
*/
|
||||
setAlive(newAlive: boolean): void;
|
||||
/** Set the unit as user-selected
|
||||
*
|
||||
* @param selected (boolean)
|
||||
*/
|
||||
setSelected(selected: boolean): void;
|
||||
/** Is this unit selected?
|
||||
*
|
||||
* @returns boolean
|
||||
*/
|
||||
getSelected(): boolean;
|
||||
/** Set whether this unit is selectable
|
||||
*
|
||||
* @param selectable (boolean)
|
||||
*/
|
||||
setSelectable(selectable: boolean): void;
|
||||
/** Get whether this unit is selectable
|
||||
*
|
||||
* @returns boolean
|
||||
*/
|
||||
getSelectable(): boolean;
|
||||
/** Set the number of the hotgroup to which the unit belongs
|
||||
*
|
||||
* @param hotgroup (number)
|
||||
*/
|
||||
setHotgroup(hotgroup: number | null): void;
|
||||
/** Get the unit's hotgroup number
|
||||
*
|
||||
* @returns number
|
||||
*/
|
||||
getHotgroup(): number | null;
|
||||
/** Set the unit as highlighted
|
||||
*
|
||||
* @param highlighted (boolean)
|
||||
*/
|
||||
setHighlighted(highlighted: boolean): void;
|
||||
/** Get whether the unit is highlighted or not
|
||||
*
|
||||
* @returns boolean
|
||||
*/
|
||||
getHighlighted(): boolean;
|
||||
/** Get the other members of the group which this unit is in
|
||||
*
|
||||
* @returns Unit[]
|
||||
*/
|
||||
getGroupMembers(): Unit[];
|
||||
/** Returns whether the user is allowed to command this unit, based on coalition
|
||||
*
|
||||
* @returns boolean
|
||||
*/
|
||||
belongsToCommandedCoalition(): boolean;
|
||||
getType(): string;
|
||||
getSpawnPoints(): number | undefined;
|
||||
@ -1163,7 +1250,7 @@ declare module "unit/unit" {
|
||||
setOnOff(onOff: boolean): void;
|
||||
setFollowRoads(followRoads: boolean): void;
|
||||
setOperateAs(operateAs: string): void;
|
||||
delete(explosion: boolean, immediate: boolean): void;
|
||||
delete(explosion: boolean, explosionType: string, immediate: boolean): void;
|
||||
refuel(): void;
|
||||
setAdvancedOptions(isActiveTanker: boolean, isActiveAWACS: boolean, TACAN: TACAN, radio: Radio, generalSettings: GeneralSettings): void;
|
||||
bombPoint(latlng: LatLng): void;
|
||||
@ -1193,6 +1280,7 @@ declare module "unit/unit" {
|
||||
getIconOptions(): {
|
||||
showState: boolean;
|
||||
showVvi: boolean;
|
||||
showHealth: boolean;
|
||||
showHotgroup: boolean;
|
||||
showUnitIcon: boolean;
|
||||
showShortLabel: boolean;
|
||||
@ -1223,6 +1311,7 @@ declare module "unit/unit" {
|
||||
getIconOptions(): {
|
||||
showState: boolean;
|
||||
showVvi: boolean;
|
||||
showHealth: boolean;
|
||||
showHotgroup: boolean;
|
||||
showUnitIcon: boolean;
|
||||
showShortLabel: boolean;
|
||||
@ -1247,6 +1336,7 @@ declare module "unit/unit" {
|
||||
getIconOptions(): {
|
||||
showState: boolean;
|
||||
showVvi: boolean;
|
||||
showHealth: boolean;
|
||||
showHotgroup: boolean;
|
||||
showUnitIcon: boolean;
|
||||
showShortLabel: boolean;
|
||||
@ -1618,6 +1708,30 @@ declare module "panels/serverstatuspanel" {
|
||||
update(frameRate: number, load: number): void;
|
||||
}
|
||||
}
|
||||
declare module "toolbars/toolbar" {
|
||||
export class Toolbar {
|
||||
#private;
|
||||
/**
|
||||
*
|
||||
* @param ID - the ID of the HTML element which will contain the context menu
|
||||
*/
|
||||
constructor(ID: string);
|
||||
show(): void;
|
||||
hide(): void;
|
||||
toggle(): void;
|
||||
getElement(): HTMLElement;
|
||||
getVisible(): boolean;
|
||||
}
|
||||
}
|
||||
declare module "toolbars/primarytoolbar" {
|
||||
import { Dropdown } from "controls/dropdown";
|
||||
import { Toolbar } from "toolbars/toolbar";
|
||||
export class PrimaryToolbar extends Toolbar {
|
||||
#private;
|
||||
constructor(ID: string);
|
||||
getMainDropdown(): Dropdown;
|
||||
}
|
||||
}
|
||||
declare module "panels/unitcontrolpanel" {
|
||||
import { Panel } from "panels/panel";
|
||||
export class UnitControlPanel extends Panel {
|
||||
@ -1680,35 +1794,11 @@ declare module "shortcut/shortcutmanager" {
|
||||
onKeyUp(callback: CallableFunction): void;
|
||||
}
|
||||
}
|
||||
declare module "toolbars/toolbar" {
|
||||
export class Toolbar {
|
||||
#private;
|
||||
/**
|
||||
*
|
||||
* @param ID - the ID of the HTML element which will contain the context menu
|
||||
*/
|
||||
constructor(ID: string);
|
||||
show(): void;
|
||||
hide(): void;
|
||||
toggle(): void;
|
||||
getElement(): HTMLElement;
|
||||
getVisible(): boolean;
|
||||
}
|
||||
}
|
||||
declare module "toolbars/commandmodetoolbar" {
|
||||
import { Toolbar } from "toolbars/toolbar";
|
||||
export class CommandModeToolbar extends Toolbar {
|
||||
}
|
||||
}
|
||||
declare module "toolbars/primarytoolbar" {
|
||||
import { Dropdown } from "controls/dropdown";
|
||||
import { Toolbar } from "toolbars/toolbar";
|
||||
export class PrimaryToolbar extends Toolbar {
|
||||
#private;
|
||||
constructor(ID: string);
|
||||
getMainDropdown(): Dropdown;
|
||||
}
|
||||
}
|
||||
declare module "unit/citiesDatabase" {
|
||||
export var citiesDatabase: {
|
||||
lat: number;
|
||||
@ -1996,7 +2086,7 @@ declare module "unit/unitsmanager" {
|
||||
* @param explosion If true, the unit will be deleted using an explosion
|
||||
* @returns
|
||||
*/
|
||||
selectedUnitsDelete(explosion?: boolean): void;
|
||||
selectedUnitsDelete(explosion?: boolean, explosionType?: string): void;
|
||||
/** Compute the destinations of every unit in the selected units. This function preserves the relative positions of the units, and rotates the whole formation by rotation.
|
||||
*
|
||||
* @param latlng Center of the group after the translation
|
||||
@ -2116,7 +2206,7 @@ declare module "server/servermanager" {
|
||||
isCommandExecuted(callback: CallableFunction, commandHash: string): void;
|
||||
addDestination(ID: number, path: any, callback?: CallableFunction): void;
|
||||
spawnSmoke(color: string, latlng: LatLng, callback?: CallableFunction): void;
|
||||
spawnExplosion(intensity: number, latlng: LatLng, callback?: CallableFunction): void;
|
||||
spawnExplosion(intensity: number, explosionType: string, latlng: LatLng, callback?: CallableFunction): void;
|
||||
spawnAircrafts(units: any, coalition: string, airbaseName: string, country: string, immediate: boolean, spawnPoints: number, callback?: CallableFunction): void;
|
||||
spawnHelicopters(units: any, coalition: string, airbaseName: string, country: string, immediate: boolean, spawnPoints: number, callback?: CallableFunction): void;
|
||||
spawnGroundUnits(units: any, coalition: string, country: string, immediate: boolean, spawnPoints: number, callback?: CallableFunction): void;
|
||||
@ -2131,7 +2221,7 @@ declare module "server/servermanager" {
|
||||
ID: number;
|
||||
location: LatLng;
|
||||
}[], deleteOriginal: boolean, spawnPoints: number, callback?: CallableFunction): void;
|
||||
deleteUnit(ID: number, explosion: boolean, immediate: boolean, callback?: CallableFunction): void;
|
||||
deleteUnit(ID: number, explosion: boolean, explosionType: string, immediate: boolean, callback?: CallableFunction): void;
|
||||
landAt(ID: number, latlng: LatLng, callback?: CallableFunction): void;
|
||||
changeSpeed(ID: number, speedChange: string, callback?: CallableFunction): void;
|
||||
setSpeed(ID: number, speed: number, callback?: CallableFunction): void;
|
||||
|
||||
@ -1,5 +1,9 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
var fs = require('fs');
|
||||
let rawdata = fs.readFileSync('../olympus.json');
|
||||
let config = JSON.parse(rawdata);
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@ -12,8 +16,14 @@ var http = require('http');
|
||||
* Get port from environment and store in Express.
|
||||
*/
|
||||
|
||||
var port = normalizePort(process.env.PORT || '3000');
|
||||
var configPort = null;
|
||||
if (config["client"] != undefined && config["client"]["port"] != undefined) {
|
||||
configPort = config["client"]["port"];
|
||||
}
|
||||
|
||||
var port = normalizePort(configPort || '3000');
|
||||
app.set('port', port);
|
||||
console.log("Express server listening on port: " + port)
|
||||
|
||||
/**
|
||||
* Create HTTP server.
|
||||
|
||||
296
client/demo.js
@ -2,124 +2,15 @@ const { random } = require('@turf/turf');
|
||||
var basicAuth = require('express-basic-auth')
|
||||
var enc = new TextEncoder();
|
||||
|
||||
const DEMO_UNIT_DATA = {
|
||||
["1"]:{ category: "Aircraft", alive: true, human: false, controlled: true, coalition: 2, country: 0, name: "KC-135", unitName: "Cool guy 1-1 who also has a very long name", groupName: "Cool group 1", state: 1, task: "Being cool!",
|
||||
hasTask: true, position: { lat: 37, lng: -116, alt: 1000 }, speed: 200, horizontalVelocity: 200, verticalVelicity: 0, heading: 45, isActiveTanker: true, isActiveAWACS: false, onOff: true, followRoads: false, fuel: 50,
|
||||
desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0,
|
||||
formationOffset: { x: 0, y: 0, z: 0 },
|
||||
targetID: 0,
|
||||
targetPosition: { lat: 0, lng: 0, alt: 0 },
|
||||
ROE: 1,
|
||||
reactionToThreat: 1,
|
||||
emissionsCountermeasures: 1,
|
||||
TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 },
|
||||
radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 },
|
||||
generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false },
|
||||
ammo: [{ quantity: 2, name: "A cool missile\0Ciao", guidance: 0, category: 0, missileCategory: 0 }, { quantity: 2, name: "A cool missile with a longer name\0Ciao", guidance: 0, category: 0, missileCategory: 0 }, { quantity: 2, name: "A cool missile\0Ciao", guidance: 0, category: 0, missileCategory: 0 } , { quantity: 2, name: "A cool missile\0Ciao", guidance: 0, category: 0, missileCategory: 0 } , { quantity: 2, name: "A cool missile\0Ciao", guidance: 0, category: 0, missileCategory: 0 } , { quantity: 2, name: "A cool missile\0Ciao", guidance: 0, category: 0, missileCategory: 0 } , { quantity: 2, name: "A cool missile\0Ciao", guidance: 0, category: 0, missileCategory: 0 } , { quantity: 2, name: "A cool missile\0Ciao", guidance: 0, category: 0, missileCategory: 0 } , { quantity: 2, name: "A cool missile\0Ciao", guidance: 0, category: 0, missileCategory: 0 } ],
|
||||
contacts: [{ID: 2, detectionMethod: 1}, {ID: 3, detectionMethod: 4}, {ID: 4, detectionMethod: 1}],
|
||||
activePath: [{lat: 38, lng: -115, alt: 0}, {lat: 38, lng: -114, alt: 0}]
|
||||
},
|
||||
["2"]:{ category: "Aircraft", alive: true, human: false, controlled: true, coalition: 1, country: 0, name: "E-3A", unitName: "Cool guy 1-2", groupName: "Cool group 2", state: 1, task: "Being cool",
|
||||
hasTask: true, position: { lat: 36.9, lng: -116, alt: 1000 }, speed: 200, horizontalVelocity: 200, verticalVelicity: 0, heading: 315 * Math.PI / 180, isActiveTanker: false, isActiveAWACS: true, onOff: true, followRoads: false, fuel: 50,
|
||||
desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0,
|
||||
formationOffset: { x: 0, y: 0, z: 0 },
|
||||
targetID: 0,
|
||||
targetPosition: { lat: 0, lng: 0, alt: 0 },
|
||||
ROE: 1,
|
||||
reactionToThreat: 1,
|
||||
emissionsCountermeasures: 1,
|
||||
TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 },
|
||||
radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 },
|
||||
generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false },
|
||||
ammo: [{ quantity: 2, name: "A cool missile", guidance: 0, category: 0, missileCategory: 0 } ],
|
||||
contacts: [{ID: 4, detectionMethod: 1}],
|
||||
activePath: [ ]
|
||||
}, ["3"]:{ category: "Helicopter", alive: true, human: false, controlled: false, coalition: 1, country: 0, name: "AH-64D_BLK_II", unitName: "Cool guy 1-4", groupName: "Cool group 3", state: 1, task: "Being cool",
|
||||
hasTask: false, position: { lat: 37.1, lng: -116.1, alt: 1000 }, speed: 200, horizontalVelocity: 200, verticalVelicity: 0, heading: 315 * Math.PI / 180, isActiveTanker: false, isActiveAWACS: false, onOff: true, followRoads: false, fuel: 50,
|
||||
desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0,
|
||||
formationOffset: { x: 0, y: 0, z: 0 },
|
||||
targetID: 0,
|
||||
targetPosition: { lat: 0, lng: 0, alt: 0 },
|
||||
ROE: 1,
|
||||
reactionToThreat: 1,
|
||||
emissionsCountermeasures: 1,
|
||||
TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 },
|
||||
radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 },
|
||||
generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false },
|
||||
ammo: [{ quantity: 2, name: "A cool missile", guidance: 0, category: 0, missileCategory: 0 } ],
|
||||
contacts: [{ID: 1, detectionMethod: 16}],
|
||||
activePath: [ ]
|
||||
}, ["4"]:{ category: "GroundUnit", alive: true, human: false, controlled: true, coalition: 2, country: 0, name: "Tor 9A331", unitName: "Cool guy 2-1", groupName: "Cool group 4", state: 1, task: "Being cool",
|
||||
hasTask: false, position: { lat: 37.2, lng: -116.1, alt: 1000 }, speed: 200, horizontalVelocity: 200, verticalVelicity: 0, heading: 315 * Math.PI / 180, isActiveTanker: false, isActiveAWACS: false, onOff: false, followRoads: false, fuel: 50,
|
||||
desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0,
|
||||
formationOffset: { x: 0, y: 0, z: 0 },
|
||||
targetID: 0,
|
||||
targetPosition: { lat: 0, lng: 0, alt: 0 },
|
||||
ROE: 1,
|
||||
reactionToThreat: 1,
|
||||
emissionsCountermeasures: 1,
|
||||
TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 },
|
||||
radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 },
|
||||
generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false },
|
||||
ammo: [{ quantity: 2, name: "A cool missile\0Ciao", guidance: 0, category: 0, missileCategory: 0 } ],
|
||||
contacts: [{ID: 1001, detectionMethod: 16}],
|
||||
activePath: [ ],
|
||||
isLeader: true,
|
||||
operateAs: 2
|
||||
}, ["5"]:{ category: "GroundUnit", alive: true, human: false, controlled: true, coalition: 2, country: 0, name: "Gepard", unitName: "Cool guy 2-2", groupName: "Cool group 4", state: 1, task: "Being cool",
|
||||
hasTask: false, position: { lat: 37.21, lng: -116.1, alt: 1000 }, speed: 200, horizontalVelocity: 200, verticalVelicity: 0, heading: 315 * Math.PI / 180, isActiveTanker: false, isActiveAWACS: false, onOff: false, followRoads: false, fuel: 50,
|
||||
desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0,
|
||||
formationOffset: { x: 0, y: 0, z: 0 },
|
||||
targetID: 0,
|
||||
targetPosition: { lat: 0, lng: 0, alt: 0 },
|
||||
ROE: 1,
|
||||
reactionToThreat: 1,
|
||||
emissionsCountermeasures: 1,
|
||||
TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 },
|
||||
radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 },
|
||||
generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false },
|
||||
ammo: [{ quantity: 2, name: "A cool missile", guidance: 0, category: 0, missileCategory: 0 } ],
|
||||
contacts: [],
|
||||
activePath: [ ],
|
||||
isLeader: false,
|
||||
operateAs: 2
|
||||
},
|
||||
["6"]:{ category: "Aircraft", alive: true, human: false, controlled: false, coalition: 1, country: 0, name: "FA-18C_hornet", unitName: "Bad boi 1-2", groupName: "Bad group 1", state: 1, task: "Being bad",
|
||||
hasTask: false, position: { lat: 36.8, lng: -116, alt: 1000 }, speed: 200, horizontalVelocity: 200, verticalVelicity: 0, heading: 315 * Math.PI / 180, isActiveTanker: false, isActiveAWACS: false, onOff: true, followRoads: false, fuel: 50,
|
||||
desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0,
|
||||
formationOffset: { x: 0, y: 0, z: 0 },
|
||||
targetID: 0,
|
||||
targetPosition: { lat: 0, lng: 0, alt: 0 },
|
||||
ROE: 1,
|
||||
reactionToThreat: 1,
|
||||
emissionsCountermeasures: 1,
|
||||
TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 },
|
||||
radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 },
|
||||
generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false },
|
||||
ammo: [{ quantity: 2, name: "A cool missile", guidance: 0, category: 0, missileCategory: 0 } ],
|
||||
contacts: [{ID: 1, detectionMethod: 16}],
|
||||
activePath: [ ]
|
||||
}, ["7"]:{ category: "GroundUnit", alive: true, human: false, controlled: true, coalition: 1, country: 0, name: "Tor 9A331", unitName: "Cool guy 2-1", groupName: "Cool group 10", state: 1, task: "Being cool",
|
||||
hasTask: false, position: { lat: 37.2, lng: -116.2, alt: 1000 }, speed: 200, horizontalVelocity: 200, verticalVelicity: 0, heading: 315 * Math.PI / 180, isActiveTanker: false, isActiveAWACS: false, onOff: true, followRoads: false, fuel: 50,
|
||||
desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0,
|
||||
formationOffset: { x: 0, y: 0, z: 0 },
|
||||
targetID: 0,
|
||||
targetPosition: { lat: 0, lng: 0, alt: 0 },
|
||||
ROE: 1,
|
||||
reactionToThreat: 1,
|
||||
emissionsCountermeasures: 1,
|
||||
TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 },
|
||||
radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 },
|
||||
generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false },
|
||||
ammo: [{ quantity: 2, name: "A cool missile\0Ciao", guidance: 0, category: 0, missileCategory: 0 } ],
|
||||
contacts: [{ID: 1001, detectionMethod: 16}],
|
||||
activePath: [ ],
|
||||
isLeader: true
|
||||
},
|
||||
}
|
||||
const aircraftDatabase = require('./public/databases/units/aircraftDatabase.json');
|
||||
const helicopterDatabase = require('./public/databases/units/helicopterDatabase.json');
|
||||
const groundUnitDatabase = require('./public/databases/units/groundUnitDatabase.json');
|
||||
const navyUnitDatabase = require('./public/databases/units/navyUnitDatabase.json');
|
||||
|
||||
const DEMO_UNIT_DATA = {}
|
||||
|
||||
const DEMO_WEAPONS_DATA = {
|
||||
["1001"]:{ category: "Missile", alive: true, coalition: 2, name: "", position: { lat: 37.1, lng: -116, alt: 1000 }, speed: 200, heading: 45 * Math.PI / 180 },
|
||||
/*["1001"]:{ category: "Missile", alive: true, coalition: 2, name: "", position: { lat: 37.1, lng: -116, alt: 1000 }, speed: 200, heading: 45 * Math.PI / 180 }, */
|
||||
}
|
||||
|
||||
class DemoDataGenerator {
|
||||
@ -142,6 +33,87 @@ class DemoDataGenerator {
|
||||
},
|
||||
}))
|
||||
|
||||
|
||||
let baseData = { alive: true, human: false, controlled: true, coalition: 2, country: 0, unitName: "Cool guy", groupName: "Cool group 1", state: 1, task: "Being cool!",
|
||||
hasTask: true, position: { lat: 37, lng: -116, alt: 1000 }, speed: 200, horizontalVelocity: 200, verticalVelicity: 0, heading: 45, isActiveTanker: false, isActiveAWACS: false, onOff: true, followRoads: false, fuel: 50,
|
||||
desiredSpeed: 300, desiredSpeedType: 1, desiredAltitude: 1000, desiredAltitudeType: 1, leaderID: 0,
|
||||
formationOffset: { x: 0, y: 0, z: 0 },
|
||||
targetID: 0,
|
||||
targetPosition: { lat: 0, lng: 0, alt: 0 },
|
||||
ROE: 1,
|
||||
reactionToThreat: 1,
|
||||
emissionsCountermeasures: 1,
|
||||
TACAN: { isOn: false, XY: 'Y', callsign: 'TKR', channel: 40 },
|
||||
radio: { frequency: 124000000, callsign: 1, callsignNumber: 1 },
|
||||
generalSettings: { prohibitAA: false, prohibitAfterburner: false, prohibitAG: false, prohibitAirWpn: false, prohibitJettison: false },
|
||||
ammo: [],
|
||||
contacts: [],
|
||||
activePath: [],
|
||||
isLeader: true
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
UNCOMMENT TO TEST ALL UNITS
|
||||
|
||||
var databases = Object.assign({}, aircraftDatabase, helicopterDatabase, groundUnitDatabase, navyUnitDatabase);
|
||||
var t = Object.keys(databases).length;
|
||||
var l = Math.floor(Math.sqrt(t));
|
||||
let latIdx = 0;
|
||||
let lngIdx = 0;
|
||||
let idx = 1;
|
||||
console.log(l)
|
||||
for (let name in databases) {
|
||||
if (databases[name].enabled) {
|
||||
DEMO_UNIT_DATA[idx] = JSON.parse(JSON.stringify(baseData));
|
||||
DEMO_UNIT_DATA[idx].name = name;
|
||||
DEMO_UNIT_DATA[idx].groupName = `Group-${idx}`;
|
||||
DEMO_UNIT_DATA[idx].position.lat += latIdx / 5;
|
||||
DEMO_UNIT_DATA[idx].position.lng += lngIdx / 5;
|
||||
|
||||
latIdx += 1;
|
||||
if (latIdx === l) {
|
||||
latIdx = 0;
|
||||
lngIdx += 1;
|
||||
}
|
||||
|
||||
if (name in aircraftDatabase)
|
||||
DEMO_UNIT_DATA[idx].category = "Aircraft";
|
||||
else if (name in helicopterDatabase)
|
||||
DEMO_UNIT_DATA[idx].category = "Helicopter";
|
||||
else if (name in groundUnitDatabase)
|
||||
DEMO_UNIT_DATA[idx].category = "GroundUnit";
|
||||
else if (name in navyUnitDatabase)
|
||||
DEMO_UNIT_DATA[idx].category = "NavyUnit";
|
||||
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
let idx = 1;
|
||||
DEMO_UNIT_DATA[idx] = JSON.parse(JSON.stringify(baseData));
|
||||
DEMO_UNIT_DATA[idx].name = "S_75M_Volhov";
|
||||
DEMO_UNIT_DATA[idx].groupName = `Group`;
|
||||
DEMO_UNIT_DATA[idx].position.lat += idx / 100;
|
||||
DEMO_UNIT_DATA[idx].category = "GroundUnit";
|
||||
DEMO_UNIT_DATA[idx].isLeader = true;
|
||||
|
||||
idx += 1;
|
||||
DEMO_UNIT_DATA[idx] = JSON.parse(JSON.stringify(baseData));
|
||||
DEMO_UNIT_DATA[idx].name = "SNR_75V";
|
||||
DEMO_UNIT_DATA[idx].groupName = `Group`;
|
||||
DEMO_UNIT_DATA[idx].position.lat += idx / 100;
|
||||
DEMO_UNIT_DATA[idx].category = "GroundUnit";
|
||||
DEMO_UNIT_DATA[idx].isLeader = false;
|
||||
|
||||
idx += 1;
|
||||
DEMO_UNIT_DATA[idx] = JSON.parse(JSON.stringify(baseData));
|
||||
DEMO_UNIT_DATA[idx].name = "Ural-4320 APA-5D";
|
||||
DEMO_UNIT_DATA[idx].groupName = `Group`;
|
||||
DEMO_UNIT_DATA[idx].position.lat += idx / 100;
|
||||
DEMO_UNIT_DATA[idx].category = "GroundUnit";
|
||||
DEMO_UNIT_DATA[idx].isLeader = false;
|
||||
|
||||
this.startTime = Date.now();
|
||||
}
|
||||
@ -150,51 +122,53 @@ class DemoDataGenerator {
|
||||
var array = new Uint8Array();
|
||||
var time = Date.now();
|
||||
array = this.concat(array, this.uint64ToByteArray(BigInt(time)));
|
||||
for (let idx in DEMO_UNIT_DATA) {
|
||||
const unit = DEMO_UNIT_DATA[idx];
|
||||
array = this.concat(array, this.uint32ToByteArray(idx));
|
||||
array = this.appendString(array, unit.category, 1);
|
||||
array = this.appendUint8(array, unit.alive, 2);
|
||||
array = this.appendUint8(array, unit.human, 3);
|
||||
array = this.appendUint8(array, unit.controlled, 4);
|
||||
array = this.appendUint16(array, unit.coalition, 5);
|
||||
array = this.appendUint8(array, unit.country, 6);
|
||||
array = this.appendString(array, unit.name, 7);
|
||||
array = this.appendString(array, unit.unitName, 8);
|
||||
array = this.appendString(array, unit.groupName, 9);
|
||||
array = this.appendUint8(array, unit.state, 10);
|
||||
array = this.appendString(array, unit.task, 11);
|
||||
array = this.appendUint8(array, unit.hasTask, 12);
|
||||
array = this.appendCoordinates(array, unit.position, 13);
|
||||
array = this.appendDouble(array, unit.speed, 14);
|
||||
array = this.appendDouble(array, unit.horizontalVelocity, 15);
|
||||
array = this.appendDouble(array, unit.verticalVelicity, 16);
|
||||
array = this.appendDouble(array, unit.heading, 17);
|
||||
array = this.appendUint8(array, unit.isActiveTanker, 18);
|
||||
array = this.appendUint8(array, unit.isActiveAWACS, 19);
|
||||
array = this.appendUint8(array, unit.onOff, 20);
|
||||
array = this.appendUint8(array, unit.followRoads, 21);
|
||||
array = this.appendUint16(array, unit.fuel, 22);
|
||||
array = this.appendDouble(array, unit.desiredSpeed, 23);
|
||||
array = this.appendUint8(array, unit.desiredSpeedType, 24);
|
||||
array = this.appendDouble(array, unit.desiredAltitude, 25);
|
||||
array = this.appendUint8(array, unit.desiredAltitudeType, 26);
|
||||
array = this.appendUint32(array, unit.leaderID, 27);
|
||||
array = this.appendOffset(array, unit.formationOffset, 28);
|
||||
array = this.appendUint32(array, unit.targetID, 29);
|
||||
array = this.appendCoordinates(array, unit.targetPosition, 30);
|
||||
array = this.appendUint8(array, unit.ROE, 31);
|
||||
array = this.appendUint8(array, unit.reactionToThreat, 32);
|
||||
array = this.appendUint8(array, unit.emissionsCountermeasures, 33);
|
||||
array = this.appendTACAN(array, unit.TACAN, 34);
|
||||
array = this.appendRadio(array, unit.radio, 35);
|
||||
array = this.appendRadio(array, unit.generalSettings, 36);
|
||||
array = this.appendAmmo(array, unit.ammo, 37);
|
||||
array = this.appendContacts(array, unit.contacts, 38);
|
||||
array = this.appendActivePath(array, unit.activePath, 39);
|
||||
array = this.appendUint8(array, unit.isLeader, 40);
|
||||
array = this.appendUint8(array, unit.operateAs, 41);
|
||||
array = this.concat(array, this.uint8ToByteArray(255));
|
||||
if (req.query["time"] == 0){
|
||||
for (let idx in DEMO_UNIT_DATA) {
|
||||
const unit = DEMO_UNIT_DATA[idx];
|
||||
array = this.concat(array, this.uint32ToByteArray(idx));
|
||||
array = this.appendString(array, unit.category, 1);
|
||||
array = this.appendUint8(array, unit.alive, 2);
|
||||
array = this.appendUint8(array, unit.human, 3);
|
||||
array = this.appendUint8(array, unit.controlled, 4);
|
||||
array = this.appendUint16(array, unit.coalition, 5);
|
||||
array = this.appendUint8(array, unit.country, 6);
|
||||
array = this.appendString(array, unit.name, 7);
|
||||
array = this.appendString(array, unit.unitName, 8);
|
||||
array = this.appendString(array, unit.groupName, 9);
|
||||
array = this.appendUint8(array, unit.state, 10);
|
||||
array = this.appendString(array, unit.task, 11);
|
||||
array = this.appendUint8(array, unit.hasTask, 12);
|
||||
array = this.appendCoordinates(array, unit.position, 13);
|
||||
array = this.appendDouble(array, unit.speed, 14);
|
||||
array = this.appendDouble(array, unit.horizontalVelocity, 15);
|
||||
array = this.appendDouble(array, unit.verticalVelicity, 16);
|
||||
array = this.appendDouble(array, unit.heading, 17);
|
||||
array = this.appendUint8(array, unit.isActiveTanker, 18);
|
||||
array = this.appendUint8(array, unit.isActiveAWACS, 19);
|
||||
array = this.appendUint8(array, unit.onOff, 20);
|
||||
array = this.appendUint8(array, unit.followRoads, 21);
|
||||
array = this.appendUint16(array, unit.fuel, 22);
|
||||
array = this.appendDouble(array, unit.desiredSpeed, 23);
|
||||
array = this.appendUint8(array, unit.desiredSpeedType, 24);
|
||||
array = this.appendDouble(array, unit.desiredAltitude, 25);
|
||||
array = this.appendUint8(array, unit.desiredAltitudeType, 26);
|
||||
array = this.appendUint32(array, unit.leaderID, 27);
|
||||
array = this.appendOffset(array, unit.formationOffset, 28);
|
||||
array = this.appendUint32(array, unit.targetID, 29);
|
||||
array = this.appendCoordinates(array, unit.targetPosition, 30);
|
||||
array = this.appendUint8(array, unit.ROE, 31);
|
||||
array = this.appendUint8(array, unit.reactionToThreat, 32);
|
||||
array = this.appendUint8(array, unit.emissionsCountermeasures, 33);
|
||||
array = this.appendTACAN(array, unit.TACAN, 34);
|
||||
array = this.appendRadio(array, unit.radio, 35);
|
||||
array = this.appendRadio(array, unit.generalSettings, 36);
|
||||
array = this.appendAmmo(array, unit.ammo, 37);
|
||||
array = this.appendContacts(array, unit.contacts, 38);
|
||||
array = this.appendActivePath(array, unit.activePath, 39);
|
||||
array = this.appendUint8(array, unit.isLeader, 40);
|
||||
array = this.appendUint8(array, unit.operateAs, 41);
|
||||
array = this.concat(array, this.uint8ToByteArray(255));
|
||||
}
|
||||
}
|
||||
res.end(Buffer.from(array, 'binary'));
|
||||
};
|
||||
|
||||
4
client/package-lock.json
generated
@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "DCSOlympus",
|
||||
"version": "v0.4.5-alpha",
|
||||
"version": "v0.4.6-alpha",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "DCSOlympus",
|
||||
"version": "v0.4.5-alpha",
|
||||
"version": "v0.4.6-alpha",
|
||||
"dependencies": {
|
||||
"@turf/turf": "^6.5.0",
|
||||
"body-parser": "^1.20.2",
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
"name": "DCSOlympus",
|
||||
"node-main": "./bin/www",
|
||||
"main": "http://localhost:3000",
|
||||
"version": "v0.4.5-alpha",
|
||||
"version": "v0.4.6-alpha",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "browserify .\\src\\index.ts --debug -o .\\public\\javascripts\\bundle.js -t [ babelify --global true --presets [ @babel/preset-env ] --extensions '.js'] -p [ tsify --noImplicitAny ] && copy.bat",
|
||||
|
||||
@ -54,7 +54,7 @@ class AirUnitEditor extends uniteditor_1.UnitEditor {
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Cost", (_b = String(blueprint.cost)) !== null && _b !== void 0 ? _b : "", "number", (value) => { blueprint.cost = parseFloat(value); });
|
||||
(0, utils_1.addCheckboxInput)(this.contentDiv2, "Can target point", (_c = blueprint.canTargetPoint) !== null && _c !== void 0 ? _c : false, (value) => { blueprint.canTargetPoint = value; });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Description", (_d = blueprint.description) !== null && _d !== void 0 ? _d : "", "text", (value) => { blueprint.description = value; });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Abilities", (_e = blueprint.abilities) !== null && _e !== void 0 ? _e : "", "text", (value) => { blueprint.abilities = value; });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Tags", (_e = blueprint.tags) !== null && _e !== void 0 ? _e : "", "text", (value) => { blueprint.tags = value; });
|
||||
/* Add a scrollable list of loadouts that the user can edit */
|
||||
var title = document.createElement("label");
|
||||
title.innerText = "Loadouts";
|
||||
@ -206,13 +206,13 @@ class DatabaseManagerPlugin {
|
||||
__classPrivateFieldGet(this, _DatabaseManagerPlugin_mainContentContainer, "f").classList.add("dm-container");
|
||||
__classPrivateFieldGet(this, _DatabaseManagerPlugin_element, "f").appendChild(__classPrivateFieldGet(this, _DatabaseManagerPlugin_mainContentContainer, "f"));
|
||||
__classPrivateFieldSet(this, _DatabaseManagerPlugin_contentDiv1, document.createElement("div"), "f");
|
||||
__classPrivateFieldGet(this, _DatabaseManagerPlugin_contentDiv1, "f").classList.add("dm-content-container");
|
||||
__classPrivateFieldGet(this, _DatabaseManagerPlugin_contentDiv1, "f").classList.add("dm-content-container", "ol-scrollable");
|
||||
__classPrivateFieldGet(this, _DatabaseManagerPlugin_mainContentContainer, "f").appendChild(__classPrivateFieldGet(this, _DatabaseManagerPlugin_contentDiv1, "f"));
|
||||
__classPrivateFieldSet(this, _DatabaseManagerPlugin_contentDiv2, document.createElement("div"), "f");
|
||||
__classPrivateFieldGet(this, _DatabaseManagerPlugin_contentDiv2, "f").classList.add("dm-content-container");
|
||||
__classPrivateFieldGet(this, _DatabaseManagerPlugin_contentDiv2, "f").classList.add("dm-content-container", "ol-scrollable");
|
||||
__classPrivateFieldGet(this, _DatabaseManagerPlugin_mainContentContainer, "f").appendChild(__classPrivateFieldGet(this, _DatabaseManagerPlugin_contentDiv2, "f"));
|
||||
__classPrivateFieldSet(this, _DatabaseManagerPlugin_contentDiv3, document.createElement("div"), "f");
|
||||
__classPrivateFieldGet(this, _DatabaseManagerPlugin_contentDiv3, "f").classList.add("dm-content-container");
|
||||
__classPrivateFieldGet(this, _DatabaseManagerPlugin_contentDiv3, "f").classList.add("dm-content-container", "ol-scrollable");
|
||||
__classPrivateFieldGet(this, _DatabaseManagerPlugin_mainContentContainer, "f").appendChild(__classPrivateFieldGet(this, _DatabaseManagerPlugin_contentDiv3, "f"));
|
||||
/* Create the database editors, which use the three divs created before */
|
||||
__classPrivateFieldSet(this, _DatabaseManagerPlugin_aircraftEditor, new airuniteditor_1.AirUnitEditor(__classPrivateFieldGet(this, _DatabaseManagerPlugin_contentDiv1, "f"), __classPrivateFieldGet(this, _DatabaseManagerPlugin_contentDiv2, "f"), __classPrivateFieldGet(this, _DatabaseManagerPlugin_contentDiv3, "f")), "f");
|
||||
@ -508,7 +508,7 @@ class GroundUnitEditor extends uniteditor_1.UnitEditor {
|
||||
* @param blueprint The blueprint to edit
|
||||
*/
|
||||
setBlueprint(blueprint) {
|
||||
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s;
|
||||
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w;
|
||||
__classPrivateFieldSet(this, _GroundUnitEditor_blueprint, blueprint, "f");
|
||||
if (__classPrivateFieldGet(this, _GroundUnitEditor_blueprint, "f") !== null) {
|
||||
this.contentDiv2.replaceChildren();
|
||||
@ -519,25 +519,29 @@ class GroundUnitEditor extends uniteditor_1.UnitEditor {
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Label", blueprint.label, "text", (value) => { blueprint.label = value; });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Short label", blueprint.shortLabel, "text", (value) => { blueprint.shortLabel = value; });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Type", (_a = blueprint.type) !== null && _a !== void 0 ? _a : "", "text", (value) => { blueprint.type = value; });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Unit when grouped", (_b = blueprint.unitWhenGrouped) !== null && _b !== void 0 ? _b : "", "text", (value) => { blueprint.unitWhenGrouped = value; });
|
||||
(0, utils_1.addDropdownInput)(this.contentDiv2, "Coalition", blueprint.coalition, ["", "blue", "red"], (value) => { blueprint.coalition = value; });
|
||||
(0, utils_1.addDropdownInput)(this.contentDiv2, "Era", blueprint.era, ["WW2", "Early Cold War", "Mid Cold War", "Late Cold War", "Modern"], (value) => { blueprint.era = value; });
|
||||
//addStringInput(this.contentDiv2, "Filename", blueprint.filename?? "", "text", (value: string) => {blueprint.filename = value; });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Cost", (_b = String(blueprint.cost)) !== null && _b !== void 0 ? _b : "", "number", (value) => { blueprint.cost = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Acquisition range [m]", (_c = String(blueprint.acquisitionRange)) !== null && _c !== void 0 ? _c : "", "number", (value) => { blueprint.acquisitionRange = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Engagement range [m]", (_d = String(blueprint.engagementRange)) !== null && _d !== void 0 ? _d : "", "number", (value) => { blueprint.engagementRange = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Targeting range [m]", (_e = String(blueprint.targetingRange)) !== null && _e !== void 0 ? _e : "", "number", (value) => { blueprint.targetingRange = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Barrel height [m]", (_f = String(blueprint.barrelHeight)) !== null && _f !== void 0 ? _f : "", "number", (value) => { blueprint.barrelHeight = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Muzzle velocity [m/s]", (_g = String(blueprint.muzzleVelocity)) !== null && _g !== void 0 ? _g : "", "number", (value) => { blueprint.muzzleVelocity = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Aim time [s]", (_h = String(blueprint.aimTime)) !== null && _h !== void 0 ? _h : "", "number", (value) => { blueprint.aimTime = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Burst quantity", (_j = String(blueprint.shotsToFire)) !== null && _j !== void 0 ? _j : "", "number", (value) => { blueprint.shotsToFire = Math.round(parseFloat(value)); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Burst base interval [s]", (_k = String(blueprint.shotsBaseInterval)) !== null && _k !== void 0 ? _k : "", "number", (value) => { blueprint.shotsBaseInterval = Math.round(parseFloat(value)); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Base scatter [°]", (_l = String(blueprint.shotsBaseScatter)) !== null && _l !== void 0 ? _l : "", "number", (value) => { blueprint.shotsBaseScatter = Math.round(parseFloat(value)); });
|
||||
(0, utils_1.addCheckboxInput)(this.contentDiv2, "Can target point", (_m = blueprint.canTargetPoint) !== null && _m !== void 0 ? _m : false, (value) => { blueprint.canTargetPoint = value; });
|
||||
(0, utils_1.addCheckboxInput)(this.contentDiv2, "Can rearm", (_o = blueprint.canRearm) !== null && _o !== void 0 ? _o : false, (value) => { blueprint.canRearm = value; });
|
||||
(0, utils_1.addCheckboxInput)(this.contentDiv2, "Can operate as AAA", (_p = blueprint.canAAA) !== null && _p !== void 0 ? _p : false, (value) => { blueprint.canAAA = value; });
|
||||
(0, utils_1.addCheckboxInput)(this.contentDiv2, "Indirect fire (e.g. mortar)", (_q = blueprint.indirectFire) !== null && _q !== void 0 ? _q : false, (value) => { blueprint.indirectFire = value; });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Description", (_r = blueprint.description) !== null && _r !== void 0 ? _r : "", "text", (value) => { blueprint.description = value; });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Abilities", (_s = blueprint.abilities) !== null && _s !== void 0 ? _s : "", "text", (value) => { blueprint.abilities = value; });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Cost", (_c = String(blueprint.cost)) !== null && _c !== void 0 ? _c : "", "number", (value) => { blueprint.cost = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Acquisition range [m]", (_d = String(blueprint.acquisitionRange)) !== null && _d !== void 0 ? _d : "", "number", (value) => { blueprint.acquisitionRange = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Engagement range [m]", (_e = String(blueprint.engagementRange)) !== null && _e !== void 0 ? _e : "", "number", (value) => { blueprint.engagementRange = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Targeting range [m]", (_f = String(blueprint.targetingRange)) !== null && _f !== void 0 ? _f : "", "number", (value) => { blueprint.targetingRange = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Aim method range [m]", (_g = String(blueprint.aimMethodRange)) !== null && _g !== void 0 ? _g : "", "number", (value) => { blueprint.aimMethodRange = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Barrel height [m]", (_h = String(blueprint.barrelHeight)) !== null && _h !== void 0 ? _h : "", "number", (value) => { blueprint.barrelHeight = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Muzzle velocity [m/s]", (_j = String(blueprint.muzzleVelocity)) !== null && _j !== void 0 ? _j : "", "number", (value) => { blueprint.muzzleVelocity = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Aim time [s]", (_k = String(blueprint.aimTime)) !== null && _k !== void 0 ? _k : "", "number", (value) => { blueprint.aimTime = parseFloat(value); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Shots to fire", (_l = String(blueprint.shotsToFire)) !== null && _l !== void 0 ? _l : "", "number", (value) => { blueprint.shotsToFire = Math.round(parseFloat(value)); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Shots base interval [s]", (_m = String(blueprint.shotsBaseInterval)) !== null && _m !== void 0 ? _m : "", "number", (value) => { blueprint.shotsBaseInterval = Math.round(parseFloat(value)); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Shots base scatter [°]", (_o = String(blueprint.shotsBaseScatter)) !== null && _o !== void 0 ? _o : "", "number", (value) => { blueprint.shotsBaseScatter = Math.round(parseFloat(value)); });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Alertness time constant [s]", (_p = String(blueprint.alertnessTimeConstant)) !== null && _p !== void 0 ? _p : "", "number", (value) => { blueprint.alertnessTimeConstant = Math.round(parseFloat(value)); });
|
||||
(0, utils_1.addCheckboxInput)(this.contentDiv2, "Can target point", (_q = blueprint.canTargetPoint) !== null && _q !== void 0 ? _q : false, (value) => { blueprint.canTargetPoint = value; });
|
||||
(0, utils_1.addCheckboxInput)(this.contentDiv2, "Can rearm", (_r = blueprint.canRearm) !== null && _r !== void 0 ? _r : false, (value) => { blueprint.canRearm = value; });
|
||||
(0, utils_1.addCheckboxInput)(this.contentDiv2, "Can operate as AAA", (_s = blueprint.canAAA) !== null && _s !== void 0 ? _s : false, (value) => { blueprint.canAAA = value; });
|
||||
(0, utils_1.addCheckboxInput)(this.contentDiv2, "Indirect fire (e.g. mortar)", (_t = blueprint.indirectFire) !== null && _t !== void 0 ? _t : false, (value) => { blueprint.indirectFire = value; });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Description", (_u = blueprint.description) !== null && _u !== void 0 ? _u : "", "text", (value) => { blueprint.description = value; });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Tags", (_v = blueprint.tags) !== null && _v !== void 0 ? _v : "", "text", (value) => { blueprint.tags = value; });
|
||||
(0, utils_1.addStringInput)(this.contentDiv2, "Marker file", (_w = blueprint.markerFile) !== null && _w !== void 0 ? _w : "", "text", (value) => { blueprint.markerFile = value; });
|
||||
}
|
||||
}
|
||||
/** Add a new empty blueprint
|
||||
@ -751,9 +755,9 @@ class UnitEditor {
|
||||
this.database = JSON.parse(JSON.stringify({ blueprints: database.getBlueprints(true) }));
|
||||
}
|
||||
/** Show the editor
|
||||
*
|
||||
* @param filter String filter
|
||||
*/
|
||||
show() {
|
||||
show(filter = "") {
|
||||
this.visible = true;
|
||||
this.contentDiv1.replaceChildren();
|
||||
this.contentDiv2.replaceChildren();
|
||||
@ -763,17 +767,16 @@ class UnitEditor {
|
||||
var title = document.createElement("label");
|
||||
title.innerText = "Units list";
|
||||
this.contentDiv1.appendChild(title);
|
||||
(0, utils_1.addBlueprintsScroll)(this.contentDiv1, this.database, (key) => {
|
||||
if (this.database != null)
|
||||
this.setBlueprint(this.database.blueprints[key]);
|
||||
});
|
||||
(0, utils_1.addNewElementInput)(this.contentDiv1, (ev, input) => {
|
||||
if (input.value != "")
|
||||
this.addBlueprint((input).value);
|
||||
});
|
||||
var filterInput = document.createElement("input");
|
||||
filterInput.value = filter;
|
||||
this.contentDiv1.appendChild(filterInput);
|
||||
filterInput.onchange = (e) => {
|
||||
this.show(e.target.value);
|
||||
};
|
||||
this.addBlueprints(filter);
|
||||
}
|
||||
}
|
||||
/** Hid the editor
|
||||
/** Hide the editor
|
||||
*
|
||||
*/
|
||||
hide() {
|
||||
@ -789,6 +792,22 @@ class UnitEditor {
|
||||
getDatabase() {
|
||||
return this.database;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param filter String filter
|
||||
*/
|
||||
addBlueprints(filter = "") {
|
||||
if (this.database) {
|
||||
(0, utils_1.addBlueprintsScroll)(this.contentDiv1, this.database, filter, (key) => {
|
||||
if (this.database != null)
|
||||
this.setBlueprint(this.database.blueprints[key]);
|
||||
});
|
||||
(0, utils_1.addNewElementInput)(this.contentDiv1, (ev, input) => {
|
||||
if (input.value != "")
|
||||
this.addBlueprint((input).value);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.UnitEditor = UnitEditor;
|
||||
|
||||
@ -958,36 +977,56 @@ exports.addNewElementInput = addNewElementInput;
|
||||
*
|
||||
* @param div The HTMLElement that will contain the list
|
||||
* @param database The database that will be used to fill the list of blueprints
|
||||
* @param filter A string filter that will be executed to filter the blueprints to add
|
||||
* @param callback Callback called when the user clicks on one of the elements
|
||||
*/
|
||||
function addBlueprintsScroll(div, database, callback) {
|
||||
function addBlueprintsScroll(div, database, filter, callback) {
|
||||
var scrollDiv = document.createElement("div");
|
||||
scrollDiv.classList.add("dm-scroll-container");
|
||||
if (database !== null) {
|
||||
var blueprints = database.blueprints;
|
||||
for (let key of Object.keys(blueprints).sort((a, b) => a.localeCompare(b, undefined, { sensitivity: 'base' }))) {
|
||||
var rowDiv = document.createElement("div");
|
||||
scrollDiv.appendChild(rowDiv);
|
||||
var text = document.createElement("label");
|
||||
text.textContent = key;
|
||||
text.onclick = () => callback(key);
|
||||
rowDiv.appendChild(text);
|
||||
let checkbox = document.createElement("input");
|
||||
checkbox.type = "checkbox";
|
||||
checkbox.checked = blueprints[key].enabled;
|
||||
checkbox.onclick = () => {
|
||||
console.log(checkbox.checked);
|
||||
blueprints[key].enabled = checkbox.checked;
|
||||
};
|
||||
rowDiv.appendChild(checkbox);
|
||||
/* This button allows to remove an element from the list. It requires a refresh. */
|
||||
var button = document.createElement("button");
|
||||
button.innerText = "X";
|
||||
button.onclick = () => {
|
||||
delete blueprints[key];
|
||||
div.dispatchEvent(new Event("refresh"));
|
||||
};
|
||||
rowDiv.appendChild(button);
|
||||
var addKey = true;
|
||||
if (filter !== "") {
|
||||
try {
|
||||
var blueprint = blueprints[key];
|
||||
addKey = eval(filter);
|
||||
}
|
||||
catch (_a) {
|
||||
console.error("An error has occurred evaluating the blueprint filter");
|
||||
}
|
||||
}
|
||||
if (addKey) {
|
||||
var rowDiv = document.createElement("div");
|
||||
scrollDiv.appendChild(rowDiv);
|
||||
let text = document.createElement("div");
|
||||
text.innerHTML = `<div>${key}</div> <div>${blueprints[key].label}</div>`;
|
||||
text.onclick = () => {
|
||||
callback(key);
|
||||
const collection = document.getElementsByClassName("blueprint-selected");
|
||||
for (let i = 0; i < collection.length; i++) {
|
||||
collection[i].classList.remove("blueprint-selected");
|
||||
}
|
||||
text.classList.add("blueprint-selected");
|
||||
};
|
||||
rowDiv.appendChild(text);
|
||||
let checkbox = document.createElement("input");
|
||||
checkbox.type = "checkbox";
|
||||
checkbox.checked = blueprints[key].enabled;
|
||||
checkbox.onclick = () => {
|
||||
console.log(checkbox.checked);
|
||||
blueprints[key].enabled = checkbox.checked;
|
||||
};
|
||||
rowDiv.appendChild(checkbox);
|
||||
/* This button allows to remove an element from the list. It requires a refresh. */
|
||||
var button = document.createElement("button");
|
||||
button.innerText = "X";
|
||||
button.onclick = () => {
|
||||
delete blueprints[key];
|
||||
div.dispatchEvent(new Event("refresh"));
|
||||
};
|
||||
rowDiv.appendChild(button);
|
||||
}
|
||||
}
|
||||
}
|
||||
div.appendChild(scrollDiv);
|
||||
@ -1042,8 +1081,14 @@ function arrayToString(array) {
|
||||
return "[" + array.join(", ") + "]";
|
||||
}
|
||||
exports.arrayToString = arrayToString;
|
||||
/** Converts an a single string like [val1, val2, val3] into an array
|
||||
*
|
||||
* @param input The input string
|
||||
* @returns The array
|
||||
*/
|
||||
function stringToArray(input) {
|
||||
return input.match(/(\w)+/g) || [];
|
||||
var _a;
|
||||
return (_a = input.match(/(\w)+/g)) !== null && _a !== void 0 ? _a : [];
|
||||
}
|
||||
exports.stringToArray = stringToArray;
|
||||
|
||||
|
||||
@ -46,7 +46,7 @@ export class AirUnitEditor extends UnitEditor {
|
||||
addStringInput(this.contentDiv2, "Cost", String(blueprint.cost) ?? "", "number", (value: string) => { blueprint.cost = parseFloat(value); });
|
||||
addCheckboxInput(this.contentDiv2, "Can target point", blueprint.canTargetPoint ?? false, (value: boolean) => {blueprint.canTargetPoint = value;})
|
||||
addStringInput(this.contentDiv2, "Description", blueprint.description ?? "", "text", (value: string) => {blueprint.description = value; });
|
||||
addStringInput(this.contentDiv2, "Abilities", blueprint.abilities ?? "", "text", (value: string) => {blueprint.abilities = value; });
|
||||
addStringInput(this.contentDiv2, "Tags", blueprint.tags ?? "", "text", (value: string) => {blueprint.tags = value; });
|
||||
|
||||
/* Add a scrollable list of loadouts that the user can edit */
|
||||
var title = document.createElement("label");
|
||||
|
||||
@ -89,15 +89,15 @@ export class DatabaseManagerPlugin implements OlympusPlugin {
|
||||
this.#element.appendChild(this.#mainContentContainer);
|
||||
|
||||
this.#contentDiv1 = document.createElement("div");
|
||||
this.#contentDiv1.classList.add("dm-content-container");
|
||||
this.#contentDiv1.classList.add("dm-content-container", "ol-scrollable");
|
||||
this.#mainContentContainer.appendChild(this.#contentDiv1);
|
||||
|
||||
this.#contentDiv2 = document.createElement("div");
|
||||
this.#contentDiv2.classList.add("dm-content-container");
|
||||
this.#contentDiv2.classList.add("dm-content-container", "ol-scrollable");
|
||||
this.#mainContentContainer.appendChild(this.#contentDiv2);
|
||||
|
||||
this.#contentDiv3 = document.createElement("div");
|
||||
this.#contentDiv3.classList.add("dm-content-container");
|
||||
this.#contentDiv3.classList.add("dm-content-container", "ol-scrollable");
|
||||
this.#mainContentContainer.appendChild(this.#contentDiv3);
|
||||
|
||||
/* Create the database editors, which use the three divs created before */
|
||||
|
||||
@ -30,6 +30,7 @@ export class GroundUnitEditor extends UnitEditor {
|
||||
addStringInput(this.contentDiv2, "Label", blueprint.label, "text", (value: string) => {blueprint.label = value; });
|
||||
addStringInput(this.contentDiv2, "Short label", blueprint.shortLabel, "text", (value: string) => {blueprint.shortLabel = value; });
|
||||
addStringInput(this.contentDiv2, "Type", blueprint.type?? "", "text", (value: string) => {blueprint.type = value; });
|
||||
addStringInput(this.contentDiv2, "Unit when grouped", blueprint.unitWhenGrouped?? "", "text", (value: string) => {blueprint.unitWhenGrouped = value; });
|
||||
addDropdownInput(this.contentDiv2, "Coalition", blueprint.coalition, ["", "blue", "red"], (value: string) => {blueprint.coalition = value; });
|
||||
addDropdownInput(this.contentDiv2, "Era", blueprint.era, ["WW2", "Early Cold War", "Mid Cold War", "Late Cold War", "Modern"], (value: string) => {blueprint.era = value; });
|
||||
//addStringInput(this.contentDiv2, "Filename", blueprint.filename?? "", "text", (value: string) => {blueprint.filename = value; });
|
||||
@ -50,7 +51,8 @@ export class GroundUnitEditor extends UnitEditor {
|
||||
addCheckboxInput(this.contentDiv2, "Can operate as AAA", blueprint.canAAA ?? false, (value: boolean) => {blueprint.canAAA = value;})
|
||||
addCheckboxInput(this.contentDiv2, "Indirect fire (e.g. mortar)", blueprint.indirectFire ?? false, (value: boolean) => {blueprint.indirectFire = value;})
|
||||
addStringInput(this.contentDiv2, "Description", blueprint.description ?? "", "text", (value: string) => {blueprint.description = value; });
|
||||
addStringInput(this.contentDiv2, "Abilities", blueprint.abilities ?? "", "text", (value: string) => {blueprint.abilities = value; });
|
||||
addStringInput(this.contentDiv2, "Tags", blueprint.tags ?? "", "text", (value: string) => {blueprint.tags = value; });
|
||||
addStringInput(this.contentDiv2, "Marker file", blueprint.markerFile ?? "", "text", (value: string) => {blueprint.markerFile = value; });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -198,8 +198,8 @@ export function addBlueprintsScroll(div: HTMLElement, database: {blueprints: {[k
|
||||
var rowDiv = document.createElement("div");
|
||||
scrollDiv.appendChild(rowDiv);
|
||||
|
||||
let text = document.createElement("label");
|
||||
text.textContent = key;
|
||||
let text = document.createElement("div");
|
||||
text.innerHTML = `<div>${key}</div> <div>${blueprints[key].label}</div>`;
|
||||
text.onclick = () => {
|
||||
callback(key);
|
||||
const collection = document.getElementsByClassName("blueprint-selected");
|
||||
@ -287,7 +287,11 @@ export function arrayToString(array: string[]) {
|
||||
return "[" + array.join( ", " ) + "]";
|
||||
}
|
||||
|
||||
|
||||
/** Converts an a single string like [val1, val2, val3] into an array
|
||||
*
|
||||
* @param input The input string
|
||||
* @returns The array
|
||||
*/
|
||||
export function stringToArray(input: string) {
|
||||
return input.match( /(\w)+/g ) ?? [];
|
||||
}
|
||||
@ -63,6 +63,8 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 5px;
|
||||
max-height: 100%;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
@ -71,7 +73,7 @@
|
||||
}
|
||||
|
||||
.dm-content-container:nth-of-type(1) {
|
||||
width: 300px;
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.dm-content-container:nth-of-type(2) {
|
||||
@ -151,12 +153,29 @@
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.dm-scroll-container>div>div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.dm-scroll-container>div>button {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
.dm-scroll-container>div>div>div:nth-child(1) {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
.dm-scroll-container>div>div>div:nth-child(2) {
|
||||
overflow: hidden;
|
||||
text-wrap: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.input-row {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
|
||||
@ -18529,7 +18529,7 @@
|
||||
"coalition": "blue",
|
||||
"label": "MB-339A",
|
||||
"era": "Mid Cold War",
|
||||
"shortLabel": "399",
|
||||
"shortLabel": "339",
|
||||
"loadouts": [
|
||||
{
|
||||
"items": [
|
||||
|
||||
@ -1,7 +1,3 @@
|
||||
:root {
|
||||
--right-panel-width:190px;
|
||||
}
|
||||
|
||||
/* Page style */
|
||||
#map-container {
|
||||
height: 100%;
|
||||
@ -17,54 +13,10 @@
|
||||
top: 10px;
|
||||
z-index: 99999;
|
||||
column-gap: 10px;
|
||||
row-gap: 10px;
|
||||
margin-right: 320px;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
@media (max-width: 1820px) {
|
||||
#toolbar-container {
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
row-gap: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
#primary-toolbar {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: fit-content;
|
||||
min-width: 650px;
|
||||
}
|
||||
|
||||
@media (max-width: 1820px) {
|
||||
#primary-toolbar {
|
||||
row-gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
#command-mode-toolbar {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#app-icon>.ol-select-options {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
#toolbar-summary {
|
||||
background-image: url("/images/icon-round.png");
|
||||
background-position: 20px 22px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 45px 45px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
text-indent: 60px;
|
||||
}
|
||||
|
||||
#toolbar-summary {
|
||||
white-space: nowrap;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
#connection-status-panel {
|
||||
@ -72,7 +24,7 @@
|
||||
font-size: 12px;
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
width: var( --right-panel-width );
|
||||
width: 190px;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
@ -84,35 +36,21 @@
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
row-gap: 10px;
|
||||
width: var( --right-panel-width );
|
||||
width: 190px;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
#unit-control-panel {
|
||||
height: fit-content;
|
||||
width: fit-content;
|
||||
left: 10px;
|
||||
position: absolute;
|
||||
top: 80px;
|
||||
width: 320px;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
@media (max-width: 1820px) {
|
||||
#unit-control-panel {
|
||||
top: 150px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1350px) {
|
||||
#unit-control-panel {
|
||||
top: 190px;
|
||||
}
|
||||
}
|
||||
|
||||
#unit-info-panel {
|
||||
bottom: 20px;
|
||||
font-size: 12px;
|
||||
left: 10px;
|
||||
position: absolute;
|
||||
width: fit-content;
|
||||
z-index: 9999;
|
||||
@ -120,12 +58,8 @@
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
@media (max-width: 1525px) {
|
||||
#unit-info-panel {
|
||||
flex-direction: column;
|
||||
}
|
||||
right: 210px;
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
#info-popup {
|
||||
|
||||
@ -97,6 +97,22 @@
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
[data-object|="unit-groundunit"] .unit-short-label {
|
||||
transform: translateY(7px);
|
||||
}
|
||||
|
||||
/*** Health indicator ***/
|
||||
[data-object|="unit"] .unit-health {
|
||||
background: white;
|
||||
border: var(--unit-health-border-width) solid var(--secondary-dark-steel);
|
||||
border-radius: var(--border-radius-sm);
|
||||
display: none;
|
||||
height: var(--unit-health-height);
|
||||
position: absolute;
|
||||
translate: var(--unit-health-x) var(--unit-health-y);
|
||||
width: var(--unit-health-width);
|
||||
}
|
||||
|
||||
/*** Fuel indicator ***/
|
||||
[data-object|="unit"] .unit-fuel {
|
||||
background: white;
|
||||
@ -109,7 +125,8 @@
|
||||
width: var(--unit-fuel-width);
|
||||
}
|
||||
|
||||
[data-object|="unit"] .unit-fuel-level {
|
||||
[data-object|="unit"] .unit-fuel-level,
|
||||
[data-object|="unit"] .unit-health-level {
|
||||
background-color: var(--secondary-light-grey);
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
@ -178,6 +195,7 @@
|
||||
|
||||
/*** Common ***/
|
||||
[data-object|="unit"]:hover .unit-ammo,
|
||||
[data-object|="unit"]:hover .unit-health ,
|
||||
[data-object|="unit"]:hover .unit-fuel {
|
||||
display: flex;
|
||||
}
|
||||
@ -188,13 +206,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-has-low-fuel] .unit-fuel {
|
||||
[data-object|="unit"][data-has-low-fuel] .unit-fuel, [data-object|="unit"][data-has-low-health] .unit-health {
|
||||
animation: pulse 1.5s linear infinite;
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-is-in-hotgroup] .unit-hotgroup,
|
||||
[data-object|="unit"][data-is-selected] .unit-ammo,
|
||||
[data-object|="unit"][data-is-selected] .unit-fuel,
|
||||
[data-object|="unit"][data-is-selected] .unit-health,
|
||||
[data-object|="unit"][data-is-selected] .unit-selected-spotlight {
|
||||
display: flex;
|
||||
}
|
||||
@ -211,6 +230,7 @@
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-coalition="blue"] .unit-fuel-level,
|
||||
[data-object|="unit"][data-coalition="blue"] .unit-health-level,
|
||||
[data-object|="unit"][data-coalition="blue"][data-has-fox-1] .unit-ammo>div:nth-child(1),
|
||||
[data-object|="unit"][data-coalition="blue"][data-has-fox-2] .unit-ammo>div:nth-child(2),
|
||||
[data-object|="unit"][data-coalition="blue"][data-has-fox-3] .unit-ammo>div:nth-child(3),
|
||||
@ -227,6 +247,7 @@
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-coalition="red"] .unit-fuel-level,
|
||||
[data-object|="unit"][data-coalition="red"] .unit-health-level,
|
||||
[data-object|="unit"][data-coalition="red"][data-has-fox-1] .unit-ammo>div:nth-child(1),
|
||||
[data-object|="unit"][data-coalition="red"][data-has-fox-2] .unit-ammo>div:nth-child(2),
|
||||
[data-object|="unit"][data-coalition="red"][data-has-fox-3] .unit-ammo>div:nth-child(3),
|
||||
@ -260,7 +281,8 @@
|
||||
background-image: url("/resources/theme/images/states/idle.svg");
|
||||
}
|
||||
|
||||
[data-object*="groundunit"][data-state="idle"] .unit-state {
|
||||
[data-object*="groundunit"][data-state="idle"] .unit-state,
|
||||
[data-object*="navyunit"][data-state="idle"] .unit-state {
|
||||
background-image: url(""); /* To avoid clutter, dont show the idle state for non flying units */
|
||||
}
|
||||
|
||||
@ -307,6 +329,19 @@
|
||||
background-image: url("/resources/theme/images/states/awacs.svg");
|
||||
}
|
||||
|
||||
[data-object|="unit"] .unit-health::before {
|
||||
background-image: url("/resources/theme/images/icons/health.svg");
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
content: " ";
|
||||
height: 6px;
|
||||
left: 0;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
translate: -10px -2px;
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
|
||||
/*** Dead unit ***/
|
||||
[data-object|="unit"][data-is-dead] .unit-selected-spotlight,
|
||||
@ -316,6 +351,7 @@
|
||||
[data-object|="unit"][data-is-dead] .unit-hotgroup-id,
|
||||
[data-object|="unit"][data-is-dead] .unit-state,
|
||||
[data-object|="unit"][data-is-dead] .unit-fuel,
|
||||
[data-object|="unit"][data-is-dead] .unit-health,
|
||||
[data-object|="unit"][data-is-dead] .unit-ammo,
|
||||
[data-object|="unit"][data-is-dead]:hover .unit-fuel,
|
||||
[data-object|="unit"][data-is-dead]:hover .unit-ammo {
|
||||
|
||||
@ -11,6 +11,8 @@
|
||||
|
||||
@import url("other/contextmenus.css");
|
||||
@import url("other/popup.css");
|
||||
@import url("other/toolbar.css");
|
||||
|
||||
|
||||
@import url("markers/airbase.css");
|
||||
@import url("markers/bullseye.css");
|
||||
|
||||
@ -124,6 +124,28 @@
|
||||
max-height: 300px;
|
||||
}
|
||||
|
||||
.ol-tag {
|
||||
background-color: #FFFFFF11;
|
||||
color: #FFFFFFDD;
|
||||
border: 1px solid #FFFFFF55;
|
||||
font-weight: normal;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
|
||||
/*
|
||||
.ol-tag-CA {
|
||||
background-color: #FF000022;
|
||||
}
|
||||
|
||||
.ol-tag-Radar {
|
||||
background-color: #00FF0022;
|
||||
}
|
||||
|
||||
.ol-tag-IR {
|
||||
background-color: #0000FF22;
|
||||
}
|
||||
*/
|
||||
|
||||
.unit-loadout-list {
|
||||
min-width: 0;
|
||||
}
|
||||
@ -225,13 +247,14 @@
|
||||
column-gap: 5px;
|
||||
}
|
||||
|
||||
.unit-label-count-container>div:nth-child(1) {
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
.unit-label-count-container button {
|
||||
display: flex !important;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.unit-label-count-container>div:nth-child(2) {
|
||||
font-size: large;
|
||||
.unit-label-count-container button>*:nth-child(1) {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.unit-loadout-preview {
|
||||
|
||||
74
client/public/stylesheets/other/toolbar.css
Normal file
@ -0,0 +1,74 @@
|
||||
|
||||
#primary-toolbar {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
#command-mode-toolbar {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#app-icon>.ol-select-options {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
#toolbar-summary {
|
||||
background-image: url("/images/icon-round.png");
|
||||
background-position: 20px 22px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 45px 45px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
text-indent: 60px;
|
||||
}
|
||||
|
||||
#toolbar-summary {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#toolbar-container>*:nth-child(2)>svg {
|
||||
display: none;
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
}
|
||||
|
||||
#toolbar-container>*:nth-child(3)>svg {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 1145px) {
|
||||
#toolbar-container {
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
#toolbar-container>*:nth-child(1):not(:hover) {
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
#toolbar-container>*:nth-child(1):not(:hover)>*:not(:first-child) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#toolbar-container>*:not(:first-child):not(:hover) {
|
||||
height: 52px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
aspect-ratio: 1/1;
|
||||
}
|
||||
|
||||
#toolbar-container>*:not(:first-child):not(:hover)>svg {
|
||||
display: block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
filter: invert();
|
||||
}
|
||||
|
||||
#toolbar-container>*:not(:first-child):not(:hover)>*:not(:first-child) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@ -3,11 +3,58 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
}
|
||||
|
||||
#unit-control-panel {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
column-gap: 10px;
|
||||
row-gap: 10px;
|
||||
}
|
||||
|
||||
#unit-control-panel>div:nth-child(2),
|
||||
#unit-controls {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 10px;
|
||||
}
|
||||
|
||||
#unit-controls {
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
#unit-control-panel>div:nth-child(2) {
|
||||
width: 330px;
|
||||
}
|
||||
|
||||
#unit-control-panel>*:nth-child(1) {
|
||||
display: none;
|
||||
padding: 14px;
|
||||
}
|
||||
|
||||
@media (max-width: 1145px) {
|
||||
#unit-control-panel>*:nth-child(1) {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#unit-control-panel>*:nth-child(1) svg {
|
||||
display: flex;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
filter: invert(100%);
|
||||
}
|
||||
|
||||
#unit-control-panel:hover>*:nth-child(1) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#unit-control-panel:not(:hover) {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
#unit-control-panel:not(:hover)>*:nth-child(2),
|
||||
#unit-control-panel:not(:hover)>*:nth-child(3) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
#unit-control-panel h3 {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
@ -223,18 +270,32 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
}
|
||||
|
||||
#advanced-settings-div {
|
||||
position: relative;
|
||||
column-gap: 5px;
|
||||
display: flex;
|
||||
height: fit-content;
|
||||
}
|
||||
|
||||
#advanced-settings-div>*:nth-child(2) {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
#advanced-settings-div button {
|
||||
#advanced-settings-div>button {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
#delete-options button {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-content: center;
|
||||
}
|
||||
|
||||
#delete-options button svg {
|
||||
margin-right: 10px;
|
||||
width: 18px;
|
||||
max-height: 18px;
|
||||
}
|
||||
|
||||
/* Element visibility control */
|
||||
#unit-control-panel:not([data-show-categories-tooltip]) #categories-tooltip,
|
||||
#unit-control-panel:not([data-show-speed-slider]) #speed-slider,
|
||||
|
||||
@ -1,47 +1,47 @@
|
||||
#unit-info-panel>* {
|
||||
position: relative;
|
||||
min-height: 100px;
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
@media (min-width: 1525px) {
|
||||
#unit-info-panel>.panel-section {
|
||||
border-right: 1px solid #555;
|
||||
padding: 0 30px;
|
||||
}
|
||||
|
||||
#unit-info-panel>.panel-section:first-child {
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
#unit-info-panel>.panel-section:last-child {
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
#unit-info-panel>.panel-section:last-of-type {
|
||||
border-right-width: 0;
|
||||
}
|
||||
#unit-info-panel>*:nth-child(1) {
|
||||
display: flex;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin: 6px;
|
||||
filter: invert(100%);
|
||||
}
|
||||
|
||||
@media (max-width: 1525px) {
|
||||
#unit-info-panel>.panel-section {
|
||||
border-bottom: 1px solid #555;
|
||||
padding: 30px 0px;
|
||||
}
|
||||
|
||||
#unit-info-panel>.panel-section:first-child {
|
||||
padding-top: 0px;
|
||||
}
|
||||
|
||||
#unit-info-panel>.panel-section:last-child {
|
||||
padding-bottom: 0px;
|
||||
}
|
||||
|
||||
#unit-info-panel>.panel-section:last-of-type {
|
||||
border-bottom-width: 0;
|
||||
}
|
||||
#unit-info-panel:hover>*:nth-child(1) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#unit-info-panel:not(:hover) {
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
padding: 10px;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
#unit-info-panel:not(:hover)>*:not(:first-child) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#unit-info-panel>.panel-section {
|
||||
border-right: 1px solid #555;
|
||||
padding: 0 30px;
|
||||
}
|
||||
|
||||
#unit-info-panel>.panel-section:first-of-type {
|
||||
padding-left: 0px;
|
||||
}
|
||||
|
||||
#unit-info-panel>.panel-section:last-of-type{
|
||||
padding-right: 0px;
|
||||
}
|
||||
|
||||
#unit-info-panel>.panel-section:last-of-type {
|
||||
border-right-width: 0;
|
||||
}
|
||||
|
||||
#general {
|
||||
display: flex;
|
||||
@ -49,6 +49,7 @@
|
||||
justify-content: space-between;
|
||||
row-gap: 4px;
|
||||
position: relative;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
#unit-label {
|
||||
@ -63,6 +64,10 @@
|
||||
#unit-name {
|
||||
margin-bottom: 4px;
|
||||
padding: 0px 0;
|
||||
width: 100%;
|
||||
text-overflow: ellipsis;
|
||||
text-wrap: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#current-task {
|
||||
@ -87,6 +92,7 @@
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
#loadout-silhouette {
|
||||
@ -101,9 +107,9 @@
|
||||
column-gap: 8px;
|
||||
display: flex;
|
||||
flex-flow: column nowrap;
|
||||
max-height: 108px;
|
||||
padding-right:40px;
|
||||
height: 100px;
|
||||
row-gap: 6px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
#loadout-items>* {
|
||||
|
||||
@ -88,7 +88,7 @@ form {
|
||||
}
|
||||
|
||||
.ol-scrollable {
|
||||
overflow-y: scroll;
|
||||
overflow-y: auto;
|
||||
scrollbar-color: white transparent;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
@ -339,10 +339,32 @@ h4 {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
button.ol-button-white {
|
||||
border: 1px solid white;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
button.ol-button-white>svg:first-child {
|
||||
stroke: white;
|
||||
fill: white;
|
||||
}
|
||||
|
||||
.ol-select-warning {
|
||||
border: 1px solid var(--primary-red);
|
||||
color: var(--primary-red) !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
.ol-select-warning::after {
|
||||
stroke: var(--primary-red);
|
||||
fill: var(--primary-red);
|
||||
}
|
||||
|
||||
button.ol-button-warning {
|
||||
border: 1px solid var(--primary-red);
|
||||
color: var(--primary-red);
|
||||
font-weight: bold;
|
||||
color: var(--primary-red) !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
|
||||
button.ol-button-warning>svg:first-child {
|
||||
@ -714,11 +736,8 @@ nav.ol-panel> :last-child {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 5px;
|
||||
position: absolute;
|
||||
height: fit-content;
|
||||
width: fit-content;
|
||||
left: calc(100% + 10px);
|
||||
top: 0px;
|
||||
}
|
||||
|
||||
#rapid-controls button {
|
||||
|
||||
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-208a32 32 0 1 1 0 64 32 32 0 1 1 0-64z"/></svg>
|
||||
|
After Width: | Height: | Size: 500 B |
1
client/public/themes/olympus/images/icons/fire-solid.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M159.3 5.4c7.8-7.3 19.9-7.2 27.7 .1c27.6 25.9 53.5 53.8 77.7 84c11-14.4 23.5-30.1 37-42.9c7.9-7.4 20.1-7.4 28 .1c34.6 33 63.9 76.6 84.5 118c20.3 40.8 33.8 82.5 33.8 111.9C448 404.2 348.2 512 224 512C98.4 512 0 404.1 0 276.5c0-38.4 17.8-85.3 45.4-131.7C73.3 97.7 112.7 48.6 159.3 5.4zM225.7 416c25.3 0 47.7-7 68.8-21c42.1-29.4 53.4-88.2 28.1-134.4c-4.5-9-16-9.6-22.5-2l-25.2 29.3c-6.6 7.6-18.5 7.4-24.7-.5c-16.5-21-46-58.5-62.8-79.8c-6.3-8-18.3-8.1-24.7-.1c-33.8 42.5-50.8 69.3-50.8 99.4C112 375.4 162.6 416 225.7 416z"/></svg>
|
||||
|
After Width: | Height: | Size: 765 B |
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M192 64C86 64 0 150 0 256S86 448 192 448H448c106 0 192-86 192-192s-86-192-192-192H192zM496 168a40 40 0 1 1 0 80 40 40 0 1 1 0-80zM392 304a40 40 0 1 1 80 0 40 40 0 1 1 -80 0zM168 200c0-13.3 10.7-24 24-24s24 10.7 24 24v32h32c13.3 0 24 10.7 24 24s-10.7 24-24 24H216v32c0 13.3-10.7 24-24 24s-24-10.7-24-24V280H136c-13.3 0-24-10.7-24-24s10.7-24 24-24h32V200z"/></svg>
|
||||
|
After Width: | Height: | Size: 601 B |
4
client/public/themes/olympus/images/icons/health.svg
Normal file
@ -0,0 +1,4 @@
|
||||
<svg width="6" height="6" viewBox="0 0 6 6" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<line x1="3" y1="4.37115e-08" x2="3" y2="6.00083" stroke="white" stroke-width="2"/>
|
||||
<line x1="6" y1="3" y2="3" stroke="white" stroke-width="2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 244 B |
41
client/public/themes/olympus/images/icons/napalm.svg
Normal file
@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 576 512"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="napalm.svg"
|
||||
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs8" />
|
||||
<sodipodi:namedview
|
||||
id="namedview6"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.1490485"
|
||||
inkscape:cx="251.94759"
|
||||
inkscape:cy="244.11502"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4" />
|
||||
<!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
|
||||
<path
|
||||
d="m 64,448 v 0 h 448 v 0 h 32 c 17.7,0 32,14.3 32,32 0,17.7 -14.3,32 -32,32 H 32 C 14.3,512 0,497.7 0,480 0,462.3 14.3,448 32,448 Z"
|
||||
id="path2"
|
||||
sodipodi:nodetypes="ccccssssssc" />
|
||||
<path
|
||||
d="m 233.26314,56.434762 c 5.55746,-5.201213 14.17865,-5.129964 19.73611,0.07125 19.66487,18.453622 38.11849,38.332232 55.36087,59.849578 7.83744,-10.25993 16.74363,-21.446096 26.36232,-30.566036 5.62871,-5.27246 14.32115,-5.27246 19.94986,0.0713 24.65233,23.512336 45.52843,54.577126 60.20583,84.074416 14.46365,29.0698 24.08233,58.78084 24.08233,79.7282 0,90.91437 -71.107,167.72133 -159.59889,167.72133 -89.48938,0 -159.59889,-76.87821 -159.59889,-167.79258 0,-27.35981 12.68241,-60.77583 32.34727,-93.8356 19.87862,-33.55852 47.95092,-68.542026 81.15319,-99.321808 z m 47.30967,292.550468 c 18.02613,0 33.98602,-4.98747 49.01966,-14.9624 29.99604,-20.94735 38.04724,-62.84206 20.02111,-95.75933 -3.20623,-6.41246 -11.39992,-6.83996 -16.03114,-1.42499 l -17.95487,20.8761 c -4.70247,5.41496 -13.18116,5.27247 -17.59863,-0.35625 -11.75617,-14.96239 -32.77477,-41.68096 -44.74469,-56.8571 -4.48872,-5.69996 -13.03866,-5.77121 -17.59863,-0.0713 -24.08233,30.28104 -36.19474,49.37591 -36.19474,70.82201 0.0712,48.80591 36.1235,77.73321 81.08193,77.73321 z"
|
||||
id="path1209"
|
||||
style="stroke-width:1" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M246.9 14.1C234 15.2 224 26 224 39c0 13.8 11.2 25 25 25H400c8.8 0 16-7.2 16-16V17.4C416 8 408 .7 398.7 1.4L246.9 14.1zM240 112c0 44.2 35.8 80 80 80s80-35.8 80-80c0-5.5-.6-10.8-1.6-16H241.6c-1 5.2-1.6 10.5-1.6 16zM72 224c-22.1 0-40 17.9-40 40s17.9 40 40 40H224v89.4L386.8 230.5c-13.3-4.3-27.3-6.5-41.6-6.5H240 72zm345.7 20.9L246.6 416H416V369.7l53.6 90.6c11.2 19 35.8 25.3 54.8 14.1s25.3-35.8 14.1-54.8L462.3 290.8c-11.2-18.9-26.6-34.5-44.6-45.9zM224 448v32c0 17.7 14.3 32 32 32H384c17.7 0 32-14.3 32-32V448H224z"/></svg>
|
||||
|
After Width: | Height: | Size: 759 B |
49
client/public/themes/olympus/images/icons/secondaries.svg
Normal file
@ -0,0 +1,49 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 576 512"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="secondaries.svg"
|
||||
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs8" />
|
||||
<sodipodi:namedview
|
||||
id="namedview6"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.1490485"
|
||||
inkscape:cx="228.44988"
|
||||
inkscape:cy="281.53728"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4" />
|
||||
<!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
|
||||
<path
|
||||
d="m 64,448 v 0 h 448 v 0 h 32 c 17.7,0 32,14.3 32,32 0,17.7 -14.3,32 -32,32 H 32 C 14.3,512 0,497.7 0,480 0,462.3 14.3,448 32,448 Z"
|
||||
id="path2"
|
||||
sodipodi:nodetypes="ccccssssssc" />
|
||||
<path
|
||||
d="m 278.72676,199.54168 c 3.72808,-5.9538 11.40681,-8.06823 17.63883,-4.72965 6.23202,3.33858 8.79159,10.85038 5.89816,17.24933 l -76.28657,169.59989 c 1.22415,1.27979 2.39265,2.61522 3.50551,3.95065 l 54.08501,-30.43672 c 5.84251,-3.28294 13.13175,-1.72494 17.19369,3.56115 4.06194,5.28608 3.50551,12.79789 -1.22415,17.52755 l -48.40942,48.40941 h -39.78475 c -7.34487,-20.75484 -27.09814,-35.61152 -50.35692,-35.61152 -23.25878,0 -43.06769,14.85668 -50.35692,35.61152 H 66.281757 l -42.01048,-29.04565 c -5.39737,-3.72808 -7.28923,-10.90603 -4.39579,-16.85983 2.89343,-5.9538 9.68188,-8.84724 15.96954,-6.89973 l 54.085,16.91547 c 1.66929,-2.17007 3.39422,-4.28451 5.23044,-6.28766 l -34.66559,-57.75744 c -3.39422,-5.61994 -2.17008,-12.85353 2.8378,-17.0824 5.00787,-4.22887 12.35274,-4.17323 17.30497,0.0556 l 56.978443,48.9102 c 0.83464,-0.22257 1.66929,-0.44514 2.50393,-0.61207 l 7.56745,-79.40257 c 0.66772,-6.84409 6.39895,-12.07453 13.29868,-12.07453 6.89973,0 12.63096,5.23044 13.29868,12.07453 l 7.51181,78.95743 z m -117.74061,-6.28766 c 7.40052,0 13.35432,5.9538 13.35432,13.35432 v 26.70865 c 0,7.40052 -5.9538,13.35432 -13.35432,13.35432 -7.40052,0 -13.35432,-5.9538 -13.35432,-13.35432 v -26.70865 c 0,-7.40052 5.9538,-13.35432 13.35432,-13.35432 z"
|
||||
id="path850"
|
||||
style="stroke-width:0.999999" />
|
||||
<path
|
||||
d="m 505.67595,141.18773 c 2.83997,-4.53547 8.68946,-6.1462 13.43688,-3.60294 4.74742,2.54326 6.69725,8.26558 4.49309,13.14017 L 465.49246,279.9225 c 0.93253,0.97491 1.82267,1.99221 2.67042,3.00952 l 41.20079,-23.18604 c 4.4507,-2.50087 10.00349,-1.31402 13.09779,2.71281 3.0943,4.02682 2.67042,9.74915 -0.93253,13.35211 l -36.87726,36.87724 h -30.30716 c -5.59516,-15.81059 -20.64278,-27.12809 -38.36082,-27.12809 -17.71803,0 -32.80803,11.3175 -38.36081,27.12809 h -33.78295 l -32.00268,-22.12635 c -4.1116,-2.83997 -5.55278,-8.30798 -3.34862,-12.84345 2.20416,-4.53548 7.37545,-6.73964 12.16526,-5.25607 l 41.20078,12.88584 c 1.27163,-1.65311 2.58565,-3.26385 3.98444,-4.7898 l -26.4075,-43.99838 c -2.58564,-4.28115 -1.65312,-9.79154 2.16177,-13.013 3.81489,-3.22146 9.41006,-3.17908 13.18256,0.0423 l 43.40494,37.25874 c 0.63582,-0.16955 1.27163,-0.3391 1.90744,-0.46626 l 5.76473,-60.48717 c 0.50865,-5.21368 4.87458,-9.19811 10.13064,-9.19811 5.25607,0 9.622,3.98443 10.13065,9.19811 l 5.72234,60.14807 z m -89.69226,-4.7898 c 5.63756,0 10.17304,4.53548 10.17304,10.17303 v 20.34608 c 0,5.63755 -4.53548,10.17303 -10.17304,10.17303 -5.63755,0 -10.17303,-4.53548 -10.17303,-10.17303 v -20.34608 c 0,-5.63755 4.53548,-10.17303 10.17303,-10.17303 z"
|
||||
id="path850-7"
|
||||
style="stroke-width:1" />
|
||||
<path
|
||||
d="m 265.48894,18.393218 c 2.4073,-3.844498 7.36562,-5.209832 11.38977,-3.054038 4.02416,2.155794 5.67693,7.006327 3.80858,11.138268 l -49.2599,109.514342 c 0.79046,0.82639 1.54498,1.6887 2.26358,2.55102 l 34.92387,-19.65365 c 3.77263,-2.11987 8.47946,-1.11384 11.10234,2.29951 2.62288,3.41333 2.26358,8.26387 -0.79046,11.31792 L 247.6677,163.7656 h -25.68988 c -4.74274,-13.40185 -17.49786,-22.99514 -32.51656,-22.99514 -15.0187,0 -27.80974,9.59329 -32.51656,22.99514 h -28.63613 l -27.12708,-18.75541 c -3.485195,-2.4073 -4.70681,-7.04226 -2.838453,-10.88676 1.868353,-3.8445 6.251803,-5.71285 10.311883,-4.45531 l 34.92386,10.92269 c 1.07789,-1.40126 2.19172,-2.7666 3.37741,-4.06008 L 124.57186,99.235499 c -2.19172,-3.628918 -1.40127,-8.299804 1.83243,-11.030478 3.23369,-2.730673 7.97643,-2.694745 11.1742,0.0359 l 36.79222,31.582379 c 0.53894,-0.14372 1.07789,-0.28743 1.61684,-0.39522 l 4.88646,-51.271971 c 0.43117,-4.419378 4.13195,-7.796787 8.58725,-7.796787 4.45531,0 8.15609,3.377409 8.58725,7.796787 l 4.85054,50.984531 z m -76.02768,-4.060079 c 4.77868,0 8.62318,3.844499 8.62318,8.623175 v 17.246357 c 0,4.778677 -3.8445,8.623176 -8.62318,8.623176 -4.77868,0 -8.62317,-3.844499 -8.62317,-8.623176 V 22.956314 c 0,-4.778676 3.84449,-8.623175 8.62317,-8.623175 z"
|
||||
id="path850-6"
|
||||
style="stroke-width:0.999997" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.2 KiB |
1
client/public/themes/olympus/images/icons/smog-solid.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--! Font Awesome Pro 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M32 144c0 79.5 64.5 144 144 144H299.3c22.6 19.9 52.2 32 84.7 32s62.1-12.1 84.7-32H496c61.9 0 112-50.1 112-112s-50.1-112-112-112c-10.7 0-21 1.5-30.8 4.3C443.8 27.7 401.1 0 352 0c-32.6 0-62.4 12.2-85.1 32.3C242.1 12.1 210.5 0 176 0C96.5 0 32 64.5 32 144zM616 368H280c-13.3 0-24 10.7-24 24s10.7 24 24 24H616c13.3 0 24-10.7 24-24s-10.7-24-24-24zm-64 96H440c-13.3 0-24 10.7-24 24s10.7 24 24 24H552c13.3 0 24-10.7 24-24s-10.7-24-24-24zm-192 0H24c-13.3 0-24 10.7-24 24s10.7 24 24 24H360c13.3 0 24-10.7 24-24s-10.7-24-24-24zM224 392c0-13.3-10.7-24-24-24H96c-13.3 0-24 10.7-24 24s10.7 24 24 24H200c13.3 0 24-10.7 24-24z"/></svg>
|
||||
|
After Width: | Height: | Size: 858 B |
@ -0,0 +1,41 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
viewBox="0 0 576 512"
|
||||
version="1.1"
|
||||
id="svg4"
|
||||
sodipodi:docname="white-phosphorous.svg"
|
||||
inkscape:version="1.1 (c68e22c387, 2021-05-23)"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<defs
|
||||
id="defs8" />
|
||||
<sodipodi:namedview
|
||||
id="namedview6"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
showgrid="false"
|
||||
inkscape:zoom="1.625"
|
||||
inkscape:cx="237.84615"
|
||||
inkscape:cy="323.38462"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1017"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg4" />
|
||||
<!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
|
||||
<path
|
||||
d="m 499.6,11.3 c 6.7,-10.7 20.5,-14.5 31.7,-8.5 11.2,6 15.8,19.5 10.6,31 L 404.8,338.6 c 2.2,2.3 4.3,4.7 6.3,7.1 L 508.3,291 c 10.5,-5.9 23.6,-3.1 30.9,6.4 7.3,9.5 6.3,23 -2.2,31.5 l -87,87 h -71.5 c -13.2,-37.3 -48.7,-64 -90.5,-64 -41.8,0 -77.4,26.7 -90.5,64 H 117.8 L 42.3,363.7 C 32.6,357 29.2,344.1 34.4,333.4 39.6,322.7 51.8,317.5 63.1,321 l 97.2,30.4 c 3,-3.9 6.1,-7.7 9.4,-11.3 L 107.4,236.3 c -6.1,-10.1 -3.9,-23.1 5.1,-30.7 9,-7.6 22.2,-7.5 31.1,0.1 L 246,293.6 c 1.5,-0.4 3,-0.8 4.5,-1.1 l 13.6,-142.7 c 1.2,-12.3 11.5,-21.7 23.9,-21.7 12.4,0 22.7,9.4 23.9,21.7 l 13.5,141.9 z M 288,0 c 13.3,0 24,10.7 24,24 v 48 c 0,13.3 -10.7,24 -24,24 -13.3,0 -24,-10.7 -24,-24 V 24 C 264,10.7 274.7,0 288,0 Z"
|
||||
id="path2"
|
||||
sodipodi:nodetypes="csccccssccscccsccccsccccscccsssssss"
|
||||
style="fill:#f9ffff;fill-opacity:1;opacity:0.41025641" />
|
||||
<path
|
||||
d="m 64.847242,449.52266 v 0 H 512.84724 v 0 h 32 c 17.7,0 32,14.3 32,32 0,17.7 -14.3,32 -32,32 H 32.847242 c -17.7,0 -31.99999998,-14.3 -31.99999998,-32 0,-17.7 14.29999998,-32 31.99999998,-32 z"
|
||||
id="path965" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.2 KiB |
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="50" height="50" version="1.1" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="m45.773 41.342-19.825-33.703c-0.4253-0.72303-1.4709-0.72305-1.8962 0l-19.825 33.703c-0.43135 0.7333 0.09738 1.6577 0.94813 1.6577h39.65c0.8507 0 1.3794-0.9244 0.9481-1.6577z" fill="#3BB9FF" stroke-width="2"/>
|
||||
<text x="21.590775" y="28.910091" fill="#082e44" font-family="sans-serif" font-size="8.838px" font-weight="bold" stroke="#082e44" stroke-width=".514" style="line-height:1.25" xml:space="preserve"><tspan x="21.590775" y="28.910091" font-family="sans-serif" font-size="8.838px" font-weight="bold" stroke="#082e44" stroke-width=".514">A</tspan></text>
|
||||
<path d="M6.74842 41L25 9.97231L43.2516 41H6.74842Z" fill="none" stroke="#082E44" stroke-width="2"/>
|
||||
<text x="16.189293" y="36.063168" fill="#082e44" font-family="sans-serif" font-size="8.838px" font-weight="bold" stroke="#082e44" stroke-width=".514" style="line-height:1.25" xml:space="preserve"><tspan x="16.189293" y="36.063168" font-family="sans-serif" font-size="8.838px" font-weight="bold" stroke="#082e44" stroke-width=".514">A</tspan></text>
|
||||
<text x="27.098772" y="36.063168" fill="#082e44" font-family="sans-serif" font-size="8.838px" font-weight="bold" stroke="#082e44" stroke-width=".514" style="line-height:1.25" xml:space="preserve"><tspan x="27.098772" y="36.063168" font-family="sans-serif" font-size="8.838px" font-weight="bold" stroke="#082e44" stroke-width=".514">A</tspan></text>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 6.7 KiB |
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="50" height="50" version="1.1" viewBox="0 0 50 50" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect transform="rotate(45 25 2.3724)" x="25" y="2.3724" width="32" height="32" rx="1.1" fill="white" stroke-width="2"/><rect transform="rotate(45 25 5.2008)" x="25" y="5.2008" width="28" height="28" fill="none" stroke="#082e44" stroke-width="2"/><path d="m25 14.598c0.71918 0 1.3002 0.58104 1.3002 1.3002v0.42257c3.8072 0.56478 6.814 3.5756 7.3787 7.3787h0.42257c0.71918 0 1.3002 0.58104 1.3002 1.3002s-0.58104 1.3002-1.3002 1.3002h-0.42257c-0.56478 3.8072-3.5756 6.814-7.3787 7.3787v0.42257c0 0.71918-0.58104 1.3002-1.3002 1.3002s-1.3002-0.58104-1.3002-1.3002v-0.42257c-3.8072-0.56478-6.814-3.5715-7.3787-7.3787h-0.42257c-0.71918 0-1.3002-0.58104-1.3002-1.3002s0.58104-1.3002 1.3002-1.3002h0.42257c0.56478-3.8072 3.5715-6.814 7.3787-7.3787v-0.42257c0-0.71918 0.58104-1.3002 1.3002-1.3002zm-6.0379 11.702c0.5079 2.3688 2.3729 4.2298 4.7377 4.7377v-0.83702c0-0.71918 0.58104-1.3002 1.3002-1.3002s1.3002 0.58104 1.3002 1.3002v0.83702c2.3688-0.5079 4.2298-2.3729 4.7377-4.7377h-0.83702c-0.71918 0-1.3002-0.58104-1.3002-1.3002s0.58104-1.3002 1.3002-1.3002h0.83702c-0.5079-2.3688-2.3688-4.2298-4.7377-4.7377v0.83702c0 0.71918-0.58104 1.3002-1.3002 1.3002s-1.3002-0.58104-1.3002-1.3002v-0.83702c-2.3688 0.5079-4.2298 2.3688-4.7377 4.7377h0.83702c0.71918 0 1.3002 0.58104 1.3002 1.3002s-0.58104 1.3002-1.3002 1.3002zm6.0379-2.6004a1.3002 1.3002 0 1 1 0 2.6004 1.3002 1.3002 0 1 1 0-2.6004z" fill="#082e44" stroke="#082e44" stroke-width=".040632"/></svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="50" height="50" version="1.1" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect transform="rotate(45 25 2.3724)" x="25" y="2.3724" width="32" height="32" rx="1.1" fill="white" stroke-width="2"/>
|
||||
<rect transform="rotate(45 25 5.2008)" x="25" y="5.2008" width="28" height="28" fill="none" stroke="#082E44" stroke-width="2"/>
|
||||
<path d="m24.581 23.186c0.72013 0 1.4119 0.2013 2.0073 0.56419v8.5083h-5.4435v-4.9416l-1.5196 2.5686c-0.31753 0.53868-1.015 0.71729-1.5537 0.39976-0.53868-0.31754-0.71729-1.015-0.39975-1.5537l2.1604-3.6517c0.69461-1.1737 1.9562-1.8939 3.3199-1.8939zm-2.9826-3.1754a2.2681 2.2681 0 1 1 4.5362 0 2.2681 2.2681 0 1 1-4.5362 0zm8.1652-2.2681c0.24949 0 0.45362 0.20413 0.45362 0.45362v3.2973c0.27218 0.15594 0.45362 0.45079 0.45362 0.78533v3.0988l0.45362-0.15026v-1.5877c0-0.24949 0.20413-0.45362 0.45362-0.45362h0.45362c0.24949 0 0.45362 0.20413 0.45362 0.45362v2.3957c0 0.19562-0.12474 0.36857-0.30903 0.43094l-1.5055 0.49898v0.75698h1.3609c0.24949 0 0.45362 0.20413 0.45362 0.45362v0.45362c0 0.24949-0.20413 0.45362-0.45362 0.45362h-1.2475l0.65208 2.6112c0.07088 0.28635-0.1446 0.56419-0.43945 0.56419h-1.6869c-0.24949 0-0.45362-0.20413-0.45362-0.45362v-2.7217h-0.45362c-0.50182 0-0.90724-0.40542-0.90724-0.90724v-4.0826c0-0.50182 0.40542-0.90724 0.90724-0.90724v-0.90725c0-0.33454 0.18145-0.6294 0.45362-0.78533v-2.8436c-0.24949 0-0.45362-0.20413-0.45362-0.45362 0-0.2495 0.20413-0.45362 0.45362-0.45362h0.45362z" fill="none" stroke="#082e44"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="50" height="50" version="1.1" viewBox="0 0 50 50" xml:space="preserve" xmlns="http://www.w3.org/2000/svg"><rect transform="rotate(45 25 2.3724)" x="25" y="2.3724" width="32" height="32" rx="1.1" fill="white" stroke-width="2"/><rect transform="rotate(45 25 5.2008)" x="25" y="5.2008" width="28" height="28" fill="none" stroke="#082E44" stroke-width="2"/><path d="m22.431 22.053-0.55497 1.5863h6.2472l-0.55497-1.5863c-0.09568-0.27217-0.35297-0.45504-0.64216-0.45504h-3.8529c-0.28918 0-0.54647 0.18287-0.64216 0.45504zm-2.0328 1.6883 0.74847-2.137c0.28706-0.81864 1.0589-1.3672 1.9265-1.3672h3.8529c0.86754 0 1.6394 0.5486 1.9265 1.3672l0.74847 2.137c0.49331 0.20412 0.84203 0.69106 0.84203 1.2588v4.0826c0 0.37636-0.30407 0.68042-0.68043 0.68042h-0.68042c-0.37636 0-0.68043-0.30407-0.68043-0.68042v-1.0206h-6.8043v1.0206c0 0.37636-0.30407 0.68042-0.68043 0.68042h-0.68042c-0.37636 0-0.68043-0.30407-0.68043-0.68042v-4.0826c0-0.56773 0.34872-1.0547 0.84203-1.2588zm1.8797 1.9392a0.68043 0.68043 0 1 0-1.3609 0 0.68043 0.68043 0 1 0 1.3609 0zm6.1239 0.68043a0.68043 0.68043 0 1 0 0-1.3609 0.68043 0.68043 0 1 0 0 1.3609z" fill="none" stroke="#082e44"/></svg>
|
||||
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="50" height="50" version="1.1" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg">
|
||||
<rect transform="rotate(45 25 2.3724)" x="25" y="2.3724" width="32" height="32" rx="1.1" fill="white" stroke-width="2"/>
|
||||
<rect transform="rotate(45 25 5.2008)" x="25" y="5.2008" width="28" height="28" fill="none" stroke="#082E44" stroke-width="2"/>
|
||||
<path d="m19.209 19.55c-0.56415 0-1.0219 0.45771-1.0219 1.0219v6.8124c0 0.56415 0.45771 1.0219 1.0219 1.0219h0.34062c0 1.1283 0.91541 2.0437 2.0437 2.0437 1.1283 0 2.0437-0.91541 2.0437-2.0437h2.725c0 1.1283 0.91542 2.0437 2.0437 2.0437 1.1283 0 2.0437-0.91541 2.0437-2.0437h0.68124c0.37681 0 0.68124-0.30443 0.68124-0.68124s-0.30443-0.68124-0.68124-0.68124v-2.4418c0-0.36191-0.14264-0.70892-0.3981-0.96438l-1.6456-1.6456c-0.25546-0.25546-0.60247-0.3981-0.96438-0.3981h-1.0793v-1.0219c0-0.56415-0.45771-1.0219-1.0219-1.0219zm7.8342 3.4062h1.0793l1.6456 1.6456v0.3981h-2.725zm-6.4718 5.4499a1.0219 1.0219 0 1 1 2.0437 0 1.0219 1.0219 0 1 1-2.0437 0zm7.8342-1.0219a1.0219 1.0219 0 1 1 0 2.0437 1.0219 1.0219 0 1 1 0-2.0437z" fill="none" stroke="#082e44"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 447 B After Width: | Height: | Size: 447 B |
@ -67,6 +67,12 @@
|
||||
--unit-height: 50px;
|
||||
--unit-width: 50px;
|
||||
|
||||
--unit-health-border-width: 2px;
|
||||
--unit-health-height: 6px;
|
||||
--unit-health-width: 36px;
|
||||
--unit-health-x: 0px;
|
||||
--unit-health-y: 26px;
|
||||
|
||||
/*** Air units ***/
|
||||
--unit-ammo-gap: calc(2px + var(--unit-stroke-width));
|
||||
--unit-ammo-border-radius: 50%;
|
||||
@ -81,4 +87,4 @@
|
||||
--unit-fuel-x: 0px;
|
||||
--unit-fuel-y: 22px;
|
||||
--unit-vvi-width: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -259,6 +259,7 @@ export enum DataIndexes {
|
||||
operateAs,
|
||||
shotsScatter,
|
||||
shotsIntensity,
|
||||
health,
|
||||
endOfData = 255
|
||||
};
|
||||
|
||||
@ -269,4 +270,6 @@ export const MGRS_PRECISION_10M = 5;
|
||||
export const MGRS_PRECISION_1M = 6;
|
||||
|
||||
export const DELETE_CYCLE_TIME = 0.05;
|
||||
export const DELETE_SLOW_THRESHOLD = 50;
|
||||
export const DELETE_SLOW_THRESHOLD = 50;
|
||||
|
||||
export const GROUPING_ZOOM_TRANSITION = 13;
|
||||
@ -58,7 +58,7 @@ export class MapContextMenu extends ContextMenu {
|
||||
|
||||
document.addEventListener("contextMenuExplosion", (e: any) => {
|
||||
this.hide();
|
||||
getApp().getServerManager().spawnExplosion(e.detail.strength, this.getLatLng());
|
||||
getApp().getServerManager().spawnExplosion(e.detail.strength ?? 0, e.detail.explosionType, this.getLatLng());
|
||||
});
|
||||
|
||||
document.addEventListener("editCoalitionArea", (e: any) => {
|
||||
|
||||
@ -36,18 +36,37 @@ export class Dropdown {
|
||||
return this.#container;
|
||||
}
|
||||
|
||||
setOptions(optionsList: string[], sort:""|"string"|"number" = "string") {
|
||||
|
||||
if ( sort === "number" ) {
|
||||
this.#optionsList = optionsList.sort( (optionA:string, optionB:string) => {
|
||||
const a = parseInt( optionA );
|
||||
const b = parseInt( optionB );
|
||||
if ( a > b )
|
||||
setOptions(optionsList: string[], sort: "" | "string" | "number" | "string+number" = "string") {
|
||||
if (sort === "number") {
|
||||
this.#optionsList = optionsList.sort((optionA: string, optionB: string) => {
|
||||
const a = parseInt(optionA);
|
||||
const b = parseInt(optionB);
|
||||
if (a > b)
|
||||
return 1;
|
||||
else
|
||||
return ( b > a ) ? -1 : 0;
|
||||
return (b > a) ? -1 : 0;
|
||||
});
|
||||
} else if ( sort === "string" ) {
|
||||
} else if (sort === "string+number") {
|
||||
this.#optionsList = optionsList.sort((optionA: string, optionB: string) => {
|
||||
var regex = /\d+/g;
|
||||
var matchesA = optionA.match(regex);
|
||||
var matchesB = optionB.match(regex);
|
||||
if ((matchesA != null && matchesA?.length > 0) && (matchesB != null && matchesB?.length > 0) && optionA[0] == optionB[0]) {
|
||||
const a = parseInt(matchesA[0] ?? 0);
|
||||
const b = parseInt(matchesB[0] ?? 0);
|
||||
if (a > b)
|
||||
return 1;
|
||||
else
|
||||
return (b > a) ? -1 : 0;
|
||||
} else {
|
||||
if (optionA > optionB)
|
||||
return 1;
|
||||
else
|
||||
return (optionB > optionA) ? -1 : 0;
|
||||
}
|
||||
|
||||
});
|
||||
} else if (sort === "string") {
|
||||
this.#optionsList = optionsList.sort();
|
||||
}
|
||||
|
||||
@ -169,17 +188,17 @@ export class Dropdown {
|
||||
}
|
||||
|
||||
#toggle() {
|
||||
this.#container.classList.contains("is-open")? this.close(): this.open();
|
||||
this.#container.classList.contains("is-open") ? this.close() : this.open();
|
||||
}
|
||||
|
||||
#createElement(defaultText: string | undefined) {
|
||||
var div = document.createElement("div");
|
||||
div.classList.add("ol-select");
|
||||
|
||||
|
||||
var value = document.createElement("div");
|
||||
value.classList.add("ol-select-value");
|
||||
value.innerText = defaultText? defaultText: "";
|
||||
|
||||
value.innerText = defaultText ? defaultText : "";
|
||||
|
||||
var options = document.createElement("div");
|
||||
options.classList.add("ol-select-options");
|
||||
|
||||
|
||||
@ -154,9 +154,28 @@ export class UnitSpawnMenu {
|
||||
this.#unitLiveryDropdown.reset();
|
||||
|
||||
if (this.#orderByRole)
|
||||
this.#unitLabelDropdown.setOptions(this.#unitDatabase.getByRole(this.spawnOptions.roleType).map((blueprint) => { return blueprint.label }));
|
||||
this.#unitLabelDropdown.setOptions(this.#unitDatabase.getByRole(this.spawnOptions.roleType).map((blueprint) => { return blueprint.label }), "string+number");
|
||||
else
|
||||
this.#unitLabelDropdown.setOptions(this.#unitDatabase.getByType(this.spawnOptions.roleType).map((blueprint) => { return blueprint.label }));
|
||||
this.#unitLabelDropdown.setOptions(this.#unitDatabase.getByType(this.spawnOptions.roleType).map((blueprint) => { return blueprint.label }), "string+number");
|
||||
|
||||
/* Add the tags to the options */
|
||||
var elements: HTMLElement[] = [];
|
||||
for (let idx = 0; idx < this.#unitLabelDropdown.getOptionElements().length; idx++) {
|
||||
let element = this.#unitLabelDropdown.getOptionElements()[idx] as HTMLElement;
|
||||
let entry = this.#unitDatabase.getByLabel(element.textContent ?? "");
|
||||
if (entry) {
|
||||
element.querySelectorAll("button")[0]?.append(...(entry.tags?.split(",").map((tag: string) => {
|
||||
tag = tag.trim();
|
||||
let el = document.createElement("div");
|
||||
el.classList.add("pill", `ol-tag`, `ol-tag-${tag.replace(/[\W_]+/g,"-")}`);
|
||||
el.textContent = tag;
|
||||
element.appendChild(el);
|
||||
return el;
|
||||
}) ?? []));
|
||||
elements.push(element);
|
||||
}
|
||||
}
|
||||
|
||||
this.#container.dispatchEvent(new Event("resize"));
|
||||
|
||||
this.spawnOptions.name = "";
|
||||
|
||||
@ -86,6 +86,7 @@ export interface UnitSpawnTable {
|
||||
export interface ObjectIconOptions {
|
||||
showState: boolean,
|
||||
showVvi: boolean,
|
||||
showHealth: boolean,
|
||||
showHotgroup: boolean,
|
||||
showUnitIcon: boolean,
|
||||
showShortLabel: boolean,
|
||||
@ -182,6 +183,7 @@ export interface UnitData {
|
||||
operateAs: string;
|
||||
shotsScatter: number;
|
||||
shotsIntensity: number;
|
||||
health: number;
|
||||
}
|
||||
|
||||
export interface LoadoutItemBlueprint {
|
||||
@ -219,6 +221,7 @@ export interface UnitBlueprint {
|
||||
shotsBaseScatter?: number;
|
||||
description?: string;
|
||||
abilities?: string;
|
||||
tags?: string;
|
||||
acquisitionRange?: number;
|
||||
engagementRange?: number;
|
||||
targetingRange?: number;
|
||||
@ -228,6 +231,8 @@ export interface UnitBlueprint {
|
||||
canRearm?: boolean;
|
||||
canAAA?: boolean;
|
||||
indirectFire?: boolean;
|
||||
markerFile?: string;
|
||||
unitWhenGrouped?: string;
|
||||
}
|
||||
|
||||
export interface UnitSpawnOptions {
|
||||
|
||||
@ -68,6 +68,7 @@ export class Map extends L.Map {
|
||||
#temporaryMarkers: TemporaryUnitMarker[] = [];
|
||||
#selecting: boolean = false;
|
||||
#isZooming: boolean = false;
|
||||
#previousZoom: number = 0;
|
||||
|
||||
#destinationGroupRotation: number = 0;
|
||||
#computeDestinationRotation: boolean = false;
|
||||
@ -102,8 +103,6 @@ export class Map extends L.Map {
|
||||
constructor(ID: string){
|
||||
/* Init the leaflet map */
|
||||
super(ID, {
|
||||
zoomSnap: 0,
|
||||
zoomDelta: 0.25,
|
||||
preferCanvas: true,
|
||||
doubleClickZoom: false,
|
||||
zoomControl: false,
|
||||
@ -503,6 +502,10 @@ export class Map extends L.Map {
|
||||
return this.#visibilityOptions;
|
||||
}
|
||||
|
||||
getPreviousZoom() {
|
||||
return this.#previousZoom;
|
||||
}
|
||||
|
||||
/* Event handlers */
|
||||
#onClick(e: any) {
|
||||
if (!this.#preventLeftClick) {
|
||||
@ -703,6 +706,7 @@ export class Map extends L.Map {
|
||||
}
|
||||
|
||||
#onZoomStart(e: any) {
|
||||
this.#previousZoom = this.getZoom();
|
||||
if (this.#centerUnit != null)
|
||||
this.#panToUnit(this.#centerUnit);
|
||||
this.#isZooming = true;
|
||||
|
||||
@ -36,6 +36,7 @@ export class TemporaryUnitMarker extends CustomMarker {
|
||||
|
||||
createIcon() {
|
||||
const category = getMarkerCategoryByName(this.#name);
|
||||
const databaseEntry = getUnitDatabaseByCategory(category)?.getByName(this.#name);
|
||||
|
||||
/* Set the icon */
|
||||
var icon = new DivIcon({
|
||||
@ -54,7 +55,8 @@ export class TemporaryUnitMarker extends CustomMarker {
|
||||
var unitIcon = document.createElement("div");
|
||||
unitIcon.classList.add("unit-icon");
|
||||
var img = document.createElement("img");
|
||||
img.src = `/resources/theme/images/units/${category}.svg`;
|
||||
|
||||
img.src = `/resources/theme/images/units/${databaseEntry?.markerFile ?? category}.svg`;
|
||||
img.onload = () => SVGInjector(img);
|
||||
unitIcon.appendChild(img);
|
||||
unitIcon.toggleAttribute("data-rotate-to-heading", false);
|
||||
@ -64,7 +66,7 @@ export class TemporaryUnitMarker extends CustomMarker {
|
||||
if (category == "aircraft" || category == "helicopter") {
|
||||
var shortLabel = document.createElement("div");
|
||||
shortLabel.classList.add("unit-short-label");
|
||||
shortLabel.innerText = getUnitDatabaseByCategory(category)?.getByName(this.#name)?.shortLabel || "";
|
||||
shortLabel.innerText = databaseEntry?.shortLabel || "";
|
||||
el.append(shortLabel);
|
||||
}
|
||||
|
||||
|
||||
56
client/src/map/rangecircle.ts
Normal file
@ -0,0 +1,56 @@
|
||||
// @ts-nocheck
|
||||
// This is a horrible hack. But it is needed at the moment to ovveride a default behaviour of Leaflet. TODO please fix me the proper way.
|
||||
|
||||
import { Circle, Point, Polyline } from 'leaflet';
|
||||
|
||||
/**
|
||||
* This custom Circle object implements a faster render method for very big circles. When zoomed in, the default ctx.arc method
|
||||
* is very slow since the circle is huge. Also, when zoomed in most of the circle points will be outside the screen and not needed. This
|
||||
* simpler, faster renderer approximates the circle with line segements and only draws those currently visibile.
|
||||
* A more refined version using arcs could be implemented but this works good enough.
|
||||
*/
|
||||
export class RangeCircle extends Circle {
|
||||
_updatePath() {
|
||||
if (!this._renderer._drawing || this._empty()) { return; }
|
||||
var p = this._point,
|
||||
ctx = this._renderer._ctx,
|
||||
r = Math.max(Math.round(this._radius), 1),
|
||||
s = (Math.max(Math.round(this._radiusY), 1) || r) / r;
|
||||
|
||||
if (s !== 1) {
|
||||
ctx.save();
|
||||
ctx.scale(1, s);
|
||||
}
|
||||
|
||||
let pathBegun = false;
|
||||
let dtheta = Math.PI * 2 / 120;
|
||||
for (let theta = 0; theta <= Math.PI * 2; theta += dtheta) {
|
||||
let p1 = new Point(p.x + r * Math.cos(theta), p.y / s + r * Math.sin(theta));
|
||||
let p2 = new Point(p.x + r * Math.cos(theta + dtheta), p.y / s + r * Math.sin(theta + dtheta));
|
||||
let l1 = this._map.layerPointToLatLng(p1);
|
||||
let l2 = this._map.layerPointToLatLng(p2);
|
||||
let line = new Polyline([l1, l2]);
|
||||
if (this._map.getBounds().intersects(line.getBounds())) {
|
||||
if (!pathBegun) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(p1.x, p1.y);
|
||||
pathBegun = true;
|
||||
}
|
||||
ctx.lineTo(p2.x, p2.y);
|
||||
}
|
||||
else {
|
||||
if (pathBegun) {
|
||||
this._renderer._fillStroke(ctx, this);
|
||||
pathBegun = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pathBegun)
|
||||
this._renderer._fillStroke(ctx, this);
|
||||
|
||||
if (s !== 1)
|
||||
ctx.restore();
|
||||
|
||||
}
|
||||
}
|
||||
@ -192,6 +192,10 @@ export class OlympusApp {
|
||||
this.#unitsManager = new UnitsManager();
|
||||
this.#weaponsManager = new WeaponsManager();
|
||||
|
||||
// Toolbars
|
||||
this.getToolbarsManager().add("primaryToolbar", new PrimaryToolbar("primary-toolbar"))
|
||||
.add("commandModeToolbar", new CommandModeToolbar("command-mode-toolbar"));
|
||||
|
||||
// Panels
|
||||
this.getPanelsManager()
|
||||
.add("connectionStatus", new ConnectionStatusPanel("connection-status-panel"))
|
||||
@ -206,11 +210,7 @@ export class OlympusApp {
|
||||
// Popups
|
||||
this.getPopupsManager()
|
||||
.add("infoPopup", new Popup("info-popup"));
|
||||
|
||||
// Toolbars
|
||||
this.getToolbarsManager().add("primaryToolbar", new PrimaryToolbar("primary-toolbar"))
|
||||
.add("commandModeToolbar", new CommandModeToolbar("command-mode-toolbar"));
|
||||
|
||||
|
||||
this.#pluginsManager = new PluginsManager();
|
||||
|
||||
/* Load the config file from the app server*/
|
||||
|
||||
@ -339,18 +339,14 @@ export function getMarkerCategoryByName(name: string) {
|
||||
else if (helicopterDatabase.getByName(name) != null)
|
||||
return "helicopter";
|
||||
else if (groundUnitDatabase.getByName(name) != null){
|
||||
var type = groundUnitDatabase.getByName(name)?.type;
|
||||
if (type === "SAM")
|
||||
var type = groundUnitDatabase.getByName(name)?.type ?? "";
|
||||
if (/\bAAA|SAM\b/.test(type) || /\bmanpad|stinger\b/i.test(type))
|
||||
return "groundunit-sam";
|
||||
else if (type === "SAM Search radar" || type === "SAM Track radar" || type === "SAM Search/Track radar")
|
||||
return "groundunit-sam-radar";
|
||||
else if (type === "SAM Launcher")
|
||||
return "groundunit-sam-launcher";
|
||||
else if (type === "Radar")
|
||||
return "groundunit-ewr";
|
||||
else
|
||||
return "groundunit-other";
|
||||
}
|
||||
else if (navyUnitDatabase.getByName(name) != null)
|
||||
return "navyunit";
|
||||
else
|
||||
return "groundunit-other"; // TODO add other unit types
|
||||
}
|
||||
|
||||
@ -9,6 +9,7 @@ import { Switch } from "../controls/switch";
|
||||
import { ROEDescriptions, ROEs, altitudeIncrements, emissionsCountermeasures, emissionsCountermeasuresDescriptions, maxAltitudeValues, maxSpeedValues, minAltitudeValues, minSpeedValues, reactionsToThreat, reactionsToThreatDescriptions, shotsIntensityDescriptions, shotsScatterDescriptions, speedIncrements } from "../constants/constants";
|
||||
import { ftToM, knotsToMs, mToFt, msToKnots } from "../other/utils";
|
||||
import { GeneralSettings, Radio, TACAN } from "../interfaces";
|
||||
import { PrimaryToolbar } from "../toolbars/primarytoolbar";
|
||||
|
||||
export class UnitControlPanel extends Panel {
|
||||
#altitudeSlider: Slider;
|
||||
@ -27,6 +28,7 @@ export class UnitControlPanel extends Panel {
|
||||
#advancedSettingsDialog: HTMLElement;
|
||||
#units: Unit[] = [];
|
||||
#selectedUnitsTypes: string[] = [];
|
||||
#deleteDropdown: Dropdown;
|
||||
|
||||
/**
|
||||
*
|
||||
@ -112,6 +114,7 @@ export class UnitControlPanel extends Panel {
|
||||
this.#radioDecimalsDropdown = new Dropdown("radio-decimals", () => {});
|
||||
this.#radioDecimalsDropdown.setOptions([".000", ".250", ".500", ".750"]);
|
||||
this.#radioCallsignDropdown = new Dropdown("radio-callsign", () => {});
|
||||
this.#deleteDropdown = new Dropdown("delete-options", () => { });
|
||||
|
||||
/* Events and timer */
|
||||
window.setInterval(() => {this.update();}, 25);
|
||||
@ -136,7 +139,13 @@ export class UnitControlPanel extends Panel {
|
||||
this.#updateRapidControls();
|
||||
});
|
||||
|
||||
window.addEventListener("resize", (e: any) => this.#calculateMaxHeight());
|
||||
|
||||
const element = document.getElementById("toolbar-container");
|
||||
if (element)
|
||||
new ResizeObserver(() => this.#calculateTop()).observe(element);
|
||||
|
||||
this.#calculateMaxHeight()
|
||||
this.hide();
|
||||
}
|
||||
|
||||
@ -154,6 +163,7 @@ export class UnitControlPanel extends Panel {
|
||||
this.#followRoadsSwitch.resetExpectedValue();
|
||||
this.#altitudeSlider.resetExpectedValue();
|
||||
this.#speedSlider.resetExpectedValue();
|
||||
this.#calculateMaxHeight();
|
||||
}
|
||||
|
||||
addButtons() {
|
||||
@ -470,4 +480,17 @@ export class UnitControlPanel extends Panel {
|
||||
button.addEventListener("click", callback);
|
||||
return button;
|
||||
}
|
||||
|
||||
#calculateTop() {
|
||||
const element = document.getElementById("toolbar-container");
|
||||
if (element)
|
||||
this.getElement().style.top = `${element.offsetTop + element.offsetHeight + 10}px`;
|
||||
}
|
||||
|
||||
#calculateMaxHeight() {
|
||||
const element = document.getElementById("unit-control-panel-content");
|
||||
this.#calculateTop();
|
||||
if (element)
|
||||
element.style.maxHeight = `${window.innerHeight - this.getElement().offsetTop - 10}px`;
|
||||
}
|
||||
}
|
||||
@ -160,8 +160,8 @@ export class ServerManager {
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
spawnExplosion(intensity: number, latlng: LatLng, callback: CallableFunction = () => {}) {
|
||||
var command = { "intensity": intensity, "location": latlng };
|
||||
spawnExplosion(intensity: number, explosionType: string, latlng: LatLng, callback: CallableFunction = () => {}) {
|
||||
var command = { "explosionType": explosionType, "intensity": intensity, "location": latlng };
|
||||
var data = { "explosion": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
@ -212,8 +212,8 @@ export class ServerManager {
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
deleteUnit(ID: number, explosion: boolean, immediate: boolean, callback: CallableFunction = () => {}) {
|
||||
var command = { "ID": ID, "explosion": explosion, "immediate": immediate };
|
||||
deleteUnit(ID: number, explosion: boolean, explosionType: string, immediate: boolean, callback: CallableFunction = () => {}) {
|
||||
var command = { "ID": ID, "explosion": explosion, "explosionType": explosionType, "immediate": immediate };
|
||||
var data = { "deleteUnit": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
45
client/src/unit/group.ts
Normal file
@ -0,0 +1,45 @@
|
||||
import { Unit } from "./unit";
|
||||
|
||||
export class Group {
|
||||
#members: Unit[] = [];
|
||||
#name: string;
|
||||
|
||||
constructor(name: string) {
|
||||
this.#name = name;
|
||||
|
||||
document.addEventListener("unitDeath", (e: any) => {
|
||||
if (this.#members.includes(e.detail))
|
||||
this.getLeader()?.onGroupChanged(e.detail);
|
||||
});
|
||||
}
|
||||
|
||||
getName() {
|
||||
return this.#name;
|
||||
}
|
||||
|
||||
addMember(member: Unit) {
|
||||
if (!this.#members.includes(member)) {
|
||||
this.#members.push(member);
|
||||
member.setGroup(this);
|
||||
|
||||
this.getLeader()?.onGroupChanged(member);
|
||||
}
|
||||
}
|
||||
|
||||
removeMember(member: Unit) {
|
||||
if (this.#members.includes(member)) {
|
||||
delete this.#members[this.#members.indexOf(member)];
|
||||
member.setGroup(null);
|
||||
|
||||
this.getLeader()?.onGroupChanged(member);
|
||||
}
|
||||
}
|
||||
|
||||
getMembers() {
|
||||
return this.#members;
|
||||
}
|
||||
|
||||
getLeader() {
|
||||
return this.#members.find((unit: Unit) => { return (unit.getIsLeader() && unit.getAlive())})
|
||||
}
|
||||
}
|
||||
@ -5,12 +5,14 @@ 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, 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, GROUND_UNIT_AIR_DEFENCE_REGEX } from '../constants/constants';
|
||||
import { DataExtractor } from '../server/dataextractor';
|
||||
import { groundUnitDatabase } from './databases/groundunitdatabase';
|
||||
import { navyUnitDatabase } from './databases/navyunitdatabase';
|
||||
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';
|
||||
|
||||
var pathIcon = new Icon({
|
||||
iconUrl: '/resources/theme/images/markers/marker-icon.png',
|
||||
@ -20,12 +22,11 @@ var pathIcon = new Icon({
|
||||
|
||||
/**
|
||||
* Unit class which controls unit behaviour
|
||||
*
|
||||
* Just about everything is a unit - even missiles!
|
||||
*/
|
||||
export class Unit extends CustomMarker {
|
||||
export abstract class Unit extends CustomMarker {
|
||||
ID: number;
|
||||
|
||||
/* Data controlled directly by the backend. No setters are provided to avoid misalignments */
|
||||
#alive: boolean = false;
|
||||
#human: boolean = false;
|
||||
#controlled: boolean = false;
|
||||
@ -87,8 +88,10 @@ export class Unit extends CustomMarker {
|
||||
#operateAs: string = "blue";
|
||||
#shotsScatter: number = 2;
|
||||
#shotsIntensity: number = 2;
|
||||
#health: number = 100;
|
||||
|
||||
#selectable: boolean;
|
||||
/* Other members used to draw the unit, mostly ancillary stuff like targets, ranges and so on */
|
||||
#group: Group | null = null;
|
||||
#selected: boolean = false;
|
||||
#hidden: boolean = false;
|
||||
#highlighted: boolean = false;
|
||||
@ -96,8 +99,8 @@ export class Unit extends CustomMarker {
|
||||
#pathMarkers: Marker[] = [];
|
||||
#pathPolyline: Polyline;
|
||||
#contactsPolylines: Polyline[] = [];
|
||||
#engagementCircle: Circle;
|
||||
#acquisitionCircle: Circle;
|
||||
#engagementCircle: RangeCircle;
|
||||
#acquisitionCircle: RangeCircle;
|
||||
#miniMapMarker: CircleMarker | null = null;
|
||||
#targetPositionMarker: TargetMarker;
|
||||
#targetPositionPolyline: Polyline;
|
||||
@ -105,6 +108,7 @@ export class Unit extends CustomMarker {
|
||||
#hotgroup: number | null = null;
|
||||
#detectionMethods: number[] = [];
|
||||
|
||||
/* Getters for backend driven data */
|
||||
getAlive() { return this.#alive };
|
||||
getHuman() { return this.#human };
|
||||
getControlled() { return this.#controlled };
|
||||
@ -121,8 +125,8 @@ export class Unit extends CustomMarker {
|
||||
getHorizontalVelocity() { return this.#horizontalVelocity };
|
||||
getVerticalVelocity() { return this.#verticalVelocity };
|
||||
getHeading() { return this.#heading };
|
||||
getIsActiveTanker() { return this.#isActiveTanker };
|
||||
getIsActiveAWACS() { return this.#isActiveAWACS };
|
||||
getIsActiveTanker() { return this.#isActiveTanker };
|
||||
getOnOff() { return this.#onOff };
|
||||
getFollowRoads() { return this.#followRoads };
|
||||
getFuel() { return this.#fuel };
|
||||
@ -145,8 +149,9 @@ export class Unit extends CustomMarker {
|
||||
getActivePath() { return this.#activePath };
|
||||
getIsLeader() { return this.#isLeader };
|
||||
getOperateAs() { return this.#operateAs };
|
||||
getShotsScatter() { return this.#shotsScatter};
|
||||
getShotsIntensity() { return this.#shotsIntensity};
|
||||
getShotsScatter() { return this.#shotsScatter };
|
||||
getShotsIntensity() { return this.#shotsIntensity };
|
||||
getHealth() { return this.#health };
|
||||
|
||||
static getConstructor(type: string) {
|
||||
if (type === "GroundUnit") return GroundUnit;
|
||||
@ -159,15 +164,15 @@ export class Unit extends CustomMarker {
|
||||
super(new LatLng(0, 0), { riseOnHover: true, keyboard: false });
|
||||
|
||||
this.ID = ID;
|
||||
this.#selectable = true;
|
||||
|
||||
this.#pathPolyline = new Polyline([], { color: '#2d3e50', weight: 3, opacity: 0.5, smoothFactor: 1 });
|
||||
this.#pathPolyline.addTo(getApp().getMap());
|
||||
this.#targetPositionMarker = new TargetMarker(new LatLng(0, 0));
|
||||
this.#targetPositionPolyline = new Polyline([], { color: '#FF0000', weight: 3, opacity: 0.5, smoothFactor: 1 });
|
||||
this.#engagementCircle = new Circle(this.getPosition(), { radius: 0, weight: 4, opacity: 1, fillOpacity: 0, dashArray: "4 8", interactive: false, bubblingMouseEvents: false });
|
||||
this.#acquisitionCircle = new Circle(this.getPosition(), { radius: 0, weight: 2, opacity: 1, fillOpacity: 0, dashArray: "8 12", interactive: false, bubblingMouseEvents: false });
|
||||
this.#engagementCircle = new RangeCircle(this.getPosition(), { radius: 0, weight: 4, opacity: 1, fillOpacity: 0, dashArray: "4 8", interactive: false, bubblingMouseEvents: false });
|
||||
this.#acquisitionCircle = new RangeCircle(this.getPosition(), { radius: 0, weight: 2, opacity: 1, fillOpacity: 0, dashArray: "8 12", interactive: false, bubblingMouseEvents: false });
|
||||
|
||||
/* Leaflet events listeners */
|
||||
this.on('click', (e) => this.#onClick(e));
|
||||
this.on('dblclick', (e) => this.#onDoubleClick(e));
|
||||
this.on('contextmenu', (e) => this.#onContextMenu(e));
|
||||
@ -181,7 +186,7 @@ export class Unit extends CustomMarker {
|
||||
this.setHighlighted(false);
|
||||
document.dispatchEvent(new CustomEvent("unitMouseout", { detail: this }));
|
||||
});
|
||||
getApp().getMap().on("zoomend", () => { this.#onZoom(); })
|
||||
getApp().getMap().on("zoomend", (e: any) => { this.#onZoom(e); })
|
||||
|
||||
/* Deselect units if they are hidden */
|
||||
document.addEventListener("toggleCoalitionVisibility", (ev: CustomEventInit) => {
|
||||
@ -192,13 +197,14 @@ export class Unit extends CustomMarker {
|
||||
window.setTimeout(() => { this.setSelected(this.getSelected() && !this.getHidden()) }, 300);
|
||||
});
|
||||
|
||||
/* Update the marker when the visibility options change */
|
||||
document.addEventListener("mapVisibilityOptionsChanged", (ev: CustomEventInit) => {
|
||||
this.#updateMarker();
|
||||
|
||||
/* Circles don't like to be updated when the map is zooming */
|
||||
if (!getApp().getMap().isZooming())
|
||||
if (!getApp().getMap().isZooming())
|
||||
this.#drawRanges();
|
||||
else
|
||||
else
|
||||
this.once("zoomend", () => { this.#drawRanges(); })
|
||||
|
||||
if (this.getSelected())
|
||||
@ -206,10 +212,25 @@ export class Unit extends CustomMarker {
|
||||
});
|
||||
}
|
||||
|
||||
getCategory() {
|
||||
// Overloaded by child classes
|
||||
return "";
|
||||
}
|
||||
/********************** Abstract methods *************************/
|
||||
/** Get the unit category string
|
||||
*
|
||||
* @returns string The unit category
|
||||
*/
|
||||
abstract getCategory(): string;
|
||||
|
||||
/** Get the icon options
|
||||
* Used to configure how the marker appears on the map
|
||||
*
|
||||
* @returns ObjectIconOptions
|
||||
*/
|
||||
abstract getIconOptions(): ObjectIconOptions;
|
||||
|
||||
/** Get the actions that this unit can perform
|
||||
*
|
||||
* @returns Object containing the available actions
|
||||
*/
|
||||
abstract getActions(): {[key: string]: { text: string, tooltip: string, type: string}};
|
||||
|
||||
/** Get the category but for display use - for the user. (i.e. has spaces in it)
|
||||
*
|
||||
@ -220,9 +241,15 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
/********************** Unit data *************************/
|
||||
/** This function is called by the units manager to update all the data coming from the backend. It reads the binary raw data using a DataExtractor
|
||||
*
|
||||
* @param dataExtractor The DataExtractor object pointing to the binary buffer which contains the raw data coming from the backend
|
||||
*/
|
||||
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) {
|
||||
datumIndex = dataExtractor.extractUInt8();
|
||||
@ -231,7 +258,7 @@ export class Unit extends CustomMarker {
|
||||
case DataIndexes.alive: this.setAlive(dataExtractor.extractBool()); updateMarker = true; break;
|
||||
case DataIndexes.human: this.#human = dataExtractor.extractBool(); break;
|
||||
case DataIndexes.controlled: this.#controlled = dataExtractor.extractBool(); updateMarker = true; break;
|
||||
case DataIndexes.coalition: this.#coalition = enumToCoalition(dataExtractor.extractUInt8()); updateMarker = true; this.#clearRanges(); break;
|
||||
case DataIndexes.coalition: let newCoalition = enumToCoalition(dataExtractor.extractUInt8()); updateMarker = true; if (newCoalition != this.#coalition) this.#clearRanges(); this.#coalition = newCoalition; break; // If the coalition has changed, redraw the range circles to update the colour
|
||||
case DataIndexes.country: this.#country = dataExtractor.extractUInt8(); break;
|
||||
case DataIndexes.name: this.#name = dataExtractor.extractString(); break;
|
||||
case DataIndexes.unitName: this.#unitName = dataExtractor.extractString(); break;
|
||||
@ -266,32 +293,37 @@ export class Unit extends CustomMarker {
|
||||
case DataIndexes.ammo: this.#ammo = dataExtractor.extractAmmo(); break;
|
||||
case DataIndexes.contacts: this.#contacts = dataExtractor.extractContacts(); document.dispatchEvent(new CustomEvent("contactsUpdated", { detail: this })); break;
|
||||
case DataIndexes.activePath: this.#activePath = dataExtractor.extractActivePath(); break;
|
||||
case DataIndexes.isLeader: this.#isLeader = dataExtractor.extractBool(); updateMarker = true; break;
|
||||
case DataIndexes.isLeader: this.#isLeader = dataExtractor.extractBool(); break;
|
||||
case DataIndexes.operateAs: this.#operateAs = enumToCoalition(dataExtractor.extractUInt8()); break;
|
||||
case DataIndexes.shotsScatter: this.#shotsScatter = dataExtractor.extractUInt8(); break;
|
||||
case DataIndexes.shotsIntensity: this.#shotsIntensity = dataExtractor.extractUInt8(); break;
|
||||
case DataIndexes.health: this.#health = dataExtractor.extractUInt8(); updateMarker = true; break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dead units can't be selected */
|
||||
/* Dead and hidden units can't be selected */
|
||||
this.setSelected(this.getSelected() && this.#alive && !this.getHidden())
|
||||
|
||||
/* Update the marker if required */
|
||||
if (updateMarker)
|
||||
this.#updateMarker();
|
||||
|
||||
/* Redraw the marker if isLeader has changed. TODO I don't love this approach, observables may be more elegant */
|
||||
if (oldIsLeader !== this.#isLeader) {
|
||||
this.#redrawMarker();
|
||||
|
||||
/* Reapply selection */
|
||||
if (this.getSelected()) {
|
||||
this.setSelected(false);
|
||||
this.setSelected(true);
|
||||
}
|
||||
}
|
||||
|
||||
/* 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)
|
||||
document.dispatchEvent(new CustomEvent("unitUpdated", { detail: this }));
|
||||
}
|
||||
|
||||
drawLines() {
|
||||
/* Leaflet does not like it when you change coordinates when the map is zooming */
|
||||
if (!getApp().getMap().isZooming()) {
|
||||
this.#drawPath();
|
||||
this.#drawContacts();
|
||||
this.#drawTarget();
|
||||
}
|
||||
}
|
||||
|
||||
/** Get unit data collated into an object
|
||||
*
|
||||
* @returns object populated by unit information which can also be retrieved using getters
|
||||
@ -342,7 +374,8 @@ export class Unit extends CustomMarker {
|
||||
isLeader: this.#isLeader,
|
||||
operateAs: this.#operateAs,
|
||||
shotsScatter: this.#shotsScatter,
|
||||
shotsIntensity: this.#shotsIntensity
|
||||
shotsIntensity: this.#shotsIntensity,
|
||||
health: this.#health
|
||||
}
|
||||
}
|
||||
|
||||
@ -362,27 +395,6 @@ export class Unit extends CustomMarker {
|
||||
return getUnitDatabaseByCategory(this.getMarkerCategory());
|
||||
}
|
||||
|
||||
/** Get the icon options
|
||||
* Used to configure how the marker appears on the map
|
||||
*
|
||||
* @returns ObjectIconOptions
|
||||
*/
|
||||
getIconOptions(): ObjectIconOptions {
|
||||
// Default values, overloaded by child classes if needed
|
||||
return {
|
||||
showState: false,
|
||||
showVvi: false,
|
||||
showHotgroup: false,
|
||||
showUnitIcon: true,
|
||||
showShortLabel: false,
|
||||
showFuel: false,
|
||||
showAmmo: false,
|
||||
showSummary: true,
|
||||
showCallsign: true,
|
||||
rotateToHeading: false
|
||||
}
|
||||
}
|
||||
|
||||
/** Set the unit as alive or dead
|
||||
*
|
||||
* @param newAlive (boolean) true = alive, false = dead
|
||||
@ -398,16 +410,11 @@ export class Unit extends CustomMarker {
|
||||
* @param selected (boolean)
|
||||
*/
|
||||
setSelected(selected: boolean) {
|
||||
/* Only alive units can be selected. Some units are not selectable (weapons) */
|
||||
if ((this.#alive || !selected) && this.getSelectable() && this.getSelected() != selected && this.belongsToCommandedCoalition()) {
|
||||
/* Only alive units can be selected that belong to the commanded coalition can be selected */
|
||||
if ((this.#alive || !selected) && this.belongsToCommandedCoalition() && this.getSelected() != selected) {
|
||||
this.#selected = selected;
|
||||
|
||||
/* Circles don't like to be updated when the map is zooming */
|
||||
if (!getApp().getMap().isZooming())
|
||||
this.#drawRanges();
|
||||
else
|
||||
this.once("zoomend", () => { this.#drawRanges(); })
|
||||
|
||||
/* If selected, update the marker to show the selected effects, else clear all the drawings that are only shown for selected units. */
|
||||
if (selected) {
|
||||
this.#updateMarker();
|
||||
}
|
||||
@ -417,21 +424,27 @@ export class Unit extends CustomMarker {
|
||||
this.#clearTarget();
|
||||
}
|
||||
|
||||
this.getElement()?.querySelector(`.unit`)?.toggleAttribute("data-is-selected", selected);
|
||||
if (this.getCategory() === "GroundUnit" && getApp().getMap().getZoom() < 13) {
|
||||
if (this.#isLeader)
|
||||
/* When the group leader is selected, if grouping is active, all the other group members are also selected */
|
||||
if (this.getCategory() === "GroundUnit" && getApp().getMap().getZoom() < GROUPING_ZOOM_TRANSITION) {
|
||||
if (this.#isLeader) {
|
||||
/* Redraw the marker in case the leader unit was replaced by a group marker, like for SAM Sites */
|
||||
this.#redrawMarker();
|
||||
this.getGroupMembers().forEach((unit: Unit) => unit.setSelected(selected));
|
||||
else
|
||||
}
|
||||
else {
|
||||
this.#updateMarker();
|
||||
}
|
||||
}
|
||||
|
||||
// Trigger events after all (de-)selecting has been done
|
||||
/* Activate the selection effects on the marker */
|
||||
this.getElement()?.querySelector(`.unit`)?.toggleAttribute("data-is-selected", selected);
|
||||
|
||||
/* Trigger events after all (de-)selecting has been done */
|
||||
if (selected) {
|
||||
document.dispatchEvent(new CustomEvent("unitSelection", { detail: this }));
|
||||
} else {
|
||||
document.dispatchEvent(new CustomEvent("unitDeselection", { detail: this }));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -443,22 +456,6 @@ export class Unit extends CustomMarker {
|
||||
return this.#selected;
|
||||
}
|
||||
|
||||
/** Set whether this unit is selectable
|
||||
*
|
||||
* @param selectable (boolean)
|
||||
*/
|
||||
setSelectable(selectable: boolean) {
|
||||
this.#selectable = selectable;
|
||||
}
|
||||
|
||||
/** Get whether this unit is selectable
|
||||
*
|
||||
* @returns boolean
|
||||
*/
|
||||
getSelectable() {
|
||||
return this.#selectable;
|
||||
}
|
||||
|
||||
/** Set the number of the hotgroup to which the unit belongs
|
||||
*
|
||||
* @param hotgroup (number)
|
||||
@ -481,9 +478,9 @@ export class Unit extends CustomMarker {
|
||||
* @param highlighted (boolean)
|
||||
*/
|
||||
setHighlighted(highlighted: boolean) {
|
||||
if (this.getSelectable() && this.#highlighted != highlighted) {
|
||||
this.getElement()?.querySelector(`[data-object|="unit"]`)?.toggleAttribute("data-is-highlighted", highlighted);
|
||||
if (this.#highlighted != highlighted) {
|
||||
this.#highlighted = highlighted;
|
||||
this.getElement()?.querySelector(`[data-object|="unit"]`)?.toggleAttribute("data-is-highlighted", highlighted);
|
||||
this.getGroupMembers().forEach((unit: Unit) => unit.setHighlighted(highlighted));
|
||||
}
|
||||
}
|
||||
@ -501,7 +498,19 @@ export class Unit extends CustomMarker {
|
||||
* @returns Unit[]
|
||||
*/
|
||||
getGroupMembers() {
|
||||
return Object.values(getApp().getUnitsManager().getUnits()).filter((unit: Unit) => { return unit != this && unit.getGroupName() === this.getGroupName(); });
|
||||
if (this.#group !== null)
|
||||
return this.#group.getMembers().filter((unit: Unit) => { return unit != this; })
|
||||
return [];
|
||||
}
|
||||
|
||||
/** Return the leader of the group
|
||||
*
|
||||
* @returns Unit The leader of the group
|
||||
*/
|
||||
getGroupLeader() {
|
||||
if (this.#group !== null)
|
||||
return this.#group.getLeader();
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Returns whether the user is allowed to command this unit, based on coalition
|
||||
@ -520,6 +529,31 @@ export class Unit extends CustomMarker {
|
||||
return this.getDatabase()?.getSpawnPointsByName(this.getName());
|
||||
}
|
||||
|
||||
getDatabaseEntry() {
|
||||
return this.getDatabase()?.getByName(this.#name);
|
||||
}
|
||||
|
||||
getGroup() {
|
||||
return this.#group;
|
||||
}
|
||||
|
||||
setGroup(group: Group | null) {
|
||||
this.#group = group;
|
||||
}
|
||||
|
||||
drawLines() {
|
||||
/* Leaflet does not like it when you change coordinates when the map is zooming */
|
||||
if (!getApp().getMap().isZooming()) {
|
||||
this.#drawPath();
|
||||
this.#drawContacts();
|
||||
this.#drawTarget();
|
||||
}
|
||||
}
|
||||
|
||||
checkZoomRedraw() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/********************** Icon *************************/
|
||||
createIcon(): void {
|
||||
/* Set the icon */
|
||||
@ -530,6 +564,7 @@ export class Unit extends CustomMarker {
|
||||
});
|
||||
this.setIcon(icon);
|
||||
|
||||
/* Create the base element */
|
||||
var el = document.createElement("div");
|
||||
el.classList.add("unit");
|
||||
el.setAttribute("data-object", `unit-${this.getMarkerCategory()}`);
|
||||
@ -537,8 +572,8 @@ export class Unit extends CustomMarker {
|
||||
|
||||
var iconOptions = this.getIconOptions();
|
||||
|
||||
// Generate and append elements depending on active options
|
||||
// Velocity vector
|
||||
/* Generate and append elements depending on active options */
|
||||
/* Velocity vector */
|
||||
if (iconOptions.showVvi) {
|
||||
var vvi = document.createElement("div");
|
||||
vvi.classList.add("unit-vvi");
|
||||
@ -546,7 +581,7 @@ export class Unit extends CustomMarker {
|
||||
el.append(vvi);
|
||||
}
|
||||
|
||||
// Hotgroup indicator
|
||||
/* Hotgroup indicator */
|
||||
if (iconOptions.showHotgroup) {
|
||||
var hotgroup = document.createElement("div");
|
||||
hotgroup.classList.add("unit-hotgroup");
|
||||
@ -556,42 +591,42 @@ export class Unit extends CustomMarker {
|
||||
el.append(hotgroup);
|
||||
}
|
||||
|
||||
// Main icon
|
||||
/* Main icon */
|
||||
if (iconOptions.showUnitIcon) {
|
||||
var unitIcon = document.createElement("div");
|
||||
unitIcon.classList.add("unit-icon");
|
||||
var img = document.createElement("img");
|
||||
var imgSrc;
|
||||
|
||||
/* 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)))
|
||||
imgSrc = this.getMarkerCategory();
|
||||
marker = this.getDatabaseEntry()?.markerFile ?? this.getMarkerCategory();
|
||||
else
|
||||
imgSrc = "aircraft";
|
||||
|
||||
img.src = `/resources/theme/images/units/${imgSrc}.svg`;
|
||||
marker = "aircraft";
|
||||
img.src = `/resources/theme/images/units/${marker}.svg`;
|
||||
img.onload = () => SVGInjector(img);
|
||||
unitIcon.appendChild(img);
|
||||
|
||||
unitIcon.toggleAttribute("data-rotate-to-heading", iconOptions.rotateToHeading);
|
||||
el.append(unitIcon);
|
||||
}
|
||||
|
||||
// State icon
|
||||
/* State icon */
|
||||
if (iconOptions.showState) {
|
||||
var state = document.createElement("div");
|
||||
state.classList.add("unit-state");
|
||||
el.appendChild(state);
|
||||
}
|
||||
|
||||
// Short label
|
||||
/* Short label */
|
||||
if (iconOptions.showShortLabel) {
|
||||
var shortLabel = document.createElement("div");
|
||||
shortLabel.classList.add("unit-short-label");
|
||||
shortLabel.innerText = getUnitDatabaseByCategory(this.getMarkerCategory())?.getByName(this.#name)?.shortLabel || "";
|
||||
shortLabel.innerText = this.getDatabaseEntry()?.shortLabel || "";
|
||||
el.append(shortLabel);
|
||||
}
|
||||
|
||||
// Fuel indicator
|
||||
/* Fuel indicator */
|
||||
if (iconOptions.showFuel) {
|
||||
var fuelIndicator = document.createElement("div");
|
||||
fuelIndicator.classList.add("unit-fuel");
|
||||
@ -601,7 +636,17 @@ export class Unit extends CustomMarker {
|
||||
el.append(fuelIndicator);
|
||||
}
|
||||
|
||||
// Ammo indicator
|
||||
/* Health indicator */
|
||||
if (iconOptions.showHealth) {
|
||||
var healthIndicator = document.createElement("div");
|
||||
healthIndicator.classList.add("unit-health");
|
||||
var healthLevel = document.createElement("div");
|
||||
healthLevel.classList.add("unit-health-level");
|
||||
healthIndicator.appendChild(healthLevel);
|
||||
el.append(healthIndicator);
|
||||
}
|
||||
|
||||
/* Ammo indicator */
|
||||
if (iconOptions.showAmmo) {
|
||||
var ammoIndicator = document.createElement("div");
|
||||
ammoIndicator.classList.add("unit-ammo");
|
||||
@ -610,7 +655,7 @@ export class Unit extends CustomMarker {
|
||||
el.append(ammoIndicator);
|
||||
}
|
||||
|
||||
// Unit summary
|
||||
/* Unit summary */
|
||||
if (iconOptions.showSummary) {
|
||||
var summary = document.createElement("div");
|
||||
summary.classList.add("unit-summary");
|
||||
@ -628,25 +673,29 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
this.getElement()?.appendChild(el);
|
||||
|
||||
/* Circles don't like to be updated when the map is zooming */
|
||||
if (!getApp().getMap().isZooming())
|
||||
this.#drawRanges();
|
||||
else
|
||||
this.once("zoomend", () => { this.#drawRanges(); })
|
||||
}
|
||||
|
||||
/********************** Visibility *************************/
|
||||
updateVisibility() {
|
||||
const hiddenUnits = getApp().getMap().getHiddenTypes();
|
||||
var hidden = ((this.#human && hiddenUnits.includes("human")) ||
|
||||
(this.#controlled == false && hiddenUnits.includes("dcs")) ||
|
||||
(hiddenUnits.includes(this.getMarkerCategory())) ||
|
||||
(hiddenUnits.includes(this.#coalition)) ||
|
||||
const hiddenTypes = getApp().getMap().getHiddenTypes();
|
||||
var hidden = (
|
||||
/* Hide the unit if it is a human and humans are hidden */
|
||||
(this.#human && hiddenTypes.includes("human")) ||
|
||||
/* Hide the unit if it is DCS controlled and DCS controlled units are hidden */
|
||||
(this.#controlled == false && hiddenTypes.includes("dcs")) ||
|
||||
/* Hide the unit if this specific category is hidden */
|
||||
(hiddenTypes.includes(this.getMarkerCategory())) ||
|
||||
/* Hide the unit if this coalition is hidden */
|
||||
(hiddenTypes.includes(this.#coalition)) ||
|
||||
/* 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))) ||
|
||||
(getApp().getMap().getVisibilityOptions()[HIDE_GROUP_MEMBERS] && !this.#isLeader && this.getCategory() == "GroundUnit" && getApp().getMap().getZoom() < 13 && (this.belongsToCommandedCoalition() || (!this.belongsToCommandedCoalition() && this.#detectionMethods.length == 0)))) &&
|
||||
!(this.getSelected());
|
||||
/* 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()
|
||||
);
|
||||
|
||||
/* Force dead units to be hidden */
|
||||
this.setHidden(hidden || !this.#alive);
|
||||
}
|
||||
|
||||
@ -666,11 +715,12 @@ export class Unit extends CustomMarker {
|
||||
getApp().getMap().removeLayer(this);
|
||||
}
|
||||
|
||||
/* Draw the range circles if the unit is not hidden */
|
||||
if (!this.getHidden()) {
|
||||
/* Circles don't like to be updated when the map is zooming */
|
||||
if (!getApp().getMap().isZooming())
|
||||
if (!getApp().getMap().isZooming())
|
||||
this.#drawRanges();
|
||||
else
|
||||
else
|
||||
this.once("zoomend", () => { this.#drawRanges(); })
|
||||
} else {
|
||||
this.#clearRanges();
|
||||
@ -705,7 +755,7 @@ export class Unit extends CustomMarker {
|
||||
if (typeof (roles) === "string")
|
||||
roles = [roles];
|
||||
|
||||
var loadouts = this.getDatabase()?.getByName(this.#name)?.loadouts;
|
||||
var loadouts = this.getDatabaseEntry()?.loadouts;
|
||||
if (loadouts) {
|
||||
return loadouts.some((loadout: LoadoutBlueprint) => {
|
||||
return (roles as string[]).some((role: string) => { return loadout.roles.includes(role) });
|
||||
@ -719,11 +769,11 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
canTargetPoint() {
|
||||
return this.getDatabase()?.getByName(this.#name)?.canTargetPoint === true;
|
||||
return this.getDatabaseEntry()?.canTargetPoint === true;
|
||||
}
|
||||
|
||||
canRearm() {
|
||||
return this.getDatabase()?.getByName(this.#name)?.canRearm === true;
|
||||
return this.getDatabaseEntry()?.canRearm === true;
|
||||
}
|
||||
|
||||
canLandAtPoint() {
|
||||
@ -731,11 +781,11 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
canAAA() {
|
||||
return this.getDatabase()?.getByName(this.#name)?.canAAA === true;
|
||||
return this.getDatabaseEntry()?.canAAA === true;
|
||||
}
|
||||
|
||||
indirectFire() {
|
||||
return this.getDatabase()?.getByName(this.#name)?.indirectFire === true;
|
||||
return this.getDatabaseEntry()?.indirectFire === true;
|
||||
}
|
||||
|
||||
isTanker() {
|
||||
@ -845,8 +895,8 @@ export class Unit extends CustomMarker {
|
||||
getApp().getServerManager().setOperateAs(this.ID, coalitionToEnum(operateAs));
|
||||
}
|
||||
|
||||
delete(explosion: boolean, immediate: boolean) {
|
||||
getApp().getServerManager().deleteUnit(this.ID, explosion, immediate);
|
||||
delete(explosion: boolean, explosionType: string, immediate: boolean) {
|
||||
getApp().getServerManager().deleteUnit(this.ID, explosion, explosionType, immediate);
|
||||
}
|
||||
|
||||
refuel() {
|
||||
@ -924,11 +974,6 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
|
||||
/***********************************************/
|
||||
getActions(): { [key: string]: { text: string, tooltip: string, type: string } } {
|
||||
/* To be implemented by child classes */ // TODO make Unit an abstract class
|
||||
return {};
|
||||
}
|
||||
|
||||
executeAction(e: any, action: string) {
|
||||
if (action === "center-map")
|
||||
getApp().getMap().centerOnUnit(this.ID);
|
||||
@ -952,20 +997,21 @@ export class Unit extends CustomMarker {
|
||||
return this;
|
||||
}
|
||||
|
||||
onGroupChanged(member: Unit) {
|
||||
this.#redrawMarker();
|
||||
}
|
||||
|
||||
/***********************************************/
|
||||
#onClick(e: any) {
|
||||
|
||||
// Exit if we were waiting for a doubleclick
|
||||
/* Exit if we were waiting for a doubleclick */
|
||||
if (this.#waitingForDoubleClick) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We'll wait for a doubleclick
|
||||
/* We'll wait for a doubleclick */
|
||||
this.#waitingForDoubleClick = true;
|
||||
|
||||
this.#doubleClickTimer = window.setTimeout(() => {
|
||||
|
||||
// Still waiting so no doubleclick; do the click action
|
||||
/* Still waiting so no doubleclick; do the click action */
|
||||
if (this.#waitingForDoubleClick) {
|
||||
if (getApp().getMap().getState() === IDLE || getApp().getMap().getState() === MOVE_UNIT || e.originalEvent.ctrlKey) {
|
||||
if (!e.originalEvent.ctrlKey)
|
||||
@ -975,17 +1021,17 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
}
|
||||
|
||||
// No longer waiting for a doubleclick
|
||||
/* No longer waiting for a doubleclick */
|
||||
this.#waitingForDoubleClick = false;
|
||||
}, 200);
|
||||
}
|
||||
|
||||
#onDoubleClick(e: any) {
|
||||
// Let single clicks work again
|
||||
/* Let single clicks work again */
|
||||
this.#waitingForDoubleClick = false;
|
||||
clearTimeout(this.#doubleClickTimer);
|
||||
|
||||
// Select all matching units in the viewport
|
||||
/* Select all matching units in the viewport */
|
||||
const unitsManager = getApp().getUnitsManager();
|
||||
Object.values(unitsManager.getUnits()).forEach((unit: Unit) => {
|
||||
if (unit.getAlive() === true && unit.getName() === this.getName() && unit.isInViewport())
|
||||
@ -1040,14 +1086,14 @@ export class Unit extends CustomMarker {
|
||||
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" },
|
||||
'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) => {
|
||||
@ -1138,6 +1184,10 @@ export class Unit extends CustomMarker {
|
||||
element.querySelector(".unit-fuel-level")?.setAttribute("style", `width: ${this.#fuel}%`);
|
||||
element.querySelector(".unit")?.toggleAttribute("data-has-low-fuel", this.#fuel < 20);
|
||||
|
||||
/* Set health data */
|
||||
element.querySelector(".unit-health-level")?.setAttribute("style", `width: ${this.#health}%`);
|
||||
element.querySelector(".unit")?.toggleAttribute("data-has-low-health", this.#health < 20);
|
||||
|
||||
/* Set dead/alive flag */
|
||||
element.querySelector(".unit")?.toggleAttribute("data-is-dead", !this.#alive);
|
||||
|
||||
@ -1148,7 +1198,7 @@ export class Unit extends CustomMarker {
|
||||
else if (!this.#controlled) { // Unit is under DCS control (not Olympus)
|
||||
element.querySelector(".unit")?.setAttribute("data-state", "dcs");
|
||||
}
|
||||
else if ((this.getCategory() == "Aircraft" || this.getCategory() == "Helicopter") && !this.#hasTask){
|
||||
else if ((this.getCategory() == "Aircraft" || this.getCategory() == "Helicopter") && !this.#hasTask) {
|
||||
element.querySelector(".unit")?.setAttribute("data-state", "no-task");
|
||||
}
|
||||
else { // Unit is under Olympus control
|
||||
@ -1221,6 +1271,14 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
}
|
||||
|
||||
#redrawMarker() {
|
||||
this.removeFrom(getApp().getMap());
|
||||
this.#updateMarker();
|
||||
|
||||
/* Activate the selection effects on the marker */
|
||||
this.getElement()?.querySelector(`.unit`)?.toggleAttribute("data-is-selected", this.getSelected());
|
||||
}
|
||||
|
||||
#drawPath() {
|
||||
if (this.#activePath != undefined && getApp().getMap().getVisibilityOptions()[SHOW_UNIT_PATHS]) {
|
||||
var points = [];
|
||||
@ -1316,13 +1374,13 @@ export class Unit extends CustomMarker {
|
||||
|
||||
/* Get the acquisition and engagement ranges of the entire group, not for each unit */
|
||||
if (this.getIsLeader()) {
|
||||
var engagementRange = this.getDatabase()?.getByName(this.getName())?.engagementRange?? 0;
|
||||
var acquisitionRange = this.getDatabase()?.getByName(this.getName())?.acquisitionRange?? 0;
|
||||
var engagementRange = this.getDatabase()?.getByName(this.getName())?.engagementRange ?? 0;
|
||||
var acquisitionRange = this.getDatabase()?.getByName(this.getName())?.acquisitionRange ?? 0;
|
||||
|
||||
this.getGroupMembers().forEach((unit: Unit) => {
|
||||
if (unit.getAlive()) {
|
||||
let unitEngagementRange = unit.getDatabase()?.getByName(unit.getName())?.engagementRange?? 0;
|
||||
let unitAcquisitionRange = unit.getDatabase()?.getByName(unit.getName())?.acquisitionRange?? 0;
|
||||
let unitEngagementRange = unit.getDatabase()?.getByName(unit.getName())?.engagementRange ?? 0;
|
||||
let unitAcquisitionRange = unit.getDatabase()?.getByName(unit.getName())?.acquisitionRange ?? 0;
|
||||
|
||||
if (unitEngagementRange > engagementRange)
|
||||
engagementRange = unitEngagementRange;
|
||||
@ -1332,13 +1390,14 @@ export class Unit extends CustomMarker {
|
||||
}
|
||||
})
|
||||
|
||||
if (acquisitionRange !== this.#acquisitionCircle.getRadius())
|
||||
this.#acquisitionCircle.setRadius(acquisitionRange);
|
||||
if (acquisitionRange !== this.#acquisitionCircle.getRadius()) {
|
||||
this.#acquisitionCircle.setRadius(acquisitionRange);
|
||||
}
|
||||
|
||||
if (engagementRange !== this.#engagementCircle.getRadius())
|
||||
this.#engagementCircle.setRadius(engagementRange);
|
||||
|
||||
this.#engagementCircle.options.fillOpacity = this.getSelected() && getApp().getMap().getVisibilityOptions()[FILL_SELECTED_RING]? 0.3: 0;
|
||||
this.#engagementCircle.options.fillOpacity = this.getSelected() && getApp().getMap().getVisibilityOptions()[FILL_SELECTED_RING] ? 0.3 : 0;
|
||||
|
||||
/* Acquisition circles */
|
||||
var shortAcquisitionRangeCheck = (acquisitionRange > nmToM(3) || !getApp().getMap().getVisibilityOptions()[HIDE_UNITS_SHORT_RANGE_RINGS]);
|
||||
@ -1358,13 +1417,14 @@ export class Unit extends CustomMarker {
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.#acquisitionCircle.setLatLng(this.getPosition());
|
||||
if (this.getPosition() != this.#acquisitionCircle.getLatLng())
|
||||
this.#acquisitionCircle.setLatLng(this.getPosition());
|
||||
}
|
||||
else {
|
||||
if (getApp().getMap().hasLayer(this.#acquisitionCircle))
|
||||
this.#acquisitionCircle.removeFrom(getApp().getMap());
|
||||
}
|
||||
|
||||
|
||||
/* Engagement circles */
|
||||
var shortEngagementRangeCheck = (engagementRange > nmToM(3) || !getApp().getMap().getVisibilityOptions()[HIDE_UNITS_SHORT_RANGE_RINGS]);
|
||||
if (getApp().getMap().getVisibilityOptions()[SHOW_UNITS_ENGAGEMENT_RINGS] && shortEngagementRangeCheck && (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC, IRST, RWR].includes(value)))) {
|
||||
@ -1382,7 +1442,8 @@ export class Unit extends CustomMarker {
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.#engagementCircle.setLatLng(this.getPosition());
|
||||
if (this.getPosition() != this.#engagementCircle.getLatLng())
|
||||
this.#engagementCircle.setLatLng(this.getPosition());
|
||||
}
|
||||
else {
|
||||
if (getApp().getMap().hasLayer(this.#engagementCircle))
|
||||
@ -1430,17 +1491,20 @@ export class Unit extends CustomMarker {
|
||||
this.#targetPositionPolyline.removeFrom(getApp().getMap());
|
||||
}
|
||||
|
||||
#onZoom() {
|
||||
#onZoom(e: any) {
|
||||
if (this.checkZoomRedraw())
|
||||
this.#redrawMarker();
|
||||
this.#updateMarker();
|
||||
}
|
||||
}
|
||||
|
||||
export class AirUnit extends Unit {
|
||||
export abstract class AirUnit extends Unit {
|
||||
getIconOptions() {
|
||||
var belongsToCommandedCoalition = this.belongsToCommandedCoalition();
|
||||
return {
|
||||
showState: belongsToCommandedCoalition,
|
||||
showVvi: (belongsToCommandedCoalition || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))),
|
||||
showHealth: false,
|
||||
showHotgroup: belongsToCommandedCoalition,
|
||||
showUnitIcon: (belongsToCommandedCoalition || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))),
|
||||
showShortLabel: (belongsToCommandedCoalition || this.getDetectionMethods().some(value => [VISUAL, OPTIC].includes(value))),
|
||||
@ -1514,9 +1578,10 @@ export class GroundUnit extends Unit {
|
||||
return {
|
||||
showState: belongsToCommandedCoalition,
|
||||
showVvi: false,
|
||||
showHealth: true,
|
||||
showHotgroup: belongsToCommandedCoalition,
|
||||
showUnitIcon: (belongsToCommandedCoalition || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))),
|
||||
showShortLabel: false,
|
||||
showShortLabel: this.getDatabaseEntry()?.type === "SAM Site",
|
||||
showFuel: false,
|
||||
showAmmo: false,
|
||||
showSummary: false,
|
||||
@ -1566,6 +1631,31 @@ export class GroundUnit extends Unit {
|
||||
var blueprint = groundUnitDatabase.getByName(this.getName());
|
||||
return blueprint?.type ? blueprint.type : "";
|
||||
}
|
||||
|
||||
/* When a unit is a leader of a group, the map is zoomed out and grouping when zoomed out is enabled, check if the unit should be shown as a specific group. This is used to show a SAM battery instead of the group leader */
|
||||
getDatabaseEntry() {
|
||||
let unitWhenGrouped = null;
|
||||
if (!this.getSelected() && this.getIsLeader() && getApp().getMap().getVisibilityOptions()[HIDE_GROUP_MEMBERS] && getApp().getMap().getZoom() < GROUPING_ZOOM_TRANSITION) {
|
||||
unitWhenGrouped = this.getDatabase()?.getByName(this.getName())?.unitWhenGrouped ?? null;
|
||||
let member = this.getGroupMembers().reduce((prev: Unit | null, unit: Unit, index: number) => {
|
||||
if (unit.getDatabaseEntry()?.unitWhenGrouped != undefined)
|
||||
return unit
|
||||
return prev;
|
||||
}, null);
|
||||
unitWhenGrouped = (member !== null ? member?.getDatabaseEntry()?.unitWhenGrouped : unitWhenGrouped);
|
||||
}
|
||||
if (unitWhenGrouped)
|
||||
return this.getDatabase()?.getByName(unitWhenGrouped);
|
||||
else
|
||||
return this.getDatabase()?.getByName(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 */
|
||||
checkZoomRedraw(): boolean {
|
||||
return (this.getIsLeader() && getApp().getMap().getVisibilityOptions()[HIDE_GROUP_MEMBERS] &&
|
||||
(getApp().getMap().getZoom() >= GROUPING_ZOOM_TRANSITION && getApp().getMap().getPreviousZoom() < GROUPING_ZOOM_TRANSITION ||
|
||||
getApp().getMap().getZoom() < GROUPING_ZOOM_TRANSITION && getApp().getMap().getPreviousZoom() >= GROUPING_ZOOM_TRANSITION))
|
||||
}
|
||||
}
|
||||
|
||||
export class NavyUnit extends Unit {
|
||||
@ -1578,6 +1668,7 @@ export class NavyUnit extends Unit {
|
||||
return {
|
||||
showState: belongsToCommandedCoalition,
|
||||
showVvi: false,
|
||||
showHealth: true,
|
||||
showHotgroup: true,
|
||||
showUnitIcon: (belongsToCommandedCoalition || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))),
|
||||
showShortLabel: false,
|
||||
|
||||
@ -15,6 +15,7 @@ import { Popup } from "../popups/popup";
|
||||
import { HotgroupPanel } from "../panels/hotgrouppanel";
|
||||
import { Contact, UnitData, UnitSpawnTable } from "../interfaces";
|
||||
import { Dialog } from "../dialog/dialog";
|
||||
import { Group } from "./group";
|
||||
import { UnitDataFileExport } from "./unitdatafileexport";
|
||||
import { UnitDataFileImport } from "./unitdatafileimport";
|
||||
|
||||
@ -27,8 +28,9 @@ export class UnitsManager {
|
||||
#deselectionEventDisabled: boolean = false;
|
||||
#requestDetectionUpdate: boolean = false;
|
||||
#selectionEventDisabled: boolean = false;
|
||||
#slowDeleteDialog!:Dialog;
|
||||
#slowDeleteDialog!: Dialog;
|
||||
#units: { [ID: number]: Unit };
|
||||
#groups: { [groupName: string]: Group } = {};
|
||||
#unitDataExport!:UnitDataFileExport;
|
||||
#unitDataImport!:UnitDataFileImport;
|
||||
|
||||
@ -40,7 +42,7 @@ export class UnitsManager {
|
||||
document.addEventListener('contactsUpdated', (e: CustomEvent) => { this.#requestDetectionUpdate = true });
|
||||
document.addEventListener('copy', () => this.selectedUnitsCopy());
|
||||
document.addEventListener('deleteSelectedUnits', () => this.selectedUnitsDelete());
|
||||
document.addEventListener('explodeSelectedUnits', () => this.selectedUnitsDelete(true));
|
||||
document.addEventListener('explodeSelectedUnits', (e: any) => this.selectedUnitsDelete(true, e.detail.type));
|
||||
document.addEventListener('exportToFile', () => this.exportToFile());
|
||||
document.addEventListener('importFromFile', () => this.importFromFile());
|
||||
document.addEventListener('keyup', (event) => this.#onKeyUp(event));
|
||||
@ -49,10 +51,9 @@ export class UnitsManager {
|
||||
document.addEventListener('selectedUnitsChangeSpeed', (e: any) => { this.selectedUnitsChangeSpeed(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() });
|
||||
|
||||
document.addEventListener("toggleMarkerProtection", (ev:CustomEventInit) => { this.#showNumberOfSelectedProtectedUnits() });
|
||||
|
||||
this.#slowDeleteDialog = new Dialog( "slow-delete-dialog" );
|
||||
this.#slowDeleteDialog = new Dialog("slow-delete-dialog");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -133,6 +134,22 @@ export class UnitsManager {
|
||||
this.#units[ID]?.setData(dataExtractor);
|
||||
}
|
||||
|
||||
/* Update the unit groups */
|
||||
for (let ID in this.#units) {
|
||||
const unit = this.#units[ID];
|
||||
const groupName = unit.getGroupName();
|
||||
|
||||
if (groupName !== "") {
|
||||
/* If the group does not yet exist, create it */
|
||||
if (!(groupName in this.#groups))
|
||||
this.#groups[groupName] = new Group(groupName);
|
||||
|
||||
/* If the unit was not assigned to a group yet, assign it */
|
||||
if (unit.getGroup() === null)
|
||||
this.#groups[groupName].addMember(unit);
|
||||
}
|
||||
}
|
||||
|
||||
/* If we are not in Game Master mode, visibility of units by the user is determined by the detections of the units themselves. This is performed here.
|
||||
This operation is computationally expensive, therefore it is only performed when #requestDetectionUpdate is true. This happens whenever a change in the detectionUpdates is detected
|
||||
*/
|
||||
@ -212,9 +229,9 @@ export class UnitsManager {
|
||||
*
|
||||
* @param hotgroup The hotgroup number
|
||||
*/
|
||||
selectUnitsByHotgroup(hotgroup: number, deselectAllUnits: boolean = true ) {
|
||||
selectUnitsByHotgroup(hotgroup: number, deselectAllUnits: boolean = true) {
|
||||
|
||||
if ( deselectAllUnits ) {
|
||||
if (deselectAllUnits) {
|
||||
this.deselectAllUnits();
|
||||
}
|
||||
|
||||
@ -226,8 +243,8 @@ export class UnitsManager {
|
||||
* @param options Selection options
|
||||
* @returns Array of selected units
|
||||
*/
|
||||
getSelectedUnits(options?: { excludeHumans?: boolean, excludeProtected?:boolean, onlyOnePerGroup?: boolean, showProtectionReminder?:boolean }) {
|
||||
let selectedUnits:Unit[] = [];
|
||||
getSelectedUnits(options?: { excludeHumans?: boolean, excludeProtected?: boolean, onlyOnePerGroup?: boolean, showProtectionReminder?: boolean }) {
|
||||
let selectedUnits: Unit[] = [];
|
||||
let numProtectedUnits = 0;
|
||||
for (const [ID, unit] of Object.entries(this.#units)) {
|
||||
if (unit.getSelected()) {
|
||||
@ -536,7 +553,7 @@ export class UnitsManager {
|
||||
* @param operateAsBool If true, units will operate as blue
|
||||
*/
|
||||
selectedUnitsSetOperateAs(operateAsBool: boolean) {
|
||||
var operateAs = operateAsBool? "blue": "red";
|
||||
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);
|
||||
@ -588,7 +605,7 @@ export class UnitsManager {
|
||||
|
||||
var selectedUnits = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if ( selectedUnits.length === 0)
|
||||
if (selectedUnits.length === 0)
|
||||
return;
|
||||
|
||||
var count = 1;
|
||||
@ -675,7 +692,7 @@ export class UnitsManager {
|
||||
});
|
||||
this.#showActionMessage(selectedUnits, `unit simulating fire fight`);
|
||||
}
|
||||
|
||||
|
||||
/** Instruct units to enter into scenic AAA mode. Units will shoot in the air without aiming
|
||||
*
|
||||
*/
|
||||
@ -764,7 +781,7 @@ export class UnitsManager {
|
||||
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 */);
|
||||
getApp().getServerManager().cloneUnits(units, true, 0 /* No spawn points, we delete the original units */);
|
||||
} else {
|
||||
(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`Groups can only be created from units of the same category`);
|
||||
}
|
||||
@ -797,8 +814,8 @@ export class UnitsManager {
|
||||
* @param explosion If true, the unit will be deleted using an explosion
|
||||
* @returns
|
||||
*/
|
||||
selectedUnitsDelete(explosion: boolean = false) {
|
||||
var selectedUnits = this.getSelectedUnits({excludeProtected:true}); /* Can be applied to humans too */
|
||||
selectedUnitsDelete(explosion: boolean = false, explosionType: string = "") {
|
||||
var selectedUnits = this.getSelectedUnits({ excludeProtected: true }); /* Can be applied to humans too */
|
||||
const selectionContainsAHuman = selectedUnits.some((unit: Unit) => {
|
||||
return unit.getHuman() === true;
|
||||
});
|
||||
@ -807,22 +824,22 @@ export class UnitsManager {
|
||||
return;
|
||||
}
|
||||
|
||||
const doDelete = (explosion = false, immediate = false) => {
|
||||
const doDelete = (explosion = false, explosionType = "", immediate = false) => {
|
||||
for (let idx in selectedUnits) {
|
||||
selectedUnits[idx].delete(explosion, immediate);
|
||||
selectedUnits[idx].delete(explosion, explosionType, immediate);
|
||||
}
|
||||
this.#showActionMessage(selectedUnits, `deleted`);
|
||||
}
|
||||
|
||||
if (selectedUnits.length >= DELETE_SLOW_THRESHOLD)
|
||||
this.#showSlowDeleteDialog(selectedUnits).then((action:any) => {
|
||||
this.#showSlowDeleteDialog(selectedUnits).then((action: any) => {
|
||||
if (action === "delete-slow")
|
||||
doDelete(explosion, false);
|
||||
doDelete(explosion, explosionType, false);
|
||||
else if (action === "delete-immediate")
|
||||
doDelete(explosion, true);
|
||||
doDelete(explosion, explosionType, true);
|
||||
})
|
||||
else
|
||||
doDelete(explosion);
|
||||
doDelete(explosion, explosionType);
|
||||
|
||||
}
|
||||
|
||||
@ -871,7 +888,7 @@ export class UnitsManager {
|
||||
this.#copiedUnits = JSON.parse(JSON.stringify(this.getSelectedUnits().map((unit: Unit) => { return unit.getData() }))); /* Can be applied to humans too */
|
||||
(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`${this.#copiedUnits.length} units copied`);
|
||||
}
|
||||
|
||||
|
||||
/*********************** Unit manipulation functions ************************/
|
||||
/** Paste the copied units
|
||||
*
|
||||
@ -892,7 +909,7 @@ export class UnitsManager {
|
||||
if (unitSpawnPoints !== undefined)
|
||||
spawnPoints += unitSpawnPoints;
|
||||
})
|
||||
|
||||
|
||||
if (spawnPoints > getApp().getMissionManager().getAvailableSpawnPoints()) {
|
||||
(getApp().getPopupsManager().get("infoPopup") as Popup).setText("Not enough spawn points available!");
|
||||
return false;
|
||||
@ -927,7 +944,7 @@ export class UnitsManager {
|
||||
markers.push(getApp().getMap().addTemporaryMarker(position, unit.name, unit.coalition));
|
||||
units.push({ ID: unit.ID, location: position });
|
||||
});
|
||||
|
||||
|
||||
getApp().getServerManager().cloneUnits(units, false, spawnPoints, (res: any) => {
|
||||
if (res.commandHash !== undefined) {
|
||||
markers.forEach((marker: TemporaryUnitMarker) => {
|
||||
@ -975,7 +992,7 @@ export class UnitsManager {
|
||||
if (Math.random() < IADSDensities[type]) {
|
||||
/* Get a random blueprint depending on the selected parameters and spawn the unit */
|
||||
const unitBlueprint = randomUnitBlueprint(groundUnitDatabase, { type: type, eras: activeEras, ranges: activeRanges });
|
||||
if (unitBlueprint)
|
||||
if (unitBlueprint)
|
||||
this.spawnUnits("GroundUnit", [{ unitType: unitBlueprint.name, location: latlng, liveryID: "" }], coalitionArea.getCoalition(), true);
|
||||
}
|
||||
}
|
||||
@ -1013,24 +1030,24 @@ export class UnitsManager {
|
||||
* @param callback CallableFunction called when the command is received by the server
|
||||
* @returns True if the spawn command was successfully sent
|
||||
*/
|
||||
spawnUnits(category: string, units: UnitSpawnTable[], coalition: string = "blue", immediate: boolean = true, airbase: string = "", country: string = "", callback: CallableFunction = () => {}) {
|
||||
spawnUnits(category: string, units: UnitSpawnTable[], coalition: string = "blue", immediate: boolean = true, airbase: string = "", country: string = "", callback: CallableFunction = () => { }) {
|
||||
var spawnPoints = 0;
|
||||
var spawnFunction = () => {};
|
||||
var spawnFunction = () => { };
|
||||
var spawnsRestricted = getApp().getMissionManager().getCommandModeOptions().restrictSpawns && getApp().getMissionManager().getRemainingSetupTime() < 0 && getApp().getMissionManager().getCommandModeOptions().commandMode !== GAME_MASTER;
|
||||
|
||||
|
||||
if (category === "Aircraft") {
|
||||
if (airbase == "" && spawnsRestricted) {
|
||||
(getApp().getPopupsManager().get("infoPopup") as Popup).setText("Aircrafts can be air spawned during the SETUP phase only");
|
||||
return false;
|
||||
}
|
||||
spawnPoints = units.reduce((points: number, unit: UnitSpawnTable) => {return points + aircraftDatabase.getSpawnPointsByName(unit.unitType)}, 0);
|
||||
spawnPoints = units.reduce((points: number, unit: UnitSpawnTable) => { return points + aircraftDatabase.getSpawnPointsByName(unit.unitType) }, 0);
|
||||
spawnFunction = () => getApp().getServerManager().spawnAircrafts(units, coalition, airbase, country, immediate, spawnPoints, callback);
|
||||
} else if (category === "Helicopter") {
|
||||
if (airbase == "" && spawnsRestricted) {
|
||||
(getApp().getPopupsManager().get("infoPopup") as Popup).setText("Helicopters can be air spawned during the SETUP phase only");
|
||||
return false;
|
||||
}
|
||||
spawnPoints = units.reduce((points: number, unit: UnitSpawnTable) => {return points + helicopterDatabase.getSpawnPointsByName(unit.unitType)}, 0);
|
||||
spawnPoints = units.reduce((points: number, unit: UnitSpawnTable) => { return points + helicopterDatabase.getSpawnPointsByName(unit.unitType) }, 0);
|
||||
spawnFunction = () => getApp().getServerManager().spawnHelicopters(units, coalition, airbase, country, immediate, spawnPoints, callback);
|
||||
|
||||
} else if (category === "GroundUnit") {
|
||||
@ -1038,7 +1055,7 @@ export class UnitsManager {
|
||||
(getApp().getPopupsManager().get("infoPopup") as Popup).setText("Ground units can be spawned during the SETUP phase only");
|
||||
return false;
|
||||
}
|
||||
spawnPoints = units.reduce((points: number, unit: UnitSpawnTable) => {return points + groundUnitDatabase.getSpawnPointsByName(unit.unitType)}, 0);
|
||||
spawnPoints = units.reduce((points: number, unit: UnitSpawnTable) => { return points + groundUnitDatabase.getSpawnPointsByName(unit.unitType) }, 0);
|
||||
spawnFunction = () => getApp().getServerManager().spawnGroundUnits(units, coalition, country, immediate, spawnPoints, callback);
|
||||
|
||||
} else if (category === "NavyUnit") {
|
||||
@ -1046,7 +1063,7 @@ export class UnitsManager {
|
||||
(getApp().getPopupsManager().get("infoPopup") as Popup).setText("Navy units can be spawned during the SETUP phase only");
|
||||
return false;
|
||||
}
|
||||
spawnPoints = units.reduce((points: number, unit: UnitSpawnTable) => {return points + navyUnitDatabase.getSpawnPointsByName(unit.unitType)}, 0);
|
||||
spawnPoints = units.reduce((points: number, unit: UnitSpawnTable) => { return points + navyUnitDatabase.getSpawnPointsByName(unit.unitType) }, 0);
|
||||
spawnFunction = () => getApp().getServerManager().spawnNavyUnits(units, coalition, country, immediate, spawnPoints, callback);
|
||||
}
|
||||
|
||||
@ -1112,30 +1129,30 @@ export class UnitsManager {
|
||||
else if (units.length > 1)
|
||||
(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`${units[0].getUnitName()} and ${units.length - 1} other units ${message}`);
|
||||
}
|
||||
|
||||
#showSlowDeleteDialog(selectedUnits:Unit[]) {
|
||||
let button:HTMLButtonElement | null = null;
|
||||
const deletionTime = Math.round( selectedUnits.length * DELETE_CYCLE_TIME ).toString();
|
||||
|
||||
#showSlowDeleteDialog(selectedUnits: Unit[]) {
|
||||
let button: HTMLButtonElement | null = null;
|
||||
const deletionTime = Math.round(selectedUnits.length * DELETE_CYCLE_TIME).toString();
|
||||
const dialog = this.#slowDeleteDialog;
|
||||
const element = dialog.getElement();
|
||||
const listener = (ev:MouseEvent) => {
|
||||
const listener = (ev: MouseEvent) => {
|
||||
if (ev.target instanceof HTMLButtonElement && ev.target.matches("[data-action]"))
|
||||
button = ev.target;
|
||||
}
|
||||
|
||||
element.querySelectorAll(".deletion-count").forEach( el => el.innerHTML = selectedUnits.length.toString() );
|
||||
element.querySelectorAll(".deletion-time").forEach( el => el.innerHTML = deletionTime );
|
||||
element.querySelectorAll(".deletion-count").forEach(el => el.innerHTML = selectedUnits.length.toString());
|
||||
element.querySelectorAll(".deletion-time").forEach(el => el.innerHTML = deletionTime);
|
||||
dialog.show();
|
||||
|
||||
return new Promise((resolve) => {
|
||||
element.addEventListener("click", listener);
|
||||
|
||||
const interval = setInterval(() => {
|
||||
if (button instanceof HTMLButtonElement ) {
|
||||
if (button instanceof HTMLButtonElement) {
|
||||
clearInterval(interval);
|
||||
dialog.hide();
|
||||
element.removeEventListener("click", listener);
|
||||
resolve( button.getAttribute("data-action") );
|
||||
resolve(button.getAttribute("data-action"));
|
||||
}
|
||||
}, 250);
|
||||
});
|
||||
@ -1143,18 +1160,18 @@ 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 selectedUnits = this.getSelectedUnits();
|
||||
const numSelectedUnits = selectedUnits.length;
|
||||
const numProtectedUnits = selectedUnits.filter((unit: Unit) => map.unitIsProtected(unit)).length;
|
||||
|
||||
if (numProtectedUnits === 1 && numSelectedUnits === numProtectedUnits)
|
||||
(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`Notice: unit is protected`);
|
||||
|
||||
|
||||
if (numProtectedUnits > 1)
|
||||
(getApp().getPopupsManager().get("infoPopup") as Popup).setText(`Notice: selection contains ${numProtectedUnits} protected units.`);
|
||||
}
|
||||
|
||||
#unitIsProtected(unit:Unit) {
|
||||
#unitIsProtected(unit: Unit) {
|
||||
return getApp().getMap().unitIsProtected(unit)
|
||||
}
|
||||
}
|
||||
@ -100,6 +100,7 @@ export class Weapon extends CustomMarker {
|
||||
return {
|
||||
showState: false,
|
||||
showVvi: false,
|
||||
showHealth: false,
|
||||
showHotgroup: false,
|
||||
showUnitIcon: true,
|
||||
showShortLabel: false,
|
||||
@ -276,6 +277,7 @@ export class Missile extends Weapon {
|
||||
return {
|
||||
showState: false,
|
||||
showVvi: (!this.belongsToCommandedCoalition() && !this.getDetectionMethods().some(value => [VISUAL, OPTIC].includes(value)) && this.getDetectionMethods().some(value => [RADAR, IRST, DLINK].includes(value))),
|
||||
showHealth: false,
|
||||
showHotgroup: false,
|
||||
showUnitIcon: (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))),
|
||||
showShortLabel: false,
|
||||
@ -308,6 +310,7 @@ export class Bomb extends Weapon {
|
||||
return {
|
||||
showState: false,
|
||||
showVvi: (!this.belongsToCommandedCoalition() && !this.getDetectionMethods().some(value => [VISUAL, OPTIC].includes(value)) && this.getDetectionMethods().some(value => [RADAR, IRST, DLINK].includes(value))),
|
||||
showHealth: false,
|
||||
showHotgroup: false,
|
||||
showUnitIcon: (this.belongsToCommandedCoalition() || this.getDetectionMethods().some(value => [VISUAL, OPTIC, RADAR, IRST, DLINK].includes(value))),
|
||||
showShortLabel: false,
|
||||
|
||||
@ -49,9 +49,12 @@
|
||||
<button class="smoke-button" title="" data-smoke-color="orange" data-on-click="contextMenuDeploySmoke" data-on-click-params='{ "color": "orange" }'>Orange smoke</button>
|
||||
</div>
|
||||
<div id="explosion-menu" class="ol-panel ol-contexmenu-panel hide">
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "strength": 1 }'>Small explosion</button>
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "strength": 2 }'>Medium explosion</button>
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "strength": 3 }'>Big explosion</button>
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "strength": 4 }'>Huge explosion</button>
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "explosionType": "normal", "strength": 1 }'>Small explosion</button>
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "explosionType": "normal", "strength": 10 }'>Big explosion</button>
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "explosionType": "phosphorous"}'>White phosphorous</button>
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "explosionType": "napalm"}'>Napalm</button>
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "explosionType": "secondary"}'>Explosion with secondaries</button>
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "explosionType": "fire"}'>Static fire</button>
|
||||
<button class="explosion-button" title="" data-on-click="contextMenuExplosion" data-on-click-params='{ "explosionType": "depthCharge"}'>Depth charge</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -1,113 +1,128 @@
|
||||
<div id="unit-control-panel" class="ol-panel ol-panel-padding-lg" oncontextmenu="return false;">
|
||||
<div id="unit-control-panel" oncontextmenu="return false;">
|
||||
<div class="ol-panel"><img src="resources/theme/images/icons/gamepad-solid.svg" inject-svg></div>
|
||||
|
||||
<div class="ol-panel ol-panel-padding-lg" id="unit-control-panel-content">
|
||||
<h3>Selected Units</h3>
|
||||
|
||||
<h3>Selected Units</h3>
|
||||
<div id="unit-selection">
|
||||
|
||||
<div id="unit-selection">
|
||||
<div id="selected-units-container" class="ol-scrollable">
|
||||
<!-- This is where all the unit selection buttons will be shown-->
|
||||
<!-- <button class="pill highlight-coalition" data-coalition="blue" data-label="18">Olympus 1-1</button> -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="selected-units-container" class="ol-scrollable">
|
||||
<!-- This is where all the unit selection buttons will be shown-->
|
||||
<!-- <button class="pill highlight-coalition" data-coalition="blue" data-label="18">Olympus 1-1</button> -->
|
||||
<hr />
|
||||
|
||||
<div id="unit-controls" class="ol-scrollable">
|
||||
<div id="flight-data">
|
||||
<h4>Controls</h4>
|
||||
<div id="speed-slider" class="ol-slider-container flight-control-ol-slider">
|
||||
<dl class="ol-data-grid">
|
||||
<dt>Speed</dt>
|
||||
<dd>
|
||||
<div class="ol-slider-value"></div>
|
||||
<div id="speed-type-switch" class="ol-switch"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
<input type="range" min="0" max="100" value="0" class="ol-slider">
|
||||
<div class="ol-slider-min-max"></div>
|
||||
</div>
|
||||
<div id="altitude-slider" class="ol-slider-container flight-control-ol-slider">
|
||||
<dl class="ol-data-grid">
|
||||
<dt> Altitude
|
||||
</dt>
|
||||
<dd>
|
||||
<div class="ol-slider-value"></div>
|
||||
<div id="altitude-type-switch" class="ol-switch"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
<input type="range" min="0" max="100" value="0" class="ol-slider">
|
||||
<div class="ol-slider-min-max"></div>
|
||||
</div>
|
||||
<h5 id="categories-tooltip">Multiple categories selected</h5>
|
||||
</div>
|
||||
|
||||
<div id="roe">
|
||||
<h4>Rules of engagement</h4>
|
||||
<div id="roe-buttons-container" class="ol-group ol-button-box ol-option-button">
|
||||
<!-- This is where the roe buttons will be shown -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="threat">
|
||||
<h4>Reaction to threat</h4>
|
||||
<div id="reaction-to-threat-buttons-container" class="ol-group ol-button-box ol-option-button">
|
||||
<!-- This is where the reaction to threat buttons will be shown -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="emissions-countermeasures">
|
||||
<h4>Radar & ECM</h4>
|
||||
<div id="emissions-countermeasures-buttons-container" class="ol-group ol-button-box ol-option-button">
|
||||
<!-- This is where the emissions/countermeasures buttons will be shown -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="shots-scatter">
|
||||
<h4>Shots scatter</h4>
|
||||
<div id="shots-scatter-buttons-container" class="ol-group ol-button-box ol-option-button">
|
||||
<!-- This is where the shots scatter buttons will be shown -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="shots-intensity">
|
||||
<h4>Shots intensity</h4>
|
||||
<div id="shots-intensity-buttons-container" class="ol-group ol-button-box ol-option-button">
|
||||
<!-- This is where the shots intensity buttons will be shown -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="tanker-on" class="switch-control">
|
||||
<h4>Enable tanker <img src="/resources/theme/images/icons/circle-question-regular.svg" title="Instructs the unit to operate as AAR tanker. A/A TACAN, radio frequency and callsign set in Settings dialog."></h4>
|
||||
<div id="tanker-on-switch" class="ol-switch"></div>
|
||||
</div>
|
||||
|
||||
<div id="AWACS-on" class="switch-control">
|
||||
<h4>Airborne Early Warning <img src="/resources/theme/images/icons/circle-question-regular.svg" title="Enables datalink and AI radio calls. Radio frequency and callsign set in Settings dialog."></h4>
|
||||
<div id="AWACS-on-switch" class="ol-switch"></div>
|
||||
</div>
|
||||
|
||||
<div id="operate-as" class="switch-control">
|
||||
<h4>Operate as <img src="/resources/theme/images/icons/circle-question-regular.svg" title="Determines if the unit will target red or blue units when performing scenic tasks."></h4>
|
||||
<div id="operate-as-switch" class="ol-switch"></div>
|
||||
</div>
|
||||
|
||||
<div id="ai-on-off" class="switch-control">
|
||||
<h4>Unit active <img src="/resources/theme/images/icons/circle-question-regular.svg" title="Toggling this disables unit AI completely. It will no longer move, react or emit radio waves."></h4>
|
||||
<div id="on-off-switch" class="ol-switch" title=""></div>
|
||||
</div>
|
||||
|
||||
<div id="follow-roads" class="switch-control">
|
||||
<h4>Follow roads <img src="/resources/theme/images/icons/circle-question-regular.svg" title=""></h4>
|
||||
<div id="follow-roads-switch" class="ol-switch"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div id="advanced-settings-div">
|
||||
<button id="advanced-settings-button" class="ol-button-settings" data-on-click="showAdvancedSettings">Settings</button>
|
||||
<div id="delete-options" class="ol-select">
|
||||
<div class="ol-select-value ol-select-warning">
|
||||
Delete unit
|
||||
</div>
|
||||
<div class="ol-select-options">
|
||||
<div><button class="ol-button-white" data-on-click="deleteSelectedUnits" title="Immediately remove the unit from the simulation"><img src="/resources/theme/images/icons/trash-can-regular.svg" inject-svg>Delete</button></div>
|
||||
<div><hr></div>
|
||||
<div><button class="ol-button-warning" data-on-click="explodeSelectedUnits" data-on-click-params='{ "type": "normal" }' title="Normal explosion"><img src="/resources/theme/images/icons/explosion-solid.svg" inject-svg>Blow up</button></div>
|
||||
<div><button class="ol-button-warning" data-on-click="explodeSelectedUnits" data-on-click-params='{ "type": "secondary" }' title="The unit will keep exploding at random intervals, simulating ammunition cooking"><img src="/resources/theme/images/icons/burst-solid.svg" inject-svg>Cook off</button></div>
|
||||
<div><button class="ol-button-warning" data-on-click="explodeSelectedUnits" data-on-click-params='{ "type": "phosphorous" }' title="White phosphorous explosion"><img src="/resources/theme/images/icons/smog-solid.svg" inject-svg>Phosp.</button></div>
|
||||
<div><button class="ol-button-warning" data-on-click="explodeSelectedUnits" data-on-click-params='{ "type": "napalm" }' title="Napalm"><img src="/resources/theme/images/icons/fire-solid.svg" inject-svg>Napalm</button></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div id="flight-data">
|
||||
<h4>Controls</h4>
|
||||
<div id="speed-slider" class="ol-slider-container flight-control-ol-slider">
|
||||
<dl class="ol-data-grid">
|
||||
<dt>Speed</dt>
|
||||
<dd>
|
||||
<div class="ol-slider-value"></div>
|
||||
<div id="speed-type-switch" class="ol-switch"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
<input type="range" min="0" max="100" value="0" class="ol-slider">
|
||||
<div class="ol-slider-min-max"></div>
|
||||
</div>
|
||||
<div id="altitude-slider" class="ol-slider-container flight-control-ol-slider">
|
||||
<dl class="ol-data-grid">
|
||||
<dt> Altitude
|
||||
</dt>
|
||||
<dd>
|
||||
<div class="ol-slider-value"></div>
|
||||
<div id="altitude-type-switch" class="ol-switch"></div>
|
||||
</dd>
|
||||
</dl>
|
||||
<input type="range" min="0" max="100" value="0" class="ol-slider">
|
||||
<div class="ol-slider-min-max"></div>
|
||||
</div>
|
||||
<h5 id="categories-tooltip">Multiple categories selected</h5>
|
||||
</div>
|
||||
|
||||
<div id="roe">
|
||||
<h4>Rules of engagement</h4>
|
||||
<div id="roe-buttons-container" class="ol-group ol-button-box ol-option-button">
|
||||
<!-- This is where the roe buttons will be shown -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="threat">
|
||||
<h4>Reaction to threat</h4>
|
||||
<div id="reaction-to-threat-buttons-container" class="ol-group ol-button-box ol-option-button">
|
||||
<!-- This is where the reaction to threat buttons will be shown -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="emissions-countermeasures">
|
||||
<h4>Radar & ECM</h4>
|
||||
<div id="emissions-countermeasures-buttons-container" class="ol-group ol-button-box ol-option-button">
|
||||
<!-- This is where the emissions/countermeasures buttons will be shown -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="shots-scatter">
|
||||
<h4>Shots scatter</h4>
|
||||
<div id="shots-scatter-buttons-container" class="ol-group ol-button-box ol-option-button">
|
||||
<!-- This is where the shots scatter buttons will be shown -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="shots-intensity">
|
||||
<h4>Shots intensity</h4>
|
||||
<div id="shots-intensity-buttons-container" class="ol-group ol-button-box ol-option-button">
|
||||
<!-- This is where the shots intensity buttons will be shown -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="tanker-on" class="switch-control">
|
||||
<h4>Enable tanker <img src="/resources/theme/images/icons/circle-question-regular.svg" title="Instructs the unit to operate as AAR tanker. A/A TACAN, radio frequency and callsign set in Settings dialog."></h4>
|
||||
<div id="tanker-on-switch" class="ol-switch"></div>
|
||||
</div>
|
||||
|
||||
<div id="AWACS-on" class="switch-control">
|
||||
<h4>Airborne Early Warning <img src="/resources/theme/images/icons/circle-question-regular.svg" title="Enables datalink and AI radio calls. Radio frequency and callsign set in Settings dialog."></h4>
|
||||
<div id="AWACS-on-switch" class="ol-switch"></div>
|
||||
</div>
|
||||
|
||||
<div id="operate-as" class="switch-control">
|
||||
<h4>Operate as <img src="/resources/theme/images/icons/circle-question-regular.svg" title="Determines if the unit will target red or blue units when performing scenic tasks."></h4>
|
||||
<div id="operate-as-switch" class="ol-switch"></div>
|
||||
</div>
|
||||
|
||||
<div id="ai-on-off" class="switch-control">
|
||||
<h4>Unit active <img src="/resources/theme/images/icons/circle-question-regular.svg" title="Toggling this disables unit AI completely. It will no longer move, react or emit radio waves."></h4>
|
||||
<div id="on-off-switch" class="ol-switch" title=""></div>
|
||||
</div>
|
||||
|
||||
<div id="follow-roads" class="switch-control">
|
||||
<h4>Follow roads <img src="/resources/theme/images/icons/circle-question-regular.svg" title=""></h4>
|
||||
<div id="follow-roads-switch" class="ol-switch"></div>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div id="advanced-settings-div">
|
||||
<button id="advanced-settings-button" class="ol-button-settings" data-on-click="showAdvancedSettings">Settings</button>
|
||||
<button class="ol-button-warning" data-on-click="deleteSelectedUnits"><img src="/resources/theme/images/icons/trash-can-regular.svg" inject-svg>Delete</button>
|
||||
<button class="ol-button-warning" data-on-click="explodeSelectedUnits"><img src="/resources/theme/images/icons/explosion-solid.svg" inject-svg></button>
|
||||
</div>
|
||||
|
||||
<div id="rapid-controls" class="ol-panel">
|
||||
<button id="climb" title="Increase units altitude" class="ol-button" data-on-click="selectedUnitsChangeAltitude" data-on-click-params='{ "type": "climb" }'><img src="/resources/theme/images/icons/climb.svg" inject-svg></button>
|
||||
<button id="descend" title="Descrease units altitude" class="ol-button" data-on-click="selectedUnitsChangeAltitude" data-on-click-params='{ "type": "descend" }'><img src="/resources/theme/images/icons/descent.svg" inject-svg></button>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
<div id="unit-info-panel" class="ol-panel" oncontextmenu="return false;">
|
||||
<img src="resources/theme/images/icons/circle-info-solid.svg" inject-svg>
|
||||
|
||||
<div id="general" class="panel-section">
|
||||
<h3 id="unit-name"></h3>
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
<nav id="command-mode-toolbar" class="ol-panel hide" oncontextmenu="return false;">
|
||||
<img src="resources/theme/images/icons/person-military-pointing-solid.svg" inject-svg>
|
||||
<span id="command-mode"></span>
|
||||
<div id="spawn-points-container">Spawn points<span id="spawn-points"></span></div>
|
||||
<span id="command-mode-phase"></span>
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
<div class="ol-select-options">
|
||||
<div id="toolbar-summary">
|
||||
<h3>DCS Olympus</h3>
|
||||
<div class="accent-green app-version-number">version v0.4.5-alpha</div>
|
||||
<div class="accent-green app-version-number">version v0.4.6-alpha</div>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://www.discord.com" target="_blank">Discord</a>
|
||||
@ -35,13 +35,15 @@
|
||||
</div>
|
||||
|
||||
<div id="map-visibility-options" class="ol-select">
|
||||
<div class="ol-select-value"><img src="resources/theme/images/icons/eye-solid.svg" inject-svg>Options</div>
|
||||
<div class="ol-select-value"><img src="/resources/theme/images/icons/gears-solid.svg" inject-svg>Options</div>
|
||||
<div class="ol-select-options">
|
||||
<!-- This is where the advanced visibility options will be listed -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</nav>
|
||||
<nav class="ol-panel" oncontextmenu="return false;">
|
||||
<img src="resources/theme/images/icons/eye-solid.svg" inject-svg>
|
||||
<div id="unit-visibility-control" class="ol-group ol-navbar-buttons-group">
|
||||
<!-- Here the available visibility controls will be listed -->
|
||||
</div>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
#define nwjsFolder "C:\Users\dpass\Documents\nwjs\"
|
||||
#define version "v0.4.5-alpha"
|
||||
#define version "v0.4.6-alpha"
|
||||
|
||||
[Setup]
|
||||
AppName=DCS Olympus
|
||||
@ -9,6 +9,14 @@ DefaultGroupName=DCSOlympus
|
||||
OutputBaseFilename=DCSOlympus_{#version}
|
||||
UninstallFilesDir={app}\Mods\Services\Olympus
|
||||
SetupIconFile="..\img\olympus.ico"
|
||||
DirExistsWarning=no
|
||||
AppendDefaultDirName=no
|
||||
|
||||
[Messages]
|
||||
WizardSelectDir=Select the location of DCS's Saved Games folder
|
||||
SelectDirDesc=Where is DCS's Saved Games folder?
|
||||
SelectDirLabel3=DCS Olympus must be installed within DCS's Saved Games folder.
|
||||
SelectDirBrowseLabel=This is the detected path. If this is incorrect, click Browse to set the correct folder.
|
||||
|
||||
[Tasks]
|
||||
; NOTE: The following entry contains English phrases ("Create a desktop icon" and "Additional icons"). You are free to translate them into another language if required.
|
||||
|
||||
@ -15,7 +15,7 @@ declare_plugin(self_ID,
|
||||
shortName = "Olympus",
|
||||
fileMenuName = "Olympus",
|
||||
|
||||
version = "v0.4.5-alpha",
|
||||
version = "v0.4.6-alpha",
|
||||
state = "installed",
|
||||
developerName= "DCS Refugees 767 squadron",
|
||||
info = _("DCS Olympus is a mod for DCS World. It allows users to spawn, control, task, group, and remove units from a DCS World server using a real-time map interface, similarly to Real Time Strategy games. The user interface also provides useful informations units, like loadouts, fuel, tasking, and so on. In the future, more features for DCS World GCI and JTAC will be available."),
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
"redCommanderPassword": "redpassword"
|
||||
},
|
||||
"client": {
|
||||
"port": 3000,
|
||||
"elevationProvider": {
|
||||
"provider": "https://srtm.fasma.org/{lat}{lng}.SRTMGL3S.hgt.zip",
|
||||
"username": null,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
local version = "v0.4.5-alpha"
|
||||
local version = "v0.4.6-alpha"
|
||||
|
||||
local debug = false -- True enables debug printing using DCS messages
|
||||
|
||||
@ -17,7 +17,7 @@ Olympus.weaponsData = {}
|
||||
|
||||
-- Units data structures
|
||||
Olympus.unitCounter = 1 -- Counter to generate unique names
|
||||
Olympus.spawnDatabase = {} -- Database of spawn options, used for units cloning
|
||||
Olympus.cloneDatabase = {} -- Database of spawn options, used for units cloning
|
||||
Olympus.unitIndex = 0 -- Counter used to spread the computational load of data retrievial from DCS
|
||||
Olympus.unitStep = 50 -- Max number of units that get updated each cycle
|
||||
Olympus.units = {} -- Table holding references to all the currently existing units
|
||||
@ -28,7 +28,8 @@ Olympus.weapons = {} -- Table holding references to all the currently existing
|
||||
|
||||
-- Miscellaneous initializations
|
||||
Olympus.missionStartTime = DCS.getRealTime()
|
||||
|
||||
Olympus.napalmCounter = 1
|
||||
Olympus.fireCounter = 1
|
||||
------------------------------------------------------------------------------------------------------
|
||||
-- Olympus functions
|
||||
------------------------------------------------------------------------------------------------------
|
||||
@ -434,11 +435,95 @@ function Olympus.smoke(color, lat, lng)
|
||||
end
|
||||
|
||||
-- Creates an explosion on the ground
|
||||
function Olympus.explosion(intensity, lat, lng)
|
||||
Olympus.debug("Olympus.explosion " .. intensity .. " (" .. lat .. ", " .. lng ..")", 2)
|
||||
trigger.action.explosion(mist.utils.makeVec3GL(coord.LLtoLO(lat, lng, 0)), intensity)
|
||||
function Olympus.explosion(intensity, explosionType, lat, lng, alt)
|
||||
Olympus.debug("Olympus.explosion " .. explosionType .. " " .. intensity .. " (" .. lat .. ", " .. lng .. ")", 2)
|
||||
|
||||
local vec3 = nil
|
||||
if alt ~= nil then
|
||||
vec3 = coord.LLtoLO(lat, lng, alt)
|
||||
else
|
||||
vec3 = mist.utils.makeVec3GL(coord.LLtoLO(lat, lng))
|
||||
end
|
||||
|
||||
if explosionType == "normal" then
|
||||
trigger.action.explosion(vec3, intensity)
|
||||
elseif explosionType == "phosphorous" then
|
||||
Olympus.phosphorous(vec3)
|
||||
elseif explosionType == "napalm" then
|
||||
Olympus.napalm(vec3)
|
||||
elseif explosionType == "secondary" then
|
||||
Olympus.secondaries(vec3)
|
||||
elseif explosionType == "fire" then
|
||||
Olympus.createFire(vec3)
|
||||
elseif explosionType == "depthCharge" then
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
function Olympus.phosphorous(vec3)
|
||||
trigger.action.explosion(vec3, 1)
|
||||
for i = 1,math.random(3, 10) do
|
||||
angle = mist.utils.toRadian((math.random(1, 360)))
|
||||
local randVec = mist.utils.makeVec3GL((mist.getRandPointInCircle(vec3, 5, 1, 0, 360)))
|
||||
trigger.action.signalFlare(randVec, 2, angle)
|
||||
end
|
||||
end
|
||||
|
||||
function Olympus.napalm(vec3)
|
||||
local napeName = "napalmStrike" .. Olympus.napalmCounter
|
||||
Olympus.napalmCounter = Olympus.napalmCounter + 1
|
||||
mist.dynAddStatic(
|
||||
{
|
||||
country = 20,
|
||||
category = 'Fortifications',
|
||||
hidden = true,
|
||||
name = napeName,
|
||||
type ="Fuel tank",
|
||||
x = vec3.x,
|
||||
y = vec3.z,
|
||||
heading = 0,
|
||||
} -- end of function
|
||||
)
|
||||
timer.scheduleFunction(Olympus.explodeNapalm, vec3, timer.getTime() + 0.1)
|
||||
timer.scheduleFunction(Olympus.removeNapalm, napeName, timer.getTime() + 0.12)
|
||||
end
|
||||
|
||||
function Olympus.explodeNapalm(vec3)
|
||||
trigger.action.explosion(vec3, 10)
|
||||
end
|
||||
|
||||
function Olympus.removeNapalm(staticName)
|
||||
StaticObject.getByName(staticName):destroy()
|
||||
end
|
||||
|
||||
function Olympus.createFire(vec3)
|
||||
local smokeName = "smokeName" .. Olympus.fireCounter
|
||||
Olympus.fireCounter = Olympus.fireCounter + 1
|
||||
trigger.action.effectSmokeBig(vec3, 2 , 1, smokeName)
|
||||
trigger.action.explosion(vec3, 1) -- looks wierd to spawn in on flat land without this
|
||||
timer.scheduleFunction(Olympus.removeFire, smokeName, timer.getTime() + 20)
|
||||
end
|
||||
|
||||
function Olympus.removeFire (smokeName)
|
||||
trigger.action.effectSmokeStop(smokeName)
|
||||
end
|
||||
|
||||
function Olympus.secondaries(vec3)
|
||||
trigger.action.explosion(vec3, 1)
|
||||
for i = 1, 10 do
|
||||
timer.scheduleFunction(Olympus.randomDebries, vec3, timer.getTime() + math.random(0, 180))
|
||||
end
|
||||
end
|
||||
|
||||
function Olympus.randomDebries(vec3)
|
||||
trigger.action.explosion(vec3, 1)
|
||||
for i = 1,math.random(3, 10) do
|
||||
angle = mist.utils.toRadian((math.random(1, 360)))
|
||||
local randVec = mist.utils.makeVec3GL((mist.getRandPointInCircle(vec3, 5, 1, 0, 360)))
|
||||
trigger.action.signalFlare(randVec, 3, angle)
|
||||
end
|
||||
end
|
||||
|
||||
-- Spawns a new unit or group
|
||||
-- Spawn table contains the following parameters
|
||||
-- category: (string), either Aircraft, Helicopter, GroundUnit or NavyUnit
|
||||
@ -500,6 +585,7 @@ function Olympus.spawnUnits(spawnTable)
|
||||
name = "Olympus-" .. Olympus.unitCounter,
|
||||
task = 'CAP'
|
||||
}
|
||||
Olympus.debug(Olympus.serializeTable(vars), 2)
|
||||
mist.dynAdd(vars)
|
||||
|
||||
Olympus.unitCounter = Olympus.unitCounter + 1
|
||||
@ -674,7 +760,17 @@ end
|
||||
|
||||
-- Add the unit data to the database, used for unit cloning
|
||||
function Olympus.addToDatabase(unitTable)
|
||||
Olympus.spawnDatabase[unitTable.name] = unitTable
|
||||
Olympus.cloneDatabase[unitTable.name] = unitTable
|
||||
end
|
||||
|
||||
-- Find a database entry by ID
|
||||
function Olympus.findInDatabase(ID)
|
||||
for idx, unit in pairs(Olympus.cloneDatabase) do
|
||||
if unit ~= nil and unit["ID"] == ID then
|
||||
return unit
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Clones a unit by ID. Will clone the unit with the same original payload as the source unit. TODO: only works on Olympus unit not ME units (TO BE VERIFIED).
|
||||
@ -693,27 +789,22 @@ function Olympus.clone(cloneTable, deleteOriginal)
|
||||
-- All the units in the table will be cloned in a single group
|
||||
for idx, cloneData in pairs(cloneTable) do
|
||||
local ID = cloneData.ID
|
||||
local unit = Olympus.getUnitByID(ID)
|
||||
local unit = Olympus.findInDatabase(ID)
|
||||
|
||||
if unit then
|
||||
local position = unit:getPosition()
|
||||
local heading = math.atan2( position.x.z, position.x.x )
|
||||
|
||||
if unit ~= nil then
|
||||
-- Update the data of the cloned unit
|
||||
local unitTable = mist.utils.deepCopy(Olympus.spawnDatabase[unit:getName()])
|
||||
local unitTable = mist.utils.deepCopy(unit)
|
||||
|
||||
local point = coord.LLtoLO(cloneData['lat'], cloneData['lng'], 0)
|
||||
if unitTable then
|
||||
unitTable["x"] = point.x
|
||||
unitTable["y"] = point.z
|
||||
unitTable["alt"] = unit:getPoint().y
|
||||
unitTable["heading"] = heading
|
||||
unitTable["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitsTable + 1
|
||||
end
|
||||
|
||||
if countryID == nil and category == nil then
|
||||
countryID = unit:getCountry()
|
||||
if unit:getDesc().category == Unit.Category.AIRPLANE then
|
||||
countryID = unit["country"]
|
||||
if unit["category"] == Unit.Category.AIRPLANE then
|
||||
category = 'plane'
|
||||
route = {
|
||||
["points"] =
|
||||
@ -732,7 +823,7 @@ function Olympus.clone(cloneTable, deleteOriginal)
|
||||
},
|
||||
},
|
||||
}
|
||||
elseif unit:getDesc().category == Unit.Category.HELICOPTER then
|
||||
elseif unit["category"] == Unit.Category.HELICOPTER then
|
||||
category = 'helicopter'
|
||||
route = {
|
||||
["points"] =
|
||||
@ -751,13 +842,12 @@ function Olympus.clone(cloneTable, deleteOriginal)
|
||||
},
|
||||
},
|
||||
}
|
||||
elseif unit:getDesc().category == Unit.Category.GROUND_UNIT then
|
||||
elseif unit["category"] == Unit.Category.GROUND_UNIT then
|
||||
category = 'vehicle'
|
||||
elseif unit:getDesc().category == Unit.Category.SHIP then
|
||||
elseif unit["category"] == Unit.Category.SHIP then
|
||||
category = 'ship'
|
||||
end
|
||||
end
|
||||
|
||||
unitsTable[#unitsTable + 1] = mist.utils.deepCopy(unitTable)
|
||||
end
|
||||
|
||||
@ -783,6 +873,7 @@ function Olympus.clone(cloneTable, deleteOriginal)
|
||||
Olympus.addToDatabase(unitTable)
|
||||
end
|
||||
|
||||
Olympus.debug(Olympus.serializeTable(vars), 2)
|
||||
mist.dynAdd(vars)
|
||||
Olympus.unitCounter = Olympus.unitCounter + 1
|
||||
|
||||
@ -790,12 +881,16 @@ function Olympus.clone(cloneTable, deleteOriginal)
|
||||
end
|
||||
|
||||
-- Delete a unit by ID, optionally use an explosion
|
||||
function Olympus.delete(ID, explosion)
|
||||
function Olympus.delete(ID, explosion, explosionType)
|
||||
Olympus.debug("Olympus.delete " .. ID .. " " .. tostring(explosion), 2)
|
||||
local unit = Olympus.getUnitByID(ID)
|
||||
if unit then
|
||||
if unit:getPlayerName() or explosion then
|
||||
trigger.action.explosion(unit:getPoint() , 250 ) --consider replacing with forcibly deslotting the player, however this will work for now
|
||||
if explosionType == nil then
|
||||
explosionType = "normal"
|
||||
end
|
||||
local lat, lng, alt = coord.LOtoLL(unit:getPoint())
|
||||
Olympus.explosion(250, explosionType, lat, lng, alt)
|
||||
Olympus.debug("Olympus.delete completed successfully", 2)
|
||||
else
|
||||
unit:destroy(); --works for AI units not players
|
||||
@ -937,11 +1032,25 @@ function Olympus.setUnitsData(arg, time)
|
||||
table["hasTask"] = controller:hasTask()
|
||||
table["ammo"] = unit:getAmmo() --TODO remove a lot of stuff we don't really need
|
||||
table["fuel"] = unit:getFuel()
|
||||
table["life"] = unit:getLife() / unit:getLife0()
|
||||
table["health"] = unit:getLife() / unit:getLife0() * 100
|
||||
table["contacts"] = contacts
|
||||
|
||||
-- Update the database used for unit cloning
|
||||
local name = unit:getName()
|
||||
if Olympus.cloneDatabase[name] ~= nil then
|
||||
Olympus.cloneDatabase[name]["ID"] = ID
|
||||
Olympus.cloneDatabase[name]["category"] = unit:getDesc().category
|
||||
Olympus.cloneDatabase[name]["heading"] = table["heading"]
|
||||
Olympus.cloneDatabase[name]["alt"] = alt
|
||||
Olympus.cloneDatabase[name]["country"] = unit:getCountry()
|
||||
end
|
||||
|
||||
units[ID] = table
|
||||
end
|
||||
else
|
||||
-- If the unit reference is nil it means the unit no longer exits
|
||||
units[ID] = {isAlive = false}
|
||||
Olympus.units[ID] = nil
|
||||
end
|
||||
end
|
||||
else
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
local version = 'v0.4.5-alpha'
|
||||
local version = 'v0.4.6-alpha'
|
||||
|
||||
Olympus = {}
|
||||
Olympus.OlympusDLL = nil
|
||||
|
||||
@ -12,6 +12,40 @@ from dcs.weapons_data import Weapons
|
||||
from dcs.planes import *
|
||||
from dcs.helicopters import *
|
||||
|
||||
|
||||
clsid_conversion = {
|
||||
'ExtFuelTankID' : "{EFT_230GAL}" ,
|
||||
'InternalFuelTank100' : "{IAFS_ComboPak_100}" ,
|
||||
'NURSLauncherID_MK151' : "M261_MK151" ,
|
||||
'NURSLauncherID_M229' : "{M261_M229}" ,
|
||||
'NURSLauncherID_M257' : "{M261_M257}" ,
|
||||
'NURSLauncherID_M274' : "{M261_M274}" ,
|
||||
'NURSLauncherID_M282' : "{M261_M282}" ,
|
||||
'NURSLauncherID_M433' : "{M261_M151_M433}" ,
|
||||
'NURSLauncherID_M151_M274_OUTBOARD' : "{M261_OUTBOARD_AB_M151_E_M274}" ,
|
||||
'NURSLauncherID_M151_M257_OUTBOARD' : "{M261_OUTBOARD_AB_M151_E_M257}" ,
|
||||
'NURSLauncherID_M274_M151_INBOARD' : "{M261_INBOARD_DE_M151_C_M274}" ,
|
||||
'NURSLauncherID_M257_M151_INBOARD' : "{M261_INBOARD_DE_M151_C_M257}" ,
|
||||
'HellfireLauncherID_AGM114K_0' : "{M299_EMPTY}" ,
|
||||
'HellfireLauncherID_AGM114K_4' : "{88D18A5E-99C8-4B04-B40B-1C02F2018B6E}" ,
|
||||
'HellfireLauncherID_AGM114K_3_L' : "{M299_3xAGM_114K_OUTBOARD_PORT}" ,
|
||||
'HellfireLauncherID_AGM114K_3_R' : "{M299_3xAGM_114K_OUTBOARD_STARBOARD}" ,
|
||||
'HellfireLauncherID_AGM114K_2' : "{M299_2xAGM_114K}" ,
|
||||
'HellfireLauncherID_AGM114K_1_L' : "{M299_1xAGM_114K_OUTBOARD_PORT}" ,
|
||||
'HellfireLauncherID_AGM114K_1_R' : "{M299_1xAGM_114K_OUTBOARD_STARBOARD}" ,
|
||||
'HellfireLauncherID_AGM114L_4' : "{M299_4xAGM_114L}" ,
|
||||
'HellfireLauncherID_AGM114L_3_L' : "{M299_3xAGM_114L_OUTBOARD_PORT}" ,
|
||||
'HellfireLauncherID_AGM114L_3_R' : "{M299_3xAGM_114L_OUTBOARD_STARBOARD}" ,
|
||||
'HellfireLauncherID_AGM114L_2' : "{M299_2xAGM_114L}" ,
|
||||
'HellfireLauncherID_AGM114L_1_L' : "{M299_1xAGM_114L_OUTBOARD_PORT}" ,
|
||||
'HellfireLauncherID_AGM114L_1_R' : "{M299_1xAGM_114L_OUTBOARD_STARBOARD}" ,
|
||||
'HellfireLauncherID_AGM114_1K3L_L' : "{M299_1xAGM_114K_3xAGM_114L_PRT}" ,
|
||||
'HellfireLauncherID_AGM114_1K3L_R' : "{M299_1xAGM_114K_3xAGM_114L_STRBRD}" ,
|
||||
'HellfireLauncherID_AGM114_2K2L' : "{M299_2xAGM_114K_2xAGM_114L}" ,
|
||||
'HellfireLauncherID_AGM114_3K1L_R' : "{M299_3xAGM_114K_1xAGM_114L_STRBRD}" ,
|
||||
'HellfireLauncherID_AGM114_3K1L_L' : "{M299_3xAGM_114K_1xAGM_114L_PRT}" ,
|
||||
}
|
||||
|
||||
def rename_task(task_name):
|
||||
task_map = {
|
||||
"AFAC": "FAC-A",
|
||||
@ -20,13 +54,35 @@ def rename_task(task_name):
|
||||
"Intercept": "CAP",
|
||||
"Pinpoint Strike": "Strike",
|
||||
"Refueling": "Tanker",
|
||||
"Nothing": "No task"
|
||||
"Nothing": "No task",
|
||||
}
|
||||
|
||||
if task_name in task_map:
|
||||
return task_map[task_name]
|
||||
else:
|
||||
return task_name
|
||||
|
||||
def convert_role(role):
|
||||
other_roles = {
|
||||
"tAntiShip": "AntishipStrike",
|
||||
"tGndAttack": "GroundAttack",
|
||||
"tAFAC": "AFAC",
|
||||
"tRecon": "Reconnaissance",
|
||||
"tRwyAttack": "RunwayAttack",
|
||||
"tCAP": "CAP",
|
||||
"tCAS": "CAS",
|
||||
"tSEAD": "SEAD",
|
||||
"tPinpntStrike": "PinpointStike",
|
||||
"tIntercept": "Intercept",
|
||||
"tCAP": "CAP",
|
||||
"tFighterSweep": "FighterSweep",
|
||||
"tEscort": "CAP"
|
||||
}
|
||||
|
||||
if role in other_roles:
|
||||
return other_roles[role]
|
||||
else:
|
||||
return role
|
||||
|
||||
# Known id mismatches (because reasons, ask ED)
|
||||
mismatched_ids = {
|
||||
@ -42,6 +98,9 @@ def find_weapon_name(clsid):
|
||||
if getattr(Weapons, weapon_id)["clsid"] == clsid:
|
||||
return getattr(Weapons, weapon_id)["name"]
|
||||
|
||||
if clsid in clsid_conversion:
|
||||
return clsid_conversion[clsid]
|
||||
|
||||
# The database file on which to operate is the first standard argument of the call
|
||||
if len(sys.argv) > 1:
|
||||
if (sys.argv[1] == "aircraft"):
|
||||
@ -105,6 +164,8 @@ if len(sys.argv) > 1:
|
||||
for payload_idx in unit_payloads[unit_name][payload_name]:
|
||||
payload_clsid = unit_payloads[unit_name][payload_name][payload_idx]["CLSID"]
|
||||
weapon_name = find_weapon_name(payload_clsid)
|
||||
if weapon_name is None:
|
||||
weapon_name = payload_clsid
|
||||
if weapon_name in payload_weapons:
|
||||
payload_weapons[weapon_name] += 1
|
||||
else:
|
||||
@ -121,7 +182,7 @@ if len(sys.argv) > 1:
|
||||
else:
|
||||
for name, obj in inspect.getmembers(task):
|
||||
if inspect.isclass(obj) and issubclass(obj, task.MainTask):
|
||||
if (name == role):
|
||||
if (name == convert_role(role)):
|
||||
payload_roles.append(rename_task(obj.name))
|
||||
|
||||
# Create the loadout structure and append it to the table
|
||||
|
||||
35
scripts/python/convertTags.py
Normal file
@ -0,0 +1,35 @@
|
||||
import sys
|
||||
import json
|
||||
import re
|
||||
|
||||
|
||||
# The database file on which to operate is the first standard argument of the call
|
||||
if len(sys.argv) > 1:
|
||||
if (sys.argv[1] == "aircraft"):
|
||||
filename = '..\\..\\client\\public\\databases\\units\\aircraftdatabase.json'
|
||||
elif (sys.argv[1] == "helicopter"):
|
||||
filename = '..\\..\\client\\public\\databases\\units\\helicopterdatabase.json'
|
||||
elif (sys.argv[1] == "groundunit"):
|
||||
filename = '..\\..\\client\\public\\databases\\units\\groundunitdatabase.json'
|
||||
elif (sys.argv[1] == "navyunit"):
|
||||
filename = '..\\..\\client\\public\\databases\\units\\navyunitdatabase.json'
|
||||
|
||||
# Loads the database
|
||||
with open(filename) as f:
|
||||
database = json.load(f)
|
||||
|
||||
for name in database:
|
||||
label = database[name]['label']
|
||||
print(label)
|
||||
res = re.findall("\((.*?)\)", label)
|
||||
for tag in res:
|
||||
label = label.replace(f"({tag})", "")
|
||||
label = database[name]['label'] = label
|
||||
if len(res) > 0:
|
||||
database[name]["tags"] = "".join([f'{tag}{", " if i < len(res) - 1 else ""}' for i, tag in enumerate(res)])
|
||||
|
||||
# Dump everything in the database
|
||||
with open(filename, "w") as f:
|
||||
json.dump(database, f, indent=2)
|
||||
|
||||
print("Done!")
|
||||
@ -8,6 +8,45 @@ sys.path.append("..\..\..\dcs-master\dcs-master")
|
||||
|
||||
SEARCH_FOLDER = "D:\\Eagle Dynamics\\DCS World OpenBeta"
|
||||
|
||||
clsid_conversion = {
|
||||
'ExtFuelTankID' : "{EFT_230GAL}" ,
|
||||
'InternalFuelTank100' : "{IAFS_ComboPak_100}" ,
|
||||
'NURSLauncherID_MK151' : "M261_MK151" ,
|
||||
'NURSLauncherID_M229' : "{M261_M229}" ,
|
||||
'NURSLauncherID_M257' : "{M261_M257}" ,
|
||||
'NURSLauncherID_M274' : "{M261_M274}" ,
|
||||
'NURSLauncherID_M282' : "{M261_M282}" ,
|
||||
'NURSLauncherID_M433' : "{M261_M151_M433}" ,
|
||||
'NURSLauncherID_M151_M274_OUTBOARD' : "{M261_OUTBOARD_AB_M151_E_M274}" ,
|
||||
'NURSLauncherID_M151_M257_OUTBOARD' : "{M261_OUTBOARD_AB_M151_E_M257}" ,
|
||||
'NURSLauncherID_M274_M151_INBOARD' : "{M261_INBOARD_DE_M151_C_M274}" ,
|
||||
'NURSLauncherID_M257_M151_INBOARD' : "{M261_INBOARD_DE_M151_C_M257}" ,
|
||||
'HellfireLauncherID_AGM114K_0' : "{M299_EMPTY}" ,
|
||||
'HellfireLauncherID_AGM114K_4' : "{88D18A5E-99C8-4B04-B40B-1C02F2018B6E}" ,
|
||||
'HellfireLauncherID_AGM114K_3_L' : "{M299_3xAGM_114K_OUTBOARD_PORT}" ,
|
||||
'HellfireLauncherID_AGM114K_3_R' : "{M299_3xAGM_114K_OUTBOARD_STARBOARD}" ,
|
||||
'HellfireLauncherID_AGM114K_2' : "{M299_2xAGM_114K}" ,
|
||||
'HellfireLauncherID_AGM114K_1_L' : "{M299_1xAGM_114K_OUTBOARD_PORT}" ,
|
||||
'HellfireLauncherID_AGM114K_1_R' : "{M299_1xAGM_114K_OUTBOARD_STARBOARD}" ,
|
||||
'HellfireLauncherID_AGM114L_4' : "{M299_4xAGM_114L}" ,
|
||||
'HellfireLauncherID_AGM114L_3_L' : "{M299_3xAGM_114L_OUTBOARD_PORT}" ,
|
||||
'HellfireLauncherID_AGM114L_3_R' : "{M299_3xAGM_114L_OUTBOARD_STARBOARD}" ,
|
||||
'HellfireLauncherID_AGM114L_2' : "{M299_2xAGM_114L}" ,
|
||||
'HellfireLauncherID_AGM114L_1_L' : "{M299_1xAGM_114L_OUTBOARD_PORT}" ,
|
||||
'HellfireLauncherID_AGM114L_1_R' : "{M299_1xAGM_114L_OUTBOARD_STARBOARD}" ,
|
||||
'HellfireLauncherID_AGM114_1K3L_L' : "{M299_1xAGM_114K_3xAGM_114L_PRT}" ,
|
||||
'HellfireLauncherID_AGM114_1K3L_R' : "{M299_1xAGM_114K_3xAGM_114L_STRBRD}" ,
|
||||
'HellfireLauncherID_AGM114_2K2L' : "{M299_2xAGM_114K_2xAGM_114L}" ,
|
||||
'HellfireLauncherID_AGM114_3K1L_R' : "{M299_3xAGM_114K_1xAGM_114L_STRBRD}" ,
|
||||
'HellfireLauncherID_AGM114_3K1L_L' : "{M299_3xAGM_114K_1xAGM_114L_PRT}" ,
|
||||
}
|
||||
|
||||
def convert_clsid(clsid):
|
||||
if clsid in clsid_conversion:
|
||||
return clsid_conversion[clsid]
|
||||
else:
|
||||
return clsid
|
||||
|
||||
def dump_lua(data):
|
||||
if type(data) is str:
|
||||
return f'"{data}"'
|
||||
@ -62,10 +101,30 @@ for filename in filenames:
|
||||
for payload in src:
|
||||
names[tmp['unitType']].append(payload['name'])
|
||||
roles[tmp['unitType']][payload['name']] = payload['tasks']
|
||||
if type(payload['pylons']) == dict:
|
||||
payloads[tmp['unitType']][payload['name']] = {payload['pylons'][key]['num']: {"CLSID" : payload['pylons'][key]['CLSID']} for key in payload['pylons']}
|
||||
|
||||
# The Tomcats are a bit special
|
||||
if (tmp['unitType'] in ["F-14A-95-GR", "F-14A-135-GR", "F-14B"]):
|
||||
pylonConversion = {
|
||||
"pylon_1A": 1,
|
||||
"pylon_1B": 2,
|
||||
"pylon_2": 3,
|
||||
"pylon_3": 4,
|
||||
"pylon_4": 5,
|
||||
"pylon_5": 6,
|
||||
"pylon_6": 7,
|
||||
"pylon_7": 8,
|
||||
"pylon_8B": 9,
|
||||
"pylon_8A": 10
|
||||
}
|
||||
if type(payload['pylons']) == dict:
|
||||
payloads[tmp['unitType']][payload['name']] = {pylonConversion[payload['pylons'][key]['num']]: {"CLSID" : convert_clsid(payload['pylons'][key]['CLSID'])} for key in payload['pylons']}
|
||||
else:
|
||||
payloads[tmp['unitType']][payload['name']] = {pylonConversion[payload['pylons'][key]['num']]: {"CLSID" : convert_clsid(payload['pylons'][key]['CLSID'])} for key in range(len(payload['pylons']))}
|
||||
else:
|
||||
payloads[tmp['unitType']][payload['name']] = {payload['pylons'][key]['num']: {"CLSID" : payload['pylons'][key]['CLSID']} for key in range(len(payload['pylons']))}
|
||||
if type(payload['pylons']) == dict:
|
||||
payloads[tmp['unitType']][payload['name']] = {payload['pylons'][key]['num']: {"CLSID" : convert_clsid(payload['pylons'][key]['CLSID'])} for key in payload['pylons']}
|
||||
else:
|
||||
payloads[tmp['unitType']][payload['name']] = {payload['pylons'][key]['num']: {"CLSID" : convert_clsid(payload['pylons'][key]['CLSID'])} for key in range(len(payload['pylons']))}
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
@ -47,6 +47,9 @@
|
||||
},
|
||||
"AGM-65H*6,Mk82*10,AIM-9M*2,ECM": {
|
||||
"1": 30
|
||||
},
|
||||
"AGM-65H*6,LAU-131*2,AIM-9M*2,ECM": {
|
||||
"1": 31
|
||||
}
|
||||
},
|
||||
"A-10C": {
|
||||
@ -432,16 +435,16 @@
|
||||
"AGM-65D*6,GBU-12*4,AIM-9M*2,ECM,TGP": {
|
||||
"1": 30
|
||||
},
|
||||
"AGM-65E*2,Mk-82AIR*2,CBU-97*2,AIM-9M*2,ECM,TGP": {
|
||||
"AGM-65L*2,Mk-82AIR*2,CBU-97*2,AIM-9M*2,ECM,TGP": {
|
||||
"1": 31
|
||||
},
|
||||
"AGM-65E*2,CBU-97*4,AIM-9M*2,ECM,TGP": {
|
||||
"AGM-65L*2,CBU-97*4,AIM-9M*2,ECM,TGP": {
|
||||
"1": 31
|
||||
},
|
||||
"AGM-65E*2,CBU-97*4,AIM-9M*2,ECM,M151 APKWS*7,TGP": {
|
||||
"AGM-65L*2,CBU-97*4,AIM-9M*2,ECM,M151 APKWS*7,TGP": {
|
||||
"1": 31
|
||||
},
|
||||
"AGM-65E*2,CBU-105*4,AIM-9M*2,ECM,M151 APKWS*7,TGP": {
|
||||
"AGM-65L*2,CBU-105*4,AIM-9M*2,ECM,M151 APKWS*7,TGP": {
|
||||
"1": 31
|
||||
},
|
||||
"Mk-82*4,Mk-8AIR*4,AIM-9*2,ECM": {
|
||||
@ -462,22 +465,22 @@
|
||||
"GBU-38*4,M151 APKWS*7,AGM-65D*1,AGM-65H*1,TGP,AIM-9*2,ECM": {
|
||||
"1": 32
|
||||
},
|
||||
"GBU-38*4,M151 APKWS*7,AGM-65E*2,TGP,AIM-9*2,ECM": {
|
||||
"GBU-38*4,M151 APKWS*7,AGM-65L*2,TGP,AIM-9*2,ECM": {
|
||||
"1": 32
|
||||
},
|
||||
"GBU-12*4,M151 APKWS*7,AGM-65E*2,TGP,AIM-9*2,ECM": {
|
||||
"GBU-12*4,M151 APKWS*7,AGM-65L*2,TGP,AIM-9*2,ECM": {
|
||||
"1": 32
|
||||
},
|
||||
"GBU-12*2,GBU-38*2,M151 APKWS*7,AGM-65E*2,TGP,AIM-9*2,ECM": {
|
||||
"GBU-12*2,GBU-38*2,M151 APKWS*7,AGM-65L*2,TGP,AIM-9*2,ECM": {
|
||||
"1": 32
|
||||
},
|
||||
"GBU-10*2,M151 APKWS*7,AGM-65E*2,TGP,AIM-9*2,ECM": {
|
||||
"GBU-10*2,M151 APKWS*7,AGM-65L*2,TGP,AIM-9*2,ECM": {
|
||||
"1": 32
|
||||
},
|
||||
"GBU-31*2,M151 APKWS*7,AGM-65E*2,TGP,AIM-9*2,ECM": {
|
||||
"GBU-31*2,M151 APKWS*7,AGM-65L*2,TGP,AIM-9*2,ECM": {
|
||||
"1": 32
|
||||
},
|
||||
"GBU-54*4,M151 APKWS*7,AGM-65E*2,TGP,AIM-9*2,ECM": {
|
||||
"GBU-54*4,M151 APKWS*7,AGM-65L*2,TGP,AIM-9*2,ECM": {
|
||||
"1": 32
|
||||
},
|
||||
"GBU-54*4,M151 APKWS*7,AGM-65D*4,TGP,AIM-9*2,ECM": {
|
||||
@ -2669,9 +2672,8 @@
|
||||
"1": 16
|
||||
},
|
||||
"2xFuel tank, 40xS-8": {
|
||||
"1": 31,
|
||||
"2": 32,
|
||||
"3": 18
|
||||
"1": 32,
|
||||
"2": 18
|
||||
},
|
||||
"80xS-8": {
|
||||
"1": 31,
|
||||
@ -2685,14 +2687,12 @@
|
||||
"1": 32
|
||||
},
|
||||
"2xFuel tank, 12x9A4172": {
|
||||
"1": 31,
|
||||
"2": 32,
|
||||
"3": 18
|
||||
"1": 32,
|
||||
"2": 18
|
||||
},
|
||||
"2xFuel tank, 2xUPK-23": {
|
||||
"1": 31,
|
||||
"2": 32,
|
||||
"3": 18
|
||||
"1": 32,
|
||||
"2": 18
|
||||
},
|
||||
"12x9A4172, 40xS-8": {
|
||||
"1": 31,
|
||||
@ -2732,9 +2732,8 @@
|
||||
"1": 32
|
||||
},
|
||||
"6x9A4172": {
|
||||
"1": 31,
|
||||
"2": 32,
|
||||
"3": 18
|
||||
"1": 32,
|
||||
"2": 18
|
||||
},
|
||||
"2xFuel tank, 2xKMGU AT": {
|
||||
"1": 32
|
||||
@ -2777,11 +2776,10 @@
|
||||
"40xS-8 TsM": {
|
||||
"1": 16
|
||||
},
|
||||
"12x9A4172, 10xS-13": {
|
||||
"2xUPK-23": {
|
||||
"1": 31,
|
||||
"2": 32,
|
||||
"3": 18,
|
||||
"4": 30
|
||||
"3": 18
|
||||
},
|
||||
"2xFuel tank, 2xFAB-500": {
|
||||
"1": 32
|
||||
@ -2800,18 +2798,20 @@
|
||||
"2xFAB-250, 12x9A4172": {
|
||||
"1": 32
|
||||
},
|
||||
"2xUPK-23": {
|
||||
"12x9A4172, 10xS-13": {
|
||||
"1": 31,
|
||||
"2": 32,
|
||||
"3": 18
|
||||
"3": 18,
|
||||
"4": 30
|
||||
}
|
||||
},
|
||||
"Ka-50_3": {
|
||||
"4xIgla": {
|
||||
"1": 31
|
||||
"1": 32
|
||||
},
|
||||
"2xKh-25ML, 10xS-13, 4xIgla": {
|
||||
"1": 30
|
||||
"1": 30,
|
||||
"2": 31
|
||||
},
|
||||
"12x9A4172, 40xS-8KOM, 4xIgla": {
|
||||
"1": 31,
|
||||
@ -3165,7 +3165,7 @@
|
||||
"1": 31
|
||||
},
|
||||
"4xPTB-450 Fuel tank": {
|
||||
"1": 31
|
||||
"1": 32
|
||||
}
|
||||
},
|
||||
"MiG-19P": {
|
||||
@ -4667,6 +4667,65 @@
|
||||
"1": 18
|
||||
}
|
||||
},
|
||||
"B-1B": {
|
||||
"Mk-82*84": {
|
||||
"1": 34,
|
||||
"2": 32
|
||||
},
|
||||
"AGM-154*12": {
|
||||
"1": 33
|
||||
},
|
||||
"GBU-38*48": {
|
||||
"1": 31,
|
||||
"2": 32,
|
||||
"3": 33
|
||||
},
|
||||
"CBU-87*30": {
|
||||
"1": 31
|
||||
},
|
||||
"CBU-97*30": {
|
||||
"1": 31
|
||||
},
|
||||
"GBU-38*16, CBU-97*20": {
|
||||
"1": 31
|
||||
},
|
||||
"Mk-84*24": {
|
||||
"1": 34,
|
||||
"2": 32
|
||||
},
|
||||
"GBU-31*24": {
|
||||
"1": 32,
|
||||
"2": 33
|
||||
},
|
||||
"GBU-31(V)3/B*24": {
|
||||
"1": 32,
|
||||
"2": 33
|
||||
},
|
||||
"GBU-31*8, GBU-38*32": {
|
||||
"1": 32,
|
||||
"2": 33
|
||||
}
|
||||
},
|
||||
"B-52H": {
|
||||
"Mk-84*18": {
|
||||
"1": 32,
|
||||
"2": 34
|
||||
},
|
||||
"Mk 82*51": {
|
||||
"1": 32,
|
||||
"2": 34
|
||||
},
|
||||
"Mk20*18": {
|
||||
"1": 32,
|
||||
"2": 34
|
||||
},
|
||||
"AGM-86C*20": {
|
||||
"1": 33
|
||||
},
|
||||
"AGM-84A*8": {
|
||||
"1": 30
|
||||
}
|
||||
},
|
||||
"A-20G": {
|
||||
"500 lb GP bomb LD*4": {
|
||||
"1": 31,
|
||||
@ -4943,7 +5002,7 @@
|
||||
"2": 31,
|
||||
"3": 32
|
||||
},
|
||||
"8xBGM-71, 38xHYDRA-70": {
|
||||
"8xAGM-114, 14xHYDRA-70": {
|
||||
"1": 18,
|
||||
"2": 31,
|
||||
"3": 32
|
||||
@ -4970,7 +5029,7 @@
|
||||
"3": 32,
|
||||
"4": 30
|
||||
},
|
||||
"8xAGM-114, 14xHYDRA-70": {
|
||||
"8xBGM-71, 38xHYDRA-70": {
|
||||
"1": 18,
|
||||
"2": 31,
|
||||
"3": 32
|
||||
@ -5032,79 +5091,20 @@
|
||||
"8xAGM-114, 38xHYDRA-70 WP": {
|
||||
"1": 16
|
||||
},
|
||||
"8xAGM-114, 38xHYDRA-70": {
|
||||
"1": 18,
|
||||
"2": 31,
|
||||
"3": 32
|
||||
},
|
||||
"AGM-114K*16": {
|
||||
"1": 18,
|
||||
"2": 31,
|
||||
"3": 32,
|
||||
"4": 30
|
||||
},
|
||||
"8xAGM-114, 38xHYDRA-70": {
|
||||
"1": 18,
|
||||
"2": 31,
|
||||
"3": 32
|
||||
}
|
||||
},
|
||||
"An-26B": {},
|
||||
"An-30M": {},
|
||||
"B-1B": {
|
||||
"Mk-82*84": {
|
||||
"1": 34,
|
||||
"2": 32
|
||||
},
|
||||
"AGM-154*12": {
|
||||
"1": 33
|
||||
},
|
||||
"GBU-38*48": {
|
||||
"1": 31,
|
||||
"2": 32,
|
||||
"3": 33
|
||||
},
|
||||
"CBU-87*30": {
|
||||
"1": 31
|
||||
},
|
||||
"CBU-97*30": {
|
||||
"1": 31
|
||||
},
|
||||
"GBU-38*16, CBU-97*20": {
|
||||
"1": 31
|
||||
},
|
||||
"Mk-84*24": {
|
||||
"1": 34,
|
||||
"2": 32
|
||||
},
|
||||
"GBU-31*24": {
|
||||
"1": 32,
|
||||
"2": 33
|
||||
},
|
||||
"GBU-31(V)3/B*24": {
|
||||
"1": 32,
|
||||
"2": 33
|
||||
},
|
||||
"GBU-31*8, GBU-38*32": {
|
||||
"1": 32,
|
||||
"2": 33
|
||||
}
|
||||
},
|
||||
"B-52H": {
|
||||
"Mk-84*18": {
|
||||
"1": 32,
|
||||
"2": 34
|
||||
},
|
||||
"Mk 82*51": {
|
||||
"1": 32,
|
||||
"2": 34
|
||||
},
|
||||
"Mk20*18": {
|
||||
"1": 32,
|
||||
"2": 34
|
||||
},
|
||||
"AGM-86C*20": {
|
||||
"1": 33
|
||||
},
|
||||
"AGM-84A*8": {
|
||||
"1": 30
|
||||
}
|
||||
},
|
||||
"C-130": {},
|
||||
"C-17A": {},
|
||||
"CH-47D": {},
|
||||
@ -5875,11 +5875,10 @@
|
||||
"2": 32,
|
||||
"3": 18
|
||||
},
|
||||
"16x9M114, 10xS-13": {
|
||||
"16x9M114, 2xKMGU AT": {
|
||||
"1": 31,
|
||||
"2": 32,
|
||||
"3": 18,
|
||||
"4": 30
|
||||
"3": 18
|
||||
},
|
||||
"4xFAB-500": {
|
||||
"1": 32
|
||||
@ -5963,10 +5962,11 @@
|
||||
"2xFAB-250, 16x9M114": {
|
||||
"1": 32
|
||||
},
|
||||
"16x9M114, 2xKMGU AT": {
|
||||
"16x9M114, 10xS-13": {
|
||||
"1": 31,
|
||||
"2": 32,
|
||||
"3": 18
|
||||
"3": 18,
|
||||
"4": 30
|
||||
}
|
||||
},
|
||||
"Mi-8MT": {
|
||||
@ -6713,7 +6713,7 @@
|
||||
"1": 16
|
||||
},
|
||||
"Kh-29L*2,Kh-25ML*4,S-25L*2,R-60M*2": {
|
||||
"1": 33
|
||||
"1": 31
|
||||
},
|
||||
"FAB-500*6,R-60M*2,Fuel*2": {
|
||||
"1": 32
|
||||
@ -7368,16 +7368,19 @@
|
||||
"1": 16
|
||||
},
|
||||
"AGM-88*4,AIM-9*2,ECM": {
|
||||
"1": 29
|
||||
"1": 29,
|
||||
"2": 31
|
||||
},
|
||||
"AGM-88*2,AIM-9*2,Fuel*2,ECM": {
|
||||
"1": 29
|
||||
"1": 29,
|
||||
"2": 31
|
||||
},
|
||||
"Kormoran*4,AIM-9*2": {
|
||||
"1": 30
|
||||
},
|
||||
"Kormoran*2,AIM-9*2,AGM-88*2": {
|
||||
"1": 30
|
||||
"1": 30,
|
||||
"2": 31
|
||||
},
|
||||
"Mk-82*4,AIM-9*2,Fuel*2": {
|
||||
"1": 32
|
||||
|
||||
@ -37,7 +37,6 @@
|
||||
<ClInclude Include="include\airunit.h" />
|
||||
<ClInclude Include="include\commands.h" />
|
||||
<ClInclude Include="include\datatypes.h" />
|
||||
<ClInclude Include="include\measure.h" />
|
||||
<ClInclude Include="include\groundunit.h" />
|
||||
<ClInclude Include="include\helicopter.h" />
|
||||
<ClInclude Include="include\navyunit.h" />
|
||||
@ -55,7 +54,6 @@
|
||||
<ClCompile Include="src\commands.cpp" />
|
||||
<ClCompile Include="src\core.cpp" />
|
||||
<ClCompile Include="src\datatypes.cpp" />
|
||||
<ClCompile Include="src\measure.cpp" />
|
||||
<ClCompile Include="src\groundunit.cpp" />
|
||||
<ClCompile Include="src\helicopter.cpp" />
|
||||
<ClCompile Include="src\navyunit.cpp" />
|
||||
|
||||
@ -45,9 +45,6 @@
|
||||
<ClInclude Include="include\weapon.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\measure.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\datatypes.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
@ -95,9 +92,6 @@
|
||||
<ClCompile Include="src\weapon.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\measure.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\datatypes.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
|
||||
@ -278,10 +278,11 @@ private:
|
||||
class Delete : public Command
|
||||
{
|
||||
public:
|
||||
Delete(unsigned int ID, bool explosion, bool immediate, function<void(void)> callback = [](){}) :
|
||||
Delete(unsigned int ID, bool explosion, string explosionType, bool immediate, function<void(void)> callback = [](){}) :
|
||||
Command(callback),
|
||||
ID(ID),
|
||||
explosion(explosion),
|
||||
explosionType(explosionType),
|
||||
immediate(immediate)
|
||||
{
|
||||
priority = CommandPriority::HIGH;
|
||||
@ -293,6 +294,7 @@ public:
|
||||
private:
|
||||
const unsigned int ID;
|
||||
const bool explosion;
|
||||
const string explosionType;
|
||||
const bool immediate;
|
||||
};
|
||||
|
||||
@ -410,10 +412,11 @@ private:
|
||||
class Explosion : public Command
|
||||
{
|
||||
public:
|
||||
Explosion(unsigned int intensity, Coords location, function<void(void)> callback = [](){}) :
|
||||
Explosion(unsigned int intensity, string explosionType, Coords location, function<void(void)> callback = [](){}) :
|
||||
Command(callback),
|
||||
location(location),
|
||||
intensity(intensity)
|
||||
intensity(intensity),
|
||||
explosionType(explosionType)
|
||||
{
|
||||
priority = CommandPriority::MEDIUM;
|
||||
};
|
||||
@ -423,4 +426,5 @@ public:
|
||||
private:
|
||||
const Coords location;
|
||||
const unsigned int intensity;
|
||||
const string explosionType;
|
||||
};
|
||||
|
||||
@ -48,6 +48,7 @@ namespace DataIndex {
|
||||
operateAs,
|
||||
shotsScatter,
|
||||
shotsIntensity,
|
||||
health,
|
||||
lastIndex,
|
||||
endOfData = 255
|
||||
};
|
||||
|
||||
@ -1,19 +0,0 @@
|
||||
#pragma once
|
||||
#include "framework.h"
|
||||
|
||||
class Measure
|
||||
{
|
||||
public:
|
||||
Measure(json::value value, long long time): value(value), time(time) {};
|
||||
|
||||
void setValue(json::value newValue) { value = newValue; }
|
||||
void setTime(long long newTime) { time = newTime; }
|
||||
json::value getValue() { return value; }
|
||||
long long getTime() { return time; }
|
||||
|
||||
private:
|
||||
json::value value;
|
||||
long long time;
|
||||
|
||||
};
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
#include "utils.h"
|
||||
#include "dcstools.h"
|
||||
#include "luatools.h"
|
||||
#include "measure.h"
|
||||
#include "logger.h"
|
||||
#include "commands.h"
|
||||
#include "datatypes.h"
|
||||
@ -107,6 +106,7 @@ public:
|
||||
virtual void setOperateAs(unsigned char newValue) { updateValue(operateAs, newValue, DataIndex::operateAs); }
|
||||
virtual void setShotsScatter(unsigned char newValue) { updateValue(shotsScatter, newValue, DataIndex::shotsScatter); }
|
||||
virtual void setShotsIntensity(unsigned char newValue) { updateValue(shotsIntensity, newValue, DataIndex::shotsIntensity); }
|
||||
virtual void setHealth(unsigned char newValue) { updateValue(health, newValue, DataIndex::health); }
|
||||
|
||||
/********** Getters **********/
|
||||
virtual string getCategory() { return category; };
|
||||
@ -152,6 +152,7 @@ public:
|
||||
virtual unsigned char getOperateAs() { return operateAs; }
|
||||
virtual unsigned char getShotsScatter() { return shotsScatter; }
|
||||
virtual unsigned char getShotsIntensity() { return shotsIntensity; }
|
||||
virtual unsigned char getHealth() { return health; }
|
||||
|
||||
protected:
|
||||
unsigned int ID;
|
||||
@ -200,6 +201,7 @@ protected:
|
||||
Coords activeDestination = Coords(NULL);
|
||||
unsigned char shotsScatter = 2;
|
||||
unsigned char shotsIntensity = 2;
|
||||
unsigned char health = 100;
|
||||
|
||||
/********** Other **********/
|
||||
unsigned int taskCheckCounter = 0;
|
||||
|
||||
@ -20,7 +20,7 @@ public:
|
||||
void update(json::value& missionData, double dt);
|
||||
void runAILoop();
|
||||
void getUnitData(stringstream &ss, unsigned long long time);
|
||||
void deleteUnit(unsigned int ID, bool explosion, bool immediate);
|
||||
void deleteUnit(unsigned int ID, bool explosion, string explosionType, bool immediate);
|
||||
void acquireControl(unsigned int ID);
|
||||
void loadDatabases();
|
||||
Unit* getClosestUnit(Unit* unit, unsigned char coalition, vector<string> categories, double &distance);
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
#include "utils.h"
|
||||
#include "dcstools.h"
|
||||
#include "luatools.h"
|
||||
#include "measure.h"
|
||||
#include "logger.h"
|
||||
#include "commands.h"
|
||||
#include "datatypes.h"
|
||||
|
||||
@ -165,7 +165,8 @@ string Delete::getString()
|
||||
commandSS.precision(10);
|
||||
commandSS << "Olympus.delete, "
|
||||
<< ID << ", "
|
||||
<< (explosion ? "true" : "false");
|
||||
<< (explosion ? "true" : "false") << ", "
|
||||
<< "\"" << explosionType << "\"";
|
||||
return commandSS.str();
|
||||
}
|
||||
|
||||
@ -244,6 +245,7 @@ string Explosion::getString()
|
||||
commandSS.precision(10);
|
||||
commandSS << "Olympus.explosion, "
|
||||
<< intensity << ", "
|
||||
<< "\"" << explosionType << "\"" << ", "
|
||||
<< location.lat << ", "
|
||||
<< location.lng;
|
||||
return commandSS.str();
|
||||
|
||||
@ -408,10 +408,11 @@ void Scheduler::handleRequest(string key, json::value value, string username, js
|
||||
{
|
||||
unsigned int ID = value[L"ID"].as_integer();
|
||||
bool explosion = value[L"explosion"].as_bool();
|
||||
string explosionType = to_string(value[L"explosionType"]);
|
||||
bool immediate = value[L"immediate"].as_bool();
|
||||
Unit* unit = unitsManager->getUnit(ID);
|
||||
if (unit != nullptr) {
|
||||
unitsManager->deleteUnit(ID, explosion, immediate);
|
||||
unitsManager->deleteUnit(ID, explosion, explosionType, immediate);
|
||||
log(username + " deleted unit " + unit->getUnitName() + "(" + unit->getName() + ")", true);
|
||||
}
|
||||
}
|
||||
@ -498,11 +499,12 @@ void Scheduler::handleRequest(string key, json::value value, string username, js
|
||||
else if (key.compare("explosion") == 0)
|
||||
{
|
||||
unsigned int intensity = value[L"intensity"].as_integer();
|
||||
string explosionType = to_string(value[L"explosionType"]);
|
||||
double lat = value[L"location"][L"lat"].as_double();
|
||||
double lng = value[L"location"][L"lng"].as_double();
|
||||
log("Adding " + to_string(intensity) + " explosion at (" + to_string(lat) + ", " + to_string(lng) + ")");
|
||||
log("Adding explosion of type " + explosionType + " at (" + to_string(lat) + ", " + to_string(lng) + ")");
|
||||
Coords loc; loc.lat = lat; loc.lng = lng;
|
||||
command = dynamic_cast<Command*>(new Explosion(intensity, loc));
|
||||
command = dynamic_cast<Command*>(new Explosion(intensity, explosionType, loc));
|
||||
}
|
||||
/************************/
|
||||
else if (key.compare("bombPoint") == 0)
|
||||
|
||||
@ -137,6 +137,9 @@ void Unit::update(json::value json, double dt)
|
||||
if (json.has_boolean_field(L"hasTask"))
|
||||
setHasTask(json[L"hasTask"].as_bool());
|
||||
|
||||
if (json.has_number_field(L"health"))
|
||||
setHealth(static_cast<unsigned char>(json[L"health"].as_number().to_uint32()));
|
||||
|
||||
runAILoop();
|
||||
}
|
||||
|
||||
@ -241,49 +244,50 @@ void Unit::getData(stringstream& ss, unsigned long long time)
|
||||
{
|
||||
if (checkFreshness(datumIndex, time)) {
|
||||
switch (datumIndex) {
|
||||
case DataIndex::category: appendString(ss, datumIndex, category); break;
|
||||
case DataIndex::alive: appendNumeric(ss, datumIndex, alive); break;
|
||||
case DataIndex::human: appendNumeric(ss, datumIndex, human); break;
|
||||
case DataIndex::controlled: appendNumeric(ss, datumIndex, controlled); break;
|
||||
case DataIndex::coalition: appendNumeric(ss, datumIndex, coalition); break;
|
||||
case DataIndex::country: appendNumeric(ss, datumIndex, country); break;
|
||||
case DataIndex::name: appendString(ss, datumIndex, name); break;
|
||||
case DataIndex::unitName: appendString(ss, datumIndex, unitName); break;
|
||||
case DataIndex::groupName: appendString(ss, datumIndex, groupName); break;
|
||||
case DataIndex::state: appendNumeric(ss, datumIndex, state); break;
|
||||
case DataIndex::task: appendString(ss, datumIndex, task); break;
|
||||
case DataIndex::hasTask: appendNumeric(ss, datumIndex, hasTask); break;
|
||||
case DataIndex::position: appendNumeric(ss, datumIndex, position); break;
|
||||
case DataIndex::speed: appendNumeric(ss, datumIndex, speed); break;
|
||||
case DataIndex::horizontalVelocity: appendNumeric(ss, datumIndex, horizontalVelocity); break;
|
||||
case DataIndex::verticalVelocity: appendNumeric(ss, datumIndex, verticalVelocity); break;
|
||||
case DataIndex::heading: appendNumeric(ss, datumIndex, heading); break;
|
||||
case DataIndex::isActiveTanker: appendNumeric(ss, datumIndex, isActiveTanker); break;
|
||||
case DataIndex::isActiveAWACS: appendNumeric(ss, datumIndex, isActiveAWACS); break;
|
||||
case DataIndex::onOff: appendNumeric(ss, datumIndex, onOff); break;
|
||||
case DataIndex::followRoads: appendNumeric(ss, datumIndex, followRoads); break;
|
||||
case DataIndex::fuel: appendNumeric(ss, datumIndex, fuel); break;
|
||||
case DataIndex::desiredSpeed: appendNumeric(ss, datumIndex, desiredSpeed); break;
|
||||
case DataIndex::desiredSpeedType: appendNumeric(ss, datumIndex, desiredSpeedType); break;
|
||||
case DataIndex::desiredAltitude: appendNumeric(ss, datumIndex, desiredAltitude); break;
|
||||
case DataIndex::desiredAltitudeType: appendNumeric(ss, datumIndex, desiredAltitudeType); break;
|
||||
case DataIndex::leaderID: appendNumeric(ss, datumIndex, leaderID); break;
|
||||
case DataIndex::formationOffset: appendNumeric(ss, datumIndex, formationOffset); break;
|
||||
case DataIndex::targetID: appendNumeric(ss, datumIndex, targetID); break;
|
||||
case DataIndex::targetPosition: appendNumeric(ss, datumIndex, targetPosition); break;
|
||||
case DataIndex::ROE: appendNumeric(ss, datumIndex, ROE); break;
|
||||
case DataIndex::reactionToThreat: appendNumeric(ss, datumIndex, reactionToThreat); break;
|
||||
case DataIndex::emissionsCountermeasures: appendNumeric(ss, datumIndex, emissionsCountermeasures); break;
|
||||
case DataIndex::TACAN: appendNumeric(ss, datumIndex, TACAN); break;
|
||||
case DataIndex::radio: appendNumeric(ss, datumIndex, radio); break;
|
||||
case DataIndex::generalSettings: appendNumeric(ss, datumIndex, generalSettings); break;
|
||||
case DataIndex::ammo: appendVector(ss, datumIndex, ammo); break;
|
||||
case DataIndex::contacts: appendVector(ss, datumIndex, contacts); break;
|
||||
case DataIndex::activePath: appendList(ss, datumIndex, activePath); break;
|
||||
case DataIndex::isLeader: appendNumeric(ss, datumIndex, isLeader); break;
|
||||
case DataIndex::operateAs: appendNumeric(ss, datumIndex, operateAs); break;
|
||||
case DataIndex::shotsScatter: appendNumeric(ss, datumIndex, shotsScatter); break;
|
||||
case DataIndex::shotsIntensity: appendNumeric(ss, datumIndex, shotsIntensity); break;
|
||||
case DataIndex::category: appendString(ss, datumIndex, category); break;
|
||||
case DataIndex::alive: appendNumeric(ss, datumIndex, alive); break;
|
||||
case DataIndex::human: appendNumeric(ss, datumIndex, human); break;
|
||||
case DataIndex::controlled: appendNumeric(ss, datumIndex, controlled); break;
|
||||
case DataIndex::coalition: appendNumeric(ss, datumIndex, coalition); break;
|
||||
case DataIndex::country: appendNumeric(ss, datumIndex, country); break;
|
||||
case DataIndex::name: appendString(ss, datumIndex, name); break;
|
||||
case DataIndex::unitName: appendString(ss, datumIndex, unitName); break;
|
||||
case DataIndex::groupName: appendString(ss, datumIndex, groupName); break;
|
||||
case DataIndex::state: appendNumeric(ss, datumIndex, state); break;
|
||||
case DataIndex::task: appendString(ss, datumIndex, task); break;
|
||||
case DataIndex::hasTask: appendNumeric(ss, datumIndex, hasTask); break;
|
||||
case DataIndex::position: appendNumeric(ss, datumIndex, position); break;
|
||||
case DataIndex::speed: appendNumeric(ss, datumIndex, speed); break;
|
||||
case DataIndex::horizontalVelocity: appendNumeric(ss, datumIndex, horizontalVelocity); break;
|
||||
case DataIndex::verticalVelocity: appendNumeric(ss, datumIndex, verticalVelocity); break;
|
||||
case DataIndex::heading: appendNumeric(ss, datumIndex, heading); break;
|
||||
case DataIndex::isActiveTanker: appendNumeric(ss, datumIndex, isActiveTanker); break;
|
||||
case DataIndex::isActiveAWACS: appendNumeric(ss, datumIndex, isActiveAWACS); break;
|
||||
case DataIndex::onOff: appendNumeric(ss, datumIndex, onOff); break;
|
||||
case DataIndex::followRoads: appendNumeric(ss, datumIndex, followRoads); break;
|
||||
case DataIndex::fuel: appendNumeric(ss, datumIndex, fuel); break;
|
||||
case DataIndex::desiredSpeed: appendNumeric(ss, datumIndex, desiredSpeed); break;
|
||||
case DataIndex::desiredSpeedType: appendNumeric(ss, datumIndex, desiredSpeedType); break;
|
||||
case DataIndex::desiredAltitude: appendNumeric(ss, datumIndex, desiredAltitude); break;
|
||||
case DataIndex::desiredAltitudeType: appendNumeric(ss, datumIndex, desiredAltitudeType); break;
|
||||
case DataIndex::leaderID: appendNumeric(ss, datumIndex, leaderID); break;
|
||||
case DataIndex::formationOffset: appendNumeric(ss, datumIndex, formationOffset); break;
|
||||
case DataIndex::targetID: appendNumeric(ss, datumIndex, targetID); break;
|
||||
case DataIndex::targetPosition: appendNumeric(ss, datumIndex, targetPosition); break;
|
||||
case DataIndex::ROE: appendNumeric(ss, datumIndex, ROE); break;
|
||||
case DataIndex::reactionToThreat: appendNumeric(ss, datumIndex, reactionToThreat); break;
|
||||
case DataIndex::emissionsCountermeasures: appendNumeric(ss, datumIndex, emissionsCountermeasures); break;
|
||||
case DataIndex::TACAN: appendNumeric(ss, datumIndex, TACAN); break;
|
||||
case DataIndex::radio: appendNumeric(ss, datumIndex, radio); break;
|
||||
case DataIndex::generalSettings: appendNumeric(ss, datumIndex, generalSettings); break;
|
||||
case DataIndex::ammo: appendVector(ss, datumIndex, ammo); break;
|
||||
case DataIndex::contacts: appendVector(ss, datumIndex, contacts); break;
|
||||
case DataIndex::activePath: appendList(ss, datumIndex, activePath); break;
|
||||
case DataIndex::isLeader: appendNumeric(ss, datumIndex, isLeader); break;
|
||||
case DataIndex::operateAs: appendNumeric(ss, datumIndex, operateAs); break;
|
||||
case DataIndex::shotsScatter: appendNumeric(ss, datumIndex, shotsScatter); break;
|
||||
case DataIndex::shotsIntensity: appendNumeric(ss, datumIndex, shotsIntensity); break;
|
||||
case DataIndex::health: appendNumeric(ss, datumIndex, health); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -534,6 +538,12 @@ void Unit::setTACAN(DataTypes::TACAN newTACAN, bool force)
|
||||
TACAN = newTACAN;
|
||||
if (TACAN.isOn) {
|
||||
std::ostringstream commandSS;
|
||||
|
||||
if (TACAN.channel < 0)
|
||||
TACAN.channel = 0;
|
||||
if (TACAN.channel > 126)
|
||||
TACAN.channel = 126;
|
||||
|
||||
commandSS << "{"
|
||||
<< "id = 'ActivateBeacon',"
|
||||
<< "params = {"
|
||||
@ -571,6 +581,12 @@ void Unit::setRadio(DataTypes::Radio newRadio, bool force)
|
||||
std::ostringstream commandSS;
|
||||
Command* command;
|
||||
|
||||
if (radio.frequency < 0)
|
||||
radio.frequency = 0;
|
||||
|
||||
if (radio.frequency > 999000000)
|
||||
radio.frequency = 999000000;
|
||||
|
||||
commandSS << "{"
|
||||
<< "id = 'SetFrequency',"
|
||||
<< "params = {"
|
||||
|
||||
@ -142,11 +142,11 @@ void UnitsManager::getUnitData(stringstream &ss, unsigned long long time)
|
||||
p.second->getData(ss, time);
|
||||
}
|
||||
|
||||
void UnitsManager::deleteUnit(unsigned int ID, bool explosion, bool immediate)
|
||||
void UnitsManager::deleteUnit(unsigned int ID, bool explosion, string explosionType, bool immediate)
|
||||
{
|
||||
if (getUnit(ID) != nullptr)
|
||||
{
|
||||
Command* command = dynamic_cast<Command*>(new Delete(ID, explosion, immediate));
|
||||
Command* command = dynamic_cast<Command*>(new Delete(ID, explosion, explosionType, immediate));
|
||||
scheduler->appendCommand(command);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#define VERSION "v0.4.5-alpha"
|
||||
#define VERSION "v0.4.6-alpha"
|
||||
#define LOG_NAME "Olympus_log.txt"
|
||||
#define REST_ADDRESS "http://localhost:30000"
|
||||
#define REST_URI "olympus"
|
||||
|
||||