From 5613394a2c4229096243be0bc2ae72d66331b623 Mon Sep 17 00:00:00 2001 From: Pax1601 Date: Thu, 20 Jul 2023 17:49:41 +0200 Subject: [PATCH] Completed RTS front end --- client/demo.js | 27 ++++- client/public/stylesheets/olympus.css | 104 ++++++++++++++++-- .../public/stylesheets/other/contextmenus.css | 2 +- .../public/stylesheets/panels/unitcontrol.css | 2 +- client/public/stylesheets/panels/unitinfo.css | 2 +- client/src/@types/server.d.ts | 18 +-- client/src/constants/constants.ts | 1 + .../src/controls/coalitionareacontextmenu.ts | 38 +------ client/src/controls/mapcontextmenu.ts | 3 +- client/src/missionhandler/missionhandler.ts | 87 +++++++++++++-- client/src/other/utils.ts | 30 +++++ client/src/server/server.ts | 40 +++++-- client/src/units/groundunitdatabase.ts | 2 +- client/src/units/unit.ts | 4 +- client/src/units/unitdatabase.ts | 4 +- client/src/units/unitsmanager.ts | 21 ++-- client/views/other/dialogs.ejs | 73 +++++++++++- client/views/toolbars/rts.ejs | 5 +- src/core/src/server.cpp | 6 +- 19 files changed, 368 insertions(+), 101 deletions(-) diff --git a/client/demo.js b/client/demo.js index 04522530..1bfa4995 100644 --- a/client/demo.js +++ b/client/demo.js @@ -114,6 +114,8 @@ class DemoDataGenerator { 'red': 'redsocks' }, })) + + this.startTime = Date.now(); } units(req, res){ @@ -380,18 +382,37 @@ class DemoDataGenerator { mission(req, res){ var ret = {mission: {theatre: "Nevada"}}; ret.time = Date.now(); + + ret.mission.dateAndTime = { + time: Date.now(), + date: "", + elapsedTime: (Date.now() - this.startTime) / 1000, + startTime: 0 + } + + ret.mission.RTSOptions = { + restrictSpawns: true, + restrictToCoalition: true, + setupTime: 300, + spawnPoints: { + red: 1000, + blue: 400 + }, + eras: ["WW2", "Early Cold War", "Late Cold War", "Modern"] + } + var auth = req.get("Authorization"); if (auth) { var username = atob(auth.replace("Basic ", "")).split(":")[0]; switch (username) { case "admin": - ret.mission.visibilityMode = "Game master"; + ret.mission.RTSOptions.commandMode = "Game master"; break case "blue": - ret.mission.visibilityMode = "Blue commander"; + ret.mission.RTSOptions.commandMode = "Blue commander"; break; case "red": - ret.mission.visibilityMode = "Red commander"; + ret.mission.RTSOptions.commandMode = "Red commander"; break; } } diff --git a/client/public/stylesheets/olympus.css b/client/public/stylesheets/olympus.css index 15f6e73d..5b527764 100644 --- a/client/public/stylesheets/olympus.css +++ b/client/public/stylesheets/olympus.css @@ -131,7 +131,7 @@ form>div { .ol-panel { background-color: var(--background-steel); - border-radius: 15px; + border-radius: var(--border-radius-md);; box-shadow: 0px 2px 5px #000A; color: white; font-size: 12px; @@ -704,7 +704,7 @@ nav.ol-panel> :last-child { background-image: url("/resources/theme/images/splash/1.png"); background-position: 100% 50%; background-size: 60%; - border-radius: var(--border-radius-lg); + border-radius: var(--border-radius-md); overflow: hidden; width: 1200px; z-index: 99999; @@ -868,36 +868,122 @@ nav.ol-panel> :last-child { } #command-mode { + display: flex; font-size: 14px; font-weight: bolder; padding-left: 10px; + margin-left: -11px; + margin-top: -0px; + margin-bottom: -0px; + height: 58px; + padding: 10px; + border-top-left-radius: var(--border-radius-md); + border-bottom-left-radius: var(--border-radius-md); + align-items: center; +} + +#command-mode[data-mode="Game master"] { + background-color: lightgray; + color: var(--secondary-gunmetal-grey); } #command-mode[data-mode="Blue commander"] { - color: var(--primary-blue); + background-color: var(--primary-blue); } #command-mode[data-mode="Red commander"] { - color: var(--primary-red); + background-color: var(--primary-red); } #spawn-points-container { font-size: 14px; - font-weight: bolder; } #spawn-points { background-color: var(--background-grey); padding: 5px 15px; - margin: 5px; + margin-left: 15px; border: 1px white solid; font-size: 14px; - border-radius: var(--border-radius-sm); + border-radius: var(--border-radius-md); } -#rts-phase { +#spawn-points-container { + height: 100%; + border-right: 1px solid gray; + display: flex; + align-items: center; + padding-right: 20px; +} + +#rts-phase::before { + content: "Time to start"; font-size: 14px; - font-weight: bolder; +} + +#rts-phase.setup-phase::after { + color: orange; + border: 1px solid orange; + border-radius: 999px; + padding: 5px 10px; + background-color: var(--background-grey); + margin-left: 15px; + content: attr(data-remaining-time); + font-size: 14px; +} + +#rts-phase.game-commenced { + background-color: var(--background-grey); + color: lightgreen; + border: 1px solid lightgreen; + padding: 5px 15px; + border-radius: var(--border-radius-md); + display: flex; + flex-direction: column; + align-items: center; +} + +#rts-phase.game-commenced::before { + content: "Game commenced"; + font-weight: bold; +} + +#rts-phase.game-commenced::after { + content: "Spawn restrictions are being enforced"; + font-size: 10px; +} + +#rts-toolbar .ol-button { + border: 1px solid white; +} + +#rts-toolbar .ol-button>svg { + width: 20px; + height: 20px; + fill: white; +} + +#rts-settings-dialog { + width: 400px; +} + +#rts-settings-dialog>.ol-dialog-content { + display: flex; + flex-direction: column; + flex-wrap: nowrap; + margin-bottom: 10px; + margin-top: 10px; + row-gap: 10px; + width: 100%; +} + +#rts-settings-dialog>.ol-dialog-content .ol-group { + justify-content: space-between; +} + +#rts-settings-dialog h4 { + white-space: nowrap; + width: fit-content; } .ol-destination-preview-icon { diff --git a/client/public/stylesheets/other/contextmenus.css b/client/public/stylesheets/other/contextmenus.css index 8ba49bbd..6a8494de 100644 --- a/client/public/stylesheets/other/contextmenus.css +++ b/client/public/stylesheets/other/contextmenus.css @@ -69,7 +69,7 @@ width: 100%; } -.deploy-unit-button[data-points]:not([data-points='']):not([data-points='0'])::after { +.deploy-unit-button[data-points]:not([data-points='']):not([data-points='0']):not([data-points='Infinity'])::after { content: " (" attr(data-points) " points)"; } diff --git a/client/public/stylesheets/panels/unitcontrol.css b/client/public/stylesheets/panels/unitcontrol.css index 0ad67eb6..426a5790 100644 --- a/client/public/stylesheets/panels/unitcontrol.css +++ b/client/public/stylesheets/panels/unitcontrol.css @@ -25,7 +25,7 @@ body.feature-forceShowUnitControlPanel #unit-control-panel { #unit-control-panel #selected-units-container button { align-items: center; - border-radius: var(--border-radius-lg); + border-radius: var(--border-radius-md); display: flex; font-size: 11px; height: 32px; diff --git a/client/public/stylesheets/panels/unitinfo.css b/client/public/stylesheets/panels/unitinfo.css index 95703f8a..6116a0c4 100644 --- a/client/public/stylesheets/panels/unitinfo.css +++ b/client/public/stylesheets/panels/unitinfo.css @@ -27,7 +27,7 @@ } #current-task { - border-radius: var(--border-radius-lg); + border-radius: var(--border-radius-md); margin-top: auto; padding: 6px 16px; } diff --git a/client/src/@types/server.d.ts b/client/src/@types/server.d.ts index c8a5f7b6..3b1d3dfb 100644 --- a/client/src/@types/server.d.ts +++ b/client/src/@types/server.d.ts @@ -10,6 +10,16 @@ interface BullseyesData { time: number; } +interface MissionData { + mission: { + theatre: string, + dateAndTime: DateAndTime; + RTSOptions: RTSOptions; + } + time: number; + sessionHash: string; +} + interface RTSOptions { commandMode: string; restrictSpawns: boolean; @@ -29,14 +39,6 @@ interface DateAndTime { startTime: number; } -interface MissionData { - theatre: string, - dateAndTime: DateAndTime; - RTSOptions: RTSOptions; - time: number; - sessionHash: string; -} - interface LogData { logs: {[key: string]: string}, sessionHash: string; diff --git a/client/src/constants/constants.ts b/client/src/constants/constants.ts index 54e2f760..b3b2601f 100644 --- a/client/src/constants/constants.ts +++ b/client/src/constants/constants.ts @@ -1,5 +1,6 @@ import { LatLng, LatLngBounds } from "leaflet"; +export const NONE = "None"; export const GAME_MASTER = "Game master"; export const BLUE_COMMANDER = "Blue commander"; export const RED_COMMANDER = "Red commander"; diff --git a/client/src/controls/coalitionareacontextmenu.ts b/client/src/controls/coalitionareacontextmenu.ts index 4273cf9c..ff474440 100644 --- a/client/src/controls/coalitionareacontextmenu.ts +++ b/client/src/controls/coalitionareacontextmenu.ts @@ -7,6 +7,7 @@ import { Dropdown } from "./dropdown"; import { Slider } from "./slider"; import { Switch } from "./switch"; import { groundUnitDatabase } from "../units/groundunitdatabase"; +import { createCheckboxOption, getCheckboxOptions } from "../other/utils"; export class CoalitionAreaContextMenu extends ContextMenu { #coalitionSwitch: Switch; @@ -56,7 +57,7 @@ export class CoalitionAreaContextMenu extends ContextMenu { document.addEventListener("contextMenuCreateIads", (e: any) => { const area = this.getCoalitionArea(); if (area) - getUnitsManager().createIADS(area, this.#getCheckboxOptions(this.#iadsTypesDropdown), this.#getCheckboxOptions(this.#iadsErasDropdown), this.#getCheckboxOptions(this.#iadsRangesDropdown), this.#iadsDensitySlider.getValue(), this.#iadsDistributionSlider.getValue()); + getUnitsManager().createIADS(area, getCheckboxOptions(this.#iadsTypesDropdown), getCheckboxOptions(this.#iadsErasDropdown), getCheckboxOptions(this.#iadsRangesDropdown), this.#iadsDensitySlider.getValue(), this.#iadsDistributionSlider.getValue()); }) this.hide(); } @@ -66,18 +67,18 @@ export class CoalitionAreaContextMenu extends ContextMenu { /* Create the checkboxes to select the unit roles */ this.#iadsTypesDropdown.setOptionsElements(IADSTypes.map((role: string) => { - return this.#createCheckboxOption(role); + return createCheckboxOption(role, `Add ${role}s to the IADS` ); })); /* Create the checkboxes to select the unit periods */ this.#iadsErasDropdown.setOptionsElements(groundUnitDatabase.getEras().map((era: string) => { - return this.#createCheckboxOption(era); + return createCheckboxOption(era, `Add ${era} era units to the IADS`); })); /* Create the checkboxes to select the unit ranges */ this.#iadsRangesDropdown.setOptionsElements(groundUnitDatabase.getRanges().map((range: string) => { - return this.#createCheckboxOption(range); + return createCheckboxOption(range, `Add ${range} units to the IADS`); })); if (getUnitsManager().getCommandMode() !== GAME_MASTER) @@ -120,33 +121,4 @@ export class CoalitionAreaContextMenu extends ContextMenu { }); } } - - #createCheckboxOption(text: string) { - var div = document.createElement("div"); - div.classList.add("ol-checkbox"); - var label = document.createElement("label"); - label.title = `Add ${text}s to the IADS`; - var input = document.createElement("input"); - input.type = "checkbox"; - input.checked = true; - var span = document.createElement("span"); - span.innerText = text; - label.appendChild(input); - label.appendChild(span); - div.appendChild(label); - return div as HTMLElement; - } - - #getCheckboxOptions(dropdown: Dropdown) { - var values: { [key: string]: boolean } = {}; - const element = dropdown.getOptionElements(); - for (let idx = 0; idx < element.length; idx++) { - const option = element.item(idx) as HTMLElement; - const key = option.querySelector("span")?.innerText; - const value = option.querySelector("input")?.checked; - if (key !== undefined && value !== undefined) - values[key] = value; - } - return values; - } } \ No newline at end of file diff --git a/client/src/controls/mapcontextmenu.ts b/client/src/controls/mapcontextmenu.ts index f91d19ed..84e87448 100644 --- a/client/src/controls/mapcontextmenu.ts +++ b/client/src/controls/mapcontextmenu.ts @@ -90,7 +90,6 @@ export class MapContextMenu extends ContextMenu { }); document.addEventListener("contextMenuDeployAircrafts", () => { - this.#spawnOptions.coalition = getActiveCoalition(); if (this.#spawnOptions) { var unitTable = {unitType: this.#spawnOptions.name, location: this.#spawnOptions.latlng, altitude: this.#spawnOptions.altitude, loadout: this.#spawnOptions.loadout}; @@ -219,6 +218,8 @@ export class MapContextMenu extends ContextMenu { this.getContainer()?.querySelector("#explosion-menu")?.classList.toggle("hide", type !== "explosion"); this.getContainer()?.querySelector("#explosion-spawn-button")?.classList.toggle("is-open", type === "explosion"); + (this.getContainer()?.querySelectorAll(".deploy-unit-button"))?.forEach((element: Node) => {(element as HTMLButtonElement).disabled = true;}) + this.#resetAircraftRole(); this.#resetAircraftLabel(); this.#resetHelicopterRole(); diff --git a/client/src/missionhandler/missionhandler.ts b/client/src/missionhandler/missionhandler.ts index 426051c5..7c15210b 100644 --- a/client/src/missionhandler/missionhandler.ts +++ b/client/src/missionhandler/missionhandler.ts @@ -3,6 +3,13 @@ import { getInfoPopup, getMap, getUnitsManager } from ".."; import { Airbase } from "./airbase"; import { Bullseye } from "./bullseye"; import { BLUE_COMMANDER, GAME_MASTER, RED_COMMANDER } from "../constants/constants"; +import { setRTSOptions } from "../server/server"; +import { Dropdown } from "../controls/dropdown"; +import { groundUnitDatabase } from "../units/groundunitdatabase"; +import { createCheckboxOption, getCheckboxOptions } from "../other/utils"; +import { aircraftDatabase } from "../units/aircraftdatabase"; +import { helicopterDatabase } from "../units/helicopterdatabase"; +import { navyUnitDatabase } from "../units/navyunitdatabase"; export class MissionHandler { #bullseyes: { [name: string]: Bullseye } = {}; @@ -12,9 +19,17 @@ export class MissionHandler { #RTSOptions: RTSOptions = {commandMode: "Hide all", restrictSpawns: false, restrictToCoalition: false, setupTime: Infinity, spawnPoints: {red: Infinity, blue: Infinity}, eras: []}; #remainingSetupTime: number = 0; #spentSpawnPoint: number = 0; + #RTSSettingsDialog: HTMLElement; + #rtsErasDropdown: Dropdown; constructor() { + document.addEventListener("showRTSSettingsDialog", () => this.showRTSSettingsDialog()); + document.addEventListener("applyRTSOptions", () => this.#applyRTSOptions()); + /* RTS settings dialog */ + this.#RTSSettingsDialog = document.querySelector("#rts-settings-dialog"); + + this.#rtsErasDropdown = new Dropdown("rts-era-options", () => {}); } updateBullseyes(data: BullseyesData) { @@ -49,26 +64,27 @@ export class MissionHandler { } updateMission(data: MissionData) { - if (data.theatre != this.#theatre) { - this.#theatre = data.theatre; + if (data.mission.theatre != this.#theatre) { + this.#theatre = data.mission.theatre; getMap().setTheatre(this.#theatre); getInfoPopup().setText("Map set to " + this.#theatre); } - this.#dateAndTime = data.dateAndTime; + this.#dateAndTime = data.mission.dateAndTime; - this.#setRTSOptions(data.RTSOptions); + this.#setRTSOptions(data.mission.RTSOptions); getUnitsManager().setCommandMode(this.#RTSOptions.commandMode); this.#remainingSetupTime = this.#RTSOptions.setupTime - this.getDateAndTime().elapsedTime; - var RTSPhaseEl = document.querySelector("#rts-phase"); + var RTSPhaseEl = document.querySelector("#rts-phase") as HTMLElement; if (RTSPhaseEl) { if (this.#remainingSetupTime > 0) { - var remainingTime = `Time to start: -${new Date(this.#remainingSetupTime * 1000).toISOString().substring(14, 19)}`; - RTSPhaseEl.textContent = remainingTime; - } else { - RTSPhaseEl.textContent = "FIGHT"; - } + var remainingTime = `-${new Date(this.#remainingSetupTime * 1000).toISOString().substring(14, 19)}`; + RTSPhaseEl.dataset.remainingTime = remainingTime; + } + + RTSPhaseEl.classList.toggle("setup-phase", this.#remainingSetupTime > 0); + RTSPhaseEl.classList.toggle("game-commenced", this.#remainingSetupTime <= 0); } } @@ -104,6 +120,9 @@ export class MissionHandler { } refreshSpawnPoints() { + if (getUnitsManager().getCommandMode() === GAME_MASTER) + document.querySelector("#spawn-points-container")?.classList.add("hide"); + var spawnPointsEl = document.querySelector("#spawn-points"); if (spawnPointsEl) { spawnPointsEl.textContent = `${this.getAvailableSpawnPoints()}`; @@ -115,6 +134,44 @@ export class MissionHandler { this.refreshSpawnPoints(); } + showRTSSettingsDialog() { + /* Create the checkboxes to select the unit eras */ + var eras = aircraftDatabase.getEras().concat(helicopterDatabase.getEras()).concat(groundUnitDatabase.getEras()).concat(navyUnitDatabase.getEras()); + eras = eras.filter((item: string, index: number) => eras.indexOf(item) === index).sort(); + this.#rtsErasDropdown.setOptionsElements(eras.map((era: string) => { + return createCheckboxOption(era, `Enable ${era} units spawns`, this.#RTSOptions.eras.includes(era)); + })); + + this.#RTSSettingsDialog.classList.remove("hide"); + + const restrictSpawnsCheckbox = this.#RTSSettingsDialog.querySelector("#restrict-spawns")?.querySelector("input") as HTMLInputElement; + const restrictToCoalitionCheckbox = this.#RTSSettingsDialog.querySelector("#restrict-to-coalition")?.querySelector("input") as HTMLInputElement; + const blueSpawnPointsInput = this.#RTSSettingsDialog.querySelector("#blue-spawn-points")?.querySelector("input") as HTMLInputElement; + const redSpawnPointsInput = this.#RTSSettingsDialog.querySelector("#red-spawn-points")?.querySelector("input") as HTMLInputElement; + const setupTimeInput = this.#RTSSettingsDialog.querySelector("#setup-time")?.querySelector("input") as HTMLInputElement; + + restrictSpawnsCheckbox.checked = this.#RTSOptions.restrictSpawns; + restrictToCoalitionCheckbox.checked = this.#RTSOptions.restrictToCoalition; + blueSpawnPointsInput.value = String(this.#RTSOptions.spawnPoints.blue); + redSpawnPointsInput.value = String(this.#RTSOptions.spawnPoints.red); + setupTimeInput.value = String(Math.floor(this.#RTSOptions.setupTime / 60.0)); + } + + #applyRTSOptions() { + this.#RTSSettingsDialog.classList.add("hide"); + + const restrictSpawnsCheckbox = this.#RTSSettingsDialog.querySelector("#restrict-spawns")?.querySelector("input") as HTMLInputElement; + const restrictToCoalitionCheckbox = this.#RTSSettingsDialog.querySelector("#restrict-to-coalition")?.querySelector("input") as HTMLInputElement; + const blueSpawnPointsInput = this.#RTSSettingsDialog.querySelector("#blue-spawn-points")?.querySelector("input") as HTMLInputElement; + const redSpawnPointsInput = this.#RTSSettingsDialog.querySelector("#red-spawn-points")?.querySelector("input") as HTMLInputElement; + const setupTimeInput = this.#RTSSettingsDialog.querySelector("#setup-time")?.querySelector("input") as HTMLInputElement; + + var eras: string[] = []; + const enabledEras = getCheckboxOptions(this.#rtsErasDropdown); + Object.keys(enabledEras).forEach((key: string) => {if (enabledEras[key]) eras.push(key)}); + setRTSOptions(restrictSpawnsCheckbox.checked, restrictToCoalitionCheckbox.checked, {blue: parseInt(blueSpawnPointsInput.value), red: parseInt(redSpawnPointsInput.value)}, eras, parseInt(setupTimeInput.value) * 60); + } + #setRTSOptions(RTSOptions: RTSOptions) { var RTSOptionsChanged = (!RTSOptions.eras.every((value: string, idx: number) => {return value === this.#RTSOptions.eras[idx]}) || RTSOptions.spawnPoints.red !== this.#RTSOptions.spawnPoints.red || @@ -126,8 +183,16 @@ export class MissionHandler { this.setSpentSpawnPoints(0); this.refreshSpawnPoints(); - if (RTSOptionsChanged) + if (RTSOptionsChanged) { document.dispatchEvent(new CustomEvent("RTSOptionsChanged", { detail: this })); + + document.getElementById("rts-toolbar")?.classList.remove("hide"); + const el = document.getElementById("command-mode"); + if (el) { + el.dataset.mode = RTSOptions.commandMode; + el.textContent = RTSOptions.commandMode.toUpperCase(); + } + } } #onAirbaseClick(e: any) { diff --git a/client/src/other/utils.ts b/client/src/other/utils.ts index c9a4c623..231da752 100644 --- a/client/src/other/utils.ts +++ b/client/src/other/utils.ts @@ -6,6 +6,7 @@ import { helicopterDatabase } from "../units/helicopterdatabase"; import { groundUnitDatabase } from "../units/groundunitdatabase"; import { Buffer } from "buffer"; import { ROEs, emissionsCountermeasures, reactionsToThreat, states } from "../constants/constants"; +import { Dropdown } from "../controls/dropdown"; export function bearing(lat1: number, lon1: number, lat2: number, lon2: number) { const φ1 = deg2rad(lat1); // φ, λ in radians @@ -363,4 +364,33 @@ export function convertDateAndTimeToDate(dateAndTime: DateAndTime) { } return new Date(year, month, date.Day, time.h, time.m, time.s); +} + +export function createCheckboxOption(value: string, text: string, checked: boolean = true) { + var div = document.createElement("div"); + div.classList.add("ol-checkbox"); + var label = document.createElement("label"); + label.title = text; + var input = document.createElement("input"); + input.type = "checkbox"; + input.checked = checked; + var span = document.createElement("span"); + span.innerText = value; + label.appendChild(input); + label.appendChild(span); + div.appendChild(label); + return div as HTMLElement; +} + +export function getCheckboxOptions(dropdown: Dropdown) { + var values: { [key: string]: boolean } = {}; + const element = dropdown.getOptionElements(); + for (let idx = 0; idx < element.length; idx++) { + const option = element.item(idx) as HTMLElement; + const key = option.querySelector("span")?.innerText; + const value = option.querySelector("input")?.checked; + if (key !== undefined && value !== undefined) + values[key] = value; + } + return values; } \ No newline at end of file diff --git a/client/src/server/server.ts b/client/src/server/server.ts index 24a160b1..03e9ff8d 100644 --- a/client/src/server/server.ts +++ b/client/src/server/server.ts @@ -1,5 +1,5 @@ import { LatLng } from 'leaflet'; -import { getConnectionStatusPanel, getInfoPopup, getMissionData, getServerStatusPanel, getUnitDataTable, getUnitsManager, setLoginStatus } from '..'; +import { getConnectionStatusPanel, getInfoPopup, getMissionHandler, getServerStatusPanel, getUnitDataTable, getUnitsManager, setLoginStatus } from '..'; import { GeneralSettings, Radio, TACAN } from '../@types/unit'; import { ROEs, emissionsCountermeasures, reactionsToThreat } from '../constants/constants'; @@ -320,13 +320,37 @@ export function setAdvacedOptions(ID: number, isTanker: boolean, isAWACS: boolea POST(data, () => { }); } +export function setRTSOptions(restrictSpawns: boolean, restrictToCoalition: boolean, spawnPoints: {blue: number, red: number}, eras: string[], setupTime: number) { + var command = { + "restrictSpawns": restrictSpawns, + "restrictToCoalition": restrictToCoalition, + "spawnPoints": spawnPoints, + "eras": eras, + "setupTime": setupTime + }; + + var data = { "setRTSOptions": command }; + POST(data, () => { }); +} + export function startUpdate() { /* On the first connection, force request of full data */ - getAirbases((data: AirbasesData) => getMissionData()?.update(data)); - getBullseye((data: BullseyesData) => getMissionData()?.update(data)); - getMission((data: any) => { - getMissionData()?.update(data); + getAirbases((data: AirbasesData) => { checkSessionHash(data.sessionHash); + getMissionHandler()?.updateAirbases(data); + }); + getBullseye((data: BullseyesData) => { + checkSessionHash(data.sessionHash); + getMissionHandler()?.updateBullseyes(data); + }); + getMission((data: MissionData) => { + checkSessionHash(data.sessionHash); + getMissionHandler()?.updateMission(data); + }); + getLogs((data: any) => { + for (let key in data.logs) + console.log(data.logs[key]); + return data.time; }); getUnits((buffer: ArrayBuffer) => {return getUnitsManager()?.update(buffer), true /* Does a full refresh */}); @@ -360,10 +384,8 @@ export function requestRefresh() { getMissionHandler()?.updateMission(data); }); getLogs((data: any) => { - for (let key in data.logs) { - if (key != "requestTime") - console.log(data.logs[key]); - } + for (let key in data.logs) + console.log(data.logs[key]); return data.time; }); diff --git a/client/src/units/groundunitdatabase.ts b/client/src/units/groundunitdatabase.ts index 71489a29..014d80c6 100644 --- a/client/src/units/groundunitdatabase.ts +++ b/client/src/units/groundunitdatabase.ts @@ -1336,7 +1336,7 @@ export class GroundUnitDatabase extends UnitDatabase { "Suidae": { "name": "Suidae", "coalition": "", - "era": "Prehistoric", + "era": "Modern", "label": "Suidae", "shortLabel": "Suidae", "filename": "", diff --git a/client/src/units/unit.ts b/client/src/units/unit.ts index 0e289eeb..8d43c4cb 100644 --- a/client/src/units/unit.ts +++ b/client/src/units/unit.ts @@ -6,7 +6,7 @@ import { CustomMarker } from '../map/custommarker'; import { SVGInjector } from '@tanem/svg-injector'; import { UnitDatabase } from './unitdatabase'; import { TargetMarker } from '../map/targetmarker'; -import { BLUE_COMMANDER, BOMBING, CARPET_BOMBING, DLINK, DataIndexes, FIRE_AT_AREA, GAME_MASTER, IDLE, IRST, MOVE_UNIT, OPTIC, RADAR, RED_COMMANDER, ROEs, RWR, VISUAL, emissionsCountermeasures, reactionsToThreat, states } from '../constants/constants'; +import { BOMBING, CARPET_BOMBING, DLINK, DataIndexes, FIRE_AT_AREA, GAME_MASTER, IDLE, IRST, MOVE_UNIT, OPTIC, RADAR, ROEs, RWR, VISUAL, emissionsCountermeasures, reactionsToThreat, states } from '../constants/constants'; import { Ammo, Contact, GeneralSettings, Offset, Radio, TACAN, UnitIconOptions } from '../@types/unit'; import { DataExtractor } from './dataextractor'; import { groundUnitDatabase } from './groundunitdatabase'; @@ -378,7 +378,7 @@ export class Unit extends CustomMarker { } belongsToCommandedCoalition() { - if (getUnitsManager().getCommandedCoalition() !== this.#coalition) + if (getUnitsManager().getCommandMode() !== GAME_MASTER && getUnitsManager().getCommandedCoalition() !== this.#coalition) return false; return true; } diff --git a/client/src/units/unitdatabase.ts b/client/src/units/unitdatabase.ts index 55f802f1..2af34c0f 100644 --- a/client/src/units/unitdatabase.ts +++ b/client/src/units/unitdatabase.ts @@ -188,10 +188,10 @@ export class UnitDatabase { if (blueprint) return this.getSpawnPointsByName(blueprint.name); else - return 0; + return Infinity; } getSpawnPointsByName(name: string) { - return 0; + return Infinity; } } \ No newline at end of file diff --git a/client/src/units/unitsmanager.ts b/client/src/units/unitsmanager.ts index c19e4afd..2bfa4826 100644 --- a/client/src/units/unitsmanager.ts +++ b/client/src/units/unitsmanager.ts @@ -5,7 +5,7 @@ import { cloneUnit, deleteUnit, spawnAircrafts, spawnGroundUnits, spawnHelicopte import { bearingAndDistanceToLatLng, deg2rad, keyEventWasInInput, latLngToMercator, mToFt, mercatorToLatLng, msToKnots, polyContains, polygonArea, randomPointInPoly, randomUnitBlueprint } from "../other/utils"; import { CoalitionArea } from "../map/coalitionarea"; import { groundUnitDatabase } from "./groundunitdatabase"; -import { BLUE_COMMANDER, DataIndexes, GAME_MASTER, IADSDensities, IDLE, MOVE_UNIT, RED_COMMANDER } from "../constants/constants"; +import { BLUE_COMMANDER, DataIndexes, GAME_MASTER, IADSDensities, IDLE, MOVE_UNIT, NONE, RED_COMMANDER } from "../constants/constants"; import { DataExtractor } from "./dataextractor"; import { Contact } from "../@types/unit"; import { citiesDatabase } from "./citiesdatabase"; @@ -19,7 +19,7 @@ export class UnitsManager { #selectionEventDisabled: boolean = false; #pasteDisabled: boolean = false; #hiddenTypes: string[] = []; - #commandMode: string = GAME_MASTER; + #commandMode: string = NONE; #requestDetectionUpdate: boolean = false; constructor() { @@ -130,11 +130,6 @@ export class UnitsManager { setCommandMode(newCommandMode: string) { if (newCommandMode !== this.#commandMode) { document.dispatchEvent(new CustomEvent("commandModeChanged", { detail: this })); - const el = document.getElementById("command-mode"); - if (el) { - el.dataset.mode = newCommandMode; - el.textContent = newCommandMode.toUpperCase(); - } this.#commandMode = newCommandMode; for (let ID in this.#units) this.#units[ID].updateVisibility(); @@ -568,7 +563,7 @@ export class UnitsManager { units.push({unitType: unit.getName(), location: unit.getPosition()}); } const category = this.getSelectedUnitsTypes()[0]; - this.spawnUnit(category, units, coalition, true); + this.spawnUnits(category, units, coalition, true); } /***********************************************/ @@ -605,7 +600,7 @@ export class UnitsManager { getMap().addTemporaryMarker(position, unit.name, unit.coalition); return {unitType: unit.name, location: position}; }); - this.spawnUnit(groups[groupName][0].category, units, groups[groupName][0].coalition, true); + this.spawnUnits(groups[groupName][0].category, units, groups[groupName][0].coalition, true); } else { groups[groupName].forEach((unit: any) => { @@ -695,28 +690,28 @@ export class UnitsManager { spawnUnits(category: string, units: any, coalition: string = "blue", immediate: boolean = true, airbase: string = "") { var spawnPoints = 0; if (category === "Aircraft") { - if (airbase == "" && getMissionHandler().getRemainingSetupTime() < 0) { + if (airbase == "" && getMissionHandler().getRemainingSetupTime() < 0 && this.getCommandMode() !== GAME_MASTER) { getInfoPopup().setText("Aircrafts can be air spawned during the SETUP phase only"); return false; } spawnPoints = units.reduce((points: number, unit: any) => {return points + aircraftDatabase.getSpawnPointsByName(unit.unitType)}, 0); spawnAircrafts(units, coalition, airbase, immediate, spawnPoints); } else if (category === "Helicopter") { - if (airbase == "" && getMissionHandler().getRemainingSetupTime() < 0) { + if (airbase == "" && getMissionHandler().getRemainingSetupTime() < 0 && this.getCommandMode() !== GAME_MASTER) { getInfoPopup().setText("Helicopters can be air spawned during the SETUP phase only"); return false; } spawnPoints = units.reduce((unit: any, points: number) => {return points + helicopterDatabase.getSpawnPointsByName(unit.unitType)}, 0); spawnHelicopters(units, coalition, airbase, immediate, spawnPoints); } else if (category === "GroundUnit") { - if (getMissionHandler().getRemainingSetupTime() < 0) { + if (getMissionHandler().getRemainingSetupTime() < 0 && this.getCommandMode() !== GAME_MASTER) { getInfoPopup().setText("Ground units can be spawned during the SETUP phase only"); return false; } spawnPoints = units.reduce((unit: any, points: number) => {return points + groundUnitDatabase.getSpawnPointsByName(unit.unitType)}, 0); spawnGroundUnits(units, coalition, immediate, spawnPoints); } else if (category === "NavyUnit") { - if (getMissionHandler().getRemainingSetupTime() < 0) { + if (getMissionHandler().getRemainingSetupTime() < 0 && this.getCommandMode() !== GAME_MASTER) { getInfoPopup().setText("Navy units can be spawned during the SETUP phase only"); return false; } diff --git a/client/views/other/dialogs.ejs b/client/views/other/dialogs.ejs index 7a1be4f0..0619c62b 100644 --- a/client/views/other/dialogs.ejs +++ b/client/views/other/dialogs.ejs @@ -183,7 +183,6 @@ -
@@ -231,4 +230,76 @@
+ + + +
+
+ +
+

RTS mode settings

+
+ +
+
+ +
+ +
+ +
+ +
+ + +
+
+ +
+ +
+
+ +
+ + +
+
Select eras
+
+ +
+
+
+ +
+

Spawn points

+
+
+ +
+ +
+ +
+
+ +
+ +
+ +
+
+ +
+ +
\ No newline at end of file diff --git a/client/views/toolbars/rts.ejs b/client/views/toolbars/rts.ejs index b0aa3d33..0fcf6ae9 100644 --- a/client/views/toolbars/rts.ejs +++ b/client/views/toolbars/rts.ejs @@ -1,5 +1,6 @@ -