Merge and fix conflict

This commit is contained in:
PeekabooSteam 2023-12-01 16:55:14 +00:00
commit f0c3f95189
15 changed files with 495 additions and 243 deletions

View File

@ -1,4 +1,4 @@
# Important note: DCS Olympus is in alpha state. No official release has been produced yet. The first public version is planned for Q4 2023.
# Important note: DCS Olympus is in alpha state. No official release has been produced yet. The first public version is planned for mid december 2023.
# DCS Olympus
*A real-time web interface to spawn and control units in DCS World*
@ -6,32 +6,19 @@
![alt text](https://github.com/Pax1601/DCSOlympus/blob/main/client/sample.png?raw=true)
### What is this?
DCS Olympus is a mod for DCS World. It allows users to spawn, control, task, group, and remove units from a DCS World server using a real-time map interface, similarly to Real Time Strategy games. The user interface also provides useful informations units, like loadouts, fuel, tasking, and so on. In the future, more features for DCS World GCI and JTAC will be available.
DCS: Olympus is a free and open-source mod for DCS that enables dynamic real-time control through a map interface. The user is able to spawn units/groups, deploy a variety of effects such as smoke, flares, or explosions, and waypoints/tasks can be given to AI units in real-time in a way similar to a classic RTS game.
### Features and how to use it
- Spawn air and ground units, with preset loadouts
- Double click on the map to spawn a blue and red units, both in the air and in the ground, with preset loadouts for air-to-air or air-to-ground tasks;
- Control units
- Select one ore more units to move them around. Hold down ctrl and click to create a route for the unit to follow;
- Attack other units
- After selecting one ore more units, double click on another unit and select "Attack" to attack it, depending on the available weapons.
Additionally Olympus is able to run several effects and unit behaviours beyond the core DCS offerings. This includes such things as napalm and white phosphosous explosions, or setting up AA units to fire at players and miss, and more.
It even includes Red and Blue modes which limit your view and powers to just seeing what your coalition sees, with a spawning budget you could play against your friends even with no-one in the game piloting, or have a Red commander working against a squadron of blue pilots, and/or a blue commander working with them.
Even better it requires no client mods be installed if used on a server
The full feature list is simply too long to enumerate in a short summary but needless to say Olympus offers up a lot of unique gameplay that has previously not existed, and enhances many other elements of DCS in exciting ways
### Installing DCS Olympus
A prebuilt installer will soon be released and available here
### Building DCS Olympus
DCS Olympus is comprised of two modules:
A "core" c++ .dll module, which is run by DCS and exposes all the necessary data, and provides endpoints for commands from a REST server. A Visual Studio 2017/2019/2022 solution is provided, and requires no additional configuration. The core dll solution has two dependencies, both can be installed using vcpkg (https://vcpkg.io/en/getting-started.html):
- cpprestsdk: `vcpkg install cpprestsdk:x64-windows`
- geographiclib: `vcpkg install geographiclib:x64-windows`
A "client" node.js typescript web app, which can be hosted on the server using express.js. A Visual Studio Code configuration is provided for debugging. The client requires node.js to be installed for building (https://nodejs.org/en/). After installing node.js, move in the client folder and run the following commands:
- `npm install`
- `npm -g install`
After installing all the necessary dependencies you can start a development server executing the *client/debug.bat* batch file, and visiting http:\\localhost:3000 with any modern browser (tested with updated Chrome, Firefox and Edge). However, it is highly suggested to simply run the `Launch Chrome against localhost` debug configuration in Visual Studio Code.

View File

@ -154,6 +154,10 @@ declare module "constants/constants" {
bounds: LatLngBounds;
zoom: number;
};
Normandy: {
bounds: LatLngBounds;
zoom: number;
};
};
export const mapLayers: {
"ArcGIS Satellite": {
@ -204,6 +208,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)";
@ -232,33 +237,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;
@ -529,6 +535,7 @@ declare module "interfaces" {
}
export interface UnitData {
category: string;
categoryDisplayName: string;
ID: number;
alive: boolean;
human: boolean;
@ -546,6 +553,7 @@ declare module "interfaces" {
horizontalVelocity: number;
verticalVelocity: number;
heading: number;
track: number;
isActiveTanker: boolean;
isActiveAWACS: boolean;
onOff: boolean;
@ -818,7 +826,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;
};
@ -1123,6 +1131,7 @@ declare module "unit/unit" {
getHorizontalVelocity(): number;
getVerticalVelocity(): number;
getHeading(): number;
getTrack(): number;
getIsActiveAWACS(): boolean;
getIsActiveTanker(): boolean;
getOnOff(): boolean;
@ -1178,6 +1187,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
*
@ -1708,22 +1722,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;
@ -1892,6 +1947,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";

View File

@ -55,7 +55,7 @@ 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));

View File

@ -45,6 +45,7 @@
justify-content: space-between;
row-gap: 5px;
width: 100%;
padding: 5px;
}
.contextmenu-advanced-options-toggle,
@ -61,7 +62,13 @@
.contextmenu-advanced-options-toggle:after,
.contextmenu-metadata-toggle:after {
content: url(/resources/theme/images/icons/chevron-down.svg);
margin: auto;
margin-left: auto;
margin-top: auto;
}
.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 +248,8 @@
}
.unit-label-count-container {
display: flex;
flex-direction: row;
display: grid;
grid-template-columns: 187px 1fr 1fr;
align-items: center;
column-gap: 5px;
}
@ -580,6 +587,7 @@
#iads-menu {
row-gap: 10px;
padding: 10px;
}
#coalition-area-contextmenu>div:nth-child(2) {
@ -596,6 +604,7 @@
flex-direction: column;
justify-content: space-between;
row-gap: 5px;
padding: 20px;
}
.create-iads-button {

View File

@ -211,6 +211,10 @@ button svg.fill-coalition[data-coalition="red"] * {
right: 10px;
}
.ol-select.is-open:not(.ol-select-image)>.ol-select-value:after {
transform: rotate(180deg);
}
.ol-select>.ol-select-options {
border-radius: var(--border-radius-md);
max-height: 0;
@ -1565,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;
@ -1574,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)
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 MiB

After

Width:  |  Height:  |  Size: 7.7 MiB

View File

@ -220,8 +220,8 @@ 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)";

