diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index b254d0ad..acedeb18 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -14,8 +14,8 @@
# Versioning
-
diff --git a/LICENSE b/LICENSE
index 45f950ac..4b0e677e 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright 2017-2021 Max Haniyeu (Azgaar), azgaar.fmg@yandex.com
+Copyright 2017-2024 Max Haniyeu (Azgaar), azgaar.fmg@yandex.com
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
@@ -14,6 +14,7 @@ copies or substantial portions of the Software.
You can produce, without restrictions, any derivative works from the original
software and even reap commercial benefits from the sale of the secondary product.
+The derivates include created maps, map images, screenshots, videos, and other materials.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
@@ -21,4 +22,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
\ No newline at end of file
+SOFTWARE.
diff --git a/components/slider-input.js b/components/slider-input.js
new file mode 100644
index 00000000..f1732027
--- /dev/null
+++ b/components/slider-input.js
@@ -0,0 +1,78 @@
+{
+ const style = /* css */ `
+ slider-input {
+ display: flex;
+ align-items: center;
+ gap: .4em;
+ }
+ `;
+
+ const styleElement = document.createElement("style");
+ styleElement.setAttribute("type", "text/css");
+ styleElement.innerHTML = style;
+ document.head.appendChild(styleElement);
+}
+
+{
+ const template = document.createElement("template");
+ template.innerHTML = /* html */ `
+
+
+ `;
+
+ class SliderInput extends HTMLElement {
+ constructor() {
+ super();
+ this.appendChild(template.content.cloneNode(true));
+
+ const range = this.querySelector("input[type=range]");
+ const number = this.querySelector("input[type=number]");
+
+ range.value = number.value = this.value || this.getAttribute("value") || 50;
+ range.min = number.min = this.getAttribute("min") || 0;
+ range.max = number.max = this.getAttribute("max") || 100;
+ range.step = number.step = this.getAttribute("step") || 1;
+
+ range.addEventListener("input", this.handleEvent.bind(this));
+ number.addEventListener("input", this.handleEvent.bind(this));
+ range.addEventListener("change", this.handleEvent.bind(this));
+ number.addEventListener("change", this.handleEvent.bind(this));
+ }
+
+ handleEvent(e) {
+ const value = e.target.value;
+ const isNaN = Number.isNaN(Number(value));
+ if (isNaN || value === "") return e.stopPropagation();
+
+ const range = this.querySelector("input[type=range]");
+ const number = this.querySelector("input[type=number]");
+ this.value = range.value = number.value = value;
+
+ this.dispatchEvent(
+ new CustomEvent(e.type, {
+ detail: {value},
+ bubbles: true,
+ composed: true
+ })
+ );
+ }
+
+ set value(value) {
+ const range = this.querySelector("input[type=range]");
+ const number = this.querySelector("input[type=number]");
+ range.value = number.value = value;
+ }
+
+ get value() {
+ const number = this.querySelector("input[type=number]");
+ return number.value;
+ }
+
+ get valueAsNumber() {
+ const number = this.querySelector("input[type=number]");
+ return number.valueAsNumber;
+ }
+ }
+
+ customElements.define("slider-input", SliderInput);
+}
diff --git a/icons.css b/icons.css
index ae40ee00..4740f091 100644
--- a/icons.css
+++ b/icons.css
@@ -253,7 +253,7 @@
.icon-coa:before {content:'\f3ed'; font-size: .9em; color: #999;} /* '' */
.icon-half:before {font-weight: bold;content:'½';}
.icon-voice:before {content:'🔊';}
-
+.icon-robot:before {content:'🤖';}
.icon-die:before {content:'🎲';}
.icon-button-die:before {content:'🎲'; padding-right: .4em;}
.icon-button-power:before {content:'💪'; padding-right: .6em;}
diff --git a/index.css b/index.css
index d5c5ef34..eb1e82fc 100644
--- a/index.css
+++ b/index.css
@@ -258,7 +258,6 @@ i.icon-lock {
cursor: pointer;
}
-#routeEditor > *,
#labelEditor div {
display: inline-block;
}
@@ -357,6 +356,14 @@ text.drag {
font-weight: bold;
}
+button.ui-button:disabled {
+ filter: brightness(0.95);
+}
+
+button.ui-button:disabled:hover {
+ cursor: default;
+}
+
.ui-dialog,
#optionsContainer {
user-select: none;
@@ -437,6 +444,14 @@ button.options:hover {
margin: 0.8em 0 0 0;
}
+#options .tip {
+ color: #444;
+ font-size: 0.9em;
+ font-family: sans-serif;
+ font-style: italic;
+ margin-left: 0.5em;
+}
+
#aboutContent {
text-align: justify;
}
@@ -518,7 +533,53 @@ input[type="color"]::-webkit-color-swatch-wrapper {
font-size: smaller;
}
+#options input[type="text"] {
+ border: 0px;
+ width: 62%;
+ font-size: smaller;
+}
+
+#options output {
+ text-align: right;
+ font-size: smaller;
+}
+
+#options input[type="number"] {
+ font-size: 0.8em;
+ border: 0;
+ text-align: right;
+ background-color: transparent;
+ width: 3.3em;
+}
+
+#options input[type="number"]::-webkit-inner-spin-button,
+#options input[type="number"]::-webkit-outer-spin-button {
+ -webkit-appearance: none;
+ margin: 0;
+}
+
+#options input[type="number"] {
+ appearance: textfield;
+ -moz-appearance: textfield;
+}
+
+#options input[type="number"]:hover {
+ outline: 1px solid var(--dark-solid);
+}
+
+#options input.paired {
+ text-align: center;
+ background-color: white;
+}
+
+#options input.long {
+ width: 100%;
+ background-color: white;
+ text-align: left;
+}
+
#options input[type="range"] {
+ width: 100%;
height: 8px;
background: 0;
appearance: none;
@@ -561,55 +622,7 @@ input[type="color"]::-webkit-color-swatch-wrapper {
height: 2px;
}
-#options input[type="number"] {
- font-size: 0.8em;
-}
-
-#options input[type="text"] {
- border: 0px;
- width: 62%;
- font-size: smaller;
-}
-
-#optionsContent output {
- text-align: right;
- font-size: smaller;
-}
-
-#optionsContent input[type="number"] {
- border: 0;
- text-align: right;
- background-color: transparent;
- width: 3.3em;
- -moz-appearance: textfield;
-}
-
-#optionsContent input[type="number"]::-webkit-inner-spin-button,
-#optionsContent input[type="number"]::-webkit-outer-spin-button {
- -webkit-appearance: none;
- margin: 0;
-}
-
-#optionsContent input[type="number"]:hover {
- outline: 1px solid var(--dark-solid);
-}
-
-#optionsContent input.paired {
- text-align: center;
- background-color: white;
-}
-
-#optionsContent input.long {
- width: 100%;
- background-color: white;
- text-align: left;
-}
-
-#optionsContent input[type="range"] {
- width: 100%;
-}
-
-#optionsContent select {
+#options select {
width: 100%;
}
@@ -634,19 +647,6 @@ input[type="color"]::-webkit-color-swatch-wrapper {
transform: translate(0px, 1px);
}
-#styleElements input[type="range"] {
- width: 64%;
-}
-
-#styleElements select {
- width: 64%;
-}
-
-#styleElements input[type="number"] {
- width: 6em;
- border: 0;
-}
-
#styleSelectFont > option {
font-size: 2em;
}
@@ -717,9 +717,6 @@ input[type="color"]::-webkit-color-swatch-wrapper {
padding: 0.45em 0.75em;
margin: 0.35em 0;
transition: 0.1s;
- font-size: 1em;
- text-transform: capitalize;
-
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
@@ -734,7 +731,7 @@ input[type="color"]::-webkit-color-swatch-wrapper {
background-color: var(--header-active);
}
-#toolsContent div {
+#toolsContent > .grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
margin: 0.2em 0;
@@ -758,7 +755,7 @@ input[type="color"]::-webkit-color-swatch-wrapper {
#viewMode > button {
padding: 0.35em;
- margin: 0.2em 0.3em 0.6em 0.3em;
+ margin: 0.3em 0.3em 0.6em 0.3em;
float: left;
width: 30.7%;
}
@@ -1261,7 +1258,6 @@ i.resetButton:active {
padding: 0;
height: 2px;
background: #d4d4d4;
- top: -0.35em;
position: relative;
appearance: none;
-webkit-appearance: none;
@@ -1610,6 +1606,7 @@ div.states > .riverType {
#burgBody > div > div,
#riverBody > div,
+#routeBody > div,
#lakeBody > div {
padding: 0.1em;
}
@@ -1617,6 +1614,7 @@ div.states > .riverType {
#riverBody div.label,
#riverBody input,
#riverBody select,
+#routeBody div.label,
#lakeBody div.label,
#lakeBody input,
#lakeBody select {
@@ -1624,6 +1622,12 @@ div.states > .riverType {
width: 7em;
}
+#routeBody input,
+#routeBody select {
+ display: inline-block;
+ width: 10em;
+}
+
#stateNameEditor div.label,
#provinceNameEditor div.label,
#regimentBody div.label,
@@ -1829,11 +1833,6 @@ div.editorLine {
padding: 0px 3px !important;
}
-#unitsBody > div > * {
- display: inline-block;
- margin-bottom: 0.2em;
-}
-
.unitsHeader {
margin: 0.8em 0 0 -1.1em;
font-weight: bold;
@@ -1845,28 +1844,21 @@ div.editorLine {
margin: 6px 0 0 6px;
}
-#unitsBody > div > div {
+#unitsBody label {
+ display: inline-block;
width: 9em;
}
-#unitsBody > div > input[type="range"] {
- width: 7em;
-}
-
#unitsBody > div > select,
#unitsBody > div > input[type="text"] {
- width: 12em;
-}
-
-#unitsBody > div > input[type="number"] {
- width: 4.35em;
-}
-
-#unitsBody > div > input,
-#unitsBody > div > select {
+ width: 14.4em;
border: 1px solid #e9e9e9;
}
+#unitsBody input[type="range"] {
+ width: 9em;
+}
+
#unitsEditor i.icon-lock-open,
#unitsEditor i.icon-lock {
color: #626573;
@@ -2181,7 +2173,7 @@ svg.button {
#worldControls input[type="number"] {
border: 1px solid #e5e5e5;
padding: 0px;
- width: 3.2em;
+ width: 4em;
}
#worldControls i.icon-lock-open,
@@ -2240,10 +2232,6 @@ svg.button {
fill: blue;
}
-#globeOutline {
- fill: url(#temperatureGradient);
-}
-
#globeArea {
fill: white;
fill-opacity: 0.3;
@@ -2254,6 +2242,11 @@ svg.button {
stroke-width: 0.2;
}
+#globePrimeMeridian {
+ stroke: blue;
+ stroke-width: 1.4;
+}
+
#globeEquator {
stroke: red;
stroke-width: 1.4;
@@ -2375,6 +2368,38 @@ svg.button {
background: #ccc;
}
+.separator {
+ display: flex;
+ align-items: center;
+ text-align: center;
+
+ font-style: italic;
+ font-weight: bold;
+ color: #222;
+ margin: 0.8em 0 0 0;
+}
+.separator::before,
+.separator::after {
+ content: "";
+ flex: 1;
+ border-bottom: 1px solid #333;
+}
+.separator:not(:empty)::before {
+ margin-right: 0.25em;
+}
+.separator:not(:empty)::after {
+ margin-left: 0.25em;
+}
+
+@keyframes clockwiseBorderPulse {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
@media print {
div,
canvas {
diff --git a/index.html b/index.html
index 384bafc8..42bc9c5f 100644
--- a/index.html
+++ b/index.html
@@ -138,7 +138,7 @@
}
-
+
@@ -380,7 +380,7 @@
- Displayed layers and layers order: -
+Displayed layers and layers order:
View mode:
@@ -835,27 +836,24 @@ -Click to configure:
-Click to overview:
-Click to regenerate:
-Click to add:
-Click to create a new map:
-- Fantasy Map Generator is a - free + Fantasy Map Generator is an open source @@ -2327,7 +2296,7 @@
Join our Discord server and Reddit community to ask - questions, get help and share maps. + questions, get help and share maps. The created maps can be used for free, even for commercial purposes.
@@ -2525,15 +2494,15 @@