diff --git a/client/public/stylesheets/contextmenu.css b/client/public/stylesheets/contextmenu.css
index 17cbd8de..762bd936 100644
--- a/client/public/stylesheets/contextmenu.css
+++ b/client/public/stylesheets/contextmenu.css
@@ -6,6 +6,10 @@
position: relative;
}
+#aircraft-spawn-menu {
+ height: 191px;
+}
+
.ol-contextmenu {
display: flex;
flex-direction: column;
@@ -42,18 +46,42 @@
text-align: left;
}
-.ol-contextmenu>ul::-webkit-scrollbar {
- width: 10px;
-}
-
-.ol-contextmenu>ul::-webkit-scrollbar-track {
- background-color: transparent;
- border-radius: 100px;
+.ol-contextmenu>div:nth-child(2){
+ display: flex;
+ flex-direction: column;
+
+ justify-content: space-between;
+ align-items: center;
+ row-gap: 5px;
}
-.ol-contextmenu>ul::-webkit-scrollbar-thumb {
- background-color: white;
- border-radius: 100px;
- opacity: 0.8;
- margin-top: 10px;
+.ol-contextmenu .ol-select-container{
+ width: 100%;
+ flex:0 0 auto;
+ align-self: stretch;
}
+
+#deploy-unit-button {
+ width: 100%;
+ text-align: center;
+}
+
+#unit-spawn-aircraft {
+ background-image: var( --spawn-aircraft-url );
+}
+
+#unit-spawn-ground {
+ background-image: var( --spawn-ground-url );
+}
+
+#unit-spawn button {
+ border: none;
+ height: 32px;
+ width: 32px;
+}
+
+#unit-spawn button:not(:hover){
+ background-color: transparent !important;
+}
+
+
diff --git a/client/public/stylesheets/olympus.css b/client/public/stylesheets/olympus.css
index 39ebdb41..0995cf05 100644
--- a/client/public/stylesheets/olympus.css
+++ b/client/public/stylesheets/olympus.css
@@ -5,40 +5,40 @@
/* Variables definitions */
:root {
- --accent-green : #8bff63;
- --accent-light-blue : #5ca7ff;
- --background-grey : #3d4651;
- --background-offwhite : #f2f2f3;
- --background-steel : #202831;
- --primary-blue : #247be2;
- --primary-grey : #CFD9E8;
- --primary-red : #ff5858;
- --secondary-blue-outline : #082e44;
- --secondary-dark-steel : #181e25;
- --secondary-gunmetal-grey : #2f2f2f;
- --secondary-light-grey : #797e83;
- --secondary-neutral : #111111;
- --secondary-red-outline : #262222;
- --secondary-yellow : #ffd46893;
- --nav-text : #ECECEC;
+ --accent-green: #8bff63;
+ --accent-light-blue: #5ca7ff;
+ --background-grey: #3d4651;
+ --background-offwhite: #f2f2f3;
+ --background-steel: #202831;
+ --primary-blue: #247be2;
+ --primary-grey: #CFD9E8;
+ --primary-red: #ff5858;
+ --secondary-blue-outline: #082e44;
+ --secondary-dark-steel: #181e25;
+ --secondary-gunmetal-grey: #2f2f2f;
+ --secondary-light-grey: #797e83;
+ --secondary-neutral: #111111;
+ --secondary-red-outline: #262222;
+ --secondary-yellow: #ffd46893;
+ --nav-text: #ECECEC;
- --ol-select-secondary : #545F6C;
-
- --border-radius-xs : 2px;
- --border-radius-sm : 5px;
- --border-radius-md : 10px;
- --border-radius-lg : 15px;
-
- --font-weight-bolder : 600;
+ --ol-select-secondary: #545F6C;
+
+ --border-radius-xs: 2px;
+ --border-radius-sm: 5px;
+ --border-radius-md: 10px;
+ --border-radius-lg: 15px;
+
+ --font-weight-bolder: 600;
}
-* {
- -moz-box-sizing: border-box;
- -webkit-box-sizing: border-box;
- box-sizing: border-box;
+* {
+ -moz-box-sizing: border-box;
+ -webkit-box-sizing: border-box;
+ box-sizing: border-box;
}
@@ -48,25 +48,25 @@ html {
button {
- background-color:var(--background-steel);
- border:1px solid var( --background-steel );
- border-radius: var( --border-radius-sm );
- color:whitesmoke;
- cursor:pointer;
- font-weight: var( --font-weight-bolder );
- padding:8px;
+ background-color: var(--background-steel);
+ border: 1px solid var(--background-steel);
+ border-radius: var(--border-radius-sm);
+ color: whitesmoke;
+ cursor: pointer;
+ font-weight: var(--font-weight-bolder);
+ padding: 8px;
}
button[disabled="disabled"] {
- color: var( --highlight-color );
- cursor:not-allowed;
+ color: var(--highlight-color);
+ cursor: not-allowed;
}
.pill {
- border-radius: var( --border-radius-sm );
- padding:2px 6px;
- width:fit-content;
+ border-radius: var(--border-radius-sm);
+ padding: 2px 6px;
+ width: fit-content;
}
@@ -75,248 +75,282 @@ button[disabled="disabled"] {
background-color: var(--background-steel);
border-radius: 15px;
box-shadow: 0px 2px 5px #000A;
- color:white;
+ color: white;
font-size: 12px;
- height:fit-content;
- padding:10px;
- width:fit-content;
+ height: fit-content;
+ padding: 10px;
+ width: fit-content;
}
.ol-panel hr {
- background-color: var( --secondary-light-grey );
- border:none;
- height:1px;
- margin:20px 0;
- width:100%;
+ background-color: var(--secondary-light-grey);
+ border: none;
+ height: 1px;
+ margin: 20px 0;
+ width: 100%;
}
.ol-panel-padding-lg {
- padding:24px 30px;
+ padding: 24px 30px;
+}
+
+.ol-select-container {
+ width: 100%;
}
.ol-select {
- color: var( --nav-text );
+ position: relative;
+ color: var(--nav-text);
}
-.ol-select > .ol-select-value {
+.ol-select>.ol-select-value {
align-content: center;
box-shadow: 0px 4px 4px rgba(0, 0, 0, 0.25);
- cursor:pointer;
- display:flex;
+ cursor: pointer;
+ display: flex;
justify-content: left;
text-align: center;
white-space: nowrap;
+ width: 100%;
+}
+
+.ol-select:not(.ol-select-image)>.ol-select-value {
+ align-items: center;
+ background-color: var(--background-grey);
+ border-radius: var(--border-radius-sm);
+ padding: 1em;
+ width: 100%;
+ padding-left: 20px;
+ padding-right: 30px;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.ol-select:not(.ol-select-image)>.ol-select-value svg {
+ margin-right: 10px;
+}
+
+.ol-select:not(.ol-select-image)>.ol-select-value:after {
+ position: absolute;
+ content: url("/themes/olympus/images/chevron-down.svg");
+ right: 10px;
+}
+
+.ol-select>.ol-select-options {
+ position: absolute;
+ overflow: hidden;
+ max-height: 0;
+ translate: 0 -2px;
+ z-index: 1000;
+}
+
+.ol-select.ol-select-image>.ol-select-options {
+ position: absolute;
+}
+
+
+.ol-select.is-open>.ol-select-options {
+ max-height: fit-content;
+ overflow: visible;
+ overflow-y: auto;
+ padding: 8px 0;
+ max-height: 300px;
+ min-width: 100%;
+}
+
+.ol-select>.ol-select-options>div {
+ background-color: var(--background-grey);
+ box-shadow: 0 4px 4px rgba(0, 0, 0, 0.25);
+ display: flex;
+ justify-content: left;
+ padding: 6px 25px;
+ width: 100%;
+}
+
+.ol-select>.ol-select-options>div:first-of-type {
+ border-top-left-radius: var(--border-radius-md);
+ border-top-right-radius: var(--border-radius-md);
+ padding-top: 16px;
+}
+
+.ol-select>.ol-select-options>div:last-of-type {
+ border-bottom-left-radius: var(--border-radius-md);
+ border-bottom-right-radius: var(--border-radius-md);
+ padding-bottom: 16px;
+}
+
+.ol-select>.ol-select-options div hr {
+ background-color: white;
+ height: 1px;
+ width: 100%;
+}
+
+.ol-select>.ol-select-options div button {
+ background-color: transparent;
+ border: none;
+ border-radius: 0;
+ font-size: 14px;
+ font-weight: normal;
+ padding: 6px 2px;
+ text-align: left;
+ white-space: nowrap;
width: fit-content;
}
-.ol-select:not( .ol-select-image ) > .ol-select-value {
- align-items: center;
- background-color: var( --background-grey );
- border-radius: var( --border-radius-sm );
- justify-content: center;
- padding:1em;
- width:100%;
-}
-
-.ol-select:not( .ol-select-image ) > .ol-select-value svg {
- margin-right: 10px;
-}
-
-.ol-select:not( .ol-select-image ) > .ol-select-value:after {
- content: url( "/themes/olympus/images/chevron-down.svg" );
- margin-left:10px;
-}
-
-.ol-select > .ol-select-options {
- overflow:hidden;
- max-height: 0;
- translate:0 -2px;
-}
-
-.ol-select.ol-select-image > .ol-select-options {
- position:absolute;
-}
-
-
-.ol-select.is-open > .ol-select-options {
- max-height: fit-content;
- overflow: visible;
- padding:8px 0;
-}
-
-.ol-select > .ol-select-options > div {
- background-color: var( --background-grey );
- box-shadow: 0 4px 4px rgba(0, 0, 0, 0.25);
- display:flex;
- justify-content: left;
- padding:6px 25px;
- width:100%;
-}
-
-.ol-select > .ol-select-options > div:first-of-type {
- border-top-left-radius: var( --border-radius-md );
- border-top-right-radius: var( --border-radius-md );
- padding-top:16px;
-}
-
-.ol-select > .ol-select-options > div:last-of-type {
- border-bottom-left-radius: var( --border-radius-md );
- border-bottom-right-radius: var( --border-radius-md );
- padding-bottom:16px;
-}
-
-.ol-select > .ol-select-options div hr {
- background-color: white;
- height:1px;
- width:100%;
-}
-
-.ol-select > .ol-select-options div button {
- background-color: transparent;
- border:none;
- border-radius: 0;
- font-size:14px;
- font-weight: normal;
- padding:6px 2px;
- text-align: left;
- white-space: nowrap;
- width:fit-content;
-}
-
-.ol-select > .ol-select-options > div button:hover {
+.ol-select>.ol-select-options>div button:hover {
text-decoration: underline;
}
+.ol-select>.ol-select-options::-webkit-scrollbar {
+ width: 10px;
+}
+
+.ol-select>.ol-select-options::-webkit-scrollbar-track {
+ background-color: transparent;
+ border-radius: 100px;
+}
+.ol-select>.ol-select-options::-webkit-scrollbar-thumb {
+ background-color: white;
+ border-radius: 100px;
+ opacity: 0.8;
+ margin-top: 10px;
+}
.ol-panel-list {
- border-radius: var( --border-radius-sm );
+ border-radius: var(--border-radius-sm);
display: flex;
flex-direction: column;
- height: fit-content;
+ height: fit-content;
row-gap: 5px;
- text-align: center;
- width: fit-content;
+ text-align: center;
+ width: fit-content;
}
.ol-panel-list .list-item {
- border-radius: var( --border-radius-md );
- display:flex;
+ border-radius: var(--border-radius-md);
+ display: flex;
justify-content: space-between;
- padding: 6px 10px;
+ padding: 6px 10px;
}
-.ol-panel-list.sortable > .sortable-item {
+.ol-panel-list.sortable>.sortable-item {
align-items: center;
column-gap: 5px;
- display:flex;
+ display: flex;
flex-direction: row;
}
-.ol-panel-list.sortable > .sortable-item > .handle {
- cursor:grab;
- filter:invert(100);
+.ol-panel-list.sortable>.sortable-item>.handle {
+ cursor: grab;
+ filter: invert(100);
}
-.ol-panel-list.sortable > .sortable-item > .handle img {
+.ol-panel-list.sortable>.sortable-item>.handle img {
max-width: 16px;
}
.ol-panel-board {
- display:flex;
+ display: flex;
flex-direction: row;
justify-content: space-evenly;
}
-.ol-panel-board > .panel-section {
+.ol-panel-board>.panel-section {
border-right: 1px solid #555;
- padding:10px;
+ padding: 10px;
}
-.ol-panel-board > .panel-section:last-of-type {
+.ol-panel-board>.panel-section:last-of-type {
border-right-width: 0;
}
-.ol-panel-board h1, .ol-panel-board h2 {
- font-weight: var( --font-weight-bolder );
+.ol-panel-board h1,
+.ol-panel-board h2 {
+ font-weight: var(--font-weight-bolder);
margin: 0;
- padding:0 0 5px 0;
+ padding: 0 0 5px 0;
}
.ol-panel-board h2 {
- font-size:14px;
+ font-size: 14px;
}
-h1, h2, h3, h4, h5, h6 {
+h1,
+h2,
+h3,
+h4,
+h5,
+h6 {
margin: 0px;
}
h1 {
- font-size:36px;
+ font-size: 36px;
font-weight: 800;
}
h2 {
- font-size:24px;
+ font-size: 24px;
font-weight: bold;
}
h3 {
- font-size:18px;
+ font-size: 18px;
font-weight: bold;
}
h4 {
- font-size:14px;
+ font-size: 14px;
font-weight: normal;
}
button.ol-button-warning {
- border: 1px solid var( --primary-red );
- color: var( --primary-red );
+ border: 1px solid var(--primary-red);
+ color: var(--primary-red);
}
nav.ol-panel {
column-gap: 20px;
- display:flex;
+ display: flex;
flex-direction: row;
- height:58px;
+ height: 58px;
}
-nav.ol-panel > :last-child {
- margin-right:5px;
+nav.ol-panel> :last-child {
+ margin-right: 5px;
}
.ol-panel .ol-group {
column-gap: 10px;
- display:flex;
+ display: flex;
flex-direction: row;
- flex-wrap:nowrap;
+ flex-wrap: nowrap;
}
-.ol-panel .ol-group {
- border-radius: var( --border-radius-sm );
+.ol-panel .ol-group {
+ border-radius: var(--border-radius-sm);
}
.ol-panel .ol-group-button-toggle {
align-items: center;
column-gap: 15px;
- display:flex;
+ display: flex;
flex-wrap: nowrap;
white-space: nowrap;
- width:fit-content;
+ width: fit-content;
}
.ol-panel .ol-group-button-toggle button {
align-items: center;
- background-image: url( "/themes/olympus/images/check_square.svg");
+ background-image: url("/themes/olympus/images/check_square.svg");
background-position: 5px 50%;
background-repeat: no-repeat;
- border:0;
+ border: 0;
text-indent: 15px;
}
@@ -324,46 +358,47 @@ nav.ol-panel > :last-child {
.highlight-primary {
- background-color: var(--secondary-light-grey);
+ background-color: var(--secondary-light-grey);
}
.highlight-bluefor {
- background-color: var(--primary-blue);
- color: var(--background-steel )
+ background-color: var(--primary-blue);
+ color: var(--background-steel)
}
.highlight-redfor {
- background-color: var(--primary-red);
+ background-color: var(--primary-red);
}
.highlight-neutral {
- background-color: var(--primary-grey);
+ background-color: var(--primary-grey);
color: var(--secondary-gunmetal-grey)
}
.accent-green {
color: var(--accent-green);
- font-weight: var( --font-weight-bolder );
+ font-weight: var(--font-weight-bolder);
}
+
.accent-light-blue {
color: var(--accent-light-blue);
- font-weight: var( --font-weight-bolder );
+ font-weight: var(--font-weight-bolder);
}
.accent-bluefor {
color: var(--primary-blue);
- font-weight: var( --font-weight-bolder );
+ font-weight: var(--font-weight-bolder);
}
.accent-redfor {
color: var(--primary-red);
- font-weight: var( --font-weight-bolder );
+ font-weight: var(--font-weight-bolder);
}
.accent-neutral {
- color: var( --primary-grey );
- font-weight: var( --font-weight-bolder );
+ color: var(--primary-grey);
+ font-weight: var(--font-weight-bolder);
}
@@ -391,32 +426,32 @@ nav.ol-panel > :last-child {
column-gap: 2px;
}
-.data-row>*:nth-child(2){
+.data-row>*:nth-child(2) {
width: 100px;
}
-.data-row>*:last-child{
+.data-row>*:last-child {
width: 30px;
text-align: right;
}
-.data-row>.icon-small{
+.data-row>.icon-small {
margin: 2px;
}
.slider-container {
- width: 100%;
+ width: 100%;
}
.slider {
width: 100%;
- -webkit-appearance: none;
+ -webkit-appearance: none;
appearance: none;
- height: 2px;
- background: #d3d3d3;
- outline: none;
- opacity: 0.7;
- -webkit-transition: .2s;
+ height: 2px;
+ background: #d3d3d3;
+ outline: none;
+ opacity: 0.7;
+ -webkit-transition: .2s;
transition: opacity .2s;
margin-top: 10px;
margin-bottom: 10px;
@@ -431,20 +466,20 @@ nav.ol-panel > :last-child {
appearance: none;
width: 20px;
height: 20px;
- background: gray;
- cursor: pointer;
+ background: gray;
+ cursor: pointer;
border-radius: 999px;
}
.active .slider::-webkit-slider-thumb {
- background: #5ca7ff;
+ background: #5ca7ff;
}
.slider::-moz-range-thumb {
- width: 20px;
+ width: 20px;
height: 20px;
- background: gray;
- cursor: pointer;
+ background: gray;
+ cursor: pointer;
border-radius: 999px;
}
@@ -458,20 +493,20 @@ nav.ol-panel > :last-child {
}
.ol-measure-box {
- position: absolute;
- padding-left: 0.5em;
- padding-right: 0.5em;
- padding-top: 0.2em;
- padding-bottom: 0.2em;
- background-color: var(--background-steel);
- border-radius: 999px;
- width: fit-content;
- height: fit-content;
- text-align: center;
- color: var(--primary-grey);
+ position: absolute;
+ padding-left: 0.5em;
+ padding-right: 0.5em;
+ padding-top: 0.2em;
+ padding-bottom: 0.2em;
+ background-color: var(--background-steel);
+ border-radius: 999px;
+ width: fit-content;
+ height: fit-content;
+ text-align: center;
+ color: var(--primary-grey);
font-size: 12px;
- z-index: 2000;
- font-weight: var(--font-weight-bolder);
+ z-index: 2000;
+ font-weight: var(--font-weight-bolder);
}
@@ -483,44 +518,44 @@ nav.ol-panel > :last-child {
#unit-info-panel #unit-identification {
align-items: center;
- display:flex;
- margin-bottom:11px;
+ display: flex;
+ margin-bottom: 11px;
}
#unit-info-panel #unit-identification .unit {
- height:28px;
- margin-right:6px;
- width:28px;
+ height: 28px;
+ margin-right: 6px;
+ width: 28px;
}
#unit-info-panel #unit-identification .unit .unit-marker {
background-size: 28px 28px;
- height:28px;
- width:28px;
+ height: 28px;
+ width: 28px;
}
#unit-info-panel #unit-identification .unit .unit-short-label {
- font-size:12px;
+ font-size: 12px;
}
#unit-info-panel #unit-identification #unit-name {
- background-color:transparent;
- border:none;
- color:white;
- font-size:16px;
- font-weight: var( --font-weight-bolder );
- outline:none;
+ background-color: transparent;
+ border: none;
+ color: white;
+ font-size: 16px;
+ font-weight: var(--font-weight-bolder);
+ outline: none;
overflow: hidden;
white-space: nowrap;
width: 150px;
}
#edit-unit-name {
- background-image: url( "/images/buttons/edit.svg" );
+ background-image: url("/images/buttons/edit.svg");
background-repeat: no-repeat;
- height:14px;
- margin-left:10px;
- width:15px;
+ height: 14px;
+ margin-left: 10px;
+ width: 15px;
}
@@ -530,54 +565,95 @@ nav.ol-panel > :last-child {
}
#unit-visibility-control button {
- border:none;
- height:32px;
- width:32px;
+ border: none;
+ height: 32px;
+ width: 32px;
}
#unit-visibility-control-aircraft {
- background-image: var( --visibility-control-aircraft-visible-url );
+ background-image: var(--visibility-control-aircraft-visible-url);
}
body[data-hide-aircraft] #unit-visibility-control-aircraft {
- background-image: var( --visibility-control-aircraft-hidden-url );
+ background-image: var(--visibility-control-aircraft-hidden-url);
}
#unit-visibility-control-ground {
- background-image: var( --visibility-control-ground-visible-url );
+ background-image: var(--visibility-control-ground-visible-url);
}
body[data-hide-ground] #unit-visibility-control-ground {
- background-image: var( --visibility-control-ground-hidden-url );
+ background-image: var(--visibility-control-ground-hidden-url);
}
#unit-visibility-control-sam {
- background-image: var( --visibility-control-sam-visible-url );
+ background-image: var(--visibility-control-sam-visible-url);
}
body[data-hide-sam] #unit-visibility-control-sam {
- background-image: var( --visibility-control-sam-hidden-url );
+ background-image: var(--visibility-control-sam-hidden-url);
}
#unit-visibility-control-threat {
- background-image: var( --visibility-control-threat-visible-url );
+ background-image: var(--visibility-control-threat-visible-url);
}
body[data-hide-threat] #unit-visibility-control-threat {
- background-image: var( --visibility-control-threat-hidden-url );
+ background-image: var(--visibility-control-threat-hidden-url);
}
#unit-visibility-control-naval {
- background-image: var( --visibility-control-naval-visible-url );
+ background-image: var(--visibility-control-naval-visible-url);
}
body[data-hide-naval] #unit-visibility-control-naval {
- background-image: var( --visibility-control-naval-hidden-url );
+ background-image: var(--visibility-control-naval-hidden-url);
}
[data-hide-blue] #coalition-visibility-control #coalition-visibility-control-blue,
[data-hide-red] #coalition-visibility-control #coalition-visibility-control-red,
[data-hide-neutral] #coalition-visibility-control #coalition-visibility-control-neutral {
- background-image:none;
-}
\ No newline at end of file
+ background-image: none;
+}
+
+.toggle {
+ --width: 40px;
+ --height: calc(var(--width) / 2);
+ --border-radius: calc(var(--height) / 2);
+
+ display: inline-block;
+ cursor: pointer;
+}
+
+.toggle-input {
+ display: none;
+}
+
+.toggle-fill {
+ position: relative;
+ width: var(--width);
+ height: var(--height);
+ border-radius: var(--border-radius);
+ transition: background-color 0.2s;
+}
+
+.toggle-fill::after {
+ content: "";
+ position: absolute;
+ top: 2;
+ left: 2;
+ height: calc(var(--height) - 4px);
+ width: calc(var(--height) - 4px);
+ background-color: #ffffff;
+ box-shadow: 0 0 10px rgba(0, 0, 0, 0.25);
+ border-radius: var(--border-radius);
+ transition: transform 0.2s;
+}
+
+.toggle-input:checked ~ .toggle-fill::after {
+ transform: translateX(var(--height));
+}
+
+[data-coalition=blue] {background-color: var(--primary-blue) !important}
+[data-coalition=red] {background-color:var(--primary-red)!important}
\ No newline at end of file
diff --git a/client/public/themes/olympus/images/spawn_aircraft.svg b/client/public/themes/olympus/images/spawn_aircraft.svg
new file mode 100644
index 00000000..e8793e30
--- /dev/null
+++ b/client/public/themes/olympus/images/spawn_aircraft.svg
@@ -0,0 +1,75 @@
+
+
diff --git a/client/public/themes/olympus/images/spawn_ground.svg b/client/public/themes/olympus/images/spawn_ground.svg
new file mode 100644
index 00000000..f0d8bf1c
--- /dev/null
+++ b/client/public/themes/olympus/images/spawn_ground.svg
@@ -0,0 +1,83 @@
+
+
diff --git a/client/public/themes/olympus/olympus.css b/client/public/themes/olympus/olympus.css
index 603bb88f..42e78949 100644
--- a/client/public/themes/olympus/olympus.css
+++ b/client/public/themes/olympus/olympus.css
@@ -162,4 +162,10 @@
--unit-building-marker-neutral-url: url( "/themes/olympus/images/icon_building_neutral.svg" );
--unit-building-marker-red-url: url( "/themes/olympus/images/icon_building_red.svg" );
+
+ /*** Context menu ***/
+
+ --spawn-aircraft-url: url( "/themes/olympus/images/spawn_aircraft.svg" );
+ --spawn-ground-url: url( "/themes/olympus/images/spawn_ground.svg" );
+
}
\ No newline at end of file
diff --git a/client/src/controls/contextmenu.ts b/client/src/controls/contextmenu.ts
index ab51f0bd..0786e0e6 100644
--- a/client/src/controls/contextmenu.ts
+++ b/client/src/controls/contextmenu.ts
@@ -1,41 +1,63 @@
+import { LatLng } from "leaflet";
import { getActiveCoalition, setActiveCoalition } from "..";
import { ContextMenuOption } from "../@types/dom";
+import { ClickEvent } from "../map/map";
+import { spawnAircraft } from "../server/server";
+import { aircraftDatabase } from "../units/aircraftdatabase";
+import { Dropdown } from "./dropdown";
+
+export interface SpawnOptions {
+ role: string;
+ type: string;
+ latlng: LatLng;
+ coalition: string;
+ loadout: string | null;
+ airbaseName: string | null;
+}
export class ContextMenu {
#container: HTMLElement | null;
+ #latlng: LatLng = new LatLng(0, 0);
+ #aircraftRoleDropdown: Dropdown;
+ #aircraftTypeDropdown: Dropdown;
+ #aircraftLoadoutDropdown: Dropdown;
+ //#unitsNumberDropdown: Dropdown;
+ #spawnOptions: SpawnOptions = {role: "", type: "", latlng: this.#latlng, loadout: null, coalition: "blue", airbaseName: null};
constructor(id: string,) {
this.#container = document.getElementById(id);
- this.#container?.querySelector("#switch")?.addEventListener('change', (e) => this.#onSwitch(e))
+ this.#container?.querySelector("#context-menu-switch")?.addEventListener('change', (e) => this.#onSwitch(e));
+
+ this.#aircraftRoleDropdown = new Dropdown("role-options", (role: string) => this.#setAircraftRole(role), aircraftDatabase.getRoles());
+ this.#aircraftTypeDropdown = new Dropdown("aircraft-options", (type: string) => this.#setAircraftType(type));
+ this.#aircraftLoadoutDropdown = new Dropdown("loadout-options", (loadout: string) => this.#setAircraftLoadout(loadout));
+ //this.#unitsNumberDropdown = new Dropdown("#units-options", this.#setAircraftType, [""]);
+
+ document.addEventListener("contextMenuShow", (e: any) => {
+ this.#container?.querySelector("#aircraft-spawn-menu")?.classList.toggle("hide", e.detail.unitType !== "aircraft");
+ })
+
+ document.addEventListener("contextMenuDeployAircraft", () => {
+ this.hide();
+ this.#spawnOptions.coalition = getActiveCoalition();
+ if (this.#spawnOptions)
+ spawnAircraft(this.#spawnOptions);
+ })
+
this.hide();
}
- show(x: number, y: number, title: string, options: ContextMenuOption[], showCoalition: boolean) {
+ show(x: number, y: number, latlng: LatLng) {
+ this.#spawnOptions.latlng = latlng;
this.#container?.classList.toggle("hide", false);
-
- this.#container?.querySelector("#list")?.replaceChildren(...options.map((option: ContextMenuOption) => {
- var li = document.createElement("li");
- var button = document.createElement("button");
- button.textContent = option.tooltip;
- li.appendChild(button);
- button.addEventListener("click", (e: MouseEvent) => option.callback((e.target as HTMLButtonElement).innerText));
- return button;
- }));
-
- this.#container?.querySelector("#switch")?.classList.toggle("hide", !showCoalition);
-
- if (this.#container != null && options.length >= 1) {
- var titleDiv = this.#container.querySelector("#title");
- if (titleDiv)
- titleDiv.textContent = title;
-
- if (x - this.#container.offsetWidth / 2 + this.#container.offsetWidth < window.innerWidth)
- this.#container.style.left = x - this.#container.offsetWidth / 2 + "px";
+ if (this.#container != null) {
+ if (x + this.#container.offsetWidth < window.innerWidth)
+ this.#container.style.left = x + "px";
else
this.#container.style.left = window.innerWidth - this.#container.offsetWidth + "px";
- if (y - 20 + this.#container.offsetHeight < window.innerHeight)
- this.#container.style.top = y - 20 + "px";
+ if (y + this.#container.offsetHeight < window.innerHeight)
+ this.#container.style.top = y + "px";
else
this.#container.style.top = window.innerHeight - this.#container.offsetHeight + "px";
}
@@ -47,10 +69,43 @@ export class ContextMenu {
#onSwitch(e: any) {
if (this.#container != null) {
- if (e.currentTarget.checked)
+ if (e.srcElement.checked)
setActiveCoalition("red");
else
setActiveCoalition("blue");
}
}
+
+ #setAircraftRole(role: string)
+ {
+ if (this.#spawnOptions != null)
+ {
+ this.#spawnOptions.role = role;
+ this.#aircraftTypeDropdown.setOptions(aircraftDatabase.getLabelsByRole(role));
+ }
+ }
+
+ #setAircraftType(type: string)
+ {
+ if (this.#spawnOptions != null)
+ {
+ this.#spawnOptions.type = type;
+ this.#aircraftLoadoutDropdown.setOptions(aircraftDatabase.getLoadoutNamesByRole(type, this.#spawnOptions.role));
+ }
+ }
+
+ #setAircraftLoadout(loadoutName: string)
+ {
+ if (this.#spawnOptions != null)
+ {
+ var loadout = aircraftDatabase.getLoadoutsByName(this.#spawnOptions.type, loadoutName);
+ if (loadout)
+ this.#spawnOptions.loadout = loadout.code;
+ }
+ }
+
+ #setUnitsNumber(unitsNumber: string)
+ {
+
+ }
}
\ No newline at end of file
diff --git a/client/src/controls/dropdown.ts b/client/src/controls/dropdown.ts
new file mode 100644
index 00000000..5861c3e5
--- /dev/null
+++ b/client/src/controls/dropdown.ts
@@ -0,0 +1,32 @@
+export class Dropdown {
+ #element: HTMLElement;
+ #options: HTMLElement;
+ #value: HTMLElement;
+ #callback: CallableFunction;
+
+ constructor(ID: string, callback: CallableFunction, options: string[] | null = null)
+ {
+ this.#element = document.getElementById(ID);
+ var element = this.#element;
+ this.#options = this.#element.querySelector(".ol-select-options");
+ this.#value = this.#element.querySelector(".ol-select-value");
+ this.#callback = callback;
+ if (options != null)
+ this.setOptions(options);
+ }
+
+ setOptions(options: string[])
+ {
+ this.#options.replaceChildren(...options.map((option: string) => {
+ var div = document.createElement("div");
+ var button = document.createElement("button");
+ button.textContent = option;
+ div.appendChild(button);
+ button.addEventListener("click", (e: MouseEvent) => {
+ this.#value.innerText = option;
+ this.#callback(option);
+ });
+ return div;
+ }));
+ }
+}
\ No newline at end of file
diff --git a/client/src/index.ts b/client/src/index.ts
index d58ad7a5..d86d691a 100644
--- a/client/src/index.ts
+++ b/client/src/index.ts
@@ -248,6 +248,7 @@ export function getConnectionStatusPanel() {
export function setActiveCoalition(newActiveCoalition: string) {
activeCoalition = newActiveCoalition;
+ document.querySelectorAll('[data-coalition]').forEach((element: any) => {element.setAttribute("data-coalition", activeCoalition)});
}
export function getActiveCoalition() {
diff --git a/client/src/map/map.ts b/client/src/map/map.ts
index cc24b02d..fd0e3b12 100644
--- a/client/src/map/map.ts
+++ b/client/src/map/map.ts
@@ -5,6 +5,7 @@ import { aircraftDatabase } from "../units/aircraftdatabase";
import { unitTypes } from "../units/unittypes";
import { BoxSelect } from "./boxselect";
import { ContextMenuOption } from "../@types/dom";
+import { SpawnOptions } from "../controls/contextmenu";
export const IDLE = "IDLE";
export const MOVE_UNIT = "MOVE_UNIT";
@@ -116,10 +117,10 @@ export class Map extends L.Map {
}
/* Context Menu */
- showContextMenu(e: ClickEvent | SpawnEvent, title: string, options: ContextMenuOption[], showCoalition: boolean = false) {
- var x = e.x;
- var y = e.y;
- getContextMenu()?.show(x, y, title, options, showCoalition);
+ showContextMenu(e: any, spawnOptions: SpawnOptions | null = null) {
+ var x = e.originalEvent.x;
+ var y = e.originalEvent.y;
+ getContextMenu()?.show(x, y, e.latlng);
document.dispatchEvent(new CustomEvent("mapContextMenu"));
}
@@ -139,7 +140,7 @@ export class Map extends L.Map {
/* Spawn from air base */
spawnFromAirbase(e: SpawnEvent)
{
- this.#aircraftSpawnMenu(e);
+ //this.#aircraftSpawnMenu(e);
}
/* Event handlers */
@@ -164,15 +165,8 @@ export class Map extends L.Map {
#onContextMenu(e: any) {
this.hideContextMenu();
if (this.#state === IDLE) {
- var spawnEvent: SpawnEvent = {x: e.originalEvent.x, y: e.originalEvent.y, latlng: e.latlng, airbaseName: null, coalitionID: null};
if (this.#state == IDLE) {
- var options = [
- { "tooltip": "Spawn air unit", "src": "spawnAir.png", "callback": () => this.#aircraftSpawnMenu(spawnEvent) },
- { "tooltip": "Spawn ground unit", "src": "spawnGround.png", "callback": () => this.#groundUnitSpawnMenu(spawnEvent) },
- { "tooltip": "Smoke", "src": "spawnSmoke.png", "callback": () => this.#smokeSpawnMenu(spawnEvent) },
- //{ "tooltip": "Explosion", "src": "spawnExplosion.png", "callback": () => this.#explosionSpawnMenu(e) }
- ]
- this.showContextMenu(spawnEvent, "Action", options, false);
+ this.showContextMenu(e);
}
}
else if (this.#state === MOVE_UNIT) {
@@ -215,99 +209,4 @@ export class Map extends L.Map {
this.#lastMousePosition.x = e.originalEvent.x;
this.#lastMousePosition.y = e.originalEvent.y;
}
-
- /* Spawning menus */
- #aircraftSpawnMenu(e: SpawnEvent) {
- var options = [
- { 'coalition': true, 'tooltip': 'CAP', 'src': 'spawnCAP.png', 'callback': () => this.#selectAircraft(e, "cap") },
- { 'coalition': true, 'tooltip': 'CAS', 'src': 'spawnCAS.png', 'callback': () => this.#selectAircraft(e, "cas") },
- { 'coalition': true, 'tooltip': 'Strike', 'src': 'spawnStrike.png', 'callback': () => this.#selectAircraft(e, "strike") },
- { 'coalition': true, 'tooltip': 'Recce', 'src': 'spawnStrike.png', 'callback': () => this.#selectAircraft(e, "reconnaissance") },
- { 'coalition': true, 'tooltip': 'Tanker', 'src': 'spawnTanker.png', 'callback': () => this.#selectAircraft(e, "tanker") },
- { 'coalition': true, 'tooltip': 'AWACS', 'src': 'spawnAWACS.png', 'callback': () => this.#selectAircraft(e, "awacs") },
- { 'coalition': true, 'tooltip': 'Drone', 'src': 'spawnDrone.png', 'callback': () => this.#selectAircraft(e, "drone") },
- { 'coalition': true, 'tooltip': 'Transport', 'src': 'spawnTransport.png', 'callback': () => this.#selectAircraft(e, "transport") },
- ]
- if (e.airbaseName != null)
- this.showContextMenu(e, "Spawn at " + e.airbaseName, options, true);
- else
- this.showContextMenu(e, "Spawn air unit", options, true);
- }
-
- #groundUnitSpawnMenu(e: SpawnEvent) {
- var options = [
- {'coalition': true, 'tooltip': 'Howitzer', 'src': 'spawnHowitzer.png', 'callback': () => this.#selectGroundUnit(e, "Howitzers")},
- {'coalition': true, 'tooltip': 'SAM', 'src': 'spawnSAM.png', 'callback': () => this.#selectGroundUnit(e, "SAM")},
- {'coalition': true, 'tooltip': 'IFV', 'src': 'spawnIFV.png', 'callback': () => this.#selectGroundUnit(e, "IFV")},
- {'coalition': true, 'tooltip': 'Tank', 'src': 'spawnTank.png', 'callback': () => this.#selectGroundUnit(e, "Tanks")},
- {'coalition': true, 'tooltip': 'MLRS', 'src': 'spawnMLRS.png', 'callback': () => this.#selectGroundUnit(e, "MLRS")},
- {'coalition': true, 'tooltip': 'Radar', 'src': 'spawnRadar.png', 'callback': () => this.#selectGroundUnit(e, "Radar")},
- {'coalition': true, 'tooltip': 'Unarmed', 'src': 'spawnUnarmed.png', 'callback': () => this.#selectGroundUnit(e, "Unarmed")}
- ]
- this.showContextMenu(e, "Spawn ground unit", options, true);
- }
-
- #smokeSpawnMenu(e: SpawnEvent) {
- this.hideContextMenu();
- var options = [
- {'tooltip': 'Red smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideContextMenu(); spawnSmoke('red', e.latlng)}, 'tint': 'red'},
- {'tooltip': 'White smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideContextMenu(); spawnSmoke('white', e.latlng)}, 'tint': 'white'},
- {'tooltip': 'Blue smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideContextMenu(); spawnSmoke('blue', e.latlng)}, 'tint': 'blue'},
- {'tooltip': 'Green smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideContextMenu(); spawnSmoke('green', e.latlng)}, 'tint': 'green'},
- {'tooltip': 'Orange smoke', 'src': 'spawnSmoke.png', 'callback': () => {this.hideContextMenu(); spawnSmoke('orange', e.latlng)}, 'tint': 'orange'},
- ]
- this.showContextMenu(e, "Spawn smoke", options, false);
- }
-
- #explosionSpawnMenu(e: SpawnEvent) {
-
- }
-
- /* Show unit selection for air units */
- #selectAircraft(e: SpawnEvent, role: string) {
- this.hideContextMenu();
- var options = aircraftDatabase.getLabelsByRole(role);
- this.showContextMenu(e, "Select aircraft",
- options.map((option: string) => {
- return {tooltip: option, src: "", callback: (label: string) => {
- this.hideContextMenu();
- var name = aircraftDatabase.getNameByLabel(label);
- if (name != null)
- this.#unitSelectPayload(e, name, role);
- }}}), true);
- }
-
- /* Show weapon selection for air units */
- #unitSelectPayload(e: SpawnEvent, unitType: string, role: string) {
- this.hideContextMenu();
- var options = aircraftDatabase.getLoadoutNamesByRole(unitType, role);
- //options = payloadNames[unitType]
- if (options != undefined && options.length > 0) {
- options.sort();
- this.showContextMenu({x: e.x, y: e.y, latlng: e.latlng}, "Select loadout",
- options.map((option: string) => {
- return {tooltip: option, src: "", callback: (loadoutName: string) => {
- this.hideContextMenu();
- var loadout = aircraftDatabase.getLoadoutsByName(unitType, loadoutName);
- spawnAircraft(unitType, e.latlng, getActiveCoalition(), loadout != null? loadout.code: "", e.airbaseName);
- }}}), true);
- }
- else {
- spawnAircraft(unitType, e.latlng, getActiveCoalition());
- }
- }
-
- /* Show unit selection for ground units */
- #selectGroundUnit(e: any, group: string)
- {
- this.hideContextMenu();
- var options = unitTypes.vehicles[group];
- options.sort();
- this.showContextMenu(e, "Select ground unit",
- options.map((option: string) => {
- return {tooltip: option, src: "", callback: (unitType: string) => {
- this.hideContextMenu();
- spawnGroundUnit(unitType, e.latlng, getActiveCoalition());
- }}}), true);
- }
}
diff --git a/client/src/missionhandler/missionhandler.ts b/client/src/missionhandler/missionhandler.ts
index 67f7e9ae..193b69a9 100644
--- a/client/src/missionhandler/missionhandler.ts
+++ b/client/src/missionhandler/missionhandler.ts
@@ -84,9 +84,9 @@ export class MissionHandler
else
options = ["Spawn unit"];
- getMap().showContextMenu(e.originalEvent, e.sourceTarget.getName(),
- options.map((option) => {return {tooltip: option, src: "", callback: (label: string) => {this.#onAirbaseOptionSelection(e, label)}}}, false)
- )
+ //getMap().showContextMenu(e.originalEvent, e.sourceTarget.getName(),
+ // options.map((option) => {return {tooltip: option, src: "", callback: (label: string) => {this.#onAirbaseOptionSelection(e, label)}}}, false)
+ //)
}
#onAirbaseOptionSelection(e: any, option: string) {
diff --git a/client/src/server/server.ts b/client/src/server/server.ts
index 9768bb18..7f66425a 100644
--- a/client/src/server/server.ts
+++ b/client/src/server/server.ts
@@ -1,5 +1,6 @@
import * as L from 'leaflet'
import { setConnected } from '..';
+import { SpawnOptions } from '../controls/contextmenu';
/* Edit here to change server address */
const REST_ADDRESS = "http://localhost:30000/olympus";
@@ -69,8 +70,8 @@ export function spawnGroundUnit(type: string, latlng: L.LatLng, coalition: strin
POST(data, () => { });
}
-export function spawnAircraft(type: string, latlng: L.LatLng, coalition: string, payloadName: string | null = null, airbaseName: string | null = null) {
- var command = { "type": type, "location": latlng, "coalition": coalition, "payloadName": payloadName != null? payloadName: "", "airbaseName": airbaseName != null? airbaseName: ""};
+export function spawnAircraft(spawnOptions: SpawnOptions) {
+ var command = { "type": spawnOptions.type, "location": spawnOptions.latlng, "coalition": spawnOptions.coalition, "payloadName": spawnOptions.loadout != null? spawnOptions.loadout: "", "airbaseName": spawnOptions.airbaseName != null? spawnOptions.airbaseName: ""};
var data = { "spawnAir": command }
POST(data, () => { });
}
diff --git a/client/src/units/unit.ts b/client/src/units/unit.ts
index c91bd337..f7a82c0e 100644
--- a/client/src/units/unit.ts
+++ b/client/src/units/unit.ts
@@ -290,7 +290,7 @@ export class Unit extends Marker {
'Attack',
'Follow'
]
- getMap().showContextMenu(e.originalEvent, "Action: " + this.getData().unitName, options.map((option: string) => {return {tooltip: option, src: "", callback: (action: string) => this.#executeAction(action)}}));
+ //getMap().showContextMenu(e.originalEvent, "Action: " + this.getData().unitName, options.map((option: string) => {return {tooltip: option, src: "", callback: (action: string) => this.#executeAction(action)}}));
}
#executeAction(action: string) {
diff --git a/client/src/units/unitdatabase.ts b/client/src/units/unitdatabase.ts
index 1359d491..97d23035 100644
--- a/client/src/units/unitdatabase.ts
+++ b/client/src/units/unitdatabase.ts
@@ -6,6 +6,24 @@ export class UnitDatabase {
}
+ getRoles()
+ {
+ var roles: string[] = [];
+ for (let unit in this.units)
+ {
+ for (let loadout of this.units[unit].loadouts)
+ {
+ for (let role of loadout.roles)
+ {
+ role = role.toUpperCase();
+ if (role !== "" && !roles.includes(role))
+ roles.push(role);
+ }
+ }
+ }
+ return roles;
+ }
+
getLabelsByRole(role: string)
{
var units = [];
@@ -13,7 +31,7 @@ export class UnitDatabase {
{
for (let loadout of this.units[unit].loadouts)
{
- if (loadout.roles.includes(role))
+ if (loadout.roles.includes(role) || loadout.roles.includes(role.toLowerCase()))
{
units.push(this.units[unit].label)
break;
@@ -28,7 +46,7 @@ export class UnitDatabase {
var loadouts = [];
for (let loadout of this.units[unit].loadouts)
{
- if (loadout.roles.includes(role) || loadout.roles.includes(""))
+ if (loadout.roles.includes(role) || loadout.roles.includes(role.toLowerCase()) || loadout.roles.includes(""))
{
loadouts.push(loadout.name)
}
diff --git a/client/views/contextmenu.ejs b/client/views/contextmenu.ejs
index 2997ad88..cead9fcc 100644
--- a/client/views/contextmenu.ejs
+++ b/client/views/contextmenu.ejs
@@ -1,19 +1,44 @@