View File

@ -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);
@ -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,7 +697,6 @@ export class HelicopterSpawnMenu extends UnitSpawnMenu {
}
export class GroundUnitSpawnMenu extends UnitSpawnMenu {
protected showRangeCircles: boolean = true;
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,7 +738,6 @@ export class GroundUnitSpawnMenu extends UnitSpawnMenu {
}
export class AirDefenceUnitSpawnMenu extends GroundUnitSpawnMenu {
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) {

View File

@ -7,7 +7,7 @@ import { AirbaseContextMenu } from "../contextmenus/airbasecontextmenu";
import { Dropdown } from "../controls/dropdown";
import { Airbase } from "../mission/airbase";
import { Unit } from "../unit/unit";
import { bearing, createCheckboxOption } from "../other/utils";
import { bearing, createCheckboxOption, polyContains } from "../other/utils";
import { DestinationPreviewMarker } from "./markers/destinationpreviewmarker";
import { TemporaryUnitMarker } from "./markers/temporaryunitmarker";
import { ClickableMiniMap } from "./clickableminimap";
@ -559,7 +559,7 @@ export class Map extends L.Map {
/* Coalition areas are ordered in the #coalitionAreas array according to their zindex. Select the upper one */
for (let coalitionArea of this.#coalitionAreas) {
if (coalitionArea.getBounds().contains(e.latlng)) {
if (polyContains(e.latlng, coalitionArea)) {
if (coalitionArea.getSelected())
clickedCoalitionArea = coalitionArea;
else
@ -662,7 +662,7 @@ export class Map extends L.Map {
this.#destinationGroupRotation = -bearing(this.#destinationRotationCenter.lat, this.#destinationRotationCenter.lng, this.getMouseCoordinates().lat, this.getMouseCoordinates().lng);
this.#updateDestinationCursors();
}
else if (this.#state === COALITIONAREA_DRAW_POLYGON) {
else if (this.#state === COALITIONAREA_DRAW_POLYGON && e.latlng !== undefined) {
this.#drawingCursor.setLatLng(e.latlng);
/* Update the polygon being drawn with the current position of the mouse cursor */
this.getSelectedCoalitionArea()?.moveActiveVertex(e.latlng);

View File

@ -1108,6 +1108,36 @@ 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 + 40 * 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(), true, "", "", (res: any) =>{
getApp().getMap().addTemporaryMarker(latlng, unitBlueprint.name, getApp().getActiveCoalition(), res.commandHash);
});
}
}
}
}
}
})
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)) {
@ -1125,8 +1155,11 @@ export class UnitsManager {
if (Math.random() < IADSDensities[type]) {
/* Get a random blueprint depending on the selected parameters and spawn the unit */
const unitBlueprint = randomUnitBlueprint(groundUnitDatabase, { type: type, eras: activeEras, ranges: activeRanges });
if (unitBlueprint)
this.spawnUnits("GroundUnit", [{ unitType: unitBlueprint.name, location: latlng, liveryID: "" }], coalitionArea.getCoalition(), true);
if (unitBlueprint) {
this.spawnUnits("GroundUnit", [{ unitType: unitBlueprint.name, location: latlng, liveryID: "" }], coalitionArea.getCoalition(), true, "", "", (res: any) =>{
getApp().getMap().addTemporaryMarker(latlng, unitBlueprint.name, getApp().getActiveCoalition(), res.commandHash);
});
}
}
}
}

