diff --git a/client/public/stylesheets/leaflet/leaflet.css b/client/public/stylesheets/leaflet/leaflet.css
index 9ade8dc4..1981009f 100644
--- a/client/public/stylesheets/leaflet/leaflet.css
+++ b/client/public/stylesheets/leaflet/leaflet.css
@@ -60,11 +60,6 @@
padding: 0;
}
-.leaflet-container img.leaflet-tile {
- /* See: https://bugs.chromium.org/p/chromium/issues/detail?id=600120 */
- mix-blend-mode: plus-lighter;
-}
-
.leaflet-container.leaflet-touch-zoom {
-ms-touch-action: pan-x pan-y;
touch-action: pan-x pan-y;
@@ -651,7 +646,7 @@ svg.leaflet-image-layer.leaflet-interactive path {
}
/* Printing */
-
+
@media print {
/* Prevent printers from removing background-images of controls. */
.leaflet-control {
diff --git a/client/public/stylesheets/other/contextmenus.css b/client/public/stylesheets/other/contextmenus.css
index 172afdac..9aa269a8 100644
--- a/client/public/stylesheets/other/contextmenus.css
+++ b/client/public/stylesheets/other/contextmenus.css
@@ -8,24 +8,20 @@
z-index: 9999;
}
-#map-contextmenu>div:nth-child(2) {
- align-items: center;
+
+
+/* #map-contextmenu>div:nth-child(n+4)>div {
+ width: 100%;
+} */
+
+#map-contextmenu .spawn-mode {
display: flex;
- flex-direction: row;
+ flex-direction: column;
justify-content: space-between;
- padding-right: 0px;
+ row-gap: 5px;
}
-#map-contextmenu>div:nth-child(3) {
- align-items: center;
- display: flex;
- flex-direction: row;
- justify-content: space-between;
- padding-right: 0px;
-}
-
-#map-contextmenu>div:nth-child(n+4) {
- align-items: center;
+.ol-context-menu-panel {
display: flex;
flex-direction: column;
justify-content: space-between;
@@ -33,10 +29,6 @@
padding: 20px;
}
-#map-contextmenu>div:nth-child(n+4)>div {
- width: 100%;
-}
-
.contextmenu-advanced-options,
.contextmenu-metadata {
align-items: center;
@@ -143,20 +135,6 @@
padding: 2px 5px;
}
-/*
-.ol-tag-CA {
- background-color: #FF000022;
-}
-
-.ol-tag-Radar {
- background-color: #00FF0022;
-}
-
-.ol-tag-IR {
- background-color: #0000FF22;
-}
-*/
-
.unit-loadout-list {
min-width: 0;
}
@@ -187,7 +165,65 @@
content: " (" attr(data-points) " points)";
}
-.upper-bar svg>* {
+#spawn-mode-tabs {
+ align-items: center;
+ column-gap: 6px;
+ display: flex;
+ position: absolute;
+ right: 0;
+ top:0;
+ translate: -6px -100%;
+ z-index: 9998;
+}
+
+#spawn-mode-tabs button {
+ align-items: center;
+ border-bottom:2px solid transparent;
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+ border-top-left-radius: var(--border-radius-sm);
+ border-top-right-radius: var(--border-radius-sm);
+ display: flex;
+ height:32px;
+ justify-content: center;
+ margin:0;
+ width:32px;
+}
+
+#spawn-mode-tabs button:hover {
+ background-color: var(--background-steel);
+}
+
+[data-coalition="blue"] + #spawn-mode-tabs button {
+ border-bottom-color: var(--primary-blue);
+}
+
+
+[data-coalition="red"] + #spawn-mode-tabs button {
+ border-bottom-color: var(--primary-red);
+}
+
+
+[data-coalition="neutral"] + #spawn-mode-tabs button {
+ border-bottom-color: var(--primary-neutral);
+}
+
+#spawn-mode-tabs button svg {
+ height:24px;
+ margin:3px;
+ width:24px;
+}
+
+.upper-bar {
+ align-items: center;
+ display: flex;
+ flex-direction: row;
+ justify-content: space-between;
+ padding-right: 0px;
+}
+
+.upper-bar svg>*,
+#spawn-mode-tabs button svg * {
fill: white;
}
@@ -200,24 +236,52 @@
margin-left: auto;
}
+#spawn-history-menu {
+ display:flex;
+ flex-direction: column;
+ max-height: 300px;
+ row-gap: 6px;
+}
+
+#spawn-history-menu button {
+ border-radius: 0;
+ height:32px;
+ width:100%;
+}
+
+#spawn-history-menu button[data-spawned-coalition="blue"] {
+ border-left:4px solid var(--primary-blue);
+}
+
+#spawn-history-menu button[data-spawned-coalition="red"] {
+ border-left:4px solid var(--primary-red);
+}
+
+#spawn-history-menu button[data-spawned-coalition="neutral"] {
+ border-left:4px solid var(--primary-neutral);
+}
+
[data-coalition="blue"]#active-coalition-label,
[data-coalition="blue"].deploy-unit-button,
[data-coalition="blue"]#spawn-airbase-aircraft-button,
-[data-coalition="blue"].create-iads-button {
+[data-coalition="blue"].create-iads-button,
+[data-coalition="blue"] + .upper-bar .spawn-mode-tabs button.active {
background-color: var(--primary-blue)
}
[data-coalition="red"]#active-coalition-label,
[data-coalition="red"].deploy-unit-button,
[data-coalition="red"]#spawn-airbase-aircraft-button,
-[data-coalition="red"].create-iads-button {
+[data-coalition="red"].create-iads-button,
+[data-coalition="red"] + .upper-bar .spawn-mode-tabs button.active {
background-color: var(--primary-red)
}
[data-coalition="neutral"]#active-coalition-label,
[data-coalition="neutral"].deploy-unit-button,
[data-coalition="neutral"]#spawn-airbase-aircraft-button,
-[data-coalition="neutral"].create-iads-button {
+[data-coalition="neutral"].create-iads-button,
+[data-coalition="neutral"] + .upper-bar .spawn-mode-tabs button.active {
background-color: var(--primary-neutral)
}
diff --git a/client/public/themes/olympus/images/buttons/other/arrow-down-solid.svg b/client/public/themes/olympus/images/buttons/other/arrow-down-solid.svg
new file mode 100644
index 00000000..a31ed4b6
--- /dev/null
+++ b/client/public/themes/olympus/images/buttons/other/arrow-down-solid.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/client/public/themes/olympus/images/buttons/other/clock-rotate-left-solid.svg b/client/public/themes/olympus/images/buttons/other/clock-rotate-left-solid.svg
new file mode 100644
index 00000000..50966792
--- /dev/null
+++ b/client/public/themes/olympus/images/buttons/other/clock-rotate-left-solid.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/client/src/contextmenus/mapcontextmenu.ts b/client/src/contextmenus/mapcontextmenu.ts
index d7619c99..38ece85c 100644
--- a/client/src/contextmenus/mapcontextmenu.ts
+++ b/client/src/contextmenus/mapcontextmenu.ts
@@ -7,6 +7,7 @@ import { CoalitionArea } from "../map/coalitionarea/coalitionarea";
import { AirDefenceUnitSpawnMenu, AircraftSpawnMenu, GroundUnitSpawnMenu, HelicopterSpawnMenu, NavyUnitSpawnMenu } from "../controls/unitspawnmenu";
import { Airbase } from "../mission/airbase";
import { SmokeMarker } from "../map/markers/smokemarker";
+import { UnitSpawnTable } from "../interfaces";
/** The MapContextMenu is the main contextmenu shown to the user whenever it rightclicks on the map. It is the primary interaction method for the user.
* It allows to spawn units, create explosions and smoke, and edit CoalitionAreas.
@@ -96,6 +97,8 @@ export class MapContextMenu extends ContextMenu {
this.getContainer()?.addEventListener("hide", () => this.#airDefenceUnitSpawnMenu.clearCirclesPreviews());
this.getContainer()?.addEventListener("hide", () => this.#groundUnitSpawnMenu.clearCirclesPreviews());
this.getContainer()?.addEventListener("hide", () => this.#navyUnitSpawnMenu.clearCirclesPreviews());
+
+ this.#setupHistory();
}
/** Show the contextmenu on top of the map, usually at the location where the user has clicked on it.
@@ -257,4 +260,62 @@ export class MapContextMenu extends ContextMenu {
this.#groundUnitSpawnMenu.setCountries();
this.#navyUnitSpawnMenu.setCountries();
}
+
+
+ /** Handles all of the logic for historal logging.
+ *
+ */
+ #setupHistory() {
+ const spawnModes = this.getContainer()?.querySelectorAll(".spawn-mode");
+ const activeCoalitionLabel = document.getElementById("active-coalition-label");
+ this.getContainer()?.querySelectorAll(".spawn-mode-tab").forEach((btn:Element) => {
+ btn.addEventListener("click", (ev:MouseEventInit) => {
+ spawnModes?.forEach(div => div.classList.add("hide"));
+
+ const prevSiblings = [];
+ let prev = btn.previousElementSibling;
+
+ while ( prev ) {
+ prevSiblings.push(prev);
+ prev = prev.previousElementSibling;
+ }
+
+ if (spawnModes && spawnModes[prevSiblings.length]) {
+ spawnModes[prevSiblings.length].classList.remove("hide");
+ }
+
+ if (activeCoalitionLabel) activeCoalitionLabel.classList.toggle("hide", !btn.hasAttribute("data-show-label"));
+ });
+ });
+
+ const history = document.getElementById("spawn-history-menu");
+ document.addEventListener( "unitSpawned", (ev:CustomEventInit) => {
+ const buttons = history.querySelectorAll("button");
+ const detail:any = ev.detail;
+ if (buttons.length === 0) history.innerHTML = ""; // Take out any "no data" messages
+ const button = document.createElement("button");
+ button.setAttribute("data-spawned-coalition", detail.coalition);
+ button.setAttribute("data-unit-type", detail.unitSpawnTable[0].unitType);
+ button.setAttribute("data-unit-qty", detail.unitSpawnTable.length);
+ button.innerHTML = `${detail.unitSpawnTable[0].unitType} (${detail.unitSpawnTable.length})`;
+
+ // Remove a previous instance to save clogging up the list
+ const previous:any = [].slice.call(buttons).find( (button:Element) => (
+ detail.coalition === button.getAttribute("data-spawned-coalition") &&
+ detail.unitSpawnTable[0].unitType === button.getAttribute("data-unit-type") &&
+ detail.unitSpawnTable.length === parseInt(button.getAttribute("data-unit-qty") || "-1")));
+
+ if (previous instanceof HTMLElement) previous.remove();
+
+ button.addEventListener("click", (ev:MouseEventInit) => {
+ detail.unitSpawnTable.forEach((table:UnitSpawnTable, i:number) => {
+ table.location = this.getLatLng(); // Set to new menu location
+ table.location.lat += 0.00015 * i;
+ });
+ getApp().getUnitsManager().spawnUnits(detail.category, detail.unitSpawnTable, detail.coalition, detail.immediate, detail.airbase, detail.country);
+ });
+
+ history.prepend(button);
+ });
+ }
}
\ No newline at end of file
diff --git a/client/src/unit/unitsmanager.ts b/client/src/unit/unitsmanager.ts
index da455dc1..28556b46 100644
--- a/client/src/unit/unitsmanager.ts
+++ b/client/src/unit/unitsmanager.ts
@@ -1427,6 +1427,16 @@ export class UnitsManager {
if (spawnPoints <= getApp().getMissionManager().getAvailableSpawnPoints()) {
getApp().getMissionManager().setSpentSpawnPoints(spawnPoints);
spawnFunction();
+ document.dispatchEvent( new CustomEvent( "unitSpawned", {
+ "detail": {
+ "airbase": airbase,
+ "category": category,
+ "coalition": coalition,
+ "country": country,
+ "immediate": immediate,
+ "unitSpawnTable": units
+ }
+ }));
return true;
} else {
(getApp().getPopupsManager().get("infoPopup") as Popup).setText("Not enough spawn points available!");
diff --git a/client/views/contextmenus/map.ejs b/client/views/contextmenus/map.ejs
index a3b87b49..4f375786 100644
--- a/client/views/contextmenus/map.ejs
+++ b/client/views/contextmenus/map.ejs
@@ -1,58 +1,71 @@