From 493c75b6e781cb7e2252800358565e695a3b375f Mon Sep 17 00:00:00 2001 From: Pax1601 Date: Mon, 15 May 2023 17:37:20 +0200 Subject: [PATCH] Added hotgroups ui and basic functions User interaction still TBD --- client/public/stylesheets/olympus.css | 33 ++++++++++ client/public/stylesheets/units.css | 93 +++++++++++---------------- client/src/index.ts | 23 ++++++- client/src/panels/hotgrouppanel.ts | 25 +++++++ client/src/units/unit.ts | 35 +++++++--- client/src/units/unitsmanager.ts | 22 ++++++- client/views/hotgrouppanel.ejs | 3 + client/views/index.ejs | 2 +- 8 files changed, 167 insertions(+), 69 deletions(-) create mode 100644 client/src/panels/hotgrouppanel.ts create mode 100644 client/views/hotgrouppanel.ejs diff --git a/client/public/stylesheets/olympus.css b/client/public/stylesheets/olympus.css index d7905e9b..15a5578c 100644 --- a/client/public/stylesheets/olympus.css +++ b/client/public/stylesheets/olympus.css @@ -856,4 +856,37 @@ body[data-hide-navyunit] #unit-visibility-control-navyunit { 50% { opacity: 0; } +} + +#hotgroup-panel { + position: absolute; + bottom: 40px; + left: 50%; + translate: -50%; + z-index: 9999; + display: flex; + column-gap: 10px; +} + +#hotgroup-panel>div { + width: 50px; + height: 50px; + background-color: var(--background-steel); + border-radius: var(--border-radius-sm); + color: white; + display: flex; + align-items: center; + justify-content: center; + font-weight: bold; + border: 0px solid transparent; +} + +#hotgroup-panel>div:hover { + cursor: pointer; + border: 2px solid white; +} + +.hotgroup-selector>.unit-hotgroup { + display: flex; + translate: 0% -300%; } \ No newline at end of file diff --git a/client/public/stylesheets/units.css b/client/public/stylesheets/units.css index 0142c055..0499468d 100644 --- a/client/public/stylesheets/units.css +++ b/client/public/stylesheets/units.css @@ -3,7 +3,7 @@ --unit-centre-x: calc(var(--unit-width) / 2); --unit-centre-y: calc(var(--unit-height) / 2); - --unit-hotgroup-height: 10px; + --unit-hotgroup-height: 15px; --unit-hotgroup-width: var(--unit-hotgroup-height); @@ -23,7 +23,7 @@ width: 100%; } -[data-object|="unit"] .unit-selected-spotlight { +.unit-selected-spotlight { background-color: var(--unit-spotlight-fill); border-radius: 50%; display: none; @@ -32,8 +32,7 @@ z-index: 1; } - -[data-object|="unit"] .unit-vvi { +.unit-vvi { align-self: center; background: var(--secondary-gunmetal-grey); display: flex; @@ -46,30 +45,7 @@ z-index: 3; } - -[data-object|="unit"] .unit-hotgroup { - align-content: center; - background-color: black; - border-radius: var(--border-radius-xs); - display: none; - height: var(--unit-hotgroup-height); - justify-content: center; - position: absolute; - transform: rotate(-45deg); - translate: 0 -275%; - width: var(--unit-hotgroup-width); - z-index: 5; -} - -[data-object|="unit"] .unit-hotgroup-id { - background-color: transparent; - color: white; - font-size: 9px; - font-weight: bolder; - transform: rotate(45deg); -} - -[data-object|="unit"] .unit-marker-border { +.unit-marker-border { border-radius: var(--border-radius-sm); display: none; height: calc(var(--unit-aircraft-height) + (var(--unit-label-border-width) * 2)); @@ -78,6 +54,29 @@ z-index: 2; } +.unit-hotgroup { + align-content: center; + background-color: var(--background-steel); + border-radius: var(--border-radius-xs); + display: none; + height: var(--unit-hotgroup-height); + justify-content: center; + position: absolute; + transform: rotate(-45deg); + translate: 0 -200%; + width: var(--unit-hotgroup-width); + z-index: 5; +} + +.unit-hotgroup-id { + background-color: transparent; + color: white; + font-size: 9px; + font-weight: bolder; + transform: rotate(45deg); + translate: -1px 1px; +} + /****************************** Marker @@ -92,8 +91,6 @@ z-index: 3; } - - /* Air */ [data-object|="unit-aircraft"] .unit-marker { @@ -110,7 +107,6 @@ background-image: var(--unit-aircraft-marker-neutral-selected-url); } - [data-object|="unit-aircraft"][data-coalition="blue"] .unit-marker { background-image: var(--unit-aircraft-marker-blue-url); } @@ -136,9 +132,6 @@ background-image: var(--unit-aircraft-marker-red-selected-url); } - - - /* Ground vehicles (not SAMs) */ [data-object|="unit-groundunit"] .unit-marker { @@ -279,7 +272,6 @@ /* Building */ - [data-object|="unit-building"] .unit-marker { background-image: var(--unit-building-marker-neutral-url); height: var(--unit-building-marker-height); @@ -296,8 +288,6 @@ background-image: var(--unit-building-marker-red-url); } - - /* Weapons */ [data-object|="unit-missile"], @@ -439,8 +429,6 @@ overflow: visible; } - - [data-object|="unit"]:hover .unit-ammo, [data-object|="unit"]:hover .unit-fuel { display: flex; @@ -460,9 +448,6 @@ background-color: var(--secondary-gunmetal-grey); } - - - [data-object|="unit"][data-coalition="blue"][data-is-selected] .unit-short-label { color: var(--secondary-blue-text); } @@ -496,8 +481,6 @@ background-color: var(--secondary-red-outline); } - - @keyframes pulse { 50% { opacity: 0; @@ -508,7 +491,6 @@ animation: pulse 1.5s linear infinite; } - [data-object|="unit"] .unit-state { background-repeat: no-repeat; position: absolute; @@ -569,20 +551,19 @@ } -[data-object|="unit-aircraft"][ data-is-dead] .unit-selected-spotlight, -[data-object|="unit-aircraft"][ data-is-dead] .unit-short-label, -[data-object|="unit-aircraft"][ data-is-dead] .unit-vvi, -[data-object|="unit-aircraft"][ data-is-dead] .unit-hotgroup, -[data-object|="unit-aircraft"][ data-is-dead] .unit-hotgroup-id, -[data-object|="unit-aircraft"][ data-is-dead] .unit-state, -[data-object|="unit-aircraft"][ data-is-dead] .unit-fuel, -[data-object|="unit-aircraft"][ data-is-dead] .unit-ammo, -[data-object|="unit-aircraft"][ data-is-dead]:hover .unit-fuel, -[data-object|="unit-aircraft"][ data-is-dead]:hover .unit-ammo { +[data-object|="unit-aircraft"][data-is-dead] .unit-selected-spotlight, +[data-object|="unit-aircraft"][data-is-dead] .unit-short-label, +[data-object|="unit-aircraft"][data-is-dead] .unit-vvi, +[data-object|="unit-aircraft"][data-is-dead] .unit-hotgroup, +[data-object|="unit-aircraft"][data-is-dead] .unit-hotgroup-id, +[data-object|="unit-aircraft"][data-is-dead] .unit-state, +[data-object|="unit-aircraft"][data-is-dead] .unit-fuel, +[data-object|="unit-aircraft"][data-is-dead] .unit-ammo, +[data-object|="unit-aircraft"][data-is-dead]:hover .unit-fuel, +[data-object|="unit-aircraft"][data-is-dead]:hover .unit-ammo { display: none !important; } - [data-object|="unit-aircraft"][ data-is-dead] .unit-summary>* { display: none; } diff --git a/client/src/index.ts b/client/src/index.ts index b3e7e63e..b4dbee16 100644 --- a/client/src/index.ts +++ b/client/src/index.ts @@ -9,11 +9,12 @@ import { AIC } from "./aic/aic"; import { ATC } from "./atc/atc"; import { FeatureSwitches } from "./featureswitches"; import { LogPanel } from "./panels/logpanel"; -import { getAirbases, getBullseye, getConfig, getFreezed, getMission, getUnits, setAddress, setCredentials, setFreezed, startUpdate, toggleDemoEnabled } from "./server/server"; +import { getConfig, getFreezed, setAddress, setCredentials, setFreezed, startUpdate, toggleDemoEnabled } from "./server/server"; import { UnitDataTable } from "./units/unitdatatable"; import { keyEventWasInInput } from "./other/utils"; import { Popup } from "./popups/popup"; import { Dropdown } from "./controls/dropdown"; +import { HotgroupPanel } from "./panels/hotgrouppanel"; var map: Map; @@ -28,6 +29,7 @@ var connectionStatusPanel: ConnectionStatusPanel; var unitControlPanel: UnitControlPanel; var mouseInfoPanel: MouseInfoPanel; var logPanel: LogPanel; +var hotgroupPanel: HotgroupPanel; var infoPopup: Popup; @@ -50,6 +52,7 @@ function setup() { unitControlPanel = new UnitControlPanel("unit-control-panel"); connectionStatusPanel = new ConnectionStatusPanel("connection-status-panel"); mouseInfoPanel = new MouseInfoPanel("mouse-info-panel"); + hotgroupPanel = new HotgroupPanel("hotgroup-panel"); //logPanel = new LogPanel("log-panel"); /* Popups */ @@ -147,6 +150,20 @@ function setupEvents() { case "ArrowDown": getMap().handleMapPanning(ev); break; + case "Digit1": + case "Digit2": + case "Digit3": + case "Digit4": + case "Digit5": + case "Digit6": + case "Digit7": + case "Digit8": + case "Digit9": + if (ev.ctrlKey) + getUnitsManager().selectedUnitsAddToHotgroup(parseInt(ev.key)); + else + getUnitsManager().selectUnitsByHotgroup(parseInt(ev.key)); + break; } }); @@ -232,6 +249,10 @@ export function getConnectionStatusPanel() { return connectionStatusPanel; } +export function getHotgroupPanel() { + return hotgroupPanel; +} + export function setActiveCoalition(newActiveCoalition: string) { activeCoalition = newActiveCoalition; document.querySelectorAll('[data-active-coalition]').forEach((element: any) => { element.setAttribute("data-active-coalition", activeCoalition) }); diff --git a/client/src/panels/hotgrouppanel.ts b/client/src/panels/hotgrouppanel.ts new file mode 100644 index 00000000..328ef81d --- /dev/null +++ b/client/src/panels/hotgrouppanel.ts @@ -0,0 +1,25 @@ +import { getUnitsManager } from ".."; +import { Panel } from "./panel"; + +export class HotgroupPanel extends Panel { + addHotgroup(hotgroup: number) { + this.removeHotgroup(hotgroup); + const hotgroupHtml = `
+
${hotgroup}
+
+ x${getUnitsManager().getUnitsByHotgroup(hotgroup).length}` + var el = document.createElement("div"); + el.classList.add("hotgroup-selector"); + el.innerHTML = hotgroupHtml; + el.toggleAttribute(`data-hotgroup-${hotgroup}`, true) + this.getElement().appendChild(el); + el.addEventListener("click", () => { + getUnitsManager().selectUnitsByHotgroup(hotgroup); + }); + } + + removeHotgroup(hotgroup: number) { + const el = this.getElement().querySelector(`[data-hotgroup-${hotgroup}]`) as HTMLElement; + if (el) el.remove(); + } +} \ No newline at end of file diff --git a/client/src/units/unit.ts b/client/src/units/unit.ts index 82d10681..6d3d1709 100644 --- a/client/src/units/unit.ts +++ b/client/src/units/unit.ts @@ -77,6 +77,8 @@ export class Unit extends Marker { #timer: number = 0; + #hotgroup: number | null = null; + static getConstructor(type: string) { if (type === "GroundUnit") return GroundUnit; if (type === "Aircraft") return Aircraft; @@ -265,6 +267,14 @@ export class Unit extends Marker { return this.#selectable; } + setHotgroup(hotgroup: number | null) { + this.#hotgroup = hotgroup; + } + + getHotgroup() { + return this.#hotgroup; + } + /********************** Visibility *************************/ updateVisibility() { @@ -556,7 +566,6 @@ export class Unit extends Marker { }); /* Turn on ordnance 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"); @@ -568,25 +577,30 @@ export class Unit extends Marker { var newHasOtherAmmo = false; Object.values(this.getMissionData().ammo).forEach((ammo: any) => { if (ammo.desc.category == 1 && ammo.desc.missileCategory == 1) { - if (ammo.desc.guidance == 4 || ammo.desc.guidance == 5) { + if (ammo.desc.guidance == 4 || ammo.desc.guidance == 5) newHasFox1 = true; - } - else if (ammo.desc.guidance == 2) { + else if (ammo.desc.guidance == 2) newHasFox2 = true; - } - else if (ammo.desc.guidance == 3) { + else if (ammo.desc.guidance == 3) newHasFox3 = true; - } } - else { + else newHasOtherAmmo = true; - } }); if (hasFox1 != newHasFox1) element.querySelector(".unit")?.toggleAttribute("data-has-fox-1", newHasFox1); if (hasFox2 != newHasFox2) element.querySelector(".unit")?.toggleAttribute("data-has-fox-2", newHasFox2); if (hasFox3 != newHasFox3) element.querySelector(".unit")?.toggleAttribute("data-has-fox-3", newHasFox3); if (hasOtherAmmo != newHasOtherAmmo) element.querySelector(".unit")?.toggleAttribute("data-has-other-ammo", newHasOtherAmmo); + + /* Draw the hotgroup element */ + if (this.#hotgroup) { + element.querySelector(".unit")?.toggleAttribute("data-is-in-hotgroup", this.#hotgroup != null); + const hotgroupEl = element.querySelector(".unit-hotgroup-id") as HTMLElement; + if (hotgroupEl) + hotgroupEl.innerText = String(this.#hotgroup); + } + } /* Set vertical offset for altitude stacking */ @@ -732,6 +746,9 @@ export class GroundUnit extends Unit {
${role?.substring(0, 1)?.toUpperCase() || ""}
+
+
+
` } diff --git a/client/src/units/unitsmanager.ts b/client/src/units/unitsmanager.ts index 62ac5db6..2018b4d4 100644 --- a/client/src/units/unitsmanager.ts +++ b/client/src/units/unitsmanager.ts @@ -1,5 +1,5 @@ import { LatLng, LatLngBounds } from "leaflet"; -import { getInfoPopup, getMap, getUnitDataTable } from ".."; +import { getHotgroupPanel, getInfoPopup, getMap, getUnitDataTable } from ".."; import { Unit } from "./unit"; import { cloneUnit } from "../server/server"; import { IDLE, MOVE_UNIT } from "../map/map"; @@ -45,6 +45,10 @@ export class UnitsManager { return null; } + getUnitsByHotgroup(hotgroup: number) { + return Object.values(this.#units).filter((unit: Unit) => {return unit.getHotgroup() == hotgroup}); + } + addUnit(ID: number, data: UnitData) { /* The name of the unit category is exactly the same as the constructor name */ var constructor = Unit.getConstructor(data.baseData.category); @@ -112,6 +116,10 @@ export class UnitsManager { } } + selectUnitsByHotgroup(hotgroup: number) { + this.getUnitsByHotgroup(hotgroup).forEach((unit: Unit) => unit.setSelected(true)) + } + getSelectedUnitsType() { if (this.getSelectedUnits().length == 0) return undefined; @@ -259,7 +267,7 @@ export class UnitsManager { } selectedUnitsRefuel() { - var selectedUnits = this.getSelectedUnits({excludeHumans: true}); + var selectedUnits = this.getSelectedUnits({excludeHumans: true}); for (let idx in selectedUnits) { selectedUnits[idx].refuel(); } @@ -309,6 +317,16 @@ export class UnitsManager { this.#showActionMessage(selectedUnits, `following unit ${this.getUnitByID(ID)?.getBaseData().unitName}`); } + selectedUnitsAddToHotgroup(hotgroup: number) + { + var selectedUnits = this.getSelectedUnits(); + for (let idx in selectedUnits) { + selectedUnits[idx].setHotgroup(hotgroup); + } + this.#showActionMessage(selectedUnits, `added to hotgroup ${hotgroup}`); + getHotgroupPanel().addHotgroup(hotgroup); + } + /***********************************************/ copyUnits() { this.#copiedUnits = this.getSelectedUnits(); /* Can be applied to humans too */ diff --git a/client/views/hotgrouppanel.ejs b/client/views/hotgrouppanel.ejs new file mode 100644 index 00000000..f8d065cc --- /dev/null +++ b/client/views/hotgrouppanel.ejs @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/client/views/index.ejs b/client/views/index.ejs index ae159a9d..afb19752 100644 --- a/client/views/index.ejs +++ b/client/views/index.ejs @@ -24,7 +24,6 @@
<%- include('aic.ejs') %> <%- include('atc.ejs') %> - <%- include('contextmenus.ejs') %> <%- include('unitcontrolpanel.ejs') %> <%- include('unitinfopanel.ejs') %> @@ -34,6 +33,7 @@ <%- include('dialogs.ejs') %> <%- include('unitdatatable.ejs') %> <%- include('popups.ejs') %> + <%- include('hotgrouppanel.ejs') %>