import { LatLng } from "leaflet"; import { Dropdown } from "./dropdown"; import { Slider } from "./slider"; import { UnitDatabase } from "../unit/databases/unitdatabase"; import { getApp } from ".."; import { GAME_MASTER } from "../constants/constants"; import { Airbase } from "../mission/airbase"; import { ftToM } from "../other/utils"; import { aircraftDatabase } from "../unit/databases/aircraftdatabase"; import { helicopterDatabase } from "../unit/databases/helicopterdatabase"; import { groundUnitDatabase } from "../unit/databases/groundunitdatabase"; import { navyUnitDatabase } from "../unit/databases/navyunitdatabase"; export class UnitSpawnMenu { #container: HTMLElement; #unitDatabase: UnitDatabase; #countryCodes: any; #orderByRole: boolean; #spawnOptions: UnitSpawnOptions = { roleType: "", name: "", latlng: new LatLng(0, 0), coalition: "blue", count: 1, country: "", loadout: undefined, airbase: undefined, liveryID: undefined, altitude: undefined }; /* Controls */ #unitRoleTypeDropdown: Dropdown; #unitLabelDropdown: Dropdown; #unitCountDropdown: Dropdown; #unitLoadoutDropdown: Dropdown; #unitCountryDropdown: Dropdown; #unitLiveryDropdown: Dropdown; #unitSpawnAltitudeSlider: Slider; /* HTML Elements */ #deployUnitButtonEl: HTMLButtonElement; #unitLoadoutPreviewEl: HTMLDivElement; #unitImageEl: HTMLImageElement; #unitLoadoutListEl: HTMLDivElement; constructor(ID: string, unitDatabase: UnitDatabase, orderByRole: boolean) { this.#container = document.getElementById(ID) as HTMLElement; this.#unitDatabase = unitDatabase; 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, (label: string) => this.#setUnitLabel(label), 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.#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()); /* Create the unit image and loadout elements */ this.#unitLoadoutPreviewEl = document.createElement("div"); this.#unitLoadoutPreviewEl.classList.add("unit-loadout-preview"); this.#unitImageEl = document.createElement("img"); this.#unitImageEl.classList.add("unit-image", "hide"); this.#unitLoadoutListEl = document.createElement("div"); 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"); this.#container.dispatchEvent(new Event("resize")); }); advancedOptionsDiv.append(this.#unitCountryDropdown.getContainer(), this.#unitLiveryDropdown.getContainer(), this.#unitLoadoutPreviewEl, this.#unitSpawnAltitudeSlider.getContainer() as HTMLElement); /* Create the unit deploy button */ this.#deployUnitButtonEl = document.createElement("button"); this.#deployUnitButtonEl.classList.add("deploy-unit-button"); this.#deployUnitButtonEl.disabled = true; this.#deployUnitButtonEl.innerText = "Deploy unit"; this.#deployUnitButtonEl.setAttribute("data-coalition", "blue"); this.#deployUnitButtonEl.addEventListener("click", () => { this.deployUnits(this.#spawnOptions, parseInt(this.#unitCountDropdown.getValue())); }); /* Assemble all components */ this.#container.append(this.#unitRoleTypeDropdown.getContainer(), unitLabelCountContainerEl, this.#unitLoadoutDropdown.getContainer(), advancedOptionsToggle, advancedOptionsDiv, this.#deployUnitButtonEl); /* Load the country codes from the public folder */ var xhr = new XMLHttpRequest(); xhr.open('GET', 'images/countries/codes.json', true); xhr.responseType = 'json'; xhr.onload = () => { var status = xhr.status; if (status === 200) this.#countryCodes = xhr.response; else console.error(`Error retrieving country codes`) }; xhr.send(); /* Event listeners */ this.#container.addEventListener("unitRoleTypeChanged", () => { this.#deployUnitButtonEl.disabled = true; this.#unitLabelDropdown.reset(); this.#unitLoadoutListEl.replaceChildren(); this.#unitLoadoutDropdown.reset(); this.#unitImageEl.classList.toggle("hide", true); this.#unitLiveryDropdown.reset(); if (this.#orderByRole) this.#unitLabelDropdown.setOptions(this.#unitDatabase.getByRole(this.#spawnOptions.roleType).map((blueprint) => { return blueprint.label })); else this.#unitLabelDropdown.setOptions(this.#unitDatabase.getByType(this.#spawnOptions.roleType).map((blueprint) => { return blueprint.label })); this.#container.dispatchEvent(new Event("resize")); this.#spawnOptions.name = ""; this.#spawnOptions.loadout = undefined; this.#spawnOptions.liveryID = undefined; this.#computeSpawnPoints(); }) this.#container.addEventListener("unitLabelChanged", () => { this.#deployUnitButtonEl.disabled = false; if (!this.#unitLoadoutDropdown.isHidden()) { this.#unitLoadoutDropdown.setOptions(this.#unitDatabase.getLoadoutNamesByRole(this.#spawnOptions.name, this.#spawnOptions.roleType)); this.#unitLoadoutDropdown.selectValue(0); } this.#unitImageEl.src = `images/units/${this.#unitDatabase.getByName(this.#spawnOptions.name)?.filename}`; this.#unitImageEl.classList.toggle("hide", false); this.#setUnitLiveryOptions(); this.#container.dispatchEvent(new Event("resize")); this.#computeSpawnPoints(); }) this.#container.addEventListener("unitLoadoutChanged", () => { 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") : ""; this.#unitLoadoutListEl.replaceChildren( ...items.map((item: any) => { var div = document.createElement('div'); div.innerText = item; return div; }) ); } this.#container.dispatchEvent(new Event("resize")); }) this.#container.addEventListener("unitCountChanged", () => { this.#computeSpawnPoints(); }) this.#container.addEventListener("unitCountryChanged", () => { this.#setUnitLiveryOptions(); }) this.#container.addEventListener("unitLiveryChanged", () => { }) } getContainer() { return this.#container; } reset() { this.#deployUnitButtonEl.disabled = true; this.#unitRoleTypeDropdown.reset(); this.#unitLabelDropdown.reset(); this.#unitLiveryDropdown.reset(); if (this.#orderByRole) this.#unitRoleTypeDropdown.setOptions(this.#unitDatabase.getRoles()); else this.#unitRoleTypeDropdown.setOptions(this.#unitDatabase.getTypes()); this.#unitLoadoutListEl.replaceChildren(); this.#unitLoadoutDropdown.reset(); this.#unitImageEl.classList.toggle("hide", true); this.setCountries(); this.#container.dispatchEvent(new Event("resize")); } setCountries() { 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) })); if (countries.length > 0 && !countries.includes(this.#spawnOptions.country)) { this.#unitCountryDropdown.forceValue(this.#getFormattedCountry(countries[0])); this.#setUnitCountry(countries[0]); } } 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(); } setAirbase(airbase: Airbase | undefined) { this.#spawnOptions.airbase = airbase; } setLatLng(latlng: LatLng) { this.#spawnOptions.latlng = latlng; } setMaxUnitCount(maxUnitCount: number) { /* Create the unit count options */ var countOptions: string[] = []; for (let i = 1; i <= maxUnitCount; i++) countOptions.push(i.toString()); this.#unitCountDropdown.setOptions(countOptions); this.#unitCountDropdown.selectValue(0); } getRoleTypeDrodown() { return this.#unitRoleTypeDropdown; } getLabelDropdown() { return this.#unitLabelDropdown; } getCountDropdown() { return this.#unitCountDropdown; } getLoadoutDropdown() { return this.#unitLoadoutDropdown; } getCountryDropdown() { return this.#unitCountDropdown; } getLiveryDropdown() { return this.#unitLiveryDropdown; } getLoadoutPreview() { return this.#unitLoadoutPreviewEl; } getAltitudeSlider() { return this.#unitSpawnAltitudeSlider; } #setUnitRoleType(roleType: string) { this.#spawnOptions.roleType = roleType; this.#container.dispatchEvent(new Event("unitRoleTypeChanged")); } #setUnitLabel(label: string) { var name = this.#unitDatabase.getByLabel(label)?.name || null; if (name != null) this.#spawnOptions.name = name; this.#container.dispatchEvent(new Event("unitLabelChanged")); } #setUnitLoadout(loadoutName: string) { var loadout = this.#unitDatabase.getLoadoutByName(this.#spawnOptions.name, loadoutName); if (loadout) this.#spawnOptions.loadout = loadout; this.#container.dispatchEvent(new Event("unitLoadoutChanged")); } #setUnitCount(count: string) { this.#spawnOptions.count = parseInt(count); this.#container.dispatchEvent(new Event("unitCountChanged")); } #setUnitCountry(country: string) { this.#spawnOptions.country = country; this.#container.dispatchEvent(new Event("unitCountryChanged")); } #setUnitLivery(liveryName: string) { var liveries = this.#unitDatabase.getByName(this.#spawnOptions.name)?.liveries; if (liveryName === "Default") { this.#spawnOptions.liveryID = ""; } else { if (liveries !== undefined) { for (let liveryID in liveries) if (liveries[liveryID].name === liveryName) this.#spawnOptions.liveryID = liveryID; } } this.#container.dispatchEvent(new Event("unitLiveryChanged")); } #setUnitLiveryOptions() { if (this.#spawnOptions.name !== "" && this.#spawnOptions.country !== "") { var liveries = this.#unitDatabase.getLiveryNamesByName(this.#spawnOptions.name); var countryLiveries: string[] = ["Default"]; liveries.forEach((livery: any) => { var nationLiveryCodes = this.#countryCodes[this.#spawnOptions.country].liveryCodes; if (livery.countries === "All" || livery.countries.some((country: string) => { return nationLiveryCodes.includes(country) })) countryLiveries.push(livery.name); }); this.#unitLiveryDropdown.setOptions(countryLiveries); this.#unitLiveryDropdown.selectValue(0); } } 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"); var button = document.createElement("button"); button.classList.add("country-dropdown-element"); el.appendChild(button); button.addEventListener("click", () => { callback(country); parent.forceValue(this.#getFormattedCountry(country)); parent.close(); }); if (this.#countryCodes[country] !== undefined) { var code = this.#countryCodes[country].flagCode; if (code !== undefined) { var img = document.createElement("img"); img.src = `images/countries/${code.toLowerCase()}.svg`; button.appendChild(img); } } else { console.log("Unknown country " + country); } var text = document.createElement("div"); text.innerText = this.#getFormattedCountry(country); button.appendChild(text); return el; }); } #getFormattedCountry(country: string) { var formattedCountry = ""; if (this.#countryCodes[country] !== undefined && this.#countryCodes[country].displayName !== undefined) formattedCountry = this.#countryCodes[country].displayName; else formattedCountry = country.charAt(0).toUpperCase() + country.slice(1).toLowerCase(); return formattedCountry; } #computeSpawnPoints() { if (getApp().getMissionManager() && getApp().getMissionManager().getCommandModeOptions().commandMode !== GAME_MASTER) { var unitCount = parseInt(this.#unitCountDropdown.getValue()); var unitSpawnPoints = unitCount * this.#unitDatabase.getSpawnPointsByLabel(this.#unitLabelDropdown.getValue()); this.#deployUnitButtonEl.dataset.points = `${unitSpawnPoints}`; this.#deployUnitButtonEl.disabled = unitSpawnPoints >= getApp().getMissionManager().getAvailableSpawnPoints(); } } } export class AircraftSpawnMenu extends UnitSpawnMenu { /** * * @param ID - the ID of the HTML element which will contain the context menu */ constructor(ID: string){ super(ID, aircraftDatabase, true); this.setMaxUnitCount(4); this.getAltitudeSlider().setMinMax(0, 50000); this.getAltitudeSlider().setIncrement(500); this.getAltitudeSlider().setValue(20000); } deployUnits(spawnOptions: UnitSpawnOptions, unitsCount: number) { spawnOptions.coalition = getApp().getActiveCoalition(); if (spawnOptions) { var unitTable: UnitSpawnTable = { unitType: spawnOptions.name, location: spawnOptions.latlng, altitude: spawnOptions.altitude? spawnOptions.altitude: 0, loadout: spawnOptions.loadout? spawnOptions.loadout.name: "", liveryID: spawnOptions.liveryID? spawnOptions.liveryID: "" }; var units = []; for (let i = 1; i < unitsCount + 1; i++) { units.push(unitTable); } getApp().getUnitsManager().spawnUnits("Aircraft", units, getApp().getActiveCoalition(), false, spawnOptions.airbase ? spawnOptions.airbase.getName() : "", spawnOptions.country, (res: any) => { if (res.commandHash !== undefined) getApp().getMap().addTemporaryMarker(spawnOptions.latlng, spawnOptions.name, getApp().getActiveCoalition(), res.commandHash); }); this.getContainer().dispatchEvent(new Event("hide")); } } } export class HelicopterSpawnMenu extends UnitSpawnMenu { /** * * @param ID - the ID of the HTML element which will contain the context menu */ constructor(ID: string){ super(ID, helicopterDatabase, true); this.setMaxUnitCount(4); this.getAltitudeSlider().setMinMax(0, 10000); this.getAltitudeSlider().setIncrement(100); this.getAltitudeSlider().setValue(5000); } deployUnits(spawnOptions: UnitSpawnOptions, unitsCount: number) { spawnOptions.coalition = getApp().getActiveCoalition(); if (spawnOptions) { var unitTable: UnitSpawnTable = { unitType: spawnOptions.name, location: spawnOptions.latlng, altitude: spawnOptions.altitude? spawnOptions.altitude: 0, loadout: spawnOptions.loadout? spawnOptions.loadout.name: "", liveryID: spawnOptions.liveryID? spawnOptions.liveryID: "" }; var units = []; for (let i = 1; i < unitsCount + 1; i++) { units.push(unitTable); } getApp().getUnitsManager().spawnUnits("Helicopter", units, getApp().getActiveCoalition(), false, spawnOptions.airbase ? spawnOptions.airbase.getName() : "", spawnOptions.country, (res: any) => { if (res.commandHash !== undefined) getApp().getMap().addTemporaryMarker(spawnOptions.latlng, spawnOptions.name, getApp().getActiveCoalition(), res.commandHash); }); this.getContainer().dispatchEvent(new Event("hide")); } } } export class GroundUnitSpawnMenu extends UnitSpawnMenu { /** * * @param ID - the ID of the HTML element which will contain the context menu */ constructor(ID: string){ super(ID, groundUnitDatabase, false); this.setMaxUnitCount(20); this.getAltitudeSlider().hide(); this.getLoadoutDropdown().hide(); this.getLoadoutPreview().classList.add("hide"); } deployUnits(spawnOptions: UnitSpawnOptions, unitsCount: number) { spawnOptions.coalition = getApp().getActiveCoalition(); if (spawnOptions) { var unitTable: UnitSpawnTable = { unitType: spawnOptions.name, location: spawnOptions.latlng, liveryID: spawnOptions.liveryID? spawnOptions.liveryID: "" }; var units = []; for (let i = 0; i < unitsCount; i++) { units.push(JSON.parse(JSON.stringify(unitTable))); unitTable.location.lat += i > 0? 0.0001: 0; } getApp().getUnitsManager().spawnUnits("GroundUnit", units, getApp().getActiveCoalition(), false, spawnOptions.airbase ? spawnOptions.airbase.getName() : "", spawnOptions.country, (res: any) => { if (res.commandHash !== undefined) getApp().getMap().addTemporaryMarker(spawnOptions.latlng, spawnOptions.name, getApp().getActiveCoalition(), res.commandHash); }); this.getContainer().dispatchEvent(new Event("hide")); } } } export class NavyUnitSpawnMenu extends UnitSpawnMenu { /** * * @param ID - the ID of the HTML element which will contain the context menu */ constructor(ID: string){ super(ID, navyUnitDatabase, false); this.setMaxUnitCount(4); this.getAltitudeSlider().hide(); this.getLoadoutDropdown().hide(); this.getLoadoutPreview().classList.add("hide"); } deployUnits(spawnOptions: UnitSpawnOptions, unitsCount: number) { spawnOptions.coalition = getApp().getActiveCoalition(); if (spawnOptions) { var unitTable: UnitSpawnTable = { unitType: spawnOptions.name, location: spawnOptions.latlng, liveryID: spawnOptions.liveryID? spawnOptions.liveryID: "" }; var units = []; for (let i = 0; i < unitsCount; i++) { units.push(JSON.parse(JSON.stringify(unitTable))); unitTable.location.lat += i > 0? 0.0001: 0; } getApp().getUnitsManager().spawnUnits("NavyUnit", units, getApp().getActiveCoalition(), false, spawnOptions.airbase ? spawnOptions.airbase.getName() : "", spawnOptions.country, (res: any) => { if (res.commandHash !== undefined) getApp().getMap().addTemporaryMarker(spawnOptions.latlng, spawnOptions.name, getApp().getActiveCoalition(), res.commandHash); }); this.getContainer().dispatchEvent(new Event("hide")); } } }