Compare commits
128 Commits
v0.4.8-alp
...
v0.4.13-al
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0459d5c625 | ||
|
|
43d28ebe19 | ||
|
|
187b9be57a | ||
|
|
564a650403 | ||
|
|
0e403e8b74 | ||
|
|
840cdd9049 | ||
|
|
e5e7e9be14 | ||
|
|
e677968ba7 | ||
|
|
d24b955d52 | ||
|
|
48860ff514 | ||
|
|
0e78b7559b | ||
|
|
25edfc45e5 | ||
|
|
885825e5cc | ||
|
|
344413ae74 | ||
|
|
fa0643987b | ||
|
|
dcff462b32 | ||
|
|
fa04e5f8bb | ||
|
|
c8d5f9ce0e | ||
|
|
55642a89b1 | ||
|
|
f305aa3929 | ||
|
|
47ee88c339 | ||
|
|
022e041f68 | ||
|
|
0cc53890c1 | ||
|
|
e37f7a4977 | ||
|
|
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.
|
||||
|
||||
78
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,68 @@
|
||||

|
||||
|
||||
### 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:
|
||||
# Frequently Asked Questions
|
||||
### Can I join up and help out with the project? ###
|
||||
We are currently running towards first release in the very near future so we are not looking to add more people to the core team for the moment. However that does not mean we are not open to collaborations and help going forward, if you want to help for now we are committed to the free and open source model so feel free to check out the github, familiarize yourself with the project and maybe even start submitting pull requests for open issues.
|
||||
|
||||
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.
|
||||
Post-release we will be more interested in developing partnerships/collaborations with other teams/projects and potentially bringing in more team members, we will update this after release on how that will be managed!
|
||||
|
||||
### Can I be a beta/alpha-tester? ###
|
||||
With first public release planned for the very-near future we are fully committed to the final sprint, as such we will not be formally recruiting more people to test pre-release.
|
||||
|
||||
Post-release we will be eager to hear feedback of all forms and take in bug-reports, at this time after release we will begin considering bringing in more team members to test in development versions as we go.
|
||||
|
||||
### Do you have a roadmap? ###
|
||||
We do not have a roadmap no, we have a laundry list of things we are hoping to do.
|
||||
|
||||
These include but are not limited to:
|
||||
1) Enhancements to helicopter play
|
||||
2) More features around use of ground units
|
||||
3) More unique effects and behaviours
|
||||
4) ATC/AIC features
|
||||
5) Usability features like unit painters etc
|
||||
|
||||
However we cannot commit to specific features, feature release order, or timelines, please remember this isn't our job and we work on it in our free time because we love DCS
|
||||
|
||||
### Does Olympus support mods? ###
|
||||
Generally OIympus will not have any issues with other mods, however you may need to tell olympus about modded units in order to be able to dynamically spawn them etc
|
||||
|
||||
Keep in mind that any mods you do choose to spawn your players will need to have, some mod unit just appear as a su27 or leo2 etc. when a player is missing them, others can cause client crashes. So be smart about how you use them
|
||||
|
||||
### Is Olympus compatible with mission scripts? ###
|
||||
We have tried hard to keep Olympus from interfering with other scripts, we have tested with a variety of new and old mission scripts and generally expect it will not be an issue.
|
||||
|
||||
However we cannot foresee everything people come up with so we suggest testing with what you have in mind once olympus releases
|
||||
|
||||
### How does it work? ###
|
||||
The quick answer is magic.
|
||||
|
||||
The long answer is well all the code is there for you to read.
|
||||
|
||||
The middle answer is a bit like SRS does. Olympus consists of two parts.
|
||||
|
||||
(A) Olympus back end: A dll, run by DCS, that sends data out and gets commands in via a REST API;
|
||||
(B) Webserver exe: The one you start when starting the server via the desktop shortcut.
|
||||
|
||||
A and B never communicate when you connect the client you download the web page and some other minor stuff from B, and you get the DCS data from and send commands to A.
|
||||
|
||||
### How much does Olympus impact performance? ###
|
||||
Olympus by itself should not have a noticeable impact on server performance, however the ability for the user to spawn arbitrary units and command engagements means Olympus can be used in such a way that brings the game to it's knees.
|
||||
|
||||
Be cognizant of how you play, whether it's done through Olympus or the mission editor 500 MLRS units firing at once is not going to go over well with most servers
|
||||
|
||||
|
||||
|
||||
|
||||
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"
|
||||
|
||||
235
client/@types/olympus/index.d.ts
vendored
@@ -128,7 +128,16 @@ declare module "constants/constants" {
|
||||
export const altitudeIncrements: {
|
||||
[key: string]: number;
|
||||
};
|
||||
export const minimapBoundaries: LatLng[][];
|
||||
export const minimapBoundaries: {
|
||||
Nevada: LatLng[];
|
||||
Syria: LatLng[];
|
||||
Caucasus: LatLng[];
|
||||
PersianGulf: LatLng[];
|
||||
MarianaIslands: LatLng[];
|
||||
Falklands: LatLng[];
|
||||
Normandy: LatLng[];
|
||||
SinaiMap: LatLng[];
|
||||
};
|
||||
export const mapBounds: {
|
||||
Syria: {
|
||||
bounds: LatLngBounds;
|
||||
@@ -150,6 +159,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 +221,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 +250,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 +288,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 +341,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 +548,7 @@ declare module "interfaces" {
|
||||
}
|
||||
export interface UnitData {
|
||||
category: string;
|
||||
categoryDisplayName: string;
|
||||
ID: number;
|
||||
alive: boolean;
|
||||
human: boolean;
|
||||
@@ -514,6 +566,7 @@ declare module "interfaces" {
|
||||
horizontalVelocity: number;
|
||||
verticalVelocity: number;
|
||||
heading: number;
|
||||
track: number;
|
||||
isActiveTanker: boolean;
|
||||
isActiveAWACS: boolean;
|
||||
onOff: boolean;
|
||||
@@ -786,7 +839,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 +867,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 +894,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 +1148,7 @@ declare module "unit/unit" {
|
||||
getHorizontalVelocity(): number;
|
||||
getVerticalVelocity(): number;
|
||||
getHeading(): number;
|
||||
getTrack(): number;
|
||||
getIsActiveAWACS(): boolean;
|
||||
getIsActiveTanker(): boolean;
|
||||
getOnOff(): boolean;
|
||||
@@ -1146,6 +1204,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 +1282,8 @@ declare module "unit/unit" {
|
||||
setGroup(group: Group | null): void;
|
||||
drawLines(): void;
|
||||
checkZoomRedraw(): boolean;
|
||||
isControlledByDCS(): boolean;
|
||||
isControlledByOlympus(): boolean;
|
||||
/********************** Icon *************************/
|
||||
createIcon(): void;
|
||||
/********************** Visibility *************************/
|
||||
@@ -1676,22 +1741,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 +1966,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";
|
||||
@@ -1897,6 +2040,19 @@ declare module "unit/unitsmanager" {
|
||||
* @param category Either "Aircraft", "Helicopter", "GroundUnit", or "NavyUnit". Determines what class will be used to create the new unit accordingly.
|
||||
*/
|
||||
addUnit(ID: number, category: string): void;
|
||||
/** Sort units segregated groups based on controlling type and protection, if DCS-controlled
|
||||
*
|
||||
* @param units <Unit[]>
|
||||
* @returns Object
|
||||
*/
|
||||
segregateUnits(units: Unit[]): {
|
||||
[key: string]: [];
|
||||
};
|
||||
/**
|
||||
*
|
||||
* @param numOfProtectedUnits number
|
||||
*/
|
||||
showProtectedUnitsPopup(numOfProtectedUnits: number): void;
|
||||
/** Update the data of all the units. The data is directly decoded from the binary buffer received from the REST Server. This is necessary for performance and bandwidth reasons.
|
||||
*
|
||||
* @param buffer The arraybuffer, encoded according to the ICD defined in: TODO Add reference to ICD
|
||||
@@ -2102,7 +2258,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 +2422,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 +2490,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.13-alpha-rc5 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.13-alpha-rc5",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "DCSOlympus",
|
||||
"version": "v0.4.8-alpha",
|
||||
"version": "v0.4.13-alpha-rc5",
|
||||
"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.13-alpha-rc5",
|
||||
"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": {
|
||||
|
||||
@@ -125,13 +125,6 @@ export class ControlTipsPlugin implements OlympusPlugin {
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": false
|
||||
},
|
||||
{
|
||||
"key": `Mouse2`,
|
||||
"action": `Quick options`,
|
||||
"showIfUnitSelected": false,
|
||||
"showIfHoveringOverAirbase": false,
|
||||
"showIfHoveringOverUnit": true
|
||||
},
|
||||
{
|
||||
"key": `Mouse2`,
|
||||
"action": `Airbase menu`,
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -107,147 +107,4 @@
|
||||
#coordinates-tool[data-location-system="MGRS"] [data-location-system="MGRS"],
|
||||
#coordinates-tool[data-location-system="UTM"] [data-location-system="UTM"] {
|
||||
display:flex;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
#mouse-info-panel dl {
|
||||
margin-bottom: 4px;
|
||||
row-gap: 5px;
|
||||
}
|
||||
|
||||
#mouse-info-panel dt {
|
||||
height: fit-content;
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
#mouse-info-panel dt::after, #coordinates-tool [data-label] {
|
||||
align-items: center;
|
||||
background-color: white;
|
||||
border-radius: var(--border-radius-sm);
|
||||
color: var(--background-steel);
|
||||
display: flex;
|
||||
font-size: 15.6px;
|
||||
font-weight: bolder;
|
||||
height: 16px;
|
||||
justify-content: center;
|
||||
line-height: 16px;
|
||||
padding: 4px;
|
||||
text-transform: uppercase;
|
||||
width: 16px;
|
||||
}
|
||||
|
||||
#coordinates-tool [data-label] {
|
||||
height:24px;
|
||||
width:24px;
|
||||
}
|
||||
|
||||
#mouse-info-panel #measuring-tool dt {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
background-color: var(--background-offwhite);
|
||||
border-radius: var(--border-radius-sm);
|
||||
}
|
||||
|
||||
#mouse-info-panel #measuring-tool svg {
|
||||
padding: 3px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#mouse-info-panel #measuring-tool dt svg>* {
|
||||
fill: black;
|
||||
stroke: black;
|
||||
}
|
||||
|
||||
#mouse-info-panel [data-label]::after {
|
||||
content: attr(data-label);
|
||||
}
|
||||
|
||||
#mouse-info-panel dt[data-coalition="blue"]::after {
|
||||
background-color: var(--primary-blue);
|
||||
}
|
||||
|
||||
#mouse-info-panel dt[data-coalition="red"]::after {
|
||||
background-color: var(--primary-red);
|
||||
}
|
||||
|
||||
#mouse-info-panel [data-tooltip]:hover::before {
|
||||
background-color: var(--background-grey);
|
||||
border-radius: 5px;
|
||||
content: attr(data-tooltip);
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
padding: 5px;
|
||||
position: absolute;
|
||||
translate: calc(-100% - 15px) 0;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#coordinates-tool[data-location-system] [data-location-system] {
|
||||
display:none;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#coordinates-tool[data-location-system="LatLng"] [data-location-system="LatLng"],
|
||||
#coordinates-tool[data-location-system="MGRS"] [data-location-system="MGRS"] {
|
||||
display:flex;
|
||||
}
|
||||
|
||||
#coordinates-tool > * > * {
|
||||
align-items: center;
|
||||
display:flex;
|
||||
flex-flow: row nowrap;
|
||||
}
|
||||
|
||||
#coordinates-tool > * > * > * {
|
||||
display:table-cell;
|
||||
width:fit-content;
|
||||
}
|
||||
|
||||
#coordinates-tool > * > * > :last-child {
|
||||
text-align: right;
|
||||
width:100%;
|
||||
}
|
||||
|
||||
.br-info::after {
|
||||
content: attr(data-bearing) '\00B0 / ' attr(data-distance) " " attr(data-distance-units);
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: var(--background-offwhite);
|
||||
}
|
||||
|
||||
.br-info[data-coalition="blue"]::after {
|
||||
color: var(--primary-blue)
|
||||
}
|
||||
|
||||
.br-info[data-coalition="red"]::after {
|
||||
color: var(--primary-red)
|
||||
}
|
||||
|
||||
.br-info[data-message]::after {
|
||||
content: attr(data-message);
|
||||
}
|
||||
|
||||
.coordinates::after {
|
||||
content: attr(data-value);
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: var(--background-offwhite);
|
||||
}
|
||||
|
||||
.elevation::after {
|
||||
content: attr(data-value);
|
||||
font-weight: bold;
|
||||
font-size: 13px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: var(--background-offwhite);
|
||||
} */
|
||||
}
|
||||
@@ -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 !important; /* Higher price than the Soul Stone but inline styling is causing issues. */
|
||||
stroke: white !important; /* I'm sorry, daughter. */
|
||||
}
|
||||
|
||||
.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)"
|
||||
];
|
||||
|
||||
@@ -67,58 +67,74 @@ export const minAltitudeValues: { [key: string]: number } = { Aircraft: 0, Helic
|
||||
export const maxAltitudeValues: { [key: string]: number } = { Aircraft: 50000, Helicopter: 10000 };
|
||||
export const altitudeIncrements: { [key: string]: number } = { Aircraft: 500, Helicopter: 100 };
|
||||
|
||||
export const minimapBoundaries = [
|
||||
[ // NTTR
|
||||
export const minimapBoundaries = {
|
||||
"Nevada": [ // NTTR
|
||||
new LatLng(39.7982463, -119.985425),
|
||||
new LatLng(34.4037128, -119.7806729),
|
||||
new LatLng(34.3483316, -112.4529351),
|
||||
new LatLng(39.7372411, -112.1130805),
|
||||
new LatLng(39.7982463, -119.985425)
|
||||
],
|
||||
[ // Syria
|
||||
"Syria": [ // Syria
|
||||
new LatLng(37.3630556, 29.2686111),
|
||||
new LatLng(31.8472222, 29.8975),
|
||||
new LatLng(32.1358333, 42.1502778),
|
||||
new LatLng(37.7177778, 42.3716667),
|
||||
new LatLng(37.3630556, 29.2686111)
|
||||
],
|
||||
[ // Caucasus
|
||||
"Caucasus": [ // Caucasus
|
||||
new LatLng(39.6170191, 27.634935),
|
||||
new LatLng(38.8735863, 47.1423108),
|
||||
new LatLng(47.3907982, 49.3101946),
|
||||
new LatLng(48.3955879, 26.7753625),
|
||||
new LatLng(39.6170191, 27.634935)
|
||||
],
|
||||
[ // Persian Gulf
|
||||
"PersianGulf": [ // Persian Gulf
|
||||
new LatLng(32.9355285, 46.5623682),
|
||||
new LatLng(21.729393, 47.572675),
|
||||
new LatLng(21.8501348, 63.9734737),
|
||||
new LatLng(33.131584, 64.7313594),
|
||||
new LatLng(32.9355285, 46.5623682)
|
||||
],
|
||||
[ // Marianas
|
||||
"MarianaIslands": [ // Marianas
|
||||
new LatLng(22.09, 135.0572222),
|
||||
new LatLng(10.5777778, 135.7477778),
|
||||
new LatLng(10.7725, 149.3918333),
|
||||
new LatLng(22.5127778, 149.5427778),
|
||||
new LatLng(22.09, 135.0572222)
|
||||
],
|
||||
[ // South Atlantic
|
||||
"Falklands": [ // South Atlantic
|
||||
new LatLng(-49.097217, -79.418267),
|
||||
new LatLng(-56.874517,-79.418267),
|
||||
new LatLng(-56.874517, -43.316433),
|
||||
new LatLng(-49.097217, -43.316433),
|
||||
new LatLng(-49.097217, -79.418267)
|
||||
],
|
||||
"Normandy": [ // 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)
|
||||
],
|
||||
"SinaiMap": [ // 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)
|
||||
]
|
||||
];
|
||||
};
|
||||
|
||||
export const mapBounds = {
|
||||
"Syria": { bounds: new LatLngBounds([31.8472222, 29.8975], [37.7177778, 42.3716667]), zoom: 5 },
|
||||
"MarianaIslands": { bounds: new LatLngBounds([10.5777778, 135.7477778], [22.5127778, 149.5427778]), zoom: 5 },
|
||||
"Nevada": { bounds: new LatLngBounds([34.4037128, -119.7806729], [39.7372411, -112.1130805]), zoom: 5 },
|
||||
"PersianGulf": { bounds: new LatLngBounds([21.729393, 47.572675], [33.131584, 64.7313594]), zoom: 5 },
|
||||
"PersianGulf": { bounds: new LatLngBounds([21.729393, 47.572675], [33.131584, 64.7313594]), zoom: 4 },
|
||||
"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";
|
||||
@@ -64,6 +64,7 @@ export class Map extends L.Map {
|
||||
#centerUnit: Unit | null = null;
|
||||
#miniMap: ClickableMiniMap | null = null;
|
||||
#miniMapLayerGroup: L.LayerGroup;
|
||||
#miniMapPolyline: L.Polyline;
|
||||
#temporaryMarkers: TemporaryUnitMarker[] = [];
|
||||
#selecting: boolean = false;
|
||||
#isZooming: boolean = false;
|
||||
@@ -123,8 +124,8 @@ export class Map extends L.Map {
|
||||
/* Minimap */
|
||||
var minimapLayer = new L.TileLayer(mapLayers[Object.keys(mapLayers)[0] as keyof typeof mapLayers].urlTemplate, { minZoom: 0, maxZoom: 13 });
|
||||
this.#miniMapLayerGroup = new L.LayerGroup([minimapLayer]);
|
||||
var miniMapPolyline = new L.Polyline(this.#getMinimapBoundaries(), { color: '#202831' });
|
||||
miniMapPolyline.addTo(this.#miniMapLayerGroup);
|
||||
this.#miniMapPolyline = new L.Polyline([], { color: '#202831' });
|
||||
this.#miniMapPolyline.addTo(this.#miniMapLayerGroup);
|
||||
|
||||
/* Scale */
|
||||
//@ts-ignore TODO more hacking because the module is provided as a pure javascript module only
|
||||
@@ -418,6 +419,9 @@ export class Map extends L.Map {
|
||||
if (this.#miniMap)
|
||||
this.setView(e.latlng);
|
||||
})
|
||||
|
||||
const boundaries = this.#getMinimapBoundaries();
|
||||
this.#miniMapPolyline.setLatLngs(boundaries[theatre as keyof typeof boundaries]);
|
||||
}
|
||||
|
||||
getMiniMapLayerGroup() {
|
||||
@@ -559,7 +563,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 +666,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);
|
||||
@@ -791,7 +795,7 @@ export class Map extends L.Map {
|
||||
|
||||
#showDestinationCursors() {
|
||||
const singleCursor = !this.#shiftKey;
|
||||
const selectedUnitsCount = getApp().getUnitsManager().getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }).length;
|
||||
const selectedUnitsCount = getApp().getUnitsManager().getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }).length;
|
||||
if (singleCursor) {
|
||||
this.#hideDestinationCursors();
|
||||
}
|
||||
@@ -817,7 +821,7 @@ export class Map extends L.Map {
|
||||
}
|
||||
|
||||
#updateDestinationCursors() {
|
||||
const selectedUnitsCount = getApp().getUnitsManager().getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true }).length;
|
||||
const selectedUnitsCount = getApp().getUnitsManager().getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true }).length;
|
||||
if (selectedUnitsCount > 1) {
|
||||
const groupLatLng = this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : this.getMouseCoordinates();
|
||||
if (this.#destinationPreviewCursors.length == 1)
|
||||
|
||||
@@ -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.13-alpha-rc5";
|
||||
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,24 @@ 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";
|
||||
latestVersionSpan.classList.toggle("new-version", this.#latestVersion !== VERSION);
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#setupEvents() {
|
||||
@@ -279,7 +297,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 +429,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 +454,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);
|
||||
|
||||
@@ -9,6 +9,7 @@ import { ROEDescriptions, ROEs, altitudeIncrements, emissionsCountermeasures, em
|
||||
import { ftToM, knotsToMs, mToFt, msToKnots } from "../other/utils";
|
||||
import { GeneralSettings, Radio, TACAN } from "../interfaces";
|
||||
import { ContextActionSet } from "../unit/contextactionset";
|
||||
import { Popup } from "../popups/popup";
|
||||
|
||||
export class UnitControlPanel extends Panel {
|
||||
#altitudeSlider: Slider;
|
||||
@@ -97,6 +98,8 @@ export class UnitControlPanel extends Panel {
|
||||
/* Follow roads switch */
|
||||
this.#followRoadsSwitch = new Switch("follow-roads-switch", (value: boolean) => {
|
||||
getApp().getUnitsManager().setFollowRoads(value);
|
||||
if (value)
|
||||
(getApp().getPopupsManager().get("infoPopup") as Popup).setText("Warning: follow roads movements can cause lag");
|
||||
});
|
||||
|
||||
/* Operate as */
|
||||
|
||||
@@ -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 = [];
|
||||
@@ -96,6 +100,50 @@ export class UnitsManager {
|
||||
}
|
||||
}
|
||||
|
||||
/** Sort units segregated groups based on controlling type and protection, if DCS-controlled
|
||||
*
|
||||
* @param units <Unit[]>
|
||||
* @returns Object
|
||||
*/
|
||||
segregateUnits(units: Unit[]): { [key: string]: [] } {
|
||||
const data: any = {
|
||||
controllable: [],
|
||||
dcsProtected: [],
|
||||
dcsUnprotected: [],
|
||||
human: [],
|
||||
olympus: []
|
||||
};
|
||||
const map = getApp().getMap();
|
||||
|
||||
units.forEach(unit => {
|
||||
if (unit.getHuman())
|
||||
data.human.push(unit);
|
||||
else if (unit.isControlledByOlympus())
|
||||
data.olympus.push(unit);
|
||||
else if (map.getIsUnitProtected(unit))
|
||||
data.dcsProtected.push(unit);
|
||||
else
|
||||
data.dcsUnprotected.push(unit);
|
||||
});
|
||||
data.controllable = [].concat(data.dcsUnprotected, data.human, data.olympus);
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param numOfProtectedUnits number
|
||||
*/
|
||||
showProtectedUnitsPopup(numOfProtectedUnits: number) {
|
||||
if (numOfProtectedUnits < 1)
|
||||
return;
|
||||
const messageText = (numOfProtectedUnits === 1) ? `Unit is protected` : `All selected units are protected`;
|
||||
(getApp().getPopupsManager().get("infoPopup") as Popup).setText(messageText);
|
||||
// Cheap way for now until we use more locks
|
||||
let lock = <HTMLElement>document.querySelector("#unit-visibility-control button.lock");
|
||||
lock.classList.add("prompt");
|
||||
setTimeout(() => lock.classList.remove("prompt"), 4000);
|
||||
}
|
||||
|
||||
/** Update the data of all the units. The data is directly decoded from the binary buffer received from the REST Server. This is necessary for performance and bandwidth reasons.
|
||||
*
|
||||
* @param buffer The arraybuffer, encoded according to the ICD defined in: TODO Add reference to ICD
|
||||
@@ -257,14 +305,8 @@ export class UnitsManager {
|
||||
}
|
||||
}
|
||||
if (options) {
|
||||
if (options.showProtectionReminder === true && numProtectedUnits > selectedUnits.length && selectedUnits.length === 0) {
|
||||
const messageText = (numProtectedUnits === 1) ? `Unit is protected` : `All selected units are protected`;
|
||||
(getApp().getPopupsManager().get("infoPopup") as Popup).setText(messageText);
|
||||
// Cheap way for now until we use more locks
|
||||
let lock = <HTMLElement>document.querySelector("#unit-visibility-control button.lock");
|
||||
lock.classList.add("prompt");
|
||||
setTimeout(() => lock.classList.remove("prompt"), 4000);
|
||||
}
|
||||
if (options.showProtectionReminder === true && numProtectedUnits > selectedUnits.length && selectedUnits.length === 0)
|
||||
this.showProtectedUnitsPopup(numProtectedUnits);
|
||||
|
||||
if (options.onlyOnePerGroup) {
|
||||
var temp: Unit[] = [];
|
||||
@@ -361,8 +403,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
/* Compute the destination for each unit. If mantainRelativePosition is true, compute the destination so to hold the relative positions */
|
||||
var unitDestinations: { [key: number]: LatLng } = {};
|
||||
@@ -395,8 +442,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: false });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
for (let idx in units) {
|
||||
const unit = units[idx];
|
||||
@@ -421,8 +473,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
units.forEach((unit: Unit) => unit.landAt(latlng));
|
||||
|
||||
@@ -438,8 +495,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
units.forEach((unit: Unit) => unit.changeSpeed(speedChange));
|
||||
}
|
||||
@@ -453,8 +515,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
units.forEach((unit: Unit) => unit.changeAltitude(altitudeChange));
|
||||
}
|
||||
@@ -468,8 +535,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
units.forEach((unit: Unit) => unit.setSpeed(speed));
|
||||
this.#showActionMessage(units, `setting speed to ${msToKnots(speed)} kts`);
|
||||
@@ -484,8 +556,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
units.forEach((unit: Unit) => unit.setSpeedType(speedType));
|
||||
this.#showActionMessage(units, `setting speed type to ${speedType}`);
|
||||
@@ -500,8 +577,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
units.forEach((unit: Unit) => unit.setAltitude(altitude));
|
||||
this.#showActionMessage(units, `setting altitude to ${mToFt(altitude)} ft`);
|
||||
@@ -516,8 +598,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
units.forEach((unit: Unit) => unit.setAltitudeType(altitudeType));
|
||||
this.#showActionMessage(units, `setting altitude type to ${altitudeType}`);
|
||||
@@ -532,8 +619,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
units.forEach((unit: Unit) => unit.setROE(ROE));
|
||||
this.#showActionMessage(units, `ROE set to ${ROE}`);
|
||||
@@ -548,8 +640,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
units.forEach((unit: Unit) => unit.setReactionToThreat(reactionToThreat));
|
||||
this.#showActionMessage(units, `reaction to threat set to ${reactionToThreat}`);
|
||||
@@ -564,8 +661,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
units.forEach((unit: Unit) => unit.setEmissionsCountermeasures(emissionCountermeasure));
|
||||
this.#showActionMessage(units, `emissions & countermeasures set to ${emissionCountermeasure}`);
|
||||
@@ -580,8 +682,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
units.forEach((unit: Unit) => unit.setOnOff(onOff));
|
||||
this.#showActionMessage(units, `unit active set to ${onOff}`);
|
||||
@@ -596,8 +703,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
units.forEach((unit: Unit) => unit.setFollowRoads(followRoads));
|
||||
this.#showActionMessage(units, `follow roads set to ${followRoads}`);
|
||||
@@ -613,8 +725,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
units.forEach((unit: Unit) => unit.setOperateAs(operateAs));
|
||||
this.#showActionMessage(units, `operate as set to ${operateAs}`);
|
||||
@@ -629,8 +746,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
units.forEach((unit: Unit) => unit.attackUnit(ID));
|
||||
this.#showActionMessage(units, `attacking unit ${this.getUnitByID(ID)?.getUnitName()}`);
|
||||
@@ -643,11 +765,14 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units.forEach((unit: Unit) => unit.refuel());
|
||||
this.#showActionMessage(units, `sent to nearest tanker`);
|
||||
segregatedUnits.controllable.forEach((unit: Unit) => unit.refuel());
|
||||
this.#showActionMessage(segregatedUnits.controllable, `sent to nearest tanker`);
|
||||
}
|
||||
|
||||
/** Instruct the selected units to follow another unit in a formation. Only works for aircrafts and helicopters.
|
||||
@@ -661,8 +786,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
if (offset == undefined) {
|
||||
/* Simple formations with fixed offsets */
|
||||
@@ -679,8 +809,7 @@ export class UnitsManager {
|
||||
var count = 1;
|
||||
var xr = 0; var yr = 1; var zr = -1;
|
||||
var layer = 1;
|
||||
for (let idx in units) {
|
||||
var unit = units[idx];
|
||||
units.forEach((unit: Unit) => {
|
||||
if (unit.ID !== ID) {
|
||||
if (offset != undefined)
|
||||
/* Offset is set, apply it */
|
||||
@@ -701,7 +830,7 @@ export class UnitsManager {
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
})
|
||||
this.#showActionMessage(units, `following unit ${this.getUnitByID(ID)?.getUnitName()}`);
|
||||
}
|
||||
|
||||
@@ -714,8 +843,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
units.forEach((unit: Unit) => unit.bombPoint(latlng));
|
||||
this.#showActionMessage(units, `unit bombing point`);
|
||||
@@ -730,8 +864,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
units.forEach((unit: Unit) => unit.carpetBomb(latlng));
|
||||
this.#showActionMessage(units, `unit carpet bombing point`);
|
||||
@@ -746,8 +885,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
units.forEach((unit: Unit) => unit.fireAtArea(latlng));
|
||||
this.#showActionMessage(units, `unit firing at area`);
|
||||
@@ -762,8 +906,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
getGroundElevation(latlng, (response: string) => {
|
||||
var groundElevation: number | null = null;
|
||||
@@ -784,22 +933,32 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
units.forEach((unit: Unit) => unit.scenicAAA());
|
||||
this.#showActionMessage(units, `unit set to perform scenic AAA`);
|
||||
}
|
||||
|
||||
/** Instruct units to enter into miss on purpose mode. Units will aim to the nearest enemy unit but not precisely.
|
||||
/** 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) {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
units.forEach((unit: Unit) => unit.missOnPurpose());
|
||||
this.#showActionMessage(units, `unit set to perform miss-on-purpose AAA`);
|
||||
@@ -814,9 +973,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true, showProtectionReminder: true });
|
||||
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
units.forEach((unit: Unit) => unit.landAtPoint(latlng));
|
||||
this.#showActionMessage(units, `unit landing at point`);
|
||||
@@ -831,8 +994,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
units.forEach((unit: Unit) => unit.setShotsScatter(shotsScatter));
|
||||
this.#showActionMessage(units, `shots scatter set to ${shotsScatter}`);
|
||||
@@ -847,8 +1015,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, onlyOnePerGroup: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
units.forEach((unit: Unit) => unit.setShotsIntensity(shotsIntensity));
|
||||
this.#showActionMessage(units, `shots intensity set to ${shotsIntensity}`);
|
||||
@@ -879,8 +1052,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: false, showProtectionReminder: true });
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
if (this.getUnitsCategories(units).length == 1) {
|
||||
var unitsData: { ID: number, location: LatLng }[] = [];
|
||||
@@ -925,8 +1103,13 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeProtected: true, showProtectionReminder: true }); /* Can be applied to humans too */
|
||||
|
||||
if (units.length === 0)
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return;
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
const selectionContainsAHuman = units.some((unit: Unit) => {
|
||||
return unit.getHuman() === true;
|
||||
@@ -964,6 +1147,14 @@ export class UnitsManager {
|
||||
if (units === null)
|
||||
units = this.getSelectedUnits({ excludeHumans: true, excludeProtected: true, onlyOnePerGroup: true });
|
||||
|
||||
const segregatedUnits = this.segregateUnits(units);
|
||||
if (segregatedUnits.controllable.length === 0) {
|
||||
this.showProtectedUnitsPopup(segregatedUnits.dcsProtected.length);
|
||||
return {};
|
||||
}
|
||||
|
||||
units = segregatedUnits.controllable;
|
||||
|
||||
if (units.length === 0)
|
||||
return {};
|
||||
|
||||
@@ -1001,7 +1192,7 @@ export class UnitsManager {
|
||||
*
|
||||
*/
|
||||
copy(units: Unit[] | null = null) {
|
||||
if ( !getApp().getContextManager().getCurrentContext().getAllowUnitCopying() )
|
||||
if (!getApp().getContextManager().getCurrentContext().getAllowUnitCopying())
|
||||
return;
|
||||
|
||||
if (units === null)
|
||||
@@ -1021,7 +1212,7 @@ export class UnitsManager {
|
||||
* @returns True if units were pasted successfully
|
||||
*/
|
||||
paste() {
|
||||
if ( !getApp().getContextManager().getCurrentContext().getAllowUnitPasting() )
|
||||
if (!getApp().getContextManager().getCurrentContext().getAllowUnitPasting())
|
||||
return;
|
||||
|
||||
let spawnPoints = 0;
|
||||
@@ -1104,11 +1295,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;
|
||||
@@ -1122,7 +1340,7 @@ export class UnitsManager {
|
||||
/* 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);
|
||||
this.spawnUnits("GroundUnit", [{ unitType: unitBlueprint.name, location: latlng, liveryID: "" }], coalitionArea.getCoalition(), false, "", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1134,52 +1352,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.13-alpha-rc5</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>
|
||||
@@ -100,7 +100,7 @@
|
||||
</div>
|
||||
|
||||
<div id="follow-roads" class="switch-control yes-no">
|
||||
<h4>Follow roads <img src="/resources/theme/images/icons/circle-question-regular.svg" title=""></h4>
|
||||
<h4>Follow roads <img src="/resources/theme/images/icons/circle-question-regular.svg" title="WARNING: follow roads movements can cause lag"></h4>
|
||||
<div id="follow-roads-switch" class="ol-switch"></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.13-alpha-rc5</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" }' title="Toggle Blue coalition visibility"><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" }' title="Toggle Red coalition visibility"><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" }' title="Toggle Neutral coalition visibility"><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.13-alpha-rc5"
|
||||
|
||||
[Setup]
|
||||
AppName=DCS Olympus
|
||||
@@ -44,62 +44,40 @@ 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;
|
||||
Source: "..\LEGAL"; 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}
|
||||
|
||||
[Registry]
|
||||
Root: HKCU; Subkey: "Environment"; ValueType: string; ValueName: "DCSOLYMPUS_PATH"; ValueData: "{app}\Mods\Services\Olympus"; Flags: preservestringtype
|
||||
Root: HKCU; Subkey: "Environment"; ValueType: expandsz; ValueName: "Path"; ValueData: "{olddata};%DCSOLYMPUS_PATH%\bin"; Check: NeedsAddPath('%DCSOLYMPUS_PATH%\bin');
|
||||
|
||||
[Setup]
|
||||
; Tell Windows Explorer to reload the environment
|
||||
ChangesEnvironment=yes
|
||||
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
|
||||
|
||||
[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]
|
||||
function NeedsAddPath(Param: string): boolean;
|
||||
var
|
||||
OrigPath: string;
|
||||
begin
|
||||
if not RegQueryStringValue(HKCU,
|
||||
'Environment',
|
||||
'Path', OrigPath)
|
||||
then begin
|
||||
Result := True;
|
||||
exit;
|
||||
end;
|
||||
{ look for the path with leading and trailing semicolon }
|
||||
{ Pos() returns 0 if not found }
|
||||
Result := Pos(';' + Param + ';', ';' + OrigPath + ';') = 0;
|
||||
end;
|
||||
|
||||
[Code]
|
||||
var
|
||||
lblLocalInstall: TLabel;
|
||||
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 +114,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 +150,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 +189,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 +201,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 +215,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 +231,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 +244,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 +260,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 +305,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 +317,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 +329,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 +341,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 +353,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 +440,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 +456,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 +476,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.13-alpha-rc5",
|
||||
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,11 +1,10 @@
|
||||
local version = "v0.4.8-alpha"
|
||||
local version = "v0.4.13-alpha-rc5"
|
||||
|
||||
local debug = false -- True enables debug printing using DCS messages
|
||||
|
||||
-- .dll related variables
|
||||
Olympus.OlympusDLL = nil
|
||||
Olympus.DLLsloaded = false
|
||||
Olympus.OlympusModPath = os.getenv('DCSOLYMPUS_PATH')..'\\bin\\'
|
||||
|
||||
-- Logger reference
|
||||
Olympus.log = mist.Logger:new("Olympus", 'info')
|
||||
@@ -21,6 +20,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
|
||||
@@ -30,25 +30,31 @@ Olympus.weapons = {} -- Table holding references to all the currently existing
|
||||
Olympus.missionStartTime = DCS.getRealTime()
|
||||
Olympus.napalmCounter = 1
|
||||
Olympus.fireCounter = 1
|
||||
|
||||
-- Load the lua file system
|
||||
local lfs = require('lfs')
|
||||
|
||||
------------------------------------------------------------------------------------------------------
|
||||
-- Olympus functions
|
||||
------------------------------------------------------------------------------------------------------
|
||||
-- Print a debug message if the debug option is true
|
||||
function Olympus.debug(message, displayFor)
|
||||
if debug == true then
|
||||
Olympus.log:info(message)
|
||||
trigger.action.outText(message, displayFor)
|
||||
end
|
||||
end
|
||||
|
||||
-- Print a notify message
|
||||
function Olympus.notify(message, displayFor)
|
||||
Olympus.log:info(message)
|
||||
trigger.action.outText(message, displayFor)
|
||||
end
|
||||
|
||||
-- Loads the olympus .dll
|
||||
function Olympus.loadDLLs()
|
||||
-- Add the .dll paths
|
||||
package.cpath = package.cpath..';'..Olympus.OlympusModPath..'?.dll;'
|
||||
package.cpath = package.cpath..';'..Olympus.instancePath..'?.dll;'
|
||||
|
||||
local status
|
||||
status, Olympus.OlympusDLL = pcall(require, 'olympus')
|
||||
@@ -509,10 +515,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.randomDebries(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 +552,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 +612,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 +629,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 +920,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)
|
||||
@@ -979,10 +986,16 @@ function Olympus.setUnitsData(arg, time)
|
||||
table["category"] = "GroundUnit"
|
||||
elseif unit:getDesc().category == Unit.Category.SHIP then
|
||||
table["category"] = "NavyUnit"
|
||||
elseif Olympus.modsList ~= nil and Olympus.modsList[unit:getDesc().typeName] ~= nil then
|
||||
table["category"] = Olympus.modsList[unit:getDesc().typeName]
|
||||
end
|
||||
else
|
||||
units[ID] = {isAlive = false}
|
||||
Olympus.units[ID] = nil
|
||||
if Olympus.modsList ~= nil and Olympus.modsList[unit:getDesc().typeName] ~= nil then
|
||||
table["category"] = Olympus.modsList[unit:getDesc().typeName]
|
||||
else
|
||||
units[ID] = {isAlive = false}
|
||||
Olympus.units[ID] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- If the category is handled by Olympus, get the data
|
||||
@@ -992,7 +1005,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 +1017,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 +1048,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 +1066,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
|
||||
@@ -1294,6 +1372,9 @@ end
|
||||
------------------------------------------------------------------------------------------------------
|
||||
-- Olympus startup script
|
||||
------------------------------------------------------------------------------------------------------
|
||||
Olympus.instancePath = lfs.writedir().."Mods\\Services\\Olympus\\bin\\"
|
||||
Olympus.notify("Starting DCS Olympus backend session in "..Olympus.instancePath, 2)
|
||||
|
||||
local OlympusName = 'Olympus ' .. version .. ' C++ module';
|
||||
Olympus.DLLsloaded = Olympus.loadDLLs()
|
||||
if Olympus.DLLsloaded then
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
local version = 'v0.4.8-alpha'
|
||||
local version = 'v0.4.13-alpha-rc5'
|
||||
local lfs = require("lfs")
|
||||
|
||||
Olympus = {}
|
||||
Olympus.OlympusDLL = nil
|
||||
Olympus.cppRESTDLL = nil
|
||||
Olympus.DLLsloaded = false
|
||||
Olympus.OlympusModPath = os.getenv('DCSOLYMPUS_PATH')..'\\bin\\'
|
||||
Olympus.OlympusModPath = lfs.writedir().."Mods\\Services\\Olympus\\bin\\"
|
||||
|
||||
log.write('Olympus.HOOKS.LUA', log.INFO,'Executing OlympusHook.lua')
|
||||
|
||||
@@ -15,7 +15,7 @@ function Olympus.loadDLLs()
|
||||
local status
|
||||
log.write('Olympus.HOOKS.LUA', log.INFO, 'Loading olympus.dll from ['..Olympus.OlympusModPath..']')
|
||||
status, Olympus.OlympusDLL = pcall(require, 'olympus')
|
||||
if status then
|
||||
if status then
|
||||
log.write('Olympus.HOOKS.LUA', log.INFO, 'olympus.dll loaded successfully')
|
||||
return true
|
||||
else
|
||||
|
||||
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")
|
||||
11
scripts/mods.lua
Normal file
@@ -0,0 +1,11 @@
|
||||
-- Enter here any mods required by your mission as in the example below.
|
||||
-- Possible categories are:
|
||||
-- Aircraft
|
||||
-- Helicopter
|
||||
-- GroundUnit
|
||||
-- NavyUnit
|
||||
|
||||
Olympus.modsList = {
|
||||
["A-4E-C"] = "Aircraft",
|
||||
["Bronco-OV-10A"] = "Aircraft"
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
@@ -113,7 +113,7 @@ class Move : public Command
|
||||
{
|
||||
public:
|
||||
Move(string groupName, Coords destination, double speed, string speedType, double altitude,
|
||||
string altitudeType, string taskOptions, string category, function<void(void)> callback = []() {}) :
|
||||
string altitudeType, string taskOptions, string category, bool onRoad, function<void(void)> callback = []() {}) :
|
||||
Command(callback),
|
||||
groupName(groupName),
|
||||
destination(destination),
|
||||
@@ -122,12 +122,13 @@ public:
|
||||
altitude(altitude),
|
||||
altitudeType(altitudeType),
|
||||
taskOptions(taskOptions),
|
||||
category(category)
|
||||
category(category),
|
||||
onRoad(onRoad)
|
||||
{
|
||||
priority = CommandPriority::HIGH;
|
||||
priority = CommandPriority::MEDIUM;
|
||||
};
|
||||
virtual string getString();
|
||||
virtual unsigned int getLoad() { return 2; }
|
||||
virtual unsigned int getLoad() { return onRoad? 45: 5; }
|
||||
|
||||
private:
|
||||
const string groupName;
|
||||
@@ -138,6 +139,7 @@ private:
|
||||
const string altitudeType;
|
||||
const string taskOptions;
|
||||
const string category;
|
||||
const bool onRoad;
|
||||
};
|
||||
|
||||
/* Smoke command */
|
||||
@@ -173,7 +175,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 +198,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 +222,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 +247,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 +291,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 +312,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 +330,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 +348,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 +381,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 +403,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 +423,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,7 +1,7 @@
|
||||
#pragma once
|
||||
#include "unit.h"
|
||||
|
||||
#define GROUND_DEST_DIST_THR 100
|
||||
#define GROUND_DEST_DIST_THR 10
|
||||
|
||||
class GroundUnit : public Unit
|
||||
{
|
||||
|
||||
@@ -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; }
|
||||
|
||||