mirror of
https://github.com/Pax1601/DCSOlympus.git
synced 2025-10-29 16:56:34 +00:00
More svg injection, removing stringed html from code
This commit is contained in:
2
client/src/@types/server.d.ts
vendored
2
client/src/@types/server.d.ts
vendored
@@ -8,7 +8,7 @@ interface AirbasesData {
|
||||
}
|
||||
|
||||
interface BullseyesData {
|
||||
bullseyes: {[key: string]: any},
|
||||
bullseyes: {[key: string]: {latitude: number, longitude: number, coalition: string}},
|
||||
}
|
||||
|
||||
interface LogData {
|
||||
|
||||
@@ -5,8 +5,7 @@ import { ContextMenu } from "./contextmenu";
|
||||
export class AirbaseContextMenu extends ContextMenu {
|
||||
#airbase: Airbase | null = null;
|
||||
|
||||
constructor(id: string)
|
||||
{
|
||||
constructor(id: string) {
|
||||
super(id);
|
||||
document.addEventListener("contextMenuSpawnAirbase", (e: any) => {
|
||||
this.showSpawnMenu();
|
||||
@@ -19,8 +18,7 @@ export class AirbaseContextMenu extends ContextMenu {
|
||||
})
|
||||
}
|
||||
|
||||
setAirbase(airbase: Airbase)
|
||||
{
|
||||
setAirbase(airbase: Airbase) {
|
||||
this.#airbase = airbase;
|
||||
this.setName(airbase.getName());
|
||||
this.setProperties(airbase.getProperties());
|
||||
@@ -29,24 +27,21 @@ export class AirbaseContextMenu extends ContextMenu {
|
||||
this.enableLandButton(getUnitsManager().getSelectedUnitsType() === "Aircraft" && (getUnitsManager().getSelectedUnitsCoalition() === airbase.getCoalition() || airbase.getCoalition() === "neutral"))
|
||||
}
|
||||
|
||||
setName(airbaseName: string)
|
||||
{
|
||||
setName(airbaseName: string) {
|
||||
var nameDiv = <HTMLElement>this.getContainer()?.querySelector("#airbase-name");
|
||||
if (nameDiv != null)
|
||||
nameDiv.innerText = airbaseName;
|
||||
nameDiv.innerText = airbaseName;
|
||||
}
|
||||
|
||||
setProperties(airbaseProperties: string[])
|
||||
{
|
||||
setProperties(airbaseProperties: string[]) {
|
||||
this.getContainer()?.querySelector("#airbase-properties")?.replaceChildren(...airbaseProperties.map((property: string) => {
|
||||
var div = document.createElement("div");
|
||||
div.innerText = property;
|
||||
return div;
|
||||
}), );
|
||||
}),);
|
||||
}
|
||||
|
||||
setParkings(airbaseParkings: string[])
|
||||
{
|
||||
setParkings(airbaseParkings: string[]) {
|
||||
this.getContainer()?.querySelector("#airbase-parking")?.replaceChildren(...airbaseParkings.map((parking: string) => {
|
||||
var div = document.createElement("div");
|
||||
div.innerText = parking;
|
||||
@@ -54,22 +49,18 @@ export class AirbaseContextMenu extends ContextMenu {
|
||||
}));
|
||||
}
|
||||
|
||||
setCoalition(coalition: string)
|
||||
{
|
||||
setCoalition(coalition: string) {
|
||||
(<HTMLElement>this.getContainer()?.querySelector("#spawn-airbase-aircraft-button")).dataset.activeCoalition = coalition;
|
||||
}
|
||||
|
||||
enableLandButton(enableLandButton: boolean)
|
||||
{
|
||||
enableLandButton(enableLandButton: boolean) {
|
||||
this.getContainer()?.querySelector("#land-here-button")?.classList.toggle("hide", !enableLandButton);
|
||||
}
|
||||
|
||||
showSpawnMenu()
|
||||
{
|
||||
if (this.#airbase != null)
|
||||
{
|
||||
showSpawnMenu() {
|
||||
if (this.#airbase != null) {
|
||||
setActiveCoalition(this.#airbase.getCoalition());
|
||||
getMap().showMapContextMenu({originalEvent: {x: this.getX(), y: this.getY(), latlng: this.getLatLng()}});
|
||||
getMap().showMapContextMenu({ originalEvent: { x: this.getX(), y: this.getY(), latlng: this.getLatLng() } });
|
||||
getMap().getMapContextMenu().hideUpperBar();
|
||||
getMap().getMapContextMenu().showSubMenu("aircraft");
|
||||
getMap().getMapContextMenu().setAirbaseName(this.#airbase.getName());
|
||||
|
||||
@@ -23,28 +23,23 @@ export class ContextMenu {
|
||||
this.#container?.classList.toggle("hide", true);
|
||||
}
|
||||
|
||||
getContainer()
|
||||
{
|
||||
getContainer() {
|
||||
return this.#container;
|
||||
}
|
||||
|
||||
getLatLng()
|
||||
{
|
||||
getLatLng() {
|
||||
return this.#latlng;
|
||||
}
|
||||
|
||||
getX()
|
||||
{
|
||||
getX() {
|
||||
return this.#x;
|
||||
}
|
||||
|
||||
getY()
|
||||
{
|
||||
getY() {
|
||||
return this.#y;
|
||||
}
|
||||
|
||||
clip()
|
||||
{
|
||||
clip() {
|
||||
if (this.#container != null) {
|
||||
if (this.#x + this.#container.offsetWidth < window.innerWidth)
|
||||
this.#container.style.left = this.#x + "px";
|
||||
|
||||
@@ -7,17 +7,16 @@ export class Dropdown {
|
||||
#optionsList: string[] = [];
|
||||
#index: number = 0;
|
||||
|
||||
constructor(ID: string, callback: CallableFunction, options: string[] | null = null)
|
||||
{
|
||||
this.#element = <HTMLElement>document.getElementById(ID);
|
||||
this.#options = <HTMLElement>this.#element.querySelector(".ol-select-options");
|
||||
this.#value = <HTMLElement>this.#element.querySelector(".ol-select-value");
|
||||
constructor(ID: string, callback: CallableFunction, options: string[] | null = null) {
|
||||
this.#element = <HTMLElement>document.getElementById(ID);
|
||||
this.#options = <HTMLElement>this.#element.querySelector(".ol-select-options");
|
||||
this.#value = <HTMLElement>this.#element.querySelector(".ol-select-value");
|
||||
this.#defaultValue = this.#value.innerText;
|
||||
this.#callback = callback;
|
||||
this.#callback = callback;
|
||||
|
||||
if (options != null) {
|
||||
this.setOptions(options);
|
||||
}
|
||||
}
|
||||
|
||||
this.#value.addEventListener("click", (ev) => {
|
||||
this.#element.classList.toggle("is-open");
|
||||
@@ -31,11 +30,10 @@ export class Dropdown {
|
||||
}
|
||||
});
|
||||
|
||||
this.#options.classList.add( "ol-scrollable" );
|
||||
this.#options.classList.add("ol-scrollable");
|
||||
}
|
||||
|
||||
setOptions(optionsList: string[])
|
||||
{
|
||||
setOptions(optionsList: string[]) {
|
||||
this.#optionsList = optionsList;
|
||||
this.#options.replaceChildren(...optionsList.map((option: string, idx: number) => {
|
||||
var div = document.createElement("div");
|
||||
@@ -48,7 +46,9 @@ export class Dropdown {
|
||||
|
||||
button.addEventListener("click", (e: MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
this.#value.innerHTML = `<div class = "ol-ellipsed"> ${option} </div>`;
|
||||
this.#value = document.createElement("div");
|
||||
this.#value.classList.add("ol-ellipsed");
|
||||
this.#value.innerText = option;
|
||||
this.#close();
|
||||
this.#callback(option, e);
|
||||
this.#index = idx;
|
||||
@@ -57,19 +57,15 @@ export class Dropdown {
|
||||
}));
|
||||
}
|
||||
|
||||
selectText( text:string ) {
|
||||
|
||||
const index = [].slice.call( this.#options.children ).findIndex( ( opt:Element ) => opt.querySelector( "button" )?.innerText === text );
|
||||
if ( index > -1 ) {
|
||||
this.selectValue( index );
|
||||
selectText(text: string) {
|
||||
const index = [].slice.call(this.#options.children).findIndex((opt: Element) => opt.querySelector("button")?.innerText === text);
|
||||
if (index > -1) {
|
||||
this.selectValue(index);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
selectValue(idx: number)
|
||||
{
|
||||
if (idx < this.#optionsList.length)
|
||||
{
|
||||
selectValue(idx: number) {
|
||||
if (idx < this.#optionsList.length) {
|
||||
var option = this.#optionsList[idx];
|
||||
this.#value.innerHTML = `<div class = "ol-ellipsed"> ${option} </div>`;
|
||||
this.#index = idx;
|
||||
@@ -91,8 +87,8 @@ export class Dropdown {
|
||||
}
|
||||
|
||||
setValue(value: string) {
|
||||
var index = this.#optionsList.findIndex((option) => {return option === value});
|
||||
if (index > -1)
|
||||
var index = this.#optionsList.findIndex((option) => { return option === value });
|
||||
if (index > -1)
|
||||
this.selectValue(index);
|
||||
}
|
||||
|
||||
@@ -102,21 +98,21 @@ export class Dropdown {
|
||||
|
||||
#clip() {
|
||||
const options = this.#options;
|
||||
const bounds = options.getBoundingClientRect();
|
||||
this.#element.dataset.position = ( bounds.bottom > window.innerHeight ) ? "top" : "";
|
||||
const bounds = options.getBoundingClientRect();
|
||||
this.#element.dataset.position = (bounds.bottom > window.innerHeight) ? "top" : "";
|
||||
}
|
||||
|
||||
#close() {
|
||||
this.#element.classList.remove( "is-open" );
|
||||
this.#element.classList.remove("is-open");
|
||||
this.#element.dataset.position = "";
|
||||
}
|
||||
|
||||
#open() {
|
||||
this.#element.classList.add( "is-open" );
|
||||
this.#element.classList.add("is-open");
|
||||
}
|
||||
|
||||
#toggle() {
|
||||
if ( this.#element.classList.contains( "is-open" ) ) {
|
||||
if (this.#element.classList.contains("is-open")) {
|
||||
this.#close();
|
||||
} else {
|
||||
this.#open();
|
||||
|
||||
@@ -41,8 +41,7 @@ export class MapContextMenu extends ContextMenu {
|
||||
document.addEventListener("contextMenuDeployAircraft", () => {
|
||||
this.hide();
|
||||
this.#spawnOptions.coalition = getActiveCoalition();
|
||||
if (this.#spawnOptions)
|
||||
{
|
||||
if (this.#spawnOptions) {
|
||||
getMap().addTemporaryMarker(this.#spawnOptions.latlng);
|
||||
spawnAircraft(this.#spawnOptions);
|
||||
}
|
||||
@@ -51,8 +50,7 @@ export class MapContextMenu extends ContextMenu {
|
||||
document.addEventListener("contextMenuDeployGroundUnit", () => {
|
||||
this.hide();
|
||||
this.#spawnOptions.coalition = getActiveCoalition();
|
||||
if (this.#spawnOptions)
|
||||
{
|
||||
if (this.#spawnOptions) {
|
||||
getMap().addTemporaryMarker(this.#spawnOptions.latlng);
|
||||
spawnGroundUnit(this.#spawnOptions);
|
||||
}
|
||||
@@ -189,10 +187,10 @@ export class MapContextMenu extends ContextMenu {
|
||||
this.#spawnOptions.role = role;
|
||||
this.#resetGroundUnitType();
|
||||
|
||||
const types = groundUnitsDatabase.getByRole(role).map((blueprint) => { return blueprint.label } );
|
||||
const types = groundUnitsDatabase.getByRole(role).map((blueprint) => { return blueprint.label });
|
||||
types.sort();
|
||||
|
||||
this.#groundUnitTypeDropdown.setOptions( types );
|
||||
this.#groundUnitTypeDropdown.setOptions(types);
|
||||
this.#groundUnitTypeDropdown.selectValue(0);
|
||||
this.clip();
|
||||
}
|
||||
@@ -205,8 +203,8 @@ export class MapContextMenu extends ContextMenu {
|
||||
|
||||
const roles = groundUnitsDatabase.getRoles();
|
||||
roles.sort();
|
||||
|
||||
this.#groundUnitRoleDropdown.setOptions( roles );
|
||||
|
||||
this.#groundUnitRoleDropdown.setOptions(roles);
|
||||
this.clip();
|
||||
}
|
||||
|
||||
|
||||
@@ -23,8 +23,7 @@ export class Slider {
|
||||
if (this.#container != null) {
|
||||
this.#display = this.#container.style.display;
|
||||
this.#slider = <HTMLInputElement>this.#container.querySelector("input");
|
||||
if (this.#slider != null)
|
||||
{
|
||||
if (this.#slider != null) {
|
||||
this.#slider.addEventListener("input", (e: any) => this.#onInput());
|
||||
this.#slider.addEventListener("mousedown", (e: any) => this.#onStart());
|
||||
this.#slider.addEventListener("mouseup", (e: any) => this.#onFinalize());
|
||||
@@ -33,93 +32,77 @@ export class Slider {
|
||||
}
|
||||
}
|
||||
|
||||
show()
|
||||
{
|
||||
show() {
|
||||
if (this.#container != null)
|
||||
this.#container.style.display = this.#display;
|
||||
}
|
||||
|
||||
hide()
|
||||
{
|
||||
hide() {
|
||||
if (this.#container != null)
|
||||
this.#container.style.display = 'none';
|
||||
}
|
||||
|
||||
setActive(newActive: boolean)
|
||||
{
|
||||
if (this.#container && !this.#dragged)
|
||||
{
|
||||
setActive(newActive: boolean) {
|
||||
if (this.#container && !this.#dragged) {
|
||||
this.#container.classList.toggle("active", newActive);
|
||||
if (!newActive && this.#valueText != null)
|
||||
this.#valueText.innerText = "Mixed values";
|
||||
}
|
||||
}
|
||||
|
||||
setMinMax(newMinValue: number, newMaxValue: number)
|
||||
{
|
||||
setMinMax(newMinValue: number, newMaxValue: number) {
|
||||
this.#minValue = newMinValue;
|
||||
this.#maxValue = newMaxValue;
|
||||
this.#updateMax();
|
||||
}
|
||||
|
||||
setIncrement(newIncrement: number)
|
||||
{
|
||||
setIncrement(newIncrement: number) {
|
||||
this.#increment = newIncrement;
|
||||
this.#updateMax();
|
||||
}
|
||||
|
||||
setValue(newValue: number)
|
||||
{
|
||||
setValue(newValue: number) {
|
||||
// Disable value setting if the user is dragging the element
|
||||
if (!this.#dragged)
|
||||
{
|
||||
if (!this.#dragged) {
|
||||
this.#value = newValue;
|
||||
if (this.#slider != null)
|
||||
this.#slider.value = String((newValue - this.#minValue) / (this.#maxValue - this.#minValue) * parseFloat(this.#slider.max));
|
||||
this.#slider.value = String((newValue - this.#minValue) / (this.#maxValue - this.#minValue) * parseFloat(this.#slider.max));
|
||||
this.#onValue()
|
||||
}
|
||||
}
|
||||
|
||||
getValue()
|
||||
{
|
||||
getValue() {
|
||||
return this.#value;
|
||||
}
|
||||
|
||||
getDragged()
|
||||
{
|
||||
getDragged() {
|
||||
return this.#dragged;
|
||||
}
|
||||
|
||||
#updateMax()
|
||||
{
|
||||
#updateMax() {
|
||||
var oldValue = this.getValue();
|
||||
if (this.#slider != null)
|
||||
this.#slider.max = String((this.#maxValue - this.#minValue) / this.#increment);
|
||||
this.setValue(oldValue);
|
||||
}
|
||||
|
||||
#onValue()
|
||||
{
|
||||
#onValue() {
|
||||
if (this.#valueText != null && this.#slider != null)
|
||||
this.#valueText.innerHTML = this.#minValue + Math.round(parseFloat(this.#slider.value) / parseFloat(this.#slider.max) * (this.#maxValue - this.#minValue)) + this.#unit
|
||||
this.#valueText.innerHTML = this.#minValue + Math.round(parseFloat(this.#slider.value) / parseFloat(this.#slider.max) * (this.#maxValue - this.#minValue)) + this.#unit
|
||||
this.setActive(true);
|
||||
}
|
||||
|
||||
#onInput()
|
||||
{
|
||||
#onInput() {
|
||||
this.#onValue();
|
||||
}
|
||||
|
||||
#onStart()
|
||||
{
|
||||
#onStart() {
|
||||
this.#dragged = true;
|
||||
}
|
||||
|
||||
#onFinalize()
|
||||
{
|
||||
#onFinalize() {
|
||||
this.#dragged = false;
|
||||
if (this.#slider != null)
|
||||
{
|
||||
if (this.#slider != null) {
|
||||
this.#value = this.#minValue + parseFloat(this.#slider.value) / parseFloat(this.#slider.max) * (this.#maxValue - this.#minValue);
|
||||
this.#callback(this.getValue());
|
||||
}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import { getUnitsManager } from "..";
|
||||
import { deg2rad } from "../other/utils";
|
||||
import { ContextMenu } from "./contextmenu";
|
||||
|
||||
@@ -10,21 +9,19 @@ export class UnitContextMenu extends ContextMenu {
|
||||
|
||||
document.addEventListener("applyCustomFormation", () => {
|
||||
var dialog = document.getElementById("custom-formation-dialog");
|
||||
if (dialog)
|
||||
{
|
||||
if (dialog) {
|
||||
dialog.classList.add("hide");
|
||||
var clock = 1;
|
||||
while (clock < 8)
|
||||
{
|
||||
if ((<HTMLInputElement> dialog.querySelector(`#formation-${clock}`)).checked)
|
||||
while (clock < 8) {
|
||||
if ((<HTMLInputElement>dialog.querySelector(`#formation-${clock}`)).checked)
|
||||
break
|
||||
clock++;
|
||||
}
|
||||
var angleDeg = 360 - (clock - 1) * 45;
|
||||
var angleRad = deg2rad(angleDeg);
|
||||
var distance = parseInt((<HTMLInputElement> dialog.querySelector(`#distance`)?.querySelector("input")).value) * 0.3048;
|
||||
var upDown = parseInt((<HTMLInputElement> dialog.querySelector(`#up-down`)?.querySelector("input")).value) * 0.3048;
|
||||
|
||||
var distance = parseInt((<HTMLInputElement>dialog.querySelector(`#distance`)?.querySelector("input")).value) * 0.3048;
|
||||
var upDown = parseInt((<HTMLInputElement>dialog.querySelector(`#up-down`)?.querySelector("input")).value) * 0.3048;
|
||||
|
||||
// X: front-rear, positive front
|
||||
// Y: top-bottom, positive top
|
||||
// Z: left-right, positive right
|
||||
@@ -34,7 +31,7 @@ export class UnitContextMenu extends ContextMenu {
|
||||
var z = distance * Math.sin(angleRad);
|
||||
|
||||
if (this.#customFormationCallback)
|
||||
this.#customFormationCallback({"x": x, "y": y, "z": z})
|
||||
this.#customFormationCallback({ "x": x, "y": y, "z": z })
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -43,10 +40,8 @@ export class UnitContextMenu extends ContextMenu {
|
||||
this.#customFormationCallback = callback;
|
||||
}
|
||||
|
||||
setOptions(options: {[key: string]: string}, callback: CallableFunction)
|
||||
{
|
||||
this.getContainer()?.replaceChildren(...Object.keys(options).map((option: string, idx: number) =>
|
||||
{
|
||||
setOptions(options: { [key: string]: string }, callback: CallableFunction) {
|
||||
this.getContainer()?.replaceChildren(...Object.keys(options).map((option: string, idx: number) => {
|
||||
var button = document.createElement("button");
|
||||
button.innerHTML = options[option];
|
||||
button.addEventListener("click", () => callback(option));
|
||||
|
||||
@@ -23,13 +23,13 @@ class FeatureSwitch {
|
||||
userPreference;
|
||||
|
||||
|
||||
constructor( config:FeatureSwitchInterface ) {
|
||||
constructor(config: FeatureSwitchInterface) {
|
||||
|
||||
this.defaultEnabled = config.defaultEnabled;
|
||||
this.label = config.label;
|
||||
this.masterSwitch = config.masterSwitch;
|
||||
this.name = config.name;
|
||||
this.onEnabled = config.onEnabled;
|
||||
this.label = config.label;
|
||||
this.masterSwitch = config.masterSwitch;
|
||||
this.name = config.name;
|
||||
this.onEnabled = config.onEnabled;
|
||||
|
||||
this.userPreference = this.getUserPreference();
|
||||
|
||||
@@ -38,16 +38,16 @@ class FeatureSwitch {
|
||||
|
||||
getUserPreference() {
|
||||
|
||||
let preferences = JSON.parse( localStorage.getItem( "featureSwitches" ) || "{}" );
|
||||
let preferences = JSON.parse(localStorage.getItem("featureSwitches") || "{}");
|
||||
|
||||
return ( preferences.hasOwnProperty( this.name ) ) ? preferences[ this.name ] : this.defaultEnabled;
|
||||
return (preferences.hasOwnProperty(this.name)) ? preferences[this.name] : this.defaultEnabled;
|
||||
|
||||
}
|
||||
|
||||
|
||||
isEnabled() {
|
||||
|
||||
if ( !this.masterSwitch ) {
|
||||
if (!this.masterSwitch) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ class FeatureSwitch {
|
||||
|
||||
export class FeatureSwitches {
|
||||
|
||||
#featureSwitches:FeatureSwitch[] = [
|
||||
#featureSwitches: FeatureSwitch[] = [
|
||||
|
||||
new FeatureSwitch({
|
||||
"defaultEnabled": false,
|
||||
@@ -66,7 +66,7 @@ export class FeatureSwitches {
|
||||
"masterSwitch": true,
|
||||
"name": "aic"
|
||||
}),
|
||||
|
||||
|
||||
new FeatureSwitch({
|
||||
"defaultEnabled": false,
|
||||
"label": "AI Formations",
|
||||
@@ -74,21 +74,21 @@ export class FeatureSwitches {
|
||||
"name": "ai-formations",
|
||||
"removeArtifactsIfDisabled": false
|
||||
}),
|
||||
|
||||
|
||||
new FeatureSwitch({
|
||||
"defaultEnabled": false,
|
||||
"label": "ATC",
|
||||
"masterSwitch": true,
|
||||
"name": "atc"
|
||||
}),
|
||||
|
||||
|
||||
new FeatureSwitch({
|
||||
"defaultEnabled": false,
|
||||
"label": "Force show unit control panel",
|
||||
"masterSwitch": true,
|
||||
"name": "forceShowUnitControlPanel"
|
||||
}),
|
||||
|
||||
|
||||
new FeatureSwitch({
|
||||
"defaultEnabled": true,
|
||||
"label": "Show splash screen",
|
||||
@@ -108,41 +108,41 @@ export class FeatureSwitches {
|
||||
}
|
||||
|
||||
|
||||
getSwitch( switchName:string ) {
|
||||
getSwitch(switchName: string) {
|
||||
|
||||
return this.#featureSwitches.find( featureSwitch => featureSwitch.name === switchName );
|
||||
return this.#featureSwitches.find(featureSwitch => featureSwitch.name === switchName);
|
||||
|
||||
}
|
||||
|
||||
|
||||
#testSwitches() {
|
||||
for ( const featureSwitch of this.#featureSwitches ) {
|
||||
if ( featureSwitch.isEnabled() ) {
|
||||
if ( typeof featureSwitch.onEnabled === "function" ) {
|
||||
for (const featureSwitch of this.#featureSwitches) {
|
||||
if (featureSwitch.isEnabled()) {
|
||||
if (typeof featureSwitch.onEnabled === "function") {
|
||||
featureSwitch.onEnabled();
|
||||
}
|
||||
} else {
|
||||
document.querySelectorAll( "[data-feature-switch='" + featureSwitch.name + "']" ).forEach( el => {
|
||||
if ( featureSwitch.removeArtifactsIfDisabled === false ) {
|
||||
document.querySelectorAll("[data-feature-switch='" + featureSwitch.name + "']").forEach(el => {
|
||||
if (featureSwitch.removeArtifactsIfDisabled === false) {
|
||||
el.remove();
|
||||
} else {
|
||||
el.classList.add( "hide" );
|
||||
el.classList.add("hide");
|
||||
}
|
||||
});
|
||||
}
|
||||
document.body.classList.toggle( "feature-" + featureSwitch.name, featureSwitch.isEnabled() );
|
||||
document.body.classList.toggle("feature-" + featureSwitch.name, featureSwitch.isEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
savePreferences() {
|
||||
|
||||
let preferences:any = {};
|
||||
let preferences: any = {};
|
||||
|
||||
for ( const featureSwitch of this.#featureSwitches ) {
|
||||
preferences[ featureSwitch.name ] = featureSwitch.isEnabled();
|
||||
for (const featureSwitch of this.#featureSwitches) {
|
||||
preferences[featureSwitch.name] = featureSwitch.isEnabled();
|
||||
}
|
||||
|
||||
localStorage.setItem( "featureSwitches", JSON.stringify( preferences ) );
|
||||
localStorage.setItem("featureSwitches", JSON.stringify(preferences));
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
export abstract class ToggleableFeature {
|
||||
|
||||
#status:boolean = false;
|
||||
#status: boolean = false;
|
||||
|
||||
|
||||
constructor( defaultStatus:boolean ) {
|
||||
constructor(defaultStatus: boolean) {
|
||||
|
||||
this.#status = defaultStatus;
|
||||
|
||||
@@ -12,17 +12,17 @@ export abstract class ToggleableFeature {
|
||||
}
|
||||
|
||||
|
||||
getStatus() : boolean {
|
||||
getStatus(): boolean {
|
||||
return this.#status;
|
||||
}
|
||||
|
||||
|
||||
protected onStatusUpdate() {}
|
||||
protected onStatusUpdate() { }
|
||||
|
||||
|
||||
toggleStatus( force?:boolean ) : void {
|
||||
toggleStatus(force?: boolean): void {
|
||||
|
||||
if ( force ) {
|
||||
if (force) {
|
||||
this.#status = force;
|
||||
} else {
|
||||
this.#status = !this.#status;
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import { Map } from 'leaflet';
|
||||
import { Handler} from 'leaflet';
|
||||
import { Handler } from 'leaflet';
|
||||
import { Util } from 'leaflet';
|
||||
import { DomUtil } from 'leaflet';
|
||||
import { DomEvent } from 'leaflet';
|
||||
import { LatLngBounds } from 'leaflet';
|
||||
import { Bounds } from 'leaflet';
|
||||
import { Bounds } from 'leaflet';
|
||||
|
||||
export var BoxSelect = Handler.extend({
|
||||
initialize: function (map: Map) {
|
||||
@@ -82,12 +82,12 @@ export var BoxSelect = Handler.extend({
|
||||
this._point = this._map.mouseEventToContainerPoint(e);
|
||||
|
||||
var bounds = new Bounds(this._point, this._startPoint),
|
||||
size = bounds.getSize();
|
||||
size = bounds.getSize();
|
||||
|
||||
if (bounds.min != undefined)
|
||||
DomUtil.setPosition(this._box, bounds.min);
|
||||
|
||||
this._box.style.width = size.x + 'px';
|
||||
this._box.style.width = size.x + 'px';
|
||||
this._box.style.height = size.y + 'px';
|
||||
},
|
||||
|
||||
@@ -113,7 +113,7 @@ export var BoxSelect = Handler.extend({
|
||||
if ((e.which !== 1) && (e.button !== 0)) { return; }
|
||||
|
||||
this._finish();
|
||||
|
||||
|
||||
if (!this._moved) { return; }
|
||||
// Postpone to next JS tick so internal click event handling
|
||||
// still see it as "moved".
|
||||
@@ -121,8 +121,8 @@ export var BoxSelect = Handler.extend({
|
||||
var bounds = new LatLngBounds(
|
||||
this._map.containerPointToLatLng(this._startPoint),
|
||||
this._map.containerPointToLatLng(this._point));
|
||||
|
||||
this._map.fire('selectionend', {selectionBounds: bounds});
|
||||
|
||||
this._map.fire('selectionend', { selectionBounds: bounds });
|
||||
},
|
||||
|
||||
_onKeyDown: function (e: any) {
|
||||
|
||||
12
client/src/map/clickableminimap.ts
Normal file
12
client/src/map/clickableminimap.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { MiniMap, MiniMapOptions } from "leaflet-control-mini-map";
|
||||
|
||||
export class ClickableMiniMap extends MiniMap {
|
||||
constructor(layer: L.TileLayer | L.LayerGroup, options?: MiniMapOptions) {
|
||||
super(layer, options);
|
||||
}
|
||||
|
||||
getMap() {
|
||||
//@ts-ignore needed to access not exported member. A bit of a hack, required to access click events //TODO: fix me
|
||||
return this._miniMap;
|
||||
}
|
||||
}
|
||||
24
client/src/map/custommarker.ts
Normal file
24
client/src/map/custommarker.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { Map, Marker } from "leaflet";
|
||||
import { MarkerOptions } from "leaflet";
|
||||
import { LatLngExpression } from "leaflet";
|
||||
|
||||
export class CustomMarker extends Marker {
|
||||
constructor(latlng: LatLngExpression, options?: MarkerOptions) {
|
||||
super(latlng, options);
|
||||
}
|
||||
|
||||
onAdd(map: Map): this {
|
||||
super.onAdd(map);
|
||||
this.createIcon();
|
||||
return this;
|
||||
}
|
||||
|
||||
onRemove(map: Map): this {
|
||||
super.onRemove(map);
|
||||
return this;
|
||||
}
|
||||
|
||||
createIcon() {
|
||||
/* Overloaded by child classes */
|
||||
}
|
||||
}
|
||||
15
client/src/map/destinationpreviewmarker.ts
Normal file
15
client/src/map/destinationpreviewmarker.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { DivIcon } from "leaflet";
|
||||
import { CustomMarker } from "./custommarker";
|
||||
|
||||
export class DestinationPreviewMarker extends CustomMarker {
|
||||
createIcon() {
|
||||
this.setIcon(new DivIcon({
|
||||
iconSize: [52, 52],
|
||||
iconAnchor: [26, 26],
|
||||
className: "leaflet-destination-preview"
|
||||
}));
|
||||
var el = document.createElement("div");
|
||||
el.classList.add("ol-destination-preview-icon");
|
||||
this.getElement()?.appendChild(el);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,4 @@
|
||||
import * as L from "leaflet"
|
||||
import { MiniMap, MiniMapOptions } from "leaflet-control-mini-map";
|
||||
|
||||
import { getUnitsManager } from "..";
|
||||
import { BoxSelect } from "./boxselect";
|
||||
import { MapContextMenu, SpawnOptions } from "../controls/mapcontextmenu";
|
||||
@@ -10,40 +8,20 @@ import { Dropdown } from "../controls/dropdown";
|
||||
import { Airbase } from "../missionhandler/airbase";
|
||||
import { Unit } from "../units/unit";
|
||||
import { bearing } from "../other/utils";
|
||||
|
||||
// TODO a bit of a hack, this module is provided as pure javascript only
|
||||
require("../../node_modules/leaflet.nauticscale/dist/leaflet.nauticscale.js")
|
||||
|
||||
export const IDLE = "IDLE";
|
||||
export const MOVE_UNIT = "MOVE_UNIT";
|
||||
import { DestinationPreviewMarker } from "./destinationpreviewmarker";
|
||||
import { TemporaryUnitMarker } from "./temporaryunitmarker";
|
||||
import { ClickableMiniMap } from "./clickableminimap";
|
||||
import { SVGInjector } from '@tanem/svg-injector'
|
||||
|
||||
L.Map.addInitHook('addHandler', 'boxSelect', BoxSelect);
|
||||
|
||||
var temporaryIcon = new L.Icon({
|
||||
iconUrl: 'images/icon-temporary.png',
|
||||
iconSize: [52, 52],
|
||||
iconAnchor: [26, 26]
|
||||
});
|
||||
// TODO would be nice to convert to ts
|
||||
require("../../public/javascripts/leaflet.nauticscale.js")
|
||||
|
||||
var destinationPreviewIcon = new L.DivIcon({
|
||||
html: `<div class="ol-destination-preview-icon"></div>`,
|
||||
iconSize: [52, 52],
|
||||
iconAnchor: [26, 26],
|
||||
className: "ol-destination-preview"
|
||||
})
|
||||
|
||||
const visibilityControls: string[] = ["human", "dcs", "aircraft", "groundunit-sam", "groundunit-other", "navyunit", "airbase"];
|
||||
|
||||
export class ClickableMiniMap extends MiniMap {
|
||||
constructor(layer: L.TileLayer | L.LayerGroup, options?: MiniMapOptions) {
|
||||
super(layer, options);
|
||||
}
|
||||
|
||||
getMap() {
|
||||
//@ts-ignore needed to access not exported member. A bit of a hack, required to access click events
|
||||
return this._miniMap;
|
||||
}
|
||||
}
|
||||
/* Map constants */
|
||||
export const IDLE = "IDLE";
|
||||
export const MOVE_UNIT = "MOVE_UNIT";
|
||||
export const visibilityControls: string[] = ["human", "dcs", "aircraft", "groundunit-sam", "groundunit-other", "navyunit", "airbase"];
|
||||
|
||||
export class Map extends L.Map {
|
||||
#state: string;
|
||||
@@ -107,19 +85,21 @@ export class Map extends L.Map {
|
||||
this.on('mousedown', (e: any) => this.#onMouseDown(e));
|
||||
this.on('mouseup', (e: any) => this.#onMouseUp(e));
|
||||
this.on('mousemove', (e: any) => this.#onMouseMove(e));
|
||||
this.on('keydown', (e: any) => this.#updateDestinationPreview(e));
|
||||
this.on('keyup', (e: any) => this.#updateDestinationPreview(e));
|
||||
|
||||
this.on('keydown', (e: any) => this.#updateDestinationPreview(e));
|
||||
this.on('keyup', (e: any) => this.#updateDestinationPreview(e));
|
||||
|
||||
/* Event listeners */
|
||||
document.addEventListener("toggleCoalitionVisibility", (ev: CustomEventInit) => {
|
||||
ev.detail._element.classList.toggle("off");
|
||||
document.body.toggleAttribute("data-hide-" + ev.detail.coalition);
|
||||
const el = ev.detail._element;
|
||||
el?.classList.toggle("off");
|
||||
getUnitsManager().setHiddenType(ev.detail.coalition, (el?.currentTarget as HTMLElement)?.classList.contains("off"));
|
||||
Object.values(getUnitsManager().getUnits()).forEach((unit: Unit) => unit.updateVisibility());
|
||||
});
|
||||
|
||||
document.addEventListener("toggleUnitVisibility", (ev: CustomEventInit) => {
|
||||
ev.detail._element.classList.toggle("off");
|
||||
document.body.toggleAttribute("data-hide-" + ev.detail.category);
|
||||
const el = ev.detail._element;
|
||||
el?.classList.toggle("off");
|
||||
getUnitsManager().setHiddenType(ev.detail.type, !el?.classList.contains("off"));
|
||||
Object.values(getUnitsManager().getUnits()).forEach((unit: Unit) => unit.updateVisibility());
|
||||
});
|
||||
|
||||
@@ -129,17 +109,14 @@ export class Map extends L.Map {
|
||||
});
|
||||
|
||||
/* Pan interval */
|
||||
this.#panInterval = window.setInterval(() => {
|
||||
this.panBy(new L.Point( ((this.#panLeft? -1: 0) + (this.#panRight? 1: 0)) * this.#deafultPanDelta,
|
||||
((this.#panUp? -1: 0) + (this.#panDown? 1: 0)) * this.#deafultPanDelta));
|
||||
this.#panInterval = window.setInterval(() => {
|
||||
this.panBy(new L.Point(((this.#panLeft ? -1 : 0) + (this.#panRight ? 1 : 0)) * this.#deafultPanDelta,
|
||||
((this.#panUp ? -1 : 0) + (this.#panDown ? 1 : 0)) * this.#deafultPanDelta));
|
||||
}, 20);
|
||||
|
||||
/* Option buttons */
|
||||
this.#optionButtons["visibility"] = visibilityControls.map((option: string, index: number) => {
|
||||
return this.#createOptionButton(option, `visibility/${option.toLowerCase()}.svg`, "", (e: any) => {
|
||||
getUnitsManager().setHiddenType(option, (e?.currentTarget as HTMLElement)?.classList.contains("off"));
|
||||
(e?.currentTarget as HTMLElement)?.classList.toggle("off");
|
||||
});
|
||||
return this.#createOptionButton(option, `visibility/${option.toLowerCase()}.svg`, "", "toggleUnitVisibility", `{"type": "${option}"}`);
|
||||
});
|
||||
document.querySelector("#unit-visibility-control")?.append(...this.#optionButtons["visibility"]);
|
||||
}
|
||||
@@ -217,10 +194,10 @@ export class Map extends L.Map {
|
||||
})
|
||||
this.#destinationPreviewMarkers = [];
|
||||
|
||||
if (getUnitsManager().getSelectedUnits({excludeHumans: true}).length < 20) {
|
||||
if (getUnitsManager().getSelectedUnits({ excludeHumans: true }).length < 20) {
|
||||
/* Create the unit destination preview markers */
|
||||
this.#destinationPreviewMarkers = getUnitsManager().getSelectedUnits({excludeHumans: true}).map((unit: Unit) => {
|
||||
var marker = new L.Marker(this.getMouseCoordinates(), {icon: destinationPreviewIcon, interactive: false});
|
||||
this.#destinationPreviewMarkers = getUnitsManager().getSelectedUnits({ excludeHumans: true }).map((unit: Unit) => {
|
||||
var marker = new DestinationPreviewMarker(this.getMouseCoordinates());
|
||||
marker.addTo(this);
|
||||
return marker;
|
||||
})
|
||||
@@ -352,7 +329,7 @@ export class Map extends L.Map {
|
||||
}
|
||||
|
||||
handleMapPanning(e: any) {
|
||||
if (e.type === "keyup"){
|
||||
if (e.type === "keyup") {
|
||||
switch (e.code) {
|
||||
case "KeyA":
|
||||
case "ArrowLeft":
|
||||
@@ -372,9 +349,8 @@ export class Map extends L.Map {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (e.code)
|
||||
{
|
||||
else {
|
||||
switch (e.code) {
|
||||
case 'KeyA':
|
||||
case 'ArrowLeft':
|
||||
this.#panLeft = true;
|
||||
@@ -396,7 +372,7 @@ export class Map extends L.Map {
|
||||
}
|
||||
|
||||
addTemporaryMarker(latlng: L.LatLng) {
|
||||
var marker = new L.Marker(latlng, {icon: temporaryIcon});
|
||||
var marker = new TemporaryUnitMarker(latlng);
|
||||
marker.addTo(this);
|
||||
this.#temporaryMarkers.push(marker);
|
||||
}
|
||||
@@ -413,8 +389,7 @@ export class Map extends L.Map {
|
||||
i = idx;
|
||||
}
|
||||
});
|
||||
if (closest)
|
||||
{
|
||||
if (closest) {
|
||||
this.removeLayer(closest);
|
||||
delete this.#temporaryMarkers[i];
|
||||
}
|
||||
@@ -449,7 +424,7 @@ export class Map extends L.Map {
|
||||
if (!e.originalEvent.ctrlKey) {
|
||||
getUnitsManager().selectedUnitsClearDestinations();
|
||||
}
|
||||
getUnitsManager().selectedUnitsAddDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null? this.#destinationRotationCenter: e.latlng, !e.originalEvent.shiftKey, this.#destinationGroupRotation)
|
||||
getUnitsManager().selectedUnitsAddDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : e.latlng, !e.originalEvent.shiftKey, this.#destinationGroupRotation)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -465,16 +440,14 @@ export class Map extends L.Map {
|
||||
#onMouseDown(e: any) {
|
||||
this.hideAllContextMenus();
|
||||
|
||||
if (this.#state == MOVE_UNIT && e.originalEvent.button == 2)
|
||||
{
|
||||
if (this.#state == MOVE_UNIT && e.originalEvent.button == 2) {
|
||||
this.#computeDestinationRotation = true;
|
||||
this.#destinationRotationCenter = this.getMouseCoordinates();
|
||||
}
|
||||
}
|
||||
|
||||
#onMouseUp(e: any) {
|
||||
if (this.#state == MOVE_UNIT)
|
||||
{
|
||||
if (this.#state == MOVE_UNIT) {
|
||||
this.#computeDestinationRotation = false;
|
||||
this.#destinationRotationCenter = null;
|
||||
this.#destinationGroupRotation = 0;
|
||||
@@ -488,7 +461,7 @@ export class Map extends L.Map {
|
||||
if (this.#computeDestinationRotation && this.#destinationRotationCenter != null)
|
||||
this.#destinationGroupRotation = -bearing(this.#destinationRotationCenter.lat, this.#destinationRotationCenter.lng, this.getMouseCoordinates().lat, this.getMouseCoordinates().lng);
|
||||
|
||||
this.#updateDestinationPreview(e);
|
||||
this.#updateDestinationPreview(e);
|
||||
}
|
||||
|
||||
#onZoom(e: any) {
|
||||
@@ -542,18 +515,23 @@ export class Map extends L.Map {
|
||||
}
|
||||
|
||||
#updateDestinationPreview(e: any) {
|
||||
Object.values(getUnitsManager().selectedUnitsComputeGroupDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null? this.#destinationRotationCenter: this.getMouseCoordinates(), this.#destinationGroupRotation)).forEach((latlng: L.LatLng, idx: number) => {
|
||||
Object.values(getUnitsManager().selectedUnitsComputeGroupDestination(this.#computeDestinationRotation && this.#destinationRotationCenter != null ? this.#destinationRotationCenter : this.getMouseCoordinates(), this.#destinationGroupRotation)).forEach((latlng: L.LatLng, idx: number) => {
|
||||
if (idx < this.#destinationPreviewMarkers.length)
|
||||
this.#destinationPreviewMarkers[idx].setLatLng(!e.originalEvent.shiftKey? latlng: this.getMouseCoordinates());
|
||||
})
|
||||
this.#destinationPreviewMarkers[idx].setLatLng(!e.originalEvent.shiftKey ? latlng : this.getMouseCoordinates());
|
||||
})
|
||||
}
|
||||
|
||||
#createOptionButton(value: string, url: string, title: string, callback: EventListenerOrEventListenerObject) {
|
||||
#createOptionButton(value: string, url: string, title: string, callback: string, argument: string) {
|
||||
var button = document.createElement("button");
|
||||
const img = document.createElement("img");
|
||||
img.src = `/resources/theme/images/buttons/${url}`;
|
||||
img.onload = () => SVGInjector(img);
|
||||
button.title = title;
|
||||
button.value = value;
|
||||
button.innerHTML = `<img src="/resources/theme/images/buttons/${url}" onload="SVGInject(this)" />`
|
||||
button.addEventListener("click", callback);
|
||||
button.appendChild(img);
|
||||
button.setAttribute("data-on-click", callback);
|
||||
button.setAttribute("data-on-click-params", argument);
|
||||
return button;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
13
client/src/map/temporaryunitmarker.ts
Normal file
13
client/src/map/temporaryunitmarker.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import { Icon } from "leaflet";
|
||||
import { CustomMarker } from "./custommarker";
|
||||
|
||||
export class TemporaryUnitMarker extends CustomMarker {
|
||||
createIcon() {
|
||||
var icon = new Icon({
|
||||
iconUrl: '/resources/theme/images/markers/temporary-icon.png',
|
||||
iconSize: [52, 52],
|
||||
iconAnchor: [26, 26]
|
||||
});
|
||||
this.setIcon(icon);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,14 @@
|
||||
import * as L from 'leaflet'
|
||||
import { DivIcon } from 'leaflet';
|
||||
import { CustomMarker } from '../map/custommarker';
|
||||
import { SVGInjector } from '@tanem/svg-injector';
|
||||
|
||||
export interface AirbaseOptions
|
||||
{
|
||||
name: string,
|
||||
position: L.LatLng,
|
||||
src: string
|
||||
position: L.LatLng
|
||||
}
|
||||
|
||||
export class Airbase extends L.Marker
|
||||
export class Airbase extends CustomMarker
|
||||
{
|
||||
#name: string = "";
|
||||
#coalition: string = "";
|
||||
@@ -19,21 +20,30 @@ export class Airbase extends L.Marker
|
||||
super(options.position, { riseOnHover: true });
|
||||
|
||||
this.#name = options.name;
|
||||
var icon = new L.DivIcon({
|
||||
html: ` <div class="airbase" data-object="airbase" data-coalition="neutral">
|
||||
<div class="airbase-marker"> </div>
|
||||
</div>`,
|
||||
}
|
||||
|
||||
createIcon() {
|
||||
var icon = new DivIcon({
|
||||
className: 'leaflet-airbase-marker',
|
||||
iconSize: [40, 40],
|
||||
iconAnchor: [20, 20]
|
||||
}); // Set the marker, className must be set to avoid white square
|
||||
this.setIcon(icon);
|
||||
|
||||
var el = document.createElement("div");
|
||||
el.classList.add("airbase-icon");
|
||||
el.setAttribute("data-object", "airbase");
|
||||
var img = document.createElement("img");
|
||||
img.src = "/resources/theme/images/markers/airbase.svg";
|
||||
img.onload = () => SVGInjector(img);
|
||||
el.appendChild(img);
|
||||
this.getElement()?.appendChild(el);
|
||||
}
|
||||
|
||||
setCoalition(coalition: string)
|
||||
{
|
||||
this.#coalition = coalition;
|
||||
(<HTMLElement> this.getElement()?.querySelector(".airbase")).dataset.coalition = this.#coalition;
|
||||
(<HTMLElement> this.getElement()?.querySelector(".airbase-icon")).dataset.coalition = this.#coalition;
|
||||
}
|
||||
|
||||
getCoalition()
|
||||
|
||||
36
client/src/missionhandler/bullseye.ts
Normal file
36
client/src/missionhandler/bullseye.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { DivIcon } from "leaflet";
|
||||
import { CustomMarker } from "../map/custommarker";
|
||||
import { SVGInjector } from "@tanem/svg-injector";
|
||||
|
||||
export class Bullseye extends CustomMarker {
|
||||
#coalition: string = "";
|
||||
|
||||
createIcon() {
|
||||
var icon = new DivIcon({
|
||||
className: 'leaflet-bullseye-marker',
|
||||
iconSize: [40, 40],
|
||||
iconAnchor: [20, 20]
|
||||
}); // Set the marker, className must be set to avoid white square
|
||||
this.setIcon(icon);
|
||||
|
||||
var el = document.createElement("div");
|
||||
el.classList.add("bullseye-icon");
|
||||
el.setAttribute("data-object", "bullseye");
|
||||
var img = document.createElement("img");
|
||||
img.src = "/resources/theme/images/markers/bullseye.svg";
|
||||
img.onload = () => SVGInjector(img);
|
||||
el.appendChild(img);
|
||||
this.getElement()?.appendChild(el);
|
||||
}
|
||||
|
||||
setCoalition(coalition: string)
|
||||
{
|
||||
this.#coalition = coalition;
|
||||
(<HTMLElement> this.getElement()?.querySelector(".bullseye-icon")).dataset.coalition = this.#coalition;
|
||||
}
|
||||
|
||||
getCoalition()
|
||||
{
|
||||
return this.#coalition;
|
||||
}
|
||||
}
|
||||
@@ -1,44 +1,58 @@
|
||||
import { Marker, LatLng, Icon } from "leaflet";
|
||||
import { LatLng } from "leaflet";
|
||||
import { getInfoPopup, getMap } from "..";
|
||||
import { Airbase } from "./airbase";
|
||||
|
||||
var bullseyeIcons = [
|
||||
new Icon({ iconUrl: 'images/bullseye0.png', iconAnchor: [30, 30]}),
|
||||
new Icon({ iconUrl: 'images/bullseye1.png', iconAnchor: [30, 30]}),
|
||||
new Icon({ iconUrl: 'images/bullseye2.png', iconAnchor: [30, 30]})
|
||||
]
|
||||
import { Bullseye } from "./bullseye";
|
||||
|
||||
export class MissionHandler
|
||||
{
|
||||
#bullseyes : any; //TODO declare interface
|
||||
#bullseyeMarkers: any;
|
||||
#airbases : any; //TODO declare interface
|
||||
#airbasesMarkers: {[name: string]: Airbase};
|
||||
#bullseyes : {[name: string]: Bullseye} = {};
|
||||
#airbases : {[name: string]: Airbase} = {};
|
||||
#theatre : string = "";
|
||||
|
||||
constructor()
|
||||
{
|
||||
this.#bullseyes = undefined;
|
||||
this.#bullseyeMarkers = [
|
||||
new Marker([0, 0], {icon: bullseyeIcons[0]}).addTo(getMap()),
|
||||
new Marker([0, 0], {icon: bullseyeIcons[1]}).addTo(getMap()),
|
||||
new Marker([0, 0], {icon: bullseyeIcons[2]}).addTo(getMap())
|
||||
]
|
||||
this.#airbasesMarkers = {};
|
||||
|
||||
}
|
||||
|
||||
update(data: BullseyesData | AirbasesData | any)
|
||||
{
|
||||
if ("bullseyes" in data)
|
||||
{
|
||||
this.#bullseyes = data.bullseyes;
|
||||
this.#drawBullseyes();
|
||||
for (let idx in data.bullseyes)
|
||||
{
|
||||
const bullseye = data.bullseyes[idx];
|
||||
if (!(idx in this.#bullseyes))
|
||||
this.#bullseyes[idx] = new Bullseye([0, 0]).addTo(getMap());
|
||||
|
||||
if (bullseye.latitude && bullseye.longitude && bullseye.coalition)
|
||||
{
|
||||
this.#bullseyes[idx].setLatLng(new LatLng(bullseye.latitude, bullseye.longitude));
|
||||
this.#bullseyes[idx].setCoalition(bullseye.coalition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ("airbases" in data)
|
||||
{
|
||||
this.#airbases = data.airbases;
|
||||
this.#drawAirbases();
|
||||
for (let idx in data.airbases)
|
||||
{
|
||||
var airbase = data.airbases[idx]
|
||||
if (this.#airbases[idx] === undefined)
|
||||
{
|
||||
this.#airbases[idx] = new Airbase({
|
||||
position: new LatLng(airbase.latitude, airbase.longitude),
|
||||
name: airbase.callsign
|
||||
}).addTo(getMap());
|
||||
this.#airbases[idx].on('contextmenu', (e) => this.#onAirbaseClick(e));
|
||||
}
|
||||
if (airbase.latitude && airbase.longitude && airbase.coalition)
|
||||
{
|
||||
this.#airbases[idx].setLatLng(new LatLng(airbase.latitude, airbase.longitude));
|
||||
this.#airbases[idx].setCoalition(airbase.coalition);
|
||||
}
|
||||
//this.#airbases[idx].setProperties(["Runway 1: 31L / 13R", "Runway 2: 31R / 13L", "TCN: 17X", "ILS: ---" ]);
|
||||
//this.#airbases[idx].setParkings(["2x big", "5x small"]);
|
||||
}
|
||||
}
|
||||
|
||||
if ("mission" in data)
|
||||
@@ -58,38 +72,6 @@ export class MissionHandler
|
||||
return this.#bullseyes;
|
||||
}
|
||||
|
||||
#drawBullseyes()
|
||||
{
|
||||
for (let idx in this.#bullseyes)
|
||||
{
|
||||
var bullseye = this.#bullseyes[idx];
|
||||
this.#bullseyeMarkers[idx].setLatLng(new LatLng(bullseye.latitude, bullseye.longitude));
|
||||
}
|
||||
}
|
||||
|
||||
#drawAirbases()
|
||||
{
|
||||
for (let idx in this.#airbases)
|
||||
{
|
||||
var airbase = this.#airbases[idx]
|
||||
if (this.#airbasesMarkers[idx] === undefined)
|
||||
{
|
||||
this.#airbasesMarkers[idx] = new Airbase({
|
||||
position: new LatLng(airbase.latitude, airbase.longitude),
|
||||
name: airbase.callsign,
|
||||
src: "images/airbase.png"}).addTo(getMap());
|
||||
this.#airbasesMarkers[idx].on('contextmenu', (e) => this.#onAirbaseClick(e));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.#airbasesMarkers[idx].setLatLng(new LatLng(airbase.latitude, airbase.longitude));
|
||||
this.#airbasesMarkers[idx].setCoalition(airbase.coalition);
|
||||
//this.#airbasesMarkers[idx].setProperties(["Runway 1: 31L / 13R", "Runway 2: 31R / 13L", "TCN: 17X", "ILS: ---" ]);
|
||||
//this.#airbasesMarkers[idx].setParkings(["2x big", "5x small"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#onAirbaseClick(e: any)
|
||||
{
|
||||
getMap().showAirbaseContextMenu(e, e.sourceTarget);
|
||||
|
||||
@@ -154,4 +154,10 @@ export function mercatorToLatLng(x: number, y: number) {
|
||||
lat = 180 / Math.PI * (2 * Math.atan(Math.exp(lat * Math.PI / 180.0)) - Math.PI / 2.0);
|
||||
|
||||
return { lng: lng, lat: lat };
|
||||
}
|
||||
|
||||
export function createDivWithClass(className: string) {
|
||||
var el = document.createElement("div");
|
||||
el.classList.add(className);
|
||||
return el;
|
||||
}
|
||||
@@ -37,8 +37,8 @@ export class MouseInfoPanel extends Panel {
|
||||
|
||||
if ( el != null ) {
|
||||
|
||||
var dist = distance(bullseyes[idx].latitude, bullseyes[idx].longitude, mousePosition.lat, mousePosition.lng);
|
||||
var bear = bearing(bullseyes[idx].latitude, bullseyes[idx].longitude, mousePosition.lat, mousePosition.lng);
|
||||
var dist = distance(bullseyes[idx].getLatLng().lat, bullseyes[idx].getLatLng().lng, mousePosition.lat, mousePosition.lng);
|
||||
var bear = bearing(bullseyes[idx].getLatLng().lat, bullseyes[idx].getLatLng().lng, mousePosition.lat, mousePosition.lng);
|
||||
|
||||
let bng = zeroAppend(Math.floor(bear), 3);
|
||||
|
||||
|
||||
@@ -346,7 +346,7 @@ export class UnitControlPanel extends Panel {
|
||||
var button = document.createElement("button");
|
||||
button.title = title;
|
||||
button.value = value;
|
||||
button.innerHTML = `<img src="/resources/theme/images/buttons/${url}" onload="SVGInject(this)" />`
|
||||
button.innerHTML = `<img src="/resources/theme/images/buttons/${url}" />`
|
||||
button.addEventListener("click", callback);
|
||||
return button;
|
||||
}
|
||||
|
||||
@@ -4,14 +4,16 @@ import { rad2deg } from '../other/utils';
|
||||
import { addDestination, attackUnit, changeAltitude, changeSpeed, createFormation as setLeader, deleteUnit, getUnits, landAt, setAltitude, setReactionToThreat, setROE, setSpeed, refuel, setAdvacedOptions, followUnit, setEmissionsCountermeasures } from '../server/server';
|
||||
import { aircraftDatabase } from './aircraftdatabase';
|
||||
import { groundUnitsDatabase } from './groundunitsdatabase';
|
||||
import { CustomMarker } from '../map/custommarker';
|
||||
import { SVGInjector } from '@tanem/svg-injector';
|
||||
|
||||
var pathIcon = new Icon({
|
||||
iconUrl: 'images/marker-icon.png',
|
||||
shadowUrl: 'images/marker-shadow.png',
|
||||
iconUrl: '/resources/theme/images/markers/marker-icon.png',
|
||||
shadowUrl: '/resources/theme/images/markers/marker-shadow.png',
|
||||
iconAnchor: [13, 41]
|
||||
});
|
||||
|
||||
export class Unit extends Marker {
|
||||
export class Unit extends CustomMarker {
|
||||
ID: number;
|
||||
|
||||
#data: UnitData = {
|
||||
@@ -114,22 +116,7 @@ export class Unit extends Marker {
|
||||
/* Set the unit data */
|
||||
this.setData(data);
|
||||
|
||||
/* Set the icon */
|
||||
var icon = new DivIcon({
|
||||
html: this.getMarkerHTML(),
|
||||
className: 'leaflet-unit-marker',
|
||||
iconAnchor: [25, 25],
|
||||
iconSize: [50, 50],
|
||||
});
|
||||
this.setIcon(icon);
|
||||
}
|
||||
|
||||
getMarkerHTML() {
|
||||
return `<div class="unit" data-object="unit-${this.getMarkerCategory()}" data-coalition="${this.getMissionData().coalition}">
|
||||
<div class="unit-selected-spotlight"></div>
|
||||
<div class="unit-marker"><img src="/resources/theme/images/units/${this.getMarkerCategory()}.svg" onload="SVGInject(this)"/></div>
|
||||
<div class="unit-short-label"></div>
|
||||
</div>`
|
||||
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
@@ -137,6 +124,20 @@ export class Unit extends Marker {
|
||||
return "";
|
||||
}
|
||||
|
||||
getActiveMarkerElements() {
|
||||
// Default values
|
||||
return {
|
||||
state: false,
|
||||
vvi: false,
|
||||
hotgroup: false,
|
||||
unitIcon: true,
|
||||
shortLabel: false,
|
||||
fuel: false,
|
||||
ammo: false,
|
||||
summary: false
|
||||
}
|
||||
}
|
||||
|
||||
setSelected(selected: boolean) {
|
||||
/* Only alive units can be selected. Some units are not selectable (weapons) */
|
||||
if ((this.getBaseData().alive || !selected) && this.getSelectable() && this.getSelected() != selected) {
|
||||
@@ -285,6 +286,104 @@ export class Unit extends Marker {
|
||||
return this.getData().optionsData;
|
||||
}
|
||||
|
||||
/********************** Icon *************************/
|
||||
createIcon(): void {
|
||||
/* Set the icon */
|
||||
var icon = new DivIcon({
|
||||
className: 'leaflet-unit-icon',
|
||||
iconAnchor: [25, 25],
|
||||
iconSize: [50, 50],
|
||||
});
|
||||
this.setIcon(icon);
|
||||
|
||||
var el = document.createElement("div");
|
||||
el.setAttribute("data-object", `unit-${this.getMarkerCategory()}`);
|
||||
el.setAttribute("data-coalition", this.getMissionData().coalition);
|
||||
|
||||
// Generate and append elements depending on active options
|
||||
// State icon
|
||||
if (this.getActiveMarkerElements().state){
|
||||
var state = document.createElement("div");
|
||||
state.classList.add("unit-state");
|
||||
el.appendChild(state);
|
||||
}
|
||||
|
||||
// Velocity vector
|
||||
if (this.getActiveMarkerElements().vvi) {
|
||||
var vvi = document.createElement("div");
|
||||
vvi.classList.add("unit-vvi");
|
||||
vvi.toggleAttribute("data-rotate-to-heading");
|
||||
el.append(vvi);
|
||||
}
|
||||
|
||||
// Hotgroup indicator
|
||||
if (this.getActiveMarkerElements().hotgroup) {
|
||||
var hotgroup = document.createElement("div");
|
||||
hotgroup.classList.add("unit-hotgroup");
|
||||
var hotgroupId = document.createElement("div");
|
||||
hotgroupId.classList.add("unit-hotgroup-id");
|
||||
hotgroup.appendChild(hotgroupId);
|
||||
el.append(hotgroup);
|
||||
}
|
||||
|
||||
// Main icon
|
||||
if (this.getActiveMarkerElements().unitIcon) {
|
||||
var unitIcon = document.createElement("div");
|
||||
unitIcon.classList.add("unit-icon");
|
||||
var img = document.createElement("img");
|
||||
img.src = `/resources/theme/images/units/${this.getMarkerCategory()}.svg`;
|
||||
img.onload = () => SVGInjector(img);
|
||||
unitIcon.appendChild(img);
|
||||
el.append(unitIcon);
|
||||
}
|
||||
|
||||
// Short label
|
||||
if (this.getActiveMarkerElements().shortLabel) {
|
||||
var shortLabel = document.createElement("div");
|
||||
shortLabel.classList.add("unit-short-label");
|
||||
shortLabel.innerText = aircraftDatabase.getByName(this.getBaseData().name)?.shortLabel || ""; //TODO: fix, use correct database
|
||||
el.append(shortLabel);
|
||||
}
|
||||
|
||||
// Fuel indicator
|
||||
if (this.getActiveMarkerElements().fuel) {
|
||||
var fuelIndicator = document.createElement("div");
|
||||
fuelIndicator.classList.add("unit-fuel");
|
||||
var fuelLevel = document.createElement("div");
|
||||
fuelLevel.classList.add("unit-fuel-level");
|
||||
fuelIndicator.appendChild(fuelLevel);
|
||||
el.append(fuelIndicator);
|
||||
}
|
||||
|
||||
// Ammo indicator
|
||||
if (this.getActiveMarkerElements().ammo){
|
||||
var ammoIndicator = document.createElement("div");
|
||||
ammoIndicator.classList.add("unit-ammo");
|
||||
for (let i = 0; i <= 3; i++)
|
||||
ammoIndicator.appendChild(document.createElement("div"));
|
||||
el.append(ammoIndicator);
|
||||
}
|
||||
|
||||
// Unit summary
|
||||
if (this.getActiveMarkerElements().summary) {
|
||||
var summary = document.createElement("div");
|
||||
summary.classList.add("unit-summary");
|
||||
var callsign = document.createElement("div");
|
||||
callsign.classList.add("unit-callsign");
|
||||
callsign.innerText = this.getBaseData().unitName;
|
||||
var altitude = document.createElement("div");
|
||||
altitude.classList.add("unit-altitude");
|
||||
var speed = document.createElement("div");
|
||||
speed.classList.add("unit-speed");
|
||||
summary.appendChild(callsign);
|
||||
summary.appendChild(altitude);
|
||||
summary.appendChild(speed);
|
||||
el.appendChild(summary);
|
||||
}
|
||||
|
||||
this.getElement()?.appendChild(el);
|
||||
}
|
||||
|
||||
/********************** Visibility *************************/
|
||||
updateVisibility() {
|
||||
var hidden = false;
|
||||
@@ -295,7 +394,9 @@ export class Unit extends Marker {
|
||||
hidden = true;
|
||||
else if (hiddenUnits.includes(this.getMarkerCategory()))
|
||||
hidden = true;
|
||||
this.setHidden(document.body.getAttribute(`data-hide-${this.getMissionData().coalition}`) != null || hidden || !this.getBaseData().alive);
|
||||
else if (hiddenUnits.includes(this.getMissionData().coalition))
|
||||
hidden = true;
|
||||
this.setHidden(hidden || !this.getBaseData().alive);
|
||||
}
|
||||
|
||||
setHidden(hidden: boolean) {
|
||||
@@ -576,7 +677,7 @@ export class Unit extends Marker {
|
||||
el.setAttribute("style", currentStyle + `transform:rotate(${headingDeg}deg);`);
|
||||
});
|
||||
|
||||
/* Turn on ordnance indicators */
|
||||
/* Turn on ammo indicators */
|
||||
var hasFox1 = element.querySelector(".unit")?.hasAttribute("data-has-fox-1");
|
||||
var hasFox2 = element.querySelector(".unit")?.hasAttribute("data-has-fox-2");
|
||||
var hasFox3 = element.querySelector(".unit")?.hasAttribute("data-has-fox-3");
|
||||
@@ -690,7 +791,18 @@ export class Unit extends Marker {
|
||||
}
|
||||
|
||||
export class AirUnit extends Unit {
|
||||
|
||||
getActiveMarkerElements() {
|
||||
return {
|
||||
state: true,
|
||||
vvi: true,
|
||||
hotgroup: true,
|
||||
unitIcon: true,
|
||||
shortLabel: true,
|
||||
fuel: true,
|
||||
ammo: true,
|
||||
summary: true
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class Aircraft extends AirUnit {
|
||||
@@ -698,30 +810,6 @@ export class Aircraft extends AirUnit {
|
||||
super(ID, data);
|
||||
}
|
||||
|
||||
getMarkerHTML() {
|
||||
return `<div class="unit" data-object="unit-aircraft" data-coalition="${this.getMissionData().coalition}">
|
||||
<div class="unit-state"></div>
|
||||
<div class="unit-vvi" data-rotate-to-heading></div>
|
||||
<div class="unit-hotgroup"><div class="unit-hotgroup-id"></div></div>
|
||||
<div class="unit-marker"><img src="/resources/theme/images/units/aircraft.svg" onload="SVGInject(this)"/></div>
|
||||
<div class="unit-short-label">${aircraftDatabase.getByName(this.getBaseData().name)?.shortLabel || ""}</div>
|
||||
<div class="unit-fuel">
|
||||
<div class="unit-fuel-level" style="width:100%;"></div>
|
||||
</div>
|
||||
<div class="unit-ammo">
|
||||
<div class="unit-ammo-fox-1"></div>
|
||||
<div class="unit-ammo-fox-2"></div>
|
||||
<div class="unit-ammo-fox-3"></div>
|
||||
<div class="unit-ammo-other"></div>
|
||||
</div>
|
||||
<div class="unit-summary">
|
||||
<div class="unit-callsign">${this.getBaseData().unitName}</div>
|
||||
<div class="unit-altitude"></div>
|
||||
<div class="unit-speed"></div>
|
||||
</div>
|
||||
</div>`
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
return "aircraft";
|
||||
}
|
||||
@@ -732,7 +820,7 @@ export class Helicopter extends AirUnit {
|
||||
super(ID, data);
|
||||
}
|
||||
|
||||
getVisibilityCategory() {
|
||||
getMarkerCategory() {
|
||||
return "helicopter";
|
||||
}
|
||||
}
|
||||
@@ -742,15 +830,17 @@ export class GroundUnit extends Unit {
|
||||
super(ID, data);
|
||||
}
|
||||
|
||||
getMarkerHTML() {
|
||||
var role = groundUnitsDatabase.getByName(this.getBaseData().name)?.loadouts[0].roles[0];
|
||||
return `<div class="unit" data-object="unit-${this.getMarkerCategory()}" data-coalition="${this.getMissionData().coalition}">
|
||||
<div class="unit-marker"><img src="/resources/theme/images/units/${this.getMarkerCategory()}.svg" onload="SVGInject(this)"/></div>
|
||||
<div class="unit-short-label">${role?.substring(0, 1)?.toUpperCase() || ""}</div>
|
||||
<div class="unit-hotgroup">
|
||||
<div class="unit-hotgroup-id"></div>
|
||||
</div>
|
||||
</div>`
|
||||
getActiveMarkerElements() {
|
||||
return {
|
||||
state: true,
|
||||
vvi: false,
|
||||
hotgroup: true,
|
||||
unitIcon: true,
|
||||
shortLabel: true,
|
||||
fuel: false,
|
||||
ammo: false,
|
||||
summary: false
|
||||
};
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
@@ -766,6 +856,19 @@ export class NavyUnit extends Unit {
|
||||
super(ID, data);
|
||||
}
|
||||
|
||||
getActiveMarkerElements() {
|
||||
return {
|
||||
state: true,
|
||||
vvi: false,
|
||||
hotgroup: true,
|
||||
unitIcon: true,
|
||||
shortLabel: true,
|
||||
fuel: false,
|
||||
ammo: false,
|
||||
summary: false
|
||||
};
|
||||
}
|
||||
|
||||
getMarkerCategory() {
|
||||
return "navyunit";
|
||||
}
|
||||
@@ -776,14 +879,6 @@ export class Weapon extends Unit {
|
||||
super(ID, data);
|
||||
this.setSelectable(false);
|
||||
}
|
||||
|
||||
getMarkerHTML(): string {
|
||||
return `<div class="unit" data-object="unit-${this.getMarkerCategory()}" data-coalition="${this.getMissionData().coalition}">
|
||||
<div class="unit-marker" data-rotate-to-heading><img src="/resources/theme/images/units/${this.getMarkerCategory()}.svg" onload="SVGInject(this)"/></div>
|
||||
<div class="unit-short-label"></div>
|
||||
</div>`
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export class Missile extends Weapon {
|
||||
|
||||
Reference in New Issue
Block a user