Compare commits
104 Commits
v0.4.8-alp
...
v0.4.10-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d03c6a767 | ||
|
|
e29fdfd8c7 | ||
|
|
af619c3687 | ||
|
|
6afb6682ea | ||
|
|
9c2c7f45c6 | ||
|
|
7fbc19e90a | ||
|
|
8f2f73dc0e | ||
|
|
24a1681337 | ||
|
|
304e8ba3a0 | ||
|
|
45b840fa72 | ||
|
|
82704f042b | ||
|
|
87d71da55e | ||
|
|
71cf7c67f7 | ||
|
|
62142ed976 | ||
|
|
f56bd514dc | ||
|
|
e30f161d1d | ||
|
|
720bfb3118 | ||
|
|
4100a3cc67 | ||
|
|
942993ff6d | ||
|
|
fbf435c799 | ||
|
|
8fdb0d82e8 | ||
|
|
dcbebab61b | ||
|
|
7b10afae43 | ||
|
|
81d8a88abd | ||
|
|
cb14158a0f | ||
|
|
38fc60352a | ||
|
|
3748f0d6e3 | ||
|
|
edd626d5e5 | ||
|
|
f785e75686 | ||
|
|
9a46ed70b7 | ||
|
|
532c09a815 | ||
|
|
e430d38c63 | ||
|
|
289b36de6d | ||
|
|
7e01f40e5c | ||
|
|
cac4bb5f13 | ||
|
|
c8bb041887 | ||
|
|
9302c5c6d1 | ||
|
|
84a3313f2c | ||
|
|
1eb3beeb2e | ||
|
|
d0e6ef8c6c | ||
|
|
5da1df7e20 | ||
|
|
16fa6d9805 | ||
|
|
0ca7766689 | ||
|
|
4342575418 | ||
|
|
4cdb73a60d | ||
|
|
a23c53bcb8 | ||
|
|
19ac4f92e0 | ||
|
|
0b8f48b4fd | ||
|
|
d2fa94a6cb | ||
|
|
da1c674911 | ||
|
|
7184bc1eb2 | ||
|
|
37f30a0f1a | ||
|
|
04f4b3e78a | ||
|
|
dbdc162fae | ||
|
|
563f673fb3 | ||
|
|
423e799a82 | ||
|
|
4734b89414 | ||
|
|
6f708e7733 | ||
|
|
f0c3f95189 | ||
|
|
93ca0e3f22 | ||
|
|
d94432636f | ||
|
|
60fca35d80 | ||
|
|
db71462d1c | ||
|
|
86f522176a | ||
|
|
94ee71c48f | ||
|
|
fda0b21fb0 | ||
|
|
775148cec8 | ||
|
|
f2dc4a1a00 | ||
|
|
eda5723a3c | ||
|
|
b044d9a6c0 | ||
|
|
125d5396b9 | ||
|
|
17b8b35a43 | ||
|
|
4bd017e3c7 | ||
|
|
37447a7fd3 | ||
|
|
1ed1dec65e | ||
|
|
5e2e465813 | ||
|
|
335655406e | ||
|
|
bf12d6330c | ||
|
|
3cca3187ad | ||
|
|
f4388a2cff | ||
|
|
7cf5d324eb | ||
|
|
ec9ef2b0fb | ||
|
|
6c852d93a9 | ||
|
|
290067932e | ||
|
|
4aa3a43604 | ||
|
|
3c33696452 | ||
|
|
3ea1ab8f30 | ||
|
|
76801f773d | ||
|
|
de7eeec94a | ||
|
|
0723a3ab95 | ||
|
|
8ef48ad977 | ||
|
|
ed24d1af60 | ||
|
|
e3dffb8245 | ||
|
|
74310a5ad3 | ||
|
|
a2223165e8 | ||
|
|
a879761dc2 | ||
|
|
210c1fbecf | ||
|
|
b89c5142b6 | ||
|
|
0421f6b8fe | ||
|
|
9ef6efa3e0 | ||
|
|
b43afd4e9c | ||
|
|
7700aa2030 | ||
|
|
e68683acb7 | ||
|
|
8dc48c10c3 |
14
LEGAL
@@ -2,7 +2,8 @@ DCS Olympus
|
||||
|
||||
A real-time AI unit control mod for DCS World
|
||||
|
||||
Copyright (C) 2023 Veltro & Gang
|
||||
Copyright (C) 2023 Veltro & Gang (the "DCS Olympus Team" or the
|
||||
"Rightsholders")
|
||||
|
||||
DCS Olympus (the "MATERIAL" or "Software") is provided completely free
|
||||
to users subject to the it under both the terms of version 3 of the GNU
|
||||
@@ -665,4 +666,13 @@ you, the licensee, and the authors and/or copyright holders of the
|
||||
Software with respect to the subject matter to which it pertains.
|
||||
It supersedes all prior agreements and understandings (if applicable),
|
||||
oral or written, with respect to such matters.
|
||||
|
||||
|
||||
3. Unilateral Modification
|
||||
|
||||
The parties agree that the DCS Olympus Team shall have the right to
|
||||
unilaterally modify these terms (i.e. the agreement between you and the
|
||||
DCS Olympus Team for the use of the Software), and that parties shall
|
||||
be bound by such terms as modified from time to time. The DCS Olympus Team
|
||||
shall not have an obligation to inform you of such modification, save that
|
||||
such changes will be published on the DCS Olympus Github Repository located at
|
||||
https://github.com/Pax1601/DCSOlympus.
|
||||
|
||||
31
README.md
@@ -1,4 +1,4 @@
|
||||
# Important note: DCS Olympus is in alpha state. No official release has been produced yet. The first public version is planned for Q4 2023.
|
||||
# Important note: DCS Olympus is in alpha state. No official release has been produced yet. The first public version is planned for mid december 2023.
|
||||
|
||||
# DCS Olympus
|
||||
*A real-time web interface to spawn and control units in DCS World*
|
||||
@@ -6,32 +6,19 @@
|
||||

|
||||
|
||||
### What is this?
|
||||
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.
|
||||
DCS: Olympus is a free and open-source mod for DCS that enables dynamic real-time control through a map interface. The user is able to spawn units/groups, deploy a variety of effects such as smoke, flares, or explosions, and waypoints/tasks can be given to AI units in real-time in a way similar to a classic RTS game.
|
||||
|
||||
### Features and how to use it
|
||||
- Spawn air and ground units, with preset loadouts
|
||||
- Double click on the map to spawn a blue and red units, both in the air and in the ground, with preset loadouts for air-to-air or air-to-ground tasks;
|
||||
- Control units
|
||||
- Select one ore more units to move them around. Hold down ctrl and click to create a route for the unit to follow;
|
||||
- Attack other units
|
||||
- After selecting one ore more units, double click on another unit and select "Attack" to attack it, depending on the available weapons.
|
||||
Additionally Olympus is able to run several effects and unit behaviours beyond the core DCS offerings. This includes such things as napalm and white phosphosous explosions, or setting up AA units to fire at players and miss, and more.
|
||||
|
||||
It even includes Red and Blue modes which limit your view and powers to just seeing what your coalition sees, with a spawning budget you could play against your friends even with no-one in the game piloting, or have a Red commander working against a squadron of blue pilots, and/or a blue commander working with them.
|
||||
|
||||
Even better it requires no client mods be installed if used on a server
|
||||
|
||||
The full feature list is simply too long to enumerate in a short summary but needless to say Olympus offers up a lot of unique gameplay that has previously not existed, and enhances many other elements of DCS in exciting ways
|
||||
|
||||
### Installing DCS Olympus
|
||||
A prebuilt installer will soon be released and available here
|
||||
|
||||
### Building DCS Olympus
|
||||
DCS Olympus is comprised of two modules:
|
||||
|
||||
A "core" c++ .dll module, which is run by DCS and exposes all the necessary data, and provides endpoints for commands from a REST server. A Visual Studio 2017/2019/2022 solution is provided, and requires no additional configuration. The core dll solution has two dependencies, both can be installed using vcpkg (https://vcpkg.io/en/getting-started.html):
|
||||
- cpprestsdk: `vcpkg install cpprestsdk:x64-windows`
|
||||
- geographiclib: `vcpkg install geographiclib:x64-windows`
|
||||
|
||||
|
||||
A "client" node.js typescript web app, which can be hosted on the server using express.js. A Visual Studio Code configuration is provided for debugging. The client requires node.js to be installed for building (https://nodejs.org/en/). After installing node.js, move in the client folder and run the following commands:
|
||||
- `npm install`
|
||||
- `npm -g install`
|
||||
|
||||
After installing all the necessary dependencies you can start a development server executing the *client/debug.bat* batch file, and visiting http:\\localhost:3000 with any modern browser (tested with updated Chrome, Firefox and Edge). However, it is highly suggested to simply run the `Launch Chrome against localhost` debug configuration in Visual Studio Code.
|
||||
|
||||
|
||||
|
||||
|
||||
28
build.bat
Normal file
@@ -0,0 +1,28 @@
|
||||
call git clean -fx
|
||||
|
||||
cd src
|
||||
msbuild olympus.sln /t:Rebuild /p:Configuration=Release
|
||||
cd ..
|
||||
|
||||
cd scripts/python/configurator
|
||||
call build_configurator.bat
|
||||
cd ../../..
|
||||
|
||||
cd client
|
||||
rmdir /s /q "hgt"
|
||||
call npm install
|
||||
call npm run emit-declarations
|
||||
call npm run build-release
|
||||
|
||||
cd "plugins\controltips"
|
||||
call npm install
|
||||
call npm run build-release
|
||||
cd "..\.."
|
||||
|
||||
cd "plugins\databasemanager"
|
||||
call npm install
|
||||
call npm run build-release
|
||||
cd "..\.."
|
||||
|
||||
call npm prune --production
|
||||
cd ..
|
||||
@@ -1,28 +1,2 @@
|
||||
cd src
|
||||
msbuild olympus.sln /t:Rebuild /p:Configuration=Release
|
||||
cd ..
|
||||
|
||||
cd scripts/python/configurator
|
||||
call build_configurator.bat
|
||||
cd ../../..
|
||||
|
||||
cd client
|
||||
rmdir /s /q "hgt"
|
||||
call npm install
|
||||
call npm run emit-declarations
|
||||
call npm run build-release
|
||||
|
||||
cd "plugins\controltips"
|
||||
call npm install
|
||||
call npm run build-release
|
||||
cd "..\.."
|
||||
|
||||
cd "plugins\databasemanager"
|
||||
call npm install
|
||||
call npm run build-release
|
||||
cd "..\.."
|
||||
|
||||
call npm prune --production
|
||||
cd ..
|
||||
|
||||
call build.bat
|
||||
call "C:\Program Files (x86)\Inno Setup 6\iscc.exe" "installer\olympus.iss"
|
||||
|
||||
211
client/@types/olympus/index.d.ts
vendored
@@ -150,6 +150,18 @@ declare module "constants/constants" {
|
||||
bounds: LatLngBounds;
|
||||
zoom: number;
|
||||
};
|
||||
Falklands: {
|
||||
bounds: LatLngBounds;
|
||||
zoom: number;
|
||||
};
|
||||
Normandy: {
|
||||
bounds: LatLngBounds;
|
||||
zoom: number;
|
||||
};
|
||||
SinaiMap: {
|
||||
bounds: LatLngBounds;
|
||||
zoom: number;
|
||||
};
|
||||
};
|
||||
export const mapLayers: {
|
||||
"ArcGIS Satellite": {
|
||||
@@ -200,6 +212,7 @@ declare module "constants/constants" {
|
||||
export const IADSDensities: {
|
||||
[key: string]: number;
|
||||
};
|
||||
export const GROUND_UNIT_AIR_DEFENCE_REGEX: RegExp;
|
||||
export const HIDE_GROUP_MEMBERS = "Hide group members when zoomed out";
|
||||
export const SHOW_UNIT_LABELS = "Show unit labels (L)";
|
||||
export const SHOW_UNITS_ENGAGEMENT_RINGS = "Show units threat range rings (Q)";
|
||||
@@ -228,33 +241,34 @@ declare module "constants/constants" {
|
||||
horizontalVelocity = 15,
|
||||
verticalVelocity = 16,
|
||||
heading = 17,
|
||||
isActiveTanker = 18,
|
||||
isActiveAWACS = 19,
|
||||
onOff = 20,
|
||||
followRoads = 21,
|
||||
fuel = 22,
|
||||
desiredSpeed = 23,
|
||||
desiredSpeedType = 24,
|
||||
desiredAltitude = 25,
|
||||
desiredAltitudeType = 26,
|
||||
leaderID = 27,
|
||||
formationOffset = 28,
|
||||
targetID = 29,
|
||||
targetPosition = 30,
|
||||
ROE = 31,
|
||||
reactionToThreat = 32,
|
||||
emissionsCountermeasures = 33,
|
||||
TACAN = 34,
|
||||
radio = 35,
|
||||
generalSettings = 36,
|
||||
ammo = 37,
|
||||
contacts = 38,
|
||||
activePath = 39,
|
||||
isLeader = 40,
|
||||
operateAs = 41,
|
||||
shotsScatter = 42,
|
||||
shotsIntensity = 43,
|
||||
health = 44,
|
||||
track = 18,
|
||||
isActiveTanker = 19,
|
||||
isActiveAWACS = 20,
|
||||
onOff = 21,
|
||||
followRoads = 22,
|
||||
fuel = 23,
|
||||
desiredSpeed = 24,
|
||||
desiredSpeedType = 25,
|
||||
desiredAltitude = 26,
|
||||
desiredAltitudeType = 27,
|
||||
leaderID = 28,
|
||||
formationOffset = 29,
|
||||
targetID = 30,
|
||||
targetPosition = 31,
|
||||
ROE = 32,
|
||||
reactionToThreat = 33,
|
||||
emissionsCountermeasures = 34,
|
||||
TACAN = 35,
|
||||
radio = 36,
|
||||
generalSettings = 37,
|
||||
ammo = 38,
|
||||
contacts = 39,
|
||||
activePath = 40,
|
||||
isLeader = 41,
|
||||
operateAs = 42,
|
||||
shotsScatter = 43,
|
||||
shotsIntensity = 44,
|
||||
health = 45,
|
||||
endOfData = 255
|
||||
}
|
||||
export const MGRS_PRECISION_10KM = 2;
|
||||
@@ -265,6 +279,9 @@ declare module "constants/constants" {
|
||||
export const DELETE_CYCLE_TIME = 0.05;
|
||||
export const DELETE_SLOW_THRESHOLD = 50;
|
||||
export const GROUPING_ZOOM_TRANSITION = 13;
|
||||
export const MAX_SHOTS_SCATTER = 3;
|
||||
export const MAX_SHOTS_INTENSITY = 3;
|
||||
export const SHOTS_SCATTER_DEGREES = 10;
|
||||
}
|
||||
declare module "map/markers/custommarker" {
|
||||
import { Map, Marker } from "leaflet";
|
||||
@@ -315,15 +332,40 @@ declare module "controls/dropdown" {
|
||||
#private;
|
||||
constructor(ID: string | null, callback: CallableFunction, options?: string[] | null, defaultText?: string);
|
||||
getContainer(): HTMLElement;
|
||||
setOptions(optionsList: string[], sort?: "" | "string" | "number" | "string+number"): void;
|
||||
/** Set the dropdown options strings
|
||||
*
|
||||
* @param optionsList List of options. These are the keys that will always be returned on selection
|
||||
* @param sort Sort method. "string" performs js default sort. "number" sorts purely by numeric value.
|
||||
* "string+number" sorts by string, unless two elements are lexicographically identical up to a numeric value (e.g. "SA-2" and "SA-3"), in which case it sorts by number.
|
||||
* @param labelsList (Optional) List of labels to be shown instead of the keys directly. If provided, the options will be sorted by label.
|
||||
*/
|
||||
setOptions(optionsList: string[], sort?: "" | "string" | "number" | "string+number", labelsList?: string[] | undefined): void;
|
||||
getOptionsList(): string[];
|
||||
getLabelsList(): string[] | undefined;
|
||||
/** Manually set the HTMLElements of the dropdown values. Handling of the selection must be performed externally.
|
||||
*
|
||||
* @param optionsElements List of elements to be added to the dropdown
|
||||
*/
|
||||
setOptionsElements(optionsElements: HTMLElement[]): void;
|
||||
getOptionElements(): HTMLCollection;
|
||||
addOptionElement(optionElement: HTMLElement): void;
|
||||
selectText(text: string): void;
|
||||
/** Select the active value of the dropdown
|
||||
*
|
||||
* @param idx The index of the element to select
|
||||
* @returns True if the index is valid, false otherwise
|
||||
*/
|
||||
selectValue(idx: number): boolean;
|
||||
reset(): void;
|
||||
getValue(): string;
|
||||
/** Manually set the selected value of the dropdown
|
||||
*
|
||||
* @param value The value to select. Must be one of the valid options
|
||||
*/
|
||||
setValue(value: string): void;
|
||||
getValue(): string;
|
||||
/** Force the selected value of the dropdown.
|
||||
*
|
||||
* @param value Any string. Will be shown as selected value even if not one of the options.
|
||||
*/
|
||||
forceValue(value: string): void;
|
||||
getIndex(): number;
|
||||
clip(): void;
|
||||
@@ -497,6 +539,7 @@ declare module "interfaces" {
|
||||
}
|
||||
export interface UnitData {
|
||||
category: string;
|
||||
categoryDisplayName: string;
|
||||
ID: number;
|
||||
alive: boolean;
|
||||
human: boolean;
|
||||
@@ -514,6 +557,7 @@ declare module "interfaces" {
|
||||
horizontalVelocity: number;
|
||||
verticalVelocity: number;
|
||||
heading: number;
|
||||
track: number;
|
||||
isActiveTanker: boolean;
|
||||
isActiveAWACS: boolean;
|
||||
onOff: boolean;
|
||||
@@ -786,7 +830,7 @@ declare module "other/utils" {
|
||||
export function enumToCoalition(coalitionID: number): "" | "blue" | "red" | "neutral";
|
||||
export function coalitionToEnum(coalition: string): 0 | 1 | 2;
|
||||
export function convertDateAndTimeToDate(dateAndTime: DateAndTime): Date;
|
||||
export function createCheckboxOption(value: string, text: string, checked?: boolean, callback?: CallableFunction): HTMLElement;
|
||||
export function createCheckboxOption(value: string, text: string, checked?: boolean, callback?: CallableFunction, options?: any): HTMLElement;
|
||||
export function getCheckboxOptions(dropdown: Dropdown): {
|
||||
[key: string]: boolean;
|
||||
};
|
||||
@@ -814,17 +858,20 @@ declare module "controls/unitspawnmenu" {
|
||||
import { UnitDatabase } from "unit/databases/unitdatabase";
|
||||
import { Airbase } from "mission/airbase";
|
||||
import { UnitSpawnOptions } from "interfaces";
|
||||
export class UnitSpawnMenu {
|
||||
/** This is the common code for all the unit spawn menus. It is shown both when right clicking on the map and when spawning from airbase.
|
||||
*
|
||||
*/
|
||||
export abstract class UnitSpawnMenu {
|
||||
#private;
|
||||
protected showRangeCircles: boolean;
|
||||
protected spawnOptions: UnitSpawnOptions;
|
||||
protected unitTypeFilter: (unit: any) => boolean;
|
||||
protected spawnOptions: UnitSpawnOptions;
|
||||
constructor(ID: string, unitDatabase: UnitDatabase, orderByRole: boolean);
|
||||
abstract deployUnits(spawnOptions: UnitSpawnOptions, unitsCount: number): void;
|
||||
getContainer(): HTMLElement;
|
||||
getVisible(): boolean;
|
||||
reset(): void;
|
||||
setCountries(): void;
|
||||
refreshOptions(): void;
|
||||
showCirclesPreviews(): void;
|
||||
clearCirclesPreviews(): void;
|
||||
setAirbase(airbase: Airbase | undefined): void;
|
||||
@@ -838,7 +885,8 @@ declare module "controls/unitspawnmenu" {
|
||||
getLiveryDropdown(): Dropdown;
|
||||
getLoadoutPreview(): HTMLDivElement;
|
||||
getAltitudeSlider(): Slider;
|
||||
deployUnits(spawnOptions: UnitSpawnOptions, unitsCount: number): void;
|
||||
setShowLoadout(showLoadout: boolean): void;
|
||||
setShowAltitudeSlider(showAltitudeSlider: boolean): void;
|
||||
}
|
||||
export class AircraftSpawnMenu extends UnitSpawnMenu {
|
||||
/**
|
||||
@@ -1091,6 +1139,7 @@ declare module "unit/unit" {
|
||||
getHorizontalVelocity(): number;
|
||||
getVerticalVelocity(): number;
|
||||
getHeading(): number;
|
||||
getTrack(): number;
|
||||
getIsActiveAWACS(): boolean;
|
||||
getIsActiveTanker(): boolean;
|
||||
getOnOff(): boolean;
|
||||
@@ -1146,6 +1195,11 @@ declare module "unit/unit" {
|
||||
* @returns string containing the default marker
|
||||
*/
|
||||
abstract getDefaultMarker(): string;
|
||||
/** Get the category but for display use - for the user. (i.e. has spaces in it)
|
||||
*
|
||||
* @returns string
|
||||
*/
|
||||
getCategoryLabel(): string;
|
||||
/********************** 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
|
||||
*
|
||||
@@ -1219,6 +1273,8 @@ declare module "unit/unit" {
|
||||
setGroup(group: Group | null): void;
|
||||
drawLines(): void;
|
||||
checkZoomRedraw(): boolean;
|
||||
isControlledByDCS(): boolean;
|
||||
isControlledByOlympus(): boolean;
|
||||
/********************** Icon *************************/
|
||||
createIcon(): void;
|
||||
/********************** Visibility *************************/
|
||||
@@ -1676,22 +1732,63 @@ declare module "mission/missionmanager" {
|
||||
export class MissionManager {
|
||||
#private;
|
||||
constructor();
|
||||
/** Update location of bullseyes
|
||||
*
|
||||
* @param object <BulleyesData>
|
||||
*/
|
||||
updateBullseyes(data: BullseyesData): void;
|
||||
/** Update airbase information
|
||||
*
|
||||
* @param object <AirbasesData>
|
||||
*/
|
||||
updateAirbases(data: AirbasesData): void;
|
||||
/** Update mission information
|
||||
*
|
||||
* @param object <MissionData>
|
||||
*/
|
||||
updateMission(data: MissionData): void;
|
||||
/** Get the bullseyes set in this theatre
|
||||
*
|
||||
* @returns object
|
||||
*/
|
||||
getBullseyes(): {
|
||||
[name: string]: Bullseye;
|
||||
};
|
||||
/** Get the airbases in this theatre
|
||||
*
|
||||
* @returns object
|
||||
*/
|
||||
getAirbases(): {
|
||||
[name: string]: Airbase;
|
||||
};
|
||||
/** Get the options/settings as set in the command mode
|
||||
*
|
||||
* @returns object
|
||||
*/
|
||||
getCommandModeOptions(): CommandModeOptions;
|
||||
/** Get the current date and time of the mission (based on local time)
|
||||
*
|
||||
* @returns object
|
||||
*/
|
||||
getDateAndTime(): DateAndTime;
|
||||
/**
|
||||
* Get the number of seconds left of setup time
|
||||
* @returns number
|
||||
*/
|
||||
getRemainingSetupTime(): number;
|
||||
/** Get an object with the coalitions in it
|
||||
*
|
||||
* @returns object
|
||||
*/
|
||||
getCoalitions(): {
|
||||
red: string[];
|
||||
blue: string[];
|
||||
};
|
||||
/** Get the current theatre (map) name
|
||||
*
|
||||
* @returns string
|
||||
*/
|
||||
getTheatre(): string;
|
||||
getAvailableSpawnPoints(): number;
|
||||
getCommandedCoalition(): "blue" | "red" | "all";
|
||||
refreshSpawnPoints(): void;
|
||||
@@ -1860,6 +1957,43 @@ declare module "dialog/dialog" {
|
||||
show(): void;
|
||||
}
|
||||
}
|
||||
declare module "unit/importexport/unitdatafile" {
|
||||
import { Dialog } from "dialog/dialog";
|
||||
export abstract class UnitDataFile {
|
||||
#private;
|
||||
protected data: any;
|
||||
protected dialog: Dialog;
|
||||
constructor();
|
||||
buildCategoryCoalitionTable(): void;
|
||||
getData(): any;
|
||||
}
|
||||
}
|
||||
declare module "unit/importexport/unitdatafileexport" {
|
||||
import { Dialog } from "dialog/dialog";
|
||||
import { Unit } from "unit/unit";
|
||||
import { UnitDataFile } from "unit/importexport/unitdatafile";
|
||||
export class UnitDataFileExport extends UnitDataFile {
|
||||
#private;
|
||||
protected data: any;
|
||||
protected dialog: Dialog;
|
||||
constructor(elementId: string);
|
||||
/**
|
||||
* Show the form to start the export journey
|
||||
*/
|
||||
showForm(units: Unit[]): void;
|
||||
}
|
||||
}
|
||||
declare module "unit/importexport/unitdatafileimport" {
|
||||
import { Dialog } from "dialog/dialog";
|
||||
import { UnitDataFile } from "unit/importexport/unitdatafile";
|
||||
export class UnitDataFileImport extends UnitDataFile {
|
||||
#private;
|
||||
protected data: any;
|
||||
protected dialog: Dialog;
|
||||
constructor(elementId: string);
|
||||
selectFile(): void;
|
||||
}
|
||||
}
|
||||
declare module "unit/unitsmanager" {
|
||||
import { LatLng, LatLngBounds } from "leaflet";
|
||||
import { Unit } from "unit/unit";
|
||||
@@ -2102,7 +2236,7 @@ declare module "unit/unitsmanager" {
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
scenicAAA(units?: Unit[] | null): void;
|
||||
/** Instruct units to enter into miss on purpose mode. Units will aim to the nearest enemy unit but not precisely.
|
||||
/** Instruct units to enter into dynamic accuracy/miss on purpose mode. Units will aim to the nearest enemy unit but not precisely.
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
missOnPurpose(units?: Unit[] | null): void;
|
||||
@@ -2266,7 +2400,7 @@ declare module "server/servermanager" {
|
||||
constructor();
|
||||
toggleDemoEnabled(): void;
|
||||
setCredentials(newUsername: string, newPassword: string): void;
|
||||
GET(callback: CallableFunction, uri: string, options?: ServerRequestOptions, responseType?: string): void;
|
||||
GET(callback: CallableFunction, uri: string, options?: ServerRequestOptions, responseType?: string, force?: boolean): void;
|
||||
PUT(request: object, callback: CallableFunction): void;
|
||||
getConfig(callback: CallableFunction): void;
|
||||
setAddress(address: string, port: number): void;
|
||||
@@ -2334,6 +2468,9 @@ declare module "server/servermanager" {
|
||||
setPaused(newPaused: boolean): void;
|
||||
getPaused(): boolean;
|
||||
getServerIsPaused(): boolean;
|
||||
getRequests(): {
|
||||
[key: string]: XMLHttpRequest;
|
||||
};
|
||||
}
|
||||
}
|
||||
declare module "panels/unitlistpanel" {
|
||||
|
||||
@@ -112,5 +112,5 @@ function onListening() {
|
||||
debug('Listening on ' + bind);
|
||||
}
|
||||
|
||||
console.log("DCS Olympus server v0.4.8 started correctly!")
|
||||
console.log("DCS Olympus server v0.4.9-alpha-rc1 started correctly!")
|
||||
console.log("Waiting for connections...")
|
||||
|
||||
@@ -35,7 +35,7 @@ class DemoDataGenerator {
|
||||
|
||||
|
||||
let baseData = { alive: true, human: false, controlled: true, coalition: 2, country: 0, unitName: "Cool guy", groupName: "Cool group 1", state: 13, 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,
|
||||
hasTask: true, position: { lat: 37, lng: -116, alt: 1000 }, speed: 200, horizontalVelocity: 200, verticalVelicity: 0, heading: 45, track: 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,
|
||||
@@ -53,9 +53,9 @@ class DemoDataGenerator {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
|
||||
// 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));
|
||||
@@ -70,6 +70,7 @@ class DemoDataGenerator {
|
||||
DEMO_UNIT_DATA[idx].groupName = `Group-${idx}`;
|
||||
DEMO_UNIT_DATA[idx].position.lat += latIdx / 5;
|
||||
DEMO_UNIT_DATA[idx].position.lng += lngIdx / 5;
|
||||
DEMO_UNIT_DATA[idx].coalition = Math.floor(Math.random() * 3)
|
||||
|
||||
latIdx += 1;
|
||||
if (latIdx === l) {
|
||||
@@ -89,9 +90,9 @@ class DemoDataGenerator {
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
*/
|
||||
let idx = 1;
|
||||
DEMO_UNIT_DATA[idx] = JSON.parse(JSON.stringify(baseData));
|
||||
DEMO_UNIT_DATA[idx].name = "S_75M_Volhov";
|
||||
@@ -164,48 +165,50 @@ class DemoDataGenerator {
|
||||
if (req.query["time"] == 0){
|
||||
for (let idx in DEMO_UNIT_DATA) {
|
||||
const unit = DEMO_UNIT_DATA[idx];
|
||||
var dataIndex = 1;
|
||||
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.appendString(array, unit.category, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.alive, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.human, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.controlled, dataIndex); dataIndex++;
|
||||
array = this.appendUint16(array, unit.coalition, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.country, dataIndex); dataIndex++;
|
||||
array = this.appendString(array, unit.name, dataIndex); dataIndex++;
|
||||
array = this.appendString(array, unit.unitName, dataIndex); dataIndex++;
|
||||
array = this.appendString(array, unit.groupName, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.state, dataIndex); dataIndex++;
|
||||
array = this.appendString(array, unit.task, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.hasTask, dataIndex); dataIndex++;
|
||||
array = this.appendCoordinates(array, unit.position, dataIndex); dataIndex++;
|
||||
array = this.appendDouble(array, unit.speed, dataIndex); dataIndex++;
|
||||
array = this.appendDouble(array, unit.horizontalVelocity, dataIndex); dataIndex++;
|
||||
array = this.appendDouble(array, unit.verticalVelicity, dataIndex); dataIndex++;
|
||||
array = this.appendDouble(array, unit.heading, dataIndex); dataIndex++;
|
||||
array = this.appendDouble(array, unit.track, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.isActiveTanker, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.isActiveAWACS, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.onOff, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.followRoads, dataIndex); dataIndex++;
|
||||
array = this.appendUint16(array, unit.fuel, dataIndex); dataIndex++;
|
||||
array = this.appendDouble(array, unit.desiredSpeed, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.desiredSpeedType, dataIndex); dataIndex++;
|
||||
array = this.appendDouble(array, unit.desiredAltitude, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.desiredAltitudeType, dataIndex); dataIndex++;
|
||||
array = this.appendUint32(array, unit.leaderID, dataIndex); dataIndex++;
|
||||
array = this.appendOffset(array, unit.formationOffset, dataIndex); dataIndex++;
|
||||
array = this.appendUint32(array, unit.targetID, dataIndex); dataIndex++;
|
||||
array = this.appendCoordinates(array, unit.targetPosition, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.ROE, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.reactionToThreat, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.emissionsCountermeasures, dataIndex); dataIndex++;
|
||||
array = this.appendTACAN(array, unit.TACAN, dataIndex); dataIndex++;
|
||||
array = this.appendRadio(array, unit.radio, dataIndex); dataIndex++;
|
||||
array = this.appendRadio(array, unit.generalSettings, dataIndex); dataIndex++;
|
||||
array = this.appendAmmo(array, unit.ammo, dataIndex); dataIndex++;
|
||||
array = this.appendContacts(array, unit.contacts, dataIndex); dataIndex++;
|
||||
array = this.appendActivePath(array, unit.activePath, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.isLeader, dataIndex); dataIndex++;
|
||||
array = this.appendUint8(array, unit.operateAs, dataIndex); dataIndex++;
|
||||
array = this.concat(array, this.uint8ToByteArray(255));
|
||||
}
|
||||
}
|
||||
|
||||
45
client/package-lock.json
generated
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "DCSOlympus",
|
||||
"version": "v0.4.8-alpha",
|
||||
"version": "v0.4.9-alpha-rc1",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "DCSOlympus",
|
||||
"version": "v0.4.8-alpha",
|
||||
"version": "v0.4.9-alpha-rc1",
|
||||
"dependencies": {
|
||||
"@turf/turf": "^6.5.0",
|
||||
"body-parser": "^1.20.2",
|
||||
@@ -19,11 +19,12 @@
|
||||
"leaflet-gesture-handling": "^1.2.2",
|
||||
"morgan": "~1.9.1",
|
||||
"save": "^2.9.0",
|
||||
"srtm-elevation": "^2.1.2"
|
||||
"srtm-elevation": "^2.1.2",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.21.4",
|
||||
"@tanem/svg-injector": "^10.1.55",
|
||||
"@tanem/svg-injector": "^10.1.68",
|
||||
"@types/formatcoords": "^1.1.0",
|
||||
"@types/geojson": "^7946.0.10",
|
||||
"@types/leaflet": "^1.9.0",
|
||||
@@ -1714,12 +1715,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.5.tgz",
|
||||
"integrity": "sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==",
|
||||
"version": "7.23.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.5.tgz",
|
||||
"integrity": "sha512-NdUTHcPe4C99WxPub+K9l9tK5/lV4UXIoaHSYgzco9BCyjKAAwzdBI+wWtYqHt7LJdbo74ZjRPJgzVweq1sz0w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"regenerator-runtime": "^0.13.11"
|
||||
"regenerator-runtime": "^0.14.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -2015,14 +2016,14 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@tanem/svg-injector": {
|
||||
"version": "10.1.55",
|
||||
"resolved": "https://registry.npmjs.org/@tanem/svg-injector/-/svg-injector-10.1.55.tgz",
|
||||
"integrity": "sha512-xh8ejdvjDaH1eddZC0CdI45eeid4BIU2ppjNEhiTiWMYcLGT19KWjbES/ttDS4mq9gIAQfXx57g5zimEVohqYA==",
|
||||
"version": "10.1.68",
|
||||
"resolved": "https://registry.npmjs.org/@tanem/svg-injector/-/svg-injector-10.1.68.tgz",
|
||||
"integrity": "sha512-UkJajeR44u73ujtr5GVSbIlELDWD/mzjqWe54YMK61ljKxFcJoPd9RBSaO7xj02ISCWUqJW99GjrS+sVF0UnrA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.21.5",
|
||||
"@babel/runtime": "^7.23.2",
|
||||
"content-type": "^1.0.5",
|
||||
"tslib": "^2.5.0"
|
||||
"tslib": "^2.6.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@turf/along": {
|
||||
@@ -7672,9 +7673,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/regenerator-runtime": {
|
||||
"version": "0.13.11",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
|
||||
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
|
||||
"version": "0.14.0",
|
||||
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz",
|
||||
"integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/regenerator-transform": {
|
||||
@@ -8820,6 +8821,18 @@
|
||||
"node": ">= 0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/uuid": {
|
||||
"version": "9.0.1",
|
||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
|
||||
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
|
||||
"funding": [
|
||||
"https://github.com/sponsors/broofa",
|
||||
"https://github.com/sponsors/ctavan"
|
||||
],
|
||||
"bin": {
|
||||
"uuid": "dist/bin/uuid"
|
||||
}
|
||||
},
|
||||
"node_modules/vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
|
||||
@@ -2,11 +2,11 @@
|
||||
"name": "DCSOlympus",
|
||||
"node-main": "./bin/www",
|
||||
"main": "http://localhost:3000",
|
||||
"version": "v0.4.8-alpha",
|
||||
"version": "v0.4.9-alpha-rc1",
|
||||
"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",
|
||||
"build-release": "browserify .\\src\\index.ts -o .\\public\\javascripts\\bundle.js -t [ babelify --global true --presets [ @babel/preset-env ] --extensions '.js'] -p [ tsify --noImplicitAny ] -p [ tinyify ] && copy.bat",
|
||||
"build-release": "browserify .\\src\\index.ts -o .\\public\\javascripts\\bundle.js -t [ babelify --global true --presets [ @babel/preset-env ] --extensions '.js'] -p [ tsify --noImplicitAny ] && copy.bat",
|
||||
"emit-declarations": "tsc --project tsconfig.json --declaration --emitDeclarationOnly --outfile ./@types/olympus/index.d.ts",
|
||||
"copy": "copy.bat",
|
||||
"start": "node ./bin/www",
|
||||
@@ -25,11 +25,12 @@
|
||||
"leaflet-gesture-handling": "^1.2.2",
|
||||
"morgan": "~1.9.1",
|
||||
"save": "^2.9.0",
|
||||
"srtm-elevation": "^2.1.2"
|
||||
"srtm-elevation": "^2.1.2",
|
||||
"uuid": "^9.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-env": "^7.21.4",
|
||||
"@tanem/svg-injector": "^10.1.55",
|
||||
"@tanem/svg-injector": "^10.1.68",
|
||||
"@types/formatcoords": "^1.1.0",
|
||||
"@types/geojson": "^7946.0.10",
|
||||
"@types/leaflet": "^1.9.0",
|
||||
|
||||
8
client/plugins/_boilerplate/copy.bat
Normal file
@@ -0,0 +1,8 @@
|
||||
mkdir .\\..\\DCSOlympus\\client\\public\\plugins\\boilerplateplugin
|
||||
|
||||
copy .\\index.js .\\..\\DCSOlympus\\client\\public\\plugins\\boilerplateplugin\\index.js
|
||||
copy .\\plugin.json .\\..\\DCSOlympus\\client\\public\\plugins\\boilerplateplugin\\plugin.json
|
||||
copy .\\style.css .\\..\\DCSOlympus\\client\\public\\plugins\\boilerplateplugin\\style.css
|
||||
|
||||
mkdir .\\..\\DCSOlympus\\client\\public\\plugins\\boilerplateplugin\\images
|
||||
copy .\\images\\*.* .\\..\\DCSOlympus\\client\\public\\plugins\\boilerplateplugin\\images\\
|
||||
BIN
client/plugins/_boilerplate/images/placeholder1x1.png
Normal file
|
After Width: | Height: | Size: 928 B |
14
client/plugins/_boilerplate/package.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"name": "boilerplateplugin",
|
||||
"version": "v0.0.1",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "browserify ./src/index.ts -p [ tsify --noImplicitAny] > index.js && copy.bat"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/sortablejs": "^1.15.4",
|
||||
"browserify": "^17.0.0",
|
||||
"sortablejs": "^1.15.0",
|
||||
"tsify": "^5.0.4"
|
||||
}
|
||||
}
|
||||
7
client/plugins/_boilerplate/plugin.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"name": "Boilerplate",
|
||||
"version": "0.0.1",
|
||||
"description": "Base plugin starter",
|
||||
"authorName": "",
|
||||
"authorContact": ""
|
||||
}
|
||||
3
client/plugins/_boilerplate/readme.md
Normal file
@@ -0,0 +1,3 @@
|
||||
# Boilerplate plugin
|
||||
|
||||
See: https://github.com/Pax1601/DCSOlympus/wiki/Developer-Guide
|
||||
27
client/plugins/_boilerplate/src/boilerplate.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import { OlympusPlugin } from "interfaces";
|
||||
import { OlympusApp } from "olympusapp";
|
||||
|
||||
|
||||
export class BoilerplatePlugin implements OlympusPlugin {
|
||||
#app!: OlympusApp;
|
||||
|
||||
constructor() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param app <OlympusApp>
|
||||
*
|
||||
* @returns boolean on success/fail
|
||||
*/
|
||||
|
||||
initialize(app: OlympusApp) : boolean {
|
||||
this.#app = app;
|
||||
|
||||
return true; // Return true on success
|
||||
}
|
||||
|
||||
getName() {
|
||||
return "Boilerplate";
|
||||
}
|
||||
|
||||
}
|
||||
5
client/plugins/_boilerplate/src/index.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { BoilerplatePlugin } from "./boilerplate";
|
||||
|
||||
globalThis.getOlympusPlugin = () => {
|
||||
return new BoilerplatePlugin();
|
||||
}
|
||||
0
client/plugins/_boilerplate/style.css
Normal file
104
client/plugins/_boilerplate/tsconfig.json
Normal file
@@ -0,0 +1,104 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig to read more about this file */
|
||||
/* Projects */
|
||||
// "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
|
||||
// "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
|
||||
// "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
|
||||
// "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
|
||||
// "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
|
||||
// "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
|
||||
/* Language and Environment */
|
||||
"target": "ES2017", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
|
||||
// "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
|
||||
// "jsx": "preserve", /* Specify what JSX code is generated. */
|
||||
// "experimentalDecorators": true, /* Enable experimental support for TC39 stage 2 draft decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
|
||||
// "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
|
||||
// "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
|
||||
// "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
|
||||
// "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
|
||||
// "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
|
||||
// "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
|
||||
// "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
|
||||
/* Modules */
|
||||
"module": "commonjs", /* Specify what module code is generated. */
|
||||
"rootDirs": ["./src", "./@types"], /* Specify the root folder within your source files. */
|
||||
// "moduleResolution": "node", /* Specify how TypeScript looks up a file from a given module specifier. */
|
||||
// "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
|
||||
// "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
|
||||
// "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
|
||||
"typeRoots": [
|
||||
"./node_modules/@types",
|
||||
"@types"
|
||||
], /* Specify multiple folders that act like './node_modules/@types'. */
|
||||
"types": [
|
||||
"olympus"
|
||||
], /* Specify type package names to be included without being referenced in a source file. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
// "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
|
||||
// "resolveJsonModule": true, /* Enable importing .json files. */
|
||||
// "noResolve": true, /* Disallow 'import's, 'require's or '<reference>'s from expanding the number of files TypeScript should add to a project. */
|
||||
/* JavaScript Support */
|
||||
"allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
|
||||
// "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
|
||||
// "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
|
||||
/* Emit */
|
||||
// "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
|
||||
// "declarationMap": true, /* Create sourcemaps for d.ts files. */
|
||||
// "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
|
||||
// "sourceMap": true, /* Create source map files for emitted JavaScript files. */
|
||||
// "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
|
||||
// "outDir": "./", /* Specify an output folder for all emitted files. */
|
||||
// "removeComments": true, /* Disable emitting comments. */
|
||||
// "noEmit": true, /* Disable emitting files from a compilation. */
|
||||
// "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
|
||||
// "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
|
||||
// "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
|
||||
// "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
|
||||
// "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
|
||||
// "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
|
||||
// "newLine": "crlf", /* Set the newline character for emitting files. */
|
||||
// "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
|
||||
// "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
|
||||
// "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
|
||||
// "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
|
||||
// "declarationDir": "./", /* Specify the output directory for generated declaration files. */
|
||||
// "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
|
||||
/* Interop Constraints */
|
||||
// "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
|
||||
"esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
|
||||
// "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
|
||||
"forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
|
||||
/* Type Checking */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
|
||||
// "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
|
||||
// "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
|
||||
// "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
|
||||
// "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
|
||||
// "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
|
||||
// "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
|
||||
// "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
|
||||
// "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
|
||||
// "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
|
||||
// "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
|
||||
// "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
|
||||
// "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
|
||||
// "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
|
||||
/* Completeness */
|
||||
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
|
||||
"skipLibCheck": true /* Skip type checking all .d.ts files. */
|
||||
},
|
||||
"include": [
|
||||
"src/*.ts",
|
||||
"../DCSOlympus/client/@types/olympus/index.d.ts"
|
||||
]
|
||||
}
|
||||
4625
client/plugins/controltips/package-lock.json
generated
@@ -4,7 +4,7 @@
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "browserify ./src/index.ts -p [ tsify --noImplicitAny] > index.js && copy.bat",
|
||||
"build-release": "browserify ./src/index.ts -p [ tsify --noImplicitAny] -p [ tinyify ] > index.js && copy.bat"
|
||||
"build-release": "browserify ./src/index.ts -p [ tsify --noImplicitAny] > index.js && copy.bat"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
|
||||
4624
client/plugins/databasemanager/package-lock.json
generated
@@ -7,7 +7,7 @@
|
||||
"build-release": "browserify ./src/index.ts -p [ tsify --noImplicitAny] -p [ tinyify ] > index.js && copy.bat",
|
||||
"start": "npm run copy & concurrently --kill-others \"npm run watch\"",
|
||||
"copy": "copy.bat",
|
||||
"watch": "watchify ./src/index.ts --debug -o ../../public/plugins/databasemanager/index.js -t [ babelify --global true --presets [ @babel/preset-env ] --extensions '.js'] -p [ tsify --noImplicitAny ]"
|
||||
"watch": "watchify ./src/index.ts --debug -o ../../public/plugins/databasemanager/index.js -t [ babelify --global true --presets [ @babel/preset-env ] --extensions '.js']"
|
||||
},
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,25 +1,8 @@
|
||||
{
|
||||
"BDK-775": {
|
||||
"name": "BDK-775",
|
||||
"coalition": "blue",
|
||||
"type": "Landing Ship",
|
||||
"era": "Mid Cold War",
|
||||
"label": "LS Ropucha",
|
||||
"shortLabel": "LS Ropucha",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 25000,
|
||||
"engagementRange": 6000,
|
||||
"description": "Landing ship Ropucha",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
},
|
||||
"CVN_71": {
|
||||
"name": "CVN_71",
|
||||
"coalition": "blue",
|
||||
"type": "Super Aircraft Carrier",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": "Late Cold War",
|
||||
"label": "CVN-71 Theodore Roosevelt",
|
||||
"shortLabel": "CVN-71",
|
||||
@@ -28,7 +11,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 50000,
|
||||
"engagementRange": 25000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
@@ -36,7 +20,7 @@
|
||||
"CVN_72": {
|
||||
"name": "CVN_72",
|
||||
"coalition": "blue",
|
||||
"type": "Super Aircraft Carrier",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": "Late Cold War",
|
||||
"label": "CVN-72 Abraham Lincoln",
|
||||
"shortLabel": "CVN-72",
|
||||
@@ -45,7 +29,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 50000,
|
||||
"engagementRange": 25000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
@@ -53,7 +38,7 @@
|
||||
"CVN_73": {
|
||||
"name": "CVN_73",
|
||||
"coalition": "blue",
|
||||
"type": "Super Aircraft Carrier",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": "Late Cold War",
|
||||
"label": "CVN-73 George Washington",
|
||||
"shortLabel": "CVN-73",
|
||||
@@ -62,7 +47,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 50000,
|
||||
"engagementRange": 25000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
@@ -101,7 +87,8 @@
|
||||
},
|
||||
"acquisitionRange": 50000,
|
||||
"engagementRange": 25000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
@@ -111,13 +98,14 @@
|
||||
"coalition": "red",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": "Modern",
|
||||
"label": "CV Admiral Kuznetsov(2017)",
|
||||
"shortLabel": "Admiral Kuznetsov(2017)",
|
||||
"label": "Admiral Kuznetsov (2017)",
|
||||
"shortLabel": "Admiral Kuznetsov",
|
||||
"range": "Medium",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 25000,
|
||||
"engagementRange": 12000,
|
||||
"tags": "",
|
||||
"description": "Admiral Kuznetsov. Conventional STOBAR carrier",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -126,8 +114,8 @@
|
||||
"CastleClass_01": {
|
||||
"name": "CastleClass_01",
|
||||
"coalition": "blue",
|
||||
"type": "Patrol",
|
||||
"era": "Mid Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "HMS Leeds Castle (P-258)",
|
||||
"shortLabel": "HMS Leeds Castle (P-258)",
|
||||
"range": "",
|
||||
@@ -141,7 +129,8 @@
|
||||
},
|
||||
"acquisitionRange": 25000,
|
||||
"engagementRange": 3000,
|
||||
"description": "HMS Leeds Castle. Smaller. Patrol craft",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -149,7 +138,7 @@
|
||||
"HandyWind": {
|
||||
"name": "HandyWind",
|
||||
"coalition": "blue",
|
||||
"type": "Cargoship",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Late Cold War",
|
||||
"label": "Bulker Handy Wind",
|
||||
"shortLabel": "Bulker Handy Wind",
|
||||
@@ -176,6 +165,7 @@
|
||||
},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -184,7 +174,7 @@
|
||||
"HarborTug": {
|
||||
"name": "HarborTug",
|
||||
"coalition": "",
|
||||
"type": "Tug",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Mid Cold War",
|
||||
"label": "Harbor Tug",
|
||||
"shortLabel": "Harbor Tug",
|
||||
@@ -211,6 +201,7 @@
|
||||
},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -228,7 +219,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 150000,
|
||||
"engagementRange": 20000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -481,6 +473,7 @@
|
||||
},
|
||||
"acquisitionRange": 19000,
|
||||
"engagementRange": 4000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -489,15 +482,16 @@
|
||||
"Seawise_Giant": {
|
||||
"name": "Seawise_Giant",
|
||||
"coalition": "blue",
|
||||
"type": "Tanker",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Late Cold War",
|
||||
"label": "Tanker Seawise Giant",
|
||||
"label": "Seawise Giant (Tanker)",
|
||||
"shortLabel": "Seawise Giant",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -506,7 +500,7 @@
|
||||
"Ship_Tilde_Supply": {
|
||||
"name": "Ship_Tilde_Supply",
|
||||
"coalition": "blue",
|
||||
"type": "Transport",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Late Cold War",
|
||||
"label": "Supply Ship MV Tilde",
|
||||
"shortLabel": "Supply Ship Tilde",
|
||||
@@ -515,6 +509,7 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -532,6 +527,7 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 50000,
|
||||
"engagementRange": 25000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -540,9 +536,9 @@
|
||||
"TICONDEROG": {
|
||||
"name": "TICONDEROG",
|
||||
"coalition": "blue",
|
||||
"type": "Cruiser",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "Ticonderoga",
|
||||
"label": "Ticonderoga Class (Cruiser)",
|
||||
"shortLabel": "Ticonderoga",
|
||||
"range": "Medium",
|
||||
"filename": "",
|
||||
@@ -611,7 +607,8 @@
|
||||
},
|
||||
"acquisitionRange": 150000,
|
||||
"engagementRange": 100000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -619,9 +616,9 @@
|
||||
"Type_052B": {
|
||||
"name": "Type_052B",
|
||||
"coalition": "red",
|
||||
"type": "Destroyer",
|
||||
"type": "Combatants",
|
||||
"era": "Modern",
|
||||
"label": "052B DDG-168 Guangzhou",
|
||||
"label": "Type 52B Guangzhou",
|
||||
"shortLabel": "Type 52B",
|
||||
"range": "Short",
|
||||
"filename": "",
|
||||
@@ -646,7 +643,8 @@
|
||||
},
|
||||
"acquisitionRange": 100000,
|
||||
"engagementRange": 30000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -654,9 +652,9 @@
|
||||
"Type_052C": {
|
||||
"name": "Type_052C",
|
||||
"coalition": "red",
|
||||
"type": "Destroyer",
|
||||
"type": "Combatants",
|
||||
"era": "Modern",
|
||||
"label": "052C DDG-171 Haikou",
|
||||
"label": "Type 52C Haikou (Destroyer)",
|
||||
"shortLabel": "Type 52C",
|
||||
"range": "Short",
|
||||
"filename": "",
|
||||
@@ -705,17 +703,18 @@
|
||||
},
|
||||
"acquisitionRange": 260000,
|
||||
"engagementRange": 100000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
},
|
||||
"Type_054A": {
|
||||
"name": "",
|
||||
"name": "Type_054A",
|
||||
"coalition": "red",
|
||||
"type": "Frigate",
|
||||
"type": "Combatants",
|
||||
"era": "Modern",
|
||||
"label": "054A FFG-538 Yantai",
|
||||
"label": "Type 54A Yantai (Frigate)",
|
||||
"shortLabel": "Type 54A",
|
||||
"range": "Medium",
|
||||
"filename": "",
|
||||
@@ -872,15 +871,16 @@
|
||||
},
|
||||
"acquisitionRange": 160000,
|
||||
"engagementRange": 45000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "35nm >50,000ft range"
|
||||
},
|
||||
"Type_071": {
|
||||
"name": "Type_071",
|
||||
"coalition": "red",
|
||||
"type": "Transport",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Modern",
|
||||
"label": "Type 071",
|
||||
"label": "Type 071 (Transport dock)",
|
||||
"shortLabel": "Type 071",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
@@ -925,7 +925,8 @@
|
||||
},
|
||||
"acquisitionRange": 300000,
|
||||
"engagementRange": 150000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -935,7 +936,7 @@
|
||||
"coalition": "red",
|
||||
"type": "Submarine",
|
||||
"era": "Modern",
|
||||
"label": "Type 093",
|
||||
"label": "Type 093 (Shang)",
|
||||
"shortLabel": "Type 093",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
@@ -948,6 +949,7 @@
|
||||
},
|
||||
"acquisitionRange": 40000,
|
||||
"engagementRange": 40000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -956,10 +958,10 @@
|
||||
"USS_Arleigh_Burke_IIa": {
|
||||
"name": "USS_Arleigh_Burke_IIa",
|
||||
"coalition": "blue",
|
||||
"type": "Destroyer",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "DDG Arleigh Burke lla",
|
||||
"shortLabel": "DDG Arleigh Burke",
|
||||
"label": "Arleigh Burke Class (Destroyer)",
|
||||
"shortLabel": "Arleigh Burke Class (Destroyer)",
|
||||
"range": "Medium",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
@@ -1064,6 +1066,7 @@
|
||||
"acquisitionRange": 150000,
|
||||
"engagementRange": 100000,
|
||||
"abilities": "",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1080,7 +1083,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 18000,
|
||||
"engagementRange": 5000,
|
||||
"description": "ARA Vienticinco de Mayo. Conventional CATOBAR carrier",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1089,15 +1093,16 @@
|
||||
"name": "hms_invincible",
|
||||
"coalition": "blue",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": "Mid Cold War",
|
||||
"label": "HMS Invincible (R05)",
|
||||
"era": "Late Cold War",
|
||||
"label": "HMS Invincible",
|
||||
"shortLabel": "HMS Invincible",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 100000,
|
||||
"engagementRange": 74000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1105,8 +1110,8 @@
|
||||
"leander-gun-achilles": {
|
||||
"name": "leander-gun-achilles",
|
||||
"coalition": "blue",
|
||||
"type": "Frigate",
|
||||
"era": "Mid Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "HMS Achilles (F12)",
|
||||
"shortLabel": "HMS Achilles",
|
||||
"range": "",
|
||||
@@ -1114,7 +1119,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 180000,
|
||||
"engagementRange": 8000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1122,8 +1128,8 @@
|
||||
"leander-gun-andromeda": {
|
||||
"name": "leander-gun-andromeda",
|
||||
"coalition": "blue",
|
||||
"type": "Frigate",
|
||||
"era": "Mid Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "HMS Andromeda (F57)",
|
||||
"shortLabel": "HMS Andromeda",
|
||||
"range": "",
|
||||
@@ -1131,7 +1137,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 180000,
|
||||
"engagementRange": 140000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1139,8 +1146,8 @@
|
||||
"leander-gun-ariadne": {
|
||||
"name": "leander-gun-ariadne",
|
||||
"coalition": "blue",
|
||||
"type": "Frigate",
|
||||
"era": "Mid Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "HMS Ariadne (F72)",
|
||||
"shortLabel": "HMS Ariadne",
|
||||
"range": "",
|
||||
@@ -1148,7 +1155,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 150000,
|
||||
"engagementRange": 100000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1156,8 +1164,8 @@
|
||||
"leander-gun-condell": {
|
||||
"name": "leander-gun-condell",
|
||||
"coalition": "",
|
||||
"type": "Frigate",
|
||||
"era": "Mid Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "Almirante Condell PFG-06",
|
||||
"shortLabel": "Almirante Condell",
|
||||
"range": "",
|
||||
@@ -1165,7 +1173,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 150000,
|
||||
"engagementRange": 100000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1173,8 +1182,8 @@
|
||||
"leander-gun-lynch": {
|
||||
"name": "leander-gun-lynch",
|
||||
"coalition": "",
|
||||
"type": "Frigate",
|
||||
"era": "Mid Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "CNS Almirante Lynch (PFG-07)",
|
||||
"shortLabel": "CNS Almirante Lynch",
|
||||
"range": "",
|
||||
@@ -1182,7 +1191,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 180000,
|
||||
"engagementRange": 140000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1191,7 +1201,7 @@
|
||||
"name": "santafe",
|
||||
"coalition": "",
|
||||
"type": "Submarine",
|
||||
"era": "Early Cold War",
|
||||
"era": "WW2",
|
||||
"label": "ARA Santa Fe S-21",
|
||||
"shortLabel": "ARA Santa",
|
||||
"range": "",
|
||||
@@ -1199,6 +1209,7 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -1210,36 +1221,21 @@
|
||||
"era": "",
|
||||
"label": "Boat Armed Hi-speed",
|
||||
"shortLabel": "Boat Armed Hi-speed",
|
||||
"type": "Speedboat",
|
||||
"type": "Fast Attack Craft",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 5000,
|
||||
"engagementRange": 1000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
},
|
||||
"VINSON": {
|
||||
"name": "VINSON",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"label": "CVN-70 Carl Vinson",
|
||||
"shortLabel": "CVN-70 Carl Vinson",
|
||||
"type": "Aircraft Carrier",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 30000,
|
||||
"engagementRange": 15000,
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"PERRY": {
|
||||
"name": "perry",
|
||||
"name": "PERRY",
|
||||
"coalition": "blue",
|
||||
"type": "Frigate",
|
||||
"type": "Combatants",
|
||||
"era": "Mid Cold War",
|
||||
"label": "Oliver H. Perry",
|
||||
"shortLabel": "Oliver H. Perry",
|
||||
@@ -1289,13 +1285,18 @@
|
||||
}
|
||||
},
|
||||
"acquisitionRange": 150000,
|
||||
"engagementRange": 100000
|
||||
"engagementRange": 100000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"ALBATROS": {
|
||||
"name": "albatros",
|
||||
"name": "ALBATROS",
|
||||
"coalition": "red",
|
||||
"type": "Frigate",
|
||||
"era": "Early Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Modern",
|
||||
"label": "Albatros (Grisha-5)",
|
||||
"shortLabel": "Albatros",
|
||||
"range": "",
|
||||
@@ -1340,25 +1341,35 @@
|
||||
}
|
||||
},
|
||||
"acquisitionRange": 30000,
|
||||
"engagementRange": 16000
|
||||
"engagementRange": 16000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"KUZNECOW": {
|
||||
"name": "kuznecow",
|
||||
"name": "KUZNECOW",
|
||||
"coalition": "red",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": "Late Cold War",
|
||||
"label": "Admiral Kuznetsov",
|
||||
"shortLabel": "Admiral Kuznetsov",
|
||||
"shortLabel": "AK",
|
||||
"range": "Medium",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 25000,
|
||||
"engagementRange": 12000
|
||||
"engagementRange": 12000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"MOLNIYA": {
|
||||
"name": "molniya",
|
||||
"name": "MOLNIYA",
|
||||
"coalition": "",
|
||||
"type": "Corvette",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "Molniya (Tarantul-3)",
|
||||
"shortLabel": "Molniya",
|
||||
@@ -1380,12 +1391,17 @@
|
||||
}
|
||||
},
|
||||
"acquisitionRange": 21000,
|
||||
"engagementRange": 2000
|
||||
"engagementRange": 2000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"MOSCOW": {
|
||||
"name": "moscow",
|
||||
"name": "MOSCOW",
|
||||
"coalition": "red",
|
||||
"type": "Cruiser",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "Moscow",
|
||||
"shortLabel": "Moscow",
|
||||
@@ -1411,54 +1427,70 @@
|
||||
}
|
||||
},
|
||||
"acquisitionRange": 160000,
|
||||
"engagementRange": 75000
|
||||
"engagementRange": 75000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"NEUSTRASH": {
|
||||
"name": "neustrash",
|
||||
"name": "NEUSTRASH",
|
||||
"coalition": "red",
|
||||
"type": "Frigate",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "Neustrashimy",
|
||||
"shortLabel": "Neustrashimy",
|
||||
"range": "Short",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 27000,
|
||||
"engagementRange": 12000
|
||||
"engagementRange": 12000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"PIOTR": {
|
||||
"name": "PIOTR",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"label": "Battlecruiser 1144.2 Pyotr Velikiy",
|
||||
"shortLabel": "Battlecruiser 1144.2 Pyotr Velikiy",
|
||||
"type": "Cruiser",
|
||||
"coalition": "red",
|
||||
"era": "Late Cold War",
|
||||
"label": "Pyotr Velikiy (Battlecruiser)",
|
||||
"shortLabel": "Pyotr Velikiy",
|
||||
"type": "Combatants",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 250000,
|
||||
"engagementRange": 190000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
},
|
||||
"REZKY": {
|
||||
"name": "Rezky (Krivak-2)",
|
||||
"name": "REZKY",
|
||||
"coalition": "red",
|
||||
"type": "Frigate",
|
||||
"era": "Early Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "WW2",
|
||||
"label": "Rezky (Krivak-2)",
|
||||
"shortLabel": "Rezky",
|
||||
"range": "Short",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 30000,
|
||||
"engagementRange": 16000
|
||||
"engagementRange": 16000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"ELNYA": {
|
||||
"name": "elnya",
|
||||
"name": "ELNYA",
|
||||
"coalition": "red",
|
||||
"type": "Tanker",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Late Cold War",
|
||||
"label": "Elnya tanker",
|
||||
"shortLabel": "Elnya tanker",
|
||||
@@ -1480,7 +1512,12 @@
|
||||
}
|
||||
},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"Dry-cargo ship-2": {
|
||||
"name": "Dry-cargo ship-2",
|
||||
@@ -1488,11 +1525,12 @@
|
||||
"era": "",
|
||||
"label": "Cargo Ivanov",
|
||||
"shortLabel": "Cargo Ivanov",
|
||||
"type": "Cargoship",
|
||||
"type": "Cargo/Transport",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -1504,20 +1542,21 @@
|
||||
"era": "",
|
||||
"label": "Bulker Yakushev",
|
||||
"shortLabel": "Bulker Yakushev",
|
||||
"type": "Cargoship",
|
||||
"type": "Cargo/Transport",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"ZWEZDNY": {
|
||||
"name": "zwezdny",
|
||||
"name": "ZWEZDNY",
|
||||
"coalition": "",
|
||||
"type": "Civilian Boat",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Modern",
|
||||
"label": "Zwezdny",
|
||||
"shortLabel": "Zwezdny",
|
||||
@@ -1525,32 +1564,43 @@
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"KILO": {
|
||||
"name": "kilo",
|
||||
"name": "KILO",
|
||||
"coalition": "red",
|
||||
"type": "Submarine",
|
||||
"era": "Late Cold War",
|
||||
"era": "Mid Cold War",
|
||||
"label": "Project 636 Varshavyanka Basic",
|
||||
"shortLabel": "Varshavyanka Basic",
|
||||
"range": "Medium",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"IMPROVED_KILO": {
|
||||
"name": "IMPROVED_KILO",
|
||||
"coalition": "",
|
||||
"coalition": "red",
|
||||
"era": "",
|
||||
"label": "SSK 636 Improved Kilo",
|
||||
"shortLabel": "SSK 636 Improved Kilo",
|
||||
"shortLabel": "Kilo",
|
||||
"type": "Submarine",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -1561,12 +1611,13 @@
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"label": "SSK 641B Tango",
|
||||
"shortLabel": "SSK 641B Tango",
|
||||
"shortLabel": "Tango",
|
||||
"type": "Submarine",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -1574,8 +1625,8 @@
|
||||
},
|
||||
"Forrestal": {
|
||||
"name": "Forrestal",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"coalition": "blue",
|
||||
"era": "Early Cold War",
|
||||
"label": "CV-59 Forrestal",
|
||||
"shortLabel": "CV-59 Forrestal",
|
||||
"type": "Aircraft Carrier",
|
||||
@@ -1583,6 +1634,7 @@
|
||||
"liveries": {},
|
||||
"acquisitionRange": 50000,
|
||||
"engagementRange": 25000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -1590,15 +1642,16 @@
|
||||
},
|
||||
"LST_Mk2": {
|
||||
"name": "LST_Mk2",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"coalition": "blue",
|
||||
"era": "WW2",
|
||||
"label": "LST Mk.II",
|
||||
"shortLabel": "LST Mk.II",
|
||||
"type": "Landing Ship",
|
||||
"type": "Cargo/Transport",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 4000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -1606,15 +1659,16 @@
|
||||
},
|
||||
"USS_Samuel_Chase": {
|
||||
"name": "USS_Samuel_Chase",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"coalition": "blue",
|
||||
"era": "WW2",
|
||||
"label": "LS Samuel Chase",
|
||||
"shortLabel": "LS Samuel Chase",
|
||||
"type": "Landing Ship",
|
||||
"type": "Cargo/Transport",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 7000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -1622,15 +1676,16 @@
|
||||
},
|
||||
"Higgins_boat": {
|
||||
"name": "Higgins_boat",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"coalition": "blue",
|
||||
"era": "WW2",
|
||||
"label": "Boat LCVP Higgins",
|
||||
"shortLabel": "Boat LCVP Higgins",
|
||||
"type": "Landing Ship",
|
||||
"type": "Cargo/Transport",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 3000,
|
||||
"engagementRange": 1000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -1638,15 +1693,16 @@
|
||||
},
|
||||
"Uboat_VIIC": {
|
||||
"name": "Uboat_VIIC",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"label": "U-boat VIIC U-flak",
|
||||
"shortLabel": "U-boat VIIC U-flak",
|
||||
"coalition": "red",
|
||||
"era": "WW2",
|
||||
"label": "U-boat VIIC",
|
||||
"shortLabel": "U-boat",
|
||||
"type": "Submarine",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 20000,
|
||||
"engagementRange": 4000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -1654,15 +1710,16 @@
|
||||
},
|
||||
"Schnellboot_type_S130": {
|
||||
"name": "Schnellboot_type_S130",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"coalition": "red",
|
||||
"era": "WW2",
|
||||
"label": "Boat Schnellboot type S130",
|
||||
"shortLabel": "Boat Schnellboot type S130",
|
||||
"type": "Torpedo Boat",
|
||||
"type": "Fast Attack Craft",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 10000,
|
||||
"engagementRange": 4000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
|
||||
@@ -1,25 +1,8 @@
|
||||
{
|
||||
"BDK-775": {
|
||||
"name": "BDK-775",
|
||||
"coalition": "blue",
|
||||
"type": "Landing Ship",
|
||||
"era": "Mid Cold War",
|
||||
"label": "LS Ropucha",
|
||||
"shortLabel": "LS Ropucha",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 25000,
|
||||
"engagementRange": 6000,
|
||||
"description": "Landing ship Ropucha",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
},
|
||||
"CVN_71": {
|
||||
"name": "CVN_71",
|
||||
"coalition": "blue",
|
||||
"type": "Super Aircraft Carrier",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": "Late Cold War",
|
||||
"label": "CVN-71 Theodore Roosevelt",
|
||||
"shortLabel": "CVN-71",
|
||||
@@ -28,7 +11,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 50000,
|
||||
"engagementRange": 25000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
@@ -36,7 +20,7 @@
|
||||
"CVN_72": {
|
||||
"name": "CVN_72",
|
||||
"coalition": "blue",
|
||||
"type": "Super Aircraft Carrier",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": "Late Cold War",
|
||||
"label": "CVN-72 Abraham Lincoln",
|
||||
"shortLabel": "CVN-72",
|
||||
@@ -45,7 +29,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 50000,
|
||||
"engagementRange": 25000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
@@ -53,7 +38,7 @@
|
||||
"CVN_73": {
|
||||
"name": "CVN_73",
|
||||
"coalition": "blue",
|
||||
"type": "Super Aircraft Carrier",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": "Late Cold War",
|
||||
"label": "CVN-73 George Washington",
|
||||
"shortLabel": "CVN-73",
|
||||
@@ -62,7 +47,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 50000,
|
||||
"engagementRange": 25000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
@@ -101,7 +87,8 @@
|
||||
},
|
||||
"acquisitionRange": 50000,
|
||||
"engagementRange": 25000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
@@ -111,13 +98,14 @@
|
||||
"coalition": "red",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": "Modern",
|
||||
"label": "CV Admiral Kuznetsov(2017)",
|
||||
"shortLabel": "Admiral Kuznetsov(2017)",
|
||||
"label": "Admiral Kuznetsov (2017)",
|
||||
"shortLabel": "Admiral Kuznetsov",
|
||||
"range": "Medium",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 25000,
|
||||
"engagementRange": 12000,
|
||||
"tags": "",
|
||||
"description": "Admiral Kuznetsov. Conventional STOBAR carrier",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -126,8 +114,8 @@
|
||||
"CastleClass_01": {
|
||||
"name": "CastleClass_01",
|
||||
"coalition": "blue",
|
||||
"type": "Patrol",
|
||||
"era": "Mid Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "HMS Leeds Castle (P-258)",
|
||||
"shortLabel": "HMS Leeds Castle (P-258)",
|
||||
"range": "",
|
||||
@@ -141,7 +129,8 @@
|
||||
},
|
||||
"acquisitionRange": 25000,
|
||||
"engagementRange": 3000,
|
||||
"description": "HMS Leeds Castle. Smaller. Patrol craft",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -149,7 +138,7 @@
|
||||
"HandyWind": {
|
||||
"name": "HandyWind",
|
||||
"coalition": "blue",
|
||||
"type": "Cargoship",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Late Cold War",
|
||||
"label": "Bulker Handy Wind",
|
||||
"shortLabel": "Bulker Handy Wind",
|
||||
@@ -176,6 +165,7 @@
|
||||
},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -184,7 +174,7 @@
|
||||
"HarborTug": {
|
||||
"name": "HarborTug",
|
||||
"coalition": "",
|
||||
"type": "Tug",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Mid Cold War",
|
||||
"label": "Harbor Tug",
|
||||
"shortLabel": "Harbor Tug",
|
||||
@@ -211,6 +201,7 @@
|
||||
},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -228,7 +219,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 150000,
|
||||
"engagementRange": 20000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -481,6 +473,7 @@
|
||||
},
|
||||
"acquisitionRange": 19000,
|
||||
"engagementRange": 4000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -489,15 +482,16 @@
|
||||
"Seawise_Giant": {
|
||||
"name": "Seawise_Giant",
|
||||
"coalition": "blue",
|
||||
"type": "Tanker",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Late Cold War",
|
||||
"label": "Tanker Seawise Giant",
|
||||
"label": "Seawise Giant (Tanker)",
|
||||
"shortLabel": "Seawise Giant",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -506,7 +500,7 @@
|
||||
"Ship_Tilde_Supply": {
|
||||
"name": "Ship_Tilde_Supply",
|
||||
"coalition": "blue",
|
||||
"type": "Transport",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Late Cold War",
|
||||
"label": "Supply Ship MV Tilde",
|
||||
"shortLabel": "Supply Ship Tilde",
|
||||
@@ -515,6 +509,7 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -532,6 +527,7 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 50000,
|
||||
"engagementRange": 25000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -540,9 +536,9 @@
|
||||
"TICONDEROG": {
|
||||
"name": "TICONDEROG",
|
||||
"coalition": "blue",
|
||||
"type": "Cruiser",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "Ticonderoga",
|
||||
"label": "Ticonderoga Class (Cruiser)",
|
||||
"shortLabel": "Ticonderoga",
|
||||
"range": "Medium",
|
||||
"filename": "",
|
||||
@@ -611,7 +607,8 @@
|
||||
},
|
||||
"acquisitionRange": 150000,
|
||||
"engagementRange": 100000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -619,9 +616,9 @@
|
||||
"Type_052B": {
|
||||
"name": "Type_052B",
|
||||
"coalition": "red",
|
||||
"type": "Destroyer",
|
||||
"type": "Combatants",
|
||||
"era": "Modern",
|
||||
"label": "052B DDG-168 Guangzhou",
|
||||
"label": "Type 52B Guangzhou",
|
||||
"shortLabel": "Type 52B",
|
||||
"range": "Short",
|
||||
"filename": "",
|
||||
@@ -646,7 +643,8 @@
|
||||
},
|
||||
"acquisitionRange": 100000,
|
||||
"engagementRange": 30000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -654,9 +652,9 @@
|
||||
"Type_052C": {
|
||||
"name": "Type_052C",
|
||||
"coalition": "red",
|
||||
"type": "Destroyer",
|
||||
"type": "Combatants",
|
||||
"era": "Modern",
|
||||
"label": "052C DDG-171 Haikou",
|
||||
"label": "Type 52C Haikou (Destroyer)",
|
||||
"shortLabel": "Type 52C",
|
||||
"range": "Short",
|
||||
"filename": "",
|
||||
@@ -705,17 +703,18 @@
|
||||
},
|
||||
"acquisitionRange": 260000,
|
||||
"engagementRange": 100000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
},
|
||||
"Type_054A": {
|
||||
"name": "",
|
||||
"name": "Type_054A",
|
||||
"coalition": "red",
|
||||
"type": "Frigate",
|
||||
"type": "Combatants",
|
||||
"era": "Modern",
|
||||
"label": "054A FFG-538 Yantai",
|
||||
"label": "Type 54A Yantai (Frigate)",
|
||||
"shortLabel": "Type 54A",
|
||||
"range": "Medium",
|
||||
"filename": "",
|
||||
@@ -872,15 +871,16 @@
|
||||
},
|
||||
"acquisitionRange": 160000,
|
||||
"engagementRange": 45000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "35nm >50,000ft range"
|
||||
},
|
||||
"Type_071": {
|
||||
"name": "Type_071",
|
||||
"coalition": "red",
|
||||
"type": "Transport",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Modern",
|
||||
"label": "Type 071",
|
||||
"label": "Type 071 (Transport dock)",
|
||||
"shortLabel": "Type 071",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
@@ -925,7 +925,8 @@
|
||||
},
|
||||
"acquisitionRange": 300000,
|
||||
"engagementRange": 150000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -935,7 +936,7 @@
|
||||
"coalition": "red",
|
||||
"type": "Submarine",
|
||||
"era": "Modern",
|
||||
"label": "Type 093",
|
||||
"label": "Type 093 (Shang)",
|
||||
"shortLabel": "Type 093",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
@@ -948,6 +949,7 @@
|
||||
},
|
||||
"acquisitionRange": 40000,
|
||||
"engagementRange": 40000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -956,10 +958,10 @@
|
||||
"USS_Arleigh_Burke_IIa": {
|
||||
"name": "USS_Arleigh_Burke_IIa",
|
||||
"coalition": "blue",
|
||||
"type": "Destroyer",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "DDG Arleigh Burke lla",
|
||||
"shortLabel": "DDG Arleigh Burke",
|
||||
"label": "Arleigh Burke Class (Destroyer)",
|
||||
"shortLabel": "Arleigh Burke Class (Destroyer)",
|
||||
"range": "Medium",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
@@ -1064,6 +1066,7 @@
|
||||
"acquisitionRange": 150000,
|
||||
"engagementRange": 100000,
|
||||
"abilities": "",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1080,7 +1083,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 18000,
|
||||
"engagementRange": 5000,
|
||||
"description": "ARA Vienticinco de Mayo. Conventional CATOBAR carrier",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1089,15 +1093,16 @@
|
||||
"name": "hms_invincible",
|
||||
"coalition": "blue",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": "Mid Cold War",
|
||||
"label": "HMS Invincible (R05)",
|
||||
"era": "Late Cold War",
|
||||
"label": "HMS Invincible",
|
||||
"shortLabel": "HMS Invincible",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 100000,
|
||||
"engagementRange": 74000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1105,8 +1110,8 @@
|
||||
"leander-gun-achilles": {
|
||||
"name": "leander-gun-achilles",
|
||||
"coalition": "blue",
|
||||
"type": "Frigate",
|
||||
"era": "Mid Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "HMS Achilles (F12)",
|
||||
"shortLabel": "HMS Achilles",
|
||||
"range": "",
|
||||
@@ -1114,7 +1119,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 180000,
|
||||
"engagementRange": 8000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1122,8 +1128,8 @@
|
||||
"leander-gun-andromeda": {
|
||||
"name": "leander-gun-andromeda",
|
||||
"coalition": "blue",
|
||||
"type": "Frigate",
|
||||
"era": "Mid Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "HMS Andromeda (F57)",
|
||||
"shortLabel": "HMS Andromeda",
|
||||
"range": "",
|
||||
@@ -1131,7 +1137,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 180000,
|
||||
"engagementRange": 140000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1139,8 +1146,8 @@
|
||||
"leander-gun-ariadne": {
|
||||
"name": "leander-gun-ariadne",
|
||||
"coalition": "blue",
|
||||
"type": "Frigate",
|
||||
"era": "Mid Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "HMS Ariadne (F72)",
|
||||
"shortLabel": "HMS Ariadne",
|
||||
"range": "",
|
||||
@@ -1148,7 +1155,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 150000,
|
||||
"engagementRange": 100000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1156,8 +1164,8 @@
|
||||
"leander-gun-condell": {
|
||||
"name": "leander-gun-condell",
|
||||
"coalition": "",
|
||||
"type": "Frigate",
|
||||
"era": "Mid Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "Almirante Condell PFG-06",
|
||||
"shortLabel": "Almirante Condell",
|
||||
"range": "",
|
||||
@@ -1165,7 +1173,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 150000,
|
||||
"engagementRange": 100000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1173,8 +1182,8 @@
|
||||
"leander-gun-lynch": {
|
||||
"name": "leander-gun-lynch",
|
||||
"coalition": "",
|
||||
"type": "Frigate",
|
||||
"era": "Mid Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "CNS Almirante Lynch (PFG-07)",
|
||||
"shortLabel": "CNS Almirante Lynch",
|
||||
"range": "",
|
||||
@@ -1182,7 +1191,8 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 180000,
|
||||
"engagementRange": 140000,
|
||||
"description": "Ship",
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
@@ -1191,7 +1201,7 @@
|
||||
"name": "santafe",
|
||||
"coalition": "",
|
||||
"type": "Submarine",
|
||||
"era": "Early Cold War",
|
||||
"era": "WW2",
|
||||
"label": "ARA Santa Fe S-21",
|
||||
"shortLabel": "ARA Santa",
|
||||
"range": "",
|
||||
@@ -1199,6 +1209,7 @@
|
||||
"enabled": true,
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -1210,36 +1221,21 @@
|
||||
"era": "",
|
||||
"label": "Boat Armed Hi-speed",
|
||||
"shortLabel": "Boat Armed Hi-speed",
|
||||
"type": "Speedboat",
|
||||
"type": "Fast Attack Craft",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 5000,
|
||||
"engagementRange": 1000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
},
|
||||
"VINSON": {
|
||||
"name": "VINSON",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"label": "CVN-70 Carl Vinson",
|
||||
"shortLabel": "CVN-70 Carl Vinson",
|
||||
"type": "Aircraft Carrier",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 30000,
|
||||
"engagementRange": 15000,
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"PERRY": {
|
||||
"name": "perry",
|
||||
"name": "PERRY",
|
||||
"coalition": "blue",
|
||||
"type": "Frigate",
|
||||
"type": "Combatants",
|
||||
"era": "Mid Cold War",
|
||||
"label": "Oliver H. Perry",
|
||||
"shortLabel": "Oliver H. Perry",
|
||||
@@ -1289,13 +1285,18 @@
|
||||
}
|
||||
},
|
||||
"acquisitionRange": 150000,
|
||||
"engagementRange": 100000
|
||||
"engagementRange": 100000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"ALBATROS": {
|
||||
"name": "albatros",
|
||||
"name": "ALBATROS",
|
||||
"coalition": "red",
|
||||
"type": "Frigate",
|
||||
"era": "Early Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "Modern",
|
||||
"label": "Albatros (Grisha-5)",
|
||||
"shortLabel": "Albatros",
|
||||
"range": "",
|
||||
@@ -1340,25 +1341,35 @@
|
||||
}
|
||||
},
|
||||
"acquisitionRange": 30000,
|
||||
"engagementRange": 16000
|
||||
"engagementRange": 16000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"KUZNECOW": {
|
||||
"name": "kuznecow",
|
||||
"name": "KUZNECOW",
|
||||
"coalition": "red",
|
||||
"type": "Aircraft Carrier",
|
||||
"era": "Late Cold War",
|
||||
"label": "Admiral Kuznetsov",
|
||||
"shortLabel": "Admiral Kuznetsov",
|
||||
"shortLabel": "AK",
|
||||
"range": "Medium",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 25000,
|
||||
"engagementRange": 12000
|
||||
"engagementRange": 12000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"MOLNIYA": {
|
||||
"name": "molniya",
|
||||
"name": "MOLNIYA",
|
||||
"coalition": "",
|
||||
"type": "Corvette",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "Molniya (Tarantul-3)",
|
||||
"shortLabel": "Molniya",
|
||||
@@ -1380,12 +1391,17 @@
|
||||
}
|
||||
},
|
||||
"acquisitionRange": 21000,
|
||||
"engagementRange": 2000
|
||||
"engagementRange": 2000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"MOSCOW": {
|
||||
"name": "moscow",
|
||||
"name": "MOSCOW",
|
||||
"coalition": "red",
|
||||
"type": "Cruiser",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "Moscow",
|
||||
"shortLabel": "Moscow",
|
||||
@@ -1411,54 +1427,70 @@
|
||||
}
|
||||
},
|
||||
"acquisitionRange": 160000,
|
||||
"engagementRange": 75000
|
||||
"engagementRange": 75000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"NEUSTRASH": {
|
||||
"name": "neustrash",
|
||||
"name": "NEUSTRASH",
|
||||
"coalition": "red",
|
||||
"type": "Frigate",
|
||||
"type": "Combatants",
|
||||
"era": "Late Cold War",
|
||||
"label": "Neustrashimy",
|
||||
"shortLabel": "Neustrashimy",
|
||||
"range": "Short",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 27000,
|
||||
"engagementRange": 12000
|
||||
"engagementRange": 12000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"PIOTR": {
|
||||
"name": "PIOTR",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"label": "Battlecruiser 1144.2 Pyotr Velikiy",
|
||||
"shortLabel": "Battlecruiser 1144.2 Pyotr Velikiy",
|
||||
"type": "Cruiser",
|
||||
"coalition": "red",
|
||||
"era": "Late Cold War",
|
||||
"label": "Pyotr Velikiy (Battlecruiser)",
|
||||
"shortLabel": "Pyotr Velikiy",
|
||||
"type": "Combatants",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 250000,
|
||||
"engagementRange": 190000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
"canRearm": false
|
||||
},
|
||||
"REZKY": {
|
||||
"name": "Rezky (Krivak-2)",
|
||||
"name": "REZKY",
|
||||
"coalition": "red",
|
||||
"type": "Frigate",
|
||||
"era": "Early Cold War",
|
||||
"type": "Combatants",
|
||||
"era": "WW2",
|
||||
"label": "Rezky (Krivak-2)",
|
||||
"shortLabel": "Rezky",
|
||||
"range": "Short",
|
||||
"range": "",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 30000,
|
||||
"engagementRange": 16000
|
||||
"engagementRange": 16000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"ELNYA": {
|
||||
"name": "elnya",
|
||||
"name": "ELNYA",
|
||||
"coalition": "red",
|
||||
"type": "Tanker",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Late Cold War",
|
||||
"label": "Elnya tanker",
|
||||
"shortLabel": "Elnya tanker",
|
||||
@@ -1480,7 +1512,12 @@
|
||||
}
|
||||
},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"Dry-cargo ship-2": {
|
||||
"name": "Dry-cargo ship-2",
|
||||
@@ -1488,11 +1525,12 @@
|
||||
"era": "",
|
||||
"label": "Cargo Ivanov",
|
||||
"shortLabel": "Cargo Ivanov",
|
||||
"type": "Cargoship",
|
||||
"type": "Cargo/Transport",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -1504,20 +1542,21 @@
|
||||
"era": "",
|
||||
"label": "Bulker Yakushev",
|
||||
"shortLabel": "Bulker Yakushev",
|
||||
"type": "Cargoship",
|
||||
"type": "Cargo/Transport",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"ZWEZDNY": {
|
||||
"name": "zwezdny",
|
||||
"name": "ZWEZDNY",
|
||||
"coalition": "",
|
||||
"type": "Civilian Boat",
|
||||
"type": "Cargo/Transport",
|
||||
"era": "Modern",
|
||||
"label": "Zwezdny",
|
||||
"shortLabel": "Zwezdny",
|
||||
@@ -1525,32 +1564,43 @@
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"KILO": {
|
||||
"name": "kilo",
|
||||
"name": "KILO",
|
||||
"coalition": "red",
|
||||
"type": "Submarine",
|
||||
"era": "Late Cold War",
|
||||
"era": "Mid Cold War",
|
||||
"label": "Project 636 Varshavyanka Basic",
|
||||
"shortLabel": "Varshavyanka Basic",
|
||||
"range": "Medium",
|
||||
"filename": "",
|
||||
"enabled": true,
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
"canRearm": false
|
||||
},
|
||||
"IMPROVED_KILO": {
|
||||
"name": "IMPROVED_KILO",
|
||||
"coalition": "",
|
||||
"coalition": "red",
|
||||
"era": "",
|
||||
"label": "SSK 636 Improved Kilo",
|
||||
"shortLabel": "SSK 636 Improved Kilo",
|
||||
"shortLabel": "Kilo",
|
||||
"type": "Submarine",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -1561,12 +1611,13 @@
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"label": "SSK 641B Tango",
|
||||
"shortLabel": "SSK 641B Tango",
|
||||
"shortLabel": "Tango",
|
||||
"type": "Submarine",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 0,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -1574,8 +1625,8 @@
|
||||
},
|
||||
"Forrestal": {
|
||||
"name": "Forrestal",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"coalition": "blue",
|
||||
"era": "Early Cold War",
|
||||
"label": "CV-59 Forrestal",
|
||||
"shortLabel": "CV-59 Forrestal",
|
||||
"type": "Aircraft Carrier",
|
||||
@@ -1583,6 +1634,7 @@
|
||||
"liveries": {},
|
||||
"acquisitionRange": 50000,
|
||||
"engagementRange": 25000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": false,
|
||||
@@ -1590,15 +1642,16 @@
|
||||
},
|
||||
"LST_Mk2": {
|
||||
"name": "LST_Mk2",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"coalition": "blue",
|
||||
"era": "WW2",
|
||||
"label": "LST Mk.II",
|
||||
"shortLabel": "LST Mk.II",
|
||||
"type": "Landing Ship",
|
||||
"type": "Cargo/Transport",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 4000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -1606,15 +1659,16 @@
|
||||
},
|
||||
"USS_Samuel_Chase": {
|
||||
"name": "USS_Samuel_Chase",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"coalition": "blue",
|
||||
"era": "WW2",
|
||||
"label": "LS Samuel Chase",
|
||||
"shortLabel": "LS Samuel Chase",
|
||||
"type": "Landing Ship",
|
||||
"type": "Cargo/Transport",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 0,
|
||||
"engagementRange": 7000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -1622,15 +1676,16 @@
|
||||
},
|
||||
"Higgins_boat": {
|
||||
"name": "Higgins_boat",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"coalition": "blue",
|
||||
"era": "WW2",
|
||||
"label": "Boat LCVP Higgins",
|
||||
"shortLabel": "Boat LCVP Higgins",
|
||||
"type": "Landing Ship",
|
||||
"type": "Cargo/Transport",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 3000,
|
||||
"engagementRange": 1000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -1638,15 +1693,16 @@
|
||||
},
|
||||
"Uboat_VIIC": {
|
||||
"name": "Uboat_VIIC",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"label": "U-boat VIIC U-flak",
|
||||
"shortLabel": "U-boat VIIC U-flak",
|
||||
"coalition": "red",
|
||||
"era": "WW2",
|
||||
"label": "U-boat VIIC",
|
||||
"shortLabel": "U-boat",
|
||||
"type": "Submarine",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 20000,
|
||||
"engagementRange": 4000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
@@ -1654,15 +1710,16 @@
|
||||
},
|
||||
"Schnellboot_type_S130": {
|
||||
"name": "Schnellboot_type_S130",
|
||||
"coalition": "",
|
||||
"era": "",
|
||||
"coalition": "red",
|
||||
"era": "WW2",
|
||||
"label": "Boat Schnellboot type S130",
|
||||
"shortLabel": "Boat Schnellboot type S130",
|
||||
"type": "Torpedo Boat",
|
||||
"type": "Fast Attack Craft",
|
||||
"enabled": true,
|
||||
"liveries": {},
|
||||
"acquisitionRange": 10000,
|
||||
"engagementRange": 4000,
|
||||
"tags": "",
|
||||
"description": "",
|
||||
"abilities": "",
|
||||
"canTargetPoint": true,
|
||||
|
||||
BIN
client/public/images/units/f-1.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
client/public/images/units/mb-339.png
Normal file
|
After Width: | Height: | Size: 15 KiB |
@@ -62,6 +62,16 @@
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
#hotgroup-panel {
|
||||
bottom: 40px;
|
||||
column-gap: 10px;
|
||||
display: flex;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
translate: -50%;
|
||||
z-index: 9998;
|
||||
}
|
||||
|
||||
#info-popup {
|
||||
position: absolute;
|
||||
width: fit-content;
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.airbase-icon[data-coalition="red"] svg * {
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.bullseye-icon[data-coalition="red"] svg * {
|
||||
|
||||
@@ -50,6 +50,11 @@
|
||||
width: var(--unit-width);
|
||||
}
|
||||
|
||||
.unit-icon svg {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
[data-is-selected] .unit-icon::before {
|
||||
background-color: var(--unit-spotlight-fill);
|
||||
border-radius: 50%;
|
||||
@@ -200,12 +205,6 @@
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
[data-object|="unit"][data-has-low-fuel] .unit-fuel, [data-object|="unit"][data-has-low-health] .unit-health {
|
||||
animation: pulse 1.5s linear infinite;
|
||||
}
|
||||
|
||||
@@ -45,6 +45,7 @@
|
||||
justify-content: space-between;
|
||||
row-gap: 5px;
|
||||
width: 100%;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.contextmenu-advanced-options-toggle,
|
||||
@@ -60,8 +61,18 @@
|
||||
|
||||
.contextmenu-advanced-options-toggle:after,
|
||||
.contextmenu-metadata-toggle:after {
|
||||
content: url(/resources/theme/images/icons/chevron-down.svg);
|
||||
margin: auto;
|
||||
content: "";
|
||||
margin-left: auto;
|
||||
margin-top: auto;
|
||||
background-image: url(/resources/theme/images/icons/chevron-down.svg);
|
||||
background-size: 100% 100%;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
.contextmenu-advanced-options-toggle.is-open:after,
|
||||
.contextmenu-metadata-toggle.is-open:after {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.contextmenu-advanced-options-toggle div:first-child,
|
||||
@@ -241,8 +252,8 @@
|
||||
}
|
||||
|
||||
.unit-label-count-container {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
display: grid;
|
||||
grid-template-columns: 187px 1fr 1fr;
|
||||
align-items: center;
|
||||
column-gap: 5px;
|
||||
}
|
||||
@@ -400,84 +411,124 @@
|
||||
|
||||
/* Buttons */
|
||||
#center-map::before {
|
||||
content: url("/resources/theme/images/icons/arrows-to-eye-solid.svg");
|
||||
background-image: url("/resources/theme/images/icons/arrows-to-eye-solid.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#refuel::before {
|
||||
content: url("/resources/theme/images/icons/fuel.svg");
|
||||
background-image: url("/resources/theme/images/icons/fuel.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#attack::before {
|
||||
content: url("/resources/theme/images/icons/sword.svg");
|
||||
background-image: url("/resources/theme/images/icons/sword.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#bomb::before {
|
||||
content: url("/resources/theme/images/icons/crosshairs-solid.svg");
|
||||
background-image: url("/resources/theme/images/icons/crosshairs-solid.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#carpet-bomb::before {
|
||||
content: url("/resources/theme/images/icons/explosion-solid.svg");
|
||||
background-image: url("/resources/theme/images/icons/explosion-solid.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#fire-at-area::before {
|
||||
content: url("/resources/theme/images/icons/crosshairs-solid.svg");
|
||||
background-image: url("/resources/theme/images/icons/crosshairs-solid.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#simulate-fire-fight::before {
|
||||
content: url("/resources/theme/images/icons/crosshairs-solid.svg");
|
||||
background-image: url("/resources/theme/images/icons/crosshairs-solid.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#follow::before {
|
||||
content: url("/resources/theme/images/icons/follow.svg");
|
||||
background-image: url("/resources/theme/images/icons/follow.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#scenic-aaa::before {
|
||||
content: url("/resources/theme/images/icons/scenic.svg");
|
||||
background-image: url("/resources/theme/images/icons/scenic.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#miss-aaa::before {
|
||||
content: url("/resources/theme/images/icons/miss.svg");
|
||||
background-image: url("/resources/theme/images/icons/miss.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#group-ground::before {
|
||||
content: url("/resources/theme/images/icons/group-ground.svg");
|
||||
background-image: url("/resources/theme/images/icons/group-ground.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#group-navy::before {
|
||||
content: url("/resources/theme/images/icons/group-navy.svg");
|
||||
background-image: url("/resources/theme/images/icons/group-navy.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#land-at-point::before {
|
||||
content: url("/resources/theme/images/icons/land-at-point.svg");
|
||||
background-image: url("/resources/theme/images/icons/land-at-point.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#trail::before {
|
||||
content: url("/resources/theme/images/icons/trail.svg");
|
||||
background-image: url("/resources/theme/images/icons/trail.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#echelon-lh::before {
|
||||
content: url("/resources/theme/images/icons/echelon-lh.svg");
|
||||
background-image: url("/resources/theme/images/icons/echelon-lh.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#echelon-rh::before {
|
||||
content: url("/resources/theme/images/icons/echelon-rh.svg");
|
||||
background-image: url("/resources/theme/images/icons/echelon-rh.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#line-abreast-rh::before,
|
||||
#line-abreast-lh::before {
|
||||
content: url("/resources/theme/images/icons/line-abreast.svg");
|
||||
background-image: url("/resources/theme/images/icons/line-abreast.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#front::before {
|
||||
content: url("/resources/theme/images/icons/front.svg");
|
||||
background-image: url("/resources/theme/images/icons/front.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#diamond::before {
|
||||
content: url("/resources/theme/images/icons/diamond.svg");
|
||||
background-image: url("/resources/theme/images/icons/diamond.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#custom::before {
|
||||
content: url("/resources/theme/images/icons/custom.svg");
|
||||
background-image: url("/resources/theme/images/icons/custom.svg");
|
||||
content: "";
|
||||
background-size: 20px 20px;
|
||||
}
|
||||
|
||||
#custom-formation-dialog {
|
||||
@@ -580,6 +631,7 @@
|
||||
|
||||
#iads-menu {
|
||||
row-gap: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#coalition-area-contextmenu>div:nth-child(2) {
|
||||
@@ -596,6 +648,7 @@
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
row-gap: 5px;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.create-iads-button {
|
||||
|
||||
@@ -14,6 +14,10 @@
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
#app-icon>.ol-select-value {
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
#toolbar-summary {
|
||||
background-image: url("/images/icon-round.png");
|
||||
background-position: 20px 22px;
|
||||
@@ -29,22 +33,69 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#toolbar-container>*:nth-child(2)>svg {
|
||||
display: none;
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
.ol-panel-tab {
|
||||
align-items: center;
|
||||
display:flex;
|
||||
flex-direction: row;
|
||||
margin-right:6px;
|
||||
}
|
||||
|
||||
.ol-panel-tab svg {
|
||||
height:24;
|
||||
width:24px;
|
||||
}
|
||||
|
||||
.ol-panel-tab svg * {
|
||||
fill:white;
|
||||
}
|
||||
|
||||
.ol-panel-tab span {
|
||||
font-size:13px;
|
||||
font-weight:400;
|
||||
padding:0 6px;
|
||||
}
|
||||
|
||||
#view-label {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
#view-label svg {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
#toolbar-container>*:nth-child(3)>svg {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#unit-visibility-control > div:nth-child(4) {
|
||||
border-left: 2px solid white;
|
||||
padding-left: 12px;
|
||||
}
|
||||
|
||||
#unit-visibility-control > div:last-child {
|
||||
border-right: 2px solid white;
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
@media (max-width: 1145px) {
|
||||
#toolbar-container {
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
}
|
||||
|
||||
#toolbar-container .ol-panel .ol-panel-tab {
|
||||
margin-right:0;
|
||||
}
|
||||
|
||||
#toolbar-container .ol-panel:hover .ol-panel-tab {
|
||||
display:none;
|
||||
}
|
||||
|
||||
#toolbar-container .ol-panel-tab span {
|
||||
display:none;
|
||||
}
|
||||
|
||||
#toolbar-container>*:nth-child(1):not(:hover) {
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
@@ -62,10 +113,10 @@
|
||||
}
|
||||
|
||||
#toolbar-container>*:not(:first-child):not(:hover)>svg {
|
||||
display: block;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: block;
|
||||
filter: invert();
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
|
||||
#toolbar-container>*:not(:first-child):not(:hover)>*:not(:first-child) {
|
||||
|
||||
@@ -50,15 +50,6 @@
|
||||
animation: pulse 1s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
#connection-status-panel[data-is-paused] #connection-status-light {
|
||||
background: var(--accent-amber);
|
||||
}
|
||||
@@ -27,7 +27,6 @@
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
#log-panel-header-right {
|
||||
align-items: center;
|
||||
column-gap: 16px;
|
||||
@@ -35,6 +34,11 @@
|
||||
flex-flow: row nowrap;
|
||||
}
|
||||
|
||||
#log-panel-header-right svg {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
#server-status-panel abbr {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@@ -6,65 +6,71 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
#roe-buttons-container button,
|
||||
#reaction-to-threat-buttons-container button,
|
||||
#emissions-countermeasures-buttons-container button,
|
||||
#shots-scatter-buttons-container button
|
||||
#shots-intensity-buttons-container button {
|
||||
align-items: center;
|
||||
background-color: transparent;
|
||||
border: 1px solid var(--accent-light-blue);
|
||||
display: flex;
|
||||
height: 30px;
|
||||
justify-content: center;
|
||||
width: 30px;
|
||||
#shots-scatter-buttons-container button #shots-intensity-buttons-container button {
|
||||
align-items: center;
|
||||
background-color: transparent;
|
||||
border: 1px solid var(--accent-light-blue);
|
||||
display: flex;
|
||||
height: 30px;
|
||||
justify-content: center;
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
#reaction-to-threat-buttons-container button:not(:first-child) svg {
|
||||
width: 150%;
|
||||
margin: -5px;
|
||||
width: 150%;
|
||||
margin: -5px;
|
||||
}
|
||||
|
||||
#unit-control-panel .ol-option-button button {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
#unit-control-panel .ol-option-button svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#unit-control-panel .ol-option-button button.selected {
|
||||
background-color: white;
|
||||
border-color: white;
|
||||
background-color: white;
|
||||
border-color: white;
|
||||
}
|
||||
|
||||
#unit-control-panel .ol-option-button button.selected svg * {
|
||||
fill: var(--background-steel);
|
||||
stroke: var(--background-steel);
|
||||
fill: var(--background-steel);
|
||||
stroke: var(--background-steel);
|
||||
}
|
||||
|
||||
#rapid-controls {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 5px;
|
||||
height: fit-content;
|
||||
width: fit-content;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
row-gap: 5px;
|
||||
height: fit-content;
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
#rapid-controls button {
|
||||
padding: 4px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
#rapid-controls button.pulse {
|
||||
animation: pulse 1.5s linear infinite;
|
||||
animation: pulse 1.5s linear infinite;
|
||||
}
|
||||
|
||||
#rapid-controls svg {
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
fill: white;
|
||||
stroke: white;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
fill: white;
|
||||
stroke: white;
|
||||
}
|
||||
|
||||
#rapid-controls button:before {
|
||||
display: inline-block;
|
||||
filter: invert(100%);
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
display: inline-block;
|
||||
filter: invert(100%);
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#unit-control-panel {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
@@ -112,7 +118,7 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
width: fit-content;
|
||||
}
|
||||
|
||||
#unit-control-panel:not(:hover)>*:nth-child(2),
|
||||
#unit-control-panel:not(:hover)>*:nth-child(2),
|
||||
#unit-control-panel:not(:hover)>*:nth-child(3) {
|
||||
display: none;
|
||||
}
|
||||
@@ -199,7 +205,7 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
}
|
||||
|
||||
#advanced-settings-dialog>.ol-dialog-content>div input[type="number"] {
|
||||
width: 60px;
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
#advanced-settings-dialog hr {
|
||||
@@ -269,7 +275,6 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
width: 40px;
|
||||
}
|
||||
|
||||
|
||||
.ol-slider-value {
|
||||
color: var(--accent-light-blue);
|
||||
cursor: pointer;
|
||||
@@ -279,13 +284,15 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
|
||||
.switch-control {
|
||||
align-items: center;
|
||||
align-content: center;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.switch-control h4 {
|
||||
margin: 0px;
|
||||
margin: 0px !important;
|
||||
padding: 0px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
@@ -306,30 +313,30 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#advanced-settings-div > button {
|
||||
#advanced-settings-div>button {
|
||||
background-color: var(--background-grey);
|
||||
box-shadow: 0px 2px 5px #000A;
|
||||
font-size:13px;
|
||||
box-shadow: 0px 2px 5px #000A;
|
||||
font-size: 13px;
|
||||
height: 40px;
|
||||
padding:0 20px;
|
||||
padding: 0 20px;
|
||||
}
|
||||
|
||||
#delete-options {
|
||||
font-size:13px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
#delete-options.ol-select > .ol-select-value:after {
|
||||
#delete-options.ol-select>.ol-select-value:after {
|
||||
content: "";
|
||||
}
|
||||
|
||||
#delete-options.ol-select > .ol-select-value svg {
|
||||
#delete-options.ol-select>.ol-select-value svg {
|
||||
background-color: transparent;
|
||||
position: absolute;
|
||||
right:2px;
|
||||
translate:0 1px;
|
||||
right: 2px;
|
||||
translate: 0 1px;
|
||||
}
|
||||
|
||||
#delete-options.ol-select > .ol-select-value svg * {
|
||||
#delete-options.ol-select>.ol-select-value svg * {
|
||||
fill: var(--primary-red);
|
||||
}
|
||||
|
||||
@@ -337,21 +344,21 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
background-color: var(--background-steel);
|
||||
}
|
||||
|
||||
#delete-options.ol-select > .ol-select-value:hover,
|
||||
#delete-options .ol-select-options > div:not(.hr):hover,
|
||||
#delete-options .ol-select-options > div:not(.hr):hover button,
|
||||
#delete-options .ol-select-options > div hr {
|
||||
#delete-options.ol-select>.ol-select-value:hover,
|
||||
#delete-options .ol-select-options>div:not(.hr):hover,
|
||||
#delete-options .ol-select-options>div:not(.hr):hover button,
|
||||
#delete-options .ol-select-options>div hr {
|
||||
background-color: var(--background-grey);
|
||||
}
|
||||
|
||||
#delete-options .ol-select-options > div:first-of-type {
|
||||
margin-top:12px;
|
||||
padding-top:0;
|
||||
#delete-options .ol-select-options>div:first-of-type {
|
||||
margin-top: 12px;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
#delete-options .ol-select-options > div:last-of-type {
|
||||
margin-bottom:12px;
|
||||
padding-bottom:0;
|
||||
#delete-options .ol-select-options>div:last-of-type {
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
#delete-options button {
|
||||
@@ -392,4 +399,4 @@ body.feature-forceShowUnitControlPanel #unit-control-panel {
|
||||
#advanced-settings-dialog:not([data-show-radio]) #radio-options,
|
||||
#advanced-settings-dialog:not([data-show-air-unit-checkboxes]) .air-unit-checkbox {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -146,12 +146,12 @@
|
||||
}
|
||||
|
||||
#fuel-percentage::before {
|
||||
content: url("/resources/theme/images/icons/fuel.svg");
|
||||
content: "";
|
||||
background-image: url("/resources/theme/images/icons/fuel.svg");
|
||||
background-size: 16px 16px;
|
||||
display: inline-block;
|
||||
filter: invert(100%);
|
||||
height: 16px;
|
||||
margin-right: 6px;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
#fuel-percentage::after {
|
||||
|
||||
@@ -72,6 +72,18 @@ form {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
button svg.fill-coalition * {
|
||||
fill: var(--primary-neutral) !important;
|
||||
}
|
||||
|
||||
button svg.fill-coalition[data-coalition="blue"] * {
|
||||
fill: var(--primary-blue) !important;
|
||||
}
|
||||
|
||||
button svg.fill-coalition[data-coalition="red"] * {
|
||||
fill: var(--primary-red) !important;
|
||||
}
|
||||
|
||||
.pill {
|
||||
background-color: var(--background-steel);
|
||||
border-radius: 999px;
|
||||
@@ -194,9 +206,21 @@ form {
|
||||
}
|
||||
|
||||
.ol-select:not(.ol-select-image)>.ol-select-value:after {
|
||||
content: url("/resources/theme/images/icons/chevron-down.svg");
|
||||
background-image: url("/resources/theme/images/icons/chevron-down.svg");
|
||||
content: "";
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.ol-select:not(.ol-select-image)>.ol-select-value.ol-select-warning:after {
|
||||
background-image: url("/resources/theme/images/icons/chevron-down-warning.svg") !important;
|
||||
}
|
||||
|
||||
.ol-select.is-open:not(.ol-select-image)>.ol-select-value:after {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.ol-select>.ol-select-options {
|
||||
@@ -372,7 +396,7 @@ button.ol-button-warning>svg:first-child {
|
||||
}
|
||||
|
||||
nav.ol-panel {
|
||||
column-gap: 20px;
|
||||
column-gap: 10px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 58px;
|
||||
@@ -384,8 +408,7 @@ nav.ol-panel> :last-child {
|
||||
|
||||
.ol-panel .ol-group {
|
||||
align-items: center;
|
||||
border-radius: var(--border-radius-sm);
|
||||
column-gap: 10px;
|
||||
column-gap: 12px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
@@ -426,6 +449,7 @@ nav.ol-panel> :last-child {
|
||||
-webkit-filter: invert(100%);
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.ol-panel .ol-group-button-toggle button.off::before {
|
||||
@@ -652,13 +676,13 @@ nav.ol-panel> :last-child {
|
||||
}
|
||||
|
||||
.ol-navbar-buttons-group button.off svg * {
|
||||
fill: white !important;
|
||||
stroke: white !important;
|
||||
fill: white;
|
||||
stroke: white;
|
||||
}
|
||||
|
||||
.ol-navbar-buttons-group button svg * {
|
||||
fill: var(--background-steel) !important;
|
||||
stroke: var(--background-steel) !important;
|
||||
fill: var(--background-steel);
|
||||
stroke: var(--background-steel);
|
||||
}
|
||||
|
||||
.ol-navbar-buttons-group .protectable button:first-of-type {
|
||||
@@ -777,6 +801,7 @@ nav.ol-panel> :last-child {
|
||||
width: 70%;
|
||||
max-width: 1200px;
|
||||
z-index: 999999;
|
||||
min-width: 500px;
|
||||
}
|
||||
|
||||
@media (min-width: 1700px) {
|
||||
@@ -827,11 +852,11 @@ nav.ol-panel> :last-child {
|
||||
justify-content: space-between;
|
||||
min-height: 75px;
|
||||
text-indent: 85px;
|
||||
row-gap: 5px;
|
||||
}
|
||||
|
||||
#splash-content #app-summary>* {
|
||||
height: fit-content;
|
||||
line-height: 25px;
|
||||
padding: 2px;
|
||||
white-space: nowrap;
|
||||
width: fit-content;
|
||||
@@ -839,10 +864,23 @@ nav.ol-panel> :last-child {
|
||||
|
||||
#splash-content .app-version {
|
||||
font-size: 11px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
#splash-content .new-version {
|
||||
animation: pulse 1.5s linear infinite;
|
||||
}
|
||||
|
||||
#splash-content .app-version:first-of-type {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
#splash-content #legal-stuff {
|
||||
width: 100%;
|
||||
width: 120%;
|
||||
text-wrap: wrap;
|
||||
max-height: 250px;
|
||||
overflow-x: hidden;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
#splash-content #legal-stuff h5 {
|
||||
@@ -852,11 +890,11 @@ nav.ol-panel> :last-child {
|
||||
#splash-content #legal-stuff p {
|
||||
color: #FFF7;
|
||||
font-size: 10px;
|
||||
width: 120%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 1700px) {
|
||||
#splash-content #legal-stuff p {
|
||||
#splash-content #legal-stuff {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
@@ -898,13 +936,7 @@ nav.ol-panel> :last-child {
|
||||
#loading-screen div {
|
||||
color: white;
|
||||
font-size: 18px;
|
||||
animation: blinker 3s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes blinker {
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
animation: pulse 3s linear infinite;
|
||||
}
|
||||
|
||||
.fade-out {
|
||||
@@ -962,16 +994,6 @@ nav.ol-panel> :last-child {
|
||||
}
|
||||
}
|
||||
|
||||
#hotgroup-panel {
|
||||
bottom: 40px;
|
||||
column-gap: 10px;
|
||||
display: flex;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
translate: -50%;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
#hotgroup-panel>div {
|
||||
align-items: center;
|
||||
background-color: var(--background-steel);
|
||||
@@ -1290,11 +1312,22 @@ dl.ol-data-grid dd {
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.ol-dialog-content table th {
|
||||
background-color: var(--background-grey);
|
||||
color:white;
|
||||
font-size:14px;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.ol-dialog-content table tbody th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.ol-dialog-footer {
|
||||
align-content: center;
|
||||
border-top: 1px solid var(--background-grey);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
justify-content: flex-end;
|
||||
padding-top: 15px;
|
||||
row-gap: 10px;
|
||||
}
|
||||
@@ -1328,6 +1361,11 @@ dl.ol-data-grid dd {
|
||||
height: 16px;
|
||||
margin-right: 10px;
|
||||
width: 16px;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.ol-checkbox input[type="checkbox"]:disabled:before {
|
||||
opacity: 10%;
|
||||
}
|
||||
|
||||
.ol-checkbox input[type="checkbox"]:checked::before {
|
||||
@@ -1531,7 +1569,7 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ol-contexmenu-button {
|
||||
.ol-context-menu-button {
|
||||
border: none;
|
||||
border-radius: 0px;
|
||||
height: 48px;
|
||||
@@ -1540,23 +1578,23 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
width: 48px;
|
||||
}
|
||||
|
||||
.ol-contexmenu-button:last-of-type {
|
||||
.ol-context-menu-button:last-of-type {
|
||||
border-bottom-right-radius: var(--border-radius-sm);
|
||||
border-top-right-radius: var(--border-radius-sm);
|
||||
}
|
||||
|
||||
[data-coalition="blue"].ol-contexmenu-button:hover,
|
||||
[data-coalition="blue"].ol-contexmenu-button.is-open {
|
||||
[data-coalition="blue"].ol-context-menu-button:hover,
|
||||
[data-coalition="blue"].ol-context-menu-button.is-open {
|
||||
background-color: var(--primary-blue)
|
||||
}
|
||||
|
||||
[data-coalition="red"].ol-contexmenu-button:hover,
|
||||
[data-coalition="red"].ol-contexmenu-button.is-open {
|
||||
[data-coalition="red"].ol-context-menu-button:hover,
|
||||
[data-coalition="red"].ol-context-menu-button.is-open {
|
||||
background-color: var(--primary-red)
|
||||
}
|
||||
|
||||
[data-coalition="neutral"].ol-contexmenu-button:hover,
|
||||
[data-coalition="neutral"].ol-contexmenu-button.is-open {
|
||||
[data-coalition="neutral"].ol-context-menu-button:hover,
|
||||
[data-coalition="neutral"].ol-context-menu-button.is-open {
|
||||
background-color: var(--primary-neutral)
|
||||
}
|
||||
|
||||
@@ -1567,6 +1605,24 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
fill: lightgray;
|
||||
}
|
||||
|
||||
#map-visibility-options .ol-select-options .ol-checkbox {
|
||||
font-size:13px;
|
||||
font-weight:400;
|
||||
padding:6px 15px;
|
||||
}
|
||||
|
||||
#map-visibility-options .ol-select-options .ol-checkbox:first-of-type {
|
||||
padding-top:12px;
|
||||
}
|
||||
|
||||
#map-visibility-options .ol-select-options .ol-checkbox:last-of-type {
|
||||
padding-bottom:12px;
|
||||
}
|
||||
|
||||
#map-visibility-options .ol-select-options .ol-checkbox label:hover span {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.ol-log-entry:first-of-type {
|
||||
border-top: 1px solid #FFFFFF44;
|
||||
}
|
||||
@@ -1574,3 +1630,93 @@ input[type=number]::-webkit-outer-spin-button {
|
||||
.ol-log-entry {
|
||||
border-bottom: 1px solid #FFFFFF44;
|
||||
}
|
||||
|
||||
.file-import-export {
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.file-import-export .ol-dialog-content {
|
||||
display:flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.file-import-export p {
|
||||
background-color: var(--background-grey);
|
||||
border-left:6px solid var(--secondary-blue-text);
|
||||
padding:12px;
|
||||
}
|
||||
|
||||
.file-import-export th {
|
||||
padding:4px 6px;
|
||||
}
|
||||
|
||||
.file-import-export tr td:first-child {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.file-import-export td {
|
||||
color:white;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.file-import-export .ol-checkbox {
|
||||
display:flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.file-import-export .ol-checkbox input::before {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.file-import-export .ol-checkbox span {
|
||||
display:none;
|
||||
}
|
||||
|
||||
.file-import-export button.start-transfer {
|
||||
background-color: var(--secondary-blue-text);
|
||||
border-color: var(--secondary-blue-text);
|
||||
}
|
||||
|
||||
.file-import-export .export-filename-container {
|
||||
display: flex;
|
||||
column-gap: 15px;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
padding: 10px 0px;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.file-import-export .export-filename-container input {
|
||||
width: 100%;
|
||||
background-color: var(--background-grey);
|
||||
appearance: none;
|
||||
-moz-appearance: none;
|
||||
-webkit-appearance: none;
|
||||
border-style: solid;
|
||||
border: 1px solid var(--background-steel);
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
padding: 4px;
|
||||
}
|
||||
|
||||
.file-import-export .export-filename-container img {
|
||||
height: 16px;
|
||||
width: 16px;
|
||||
filter: invert(100%);
|
||||
margin-left: -31px;
|
||||
transform: translateX(-15px);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.file-import-export .ol-dialog-footer button:first-of-type{
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M256 512A256 256 0 1 0 256 0a256 256 0 1 0 0 512zm0-352a96 96 0 1 1 0 192 96 96 0 1 1 0-192z"/></svg>
|
||||
|
After Width: | Height: | Size: 346 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="14" viewBox="0 0 448 512"><!--!Font Awesome Free 6.5.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M64 32C64 14.3 49.7 0 32 0S0 14.3 0 32V64 368 480c0 17.7 14.3 32 32 32s32-14.3 32-32V352l64.3-16.1c41.1-10.3 84.6-5.5 122.5 13.4c44.2 22.1 95.5 24.8 141.7 7.4l34.7-13c12.5-4.7 20.8-16.6 20.8-30V66.1c0-23-24.2-38-44.8-27.7l-9.6 4.8c-46.3 23.2-100.8 23.2-147.1 0c-35.1-17.6-75.4-22-113.5-12.5L64 48V32z"/></svg>
|
||||
|
After Width: | Height: | Size: 554 B |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.1 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M0 224.2C0 100.6 100.2 0 224 0h24c95.2 0 181.2 69.3 197.3 160.2c2.3 13 6.8 25.7 15.1 36l42 52.6c6.2 7.8 9.6 17.4 9.6 27.4c0 24.2-19.6 43.8-43.8 43.8H448v64c0 35.3-28.7 64-64 64H320v32c0 17.7-14.3 32-32 32H96c-17.7 0-32-14.3-32-32V407.3c0-16.7-6.9-32.5-17.1-45.8C16.6 322.4 0 274.1 0 224.2zM224 64c-8.8 0-16 7.2-16 16c0 33-39.9 49.5-63.2 26.2c-6.2-6.2-16.4-6.2-22.6 0s-6.2 16.4 0 22.6C145.5 152.1 129 192 96 192c-8.8 0-16 7.2-16 16s7.2 16 16 16c33 0 49.5 39.9 26.2 63.2c-6.2 6.2-6.2 16.4 0 22.6s16.4 6.2 22.6 0C168.1 286.5 208 303 208 336c0 8.8 7.2 16 16 16s16-7.2 16-16c0-33 39.9-49.5 63.2-26.2c6.2 6.2 16.4 6.2 22.6 0s6.2-16.4 0-22.6C302.5 263.9 319 224 352 224c8.8 0 16-7.2 16-16s-7.2-16-16-16c-33 0-49.5-39.9-26.2-63.2c6.2-6.2 6.2-16.4 0-22.6s-16.4-6.2-22.6 0C279.9 129.5 240 113 240 80c0-8.8-7.2-16-16-16zm-24 96a24 24 0 1 1 0 48 24 24 0 1 1 0-48zm40 80a16 16 0 1 1 32 0 16 16 0 1 1 -32 0z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="16" viewBox="0 0 512 512"><!--!Font Awesome Free 6.5.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M256 0c4.6 0 9.2 1 13.4 2.9L457.7 82.8c22 9.3 38.4 31 38.3 57.2c-.5 99.2-41.3 280.7-213.6 363.2c-16.7 8-36.1 8-52.8 0C57.3 420.7 16.5 239.2 16 140c-.1-26.2 16.3-47.9 38.3-57.2L242.7 2.9C246.8 1 251.4 0 256 0zm0 66.8V444.8C394 378 431.1 230.1 432 141.4L256 66.8l0 0z"/></svg>
|
||||
|
After Width: | Height: | Size: 519 B |
@@ -0,0 +1,3 @@
|
||||
<svg width="14" height="8" viewBox="0 0 14 8" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6.37109 7.37891C6.69922 7.73438 7.27344 7.73438 7.60156 7.37891L12.8516 2.12891C13.207 1.80078 13.207 1.22656 12.8516 0.898438C12.5234 0.542969 11.9492 0.542969 11.6211 0.898438L7 5.51953L2.35156 0.898438C2.02344 0.542969 1.44922 0.542969 1.12109 0.898438C0.765625 1.22656 0.765625 1.80078 1.12109 2.12891L6.37109 7.37891Z" fill="#ff5858"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 452 B |
@@ -1 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><!--! Font Awesome Pro 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM144 256a144 144 0 1 1 288 0 144 144 0 1 1 -288 0zm144-64c0 35.3-28.7 64-64 64c-7.1 0-13.9-1.2-20.3-3.3c-5.5-1.8-11.9 1.6-11.7 7.4c.3 6.9 1.3 13.8 3.2 20.7c13.7 51.2 66.4 81.6 117.6 67.9s81.6-66.4 67.9-117.6c-11.1-41.5-47.8-69.4-88.6-71.1c-5.8-.2-9.2 6.1-7.4 11.7c2.1 6.4 3.3 13.2 3.3 20.3z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" height="16" width="18" viewBox="0 0 576 512"><!--!Font Awesome Free 6.5.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2023 Fonticons, Inc.--><path d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM144 256a144 144 0 1 1 288 0 144 144 0 1 1 -288 0zm144-64c0 35.3-28.7 64-64 64c-7.1 0-13.9-1.2-20.3-3.3c-5.5-1.8-11.9 1.6-11.7 7.4c.3 6.9 1.3 13.8 3.2 20.7c13.7 51.2 66.4 81.6 117.6 67.9s81.6-66.4 67.9-117.6c-11.1-41.5-47.8-69.4-88.6-71.1c-5.8-.2-9.2 6.1-7.4 11.7c2.1 6.4 3.3 13.2 3.3 20.3z"/></svg>
|
||||
|
Before Width: | Height: | Size: 820 B After Width: | Height: | Size: 826 B |
@@ -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="M64 64C28.7 64 0 92.7 0 128V384c0 35.3 28.7 64 64 64H512c35.3 0 64-28.7 64-64V128c0-35.3-28.7-64-64-64H64zm16 64h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V144c0-8.8 7.2-16 16-16zM64 240c0-8.8 7.2-16 16-16h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V240zm16 80h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H80c-8.8 0-16-7.2-16-16V336c0-8.8 7.2-16 16-16zm80-176c0-8.8 7.2-16 16-16h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H176c-8.8 0-16-7.2-16-16V144zm16 80h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H176c-8.8 0-16-7.2-16-16V240c0-8.8 7.2-16 16-16zM160 336c0-8.8 7.2-16 16-16H400c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H176c-8.8 0-16-7.2-16-16V336zM272 128h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H272c-8.8 0-16-7.2-16-16V144c0-8.8 7.2-16 16-16zM256 240c0-8.8 7.2-16 16-16h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H272c-8.8 0-16-7.2-16-16V240zM368 128h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H368c-8.8 0-16-7.2-16-16V144c0-8.8 7.2-16 16-16zM352 240c0-8.8 7.2-16 16-16h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H368c-8.8 0-16-7.2-16-16V240zM464 128h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H464c-8.8 0-16-7.2-16-16V144c0-8.8 7.2-16 16-16zM448 240c0-8.8 7.2-16 16-16h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H464c-8.8 0-16-7.2-16-16V240zm16 80h32c8.8 0 16 7.2 16 16v32c0 8.8-7.2 16-16 16H464c-8.8 0-16-7.2-16-16V336c0-8.8 7.2-16 16-16z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
BIN
client/public/themes/olympus/images/parrot/parrot.png
Normal file
|
After Width: | Height: | Size: 41 KiB |
54
client/public/themes/parrot/images/parrot.svg
Normal file
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="iso-8859-1"?>
|
||||
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg height="800px" width="800px" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
|
||||
viewBox="0 0 460 460" xml:space="preserve">
|
||||
<g id="XMLID_2582_">
|
||||
<path id="XMLID_4_" style="fill:#871A27;" d="M353.16,459.97L353.16,459.97c-32.138-3.383-61.944-18.336-83.849-42.066
|
||||
L65.784,206.343c-6.885-7.157-3.266-19.083,6.439-21.217l26.729-5.878l258.429,272.378
|
||||
C360.487,455.023,357.74,460.452,353.16,459.97z"/>
|
||||
<path id="XMLID_2108_" style="fill:#A52531;" d="M403.222,459.97L403.222,459.97c-32.138-3.383-61.944-18.336-83.849-42.066
|
||||
L100.124,190l48.891-10.752l258.429,272.378C410.549,455.023,407.802,460.452,403.222,459.97z"/>
|
||||
<path id="XMLID_2018_" style="fill:#CC3248;" d="M454.455,459.97L454.455,459.97c-32.138-3.383-61.944-18.336-83.849-42.066
|
||||
L151.357,190l48.891-10.752l258.429,272.378C461.782,455.023,459.035,460.452,454.455,459.97z"/>
|
||||
<path id="XMLID_2391_" style="fill:#C1A991;" d="M280.347,380h-80.099l-9.48,37.873C188.984,425,182.572,430,175.217,430
|
||||
s-13.767-5-15.551-12.127L150.186,380h-0.277c-40.008,0-73.881-29.485-79.348-69.069L44.112,119.414
|
||||
C30.376,116.668,20.025,104.544,20.025,90H0c0-20.678,15.692-37.69,35.817-39.781C35.079,22.989,56.998,0,84.812,0l0,0
|
||||
c21.102,0,39.837,13.487,46.51,33.481l0.617,1.848c8.632,25.864,26.455,47.418,49.597,61.877
|
||||
c8.184,5.113,15.785,13.683,19.63,27.974L280.347,380z"/>
|
||||
<path id="XMLID_2087_" style="fill:#473C3F;" d="M180.223,420v10v10v5c0,8.284-6.724,15-15.019,15h-50.062
|
||||
c-2.765,0-5.006-2.239-5.006-5l0,0c0-8.284,6.724-15,15.019-15h35.043c5.53,0,10.012-4.477,10.012-10v-10H180.223z"/>
|
||||
<path id="XMLID_1701_" style="fill:#9B1C2B;" d="M280.347,380h-80.099l-9.48,37.873C188.984,425,182.572,430,175.217,430
|
||||
s-13.767-5-15.551-12.127L150.186,380h-0.277c-40.008,0-73.881-29.485-79.348-69.069L44.112,119.414L30.382,90H52.77
|
||||
c4.803,0,9.541-1.117,13.837-3.262l16.413-8.197c10.482-5.234,17.103-15.935,17.103-27.639C100.124,33.835,86.272,20,69.184,20
|
||||
H45.307C54.179,7.937,68.475,0,84.812,0l0,0c21.102,0,39.837,13.487,46.51,33.481l0.617,1.848
|
||||
c8.632,25.864,26.455,47.418,49.597,61.877c8.184,5.113,15.785,13.683,19.63,27.974L280.347,380z"/>
|
||||
<path id="XMLID_2389_" style="fill:#8C735D;" d="M40.05,90l4.062,29.414C30.376,116.668,20.025,104.544,20.025,90H0
|
||||
c0-3.454,0.447-6.803,1.27-10h37.667L40.05,90z"/>
|
||||
<path id="XMLID_1631_" style="fill:#AF8F6D;" d="M40.05,90H0c0-20.676,15.688-37.686,35.81-39.781
|
||||
c0.037,1.375,0.121,2.759,0.276,4.154L40.05,90z"/>
|
||||
<path id="XMLID_2088_" style="fill:#66313A;" d="M70.087,45c0,2.761-2.241,5-5.006,5s-5.006-2.239-5.006-5s2.241-5,5.006-5
|
||||
C67.845,40,70.087,42.239,70.087,45z"/>
|
||||
<path id="XMLID_1864_" style="fill:#720C1F;" d="M150.186,380h50.062l-5.006,20h-40.049L150.186,380z"/>
|
||||
<path id="XMLID_1836_" style="fill:#720C1F;" d="M331.261,397.796L246.772,194.5c-19.25-46.319-62.419-73.992-106.431-68.225
|
||||
l-33.801,4.429c-6.283,0.823-10.071,7.378-7.641,13.223l82.08,197.501c19.25,46.32,62.419,73.992,106.431,68.226l40.03-5.245
|
||||
C330.582,403.996,332.476,400.719,331.261,397.796z"/>
|
||||
<path id="XMLID_1924_" style="fill:#871A27;" d="M349.271,380.344l-39.554,8.084c-43.488,8.888-88.523-15.638-111.033-60.467
|
||||
l-95.979-191.149c-2.841-5.657,0.469-12.465,6.677-13.734l33.398-6.826c43.488-8.888,88.523,15.638,111.033,60.468l98.796,196.758
|
||||
C354.03,376.305,352.375,379.709,349.271,380.344z"/>
|
||||
<path id="XMLID_1905_" style="fill:#A52531;" d="M351.863,378.865c-39.327,2.272-78.009-21.643-98.331-62.115l-73.749-146.618
|
||||
c-1.957-3.897-0.994-8.339,1.823-11.141c37.969-0.535,74.788,23.122,94.439,62.258l76.565,152.227
|
||||
C353.548,375.345,353.139,377.459,351.863,378.865z"/>
|
||||
<path id="XMLID_1810_" style="fill:#82542E;" d="M349.27,380.344L310.841,385c-43.488,8.888-89.648-12.21-112.158-57.04
|
||||
l-66.414-132.268c34.538,30.088,78.926,43.67,122.276,34.81l23.828-4.87l74.236,147.845
|
||||
C354.029,376.305,352.374,379.709,349.27,380.344z"/>
|
||||
<path id="XMLID_1705_" style="fill:#684627;" d="M351.864,378.865c-0.656,0.723-1.538,1.263-2.592,1.479l-39.307,6.781
|
||||
c-43.488,8.888-88.77-14.335-111.28-59.165l-66.414-132.268c22.958,20,50.271,32.696,78.737,36.365l42.526,84.693
|
||||
C273.856,357.223,312.537,381.137,351.864,378.865z"/>
|
||||
<path id="XMLID_1636_" style="fill:#385056;" d="M349.27,380.344l-39.554,8.084c-43.488,8.888-88.523-15.638-111.033-60.467
|
||||
l-21.17-42.161c28.416,19.045,62.146,26.827,95.205,20.07l41.667-8.516l38.223,76.123
|
||||
C354.029,376.305,352.374,379.709,349.27,380.344z"/>
|
||||
<path id="XMLID_1807_" style="fill:#446772;" d="M249.716,308.257c7.802-0.018,15.644-0.792,23.45-2.388l41.667-8.516
|
||||
l38.223,76.123c0.594,1.184,0.644,2.466,0.281,3.606c-0.209,0.65-0.552,1.254-1.012,1.765c-0.005,0.006-0.01,0.012-0.016,0.018
|
||||
c-39.326,2.271-78.007-21.643-98.329-62.115L249.716,308.257z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
109
client/public/themes/parrot/theme.css
Normal file
@@ -0,0 +1,109 @@
|
||||
:root {
|
||||
/** Colours **/
|
||||
|
||||
/*** Coalition: neutral ***/
|
||||
--primary-neutral: #949ba7;
|
||||
--secondary-neutral-outline: #111111;
|
||||
--secondary-neutral-text: #111111;
|
||||
--unit-background-neutral: #CFD9E8;
|
||||
|
||||
/*** Coalition: blue ***/
|
||||
--primary-blue: #247be2;
|
||||
--secondary-blue-outline: #082e44;
|
||||
--secondary-blue-text: #017DC1;
|
||||
--unit-background-blue: #3BB9FF;
|
||||
|
||||
/*** Coalition: red ***/
|
||||
--primary-red: #ff5858;
|
||||
--secondary-red-outline: #262222;
|
||||
--secondary-red-text: #D42121;
|
||||
--unit-background-red: #FF5858;
|
||||
|
||||
/*** UI Colours **/
|
||||
--accent-amber: #ffd828;
|
||||
--accent-green: #8bff63;
|
||||
--accent-light-blue: #5ca7ff;
|
||||
--accent-dark-blue: #017DC1;
|
||||
--transparent-accent-light-blue: rgba(92, 167, 255, .33);
|
||||
--accent-light-red: #F5B6B6;
|
||||
|
||||
--background-grey: #3d4651;
|
||||
--background-slate-blue: #363c43;
|
||||
--background-offwhite: #f2f2f3;
|
||||
--background-steel: #202831;
|
||||
|
||||
--secondary-dark-steel: #181e25;
|
||||
--secondary-gunmetal-grey: #2f2f2f;
|
||||
--secondary-lighter-grey: #949ba7;
|
||||
--secondary-light-grey: #797e83;
|
||||
--secondary-semitransparent-white: #FFFFFFAA;
|
||||
--secondary-transparent-white: #FFFFFF30;
|
||||
--secondary-yellow: #ffd46893;
|
||||
|
||||
--background-hover: #f2f2f333;
|
||||
|
||||
--nav-text: #ECECEC;
|
||||
|
||||
--ol-select-secondary: #545F6C;
|
||||
--ol-switch-off:#686868;
|
||||
--ol-switch-undefined:#383838;
|
||||
|
||||
/*** General border radii **/
|
||||
--border-radius-xs: 2px;
|
||||
--border-radius-sm: 5px;
|
||||
--border-radius-md: 10px;
|
||||
--border-radius-lg: 15px;
|
||||
|
||||
/*** Fonts **/
|
||||
--font-weight-bolder: 600;
|
||||
|
||||
/*** Unit marker settings ***/
|
||||
/*** All markers **/
|
||||
--unit-border-radius: var(--border-radius-xs);
|
||||
--unit-font-size: 14px;
|
||||
--unit-font-weight: bolder;
|
||||
--unit-label-border-width: 2px;
|
||||
--unit-spotlight-fill: var(--secondary-yellow);
|
||||
--unit-spotlight-radius: 26px;
|
||||
--unit-stroke-width: 3px;
|
||||
--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%;
|
||||
--unit-ammo-border-width: 2px;
|
||||
--unit-ammo-radius: 2px;
|
||||
--unit-ammo-spacing: 2px;
|
||||
--unit-ammo-x: 0px;
|
||||
--unit-ammo-y: 30px;
|
||||
--unit-fuel-border-width: 2px;
|
||||
--unit-fuel-height: 6px;
|
||||
--unit-fuel-width: 36px;
|
||||
--unit-fuel-x: 0px;
|
||||
--unit-fuel-y: 22px;
|
||||
--unit-vvi-width: 4px;
|
||||
}
|
||||
|
||||
* {
|
||||
font-weight:600;
|
||||
}
|
||||
|
||||
svg {
|
||||
animation: spin linear infinite 1s;
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
from {
|
||||
transform:rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform:rotate(360deg);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,42 @@
|
||||
const express = require('express');
|
||||
const router = express.Router();
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
|
||||
// TODO should be user selectable or at least configurable from configuration file
|
||||
var theme = "olympus";
|
||||
var themesMap = {};
|
||||
|
||||
router.get('/theme/*', function (req, res, next) {
|
||||
res.redirect(req.url.replace("theme", "themes/" + theme));
|
||||
/* If this is the first time this session makes a request, create a uuid and save it to the map. Default theme is the olympus theme */
|
||||
if (!req.cookies.id) {
|
||||
const id = uuidv4();
|
||||
res.cookie('id', id, { httpOnly: true });
|
||||
themesMap[id] = "olympus";
|
||||
reqTheme = "olympus";
|
||||
}
|
||||
else {
|
||||
/* If it is present, recover the session theme from the map */
|
||||
if (!(req.cookies.id in themesMap))
|
||||
themesMap[req.cookies.id] = "olympus";
|
||||
reqTheme = themesMap[req.cookies.id];
|
||||
}
|
||||
|
||||
/* Yes, this in an easter egg! :D Feel free to ignore it, or activate the parrot theme to check what it does. Why parrots? The story is a bit long, come to the Discord and ask :D */
|
||||
if (reqTheme === "parrot" && !req.url.includes(".css"))
|
||||
res.redirect('/themes/parrot/images/parrot.svg');
|
||||
else
|
||||
res.redirect(req.url.replace("theme", "themes/" + reqTheme));
|
||||
});
|
||||
|
||||
router.put('/theme/:newTheme', function (req, res, next) {
|
||||
/* Add the theme to the map, if this session already has an id */
|
||||
const newTheme = req.params.newTheme;
|
||||
if (req.cookies.id) {
|
||||
themesMap[req.cookies.id] = newTheme;
|
||||
console.log("Theme set to " + newTheme + " for session " + req.cookies.id);
|
||||
} else {
|
||||
console.log("Failed to set theme to " + newTheme + ", no session id");
|
||||
}
|
||||
|
||||
res.end("Ok");
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
||||
|
Before Width: | Height: | Size: 2.6 MiB After Width: | Height: | Size: 7.7 MiB |
@@ -28,9 +28,9 @@ export const emissionsCountermeasures: string[] = ["silent", "attack", "defend",
|
||||
|
||||
export const ROEDescriptions: string[] = [
|
||||
"Free (Attack anyone)",
|
||||
"Designated (Attack the designated target only)",
|
||||
"Designated (Attack the designated target only) \nWARNING: Ground and Navy units don't respect this ROE, it will be equivalent to weapons FREE.",
|
||||
"",
|
||||
"Return (Only fire if fired upon)",
|
||||
"Return (Only fire if fired upon) \nWARNING: Ground and Navy units don't respect this ROE, it will be equivalent to weapons FREE.",
|
||||
"Hold (Never fire)"
|
||||
];
|
||||
|
||||
@@ -109,6 +109,20 @@ export const minimapBoundaries = [
|
||||
new LatLng(-56.874517, -43.316433),
|
||||
new LatLng(-49.097217, -43.316433),
|
||||
new LatLng(-49.097217, -79.418267)
|
||||
],
|
||||
[ // Normandy
|
||||
new LatLng(50.44, -3.29),
|
||||
new LatLng(48.12,-3.29),
|
||||
new LatLng(48.12, 3.70),
|
||||
new LatLng(50.44, 3.70),
|
||||
new LatLng(50.44, -3.29)
|
||||
],
|
||||
[ // Sinai
|
||||
new LatLng(34.312222, 28.523333),
|
||||
new LatLng(25.946944, 28.523333),
|
||||
new LatLng(25.946944, 36.897778),
|
||||
new LatLng(34.312222, 36.897778),
|
||||
new LatLng(34.312222, 28.523333)
|
||||
]
|
||||
];
|
||||
|
||||
@@ -119,6 +133,8 @@ export const mapBounds = {
|
||||
"PersianGulf": { bounds: new LatLngBounds([21.729393, 47.572675], [33.131584, 64.7313594]), zoom: 5 },
|
||||
"Caucasus": { bounds: new LatLngBounds([39.6170191, 27.634935], [47.3907982, 49.3101946]), zoom: 4 },
|
||||
"Falklands": { bounds: new LatLngBounds([-49.097217, -79.418267], [-56.874517, -43.316433]), zoom: 3 },
|
||||
"Normandy": { bounds: new LatLngBounds([50.44, -3.29], [48.12, 3.70]), zoom: 5 },
|
||||
"SinaiMap": { bounds: new LatLngBounds([34.312222, 28.523333], [25.946944, 36.897778]), zoom: 4 },
|
||||
}
|
||||
|
||||
export const mapLayers = {
|
||||
@@ -126,7 +142,7 @@ export const mapLayers = {
|
||||
urlTemplate: "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
|
||||
minZoom: 1,
|
||||
maxZoom: 19,
|
||||
attribution: "Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, GetApp().getMap()ping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community"
|
||||
attribution: "Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Mapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community"
|
||||
},
|
||||
"USGS Topo": {
|
||||
urlTemplate: 'https://basemap.nationalmap.gov/arcgis/rest/services/USGSTopo/MapServer/tile/{z}/{y}/{x}',
|
||||
@@ -167,11 +183,19 @@ export const COALITIONAREA_DRAW_POLYGON = "Draw Coalition Area";
|
||||
export const visibilityControls: string[] = ["human", "dcs", "aircraft", "helicopter", "groundunit-sam", "groundunit", "navyunit", "airbase"];
|
||||
export const visibilityControlsTypes: string[][] = [["human"], ["dcs"], ["aircraft"], ["helicopter"], ["groundunit-sam"], ["groundunit"], ["navyunit"], ["airbase"]];
|
||||
export const visibilityControlsTooltips: string[] = ["Toggle human players visibility", "Toggle DCS controlled units visibility", "Toggle aircrafts visibility", "Toggle helicopter visibility", "Toggle SAM units visibility", "Toggle ground units (not SAM) visibility", "Toggle navy units visibility", "Toggle airbases visibility"];
|
||||
|
||||
export const MAP_MARKER_CONTROLS: MapMarkerVisibilityControl[] = [{
|
||||
"name": "Human",
|
||||
"image": "visibility/human.svg",
|
||||
"toggles": ["human"],
|
||||
"tooltip": "Toggle human players' visibility"
|
||||
}, {
|
||||
"image": "visibility/head-side-virus-solid.svg",
|
||||
"isProtected": false,
|
||||
"name": "Olympus",
|
||||
"protectable": false,
|
||||
"toggles": ["olympus"],
|
||||
"tooltip": "Toggle Olympus-controlled units' visibility"
|
||||
}, {
|
||||
"image": "visibility/dcs.svg",
|
||||
"isProtected": true,
|
||||
@@ -211,9 +235,9 @@ export const MAP_MARKER_CONTROLS: MapMarkerVisibilityControl[] = [{
|
||||
"tooltip": "Toggle airbase' visibility"
|
||||
}];
|
||||
|
||||
export const IADSTypes = ["AAA", "MANPADS", "SAM Site", "Radar"];
|
||||
export const IADSDensities: { [key: string]: number } = { "AAA": 0.8, "MANPADS": 0.3, "SAM Site": 0.1, "Radar": 0.05 };
|
||||
|
||||
export const IADSTypes = ["AAA", "SAM Site", "Radar (EWR)"];
|
||||
export const IADSDensities: { [key: string]: number } = { "AAA": 0.8, "SAM Site": 0.1, "Radar (EWR)": 0.05 };
|
||||
export const GROUND_UNIT_AIR_DEFENCE_REGEX:RegExp = /(\b(AAA|SAM|MANPADS?|[mM]anpads?)|[sS]tinger\b)/;
|
||||
export const HIDE_GROUP_MEMBERS = "Hide group members when zoomed out";
|
||||
export const SHOW_UNIT_LABELS = "Show unit labels (L)";
|
||||
export const SHOW_UNITS_ENGAGEMENT_RINGS = "Show units threat range rings (Q)";
|
||||
@@ -243,6 +267,7 @@ export enum DataIndexes {
|
||||
horizontalVelocity,
|
||||
verticalVelocity,
|
||||
heading,
|
||||
track,
|
||||
isActiveTanker,
|
||||
isActiveAWACS,
|
||||
onOff,
|
||||
|
||||
@@ -27,8 +27,8 @@ export class CoalitionAreaContextMenu extends ContextMenu {
|
||||
super(ID);
|
||||
|
||||
/* Create the coalition switch */
|
||||
this.#coalitionSwitch = new Switch("coalition-area-switch", (value: boolean) => this.#onSwitchClick(value));
|
||||
this.#coalitionSwitch.setValue(false);
|
||||
this.#coalitionSwitch = new Switch("coalition-area-switch", (value: boolean) => this.#onSwitchClick(value), true);
|
||||
this.#coalitionSwitch.setValue(true);
|
||||
|
||||
/* Create the controls of the IADS creation submenu */
|
||||
this.#iadsTypesDropdown = new Dropdown("iads-units-type-options", () => { });
|
||||
@@ -68,7 +68,8 @@ export class CoalitionAreaContextMenu extends ContextMenu {
|
||||
const area = this.getCoalitionArea();
|
||||
if (area)
|
||||
getApp().getUnitsManager().createIADS(area, getCheckboxOptions(this.#iadsTypesDropdown), getCheckboxOptions(this.#iadsErasDropdown), getCheckboxOptions(this.#iadsRangesDropdown), this.#iadsDensitySlider.getValue(), this.#iadsDistributionSlider.getValue());
|
||||
})
|
||||
this.hide();
|
||||
});
|
||||
this.hide();
|
||||
}
|
||||
|
||||
@@ -110,7 +111,7 @@ export class CoalitionAreaContextMenu extends ContextMenu {
|
||||
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => {
|
||||
element.setAttribute("data-coalition", this.getCoalitionArea()?.getCoalition())
|
||||
});
|
||||
this.#coalitionSwitch.setValue(this.getCoalitionArea()?.getCoalition() === "red");
|
||||
this.#coalitionSwitch.setValue(this.getCoalitionArea()?.getCoalition() === "blue");
|
||||
}
|
||||
|
||||
/** Get the CoalitionArea object the contextmenu is editing
|
||||
@@ -146,11 +147,11 @@ export class CoalitionAreaContextMenu extends ContextMenu {
|
||||
|
||||
/** Callback event called when the coalition switch is clicked to change the coalition of the CoalitionArea
|
||||
*
|
||||
* @param value Switch position (false: blue, true: red)
|
||||
* @param value Switch position (false: red, true: blue)
|
||||
*/
|
||||
#onSwitchClick(value: boolean) {
|
||||
if (getApp().getMissionManager().getCommandModeOptions().commandMode == GAME_MASTER) {
|
||||
this.getCoalitionArea()?.setCoalition(value ? "red" : "blue");
|
||||
this.getCoalitionArea()?.setCoalition(value ? "blue" : "red");
|
||||
this.getContainer()?.querySelectorAll('[data-coalition]').forEach((element: any) => {
|
||||
element.setAttribute("data-coalition", this.getCoalitionArea()?.getCoalition())
|
||||
});
|
||||
|
||||
@@ -26,7 +26,7 @@ export class Dropdown {
|
||||
|
||||
if (options != null) this.setOptions(options);
|
||||
|
||||
this.#value.addEventListener("click", (ev) => { this.#toggle(); });
|
||||
(this.#container.querySelector(".ol-select-value") as HTMLElement)?.addEventListener("click", (ev) => { this.#toggle(); });
|
||||
|
||||
document.addEventListener("click", (ev) => {
|
||||
if (!(this.#value.contains(ev.target as Node) || this.#options.contains(ev.target as Node) || this.#container.contains(ev.target as Node))) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import { Dropdown } from "./dropdown";
|
||||
import { Slider } from "./slider";
|
||||
import { UnitDatabase } from "../unit/databases/unitdatabase";
|
||||
import { getApp } from "..";
|
||||
import { GAME_MASTER } from "../constants/constants";
|
||||
import { GAME_MASTER, GROUND_UNIT_AIR_DEFENCE_REGEX } from "../constants/constants";
|
||||
import { Airbase } from "../mission/airbase";
|
||||
import { ftToM } from "../other/utils";
|
||||
import { aircraftDatabase } from "../unit/databases/aircraftdatabase";
|
||||
@@ -12,8 +12,14 @@ import { groundUnitDatabase } from "../unit/databases/groundunitdatabase";
|
||||
import { navyUnitDatabase } from "../unit/databases/navyunitdatabase";
|
||||
import { UnitBlueprint, UnitSpawnOptions, UnitSpawnTable } from "../interfaces";
|
||||
|
||||
export class UnitSpawnMenu {
|
||||
/** This is the common code for all the unit spawn menus. It is shown both when right clicking on the map and when spawning from airbase.
|
||||
*
|
||||
*/
|
||||
|
||||
export abstract class UnitSpawnMenu {
|
||||
protected showRangeCircles: boolean = false;
|
||||
protected unitTypeFilter = (unit:any) => { return true; };
|
||||
/* Default options */
|
||||
protected spawnOptions: UnitSpawnOptions = {
|
||||
roleType: "",
|
||||
name: "",
|
||||
@@ -31,8 +37,9 @@ export class UnitSpawnMenu {
|
||||
#unitDatabase: UnitDatabase;
|
||||
#countryCodes: any;
|
||||
#orderByRole: boolean;
|
||||
protected unitTypeFilter = (unit:any) => { return true; };
|
||||
|
||||
#showLoadout: boolean = true;
|
||||
#showAltitudeSlider: boolean = true;
|
||||
|
||||
/* Controls */
|
||||
#unitRoleTypeDropdown: Dropdown;
|
||||
#unitLabelDropdown: Dropdown;
|
||||
@@ -44,11 +51,18 @@ export class UnitSpawnMenu {
|
||||
|
||||
/* HTML Elements */
|
||||
#deployUnitButtonEl: HTMLButtonElement;
|
||||
#unitCountDivider: HTMLDivElement;
|
||||
#unitLoadoutPreviewEl: HTMLDivElement;
|
||||
#unitImageEl: HTMLImageElement;
|
||||
#unitLoadoutListEl: HTMLDivElement;
|
||||
#descriptionDiv: HTMLDivElement;
|
||||
#abilitiesDiv: HTMLDivElement;
|
||||
#advancedOptionsDiv: HTMLDivElement;
|
||||
#unitInfoDiv: HTMLDivElement;
|
||||
#advancedOptionsToggle: HTMLDivElement;
|
||||
#advancedOptionsText: HTMLDivElement;
|
||||
#unitInfoToggle: HTMLDivElement;
|
||||
#unitInfoText: HTMLDivElement;
|
||||
|
||||
/* Range circle previews */
|
||||
#engagementCircle: Circle;
|
||||
@@ -60,20 +74,20 @@ export class UnitSpawnMenu {
|
||||
this.#orderByRole = orderByRole;
|
||||
|
||||
/* Create the dropdowns and the altitude slider */
|
||||
this.#unitRoleTypeDropdown = new Dropdown(null, (roleType: string) => this.#setUnitRoleType(roleType), undefined, "Unit type");
|
||||
this.#unitLabelDropdown = new Dropdown(null, (name: string) => this.#setUnitName(name), undefined, "Unit label");
|
||||
this.#unitLoadoutDropdown = new Dropdown(null, (loadout: string) => this.#setUnitLoadout(loadout), undefined, "Unit loadout");
|
||||
this.#unitCountDropdown = new Dropdown(null, (count: string) => this.#setUnitCount(count), undefined, "Unit count");
|
||||
this.#unitCountryDropdown = new Dropdown(null, () => { /* Custom button implementation */ }, undefined, "Unit country");
|
||||
this.#unitLiveryDropdown = new Dropdown(null, (livery: string) => this.#setUnitLivery(livery), undefined, "Unit livery");
|
||||
this.#unitRoleTypeDropdown = new Dropdown(null, (roleType: string) => this.#setUnitRoleType(roleType), undefined, "Role");
|
||||
this.#unitLabelDropdown = new Dropdown(null, (name: string) => this.#setUnitName(name), undefined, "Type");
|
||||
this.#unitLoadoutDropdown = new Dropdown(null, (loadout: string) => this.#setUnitLoadout(loadout), undefined, "Loadout");
|
||||
this.#unitCountDropdown = new Dropdown(null, (count: string) => this.#setUnitCount(count), undefined, "Count");
|
||||
this.#unitCountryDropdown = new Dropdown(null, () => { /* Custom button implementation */ }, undefined, "Country");
|
||||
this.#unitLiveryDropdown = new Dropdown(null, (livery: string) => this.#setUnitLivery(livery), undefined, "Livery");
|
||||
this.#unitSpawnAltitudeSlider = new Slider(null, 0, 1000, "ft", (value: number) => { this.spawnOptions.altitude = ftToM(value); }, { title: "Spawn altitude" });
|
||||
|
||||
/* The unit label and unit count are in the same "row" for clarity and compactness */
|
||||
var unitLabelCountContainerEl = document.createElement("div");
|
||||
unitLabelCountContainerEl.classList.add("unit-label-count-container");
|
||||
var divider = document.createElement("div");
|
||||
divider.innerText = "x";
|
||||
unitLabelCountContainerEl.append(this.#unitLabelDropdown.getContainer(), divider, this.#unitCountDropdown.getContainer());
|
||||
this.#unitCountDivider = document.createElement("div");
|
||||
this.#unitCountDivider.innerText = "x";
|
||||
unitLabelCountContainerEl.append(this.#unitLabelDropdown.getContainer(), this.#unitCountDivider, this.#unitCountDropdown.getContainer());
|
||||
|
||||
/* Create the unit image and loadout elements */
|
||||
this.#unitImageEl = document.createElement("img");
|
||||
@@ -84,38 +98,37 @@ export class UnitSpawnMenu {
|
||||
this.#unitLoadoutListEl.classList.add("unit-loadout-list");
|
||||
this.#unitLoadoutPreviewEl.append(this.#unitImageEl, this.#unitLoadoutListEl);
|
||||
|
||||
/* Create the divider and the advanced options collapsible div */
|
||||
var advancedOptionsDiv = document.createElement("div");
|
||||
advancedOptionsDiv.classList.add("contextmenu-advanced-options", "hide");
|
||||
var advancedOptionsToggle = document.createElement("div");
|
||||
advancedOptionsToggle.classList.add("contextmenu-advanced-options-toggle");
|
||||
var advancedOptionsText = document.createElement("div");
|
||||
advancedOptionsText.innerText = "Advanced options";
|
||||
var advancedOptionsHr = document.createElement("hr");
|
||||
advancedOptionsToggle.append(advancedOptionsText, advancedOptionsHr);
|
||||
advancedOptionsToggle.addEventListener("click", () => {
|
||||
advancedOptionsDiv.classList.toggle("hide");
|
||||
/* Create the advanced options collapsible div */
|
||||
this.#advancedOptionsDiv = document.createElement("div");
|
||||
this.#advancedOptionsDiv.classList.add("contextmenu-advanced-options", "hide");
|
||||
this.#advancedOptionsToggle = document.createElement("div");
|
||||
this.#advancedOptionsToggle.classList.add("contextmenu-advanced-options-toggle");
|
||||
this.#advancedOptionsText = document.createElement("div");
|
||||
this.#advancedOptionsText.innerText = "Faction / Liveries";
|
||||
this.#advancedOptionsToggle.append(this.#advancedOptionsText);
|
||||
this.#advancedOptionsToggle.addEventListener("click", () => {
|
||||
this.#advancedOptionsToggle.classList.toggle("is-open");
|
||||
this.#advancedOptionsDiv.classList.toggle("hide");
|
||||
this.#container.dispatchEvent(new Event("resize"));
|
||||
});
|
||||
advancedOptionsDiv.append(this.#unitCountryDropdown.getContainer(), this.#unitLiveryDropdown.getContainer(),
|
||||
this.#unitSpawnAltitudeSlider.getContainer() as HTMLElement);
|
||||
this.#advancedOptionsDiv.append(this.#unitCountryDropdown.getContainer(), this.#unitLiveryDropdown.getContainer());
|
||||
|
||||
/* Create the divider and the metadata collapsible div */
|
||||
var metadataDiv = document.createElement("div");
|
||||
metadataDiv.classList.add("contextmenu-metadata", "hide");
|
||||
var metadataToggle = document.createElement("div");
|
||||
metadataToggle.classList.add("contextmenu-metadata-toggle");
|
||||
var metadataText = document.createElement("div");
|
||||
metadataText.innerText = "Info";
|
||||
var metadataHr = document.createElement("hr");
|
||||
metadataToggle.append(metadataText, metadataHr);
|
||||
metadataToggle.addEventListener("click", () => {
|
||||
metadataDiv.classList.toggle("hide");
|
||||
/* Create the unit info collapsible div */
|
||||
this.#unitInfoDiv = document.createElement("div");
|
||||
this.#unitInfoDiv.classList.add("contextmenu-metadata", "hide");
|
||||
this.#unitInfoToggle = document.createElement("div");
|
||||
this.#unitInfoToggle.classList.add("contextmenu-metadata-toggle");
|
||||
this.#unitInfoText = document.createElement("div");
|
||||
this.#unitInfoText.innerText = "Unit information";
|
||||
this.#unitInfoToggle.append(this.#unitInfoText);
|
||||
this.#unitInfoToggle.addEventListener("click", () => {
|
||||
this.#unitInfoToggle.classList.toggle("is-open");
|
||||
this.#unitInfoDiv.classList.toggle("hide");
|
||||
this.#container.dispatchEvent(new Event("resize"));
|
||||
});
|
||||
this.#descriptionDiv = document.createElement("div");
|
||||
this.#abilitiesDiv = document.createElement("div");
|
||||
metadataDiv.append(this.#descriptionDiv, this.#abilitiesDiv);
|
||||
this.#unitInfoDiv.append(this.#descriptionDiv, this.#abilitiesDiv);
|
||||
|
||||
/* Create the unit deploy button */
|
||||
this.#deployUnitButtonEl = document.createElement("button");
|
||||
@@ -128,8 +141,8 @@ export class UnitSpawnMenu {
|
||||
});
|
||||
|
||||
/* Assemble all components */
|
||||
this.#container.append(this.#unitRoleTypeDropdown.getContainer(), unitLabelCountContainerEl, this.#unitLoadoutDropdown.getContainer(),
|
||||
this.#unitLoadoutPreviewEl, advancedOptionsToggle, advancedOptionsDiv, metadataToggle, metadataDiv, this.#deployUnitButtonEl);
|
||||
this.#container.append(this.#unitRoleTypeDropdown.getContainer(), unitLabelCountContainerEl, this.#unitLoadoutDropdown.getContainer(), this.#unitSpawnAltitudeSlider.getContainer() as HTMLElement,
|
||||
this.#unitLoadoutPreviewEl, this.#advancedOptionsToggle, this.#advancedOptionsDiv, this.#unitInfoToggle, this.#unitInfoDiv, this.#deployUnitButtonEl);
|
||||
|
||||
/* Load the country codes from the public folder */
|
||||
var xhr = new XMLHttpRequest();
|
||||
@@ -144,8 +157,29 @@ export class UnitSpawnMenu {
|
||||
};
|
||||
xhr.send();
|
||||
|
||||
/* Create the range circle previews */
|
||||
this.#engagementCircle = new Circle(this.spawnOptions.latlng, { radius: 0, weight: 4, opacity: 0.8, fillOpacity: 0, dashArray: "4 8", interactive: false, bubblingMouseEvents: false });
|
||||
this.#acquisitionCircle = new Circle(this.spawnOptions.latlng, { radius: 0, weight: 2, opacity: 0.8, fillOpacity: 0, dashArray: "8 12", interactive: false, bubblingMouseEvents: false });
|
||||
|
||||
/* Event listeners */
|
||||
this.#container.addEventListener("unitRoleTypeChanged", () => {
|
||||
/* Shown the unit label and the unit count dropdowns */
|
||||
this.#unitLabelDropdown.show();
|
||||
this.#unitCountDivider.classList.remove("hide");
|
||||
this.#unitCountDropdown.show();
|
||||
|
||||
/* Hide all the other components */
|
||||
this.#unitLoadoutDropdown.hide();
|
||||
this.#unitSpawnAltitudeSlider.hide();
|
||||
this.#unitLoadoutPreviewEl.classList.add("hide");
|
||||
this.#advancedOptionsDiv.classList.add("hide");
|
||||
this.#unitInfoDiv.classList.add("hide");
|
||||
this.#advancedOptionsText.classList.add("hide");
|
||||
this.#advancedOptionsToggle.classList.add("hide");
|
||||
this.#unitInfoText.classList.add("hide");
|
||||
this.#unitInfoToggle.classList.add("hide");
|
||||
|
||||
/* Disable the spawn button */
|
||||
this.#deployUnitButtonEl.disabled = true;
|
||||
this.#unitLabelDropdown.reset();
|
||||
this.#unitLoadoutListEl.replaceChildren();
|
||||
@@ -153,6 +187,7 @@ export class UnitSpawnMenu {
|
||||
this.#unitImageEl.classList.toggle("hide", true);
|
||||
this.#unitLiveryDropdown.reset();
|
||||
|
||||
/* Populate the labels dropdown from the database */
|
||||
var blueprints: UnitBlueprint[] = [];
|
||||
if (this.#orderByRole)
|
||||
blueprints = this.#unitDatabase.getByRole(this.spawnOptions.roleType);
|
||||
@@ -175,7 +210,7 @@ export class UnitSpawnMenu {
|
||||
let name = this.#unitLabelDropdown.getOptionsList()[idx];
|
||||
let element = this.#unitLabelDropdown.getOptionElements()[idx] as HTMLElement;
|
||||
let entry = this.#unitDatabase.getByName(name);
|
||||
if (entry) {
|
||||
if (entry && entry.tags?.trim() !== "") {
|
||||
element.querySelectorAll("button")[0]?.append(...(entry.tags?.split(",").map((tag: string) => {
|
||||
tag = tag.trim();
|
||||
let el = document.createElement("div");
|
||||
@@ -188,8 +223,10 @@ export class UnitSpawnMenu {
|
||||
}
|
||||
}
|
||||
|
||||
/* Request resizing */
|
||||
this.#container.dispatchEvent(new Event("resize"));
|
||||
|
||||
/* Reset the spawn options */
|
||||
this.spawnOptions.name = "";
|
||||
this.spawnOptions.loadout = undefined;
|
||||
this.spawnOptions.liveryID = undefined;
|
||||
@@ -198,23 +235,43 @@ export class UnitSpawnMenu {
|
||||
})
|
||||
|
||||
this.#container.addEventListener("unitLabelChanged", () => {
|
||||
/* If enabled, show the altitude slideer and loadouts section */
|
||||
if (this.#showAltitudeSlider)
|
||||
this.#unitSpawnAltitudeSlider.show();
|
||||
|
||||
if (this.#showLoadout) {
|
||||
this.#unitLoadoutDropdown.show();
|
||||
this.#unitLoadoutPreviewEl.classList.remove("hide");
|
||||
}
|
||||
|
||||
/* Show the advanced options and unit info sections */
|
||||
this.#advancedOptionsText.classList.remove("hide");
|
||||
this.#advancedOptionsToggle.classList.remove("hide");
|
||||
this.#advancedOptionsToggle.classList.remove("is-open");
|
||||
this.#unitInfoText.classList.remove("hide");
|
||||
this.#unitInfoToggle.classList.remove("hide");
|
||||
this.#unitInfoToggle.classList.remove("is-open");
|
||||
|
||||
/* Enable the spawn button */
|
||||
this.#deployUnitButtonEl.disabled = false;
|
||||
|
||||
/* If enabled, populate the loadout dropdown */
|
||||
if (!this.#unitLoadoutDropdown.isHidden()) {
|
||||
this.#unitLoadoutDropdown.setOptions(this.#unitDatabase.getLoadoutNamesByRole(this.spawnOptions.name, this.spawnOptions.roleType));
|
||||
this.#unitLoadoutDropdown.selectValue(0);
|
||||
}
|
||||
|
||||
/* Get the unit data from the db */
|
||||
var blueprint = this.#unitDatabase.getByName(this.spawnOptions.name);
|
||||
|
||||
|
||||
/* Shown the unit silhouette */
|
||||
this.#unitImageEl.src = `images/units/${blueprint?.filename}`;
|
||||
this.#unitImageEl.classList.toggle("hide", !(blueprint?.filename !== undefined));
|
||||
this.#unitImageEl.classList.toggle("hide", !(blueprint?.filename !== undefined && blueprint?.filename !== ''));
|
||||
|
||||
/* Set the livery options */
|
||||
this.#setUnitLiveryOptions();
|
||||
|
||||
this.#container.dispatchEvent(new Event("resize"));
|
||||
this.#computeSpawnPoints();
|
||||
|
||||
/* Populate the description and abilities sections */
|
||||
this.#descriptionDiv.replaceChildren();
|
||||
this.#abilitiesDiv.replaceChildren();
|
||||
|
||||
@@ -235,10 +292,16 @@ export class UnitSpawnMenu {
|
||||
}
|
||||
}
|
||||
|
||||
/* Show the range circles */
|
||||
this.showCirclesPreviews();
|
||||
|
||||
/* Request resizing */
|
||||
this.#container.dispatchEvent(new Event("resize"));
|
||||
this.#computeSpawnPoints();
|
||||
})
|
||||
|
||||
this.#container.addEventListener("unitLoadoutChanged", () => {
|
||||
/* Update the loadout information */
|
||||
var items = this.spawnOptions.loadout?.items.map((item: any) => { return `${item.quantity}x ${item.name}`; });
|
||||
if (items != undefined) {
|
||||
items.length == 0 ? items.push("Empty loadout") : "";
|
||||
@@ -255,10 +318,12 @@ export class UnitSpawnMenu {
|
||||
})
|
||||
|
||||
this.#container.addEventListener("unitCountChanged", () => {
|
||||
/* Recompute the spawn points */
|
||||
this.#computeSpawnPoints();
|
||||
})
|
||||
|
||||
this.#container.addEventListener("unitCountryChanged", () => {
|
||||
/* Get the unit liveries by country */
|
||||
this.#setUnitLiveryOptions();
|
||||
})
|
||||
|
||||
@@ -267,13 +332,13 @@ export class UnitSpawnMenu {
|
||||
})
|
||||
|
||||
document.addEventListener('activeCoalitionChanged', () => {
|
||||
/* If the coalition changed, update the circle previews to set the colours */
|
||||
this.showCirclesPreviews();
|
||||
});
|
||||
|
||||
this.#engagementCircle = new Circle(this.spawnOptions.latlng, { radius: 0, weight: 4, opacity: 0.8, fillOpacity: 0, dashArray: "4 8", interactive: false, bubblingMouseEvents: false });
|
||||
this.#acquisitionCircle = new Circle(this.spawnOptions.latlng, { radius: 0, weight: 2, opacity: 0.8, fillOpacity: 0, dashArray: "8 12", interactive: false, bubblingMouseEvents: false });
|
||||
}
|
||||
|
||||
abstract deployUnits(spawnOptions: UnitSpawnOptions, unitsCount: number): void;
|
||||
|
||||
getContainer() {
|
||||
return this.#container;
|
||||
}
|
||||
@@ -283,7 +348,10 @@ export class UnitSpawnMenu {
|
||||
}
|
||||
|
||||
reset() {
|
||||
/* Disable the spawn button */
|
||||
this.#deployUnitButtonEl.disabled = true;
|
||||
|
||||
/* Reset all the dropdowns */
|
||||
this.#unitRoleTypeDropdown.reset();
|
||||
this.#unitLabelDropdown.reset();
|
||||
this.#unitLiveryDropdown.reset();
|
||||
@@ -292,19 +360,37 @@ export class UnitSpawnMenu {
|
||||
else
|
||||
this.#unitRoleTypeDropdown.setOptions(this.#unitDatabase.getTypes(this.unitTypeFilter));
|
||||
|
||||
/* Reset the contents of the div elements */
|
||||
this.#unitLoadoutListEl.replaceChildren();
|
||||
this.#unitLoadoutDropdown.reset();
|
||||
this.#unitImageEl.classList.toggle("hide", true);
|
||||
this.#descriptionDiv.replaceChildren();
|
||||
this.#abilitiesDiv.replaceChildren();
|
||||
|
||||
this.setCountries();
|
||||
this.#container.dispatchEvent(new Event("resize"));
|
||||
/* Hide everything but the unit type dropdown */
|
||||
this.#unitLabelDropdown.hide();
|
||||
this.#unitCountDivider.classList.add("hide");
|
||||
this.#unitCountDropdown.hide();
|
||||
this.#unitLoadoutDropdown.hide();
|
||||
this.#unitSpawnAltitudeSlider.hide();
|
||||
this.#unitLoadoutPreviewEl.classList.add("hide");
|
||||
this.#advancedOptionsDiv.classList.add("hide");
|
||||
this.#unitInfoDiv.classList.add("hide");
|
||||
this.#advancedOptionsText.classList.add("hide");
|
||||
this.#advancedOptionsToggle.classList.add("hide");
|
||||
this.#unitInfoText.classList.add("hide");
|
||||
this.#unitInfoToggle.classList.add("hide");
|
||||
|
||||
/* Get the countries and clear the circle previews */
|
||||
this.setCountries();
|
||||
this.clearCirclesPreviews();
|
||||
|
||||
/* Request resizing */
|
||||
this.#container.dispatchEvent(new Event("resize"));
|
||||
}
|
||||
|
||||
setCountries() {
|
||||
/* Create the countries dropdown elements (with the little flags) */
|
||||
var coalitions = getApp().getMissionManager().getCoalitions();
|
||||
var countries = Object.values(coalitions[getApp().getActiveCoalition() as keyof typeof coalitions]);
|
||||
this.#unitCountryDropdown.setOptionsElements(this.#createCountryButtons(this.#unitCountryDropdown, countries, (country: string) => { this.#setUnitCountry(country) }));
|
||||
@@ -315,13 +401,6 @@ export class UnitSpawnMenu {
|
||||
}
|
||||
}
|
||||
|
||||
refreshOptions() {
|
||||
//if (!this.#unitDatabase.getTypes().includes(this.#unitTypeDropdown.getValue()))
|
||||
// this.reset();
|
||||
//if (!this.#unitDatabase.getByType(this.#unitTypeDropdown.getValue()).map((blueprint) => { return blueprint.label }).includes(this.#unitLabelDropdown.getValue()))
|
||||
// this.resetUnitLabel();
|
||||
}
|
||||
|
||||
showCirclesPreviews() {
|
||||
this.clearCirclesPreviews();
|
||||
|
||||
@@ -425,6 +504,14 @@ export class UnitSpawnMenu {
|
||||
return this.#unitSpawnAltitudeSlider;
|
||||
}
|
||||
|
||||
setShowLoadout(showLoadout: boolean) {
|
||||
this.#showLoadout = showLoadout;
|
||||
}
|
||||
|
||||
setShowAltitudeSlider(showAltitudeSlider: boolean) {
|
||||
this.#showAltitudeSlider = showAltitudeSlider;
|
||||
}
|
||||
|
||||
#setUnitRoleType(roleType: string) {
|
||||
this.spawnOptions.roleType = roleType;
|
||||
this.#container.dispatchEvent(new Event("unitRoleTypeChanged"));
|
||||
@@ -482,10 +569,6 @@ export class UnitSpawnMenu {
|
||||
}
|
||||
}
|
||||
|
||||
deployUnits(spawnOptions: UnitSpawnOptions, unitsCount: number) {
|
||||
/* Virtual function must be overloaded by inheriting classes */
|
||||
}
|
||||
|
||||
#createCountryButtons(parent: Dropdown, countries: string[], callback: CallableFunction) {
|
||||
return Object.values(countries).map((country: string) => {
|
||||
var el = document.createElement("div");
|
||||
@@ -614,9 +697,8 @@ export class HelicopterSpawnMenu extends UnitSpawnMenu {
|
||||
}
|
||||
|
||||
export class GroundUnitSpawnMenu extends UnitSpawnMenu {
|
||||
|
||||
protected showRangeCircles: boolean = true;
|
||||
protected unitTypeFilter = (unit:any) => {return !(/\bAAA|SAM\b/.test(unit.type) || /\bmanpad|stinger\b/i.test(unit.type))};
|
||||
protected unitTypeFilter = (unit:any) => {return !(GROUND_UNIT_AIR_DEFENCE_REGEX.test(unit.type))};
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -625,8 +707,8 @@ export class GroundUnitSpawnMenu extends UnitSpawnMenu {
|
||||
constructor(ID: string){
|
||||
super(ID, groundUnitDatabase, false);
|
||||
this.setMaxUnitCount(20);
|
||||
this.getAltitudeSlider().hide();
|
||||
this.getLoadoutDropdown().hide();
|
||||
this.setShowAltitudeSlider(false);
|
||||
this.setShowLoadout(false);
|
||||
this.getLoadoutPreview().classList.add("hide");
|
||||
}
|
||||
|
||||
@@ -656,8 +738,7 @@ export class GroundUnitSpawnMenu extends UnitSpawnMenu {
|
||||
}
|
||||
|
||||
export class AirDefenceUnitSpawnMenu extends GroundUnitSpawnMenu {
|
||||
|
||||
protected unitTypeFilter = (unit:any) => {return /\bAAA|SAM\b/.test(unit.type) || /\bmanpad|stinger\b/i.test(unit.type)};
|
||||
protected unitTypeFilter = (unit:any) => {return GROUND_UNIT_AIR_DEFENCE_REGEX.test(unit.type)};
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -677,9 +758,8 @@ export class NavyUnitSpawnMenu extends UnitSpawnMenu {
|
||||
constructor(ID: string){
|
||||
super(ID, navyUnitDatabase, false);
|
||||
this.setMaxUnitCount(4);
|
||||
this.getAltitudeSlider().hide();
|
||||
this.getLoadoutDropdown().hide();
|
||||
this.getLoadoutPreview().classList.add("hide");
|
||||
this.setShowAltitudeSlider(false);
|
||||
this.setShowLoadout(false);
|
||||
}
|
||||
|
||||
deployUnits(spawnOptions: UnitSpawnOptions, unitsCount: number) {
|
||||
|
||||
@@ -139,6 +139,7 @@ export interface Offset {
|
||||
|
||||
export interface UnitData {
|
||||
category: string,
|
||||
categoryDisplayName: string,
|
||||
ID: number;
|
||||
alive: boolean;
|
||||
human: boolean;
|
||||
@@ -156,6 +157,7 @@ export interface UnitData {
|
||||
horizontalVelocity: number;
|
||||
verticalVelocity: number;
|
||||
heading: number;
|
||||
track: number;
|
||||
isActiveTanker: boolean;
|
||||
isActiveAWACS: boolean;
|
||||
onOff: boolean;
|
||||
|
||||
@@ -7,7 +7,7 @@ import { AirbaseContextMenu } from "../contextmenus/airbasecontextmenu";
|
||||
import { Dropdown } from "../controls/dropdown";
|
||||
import { Airbase } from "../mission/airbase";
|
||||
import { Unit } from "../unit/unit";
|
||||
import { bearing, createCheckboxOption } from "../other/utils";
|
||||
import { bearing, createCheckboxOption, polyContains } from "../other/utils";
|
||||
import { DestinationPreviewMarker } from "./markers/destinationpreviewmarker";
|
||||
import { TemporaryUnitMarker } from "./markers/temporaryunitmarker";
|
||||
import { ClickableMiniMap } from "./clickableminimap";
|
||||
@@ -559,7 +559,7 @@ export class Map extends L.Map {
|
||||
|
||||
/* Coalition areas are ordered in the #coalitionAreas array according to their zindex. Select the upper one */
|
||||
for (let coalitionArea of this.#coalitionAreas) {
|
||||
if (coalitionArea.getBounds().contains(e.latlng)) {
|
||||
if (polyContains(e.latlng, coalitionArea)) {
|
||||
if (coalitionArea.getSelected())
|
||||
clickedCoalitionArea = coalitionArea;
|
||||
else
|
||||
@@ -662,7 +662,7 @@ export class Map extends L.Map {
|
||||
this.#destinationGroupRotation = -bearing(this.#destinationRotationCenter.lat, this.#destinationRotationCenter.lng, this.getMouseCoordinates().lat, this.getMouseCoordinates().lng);
|
||||
this.#updateDestinationCursors();
|
||||
}
|
||||
else if (this.#state === COALITIONAREA_DRAW_POLYGON) {
|
||||
else if (this.#state === COALITIONAREA_DRAW_POLYGON && e.latlng !== undefined) {
|
||||
this.#drawingCursor.setLatLng(e.latlng);
|
||||
/* Update the polygon being drawn with the current position of the mouse cursor */
|
||||
this.getSelectedCoalitionArea()?.moveActiveVertex(e.latlng);
|
||||
|
||||
@@ -35,6 +35,10 @@ export class MissionManager {
|
||||
this.#commandModeErasDropdown = new Dropdown("command-mode-era-options", () => {});
|
||||
}
|
||||
|
||||
/** Update location of bullseyes
|
||||
*
|
||||
* @param object <BulleyesData>
|
||||
*/
|
||||
updateBullseyes(data: BullseyesData) {
|
||||
const commandMode = getApp().getMissionManager().getCommandModeOptions().commandMode;
|
||||
for (let idx in data.bullseyes) {
|
||||
@@ -56,6 +60,10 @@ export class MissionManager {
|
||||
}
|
||||
}
|
||||
|
||||
/** Update airbase information
|
||||
*
|
||||
* @param object <AirbasesData>
|
||||
*/
|
||||
updateAirbases(data: AirbasesData) {
|
||||
for (let idx in data.airbases) {
|
||||
var airbase = data.airbases[idx]
|
||||
@@ -75,6 +83,10 @@ export class MissionManager {
|
||||
}
|
||||
}
|
||||
|
||||
/** Update mission information
|
||||
*
|
||||
* @param object <MissionData>
|
||||
*/
|
||||
updateMission(data: MissionData) {
|
||||
if (data.mission) {
|
||||
|
||||
@@ -109,30 +121,63 @@ export class MissionManager {
|
||||
}
|
||||
}
|
||||
|
||||
/** Get the bullseyes set in this theatre
|
||||
*
|
||||
* @returns object
|
||||
*/
|
||||
getBullseyes() {
|
||||
return this.#bullseyes;
|
||||
}
|
||||
|
||||
/** Get the airbases in this theatre
|
||||
*
|
||||
* @returns object
|
||||
*/
|
||||
getAirbases() {
|
||||
return this.#airbases;
|
||||
}
|
||||
|
||||
/** Get the options/settings as set in the command mode
|
||||
*
|
||||
* @returns object
|
||||
*/
|
||||
getCommandModeOptions() {
|
||||
return this.#commandModeOptions;
|
||||
}
|
||||
|
||||
/** Get the current date and time of the mission (based on local time)
|
||||
*
|
||||
* @returns object
|
||||
*/
|
||||
getDateAndTime() {
|
||||
return this.#dateAndTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of seconds left of setup time
|
||||
* @returns number
|
||||
*/
|
||||
getRemainingSetupTime() {
|
||||
return this.#remainingSetupTime;
|
||||
}
|
||||
|
||||
/** Get an object with the coalitions in it
|
||||
*
|
||||
* @returns object
|
||||
*/
|
||||
getCoalitions() {
|
||||
return this.#coalitions;
|
||||
}
|
||||
|
||||
/** Get the current theatre (map) name
|
||||
*
|
||||
* @returns string
|
||||
*/
|
||||
getTheatre() {
|
||||
return this.#theatre;
|
||||
}
|
||||
|
||||
|
||||
getAvailableSpawnPoints() {
|
||||
if (this.getCommandModeOptions().commandMode === GAME_MASTER)
|
||||
return Infinity;
|
||||
|
||||
@@ -29,9 +29,13 @@ import { UnitListPanel } from "./panels/unitlistpanel";
|
||||
import { ContextManager } from "./context/contextmanager";
|
||||
import { Context } from "./context/context";
|
||||
|
||||
var VERSION = "v0.4.9-alpha-rc1";
|
||||
var DEBUG = false;
|
||||
|
||||
export class OlympusApp {
|
||||
/* Global data */
|
||||
#activeCoalition: string = "blue";
|
||||
#latestVersion: string|undefined = undefined;
|
||||
|
||||
/* Main leaflet map, extended by custom methods */
|
||||
#map: Map | null = null;
|
||||
@@ -50,7 +54,6 @@ export class OlympusApp {
|
||||
#weaponsManager: WeaponsManager | null = null;
|
||||
|
||||
constructor() {
|
||||
|
||||
}
|
||||
|
||||
// TODO add checks on null
|
||||
@@ -178,7 +181,6 @@ export class OlympusApp {
|
||||
|
||||
start() {
|
||||
/* Initialize base functionalitites */
|
||||
|
||||
this.#contextManager = new ContextManager();
|
||||
this.#contextManager.add( "olympus", {} );
|
||||
|
||||
@@ -245,8 +247,26 @@ export class OlympusApp {
|
||||
let loadingScreen = document.getElementById("loading-screen") as HTMLElement;
|
||||
loadingScreen.classList.add("fade-out");
|
||||
window.setInterval(() => { loadingScreen.classList.add("hide"); }, 1000);
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
/* Check if we are running the latest version */
|
||||
const request = new Request("https://raw.githubusercontent.com/Pax1601/DCSOlympus/main/version.json");
|
||||
fetch(request).then((response) => {
|
||||
if (response.status === 200) {
|
||||
return response.json();
|
||||
} else {
|
||||
throw new Error("Error connecting to Github to retrieve latest version");
|
||||
}
|
||||
}).then((res) => {
|
||||
this.#latestVersion = res["version"];
|
||||
const latestVersionSpan = document.getElementById("latest-version") as HTMLElement;
|
||||
if (latestVersionSpan) {
|
||||
latestVersionSpan.innerHTML = this.#latestVersion ?? "Unknown";
|
||||
if (this.#latestVersion !== VERSION) {
|
||||
latestVersionSpan.classList.add("new-version");
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#setupEvents() {
|
||||
@@ -279,7 +299,7 @@ export class OlympusApp {
|
||||
shortcutManager.addKeyboardShortcut("toggleDemo", {
|
||||
"altKey": false,
|
||||
"callback": () => {
|
||||
this.getServerManager().toggleDemoEnabled();
|
||||
if (DEBUG === true) this.getServerManager().toggleDemoEnabled();
|
||||
},
|
||||
"code": "KeyT",
|
||||
"context": "olympus",
|
||||
@@ -411,6 +431,7 @@ export class OlympusApp {
|
||||
// TODO: move from here in dedicated class
|
||||
document.addEventListener("closeDialog", (ev: CustomEventInit) => {
|
||||
ev.detail._element.closest(".ol-dialog").classList.add("hide");
|
||||
document.getElementById("gray-out")?.classList.toggle("hide", true);
|
||||
});
|
||||
|
||||
/* Try and connect with the Olympus REST server */
|
||||
@@ -435,7 +456,6 @@ export class OlympusApp {
|
||||
console.error("Unable to find login form.");
|
||||
}
|
||||
|
||||
|
||||
/* Reload the page, used to mimic a restart of the app */
|
||||
document.addEventListener("reloadPage", () => {
|
||||
location.reload();
|
||||
|
||||
@@ -434,14 +434,24 @@ export function convertDateAndTimeToDate(dateAndTime: DateAndTime) {
|
||||
return new Date(year, month, date.Day, time.h, time.m, time.s);
|
||||
}
|
||||
|
||||
export function createCheckboxOption(value: string, text: string, checked: boolean = true, callback: CallableFunction = (ev: any) => {}) {
|
||||
export function createCheckboxOption(value: string, text: string, checked: boolean = true, callback: CallableFunction = (ev: any) => {}, options?:any) {
|
||||
options = {
|
||||
"disabled": false,
|
||||
"name": "",
|
||||
"readOnly": false,
|
||||
...options
|
||||
};
|
||||
var div = document.createElement("div");
|
||||
div.classList.add("ol-checkbox");
|
||||
var label = document.createElement("label");
|
||||
label.title = text;
|
||||
var input = document.createElement("input");
|
||||
input.type = "checkbox";
|
||||
input.checked = checked;
|
||||
input.checked = checked;
|
||||
input.name = options.name;
|
||||
input.disabled = options.disabled;
|
||||
input.readOnly = options.readOnly;
|
||||
input.value = value;
|
||||
var span = document.createElement("span");
|
||||
span.innerText = value;
|
||||
label.appendChild(input);
|
||||
|
||||
@@ -12,15 +12,16 @@ export class ServerManager {
|
||||
#connected: boolean = false;
|
||||
#paused: boolean = false;
|
||||
#REST_ADDRESS = "http://localhost:30000/olympus";
|
||||
#DEMO_ADDRESS = window.location.href + "demo";
|
||||
#DEMO_ADDRESS = window.location.href.split('?')[0] + "demo"; /* Remove query parameters */
|
||||
#username = "";
|
||||
#password = "";
|
||||
#sessionHash: string | null = null;
|
||||
#lastUpdateTimes: {[key: string]: number} = {}
|
||||
#lastUpdateTimes: { [key: string]: number } = {}
|
||||
#demoEnabled = false;
|
||||
#previousMissionElapsedTime:number = 0; // Track if mission elapsed time is increasing (i.e. is the server paused)
|
||||
#previousMissionElapsedTime: number = 0; // Track if mission elapsed time is increasing (i.e. is the server paused)
|
||||
#serverIsPaused: boolean = false;
|
||||
#intervals: number[] = [];
|
||||
#requests: { [key: string]: XMLHttpRequest } = {};
|
||||
|
||||
constructor() {
|
||||
this.#lastUpdateTimes[UNITS_URI] = Date.now();
|
||||
@@ -40,9 +41,20 @@ export class ServerManager {
|
||||
this.#password = newPassword;
|
||||
}
|
||||
|
||||
GET(callback: CallableFunction, uri: string, options?: ServerRequestOptions, responseType?: string) {
|
||||
GET(callback: CallableFunction, uri: string, options?: ServerRequestOptions, responseType: string = 'text', force: boolean = false) {
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
|
||||
/* If a request on this uri is still pending (meaning it's not done or did not yet fail), skip the request, to avoid clogging the TCP workers */
|
||||
/* If we are forcing the request we don't care if one already exists, just send it. CAREFUL: this makes sense only for low frequency requests, like refreshes, when we
|
||||
are reasonably confident any previous request will be done before we make a new one on the same URI. */
|
||||
if (uri in this.#requests && this.#requests[uri].readyState !== 4 && !force) {
|
||||
console.warn(`GET request on ${uri} URI still pending, skipping...`);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!force)
|
||||
this.#requests[uri] = xmlHttp;
|
||||
|
||||
/* Assemble the request options string */
|
||||
var optionsString = '';
|
||||
if (options?.time != undefined)
|
||||
@@ -83,9 +95,11 @@ export class ServerManager {
|
||||
this.setConnected(false);
|
||||
}
|
||||
};
|
||||
xmlHttp.onerror = (res) => {
|
||||
console.error("An error occurred during the XMLHttpRequest");
|
||||
this.setConnected(false);
|
||||
xmlHttp.onreadystatechange = (res) => {
|
||||
if (xmlHttp.readyState == 4 && xmlHttp.status === 0) {
|
||||
console.error("An error occurred during the XMLHttpRequest");
|
||||
this.setConnected(false);
|
||||
}
|
||||
};
|
||||
xmlHttp.send(null);
|
||||
}
|
||||
@@ -105,7 +119,7 @@ export class ServerManager {
|
||||
|
||||
getConfig(callback: CallableFunction) {
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
xmlHttp.open("GET", window.location.href + "config", true);
|
||||
xmlHttp.open("GET", window.location.href.split('?')[0] + "config", true);
|
||||
xmlHttp.onload = function (e) {
|
||||
var data = JSON.parse(xmlHttp.responseText);
|
||||
callback(data);
|
||||
@@ -130,7 +144,7 @@ export class ServerManager {
|
||||
}
|
||||
|
||||
getLogs(callback: CallableFunction, refresh: boolean = false) {
|
||||
this.GET(callback, LOGS_URI, { time: refresh ? 0 : this.#lastUpdateTimes[LOGS_URI]});
|
||||
this.GET(callback, LOGS_URI, { time: refresh ? 0 : this.#lastUpdateTimes[LOGS_URI] }, 'text', refresh);
|
||||
}
|
||||
|
||||
getMission(callback: CallableFunction) {
|
||||
@@ -138,66 +152,66 @@ export class ServerManager {
|
||||
}
|
||||
|
||||
getUnits(callback: CallableFunction, refresh: boolean = false) {
|
||||
this.GET(callback, UNITS_URI, { time: refresh ? 0 : this.#lastUpdateTimes[UNITS_URI] }, 'arraybuffer');
|
||||
this.GET(callback, UNITS_URI, { time: refresh ? 0 : this.#lastUpdateTimes[UNITS_URI] }, 'arraybuffer', refresh);
|
||||
}
|
||||
|
||||
getWeapons(callback: CallableFunction, refresh: boolean = false) {
|
||||
this.GET(callback, WEAPONS_URI, { time: refresh ? 0 : this.#lastUpdateTimes[WEAPONS_URI] }, 'arraybuffer');
|
||||
this.GET(callback, WEAPONS_URI, { time: refresh ? 0 : this.#lastUpdateTimes[WEAPONS_URI] }, 'arraybuffer', refresh);
|
||||
}
|
||||
|
||||
isCommandExecuted(callback: CallableFunction, commandHash: string) {
|
||||
this.GET(callback, COMMANDS_URI, { commandHash: commandHash});
|
||||
this.GET(callback, COMMANDS_URI, { commandHash: commandHash });
|
||||
}
|
||||
|
||||
addDestination(ID: number, path: any, callback: CallableFunction = () => {}) {
|
||||
addDestination(ID: number, path: any, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "path": path }
|
||||
var data = { "setPath": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
spawnSmoke(color: string, latlng: LatLng, callback: CallableFunction = () => {}) {
|
||||
spawnSmoke(color: string, latlng: LatLng, callback: CallableFunction = () => { }) {
|
||||
var command = { "color": color, "location": latlng };
|
||||
var data = { "smoke": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
spawnExplosion(intensity: number, explosionType: string, latlng: LatLng, callback: CallableFunction = () => {}) {
|
||||
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);
|
||||
}
|
||||
|
||||
spawnAircrafts(units: any, coalition: string, airbaseName: string, country: string, immediate: boolean, spawnPoints: number, callback: CallableFunction = () => {}) {
|
||||
spawnAircrafts(units: any, coalition: string, airbaseName: string, country: string, immediate: boolean, spawnPoints: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "units": units, "coalition": coalition, "airbaseName": airbaseName, "country": country, "immediate": immediate, "spawnPoints": spawnPoints };
|
||||
var data = { "spawnAircrafts": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
spawnHelicopters(units: any, coalition: string, airbaseName: string, country: string, immediate: boolean, spawnPoints: number, callback: CallableFunction = () => {}) {
|
||||
spawnHelicopters(units: any, coalition: string, airbaseName: string, country: string, immediate: boolean, spawnPoints: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "units": units, "coalition": coalition, "airbaseName": airbaseName, "country": country, "immediate": immediate, "spawnPoints": spawnPoints };
|
||||
var data = { "spawnHelicopters": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
spawnGroundUnits(units: any, coalition: string, country: string, immediate: boolean, spawnPoints: number, callback: CallableFunction = () => {}) {
|
||||
spawnGroundUnits(units: any, coalition: string, country: string, immediate: boolean, spawnPoints: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "units": units, "coalition": coalition, "country": country, "immediate": immediate, "spawnPoints": spawnPoints };;
|
||||
var data = { "spawnGroundUnits": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
spawnNavyUnits(units: any, coalition: string, country: string, immediate: boolean, spawnPoints: number, callback: CallableFunction = () => {}) {
|
||||
spawnNavyUnits(units: any, coalition: string, country: string, immediate: boolean, spawnPoints: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "units": units, "coalition": coalition, "country": country, "immediate": immediate, "spawnPoints": spawnPoints };
|
||||
var data = { "spawnNavyUnits": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
attackUnit(ID: number, targetID: number, callback: CallableFunction = () => {}) {
|
||||
attackUnit(ID: number, targetID: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "targetID": targetID };
|
||||
var data = { "attackUnit": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
followUnit(ID: number, targetID: number, offset: { "x": number, "y": number, "z": number }, callback: CallableFunction = () => {}) {
|
||||
followUnit(ID: number, targetID: number, offset: { "x": number, "y": number, "z": number }, callback: CallableFunction = () => { }) {
|
||||
// X: front-rear, positive front
|
||||
// Y: top-bottom, positive bottom
|
||||
// Z: left-right, positive right
|
||||
@@ -207,172 +221,172 @@ export class ServerManager {
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
cloneUnits(units: {ID: number, location: LatLng}[], deleteOriginal: boolean, spawnPoints: number, callback: CallableFunction = () => {}) {
|
||||
cloneUnits(units: { ID: number, location: LatLng }[], deleteOriginal: boolean, spawnPoints: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "units": units, "deleteOriginal": deleteOriginal, "spawnPoints": spawnPoints };
|
||||
var data = { "cloneUnits": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
deleteUnit(ID: number, explosion: boolean, explosionType: string, immediate: boolean, callback: CallableFunction = () => {}) {
|
||||
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);
|
||||
}
|
||||
|
||||
landAt(ID: number, latlng: LatLng, callback: CallableFunction = () => {}) {
|
||||
landAt(ID: number, latlng: LatLng, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "location": latlng };
|
||||
var data = { "landAt": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
changeSpeed(ID: number, speedChange: string, callback: CallableFunction = () => {}) {
|
||||
changeSpeed(ID: number, speedChange: string, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "change": speedChange }
|
||||
var data = { "changeSpeed": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setSpeed(ID: number, speed: number, callback: CallableFunction = () => {}) {
|
||||
setSpeed(ID: number, speed: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "speed": speed }
|
||||
var data = { "setSpeed": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setSpeedType(ID: number, speedType: string, callback: CallableFunction = () => {}) {
|
||||
setSpeedType(ID: number, speedType: string, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "speedType": speedType }
|
||||
var data = { "setSpeedType": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
changeAltitude(ID: number, altitudeChange: string, callback: CallableFunction = () => {}) {
|
||||
changeAltitude(ID: number, altitudeChange: string, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "change": altitudeChange }
|
||||
var data = { "changeAltitude": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setAltitudeType(ID: number, altitudeType: string, callback: CallableFunction = () => {}) {
|
||||
setAltitudeType(ID: number, altitudeType: string, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "altitudeType": altitudeType }
|
||||
var data = { "setAltitudeType": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setAltitude(ID: number, altitude: number, callback: CallableFunction = () => {}) {
|
||||
setAltitude(ID: number, altitude: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "altitude": altitude }
|
||||
var data = { "setAltitude": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
createFormation(ID: number, isLeader: boolean, wingmenIDs: number[], callback: CallableFunction = () => {}) {
|
||||
createFormation(ID: number, isLeader: boolean, wingmenIDs: number[], callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "wingmenIDs": wingmenIDs, "isLeader": isLeader }
|
||||
var data = { "setLeader": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setROE(ID: number, ROE: string, callback: CallableFunction = () => {}) {
|
||||
setROE(ID: number, ROE: string, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "ROE": ROEs.indexOf(ROE) }
|
||||
var data = { "setROE": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setReactionToThreat(ID: number, reactionToThreat: string, callback: CallableFunction = () => {}) {
|
||||
setReactionToThreat(ID: number, reactionToThreat: string, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "reactionToThreat": reactionsToThreat.indexOf(reactionToThreat) }
|
||||
var data = { "setReactionToThreat": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setEmissionsCountermeasures(ID: number, emissionCountermeasure: string, callback: CallableFunction = () => {}) {
|
||||
setEmissionsCountermeasures(ID: number, emissionCountermeasure: string, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "emissionsCountermeasures": emissionsCountermeasures.indexOf(emissionCountermeasure) }
|
||||
var data = { "setEmissionsCountermeasures": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setOnOff(ID: number, onOff: boolean, callback: CallableFunction = () => {}) {
|
||||
setOnOff(ID: number, onOff: boolean, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "onOff": onOff }
|
||||
var data = { "setOnOff": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setFollowRoads(ID: number, followRoads: boolean, callback: CallableFunction = () => {}) {
|
||||
setFollowRoads(ID: number, followRoads: boolean, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "followRoads": followRoads }
|
||||
var data = { "setFollowRoads": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setOperateAs(ID: number, operateAs: number, callback: CallableFunction = () => {}) {
|
||||
setOperateAs(ID: number, operateAs: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "operateAs": operateAs }
|
||||
var data = { "setOperateAs": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
|
||||
refuel(ID: number, callback: CallableFunction = () => {}) {
|
||||
refuel(ID: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID };
|
||||
var data = { "refuel": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
bombPoint(ID: number, latlng: LatLng, callback: CallableFunction = () => {}) {
|
||||
bombPoint(ID: number, latlng: LatLng, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "location": latlng }
|
||||
var data = { "bombPoint": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
carpetBomb(ID: number, latlng: LatLng, callback: CallableFunction = () => {}) {
|
||||
carpetBomb(ID: number, latlng: LatLng, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "location": latlng }
|
||||
var data = { "carpetBomb": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
bombBuilding(ID: number, latlng: LatLng, callback: CallableFunction = () => {}) {
|
||||
bombBuilding(ID: number, latlng: LatLng, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "location": latlng }
|
||||
var data = { "bombBuilding": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
fireAtArea(ID: number, latlng: LatLng, callback: CallableFunction = () => {}) {
|
||||
fireAtArea(ID: number, latlng: LatLng, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "location": latlng }
|
||||
var data = { "fireAtArea": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
simulateFireFight(ID: number, latlng: LatLng, altitude: number, callback: CallableFunction = () => {}) {
|
||||
simulateFireFight(ID: number, latlng: LatLng, altitude: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "location": latlng, "altitude": altitude }
|
||||
var data = { "simulateFireFight": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
// TODO: Remove coalition
|
||||
scenicAAA(ID: number, coalition: string, callback: CallableFunction = () => {}) {
|
||||
scenicAAA(ID: number, coalition: string, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "coalition": coalition }
|
||||
var data = { "scenicAAA": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
// TODO: Remove coalition
|
||||
missOnPurpose(ID: number, coalition: string, callback: CallableFunction = () => {}) {
|
||||
missOnPurpose(ID: number, coalition: string, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "coalition": coalition }
|
||||
var data = { "missOnPurpose": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
landAtPoint(ID: number, latlng: LatLng, callback: CallableFunction = () => {}) {
|
||||
landAtPoint(ID: number, latlng: LatLng, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "location": latlng }
|
||||
var data = { "landAtPoint": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setShotsScatter(ID: number, shotsScatter: number, callback: CallableFunction = () => {}) {
|
||||
setShotsScatter(ID: number, shotsScatter: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "shotsScatter": shotsScatter }
|
||||
var data = { "setShotsScatter": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setShotsIntensity(ID: number, shotsIntensity: number, callback: CallableFunction = () => {}) {
|
||||
setShotsIntensity(ID: number, shotsIntensity: number, callback: CallableFunction = () => { }) {
|
||||
var command = { "ID": ID, "shotsIntensity": shotsIntensity }
|
||||
var data = { "setShotsIntensity": command }
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setAdvacedOptions(ID: number, isActiveTanker: boolean, isActiveAWACS: boolean, TACAN: TACAN, radio: Radio, generalSettings: GeneralSettings, callback: CallableFunction = () => {}) {
|
||||
setAdvacedOptions(ID: number, isActiveTanker: boolean, isActiveAWACS: boolean, TACAN: TACAN, radio: Radio, generalSettings: GeneralSettings, callback: CallableFunction = () => { }) {
|
||||
var command = {
|
||||
"ID": ID,
|
||||
"isActiveTanker": isActiveTanker,
|
||||
@@ -386,7 +400,7 @@ export class ServerManager {
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
setCommandModeOptions(restrictSpawns: boolean, restrictToCoalition: boolean, spawnPoints: {blue: number, red: number}, eras: string[], setupTime: number, callback: CallableFunction = () => {}) {
|
||||
setCommandModeOptions(restrictSpawns: boolean, restrictToCoalition: boolean, spawnPoints: { blue: number, red: number }, eras: string[], setupTime: number, callback: CallableFunction = () => { }) {
|
||||
var command = {
|
||||
"restrictSpawns": restrictSpawns,
|
||||
"restrictToCoalition": restrictToCoalition,
|
||||
@@ -399,7 +413,7 @@ export class ServerManager {
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
|
||||
reloadDatabases(callback: CallableFunction = () => {}) {
|
||||
reloadDatabases(callback: CallableFunction = () => { }) {
|
||||
var data = { "reloadDatabases": {} };
|
||||
this.PUT(data, callback);
|
||||
}
|
||||
@@ -430,7 +444,7 @@ export class ServerManager {
|
||||
}, 10000));
|
||||
|
||||
this.#intervals.push(window.setInterval(() => {
|
||||
if (!this.getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE){
|
||||
if (!this.getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
|
||||
this.getBullseye((data: BullseyesData) => {
|
||||
this.checkSessionHash(data.sessionHash);
|
||||
getApp().getMissionManager()?.updateBullseyes(data);
|
||||
@@ -452,7 +466,7 @@ export class ServerManager {
|
||||
this.#intervals.push(window.setInterval(() => {
|
||||
if (!this.getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
|
||||
this.getUnits((buffer: ArrayBuffer) => {
|
||||
var time = getApp().getUnitsManager()?.update(buffer);
|
||||
var time = getApp().getUnitsManager()?.update(buffer);
|
||||
return time;
|
||||
}, false);
|
||||
}
|
||||
@@ -461,7 +475,7 @@ export class ServerManager {
|
||||
this.#intervals.push(window.setInterval(() => {
|
||||
if (!this.getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
|
||||
this.getWeapons((buffer: ArrayBuffer) => {
|
||||
var time = getApp().getWeaponsManager()?.update(buffer);
|
||||
var time = getApp().getWeaponsManager()?.update(buffer);
|
||||
return time;
|
||||
}, false);
|
||||
}
|
||||
@@ -470,18 +484,18 @@ export class ServerManager {
|
||||
this.#intervals.push(window.setInterval(() => {
|
||||
if (!this.getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
|
||||
this.getUnits((buffer: ArrayBuffer) => {
|
||||
var time = getApp().getUnitsManager()?.update(buffer);
|
||||
var time = getApp().getUnitsManager()?.update(buffer);
|
||||
return time;
|
||||
}, true);
|
||||
|
||||
const elapsedMissionTime = getApp().getMissionManager().getDateAndTime().elapsedTime;
|
||||
this.#serverIsPaused = ( elapsedMissionTime === this.#previousMissionElapsedTime );
|
||||
const elapsedMissionTime = getApp().getMissionManager().getDateAndTime().elapsedTime;
|
||||
this.#serverIsPaused = (elapsedMissionTime === this.#previousMissionElapsedTime);
|
||||
this.#previousMissionElapsedTime = elapsedMissionTime;
|
||||
|
||||
const csp = (getApp().getPanelsManager().get("connectionStatus") as ConnectionStatusPanel);
|
||||
|
||||
if ( this.getConnected() ) {
|
||||
if ( this.getServerIsPaused() ) {
|
||||
if (this.getConnected()) {
|
||||
if (this.getServerIsPaused()) {
|
||||
csp.showServerPaused();
|
||||
} else {
|
||||
csp.showConnected();
|
||||
@@ -491,29 +505,29 @@ export class ServerManager {
|
||||
}
|
||||
|
||||
}
|
||||
}, ( this.getServerIsPaused() ? 500 : 5000 )));
|
||||
}, (this.getServerIsPaused() ? 500 : 5000)));
|
||||
|
||||
// Mission clock and elapsed time
|
||||
this.#intervals.push(window.setInterval( () => {
|
||||
|
||||
if ( !this.getConnected() || this.#serverIsPaused ) {
|
||||
this.#intervals.push(window.setInterval(() => {
|
||||
|
||||
if (!this.getConnected() || this.#serverIsPaused) {
|
||||
return;
|
||||
}
|
||||
|
||||
const elapsedMissionTime = getApp().getMissionManager().getDateAndTime().elapsedTime;
|
||||
|
||||
const csp = (getApp().getPanelsManager().get("connectionStatus") as ConnectionStatusPanel);
|
||||
const mt = getApp().getMissionManager().getDateAndTime().time;
|
||||
const mt = getApp().getMissionManager().getDateAndTime().time;
|
||||
|
||||
csp.setMissionTime( [ mt.h, mt.m, mt.s ].map( n => zeroAppend( n, 2 )).join( ":" ) );
|
||||
csp.setElapsedTime( new Date( elapsedMissionTime * 1000 ).toISOString().substring( 11, 19 ) );
|
||||
csp.setMissionTime([mt.h, mt.m, mt.s].map(n => zeroAppend(n, 2)).join(":"));
|
||||
csp.setElapsedTime(new Date(elapsedMissionTime * 1000).toISOString().substring(11, 19));
|
||||
|
||||
}, 1000));
|
||||
|
||||
this.#intervals.push(window.setInterval(() => {
|
||||
if (!this.getPaused() && getApp().getMissionManager().getCommandModeOptions().commandMode != NONE) {
|
||||
this.getWeapons((buffer: ArrayBuffer) => {
|
||||
var time = getApp().getWeaponsManager()?.update(buffer);
|
||||
var time = getApp().getWeaponsManager()?.update(buffer);
|
||||
return time;
|
||||
}, true);
|
||||
}
|
||||
@@ -540,12 +554,12 @@ export class ServerManager {
|
||||
});
|
||||
|
||||
this.getWeapons((buffer: ArrayBuffer) => {
|
||||
var time = getApp().getWeaponsManager()?.update(buffer);
|
||||
var time = getApp().getWeaponsManager()?.update(buffer);
|
||||
return time;
|
||||
}, true);
|
||||
|
||||
this.getUnits((buffer: ArrayBuffer) => {
|
||||
var time = getApp().getUnitsManager()?.update(buffer);
|
||||
var time = getApp().getUnitsManager()?.update(buffer);
|
||||
return time;
|
||||
}, true);
|
||||
}
|
||||
@@ -587,4 +601,8 @@ export class ServerManager {
|
||||
getServerIsPaused() {
|
||||
return this.#serverIsPaused;
|
||||
}
|
||||
|
||||
getRequests() {
|
||||
return this.#requests;
|
||||
}
|
||||
}
|
||||
|
||||
65
client/src/unit/importexport/unitdatafile.ts
Normal file
@@ -0,0 +1,65 @@
|
||||
import { Dialog } from "../../dialog/dialog";
|
||||
import { createCheckboxOption } from "../../other/utils";
|
||||
|
||||
var categoryMap = {
|
||||
"Aircraft": "Aircraft",
|
||||
"Helicopter": "Helicopter",
|
||||
"GroundUnit": "Ground units",
|
||||
"NavyUnit": "Naval units"
|
||||
}
|
||||
|
||||
export abstract class UnitDataFile {
|
||||
|
||||
protected data: any;
|
||||
protected dialog!: Dialog;
|
||||
|
||||
constructor() { }
|
||||
|
||||
buildCategoryCoalitionTable() {
|
||||
|
||||
const categories = this.#getCategoriesFromData();
|
||||
const coalitions = ["blue", "neutral", "red"];
|
||||
|
||||
let headersHTML: string = ``;
|
||||
let matrixHTML: string = ``;
|
||||
|
||||
categories.forEach((category: string, index) => {
|
||||
matrixHTML += `<tr><td>${categoryMap[category as keyof typeof categoryMap]}</td>`;
|
||||
|
||||
coalitions.forEach((coalition: string) => {
|
||||
if (index === 0)
|
||||
headersHTML += `<th data-coalition="${coalition}">${coalition[0].toUpperCase() + coalition.substring(1)}</th>`;
|
||||
|
||||
const optionIsValid = this.data[category].hasOwnProperty(coalition);
|
||||
let checkboxHTML = createCheckboxOption(`${category}:${coalition}`, category, optionIsValid, () => { }, {
|
||||
"disabled": !optionIsValid,
|
||||
"name": "category-coalition-selection",
|
||||
"readOnly": !optionIsValid
|
||||
}).outerHTML;
|
||||
|
||||
if (optionIsValid)
|
||||
checkboxHTML = checkboxHTML.replace(`"checkbox"`, `"checkbox" checked`); // inner and outerHTML screw default checked up
|
||||
|
||||
matrixHTML += `<td data-coalition="${coalition}">${checkboxHTML}</td>`;
|
||||
|
||||
});
|
||||
matrixHTML += "</tr>";
|
||||
});
|
||||
|
||||
const table = <HTMLTableElement>this.dialog.getElement().querySelector("table.categories-coalitions");
|
||||
|
||||
(<HTMLElement>table.tHead).innerHTML = `<tr><td> </td>${headersHTML}</tr>`;
|
||||
(<HTMLElement>table.querySelector(`tbody`)).innerHTML = matrixHTML;
|
||||
|
||||
}
|
||||
|
||||
#getCategoriesFromData() {
|
||||
const categories = Object.keys(this.data);
|
||||
categories.sort();
|
||||
return categories;
|
||||
}
|
||||
|
||||
getData() {
|
||||
return this.data;
|
||||
}
|
||||
}
|
||||
97
client/src/unit/importexport/unitdatafileexport.ts
Normal file
@@ -0,0 +1,97 @@
|
||||
import { getApp } from "../..";
|
||||
import { Dialog } from "../../dialog/dialog";
|
||||
import { zeroAppend } from "../../other/utils";
|
||||
import { Unit } from "../unit";
|
||||
import { UnitDataFile } from "./unitdatafile";
|
||||
|
||||
export class UnitDataFileExport extends UnitDataFile {
|
||||
|
||||
protected data!: any;
|
||||
protected dialog: Dialog;
|
||||
#element!: HTMLElement;
|
||||
#filename: string = "export.json";
|
||||
|
||||
constructor(elementId: string) {
|
||||
super();
|
||||
this.dialog = new Dialog(elementId);
|
||||
this.#element = this.dialog.getElement();
|
||||
|
||||
this.#element.querySelector(".start-transfer")?.addEventListener("click", (ev: MouseEventInit) => {
|
||||
this.#doExport();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form to start the export journey
|
||||
*/
|
||||
showForm(units: Unit[]) {
|
||||
const data: any = {};
|
||||
const unitCanBeExported = (unit: Unit) => !["Aircraft", "Helicopter"].includes(unit.getCategory());
|
||||
|
||||
units.filter((unit: Unit) => unit.getAlive() && unitCanBeExported(unit)).forEach((unit: Unit) => {
|
||||
const category = unit.getCategory();
|
||||
const coalition = unit.getCoalition();
|
||||
|
||||
if (!data.hasOwnProperty(category)) {
|
||||
data[category] = {};
|
||||
}
|
||||
|
||||
if (!data[category].hasOwnProperty(coalition))
|
||||
data[category][coalition] = [];
|
||||
|
||||
data[category][coalition].push(unit);
|
||||
});
|
||||
|
||||
this.data = data;
|
||||
this.buildCategoryCoalitionTable();
|
||||
this.dialog.show();
|
||||
|
||||
const date = new Date();
|
||||
this.#filename = `olympus_${getApp().getMissionManager().getTheatre().replace(/[^\w]/gi, "").toLowerCase()}_${date.getFullYear()}${zeroAppend(date.getMonth() + 1, 2)}${zeroAppend(date.getDate(), 2)}_${zeroAppend(date.getHours(), 2)}${zeroAppend(date.getMinutes(), 2)}${zeroAppend(date.getSeconds(), 2)}.json`;
|
||||
var input = this.#element.querySelector("#export-filename") as HTMLInputElement;
|
||||
input.onchange = (ev: Event) => {
|
||||
this.#filename = (ev.currentTarget as HTMLInputElement).value;
|
||||
}
|
||||
if (input)
|
||||
input.value = this.#filename;
|
||||
}
|
||||
|
||||
#doExport() {
|
||||
|
||||
let selectedUnits: Unit[] = [];
|
||||
|
||||
this.#element.querySelectorAll(`input[type="checkbox"][name="category-coalition-selection"]:checked`).forEach(<HTMLInputElement>(checkbox: HTMLInputElement) => {
|
||||
if (checkbox instanceof HTMLInputElement) {
|
||||
const [category, coalition] = checkbox.value.split(":"); // e.g. "category:coalition"
|
||||
selectedUnits = selectedUnits.concat(this.data[category][coalition]);
|
||||
}
|
||||
});
|
||||
|
||||
if (selectedUnits.length === 0) {
|
||||
alert("Please select at least one option for export.");
|
||||
return;
|
||||
}
|
||||
|
||||
var unitsToExport: { [key: string]: any } = {};
|
||||
selectedUnits.forEach((unit: Unit) => {
|
||||
var data: any = unit.getData();
|
||||
if (unit.getGroupName() in unitsToExport)
|
||||
unitsToExport[unit.getGroupName()].push(data);
|
||||
else
|
||||
unitsToExport[unit.getGroupName()] = [data];
|
||||
});
|
||||
|
||||
|
||||
const a = document.createElement("a");
|
||||
const file = new Blob([JSON.stringify(unitsToExport)], { type: 'text/plain' });
|
||||
a.href = URL.createObjectURL(file);
|
||||
|
||||
var filename = this.#filename;
|
||||
if (!this.#filename.toLowerCase().endsWith(".json"))
|
||||
filename += ".json";
|
||||
a.download = filename;
|
||||
a.click();
|
||||
this.dialog.hide();
|
||||
}
|
||||
|
||||
}
|
||||
138
client/src/unit/importexport/unitdatafileimport.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import { getApp } from "../..";
|
||||
import { Dialog } from "../../dialog/dialog";
|
||||
import { UnitData } from "../../interfaces";
|
||||
import { UnitDataFile } from "./unitdatafile";
|
||||
|
||||
export class UnitDataFileImport extends UnitDataFile {
|
||||
|
||||
protected data!: any;
|
||||
protected dialog: Dialog;
|
||||
#fileData!: { [key: string]: UnitData[] };
|
||||
|
||||
constructor(elementId: string) {
|
||||
super();
|
||||
this.dialog = new Dialog(elementId);
|
||||
this.dialog.getElement().querySelector(".start-transfer")?.addEventListener("click", (ev: MouseEventInit) => {
|
||||
this.#doImport();
|
||||
this.dialog.hide();
|
||||
});
|
||||
}
|
||||
|
||||
#doImport() {
|
||||
|
||||
let selectedCategories: any = {};
|
||||
const unitsManager = getApp().getUnitsManager();
|
||||
|
||||
this.dialog.getElement().querySelectorAll(`input[type="checkbox"][name="category-coalition-selection"]:checked`).forEach(<HTMLInputElement>(checkbox: HTMLInputElement) => {
|
||||
if (checkbox instanceof HTMLInputElement) {
|
||||
const [category, coalition] = checkbox.value.split(":"); // e.g. "category:coalition"
|
||||
selectedCategories[category] = selectedCategories[category] || {};
|
||||
selectedCategories[category][coalition] = true;
|
||||
}
|
||||
});
|
||||
|
||||
for (const [groupName, groupData] of Object.entries(this.#fileData)) {
|
||||
if (groupName === "" || groupData.length === 0 || !this.#unitGroupDataCanBeImported(groupData))
|
||||
continue;
|
||||
|
||||
let { category, coalition } = groupData[0];
|
||||
|
||||
if (!selectedCategories.hasOwnProperty(category)
|
||||
|| !selectedCategories[category].hasOwnProperty(coalition)
|
||||
|| selectedCategories[category][coalition] !== true)
|
||||
continue;
|
||||
|
||||
let unitsToSpawn = groupData.filter((unitData: UnitData) => this.#unitDataCanBeImported(unitData)).map((unitData: UnitData) => {
|
||||
return { unitType: unitData.name, location: unitData.position, liveryID: "" }
|
||||
});
|
||||
|
||||
unitsManager.spawnUnits(category, unitsToSpawn, coalition, false);
|
||||
}
|
||||
|
||||
/*
|
||||
for (let groupName in groups) {
|
||||
if (groupName !== "" && groups[groupName].length > 0 && (groups[groupName].every((unit: UnitData) => { return unit.category == "GroundUnit"; }) || groups[groupName].every((unit: any) => { return unit.category == "NavyUnit"; }))) {
|
||||
var aliveUnits = groups[groupName].filter((unit: UnitData) => { return unit.alive });
|
||||
var units = aliveUnits.map((unit: UnitData) => {
|
||||
return { unitType: unit.name, location: unit.position, liveryID: "" }
|
||||
});
|
||||
getApp().getUnitsManager().spawnUnits(groups[groupName][0].category, units, groups[groupName][0].coalition, true);
|
||||
}
|
||||
}
|
||||
//*/
|
||||
}
|
||||
|
||||
#showForm() {
|
||||
const data: any = {};
|
||||
|
||||
for (const [group, units] of Object.entries(this.#fileData)) {
|
||||
if (group === "" || units.length === 0)
|
||||
continue;
|
||||
|
||||
if (units.some((unit: UnitData) => !this.#unitDataCanBeImported(unit)))
|
||||
continue;
|
||||
|
||||
const category = units[0].category;
|
||||
|
||||
if (!data.hasOwnProperty(category)) {
|
||||
data[category] = {};
|
||||
}
|
||||
|
||||
units.forEach((unit: UnitData) => {
|
||||
if (!data[category].hasOwnProperty(unit.coalition))
|
||||
data[category][unit.coalition] = [];
|
||||
|
||||
data[category][unit.coalition].push(unit);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
groups.filter((unit:Unit) => unitCanBeImported(unit)).forEach((unit:Unit) => {
|
||||
const category = unit.getCategoryLabel();
|
||||
const coalition = unit.getCoalition();
|
||||
|
||||
if (!data.hasOwnProperty(category)) {
|
||||
data[category] = {};
|
||||
}
|
||||
|
||||
if (!data[category].hasOwnProperty(coalition))
|
||||
data[category][coalition] = [];
|
||||
|
||||
data[category][coalition].push(unit);
|
||||
});
|
||||
//*/
|
||||
this.data = data;
|
||||
this.buildCategoryCoalitionTable();
|
||||
this.dialog.show();
|
||||
}
|
||||
|
||||
selectFile() {
|
||||
var input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.addEventListener("change", (e: any) => {
|
||||
var file = e.target.files[0];
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
var reader = new FileReader();
|
||||
reader.onload = (e: any) => {
|
||||
this.#fileData = JSON.parse(e.target.result);
|
||||
this.#showForm();
|
||||
};
|
||||
reader.readAsText(file);
|
||||
})
|
||||
input.click();
|
||||
}
|
||||
|
||||
#unitDataCanBeImported(unitData: UnitData) {
|
||||
return unitData.alive && this.#unitGroupDataCanBeImported([unitData]);
|
||||
}
|
||||
|
||||
#unitGroupDataCanBeImported(unitGroupData: UnitData[]) {
|
||||
return unitGroupData.every((unitData: UnitData) => {
|
||||
return !["Aircraft", "Helicopter"].includes(unitData.category);
|
||||
}) && unitGroupData.some((unitData: UnitData) => unitData.alive);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import { CustomMarker } from '../map/markers/custommarker';
|
||||
import { SVGInjector } from '@tanem/svg-injector';
|
||||
import { UnitDatabase } from './databases/unitdatabase';
|
||||
import { TargetMarker } from '../map/markers/targetmarker';
|
||||
import { DLINK, DataIndexes, GAME_MASTER, HIDE_GROUP_MEMBERS, IDLE, IRST, MOVE_UNIT, OPTIC, RADAR, ROEs, RWR, SHOW_UNIT_CONTACTS, SHOW_UNITS_ENGAGEMENT_RINGS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, VISUAL, emissionsCountermeasures, reactionsToThreat, states, SHOW_UNITS_ACQUISITION_RINGS, HIDE_UNITS_SHORT_RANGE_RINGS, FILL_SELECTED_RING, GROUPING_ZOOM_TRANSITION, MAX_SHOTS_SCATTER, SHOTS_SCATTER_DEGREES } from '../constants/constants';
|
||||
import { DLINK, DataIndexes, GAME_MASTER, HIDE_GROUP_MEMBERS, IDLE, IRST, MOVE_UNIT, OPTIC, RADAR, ROEs, RWR, SHOW_UNIT_CONTACTS, SHOW_UNITS_ENGAGEMENT_RINGS, SHOW_UNIT_PATHS, SHOW_UNIT_TARGETS, VISUAL, emissionsCountermeasures, reactionsToThreat, states, SHOW_UNITS_ACQUISITION_RINGS, HIDE_UNITS_SHORT_RANGE_RINGS, FILL_SELECTED_RING, GROUPING_ZOOM_TRANSITION, MAX_SHOTS_SCATTER, SHOTS_SCATTER_DEGREES, GROUND_UNIT_AIR_DEFENCE_REGEX } from '../constants/constants';
|
||||
import { DataExtractor } from '../server/dataextractor';
|
||||
import { groundUnitDatabase } from './databases/groundunitdatabase';
|
||||
import { navyUnitDatabase } from './databases/navyunitdatabase';
|
||||
@@ -45,6 +45,7 @@ export abstract class Unit extends CustomMarker {
|
||||
#horizontalVelocity: number = 0;
|
||||
#verticalVelocity: number = 0;
|
||||
#heading: number = 0;
|
||||
#track: number = 0;
|
||||
#isActiveTanker: boolean = false;
|
||||
#isActiveAWACS: boolean = false;
|
||||
#onOff: boolean = true;
|
||||
@@ -127,6 +128,7 @@ export abstract class Unit extends CustomMarker {
|
||||
getHorizontalVelocity() { return this.#horizontalVelocity };
|
||||
getVerticalVelocity() { return this.#verticalVelocity };
|
||||
getHeading() { return this.#heading };
|
||||
getTrack() { return this.#track };
|
||||
getIsActiveAWACS() { return this.#isActiveAWACS };
|
||||
getIsActiveTanker() { return this.#isActiveTanker };
|
||||
getOnOff() { return this.#onOff };
|
||||
@@ -247,6 +249,14 @@ export abstract class Unit extends CustomMarker {
|
||||
*/
|
||||
abstract getDefaultMarker(): string;
|
||||
|
||||
/** Get the category but for display use - for the user. (i.e. has spaces in it)
|
||||
*
|
||||
* @returns string
|
||||
*/
|
||||
getCategoryLabel() {
|
||||
return ((GROUND_UNIT_AIR_DEFENCE_REGEX.test(this.getType())) ? "Air Defence" : this.getCategory()).replace(/([a-z])([A-Z])/g, "$1 $2");
|
||||
}
|
||||
|
||||
/********************** 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
|
||||
*
|
||||
@@ -278,6 +288,7 @@ export abstract class Unit extends CustomMarker {
|
||||
case DataIndexes.horizontalVelocity: this.#horizontalVelocity = dataExtractor.extractFloat64(); break;
|
||||
case DataIndexes.verticalVelocity: this.#verticalVelocity = dataExtractor.extractFloat64(); break;
|
||||
case DataIndexes.heading: this.#heading = dataExtractor.extractFloat64(); updateMarker = true; break;
|
||||
case DataIndexes.track: this.#track = dataExtractor.extractFloat64(); updateMarker = true; break;
|
||||
case DataIndexes.isActiveTanker: this.#isActiveTanker = dataExtractor.extractBool(); break;
|
||||
case DataIndexes.isActiveAWACS: this.#isActiveAWACS = dataExtractor.extractBool(); break;
|
||||
case DataIndexes.onOff: this.#onOff = dataExtractor.extractBool(); break;
|
||||
@@ -338,6 +349,7 @@ export abstract class Unit extends CustomMarker {
|
||||
getData(): UnitData {
|
||||
return {
|
||||
category: this.getCategory(),
|
||||
categoryDisplayName: this.getCategoryLabel(),
|
||||
ID: this.ID,
|
||||
alive: this.#alive,
|
||||
human: this.#human,
|
||||
@@ -355,6 +367,7 @@ export abstract class Unit extends CustomMarker {
|
||||
horizontalVelocity: this.#horizontalVelocity,
|
||||
verticalVelocity: this.#verticalVelocity,
|
||||
heading: this.#heading,
|
||||
track: this.#track,
|
||||
isActiveTanker: this.#isActiveTanker,
|
||||
isActiveAWACS: this.#isActiveAWACS,
|
||||
onOff: this.#onOff,
|
||||
@@ -552,6 +565,14 @@ export abstract class Unit extends CustomMarker {
|
||||
return false;
|
||||
}
|
||||
|
||||
isControlledByDCS() {
|
||||
return this.getControlled() === false && this.getHuman() === false;
|
||||
}
|
||||
|
||||
isControlledByOlympus() {
|
||||
return this.getControlled() === true;
|
||||
}
|
||||
|
||||
/********************** Icon *************************/
|
||||
createIcon(): void {
|
||||
/* Set the icon */
|
||||
@@ -678,9 +699,11 @@ export abstract class Unit extends CustomMarker {
|
||||
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")) ||
|
||||
(this.getHuman() && hiddenTypes.includes("human")) ||
|
||||
/* Hide the unit if it is DCS-controlled and DCS controlled units are hidden */
|
||||
(this.isControlledByDCS() && hiddenTypes.includes("dcs")) ||
|
||||
/* Hide the unit if it is Olympus-controlled and Olympus-controlled units are hidden */
|
||||
(this.isControlledByOlympus() && hiddenTypes.includes("olympus")) ||
|
||||
/* Hide the unit if this specific category is hidden */
|
||||
(hiddenTypes.includes(this.getMarkerCategory())) ||
|
||||
/* Hide the unit if this coalition is hidden */
|
||||
@@ -1167,7 +1190,7 @@ export abstract class Unit extends CustomMarker {
|
||||
|
||||
/* Rotate elements according to heading */
|
||||
element.querySelectorAll("[data-rotate-to-heading]").forEach(el => {
|
||||
const headingDeg = rad2deg(this.#heading);
|
||||
const headingDeg = rad2deg(this.#track);
|
||||
let currentStyle = el.getAttribute("style") || "";
|
||||
el.setAttribute("style", currentStyle + `transform:rotate(${headingDeg}deg);`);
|
||||
});
|
||||
@@ -1592,10 +1615,10 @@ export class GroundUnit extends Unit {
|
||||
}
|
||||
else {
|
||||
if (this.canAAA()) {
|
||||
contextActionSet.addContextAction(this, "scenic-aaa", "Scenic AAA", "Shoot AAA in the air without aiming at any target, when a enemy unit gets close enough.\nWARNING: works correctly only on neutral units, blue or red units will aim", (units: Unit[]) => { getApp().getUnitsManager().scenicAAA(units) }, undefined, {
|
||||
contextActionSet.addContextAction(this, "scenic-aaa", "Scenic AAA", "Shoot AAA in the air without aiming at any target, when an enemy unit gets close enough.\nWARNING: works correctly only on neutral units, blue or red units will aim", (units: Unit[]) => { getApp().getUnitsManager().scenicAAA(units) }, undefined, {
|
||||
"isScenic": true
|
||||
});
|
||||
contextActionSet.addContextAction(this, "miss-aaa", "Miss on purpose", "Shoot AAA towards the closest enemy unit, but don't aim precisely.\nWARNING: works correctly only on neutral units, blue or red units will aim", (units: Unit[]) => { getApp().getUnitsManager().missOnPurpose(units) }, undefined, {
|
||||
contextActionSet.addContextAction(this, "miss-aaa", "Dynamic accuracy AAA", "Shoot AAA towards the closest enemy unit, but don't aim precisely.\nWARNING: works correctly only on neutral units, blue or red units will aim", (units: Unit[]) => { getApp().getUnitsManager().missOnPurpose(units) }, undefined, {
|
||||
"isScenic": true
|
||||
});
|
||||
}
|
||||
|
||||
@@ -16,6 +16,8 @@ import { HotgroupPanel } from "../panels/hotgrouppanel";
|
||||
import { Contact, UnitData, UnitSpawnTable } from "../interfaces";
|
||||
import { Dialog } from "../dialog/dialog";
|
||||
import { Group } from "./group";
|
||||
import { UnitDataFileExport } from "./importexport/unitdatafileexport";
|
||||
import { UnitDataFileImport } from "./importexport/unitdatafileimport";
|
||||
|
||||
/** The UnitsManager handles the creation, update, and control of units. Data is strictly updated by the server ONLY. This means that any interaction from the user will always and only
|
||||
* result in a command to the server, executed by means of a REST PUT request. Any subsequent change in data will be reflected only when the new data is sent back by the server. This strategy allows
|
||||
@@ -29,6 +31,8 @@ export class UnitsManager {
|
||||
#slowDeleteDialog!: Dialog;
|
||||
#units: { [ID: number]: Unit };
|
||||
#groups: { [groupName: string]: Group } = {};
|
||||
#unitDataExport!:UnitDataFileExport;
|
||||
#unitDataImport!:UnitDataFileImport;
|
||||
|
||||
constructor() {
|
||||
this.#copiedUnits = [];
|
||||
@@ -791,7 +795,7 @@ export class UnitsManager {
|
||||
this.#showActionMessage(units, `unit set to perform scenic AAA`);
|
||||
}
|
||||
|
||||
/** Instruct units to enter into miss on purpose mode. Units will aim to the nearest enemy unit but not precisely.
|
||||
/** Instruct units to enter into dynamic accuracy/miss on purpose mode. Units will aim to the nearest enemy unit but not precisely.
|
||||
* @param units (Optional) Array of units to apply the control to. If not provided, the operation will be completed on all selected units.
|
||||
*/
|
||||
missOnPurpose(units: Unit[] | null = null) {
|
||||
@@ -1104,11 +1108,38 @@ export class UnitsManager {
|
||||
const activeEras = Object.keys(eras).filter((key: string) => { return eras[key]; });
|
||||
const activeRanges = Object.keys(ranges).filter((key: string) => { return ranges[key]; });
|
||||
|
||||
var airbases = getApp().getMissionManager().getAirbases();
|
||||
Object.keys(airbases).forEach((airbaseName: string) => {
|
||||
var airbase = airbases[airbaseName];
|
||||
/* Check if the city is inside the coalition area */
|
||||
if (polyContains(new LatLng(airbase.getLatLng().lat, airbase.getLatLng().lng), coalitionArea)) {
|
||||
/* Arbitrary formula to obtain a number of units */
|
||||
var pointsNumber = 2 + 10 * density / 100;
|
||||
for (let i = 0; i < pointsNumber; i++) {
|
||||
/* Place the unit nearby the airbase, depending on the distribution parameter */
|
||||
var bearing = Math.random() * 360;
|
||||
var distance = Math.random() * distribution * 100;
|
||||
const latlng = bearingAndDistanceToLatLng(airbase.getLatLng().lat, airbase.getLatLng().lng, bearing, distance);
|
||||
|
||||
/* Make sure the unit is still inside the coalition area */
|
||||
if (polyContains(latlng, coalitionArea)) {
|
||||
const type = activeTypes[Math.floor(Math.random() * activeTypes.length)];
|
||||
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)
|
||||
this.spawnUnits("GroundUnit", [{ unitType: unitBlueprint.name, location: latlng, liveryID: "" }], coalitionArea.getCoalition(), false, "", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
citiesDatabase.forEach((city: { lat: number, lng: number, pop: number }) => {
|
||||
/* Check if the city is inside the coalition area */
|
||||
if (polyContains(new LatLng(city.lat, city.lng), coalitionArea)) {
|
||||
/* Arbitrary formula to obtain a number of units depending on the city population */
|
||||
var pointsNumber = 2 + Math.pow(city.pop, 0.2) * density / 100;
|
||||
var pointsNumber = 2 + Math.pow(city.pop, 0.15) * density / 100;
|
||||
for (let i = 0; i < pointsNumber; i++) {
|
||||
/* Place the unit nearby the city, depending on the distribution parameter */
|
||||
var bearing = Math.random() * 360;
|
||||
@@ -1121,8 +1152,8 @@ 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)
|
||||
this.spawnUnits("GroundUnit", [{ unitType: unitBlueprint.name, location: latlng, liveryID: "" }], coalitionArea.getCoalition(), true);
|
||||
if (unitBlueprint)
|
||||
this.spawnUnits("GroundUnit", [{ unitType: unitBlueprint.name, location: latlng, liveryID: "" }], coalitionArea.getCoalition(), false, "", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1134,52 +1165,18 @@ export class UnitsManager {
|
||||
* TODO: Extend to aircraft and helicopters
|
||||
*/
|
||||
exportToFile() {
|
||||
var unitsToExport: { [key: string]: any } = {};
|
||||
for (let ID in this.#units) {
|
||||
var unit = this.#units[ID];
|
||||
if (!["Aircraft", "Helicopter"].includes(unit.getCategory())) {
|
||||
var data: any = unit.getData();
|
||||
if (unit.getGroupName() in unitsToExport)
|
||||
unitsToExport[unit.getGroupName()].push(data);
|
||||
else
|
||||
unitsToExport[unit.getGroupName()] = [data];
|
||||
}
|
||||
}
|
||||
var a = document.createElement("a");
|
||||
var file = new Blob([JSON.stringify(unitsToExport)], { type: 'text/plain' });
|
||||
a.href = URL.createObjectURL(file);
|
||||
a.download = 'export.json';
|
||||
a.click();
|
||||
if (!this.#unitDataExport)
|
||||
this.#unitDataExport = new UnitDataFileExport("unit-export-dialog");
|
||||
this.#unitDataExport.showForm(Object.values(this.#units));
|
||||
}
|
||||
|
||||
|
||||
/** Import ground and navy units from file
|
||||
* TODO: extend to support aircraft and helicopters
|
||||
*/
|
||||
importFromFile() {
|
||||
var input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.addEventListener("change", (e: any) => {
|
||||
var file = e.target.files[0];
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
var reader = new FileReader();
|
||||
reader.onload = function (e: any) {
|
||||
var contents = e.target.result;
|
||||
var groups = JSON.parse(contents);
|
||||
for (let groupName in groups) {
|
||||
if (groupName !== "" && groups[groupName].length > 0 && (groups[groupName].every((unit: UnitData) => { return unit.category == "GroundUnit"; }) || groups[groupName].every((unit: any) => { return unit.category == "NavyUnit"; }))) {
|
||||
var aliveUnits = groups[groupName].filter((unit: UnitData) => { return unit.alive });
|
||||
var units = aliveUnits.map((unit: UnitData) => {
|
||||
return { unitType: unit.name, location: unit.position, liveryID: "" }
|
||||
});
|
||||
getApp().getUnitsManager().spawnUnits(groups[groupName][0].category, units, groups[groupName][0].coalition, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
reader.readAsText(file);
|
||||
})
|
||||
input.click();
|
||||
if (!this.#unitDataImport)
|
||||
this.#unitDataImport = new UnitDataFileImport("unit-import-dialog");
|
||||
this.#unitDataImport.selectFile();
|
||||
}
|
||||
|
||||
/** Spawn a new group of units
|
||||
|
||||
@@ -2,14 +2,14 @@
|
||||
<div id="airbase-active-coalition-label" data-coalition="blue"></div>
|
||||
<div class="upper-bar ol-panel">
|
||||
<button data-coalition="blue" id="airbase-aircraft-spawn-button" title="Spawn aircraft" data-on-click="mapContextMenuShow"
|
||||
data-on-click-params='{ "type": "aircraft" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/aircraft.svg" inject-svg></button>
|
||||
data-on-click-params='{ "type": "aircraft" }' class="ol-context-menu-button"><img src="/resources/theme/images/buttons/spawn/aircraft.svg" inject-svg></button>
|
||||
<button data-coalition="blue" id="airbase-helicopter-spawn-button" title="Spawn helicopter" data-on-click="mapContextMenuShow"
|
||||
data-on-click-params='{ "type": "helicopter" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/helicopter.svg" inject-svg></button>
|
||||
data-on-click-params='{ "type": "helicopter" }' class="ol-context-menu-button"><img src="/resources/theme/images/buttons/spawn/helicopter.svg" inject-svg></button>
|
||||
</div>
|
||||
<div id="airbase-aircraft-spawn-menu" class="ol-contexmenu-panel ol-panel hide">
|
||||
<div id="airbase-aircraft-spawn-menu" class="ol-context-menu-panel ol-panel hide">
|
||||
<!-- Here the aircraft spawn menu will be shown -->
|
||||
</div>
|
||||
<div id="airbase-helicopter-spawn-menu" class="ol-contexmenu-panel ol-panel hide">
|
||||
<div id="airbase-helicopter-spawn-menu" class="ol-context-menu-panel ol-panel hide">
|
||||
<!-- Here the helicopter spawn menu will be shown -->
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,17 +1,17 @@
|
||||
<div id="coalition-area-contextmenu" class="ol-context-menu" oncontextmenu="return false;">
|
||||
<div id="area-coalition-label" data-coalition="blue"></div>
|
||||
<div class="upper-bar ol-panel">
|
||||
<div id="coalition-area-switch" class="ol-switch ol-coalition-switch"></div>
|
||||
<div class="switch-control coalition no-label"><div id="coalition-area-switch" class="ol-switch"></div></div>
|
||||
<button data-coalition="blue" id="iads-button" title="Create Integrated Air Defense System" data-on-click="coalitionAreaContextMenuShow"
|
||||
data-on-click-params='{ "type": "iads" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/sam.svg" inject-svg></button>
|
||||
data-on-click-params='{ "type": "iads" }' class="ol-context-menu-button"><img src="/resources/theme/images/buttons/spawn/sam.svg" inject-svg></button>
|
||||
<!--<button data-coalition="blue" id="cap-button" title="Create Combat Air Patrols" data-on-click="coalitionAreaContextMenuShow"
|
||||
data-on-click-params='{ "type": "cap" }' class="ol-contexmenu-button"></button>-->
|
||||
<button data-coalition="blue" id="coalitionarea-back-button" title="Bring area to back" data-on-click="coalitionAreaBringToBack"
|
||||
class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/other/back.svg" inject-svg></button>
|
||||
data-on-click-params='{ "type": "cap" }' class="ol-context-menu-button"></button>-->
|
||||
<button data-coalition="blue" id="coalitionarea-back-button" title="Send to back" data-on-click="coalitionAreaBringToBack"
|
||||
class="ol-context-menu-button"><img src="/resources/theme/images/buttons/other/back.svg" inject-svg></button>
|
||||
<button data-coalition="blue" id="coalitionarea-delete-button" title="Delete area" data-on-click="coalitionAreaDelete"
|
||||
class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/other/delete.svg" inject-svg></button>
|
||||
class="ol-context-menu-button"><img src="/resources/theme/images/buttons/other/delete.svg" inject-svg></button>
|
||||
</div>
|
||||
<div id="iads-menu" class="ol-panel ol-contexmenu-panel hide">
|
||||
<div id="iads-menu" class="ol-panel ol-context-menu-panel hide">
|
||||
<div id="iads-units-type-options" class="ol-select">
|
||||
<div class="ol-select-value">Unit types</div>
|
||||
<div class="ol-select-options">
|
||||
|
||||
@@ -3,57 +3,56 @@
|
||||
<div class="upper-bar ol-panel">
|
||||
<div class="switch-control coalition no-label"><div id="coalition-switch" class="ol-switch"></div></div>
|
||||
<button data-coalition="blue" id="aircraft-spawn-button" title="Spawn aircraft" data-on-click="mapContextMenuShow"
|
||||
data-on-click-params='{ "type": "aircraft" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/aircraft.svg" inject-svg></button>
|
||||
data-on-click-params='{ "type": "aircraft" }' class="ol-context-menu-button"><img src="/resources/theme/images/buttons/spawn/aircraft.svg" inject-svg></button>
|
||||
<button data-coalition="blue" id="helicopter-spawn-button" title="Spawn helicopter" data-on-click="mapContextMenuShow"
|
||||
data-on-click-params='{ "type": "helicopter" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/helicopter.svg" inject-svg></button>
|
||||
data-on-click-params='{ "type": "helicopter" }' class="ol-context-menu-button"><img src="/resources/theme/images/buttons/spawn/helicopter.svg" inject-svg></button>
|
||||
<button data-coalition="blue" id="air-defence-spawn-button" title="Spawn air defence unit" data-on-click="mapContextMenuShow"
|
||||
data-on-click-params='{ "type": "air-defence" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/sam.svg" inject-svg></button>
|
||||
data-on-click-params='{ "type": "air-defence" }' class="ol-context-menu-button"><img src="/resources/theme/images/buttons/spawn/sam.svg" inject-svg></button>
|
||||
<button data-coalition="blue" id="groundunit-spawn-button" title="Spawn ground unit" data-on-click="mapContextMenuShow"
|
||||
data-on-click-params='{ "type": "groundunit" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/groundunit.svg" inject-svg></button>
|
||||
data-on-click-params='{ "type": "groundunit" }' class="ol-context-menu-button"><img src="/resources/theme/images/buttons/spawn/groundunit.svg" inject-svg></button>
|
||||
<button data-coalition="blue" id="coalition-area-button" title="Edit coalition area" data-on-click="editCoalitionArea"
|
||||
class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/other/edit.svg" inject-svg></button>
|
||||
class="ol-context-menu-button"><img src="/resources/theme/images/buttons/other/edit.svg" inject-svg></button>
|
||||
<button data-coalition="blue" id="more-options-button" title="More options" data-on-click="mapContextMenuShow"
|
||||
data-on-click-params='{ "type": "more" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/more.svg" inject-svg></button>
|
||||
data-on-click-params='{ "type": "more" }' class="ol-context-menu-button"><img src="/resources/theme/images/buttons/spawn/more.svg" inject-svg></button>
|
||||
</div>
|
||||
<div id="more-options-button-bar" class="upper-bar ol-panel hide">
|
||||
<button data-coalition="blue" id="navyunit-spawn-button" title="Spawn navy unit" data-on-click="mapContextMenuShow"
|
||||
data-on-click-params='{ "type": "navyunit" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/navyunit.svg" inject-svg></button>
|
||||
data-on-click-params='{ "type": "navyunit" }' class="ol-context-menu-button"><img src="/resources/theme/images/buttons/spawn/navyunit.svg" inject-svg></button>
|
||||
<button data-coalition="blue" id="smoke-spawn-button" title="Spawn smoke" data-on-click="mapContextMenuShow"
|
||||
data-on-click-params='{ "type": "smoke" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/smoke.svg" inject-svg></button>
|
||||
data-on-click-params='{ "type": "smoke" }' class="ol-context-menu-button"><img src="/resources/theme/images/buttons/spawn/smoke.svg" inject-svg></button>
|
||||
<button data-coalition="blue" id="explosion-spawn-button" title="Explosion" data-on-click="mapContextMenuShow"
|
||||
data-on-click-params='{ "type": "explosion" }' class="ol-contexmenu-button"><img src="/resources/theme/images/buttons/spawn/explosion.svg" inject-svg></button>
|
||||
data-on-click-params='{ "type": "explosion" }' class="ol-context-menu-button"><img src="/resources/theme/images/buttons/spawn/explosion.svg" inject-svg></button>
|
||||
<button data-coalition="blue" id="polygon-draw-button" title="Enter polygon draw mode" data-on-click="toggleCoalitionAreaDraw"
|
||||
data-on-click-params='{"type": "polygon"}' class="ol-contexmenu-button"><img src="resources/theme/images/buttons/tools/draw-polygon-solid.svg" inject-svg></button>
|
||||
data-on-click-params='{"type": "polygon"}' class="ol-context-menu-button"><img src="resources/theme/images/buttons/tools/draw-polygon-solid.svg" inject-svg></button>
|
||||
</div>
|
||||
<div id="aircraft-spawn-menu" class="ol-contexmenu-panel ol-panel hide">
|
||||
<div id="aircraft-spawn-menu" class="ol-context-menu-panel ol-panel hide">
|
||||
<!-- Here the aircraft spawn menu will be shown -->
|
||||
</div>
|
||||
<div id="helicopter-spawn-menu" class="ol-contexmenu-panel ol-panel hide">
|
||||
<div id="helicopter-spawn-menu" class="ol-context-menu-panel ol-panel hide">
|
||||
<!-- Here the helicopter spawn menu will be shown -->
|
||||
</div>
|
||||
<div id="air-defence-spawn-menu" class="ol-panel ol-contexmenu-panel hide">
|
||||
<div id="air-defence-spawn-menu" class="ol-panel ol-context-menu-panel hide">
|
||||
<!-- Here the air defence units' spawn menu will be shown -->
|
||||
</div>
|
||||
<div id="groundunit-spawn-menu" class="ol-panel ol-contexmenu-panel hide">
|
||||
<div id="groundunit-spawn-menu" class="ol-panel ol-context-menu-panel hide">
|
||||
<!-- Here the ground units' spawn menu will be shown -->
|
||||
</div>
|
||||
<div id="navyunit-spawn-menu" class="ol-panel ol-contexmenu-panel hide">
|
||||
<div id="navyunit-spawn-menu" class="ol-panel ol-context-menu-panel hide">
|
||||
<!-- Here the navy units' spawn menu will be shown -->
|
||||
</div>
|
||||
<div id="smoke-spawn-menu" class="ol-panel ol-contexmenu-panel hide">
|
||||
<div id="smoke-spawn-menu" class="ol-panel ol-context-menu-panel hide">
|
||||
<button class="smoke-button" title="" data-smoke-color="white" data-on-click="contextMenuDeploySmoke" data-on-click-params='{ "color": "white" }'>White smoke</button>
|
||||
<button class="smoke-button" title="" data-smoke-color="blue" data-on-click="contextMenuDeploySmoke" data-on-click-params='{ "color": "blue" }'>Blue smoke</button>
|
||||
<button class="smoke-button" title="" data-smoke-color="red" data-on-click="contextMenuDeploySmoke" data-on-click-params='{ "color": "red" }'>Red smoke</button>
|
||||
<button class="smoke-button" title="" data-smoke-color="green" data-on-click="contextMenuDeploySmoke" data-on-click-params='{ "color": "green" }'>Green smoke</button>
|
||||
<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">
|
||||
<div id="explosion-menu" class="ol-panel ol-context-menu-panel hide">
|
||||
<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": "secondary"}'>Explosion with debries</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,5 +1,17 @@
|
||||
<html>
|
||||
|
||||
<script>
|
||||
const queryString = window.location.search;
|
||||
const urlParams = new URLSearchParams(queryString);
|
||||
const theme = urlParams.get('theme');
|
||||
if (theme != undefined) {
|
||||
var xmlHttp = new XMLHttpRequest();
|
||||
xmlHttp.open("PUT", "/resources/theme/" + theme);
|
||||
xmlHttp.send("");
|
||||
console.log("Setting theme to " + theme)
|
||||
}
|
||||
</script>
|
||||
|
||||
<head>
|
||||
<title>Olympus client</title>
|
||||
<link rel="stylesheet" type="text/css" href="stylesheets/olympus.css" />
|
||||
|
||||
@@ -1,333 +1,19 @@
|
||||
<div id="splash-screen" class="ol-dialog" oncontextmenu="return false;">
|
||||
<div id="splash-content" class="ol-dialog-content">
|
||||
<div id="app-summary">
|
||||
<h2>DCS Olympus</h2>
|
||||
<h4>Dynamic Unit Command</h4>
|
||||
<div class="app-version">Version <span class="app-version-number">v0.4.8-alpha</span></div>
|
||||
</div>
|
||||
|
||||
<form id="authentication-form">
|
||||
<div><h5>Name</h5> <input type="text" id="username" name="username" required autocomplete="username" placeholder="Enter name..."></div>
|
||||
<div><h5>Server password</h5> <input type="password" id="password" name="password" minlength="1" required autocomplete="current-password" placeholder="Enter server password..."></div>
|
||||
<button type="submit" id="connection-button" class="ol-button-apply">Connect</button>
|
||||
</form>
|
||||
|
||||
<h5 id="login-status"><br></h5>
|
||||
|
||||
<div id="legal-stuff">
|
||||
<h5>DISCLAIMER</h5>
|
||||
<p>
|
||||
Copyright (C) 2023 Veltro & Gang
|
||||
</p>
|
||||
<p>
|
||||
DCS Olympus (the "MATERIAL" or "Software") is provided completely free
|
||||
to users subject to the it under both the terms of version 3 of the GNU
|
||||
General Public License as published by the Free Software Foundation, and
|
||||
the additional terms set out below; except where such terms conflict with this
|
||||
disclaimer, in which case, the terms of this disclaimer shall prevail.
|
||||
</p>
|
||||
<p>
|
||||
The authors and/or copyright holders of the Software have not received any
|
||||
financial benefit in connection with the Software. In any event, the
|
||||
Software is provided “as is”, without warranty of any kind, express or
|
||||
implied, including but not limited to the warranties of merchantability,
|
||||
fitness for a particular purpose and non-infringement. In no event shall
|
||||
the authors and/or copyright holders be liable for any claim, damages or
|
||||
other liability, whether in an action of contract, tort or otherwise,
|
||||
arising from, out of or in connection with the Software or the use or o
|
||||
ther dealings in the Software.
|
||||
</p>
|
||||
<p>
|
||||
Any party making use of the Software in any manner agrees to be
|
||||
bound by the terms set out in this disclaimer, version 3 of the GNU
|
||||
General Public Licence, and the Additional Terms below.
|
||||
</p>
|
||||
<p>
|
||||
THIS MATERIAL IS NOT MADE OR SUPPORTED BY EAGLE DYNAMICS SA.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="advanced-settings-dialog" class="ol-panel ol-dialog olympus-dialog-close hide" oncontextmenu="return false;">
|
||||
<div class="ol-dialog-close" data-on-click="closeDialog"></div>
|
||||
|
||||
<div class="ol-dialog-header">
|
||||
<h3 id="unit-name">Olympus 1-1</h3>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-content">
|
||||
|
||||
<!-- General settings -->
|
||||
<div id="general-settings">
|
||||
<div class="ol-group">
|
||||
<h4>General settings</h4>
|
||||
<hr>
|
||||
</div>
|
||||
<div id="general-settings-grid">
|
||||
<div id="prohibit-jettison-checkbox" class="ol-checkbox air-unit-checkbox">
|
||||
<label title="The unit will not jettison external stores">
|
||||
<input type="checkbox"/>
|
||||
Prohibit jettison
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="prohibit-afterburner-checkbox" class="ol-checkbox air-unit-checkbox">
|
||||
<label title="The unit will not engage the afterburner">
|
||||
<input type="checkbox" />
|
||||
Prohibit afterburner
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="prohibit-AA-checkbox" class="ol-checkbox air-unit-checkbox">
|
||||
<label title="The unit will not engage airborne targets">
|
||||
<input type="checkbox" />
|
||||
Prohibit A/A
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="prohibit-AG-checkbox" class="ol-checkbox air-unit-checkbox">
|
||||
<label title="The unit will not engage ground targets">
|
||||
<input type="checkbox" />
|
||||
Prohibit A/G
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="prohibit-air-wpn-checkbox" class="ol-checkbox">
|
||||
<label title="The unit will not engage air weapons (e.g. SAM sites will not engage HARMs)">
|
||||
<input type="checkbox" />
|
||||
Prohibit air wpn engage
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- TACAN options -->
|
||||
<div id="TACAN-options">
|
||||
<div class="ol-group">
|
||||
<h4>TACAN options</h4>
|
||||
<hr>
|
||||
</div>
|
||||
<div id="TACAN-checkbox" class="ol-checkbox">
|
||||
<label title="Turn ON the TACAN">
|
||||
<input type="checkbox" />
|
||||
Activate TACAN
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>TACAN: </label>
|
||||
|
||||
<div class="ol-group">
|
||||
<div id="TACAN-channel" class="ol-text-input">
|
||||
<input type="number" onkeypress='return event.charCode >= 48 && event.charCode <= 57' onkeyup="if (value > 126) value = 126;">
|
||||
</div>
|
||||
|
||||
<div id="TACAN-XY" class="ol-select">
|
||||
<div class="ol-select-value">X</div>
|
||||
<div class="ol-select-options">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="TACAN-callsign" class="ol-text-input">
|
||||
<input type="text" maxlength="3" value="TKR" style="width: 50px">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Radio options -->
|
||||
<div id="radio-options">
|
||||
<div class="ol-group">
|
||||
<h4>Radio options</h4>
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label> Radio frequency: </label>
|
||||
|
||||
<div class="ol-group">
|
||||
<div id="radio-mhz" class="ol-text-input">
|
||||
<input type="number" onkeypress='return event.charCode >= 48 && event.charCode <= 57' onkeyup="if (value > 999) value = 999;">
|
||||
</div>
|
||||
|
||||
<div id="radio-decimals" class="ol-select">
|
||||
<div class="ol-select-value">.000</div>
|
||||
<div class="ol-select-options">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label> Radio callsign: </label>
|
||||
|
||||
<div class="ol-group">
|
||||
<div id="radio-callsign" class="ol-select">
|
||||
<div class="ol-select-value"></div>
|
||||
<div class="ol-select-options">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label>
|
||||
-
|
||||
</label>
|
||||
|
||||
<div id="radio-callsign-number" class="ol-text-input">
|
||||
<input type="number" min="1" max="999" step="1" value="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-footer ol-group">
|
||||
<button class="ol-button-apply" data-on-click="applyAdvancedSettings">Apply</button>
|
||||
<button class="ol-button-close" data-on-click="closeDialog">Close</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="custom-formation-dialog" class="ol-panel ol-dialog olympus-dialog-close hide" oncontextmenu="return false;">
|
||||
<div class="ol-dialog-close" data-on-click="closeDialog"></div>
|
||||
|
||||
<div class="ol-dialog-header">
|
||||
<h3 id="unit-name">Custom formation</h3>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-content">
|
||||
<div class="formation-position-clock">
|
||||
<div class="clock-hand" style="top: 0px; left: 50px;"><input type="radio" id="formation-1" name="formation-position" value="1"></div>
|
||||
<div class="clock-hand" style="top: 14px; left: 14px;"><input type="radio" id="formation-2" name="formation-position" value="2"></div>
|
||||
<div class="clock-hand" style="top: 50px; left: 0px; "><input type="radio" id="formation-3" name="formation-position" value="3"></div>
|
||||
<div class="clock-hand" style="top: 86px; left: 14px;"><input type="radio" id="formation-4" name="formation-position" value="4"></div>
|
||||
<div class="clock-hand" style="top: 100px; left: 50px;"><input type="radio" id="formation-5" name="formation-position" value="5"></div>
|
||||
<div class="clock-hand" style="top: 86px; left: 86px;"><input type="radio" id="formation-6" name="formation-position" value="6" checked></div>
|
||||
<div class="clock-hand" style="top: 50px; left: 100px"><input type="radio" id="formation-7" name="formation-position" value="7"></div>
|
||||
<div class="clock-hand" style="top: 14px; left: 86px;"><input type="radio" id="formation-8" name="formation-position" value="8"></div>
|
||||
<div style="top: 50px; left: 50px;" id="reference-system"></div>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>Distance: </label>
|
||||
|
||||
<div class="ol-group">
|
||||
<div id="distance" class="ol-text-input">
|
||||
<input type="number" min="0" max="999999" step="1" value="150">
|
||||
</div>
|
||||
<label>ft</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>Up-down: </label>
|
||||
|
||||
<div class="ol-group">
|
||||
<div id="up-down" class="ol-text-input">
|
||||
<input type="number" min="-99999" max="99999" step="1" value="30">
|
||||
</div>
|
||||
<label>ft</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-footer ol-group">
|
||||
<button class="ol-button-apply" data-on-click="applyCustomFormation">Apply</button>
|
||||
<button class="ol-button-close" data-on-click="closeDialog">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="command-mode-settings-dialog" class="ol-panel ol-dialog olympus-dialog-close hide" oncontextmenu="return false;">
|
||||
<div class="ol-dialog-close" data-on-click="closeDialog"></div>
|
||||
|
||||
<div class="ol-dialog-header">
|
||||
<h3 id="unit-name">Command mode settings</h3>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-content">
|
||||
<div id="restrict-spawns" class="ol-checkbox">
|
||||
<label title="If false, no spawn restrictions will be applied">
|
||||
<input type="checkbox"/>
|
||||
Restrict spawns
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="restrict-to-coalition" class="ol-checkbox">
|
||||
<label title="If true, commanders will be allowed to only spawn units that belong to their coalition. E.g. blue commanders will be able to spawn F/A-18 Hornets, but not MiG-29s.">
|
||||
<input type="checkbox"/>
|
||||
Restrict units to coalition
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>Setup time: </label>
|
||||
|
||||
<div class="ol-group">
|
||||
<div id="setup-time" class="ol-text-input">
|
||||
<input type="number" min="-99999" max="99999" step="1" value="10">
|
||||
</div>
|
||||
<label>minutes</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>Available eras: </label>
|
||||
|
||||
<div id="command-mode-era-options" class="ol-select">
|
||||
<div class="ol-select-value">Select eras</div>
|
||||
<div class="ol-select-options">
|
||||
<!-- This is where all the available era buttons will be shown-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<h4>Spawn points</h4>
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>Blue spawn points: </label>
|
||||
<div id="blue-spawn-points" class="ol-text-input">
|
||||
<input type="number" min="0" max="999999" step="1" value="1000">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>Red spawn points: </label>
|
||||
<div id="red-spawn-points" class="ol-text-input">
|
||||
<input type="number" min="0" max="999999" step="1" value="1000">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-footer ol-group">
|
||||
<button class="ol-button-apply" data-on-click="applycommandModeOptions">Apply</button>
|
||||
<button class="ol-button-close" data-on-click="closeDialog">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="slow-delete-dialog" class="ol-panel ol-dialog hide" oncontextmenu="return false;">
|
||||
<div class="ol-dialog-header">
|
||||
<h3 id="unit-name">Confirm deletion method</h3>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-content">
|
||||
<p>You are trying to delete a large amount of units (<span class="deletion-count"></span>), which can cause the server to lag for players.</p>
|
||||
<p>You may:
|
||||
<ul>
|
||||
<li>delete in batches (less lag but Olympus cannot process any additional orders until<br /> all units have been deleted);</li>
|
||||
<li>delete immediately (you can continue to give Olympus orders but players may<br />experience lag while this happens);</li>
|
||||
<li>cancel this instruction.</li>
|
||||
</ul></p>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-footer ol-group">
|
||||
<button data-action="delete-slow">Delete in batches (~<span class="deletion-time"></span>s)</button>
|
||||
<button data-action="delete-immediate">Delete immediately</button>
|
||||
<button data-action="delete-cancel">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
<%- include('dialogs/advancedsettings.ejs') %>
|
||||
<%- include('dialogs/commandmodesettings.ejs') %>
|
||||
<%- include('dialogs/customformation.ejs') %>
|
||||
<%- include('dialogs/importexport.ejs', {
|
||||
"dialogId": "unit-export-dialog",
|
||||
"submitButtonText": "Export units to file",
|
||||
"textContent": "Select the unit categories you would like to export. Note: only ground and naval units can be exported at this time.",
|
||||
"title": "Export",
|
||||
"showFilenameInput": true
|
||||
}) %>
|
||||
<%- include('dialogs/importexport.ejs', {
|
||||
"dialogId": "unit-import-dialog",
|
||||
"submitButtonText": "Import units into mission",
|
||||
"textContent": "Select the unit categories you would like to import.",
|
||||
"title": "Import",
|
||||
"showFilenameInput": false
|
||||
}) %>
|
||||
<%- include('dialogs/slowdelete.ejs') %>
|
||||
<%- include('dialogs/splash.ejs') %>
|
||||
162
client/views/other/dialogs/advancedsettings.ejs
Normal file
@@ -0,0 +1,162 @@
|
||||
<div id="advanced-settings-dialog" class="ol-panel ol-dialog olympus-dialog-close hide" oncontextmenu="return false;">
|
||||
<div class="ol-dialog-close" data-on-click="closeDialog"></div>
|
||||
|
||||
<div class="ol-dialog-header">
|
||||
<h3 id="unit-name">Olympus 1-1</h3>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-content">
|
||||
|
||||
<!-- General settings -->
|
||||
<div id="general-settings">
|
||||
<div class="ol-group">
|
||||
<h4>General settings</h4>
|
||||
<hr>
|
||||
</div>
|
||||
<div id="general-settings-grid">
|
||||
<div id="prohibit-jettison-checkbox" class="ol-checkbox air-unit-checkbox">
|
||||
<label title="The unit will not jettison external stores">
|
||||
<input type="checkbox"/>
|
||||
Prohibit jettison
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="prohibit-afterburner-checkbox" class="ol-checkbox air-unit-checkbox">
|
||||
<label title="The unit will not engage the afterburner">
|
||||
<input type="checkbox" />
|
||||
Prohibit afterburner
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="prohibit-AA-checkbox" class="ol-checkbox air-unit-checkbox">
|
||||
<label title="The unit will not engage airborne targets">
|
||||
<input type="checkbox" />
|
||||
Prohibit A/A
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="prohibit-AG-checkbox" class="ol-checkbox air-unit-checkbox">
|
||||
<label title="The unit will not engage ground targets">
|
||||
<input type="checkbox" />
|
||||
Prohibit A/G
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="prohibit-air-wpn-checkbox" class="ol-checkbox">
|
||||
<label title="The unit will not engage air weapons (e.g. SAM sites will not engage HARMs)">
|
||||
<input type="checkbox" />
|
||||
Prohibit air wpn engage
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tasking -->
|
||||
<!--
|
||||
<div id="tasking">
|
||||
<div class="ol-group">
|
||||
<h4>Tasking</h4>
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<div id="tanker-checkbox" class="ol-checkbox">
|
||||
<label title="The unit will operate as Air to Air Refuelling tanker for airplanes that have a compatible refuelling system">
|
||||
<input type="checkbox" />
|
||||
Operate as AAR tanker
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="AWACS-checkbox" class="ol-checkbox">
|
||||
<label title="The unit will operate as AWACS on datalink">
|
||||
<input type="checkbox" />
|
||||
Operate as AWACS
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
-->
|
||||
|
||||
<!-- TACAN options -->
|
||||
<div id="TACAN-options">
|
||||
<div class="ol-group">
|
||||
<h4>TACAN options</h4>
|
||||
<hr>
|
||||
</div>
|
||||
<div id="TACAN-checkbox" class="ol-checkbox">
|
||||
<label title="Turn ON the TACAN">
|
||||
<input type="checkbox" />
|
||||
Activate TACAN
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>TACAN: </label>
|
||||
|
||||
<div class="ol-group">
|
||||
<div id="TACAN-channel" class="ol-text-input">
|
||||
<input type="number" min="1" max="126" step="1" value="40">
|
||||
</div>
|
||||
|
||||
<div id="TACAN-XY" class="ol-select">
|
||||
<div class="ol-select-value">X</div>
|
||||
<div class="ol-select-options">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="TACAN-callsign" class="ol-text-input">
|
||||
<input type="text" maxlength="3" value="TKR" style="width: 50px">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Radio options -->
|
||||
<div id="radio-options">
|
||||
<div class="ol-group">
|
||||
<h4>Radio options</h4>
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label> Radio frequency: </label>
|
||||
|
||||
<div class="ol-group">
|
||||
<div id="radio-mhz" class="ol-text-input">
|
||||
<input type="number" min="1" max="999" step="1" value="260">
|
||||
</div>
|
||||
|
||||
<div id="radio-decimals" class="ol-select">
|
||||
<div class="ol-select-value">.000</div>
|
||||
<div class="ol-select-options">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label> Radio callsign: </label>
|
||||
|
||||
<div class="ol-group">
|
||||
<div id="radio-callsign" class="ol-select">
|
||||
<div class="ol-select-value"></div>
|
||||
<div class="ol-select-options">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label>
|
||||
-
|
||||
</label>
|
||||
|
||||
<div id="radio-callsign-number" class="ol-text-input">
|
||||
<input type="number" min="1" max="999" step="1" value="1">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-footer ol-group">
|
||||
<button class="ol-button-apply" data-on-click="applyAdvancedSettings">Apply</button>
|
||||
<button class="ol-button-close" data-on-click="closeDialog">Close</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
70
client/views/other/dialogs/commandmodesettings.ejs
Normal file
@@ -0,0 +1,70 @@
|
||||
<div id="command-mode-settings-dialog" class="ol-panel ol-dialog olympus-dialog-close hide" oncontextmenu="return false;">
|
||||
<div class="ol-dialog-close" data-on-click="closeDialog"></div>
|
||||
|
||||
<div class="ol-dialog-header">
|
||||
<h3 id="unit-name">Command mode settings</h3>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-content">
|
||||
<div id="restrict-spawns" class="ol-checkbox">
|
||||
<label title="If false, no spawn restrictions will be applied">
|
||||
<input type="checkbox"/>
|
||||
Restrict spawns
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div id="restrict-to-coalition" class="ol-checkbox">
|
||||
<label title="If true, commanders will be allowed to only spawn units that belong to their coalition. E.g. blue commanders will be able to spawn F/A-18 Hornets, but not MiG-29s.">
|
||||
<input type="checkbox"/>
|
||||
Restrict units to coalition
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>Setup time: </label>
|
||||
|
||||
<div class="ol-group">
|
||||
<div id="setup-time" class="ol-text-input">
|
||||
<input type="number" min="-99999" max="99999" step="1" value="10">
|
||||
</div>
|
||||
<label>minutes</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>Available eras: </label>
|
||||
|
||||
<div id="command-mode-era-options" class="ol-select">
|
||||
<div class="ol-select-value">Select eras</div>
|
||||
<div class="ol-select-options">
|
||||
<!-- This is where all the available era buttons will be shown-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<h4>Spawn points</h4>
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>Blue spawn points: </label>
|
||||
<div id="blue-spawn-points" class="ol-text-input">
|
||||
<input type="number" min="0" max="999999" step="1" value="1000">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>Red spawn points: </label>
|
||||
<div id="red-spawn-points" class="ol-text-input">
|
||||
<input type="number" min="0" max="999999" step="1" value="1000">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-footer ol-group">
|
||||
<button class="ol-button-apply" data-on-click="applycommandModeOptions">Apply</button>
|
||||
<button class="ol-button-close" data-on-click="closeDialog">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
48
client/views/other/dialogs/customformation.ejs
Normal file
@@ -0,0 +1,48 @@
|
||||
<div id="custom-formation-dialog" class="ol-panel ol-dialog olympus-dialog-close hide" oncontextmenu="return false;">
|
||||
<div class="ol-dialog-close" data-on-click="closeDialog"></div>
|
||||
|
||||
<div class="ol-dialog-header">
|
||||
<h3 id="unit-name">Custom formation</h3>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-content">
|
||||
<div class="formation-position-clock">
|
||||
<div class="clock-hand" style="top: 0px; left: 50px;"><input type="radio" id="formation-1" name="formation-position" value="1"></div>
|
||||
<div class="clock-hand" style="top: 14px; left: 14px;"><input type="radio" id="formation-2" name="formation-position" value="2"></div>
|
||||
<div class="clock-hand" style="top: 50px; left: 0px; "><input type="radio" id="formation-3" name="formation-position" value="3"></div>
|
||||
<div class="clock-hand" style="top: 86px; left: 14px;"><input type="radio" id="formation-4" name="formation-position" value="4"></div>
|
||||
<div class="clock-hand" style="top: 100px; left: 50px;"><input type="radio" id="formation-5" name="formation-position" value="5"></div>
|
||||
<div class="clock-hand" style="top: 86px; left: 86px;"><input type="radio" id="formation-6" name="formation-position" value="6" checked></div>
|
||||
<div class="clock-hand" style="top: 50px; left: 100px"><input type="radio" id="formation-7" name="formation-position" value="7"></div>
|
||||
<div class="clock-hand" style="top: 14px; left: 86px;"><input type="radio" id="formation-8" name="formation-position" value="8"></div>
|
||||
<div style="top: 50px; left: 50px;" id="reference-system"></div>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>Distance: </label>
|
||||
|
||||
<div class="ol-group">
|
||||
<div id="distance" class="ol-text-input">
|
||||
<input type="number" min="0" max="999999" step="1" value="150">
|
||||
</div>
|
||||
<label>ft</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-group">
|
||||
<label>Up-down: </label>
|
||||
|
||||
<div class="ol-group">
|
||||
<div id="up-down" class="ol-text-input">
|
||||
<input type="number" min="-99999" max="99999" step="1" value="30">
|
||||
</div>
|
||||
<label>ft</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-footer ol-group">
|
||||
<button class="ol-button-apply" data-on-click="applyCustomFormation">Apply</button>
|
||||
<button class="ol-button-close" data-on-click="closeDialog">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
29
client/views/other/dialogs/importexport.ejs
Normal file
@@ -0,0 +1,29 @@
|
||||
<div id="<%= dialogId %>" class="ol-panel ol-dialog file-import-export hide" oncontextmenu="return false;">
|
||||
<div class="ol-dialog-header">
|
||||
<h3><%= title %></h3>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-content">
|
||||
<p><%= textContent %></p>
|
||||
|
||||
<% if (showFilenameInput) { %>
|
||||
<div class="export-filename-container">
|
||||
<label>Filename:</label>
|
||||
<input id="export-filename">
|
||||
<img src="resources/theme/images/icons/keyboard-solid.svg">
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<table class="categories-coalitions">
|
||||
<thead>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-footer ol-group">
|
||||
<button class="start-transfer"><%= submitButtonText %></button>
|
||||
<button data-on-click="closeDialog">Close</button>
|
||||
</div>
|
||||
</div>
|
||||
21
client/views/other/dialogs/slowdelete.ejs
Normal file
@@ -0,0 +1,21 @@
|
||||
<div id="slow-delete-dialog" class="ol-panel ol-dialog hide" oncontextmenu="return false;">
|
||||
<div class="ol-dialog-header">
|
||||
<h3>Confirm deletion method</h3>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-content">
|
||||
<p>You are trying to delete a large amount of units (<span class="deletion-count"></span>), which can cause the server to lag for players.</p>
|
||||
<p>You may:
|
||||
<ul>
|
||||
<li>delete in batches (less lag but Olympus cannot process any additional orders until<br /> all units have been deleted);</li>
|
||||
<li>delete immediately (you can continue to give Olympus orders but players may<br />experience lag while this happens);</li>
|
||||
<li>cancel this instruction.</li>
|
||||
</ul></p>
|
||||
</div>
|
||||
|
||||
<div class="ol-dialog-footer ol-group">
|
||||
<button data-action="delete-slow">Delete in batches (~<span class="deletion-time"></span>s)</button>
|
||||
<button data-action="delete-immediate">Delete immediately</button>
|
||||
<button data-action="delete-cancel">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
51
client/views/other/dialogs/splash.ejs
Normal file
@@ -0,0 +1,51 @@
|
||||
<div id="splash-screen" class="ol-dialog" oncontextmenu="return false;">
|
||||
<div id="splash-content" class="ol-dialog-content">
|
||||
<div id="app-summary">
|
||||
<h2>DCS Olympus</h2>
|
||||
<h4>Dynamic Unit Command</h4>
|
||||
<div class="app-version">Version <span class="app-version-number">v0.4.9-alpha-rc1</span></div>
|
||||
<div class="app-version">Latest version <span id="latest-version" class="app-version-number"></span></div>
|
||||
</div>
|
||||
|
||||
<form id="authentication-form">
|
||||
<div><h5>Name</h5> <input type="text" id="username" name="username" required autocomplete="username" placeholder="Enter name..."></div>
|
||||
<div><h5>Server password</h5> <input type="password" id="password" name="password" required autocomplete="current-password" placeholder="Enter server password..."></div>
|
||||
<button type="submit" id="connection-button" class="ol-button-apply">Connect</button>
|
||||
</form>
|
||||
|
||||
<h5 id="login-status"><br></h5>
|
||||
|
||||
<div id="legal-stuff" class="ol-scrollable">
|
||||
<h5>DISCLAIMER</h5>
|
||||
<p>
|
||||
Copyright (C) 2023 Veltro & Gang
|
||||
</p>
|
||||
<p>
|
||||
DCS Olympus (the "MATERIAL" or "Software") is provided completely free
|
||||
to users subject to the it under both the terms of version 3 of the GNU
|
||||
General Public License as published by the Free Software Foundation, and
|
||||
the additional terms set out below; except where such terms conflict with this
|
||||
disclaimer, in which case, the terms of this disclaimer shall prevail.
|
||||
</p>
|
||||
<p>
|
||||
The authors and/or copyright holders of the Software have not received any
|
||||
financial benefit in connection with the Software. In any event, the
|
||||
Software is provided “as is”, without warranty of any kind, express or
|
||||
implied, including but not limited to the warranties of merchantability,
|
||||
fitness for a particular purpose and non-infringement. In no event shall
|
||||
the authors and/or copyright holders be liable for any claim, damages or
|
||||
other liability, whether in an action of contract, tort or otherwise,
|
||||
arising from, out of or in connection with the Software or the use or o
|
||||
ther dealings in the Software.
|
||||
</p>
|
||||
<p>
|
||||
Any party making use of the Software in any manner agrees to be
|
||||
bound by the terms set out in this disclaimer, version 3 of the GNU
|
||||
General Public Licence, and the Additional Terms below.
|
||||
</p>
|
||||
<p>
|
||||
THIS MATERIAL IS NOT MADE OR SUPPORTED BY EAGLE DYNAMICS SA.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -112,13 +112,12 @@
|
||||
<div id="delete-options" class="ol-select">
|
||||
<div class="ol-select-value ol-select-warning">
|
||||
Delete unit
|
||||
<img src="/resources/theme/images/icons/chevron-down.svg" inject-svg />
|
||||
</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 class="hr"><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": "secondary" }' title="Small explosion with debries"><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>
|
||||
|
||||
@@ -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.8-alpha</div>
|
||||
<div class="accent-green app-version-number">version v0.4.9-alpha-rc1</div>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://discord.gg/wWXyVVBZT7" target="_blank">Discord</a>
|
||||
@@ -14,6 +14,9 @@
|
||||
<div>
|
||||
<a href="https://github.com/Pax1601/DCSOlympus" target="_blank">Github</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="https://github.com/Pax1601/DCSOlympus/wiki/User-Guide" target="_blank">User guide</a>
|
||||
</div>
|
||||
<div data-on-click="exportToFile">
|
||||
<button>Export to file</button>
|
||||
</div>
|
||||
@@ -28,14 +31,14 @@
|
||||
|
||||
<div class="ol-group">
|
||||
<div id="map-type" class="ol-select">
|
||||
<div class="ol-select-value"><img src="resources/theme/images/icons/map-source.svg" inject-svg><span class="ol-select-value-text">ArcGIS Satellite</span></div>
|
||||
<div class="ol-select-value"><img src="resources/theme/images/icons/map-source.svg" inject-svg /><span class="ol-select-value-text">ArcGIS Satellite</span></div>
|
||||
<div class="ol-select-options">
|
||||
<!-- Here the available map sources will be listed-->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="map-visibility-options" class="ol-select">
|
||||
<div class="ol-select-value"><img src="/resources/theme/images/icons/gears-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>
|
||||
@@ -43,25 +46,28 @@
|
||||
</div>
|
||||
</nav>
|
||||
<nav class="ol-panel" oncontextmenu="return false;">
|
||||
<img src="resources/theme/images/icons/eye-solid.svg" inject-svg>
|
||||
<div id="view-label" class="ol-panel-tab">
|
||||
<img src="resources/theme/images/icons/eye-solid.svg" inject-svg />
|
||||
<span>View</span>
|
||||
</div>
|
||||
<div id="unit-visibility-control" class="ol-group ol-navbar-buttons-group">
|
||||
<!-- Here the available visibility controls will be listed -->
|
||||
</div>
|
||||
|
||||
<div id="coalition-visibility-control" class="ol-group ol-group-button-toggle">
|
||||
<div id="coalition-visibility-control" class="ol-group ol-navbar-buttons-group">
|
||||
<div>
|
||||
<button id="coalition-visibility-control-blue" data-on-click="toggleCoalitionVisibility"
|
||||
data-on-click-params='{ "coalition": "blue" }'><span class="accent-bluefor">BLUEFOR</span></button>
|
||||
data-on-click-params='{ "coalition": "blue" }'><img src="/resources/theme/images/buttons/visibility/shield.svg" class="fill-coalition" data-coalition="blue" inject-svg /></button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button id="coalition-visibility-control-red" data-on-click="toggleCoalitionVisibility"
|
||||
data-on-click-params='{ "coalition": "red" }'><span class="accent-redfor">REDFOR</span></button>
|
||||
data-on-click-params='{ "coalition": "red" }'><img src="/resources/theme/images/buttons/visibility/shield.svg" class="fill-coalition" data-coalition="red" inject-svg /></button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button id="coalition-visibility-control-neutral" data-on-click="toggleCoalitionVisibility"
|
||||
data-on-click-params='{ "coalition": "neutral" }'><span class="accent-neutral">NEUTRAL</span></button>
|
||||
data-on-click-params='{ "coalition": "neutral" }'><img src="/resources/theme/images/buttons/visibility/shield.svg" class="fill-coalition" data-coalition="neutral" inject-svg /></button>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
@@ -1,6 +1,6 @@
|
||||
#define nwjsFolder "..\..\nwjs\"
|
||||
#define nodejsFolder "..\..\node\"
|
||||
#define version "v0.4.8-alpha"
|
||||
#define version "v0.4.9-alpha-rc1"
|
||||
|
||||
[Setup]
|
||||
AppName=DCS Olympus
|
||||
@@ -44,12 +44,12 @@ Source: "..\img\olympus.ico"; DestDir: "{app}\Mods\Services\Olympus\img"; Flags:
|
||||
Source: "..\img\olympus_server.ico"; DestDir: "{app}\Mods\Services\Olympus\img"; Flags: ignoreversion;
|
||||
Source: "..\img\olympus_configurator.ico"; DestDir: "{app}\Mods\Services\Olympus\img"; Flags: ignoreversion;
|
||||
Source: "..\img\configurator_logo.png"; DestDir: "{app}\Mods\Services\Olympus\img"; Flags: ignoreversion;
|
||||
Source: "{#nwjsFolder}\*.*"; DestDir: "{app}\Mods\Services\Olympus\client\bin\nw"; Flags: ignoreversion recursesubdirs; Check: CheckLocalInstall
|
||||
Source: "{#nodejsFolder}\*.*"; DestDir: "{app}\Mods\Services\Olympus\client\bin\node"; Flags: ignoreversion recursesubdirs; Check: CheckServerInstall
|
||||
Source: "..\scripts\python\configurator\dist\configurator.exe"; DestDir: "{app}\Mods\Services\Olympus"; Flags: ignoreversion; Check: CheckServerInstall
|
||||
Source: "{#nwjsFolder}\*.*"; DestDir: "{app}\Mods\Services\Olympus\client"; Flags: ignoreversion recursesubdirs; Check: CheckLocalInstall
|
||||
Source: "{#nodejsFolder}\*.*"; DestDir: "{app}\Mods\Services\Olympus\client"; Flags: ignoreversion recursesubdirs; Check: CheckServerInstall
|
||||
Source: "..\scripts\python\configurator\dist\configurator.exe"; DestDir: "{app}\Mods\Services\Olympus"; Flags: ignoreversion;
|
||||
|
||||
[Run]
|
||||
Filename: "{app}\Mods\Services\Olympus\configurator.exe"; Parameters: -a {code:GetAddress} -c {code:GetClientPort} -b {code:GetBackendPort} -p {code:GetPassword} -bp {code:GetBluePassword} -rp {code:GetRedPassword}
|
||||
Filename: "{app}\Mods\Services\Olympus\configurator.exe"; Parameters: -a {code:GetAddress} -c {code:GetClientPort} -b {code:GetBackendPort} -p {code:GetPassword} -bp {code:GetBluePassword} -rp {code:GetRedPassword}; Check: CheckCallConfigurator
|
||||
|
||||
[Registry]
|
||||
Root: HKCU; Subkey: "Environment"; ValueType: string; ValueName: "DCSOLYMPUS_PATH"; ValueData: "{app}\Mods\Services\Olympus"; Flags: preservestringtype
|
||||
@@ -60,8 +60,8 @@ Root: HKCU; Subkey: "Environment"; ValueType: expandsz; ValueName: "Path"; Value
|
||||
ChangesEnvironment=yes
|
||||
|
||||
[Icons]
|
||||
Name: "{userdesktop}\DCS Olympus Client"; Filename: "{app}\Mods\Services\Olympus\client\bin\nw\nw.exe"; Tasks: desktopicon; IconFilename: "{app}\Mods\Services\Olympus\img\olympus.ico"; Check: CheckLocalInstall
|
||||
Name: "{userdesktop}\DCS Olympus Server"; Filename: "{app}\Mods\Services\Olympus\client\bin\node\node.exe"; Tasks: desktopicon; IconFilename: "{app}\Mods\Services\Olympus\img\olympus_server.ico"; WorkingDir: "{app}\Mods\Services\Olympus\client"; Parameters: ".\bin\www"; Check: CheckServerInstall
|
||||
Name: "{userdesktop}\DCS Olympus Client"; Filename: "{app}\Mods\Services\Olympus\client\nw.exe"; Tasks: desktopicon; IconFilename: "{app}\Mods\Services\Olympus\img\olympus.ico"; Check: CheckLocalInstall
|
||||
Name: "{userdesktop}\DCS Olympus Server"; Filename: "{app}\Mods\Services\Olympus\client\node.exe"; Tasks: desktopicon; IconFilename: "{app}\Mods\Services\Olympus\img\olympus_server.ico"; Parameters: ".\bin\www"; Check: CheckServerInstall
|
||||
Name: "{userdesktop}\DCS Olympus Configurator"; Filename: "{app}\Mods\Services\Olympus\configurator.exe"; Tasks: desktopicon; IconFilename: "{app}\Mods\Services\Olympus\img\olympus_configurator.ico"; Check: CheckServerInstall
|
||||
|
||||
[Code]
|
||||
@@ -87,19 +87,21 @@ var
|
||||
lblLocalInstallInstructions: TNewStaticText;
|
||||
lblServerInstall: TLabel;
|
||||
lblServerInstallInstructions: TNewStaticText;
|
||||
lblKeepOld: TLabel;
|
||||
lblClientPort: TLabel;
|
||||
lblBackendPort: TLabel;
|
||||
lblPassword: TLabel;
|
||||
lblBluePassword: TLabel;
|
||||
lblRedPassword: TLabel;
|
||||
txtLocalInstall: TNewRadioButton;
|
||||
txtServerInstall: TNewRadioButton;
|
||||
radioLocalInstall: TNewRadioButton;
|
||||
radioServerInstall: TNewRadioButton;
|
||||
checkKeepOld: TNewCheckBox;
|
||||
txtClientPort: TEdit;
|
||||
txtBackendPort: TEdit;
|
||||
txtPassword: TPasswordEdit;
|
||||
txtBluePassword: TPasswordEdit;
|
||||
txtRedPassword: TPasswordEdit;
|
||||
AddressPage: Integer;
|
||||
InstallationTypePage: Integer;
|
||||
PasswordPage: Integer;
|
||||
lblPasswordInstructions: TNewStaticText;
|
||||
|
||||
@@ -136,14 +138,14 @@ procedure frmAddress_CancelButtonClick(Page: TWizardPage; var Cancel, Confirm: B
|
||||
begin
|
||||
end;
|
||||
|
||||
function frmAddress_CreatePage(PreviousPageId: Integer): Integer;
|
||||
function frmInstallationType_CreatePage(PreviousPageId: Integer): Integer;
|
||||
var
|
||||
Page: TWizardPage;
|
||||
begin
|
||||
Page := CreateCustomPage(
|
||||
PreviousPageId,
|
||||
'DCS Olympus configuration',
|
||||
'Setup DCS Olympus connectivity'
|
||||
'Select installation type'
|
||||
);
|
||||
|
||||
{ lblLocalInstall }
|
||||
@@ -172,9 +174,9 @@ begin
|
||||
Caption := 'Select this to install DCS Olympus locally. DCS Olympus will not be reachable by external clients (i.e. browsers running on different PCs)';
|
||||
end;
|
||||
|
||||
{ txtLocalInstall }
|
||||
txtLocalInstall := TNewRadioButton.Create(Page);
|
||||
with txtLocalInstall do
|
||||
{ radioLocalInstall }
|
||||
radioLocalInstall := TNewRadioButton.Create(Page);
|
||||
with radioLocalInstall do
|
||||
begin
|
||||
Parent := Page.Surface;
|
||||
Left := ScaleX(10);
|
||||
@@ -211,9 +213,9 @@ begin
|
||||
Caption := 'Select this to install DCS Olympus on a dedicated server. DCS Olympus will be reachable by external clients. NOTE: to enable external connections, TCP port forwarding must be enabled on the selected ports.';
|
||||
end;
|
||||
|
||||
{ txtServerInstall }
|
||||
txtServerInstall := TNewRadioButton.Create(Page);
|
||||
with txtServerInstall do
|
||||
{ radioServerInstall }
|
||||
radioServerInstall := TNewRadioButton.Create(Page);
|
||||
with radioServerInstall do
|
||||
begin
|
||||
Parent := Page.Surface;
|
||||
Left := ScaleX(10);
|
||||
@@ -223,58 +225,6 @@ begin
|
||||
TabOrder := 1;
|
||||
end;
|
||||
|
||||
{ lblClientPort }
|
||||
lblClientPort := TLabel.Create(Page);
|
||||
with lblClientPort do
|
||||
begin
|
||||
Parent := Page.Surface;
|
||||
Left := ScaleX(24);
|
||||
Top := ScaleY(168);
|
||||
Width := ScaleX(46);
|
||||
Height := ScaleY(13);
|
||||
Caption := 'Webserver port';
|
||||
end;
|
||||
|
||||
{ txtClientPort }
|
||||
txtClientPort := TEdit.Create(Page);
|
||||
with txtClientPort do
|
||||
begin
|
||||
Parent := Page.Surface;
|
||||
Left := ScaleX(180);
|
||||
Top := ScaleY(165);
|
||||
Width := ScaleX(185);
|
||||
Height := ScaleY(21);
|
||||
Text := '3000';
|
||||
OnKeyPress := @AcceptNumbersOnlyKeyPress;
|
||||
TabOrder := 3;
|
||||
end;
|
||||
|
||||
{ lblBackendPort }
|
||||
lblBackendPort := TLabel.Create(Page);
|
||||
with lblBackendPort do
|
||||
begin
|
||||
Parent := Page.Surface;
|
||||
Left := ScaleX(24);
|
||||
Top := ScaleY(198);
|
||||
Width := ScaleX(46);
|
||||
Height := ScaleY(13);
|
||||
Caption := 'Backend port';
|
||||
end;
|
||||
|
||||
{ txtBackendPort }
|
||||
txtBackendPort := TEdit.Create(Page);
|
||||
with txtBackendPort do
|
||||
begin
|
||||
Parent := Page.Surface;
|
||||
Left := ScaleX(180);
|
||||
Top := ScaleY(195);
|
||||
Width := ScaleX(185);
|
||||
Height := ScaleY(21);
|
||||
Text := '3001';
|
||||
OnKeyPress := @AcceptNumbersOnlyKeyPress;
|
||||
TabOrder := 4;
|
||||
end;
|
||||
|
||||
with Page do
|
||||
begin
|
||||
OnActivate := @frmAddress_Activate;
|
||||
@@ -289,6 +239,8 @@ end;
|
||||
|
||||
procedure frmPassword_Activate(Page: TWizardPage);
|
||||
begin
|
||||
checkKeepOld.Enabled := FileExists(ExpandConstant('{app}\Mods\Services\Olympus\olympus.json'));
|
||||
checkKeepOld.Checked := FileExists(ExpandConstant('{app}\Mods\Services\Olympus\olympus.json'));
|
||||
end;
|
||||
|
||||
function frmPassword_ShouldSkipPage(Page: TWizardPage): Boolean;
|
||||
@@ -303,11 +255,11 @@ end;
|
||||
|
||||
function frmPassword_NextButtonClick(Page: TWizardPage): Boolean;
|
||||
begin
|
||||
if (Trim(txtPassword.Text) <> '') and (Trim(txtBluePassword.Text) <> '') and (Trim(txtRedPassword.Text) <> '') then begin
|
||||
if checkKeepOld.Checked or ((Trim(txtClientPort.Text) <> '') and (Trim(txtBackendPort.Text) <> '') and (Trim(txtPassword.Text) <> '') and (Trim(txtBluePassword.Text) <> '') and (Trim(txtRedPassword.Text) <> '')) then begin
|
||||
Result := True;
|
||||
end else
|
||||
begin
|
||||
MsgBox('All password fields must be filled to proceed.', mbInformation, MB_OK);
|
||||
MsgBox('Either keep the configuration from the previous installation (if present) or fill all the fields to continue.', mbInformation, MB_OK);
|
||||
Result := False;
|
||||
end;
|
||||
end;
|
||||
@@ -316,6 +268,15 @@ procedure frmPassword_CancelButtonClick(Page: TWizardPage; var Cancel, Confirm:
|
||||
begin
|
||||
end;
|
||||
|
||||
procedure checkKeepOldOnChange(Sender: TObject);
|
||||
begin
|
||||
txtPassword.Enabled := not checkKeepOld.Checked;
|
||||
txtBluePassword.Enabled := not checkKeepOld.Checked;
|
||||
txtRedPassword.Enabled := not checkKeepOld.Checked;
|
||||
txtBackendPort.Enabled := not checkKeepOld.Checked;
|
||||
txtClientPort.Enabled := not checkKeepOld.Checked;
|
||||
end;
|
||||
|
||||
function frmPassword_CreatePage(PreviousPageId: Integer): Integer;
|
||||
var
|
||||
Page: TWizardPage;
|
||||
@@ -323,16 +284,40 @@ begin
|
||||
Page := CreateCustomPage(
|
||||
PreviousPageId,
|
||||
'DCS Olympus passwords',
|
||||
'Set DCS Olympus Admin and Commander passwords'
|
||||
'Set DCS Olympus ports and passwords'
|
||||
);
|
||||
|
||||
{ lblKeepOld }
|
||||
lblKeepOld := TLabel.Create(Page);
|
||||
with lblKeepOld do
|
||||
begin
|
||||
Parent := Page.Surface;
|
||||
Left := ScaleX(54);
|
||||
Top := ScaleY(0);
|
||||
Width := ScaleX(46);
|
||||
Height := ScaleY(13);
|
||||
Caption := 'Keep configuration from previous installation';
|
||||
end;
|
||||
|
||||
{ checkKeepOld }
|
||||
checkKeepOld := TNewCheckBox.Create(Page);
|
||||
with checkKeepOld do
|
||||
begin
|
||||
Parent := Page.Surface;
|
||||
Left := ScaleX(24);
|
||||
Top := ScaleY(0);
|
||||
Width := ScaleX(46);
|
||||
Height := ScaleY(13);
|
||||
OnClick := @checkKeepOldOnChange;
|
||||
end;
|
||||
|
||||
{ lblPassword }
|
||||
lblPassword := TLabel.Create(Page);
|
||||
with lblPassword do
|
||||
begin
|
||||
Parent := Page.Surface;
|
||||
Left := ScaleX(24);
|
||||
Top := ScaleY(28);
|
||||
Top := ScaleY(38);
|
||||
Width := ScaleX(46);
|
||||
Height := ScaleY(13);
|
||||
Caption := 'Game Master password';
|
||||
@@ -344,10 +329,10 @@ begin
|
||||
begin
|
||||
Parent := Page.Surface;
|
||||
Left := ScaleX(180);
|
||||
Top := ScaleY(25);
|
||||
Top := ScaleY(35);
|
||||
Width := ScaleX(185);
|
||||
Height := ScaleY(21);
|
||||
TabOrder := 2;
|
||||
TabOrder := 1;
|
||||
end;
|
||||
|
||||
{ lblBluePassword }
|
||||
@@ -356,7 +341,7 @@ begin
|
||||
begin
|
||||
Parent := Page.Surface;
|
||||
Left := ScaleX(24);
|
||||
Top := ScaleY(58);
|
||||
Top := ScaleY(66);
|
||||
Width := ScaleX(46);
|
||||
Height := ScaleY(13);
|
||||
Caption := 'Blue Commander password';
|
||||
@@ -368,7 +353,7 @@ begin
|
||||
begin
|
||||
Parent := Page.Surface;
|
||||
Left := ScaleX(180);
|
||||
Top := ScaleY(55);
|
||||
Top := ScaleY(63);
|
||||
Width := ScaleX(185);
|
||||
Height := ScaleY(21);
|
||||
TabOrder := 2;
|
||||
@@ -380,7 +365,7 @@ begin
|
||||
begin
|
||||
Parent := Page.Surface;
|
||||
Left := ScaleX(24);
|
||||
Top := ScaleY(88);
|
||||
Top := ScaleY(94);
|
||||
Width := ScaleX(46);
|
||||
Height := ScaleY(13);
|
||||
Caption := 'Red Commander password';
|
||||
@@ -392,24 +377,76 @@ begin
|
||||
begin
|
||||
Parent := Page.Surface;
|
||||
Left := ScaleX(180);
|
||||
Top := ScaleY(85);
|
||||
Top := ScaleY(91);
|
||||
Width := ScaleX(185);
|
||||
Height := ScaleY(21);
|
||||
TabOrder := 2;
|
||||
TabOrder := 3;
|
||||
end;
|
||||
|
||||
|
||||
{ lblClientPort }
|
||||
lblClientPort := TLabel.Create(Page);
|
||||
with lblClientPort do
|
||||
begin
|
||||
Parent := Page.Surface;
|
||||
Left := ScaleX(24);
|
||||
Top := ScaleY(122);
|
||||
Width := ScaleX(46);
|
||||
Height := ScaleY(13);
|
||||
Caption := 'Webserver port';
|
||||
end;
|
||||
|
||||
{ txtClientPort }
|
||||
txtClientPort := TEdit.Create(Page);
|
||||
with txtClientPort do
|
||||
begin
|
||||
Parent := Page.Surface;
|
||||
Left := ScaleX(180);
|
||||
Top := ScaleY(119);
|
||||
Width := ScaleX(185);
|
||||
Height := ScaleY(21);
|
||||
Text := '3000';
|
||||
OnKeyPress := @AcceptNumbersOnlyKeyPress;
|
||||
TabOrder := 4;
|
||||
end;
|
||||
|
||||
{ lblBackendPort }
|
||||
lblBackendPort := TLabel.Create(Page);
|
||||
with lblBackendPort do
|
||||
begin
|
||||
Parent := Page.Surface;
|
||||
Left := ScaleX(24);
|
||||
Top := ScaleY(149);
|
||||
Width := ScaleX(46);
|
||||
Height := ScaleY(13);
|
||||
Caption := 'Backend port';
|
||||
end;
|
||||
|
||||
{ txtBackendPort }
|
||||
txtBackendPort := TEdit.Create(Page);
|
||||
with txtBackendPort do
|
||||
begin
|
||||
Parent := Page.Surface;
|
||||
Left := ScaleX(180);
|
||||
Top := ScaleY(147);
|
||||
Width := ScaleX(185);
|
||||
Height := ScaleY(21);
|
||||
Text := '3001';
|
||||
OnKeyPress := @AcceptNumbersOnlyKeyPress;
|
||||
TabOrder := 5;
|
||||
end;
|
||||
|
||||
{ lblPasswordInstructions }
|
||||
lblPasswordInstructions := TNewStaticText.Create(Page);
|
||||
with lblPasswordInstructions do
|
||||
begin
|
||||
Parent := Page.Surface;
|
||||
Left := ScaleX(24);
|
||||
Top := ScaleY(120);
|
||||
Top := ScaleY(180);
|
||||
Width := ScaleX(340);
|
||||
Height := ScaleY(13);
|
||||
WordWrap := True;
|
||||
Caption := 'Passwords can be changed in the future by editing the file "olympus.json". For more information, see the DCS Olympus Wiki';
|
||||
Caption := 'Passwords and ports can be changed in the future by using the DCS Olympus configurator. For more information, see the DCS Olympus Wiki.';
|
||||
end;
|
||||
|
||||
with Page do
|
||||
@@ -427,13 +464,13 @@ end;
|
||||
procedure InitializeWizard();
|
||||
begin
|
||||
{this page will come after welcome page}
|
||||
AddressPage := frmAddress_CreatePage(wpSelectDir);
|
||||
PasswordPage:= frmPassword_CreatePage(AddressPage);
|
||||
InstallationTypePage := frmInstallationType_CreatePage(wpSelectDir);
|
||||
PasswordPage := frmPassword_CreatePage(InstallationTypePage);
|
||||
end;
|
||||
|
||||
function CheckLocalInstall(): boolean;
|
||||
begin
|
||||
if txtLocalInstall.Checked then begin
|
||||
if radioLocalInstall.Checked then begin
|
||||
Result := True
|
||||
end else
|
||||
begin
|
||||
@@ -443,7 +480,17 @@ end;
|
||||
|
||||
function CheckServerInstall(): boolean;
|
||||
begin
|
||||
if txtLocalInstall.Checked then begin
|
||||
if radioLocalInstall.Checked then begin
|
||||
Result := False
|
||||
end else
|
||||
begin
|
||||
Result := True
|
||||
end
|
||||
end;
|
||||
|
||||
function CheckCallConfigurator(): boolean;
|
||||
begin
|
||||
if checkKeepOld.Checked then begin
|
||||
Result := False
|
||||
end else
|
||||
begin
|
||||
@@ -453,7 +500,7 @@ end;
|
||||
|
||||
function GetAddress(Value: string): string;
|
||||
begin
|
||||
if txtLocalInstall.Checked then begin
|
||||
if radioLocalInstall.Checked then begin
|
||||
Result := 'localhost'
|
||||
end else
|
||||
begin
|
||||
|
||||
@@ -15,7 +15,7 @@ declare_plugin(self_ID,
|
||||
shortName = "Olympus",
|
||||
fileMenuName = "Olympus",
|
||||
|
||||
version = "v0.4.8-alpha",
|
||||
version = "v0.4.9-alpha-rc1",
|
||||
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."),
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
local version = "v0.4.8-alpha"
|
||||
local version = "v0.4.9-alpha-rc1"
|
||||
|
||||
local debug = false -- True enables debug printing using DCS messages
|
||||
|
||||
@@ -21,6 +21,7 @@ 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
|
||||
Olympus.unitsInitialLife = {} -- getLife0 returns 0 for ships, so we need to store the initial life of units
|
||||
|
||||
Olympus.weaponIndex = 0 -- Counter used to spread the computational load of data retrievial from DCS
|
||||
Olympus.weaponStep = 50 -- Max number of weapons that get updated each cycle
|
||||
@@ -509,10 +510,11 @@ function Olympus.removeFire (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
|
||||
Olympus.randomDebrie(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)
|
||||
@@ -545,7 +547,7 @@ function Olympus.spawnUnits(spawnTable)
|
||||
local route = nil
|
||||
local category = nil
|
||||
|
||||
-- Generate the units table and rout as per DCS requirements
|
||||
-- Generate the units table and route as per DCS requirements
|
||||
if spawnTable.category == 'Aircraft' then
|
||||
unitsTable = Olympus.generateAirUnitsTable(spawnTable.units)
|
||||
route = Olympus.generateAirUnitsRoute(spawnTable)
|
||||
@@ -605,10 +607,10 @@ function Olympus.generateAirUnitsTable(units)
|
||||
|
||||
-- Define the loadout
|
||||
if payload == nil then
|
||||
if loadout and loadout ~= "" and Olympus.unitPayloads[unit.unitType] and Olympus.unitPayloads[unit.unitType][loadout] then
|
||||
payload = Olympus.unitPayloads[unit.unitType][loadout]
|
||||
if loadout ~= nil and loadout ~= "" and Olympus.unitPayloads[unit.unitType] and Olympus.unitPayloads[unit.unitType][loadout] then
|
||||
payload = { ["pylons"] = Olympus.unitPayloads[unit.unitType][loadout], ["fuel"] = 999999, ["flare"] = 60, ["ammo_type"] = 1, ["chaff"] = 60, ["gun"] = 100 }
|
||||
else
|
||||
payload = {}
|
||||
payload = { ["pylons"] = {}, ["fuel"] = 999999, ["flare"] = 60, ["ammo_type"] = 1, ["chaff"] = 60, ["gun"] = 100 }
|
||||
end
|
||||
end
|
||||
|
||||
@@ -622,7 +624,7 @@ function Olympus.generateAirUnitsTable(units)
|
||||
["alt"] = unit.alt,
|
||||
["alt_type"] = "BARO",
|
||||
["skill"] = "Excellent",
|
||||
["payload"] = { ["pylons"] = payload, ["fuel"] = 999999, ["flare"] = 60, ["ammo_type"] = 1, ["chaff"] = 60, ["gun"] = 100, },
|
||||
["payload"] = payload,
|
||||
["heading"] = unit.heading,
|
||||
["callsign"] = { [1] = 1, [2] = 1, [3] = 1, ["name"] = "Olympus" .. Olympus.unitCounter.. "-" .. #unitsTable + 1 },
|
||||
["name"] = "Olympus-" .. Olympus.unitCounter .. "-" .. #unitsTable + 1,
|
||||
@@ -913,7 +915,7 @@ function Olympus.setTask(groupName, taskOptions)
|
||||
end
|
||||
end
|
||||
|
||||
-- Reset the dask of a group
|
||||
-- Reset the task of a group
|
||||
function Olympus.resetTask(groupName)
|
||||
Olympus.debug("Olympus.resetTask " .. groupName, 2)
|
||||
local group = Group.getByName(groupName)
|
||||
@@ -992,7 +994,7 @@ function Olympus.setUnitsData(arg, time)
|
||||
local position = unit:getPosition()
|
||||
local heading = math.atan2( position.x.z, position.x.x )
|
||||
local velocity = unit:getVelocity();
|
||||
|
||||
|
||||
-- Fill the data table
|
||||
table["name"] = unit:getTypeName()
|
||||
table["coalitionID"] = unit:getCoalition()
|
||||
@@ -1004,6 +1006,16 @@ function Olympus.setUnitsData(arg, time)
|
||||
table["horizontalVelocity"] = math.sqrt(velocity.x * velocity.x + velocity.z * velocity.z)
|
||||
table["verticalVelocity"] = velocity.y
|
||||
table["heading"] = heading
|
||||
|
||||
-- Track angles are wrong because of weird reference systems, approximate it using latitude and longitude differences
|
||||
if Olympus.unitsData["units"] ~= nil and Olympus.unitsData["units"][ID] ~= nil and Olympus.unitsData["units"][ID]["position"] ~= nil and Olympus.unitsData["units"][ID]["position"]["lat"] ~= nil and Olympus.unitsData["units"][ID]["position"]["lng"] ~= nil then
|
||||
local latDifference = lat - Olympus.unitsData["units"][ID]["position"]["lat"]
|
||||
local lngDifference = lng - Olympus.unitsData["units"][ID]["position"]["lng"]
|
||||
table["track"] = math.atan2(lngDifference * math.cos(lat / 57.29577), latDifference)
|
||||
else
|
||||
table["track"] = math.atan2(velocity.z, velocity.x)
|
||||
end
|
||||
|
||||
table["isAlive"] = unit:isExist() and unit:isActive() and unit:getLife() >= 1
|
||||
|
||||
local group = unit:getGroup()
|
||||
@@ -1025,6 +1037,17 @@ function Olympus.setUnitsData(arg, time)
|
||||
end
|
||||
end
|
||||
|
||||
-- getLife0 does not seem to work for ships, so we need to keep a reference to the initial life of the unit
|
||||
if Olympus.unitsInitialLife[ID] == nil then
|
||||
Olympus.unitsInitialLife[ID] = unit:getLife()
|
||||
end
|
||||
|
||||
-- Get the initial life of the unit to compute the current health
|
||||
local initialLife = 1
|
||||
if Olympus.unitsInitialLife[ID] ~= nil then
|
||||
initialLife = Olympus.unitsInitialLife[ID]
|
||||
end
|
||||
|
||||
table["country"] = unit:getCountry()
|
||||
table["unitName"] = unit:getName()
|
||||
table["groupName"] = group:getName()
|
||||
@@ -1032,11 +1055,55 @@ 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["health"] = unit:getLife() / unit:getLife0() * 100
|
||||
table["health"] = unit:getLife() / initialLife * 100
|
||||
table["contacts"] = contacts
|
||||
|
||||
-- Update the database used for unit cloning
|
||||
local name = unit:getName()
|
||||
|
||||
-- If the unit is not in the clone database it means it was not spawned by Olympus. Let's try and recover it using mist
|
||||
if Olympus.cloneDatabase[name] == nil then
|
||||
if mist.DBs ~= nil and mist.DBs.unitsByName ~= nil and mist.DBs.unitsByName[name] ~= nil then
|
||||
-- Payloads can be copied from ME units only TODO: can we fix this?
|
||||
local payload = {}
|
||||
if mist.DBs.MEunitsByName[name] then
|
||||
payload = mist.getPayload(name)
|
||||
end
|
||||
|
||||
-- Create a mock spawn table to generate the database
|
||||
local unitsTable = nil
|
||||
local spawnTable = {}
|
||||
spawnTable.units = {
|
||||
[1] = {
|
||||
["unitType"] = table["name"],
|
||||
["lat"] = table["position"]["lat"],
|
||||
["lng"] = table["position"]["lng"],
|
||||
["alt"] = table["position"]["alt"],
|
||||
["payload"] = payload,
|
||||
["liveryID"] = mist.DBs.unitsByName[name]["livery_id"]
|
||||
}
|
||||
}
|
||||
|
||||
-- Generate the units table as per DCS requirements
|
||||
if table["category"] == 'Aircraft' then
|
||||
unitsTable = Olympus.generateAirUnitsTable(spawnTable.units)
|
||||
elseif table["category"] == 'Helicopter' then
|
||||
unitsTable = Olympus.generateAirUnitsTable(spawnTable.units)
|
||||
elseif table["category"] == 'GroundUnit' then
|
||||
unitsTable = Olympus.generateGroundUnitsTable(spawnTable.units)
|
||||
elseif table["category"] == 'NavyUnit' then
|
||||
unitsTable = Olympus.generateNavyUnitsTable(spawnTable.units)
|
||||
end
|
||||
|
||||
-- Save the units in the database, for cloning
|
||||
for idx, unitTable in pairs(unitsTable) do
|
||||
-- Force the name of the unit to be equal to the original name
|
||||
unitTable["name"] = name
|
||||
Olympus.addToDatabase(mist.utils.deepCopy(unitTable))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Update the database used for unit cloning
|
||||
if Olympus.cloneDatabase[name] ~= nil then
|
||||
Olympus.cloneDatabase[name]["ID"] = ID
|
||||
Olympus.cloneDatabase[name]["category"] = unit:getDesc().category
|
||||
@@ -1335,3 +1402,11 @@ Olympus.initializeUnits()
|
||||
|
||||
Olympus.notify("OlympusCommand script " .. version .. " loaded successfully", 2, true)
|
||||
|
||||
-- Load the current instance folder
|
||||
local lfs = require('lfs')
|
||||
|
||||
Olympus.instancePath = lfs.writedir().."Mods\\Services\\Olympus"
|
||||
|
||||
Olympus.notify("Starting DCS Olympus backend session in "..Olympus.instancePath, 2)
|
||||
Olympus.OlympusDLL.setInstancePath()
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
local version = 'v0.4.8-alpha'
|
||||
local version = 'v0.4.9-alpha-rc1'
|
||||
|
||||
Olympus = {}
|
||||
Olympus.OlympusDLL = nil
|
||||
|
||||
24
scripts/examples/setcoaltionScript.lua
Normal file
@@ -0,0 +1,24 @@
|
||||
function disableAutoCapture(airbaseName)
|
||||
trigger.action.outText("Olympus.disableAutoCapture " .. airbaseName, 2)
|
||||
local airbase = Airbase.getByName(airbaseName)
|
||||
if airbase then
|
||||
airbase:autoCapture(false)
|
||||
trigger.action.outText("Olympus.disableAutoCapture " .. airbaseName .. " completed successfully", 2)
|
||||
else
|
||||
trigger.action.outText("Olympus.disableAutoCapture failed", 2)
|
||||
end
|
||||
end
|
||||
|
||||
function setAirbaseCoalition(airbaseName, coalitionColor)
|
||||
trigger.action.outText("Olympus.setAirbaseCoalition trying to set " .. airbaseName .. " to " .. coalitionColor, 2)
|
||||
local airbase = Airbase.getByName(airbaseName)
|
||||
if airbase then
|
||||
disableAutoCapture(airbaseName)
|
||||
airbase:setCoalition(coalition.side[coalitionColor])
|
||||
trigger.action.outText("Olympus.setAirbaseCoalition " .. airbaseName .. " set to " .. coalitionColor .. " completed successfully", 5)
|
||||
else
|
||||
trigger.action.outText("Olympus.setAirbaseCoalition Airbase not found: " .. airbaseName, 5)
|
||||
end
|
||||
end
|
||||
|
||||
setAirbaseCoalition("Khasab", "RED")
|
||||
@@ -1,9 +1,6 @@
|
||||
# -*- mode: python ; coding: utf-8 -*-
|
||||
|
||||
|
||||
block_cipher = None
|
||||
|
||||
|
||||
a = Analysis(
|
||||
['configurator.py'],
|
||||
pathex=[],
|
||||
@@ -14,18 +11,14 @@ a = Analysis(
|
||||
hooksconfig={},
|
||||
runtime_hooks=[],
|
||||
excludes=[],
|
||||
win_no_prefer_redirects=False,
|
||||
win_private_assemblies=False,
|
||||
cipher=block_cipher,
|
||||
noarchive=False,
|
||||
)
|
||||
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
|
||||
pyz = PYZ(a.pure)
|
||||
|
||||
exe = EXE(
|
||||
pyz,
|
||||
a.scripts,
|
||||
a.binaries,
|
||||
a.zipfiles,
|
||||
a.datas,
|
||||
[],
|
||||
name='configurator',
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
#include "airunit.h"
|
||||
|
||||
#define AIRCRAFT_DEST_DIST_THR 2000 // Meters
|
||||
|
||||
class Aircraft : public AirUnit
|
||||
{
|
||||
public:
|
||||
@@ -11,6 +13,8 @@ public:
|
||||
virtual void changeSpeed(string change);
|
||||
virtual void changeAltitude(string change);
|
||||
|
||||
virtual double getDestinationReachedThreshold() { return AIRCRAFT_DEST_DIST_THR; }
|
||||
|
||||
protected:
|
||||
static json::value database;
|
||||
};
|
||||
@@ -17,6 +17,7 @@ public:
|
||||
|
||||
virtual void changeSpeed(string change) = 0;
|
||||
virtual void changeAltitude(string change) = 0;
|
||||
virtual double getDestinationReachedThreshold() { return AIR_DEST_DIST_THR; }
|
||||
|
||||
protected:
|
||||
virtual void AIloop();
|
||||
|
||||
@@ -124,10 +124,10 @@ public:
|
||||
taskOptions(taskOptions),
|
||||
category(category)
|
||||
{
|
||||
priority = CommandPriority::HIGH;
|
||||
priority = CommandPriority::MEDIUM;
|
||||
};
|
||||
virtual string getString();
|
||||
virtual unsigned int getLoad() { return 2; }
|
||||
virtual unsigned int getLoad() { return 5; }
|
||||
|
||||
private:
|
||||
const string groupName;
|
||||
@@ -173,7 +173,7 @@ public:
|
||||
priority = immediate? CommandPriority::IMMEDIATE: CommandPriority::LOW;
|
||||
};
|
||||
virtual string getString();
|
||||
virtual unsigned int getLoad() { return immediate? 1: 30; }
|
||||
virtual unsigned int getLoad() { return immediate? 5: 30; }
|
||||
|
||||
private:
|
||||
const string coalition;
|
||||
@@ -196,7 +196,7 @@ public:
|
||||
priority = immediate ? CommandPriority::IMMEDIATE : CommandPriority::LOW;
|
||||
};
|
||||
virtual string getString();
|
||||
virtual unsigned int getLoad() { return immediate ? 1 : 30; }
|
||||
virtual unsigned int getLoad() { return immediate ? 5 : 60; }
|
||||
|
||||
private:
|
||||
const string coalition;
|
||||
@@ -220,7 +220,7 @@ public:
|
||||
priority = immediate ? CommandPriority::IMMEDIATE : CommandPriority::LOW;
|
||||
};
|
||||
virtual string getString();
|
||||
virtual unsigned int getLoad() { return immediate ? 1 : 30; }
|
||||
virtual unsigned int getLoad() { return immediate ? 5 : 45; }
|
||||
|
||||
private:
|
||||
const string coalition;
|
||||
@@ -245,7 +245,7 @@ public:
|
||||
priority = immediate ? CommandPriority::IMMEDIATE : CommandPriority::LOW;
|
||||
};
|
||||
virtual string getString();
|
||||
virtual unsigned int getLoad() { return immediate ? 1 : 30; }
|
||||
virtual unsigned int getLoad() { return immediate ? 5 : 45; }
|
||||
|
||||
private:
|
||||
const string coalition;
|
||||
@@ -289,7 +289,7 @@ public:
|
||||
immediate = immediate;
|
||||
};
|
||||
virtual string getString();
|
||||
virtual unsigned int getLoad() { return immediate? 1: 5; }
|
||||
virtual unsigned int getLoad() { return immediate? 1: 30; }
|
||||
|
||||
private:
|
||||
const unsigned int ID;
|
||||
@@ -310,7 +310,7 @@ public:
|
||||
priority = CommandPriority::MEDIUM;
|
||||
};
|
||||
virtual string getString();
|
||||
virtual unsigned int getLoad() { return 1; }
|
||||
virtual unsigned int getLoad() { return 5; }
|
||||
|
||||
private:
|
||||
const string groupName;
|
||||
@@ -328,7 +328,7 @@ public:
|
||||
priority = CommandPriority::HIGH;
|
||||
};
|
||||
virtual string getString();
|
||||
virtual unsigned int getLoad() { return 1; }
|
||||
virtual unsigned int getLoad() { return 5; }
|
||||
|
||||
private:
|
||||
const string groupName;
|
||||
@@ -346,7 +346,7 @@ public:
|
||||
priority = CommandPriority::HIGH;
|
||||
};
|
||||
virtual string getString();
|
||||
virtual unsigned int getLoad() { return 1; }
|
||||
virtual unsigned int getLoad() { return 5; }
|
||||
|
||||
private:
|
||||
const string groupName;
|
||||
@@ -379,7 +379,7 @@ public:
|
||||
priority = CommandPriority::HIGH;
|
||||
};
|
||||
virtual string getString();
|
||||
virtual unsigned int getLoad() { return 1; }
|
||||
virtual unsigned int getLoad() { return 5; }
|
||||
|
||||
private:
|
||||
const string groupName;
|
||||
@@ -401,7 +401,7 @@ public:
|
||||
priority = CommandPriority::HIGH;
|
||||
};
|
||||
virtual string getString();
|
||||
virtual unsigned int getLoad() { return 1; }
|
||||
virtual unsigned int getLoad() { return 5; }
|
||||
|
||||
private:
|
||||
const string groupName;
|
||||
@@ -421,7 +421,7 @@ public:
|
||||
priority = CommandPriority::MEDIUM;
|
||||
};
|
||||
virtual string getString();
|
||||
virtual unsigned int getLoad() { return 4; }
|
||||
virtual unsigned int getLoad() { return 5; }
|
||||
|
||||
private:
|
||||
const Coords location;
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace DataIndex {
|
||||
horizontalVelocity,
|
||||
verticalVelocity,
|
||||
heading,
|
||||
track,
|
||||
isActiveTanker,
|
||||
isActiveAWACS,
|
||||
onOff,
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
#include "airunit.h"
|
||||
|
||||
#define HELICOPTER_DEST_DIST_THR 500 // Meters
|
||||
|
||||
class Helicopter : public AirUnit
|
||||
{
|
||||
public:
|
||||
@@ -11,6 +13,8 @@ public:
|
||||
virtual void changeSpeed(string change);
|
||||
virtual void changeAltitude(string change);
|
||||
|
||||
virtual double getDestinationReachedThreshold() { return HELICOPTER_DEST_DIST_THR; }
|
||||
|
||||
protected:
|
||||
static json::value database;
|
||||
};
|
||||
@@ -24,7 +24,7 @@ public:
|
||||
void setEras(vector<string> newEras) { eras = newEras; }
|
||||
void setCommandModeOptions(json::value newOptions);
|
||||
|
||||
int getFrameRate() { return frameRate; };
|
||||
int getFrameRate() { return static_cast<int>(round(frameRate)); };
|
||||
int getLoad();
|
||||
bool getRestrictSpawns() { return restrictSpawns; }
|
||||
bool getRestrictToCoalition() { return restrictToCoalition; }
|
||||
|
||||
@@ -80,6 +80,7 @@ public:
|
||||
virtual void setHorizontalVelocity(double newValue) { updateValue(horizontalVelocity, newValue, DataIndex::horizontalVelocity); }
|
||||
virtual void setVerticalVelocity(double newValue) { updateValue(verticalVelocity, newValue, DataIndex::verticalVelocity); }
|
||||
virtual void setHeading(double newValue) { updateValue(heading, newValue, DataIndex::heading); }
|
||||
virtual void setTrack(double newValue) { updateValue(track, newValue, DataIndex::track); }
|
||||
virtual void setIsActiveTanker(bool newValue);
|
||||
virtual void setIsActiveAWACS(bool newValue);
|
||||
virtual void setOnOff(bool newValue, bool force = false) { updateValue(onOff, newValue, DataIndex::onOff); };
|
||||
@@ -126,6 +127,7 @@ public:
|
||||
virtual double getHorizontalVelocity() { return horizontalVelocity; }
|
||||
virtual double getVerticalVelocity() { return verticalVelocity; }
|
||||
virtual double getHeading() { return heading; }
|
||||
virtual double getTrack() { return track; }
|
||||
virtual bool getIsActiveTanker() { return isActiveTanker; }
|
||||
virtual bool getIsActiveAWACS() { return isActiveAWACS; }
|
||||
virtual bool getOnOff() { return onOff; };
|
||||
@@ -146,7 +148,7 @@ public:
|
||||
virtual DataTypes::Radio getRadio() { return radio; }
|
||||
virtual DataTypes::GeneralSettings getGeneralSettings() { return generalSettings; }
|
||||
virtual vector<DataTypes::Ammo> getAmmo() { return ammo; }
|
||||
virtual vector<DataTypes::Contact> getTargets() { return contacts; }
|
||||
virtual vector<DataTypes::Contact> getContacts() { return contacts; }
|
||||
virtual list<Coords> getActivePath() { return activePath; }
|
||||
virtual bool getIsLeader() { return isLeader; }
|
||||
virtual unsigned char getOperateAs() { return operateAs; }
|
||||
@@ -174,6 +176,7 @@ protected:
|
||||
double horizontalVelocity = NULL;
|
||||
double verticalVelocity = NULL;
|
||||
double heading = NULL;
|
||||
double track = NULL;
|
||||
bool isActiveTanker = false;
|
||||
bool isActiveAWACS = false;
|
||||
bool onOff = true;
|
||||
@@ -217,7 +220,7 @@ protected:
|
||||
virtual void AIloop() = 0;
|
||||
|
||||
void appendString(stringstream& ss, const unsigned char& datumIndex, const string& datumValue) {
|
||||
const unsigned short size = datumValue.size();
|
||||
const unsigned short size = static_cast<unsigned short>(datumValue.size());
|
||||
ss.write((const char*)&datumIndex, sizeof(unsigned char));
|
||||
ss.write((const char*)&size, sizeof(unsigned short));
|
||||
ss << datumValue;
|
||||
@@ -242,7 +245,7 @@ protected:
|
||||
|
||||
template <typename T>
|
||||
void appendVector(stringstream& ss, const unsigned char& datumIndex, vector<T>& datumValue) {
|
||||
const unsigned short size = datumValue.size();
|
||||
const unsigned short size = static_cast<unsigned short>(datumValue.size());
|
||||
ss.write((const char*)&datumIndex, sizeof(unsigned char));
|
||||
ss.write((const char*)&size, sizeof(unsigned short));
|
||||
|
||||
@@ -252,7 +255,7 @@ protected:
|
||||
|
||||
template <typename T>
|
||||
void appendList(stringstream& ss, const unsigned char& datumIndex, list<T>& datumValue) {
|
||||
const unsigned short size = datumValue.size();
|
||||
const unsigned short size = static_cast<unsigned short>(datumValue.size());;
|
||||
ss.write((const char*)&datumIndex, sizeof(unsigned char));
|
||||
ss.write((const char*)&size, sizeof(unsigned short));
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ protected:
|
||||
|
||||
/********** Private methods **********/
|
||||
void appendString(stringstream& ss, const unsigned char& datumIndex, const string& datumValue) {
|
||||
const unsigned short size = datumValue.size();
|
||||
const unsigned short size = static_cast<unsigned short>(datumValue.size());
|
||||
ss.write((const char*)&datumIndex, sizeof(unsigned char));
|
||||
ss.write((const char*)&size, sizeof(unsigned short));
|
||||
ss << datumValue;
|
||||
|
||||
@@ -12,24 +12,18 @@ using namespace GeographicLib;
|
||||
extern Scheduler* scheduler;
|
||||
extern UnitsManager* unitsManager;
|
||||
json::value Aircraft::database = json::value();
|
||||
extern string instancePath;
|
||||
|
||||
void Aircraft::loadDatabase(string path) {
|
||||
char* buf = nullptr;
|
||||
size_t sz = 0;
|
||||
if (_dupenv_s(&buf, &sz, "DCSOLYMPUS_PATH") == 0 && buf != nullptr)
|
||||
{
|
||||
std::ifstream ifstream(string(buf) + path);
|
||||
std::stringstream ss;
|
||||
ss << ifstream.rdbuf();
|
||||
std::error_code errorCode;
|
||||
database = json::value::parse(ss.str(), errorCode);
|
||||
if (database.is_object())
|
||||
log("Aircrafts database loaded correctly");
|
||||
else
|
||||
log("Error reading Aircrafts database file");
|
||||
|
||||
free(buf);
|
||||
}
|
||||
std::ifstream ifstream(instancePath + path);
|
||||
std::stringstream ss;
|
||||
ss << ifstream.rdbuf();
|
||||
std::error_code errorCode;
|
||||
database = json::value::parse(ss.str(), errorCode);
|
||||
if (database.is_object())
|
||||
log("Aircrafts database loaded correctly from " + instancePath + path);
|
||||
else
|
||||
log("Error reading Aircrafts database file");
|
||||
}
|
||||
|
||||
/* Aircraft */
|
||||
@@ -40,7 +34,6 @@ Aircraft::Aircraft(json::value json, unsigned int ID) : AirUnit(json, ID)
|
||||
setCategory("Aircraft");
|
||||
setDesiredSpeed(knotsToMs(300));
|
||||
setDesiredAltitude(ftToM(20000));
|
||||
|
||||
};
|
||||
|
||||
void Aircraft::changeSpeed(string change)
|
||||
@@ -54,11 +47,6 @@ void Aircraft::changeSpeed(string change)
|
||||
|
||||
if (getDesiredSpeed() < knotsToMs(50))
|
||||
setDesiredSpeed(knotsToMs(50));
|
||||
|
||||
if (state == State::IDLE)
|
||||
resetTask();
|
||||
else
|
||||
goToDestination(); /* Send the command to reach the destination */
|
||||
}
|
||||
|
||||
void Aircraft::changeAltitude(string change)
|
||||
@@ -75,14 +63,9 @@ void Aircraft::changeAltitude(string change)
|
||||
if (getDesiredAltitude() > 5000)
|
||||
setDesiredAltitude(getDesiredAltitude() + ftToM(2500));
|
||||
else if (getDesiredAltitude() >= 0)
|
||||
setDesiredAltitude(getDesiredAltitude() + ftToM(500));
|
||||
setDesiredAltitude(getDesiredAltitude() + ftToM(500));
|
||||
}
|
||||
|
||||
if (getDesiredAltitude() < 0)
|
||||
setDesiredAltitude(0);
|
||||
|
||||
if (state == State::IDLE)
|
||||
resetTask();
|
||||
else
|
||||
goToDestination(); /* Send the command to reach the destination */
|
||||
}
|
||||
@@ -146,12 +146,15 @@ void AirUnit::setState(unsigned char newState)
|
||||
break;
|
||||
}
|
||||
|
||||
resetTask();
|
||||
setHasTask(false);
|
||||
resetTaskFailedCounter();
|
||||
|
||||
log(unitName + " setting state from " + to_string(state) + " to " + to_string(newState));
|
||||
state = newState;
|
||||
|
||||
triggerUpdate(DataIndex::state);
|
||||
|
||||
AIloop();
|
||||
}
|
||||
|
||||
void AirUnit::AIloop()
|
||||
@@ -218,7 +221,7 @@ void AirUnit::AIloop()
|
||||
goToDestination(enrouteTask);
|
||||
}
|
||||
else {
|
||||
if (isDestinationReached(AIR_DEST_DIST_THR)) {
|
||||
if (isDestinationReached(getDestinationReachedThreshold())) {
|
||||
if (updateActivePath(looping) && setActiveDestination())
|
||||
goToDestination(enrouteTask);
|
||||
else
|
||||
|
||||