View File

@ -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>

View File

@ -3,15 +3,15 @@
<div class="upper-bar ol-panel">
<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="Make to bottom layer" 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="Bring area 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">

View File

@ -3,51 +3,51 @@
<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>

View File

@ -49,7 +49,7 @@ Source: "{#nodejsFolder}\*.*"; DestDir: "{app}\Mods\Services\Olympus\client"; Fl
Source: "..\scripts\python\configurator\dist\configurator.exe"; DestDir: "{app}\Mods\Services\Olympus"; Flags: ignoreversion;
[Run]
Filename: "{app}\Mods\Services\Olympus\configurator.exe"; Parameters: -a {code:GetAddress} -c {code:GetClientPort} -b {code:GetBackendPort} -p {code:GetPassword} -bp {code:GetBluePassword} -rp {code:GetRedPassword}
Filename: "{app}\Mods\Services\Olympus\configurator.exe"; Parameters: -a {code:GetAddress} -c {code:GetClientPort} -b {code:GetBackendPort} -p {code:GetPassword} -bp {code:GetBluePassword} -rp {code:GetRedPassword}; Check: CheckCallConfigurator
[Registry]
Root: HKCU; Subkey: "Environment"; ValueType: string; ValueName: "DCSOLYMPUS_PATH"; ValueData: "{app}\Mods\Services\Olympus"; Flags: preservestringtype
@ -87,19 +87,21 @@ var
lblLocalInstallInstructions: TNewStaticText;
lblServerInstall: TLabel;
lblServerInstallInstructions: TNewStaticText;
lblKeepOld: TLabel;
lblClientPort: TLabel;
lblBackendPort: TLabel;
lblPassword: TLabel;
lblBluePassword: TLabel;
lblRedPassword: TLabel;
txtLocalInstall: TNewRadioButton;
txtServerInstall: TNewRadioButton;
radioLocalInstall: TNewRadioButton;
radioServerInstall: TNewRadioButton;
checkKeepOld: TNewCheckBox;
txtClientPort: TEdit;
txtBackendPort: TEdit;
txtPassword: TPasswordEdit;
txtBluePassword: TPasswordEdit;
txtRedPassword: TPasswordEdit;
AddressPage: Integer;
InstallationTypePage: Integer;
PasswordPage: Integer;
lblPasswordInstructions: TNewStaticText;
@ -136,14 +138,14 @@ procedure frmAddress_CancelButtonClick(Page: TWizardPage; var Cancel, Confirm: B
begin
end;
function frmAddress_CreatePage(PreviousPageId: Integer): Integer;
function frmInstallationType_CreatePage(PreviousPageId: Integer): Integer;
var
Page: TWizardPage;
begin
Page := CreateCustomPage(
PreviousPageId,
'DCS Olympus configuration',
'Setup DCS Olympus connectivity'
'Select installation type'
);
{ lblLocalInstall }
@ -172,9 +174,9 @@ begin
Caption := 'Select this to install DCS Olympus locally. DCS Olympus will not be reachable by external clients (i.e. browsers running on different PCs)';
end;
{ txtLocalInstall }
txtLocalInstall := TNewRadioButton.Create(Page);
with txtLocalInstall do
{ radioLocalInstall }
radioLocalInstall := TNewRadioButton.Create(Page);
with radioLocalInstall do
begin
Parent := Page.Surface;
Left := ScaleX(10);
@ -211,9 +213,9 @@ begin
Caption := 'Select this to install DCS Olympus on a dedicated server. DCS Olympus will be reachable by external clients. NOTE: to enable external connections, TCP port forwarding must be enabled on the selected ports.';
end;
{ txtServerInstall }
txtServerInstall := TNewRadioButton.Create(Page);
with txtServerInstall do
{ radioServerInstall }
radioServerInstall := TNewRadioButton.Create(Page);
with radioServerInstall do
begin
Parent := Page.Surface;
Left := ScaleX(10);
@ -223,58 +225,6 @@ begin
TabOrder := 1;
end;
{ lblClientPort }
lblClientPort := TLabel.Create(Page);
with lblClientPort do
begin
Parent := Page.Surface;
Left := ScaleX(24);
Top := ScaleY(168);
Width := ScaleX(46);
Height := ScaleY(13);
Caption := 'Webserver port';
end;
{ txtClientPort }
txtClientPort := TEdit.Create(Page);
with txtClientPort do
begin
Parent := Page.Surface;
Left := ScaleX(180);
Top := ScaleY(165);
Width := ScaleX(185);
Height := ScaleY(21);
Text := '3000';
OnKeyPress := @AcceptNumbersOnlyKeyPress;
TabOrder := 3;
end;
{ lblBackendPort }
lblBackendPort := TLabel.Create(Page);
with lblBackendPort do
begin
Parent := Page.Surface;
Left := ScaleX(24);
Top := ScaleY(198);
Width := ScaleX(46);
Height := ScaleY(13);
Caption := 'Backend port';
end;
{ txtBackendPort }
txtBackendPort := TEdit.Create(Page);
with txtBackendPort do
begin
Parent := Page.Surface;
Left := ScaleX(180);
Top := ScaleY(195);
Width := ScaleX(185);
Height := ScaleY(21);
Text := '3001';
OnKeyPress := @AcceptNumbersOnlyKeyPress;
TabOrder := 4;
end;
with Page do
begin
OnActivate := @frmAddress_Activate;
@ -289,6 +239,8 @@ end;
procedure frmPassword_Activate(Page: TWizardPage);
begin
checkKeepOld.Enabled := FileExists(ExpandConstant('{app}\Mods\Services\Olympus\olympus.json'));
checkKeepOld.Checked := FileExists(ExpandConstant('{app}\Mods\Services\Olympus\olympus.json'));
end;
function frmPassword_ShouldSkipPage(Page: TWizardPage): Boolean;
@ -303,11 +255,11 @@ end;
function frmPassword_NextButtonClick(Page: TWizardPage): Boolean;
begin
if (Trim(txtPassword.Text) <> '') and (Trim(txtBluePassword.Text) <> '') and (Trim(txtRedPassword.Text) <> '') then begin
if checkKeepOld.Checked or ((Trim(txtClientPort.Text) <> '') and (Trim(txtBackendPort.Text) <> '') and (Trim(txtPassword.Text) <> '') and (Trim(txtBluePassword.Text) <> '') and (Trim(txtRedPassword.Text) <> '')) then begin
Result := True;
end else
begin
MsgBox('All password fields must be filled to proceed.', mbInformation, MB_OK);
MsgBox('Either keep the configuration from the previous installation (if present) or fill all the fields to continue.', mbInformation, MB_OK);
Result := False;
end;
end;
@ -316,6 +268,15 @@ procedure frmPassword_CancelButtonClick(Page: TWizardPage; var Cancel, Confirm:
begin
end;
procedure checkKeepOldOnChange(Sender: TObject);
begin
txtPassword.Enabled := not checkKeepOld.Checked;
txtBluePassword.Enabled := not checkKeepOld.Checked;
txtRedPassword.Enabled := not checkKeepOld.Checked;
txtBackendPort.Enabled := not checkKeepOld.Checked;
txtClientPort.Enabled := not checkKeepOld.Checked;
end;
function frmPassword_CreatePage(PreviousPageId: Integer): Integer;
var
Page: TWizardPage;
@ -323,16 +284,40 @@ begin
Page := CreateCustomPage(
PreviousPageId,
'DCS Olympus passwords',
'Set DCS Olympus Admin and Commander passwords'
'Set DCS Olympus ports and passwords'
);
{ lblKeepOld }
lblKeepOld := TLabel.Create(Page);
with lblKeepOld do
begin
Parent := Page.Surface;
Left := ScaleX(54);
Top := ScaleY(0);
Width := ScaleX(46);
Height := ScaleY(13);
Caption := 'Keep configuration from previous installation';
end;
{ checkKeepOld }
checkKeepOld := TNewCheckBox.Create(Page);
with checkKeepOld do
begin
Parent := Page.Surface;
Left := ScaleX(24);
Top := ScaleY(0);
Width := ScaleX(46);
Height := ScaleY(13);
OnClick := @checkKeepOldOnChange;
end;
{ lblPassword }
lblPassword := TLabel.Create(Page);
with lblPassword do
begin
Parent := Page.Surface;
Left := ScaleX(24);
Top := ScaleY(28);
Top := ScaleY(38);
Width := ScaleX(46);
Height := ScaleY(13);
Caption := 'Game Master password';
@ -344,7 +329,7 @@ begin
begin
Parent := Page.Surface;
Left := ScaleX(180);
Top := ScaleY(25);
Top := ScaleY(35);
Width := ScaleX(185);
Height := ScaleY(21);
TabOrder := 2;
@ -356,7 +341,7 @@ begin
begin
Parent := Page.Surface;
Left := ScaleX(24);
Top := ScaleY(58);
Top := ScaleY(66);
Width := ScaleX(46);
Height := ScaleY(13);
Caption := 'Blue Commander password';
@ -368,7 +353,7 @@ begin
begin
Parent := Page.Surface;
Left := ScaleX(180);
Top := ScaleY(55);
Top := ScaleY(63);
Width := ScaleX(185);
Height := ScaleY(21);
TabOrder := 2;
@ -380,7 +365,7 @@ begin
begin
Parent := Page.Surface;
Left := ScaleX(24);
Top := ScaleY(88);
Top := ScaleY(94);
Width := ScaleX(46);
Height := ScaleY(13);
Caption := 'Red Commander password';
@ -392,24 +377,76 @@ begin
begin
Parent := Page.Surface;
Left := ScaleX(180);
Top := ScaleY(85);
Top := ScaleY(91);
Width := ScaleX(185);
Height := ScaleY(21);
TabOrder := 2;
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 := 3;
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 := 4;
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 using the DCS Olympus configurator. For more information, see the DCS Olympus Wiki';
Caption := 'Passwords and ports can be changed in the future by using the DCS Olympus configurator. For more information, see the DCS Olympus Wiki.';
end;
with Page do
@ -427,13 +464,13 @@ end;
procedure InitializeWizard();
begin
{this page will come after welcome page}
AddressPage := frmAddress_CreatePage(wpSelectDir);
PasswordPage:= frmPassword_CreatePage(AddressPage);
InstallationTypePage := frmInstallationType_CreatePage(wpSelectDir);
PasswordPage := frmPassword_CreatePage(InstallationTypePage);
end;
function CheckLocalInstall(): boolean;
begin
if txtLocalInstall.Checked then begin
if radioLocalInstall.Checked then begin
Result := True
end else
begin
@ -443,7 +480,17 @@ end;
function CheckServerInstall(): boolean;
begin
if txtLocalInstall.Checked then begin
if radioLocalInstall.Checked then begin
Result := False
end else
begin
Result := True
end
end;
function CheckCallConfigurator(): boolean;
begin
if checkKeepOld.Checked then begin
Result := False
end else
begin
@ -453,7 +500,7 @@ end;
function GetAddress(Value: string): string;
begin
if txtLocalInstall.Checked then begin
if radioLocalInstall.Checked then begin
Result := 'localhost'
end else
begin