mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-16 17:31:24 +01:00
v1.3a
This commit is contained in:
parent
7c74c3d29f
commit
f4a84fc6d6
22 changed files with 1678 additions and 325 deletions
|
|
@ -250,7 +250,9 @@
|
||||||
.icon-smooth:before {font-weight: bold;content:'∼';}
|
.icon-smooth:before {font-weight: bold;content:'∼';}
|
||||||
.icon-disrupt:before {font-weight: bold;content:'෴';}
|
.icon-disrupt:before {font-weight: bold;content:'෴';}
|
||||||
.icon-if:before {font-style: italic; font-weight: bold;content:'if';}
|
.icon-if:before {font-style: italic; font-weight: bold;content:'if';}
|
||||||
.icon-fleur:before {content: '⚜'; font-size: 1.1em; margin: -2px;}
|
/* .icon-coa:before {content: '⚜'; font-size: 1.1em; margin: -2px;} */
|
||||||
|
.icon-coa:before {content:'\f3ed'; font-size: .9em; color: #999;} /* '' */
|
||||||
|
.icon-half:before {font-weight: bold;content:'½';}
|
||||||
.icon-curve:before {content: 'C';}
|
.icon-curve:before {content: 'C';}
|
||||||
.icon-area:before {content: 'O';}
|
.icon-area:before {content: 'O';}
|
||||||
.icon-curve:before,
|
.icon-curve:before,
|
||||||
|
|
|
||||||
152
index.css
152
index.css
|
|
@ -10,16 +10,36 @@ body {
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
input, select, button {
|
input, select, button {
|
||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input, select, textarea {
|
||||||
|
border: 0.5px solid #DBDFE6;
|
||||||
|
border-radius: .5px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
height: 1.6em;
|
height: 1.6em;
|
||||||
|
border-top-color: #abadb3;
|
||||||
|
padding: 0;
|
||||||
|
text-indent: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
input {
|
input {
|
||||||
height: 1.1em;
|
border-top-color: #abadb3;
|
||||||
|
padding: 2px;
|
||||||
|
text-indent: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
textarea {
|
||||||
|
padding: 2px;
|
||||||
|
text-indent: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#map {
|
#map {
|
||||||
|
|
@ -50,11 +70,7 @@ input, button, select, a, textarea {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
button, select, a {
|
button, select, a, .pointer {
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.pointer {
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -79,7 +95,7 @@ button, select, a {
|
||||||
fill-rule: evenodd;
|
fill-rule: evenodd;
|
||||||
}
|
}
|
||||||
|
|
||||||
#lakes, #coastline {
|
#lakes, #coastline, #armies {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -101,7 +117,7 @@ button, select, a {
|
||||||
stroke-linejoin: round;
|
stroke-linejoin: round;
|
||||||
}
|
}
|
||||||
|
|
||||||
#regions, #provs, #terrs, #biomes, #tooltip, #temperature, #texture, #landmass {
|
#regions, #cults, #relig, #biomes, #provs, #terrs, #biomes, #tooltip, #temperature, #texture, #landmass {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -193,6 +209,29 @@ i.icon-lock {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#armies rect {
|
||||||
|
stroke-width: .3;
|
||||||
|
stroke: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
#armies text {
|
||||||
|
pointer-events: none;
|
||||||
|
user-select: none;
|
||||||
|
stroke: none;
|
||||||
|
fill: #fff;
|
||||||
|
text-shadow: 0 0 4px #000;
|
||||||
|
font-size: 6px;
|
||||||
|
dominant-baseline: central;
|
||||||
|
text-anchor: middle;
|
||||||
|
font-family: Helvetica;
|
||||||
|
}
|
||||||
|
|
||||||
|
#regimentBase {
|
||||||
|
stroke-width: .3;
|
||||||
|
stroke: #000;
|
||||||
|
cursor: move;
|
||||||
|
}
|
||||||
|
|
||||||
.chartInfo {
|
.chartInfo {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
|
|
@ -231,8 +270,8 @@ i.icon-lock {
|
||||||
marker-end: url(#end-arrow);
|
marker-end: url(#end-arrow);
|
||||||
stroke: #333333;
|
stroke: #333333;
|
||||||
stroke-dasharray: 5;
|
stroke-dasharray: 5;
|
||||||
stroke-dashoffset: 100;
|
stroke-dashoffset: 1000;
|
||||||
animation: dash 8s linear backwards;
|
animation: dash 80s linear backwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes dash {
|
@keyframes dash {
|
||||||
|
|
@ -375,6 +414,8 @@ input[type="color"]::-webkit-color-swatch-wrapper {
|
||||||
-moz-appearance: none;
|
-moz-appearance: none;
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
|
border: 0;
|
||||||
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#options input[type="range"]::-webkit-slider-thumb {
|
#options input[type="range"]::-webkit-slider-thumb {
|
||||||
|
|
@ -412,17 +453,12 @@ input[type="color"]::-webkit-color-swatch-wrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
#options input[type="number"] {
|
#options input[type="number"] {
|
||||||
height: 1.3em;
|
|
||||||
line-height: 1.2em;
|
|
||||||
font-size: .8em;
|
font-size: .8em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#options input[type="text"] {
|
#options input[type="text"] {
|
||||||
border: 0px;
|
border: 0px;
|
||||||
padding-left: 3px;
|
|
||||||
width: 62%;
|
width: 62%;
|
||||||
height: 1.1em;
|
|
||||||
line-height: 1.3em;
|
|
||||||
font-size: smaller;
|
font-size: smaller;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -441,8 +477,8 @@ input[type="color"]::-webkit-color-swatch-wrapper {
|
||||||
|
|
||||||
#optionsContent input[type=number]::-webkit-inner-spin-button,
|
#optionsContent input[type=number]::-webkit-inner-spin-button,
|
||||||
#optionsContent input[type=number]::-webkit-outer-spin-button {
|
#optionsContent input[type=number]::-webkit-outer-spin-button {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#optionsContent input[type="number"]:hover {
|
#optionsContent input[type="number"]:hover {
|
||||||
|
|
@ -450,8 +486,6 @@ input[type="color"]::-webkit-color-swatch-wrapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
#optionsContent input.paired {
|
#optionsContent input.paired {
|
||||||
width: 2.5em;
|
|
||||||
margin-left: -.3em;
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
}
|
}
|
||||||
|
|
@ -460,8 +494,6 @@ input[type="color"]::-webkit-color-swatch-wrapper {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
background-color: white;
|
background-color: white;
|
||||||
text-align: left;
|
text-align: left;
|
||||||
padding-left: 0.4em;
|
|
||||||
box-sizing: border-box;
|
|
||||||
height: 1.5em;
|
height: 1.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -509,7 +541,7 @@ input[type="color"]::-webkit-color-swatch-wrapper {
|
||||||
#styleElements input[type="number"] {
|
#styleElements input[type="number"] {
|
||||||
width: 52px;
|
width: 52px;
|
||||||
border: 0;
|
border: 0;
|
||||||
padding-left: 2.5px;
|
padding-left: 2.5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sticked button {
|
#sticked button {
|
||||||
|
|
@ -1006,6 +1038,7 @@ i.resetButton:active {
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-dialog input[type="range"] {
|
.ui-dialog input[type="range"] {
|
||||||
|
padding: 0;
|
||||||
height: 2px;
|
height: 2px;
|
||||||
background: #d4d4d4;
|
background: #d4d4d4;
|
||||||
top: -.35em;
|
top: -.35em;
|
||||||
|
|
@ -1035,8 +1068,7 @@ i.resetButton:active {
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-dialog input[type="number"] {
|
.ui-dialog input[type="number"] {
|
||||||
width: 2.5em;
|
width: 3.5em;
|
||||||
height: 1.1em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.ui-dialog .disabled {
|
.ui-dialog .disabled {
|
||||||
|
|
@ -1136,6 +1168,9 @@ div.header > div {
|
||||||
font-size: .9em;
|
font-size: .9em;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
position: sticky;
|
position: sticky;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow-x: hidden;
|
||||||
|
vertical-align: bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sortable {
|
.sortable {
|
||||||
|
|
@ -1182,15 +1217,8 @@ div.states div {
|
||||||
width: 3.2em;
|
width: 3.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.states .statePower {
|
div.states .statePower, div.states .biomeHabitability {
|
||||||
width: 3.2em;
|
width: 4em;
|
||||||
line-height: 1.4em;
|
|
||||||
-moz-appearance: textfield;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.states .biomeHabitability {
|
|
||||||
width: 3.6em;
|
|
||||||
line-height: 1.4em;
|
|
||||||
-moz-appearance: textfield;
|
-moz-appearance: textfield;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1209,13 +1237,9 @@ div.states>.statePopulation {
|
||||||
div.states .icon-pencil,
|
div.states .icon-pencil,
|
||||||
div.states .icon-trash-empty,
|
div.states .icon-trash-empty,
|
||||||
div.states .icon-eye,
|
div.states .icon-eye,
|
||||||
div.states .icon-pin {
|
div.states .icon-pin,
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
div.states .icon-flag-empty {
|
div.states .icon-flag-empty {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: .9em;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
div.states .icon-resize-vertical {
|
div.states .icon-resize-vertical {
|
||||||
|
|
@ -1332,18 +1356,22 @@ div.states>input.riverType {
|
||||||
line-height: 1.4em;
|
line-height: 1.4em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#stateNameEditor input,
|
|
||||||
#provinceNameEditor input {
|
|
||||||
padding-left: .3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#stateNameEditor div.label,
|
#stateNameEditor div.label,
|
||||||
#provinceNameEditor div.label,
|
#provinceNameEditor div.label,
|
||||||
#burgBody div.label {
|
#burgBody div.label,
|
||||||
|
#regimentBody div.label {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 5.5em;
|
width: 5.5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#regimentBody div {
|
||||||
|
margin: .1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#regimentBody input[type="number"] {
|
||||||
|
width: 5em;
|
||||||
|
}
|
||||||
|
|
||||||
.burgFeature {
|
.burgFeature {
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
padding: 1px 2px;
|
padding: 1px 2px;
|
||||||
|
|
@ -1423,18 +1451,19 @@ rect.fillRect {
|
||||||
stroke-width: 2;
|
stroke-width: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#militaryHeader > div {
|
||||||
|
width: 5.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#militaryBody div.states > input {
|
||||||
|
-moz-appearance: textfield;
|
||||||
|
}
|
||||||
|
|
||||||
|
#militaryBody div.states > input,
|
||||||
#militaryBody div.states > div {
|
#militaryBody div.states > div {
|
||||||
width: 4em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#militaryBody div.states > input.militaryArmy {
|
|
||||||
width: 5em;
|
width: 5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#militaryBody div.states > input.militaryFleet {
|
|
||||||
width: 3.3em;
|
|
||||||
}
|
|
||||||
|
|
||||||
#picker text {
|
#picker text {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
@ -1522,6 +1551,7 @@ rect.fillRect {
|
||||||
|
|
||||||
#unitsBody>div>* {
|
#unitsBody>div>* {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
|
margin-bottom: .2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.unitsHeader {
|
.unitsHeader {
|
||||||
|
|
@ -1550,7 +1580,6 @@ rect.fillRect {
|
||||||
width: 11.32em;
|
width: 11.32em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#unitsBody>div>input[type="number"] {
|
#unitsBody>div>input[type="number"] {
|
||||||
width: 3.4em;
|
width: 3.4em;
|
||||||
}
|
}
|
||||||
|
|
@ -1570,8 +1599,9 @@ rect.fillRect {
|
||||||
}
|
}
|
||||||
|
|
||||||
#barBackColor {
|
#barBackColor {
|
||||||
width: 3.45em;
|
width: 3.5em;
|
||||||
padding: 0px;
|
padding: 0px;
|
||||||
|
height: 1.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ruler {
|
#ruler {
|
||||||
|
|
@ -1627,6 +1657,20 @@ rect.fillRect {
|
||||||
font-family: Georgia;
|
font-family: Georgia;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#militaryOptionsTable select {
|
||||||
|
border: 1px solid #d4d4d4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#militaryOptionsTable input {
|
||||||
|
width: 9em;
|
||||||
|
padding-left: 3px;
|
||||||
|
border: 1px solid #d4d4d4;
|
||||||
|
}
|
||||||
|
|
||||||
|
#militaryOptionsTable input[type="number"] {
|
||||||
|
width: 4em;
|
||||||
|
}
|
||||||
|
|
||||||
#gridOverlay {
|
#gridOverlay {
|
||||||
fill: none;
|
fill: none;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
191
index.html
191
index.html
|
|
@ -25,7 +25,7 @@
|
||||||
@keyframes spin {0% {transform: rotate(0deg);} 100% {transform: rotate(359deg);}}
|
@keyframes spin {0% {transform: rotate(0deg);} 100% {transform: rotate(359deg);}}
|
||||||
#loading {opacity:1; font-size: 11px; color:#fff5da; text-align:center; text-shadow:0px 1px 4px #4c3a35; width:80%; max-width:600px; position:fixed; top:50%; left:50%; transform:translate(-50%, -50%); pointer-events:none;}
|
#loading {opacity:1; font-size: 11px; color:#fff5da; text-align:center; text-shadow:0px 1px 4px #4c3a35; width:80%; max-width:600px; position:fixed; top:50%; left:50%; transform:translate(-50%, -50%); pointer-events:none;}
|
||||||
#loading-text {font-size: 1.8em; margin: 0.2em 0 0 1em;}
|
#loading-text {font-size: 1.8em; margin: 0.2em 0 0 1em;}
|
||||||
#title_name {text-align: left;font-size: 3em;margin-left: 5%;}
|
#titleName {text-align: left;font-size: 3em;margin-left: 5%;}
|
||||||
#title {font-size: 7em;margin: -12px 0 -6px 0;}
|
#title {font-size: 7em;margin: -12px 0 -6px 0;}
|
||||||
#version {text-align: right;font-size: 2em;margin-right: 3%;}
|
#version {text-align: right;font-size: 2em;margin-right: 3%;}
|
||||||
#loading-text > span {font-size: 1.3em; padding-left: 1px; line-height: 0px;}
|
#loading-text > span {font-size: 1.3em; padding-left: 1px; line-height: 0px;}
|
||||||
|
|
@ -897,29 +897,29 @@
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<div id="loading">
|
<div id="loading">
|
||||||
<div id="title_name">Azgaar's</div>
|
<div id="titleName"><t data-t="titleName">Azgaar's</t></div>
|
||||||
<div id="title">Fantasy Map Generator</div>
|
<div id="title"><t data-t="title">Fantasy Map Generator</t></div>
|
||||||
<div id="version">v. 1.2</div>
|
<div id="version"><t data-t="version">v. </t>1.2</div>
|
||||||
<p id="loading-text">LOADING<span>.</span><span>.</span><span>.</span></p>
|
<p id="loading-text"><t data-t="loading">LOADING</t><span>.</span><span>.</span><span>.</span></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="optionsContainer" style="opacity:0">
|
<div id="optionsContainer" style="opacity:0">
|
||||||
|
|
||||||
<div id="collapsible">
|
<div id="collapsible">
|
||||||
<button id="optionsTrigger" data-tip="Click to show options pane. Shortcut: Tab" class="options glow" onclick="showOptions(event)" style="padding:.6em .45em">►</button>
|
<button id="optionsTrigger" data-t="tipOptionsTrigger" data-tip="Click to show options pane. Shortcut: Tab" class="options glow" onclick="showOptions(event)" style="padding:.6em .45em">►</button>
|
||||||
<button id="regenerate" data-tip="Click to generate a new map. Shortcut: F2" onclick="regeneratePrompt()" class="options" style="display:none; padding:.6em 1em">New Map!</button>
|
<button id="regenerate" data-t="tipRegenerate" data-tip="Click to generate a new map. Shortcut: F2" onclick="regeneratePrompt()" class="options" style="display:none; padding:.6em 1em"><t data-t="newMap">New Map!</t></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="options" style="display:none">
|
<div id="options" style="display:none">
|
||||||
<div class="drag-trigger" data-tip="Drag to move options pane"></div>
|
<div class="drag-trigger" data-t="optionsDragTrigger" data-tip="Drag to move options pane"></div>
|
||||||
|
|
||||||
<div class="tab">
|
<div class="tab">
|
||||||
<button id="optionsHide" data-tip="Click to hide options pane. Shortcut: Tab to close this or Esc to close all dialogs" class="options" onclick="hideOptions(event)">◄</button>
|
<button id="optionsHide" data-t="optionsHide" data-tip="Click to hide options pane. Shortcut: Tab to close this or Esc to close all dialogs" class="options" onclick="hideOptions(event)">◄</button>
|
||||||
<button id="layersTab" data-tip="Click to change map layers" class="options active">Layers</button>
|
<button id="layersTab" data-t="layersTab" data-tip="Click to change map layers" class="options active"><t data-t="layers">Layers</t></button>
|
||||||
<button id="styleTab" data-tip="Click to open style editor" class="options">Style</button>
|
<button id="styleTab" data-t="styleTab" data-tip="Click to open style editor" class="options"><t data-t="style">Style</t></button>
|
||||||
<button id="optionsTab" data-tip="Click to change generation and UI options" class="options">Options</button>
|
<button id="optionsTab" data-t="optionsTab" data-tip="Click to change generation and UI options" class="options"><t data-t="options">Options</t></button>
|
||||||
<button id="toolsTab" data-tip="Click to open tools menu" class="options">Tools</button>
|
<button id="toolsTab" data-t="toolsTab" data-tip="Click to open tools menu" class="options"><t data-t="tools">Tools</t></button>
|
||||||
<button id="aboutTab" data-tip="Click to see Generator info" class="options">About</button>
|
<button id="aboutTab" data-t="aboutTab" data-tip="Click to see Generator info" class="options"><t data-t="about">About</t></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="layersContent" class="tabcontent" style="display: block">
|
<div id="layersContent" class="tabcontent" style="display: block">
|
||||||
|
|
@ -964,7 +964,7 @@
|
||||||
<li id="toggleIcons" data-tip="Burg icons: click to toggle, drag to raise or lower the layer. Ctrl + click to edit layer style. Shortcut: I" onclick="toggleIcons(event)"><u>I</u>cons</li>
|
<li id="toggleIcons" data-tip="Burg icons: click to toggle, drag to raise or lower the layer. Ctrl + click to edit layer style. Shortcut: I" onclick="toggleIcons(event)"><u>I</u>cons</li>
|
||||||
<li id="toggleMarkers" data-tip="Markers: click to toggle, drag to raise or lower the layer. Ctrl + click to edit layer style. Shortcut: M" class="buttonoff" onclick="toggleMarkers(event)"><u>M</u>arkers</li>
|
<li id="toggleMarkers" data-tip="Markers: click to toggle, drag to raise or lower the layer. Ctrl + click to edit layer style. Shortcut: M" class="buttonoff" onclick="toggleMarkers(event)"><u>M</u>arkers</li>
|
||||||
<li id="toggleRulers" data-tip="Rulers: click to toggle, drag to move, click on label to delete. Ctrl + click to edit layer style. Shortcut: = (equal)" class="buttonoff" onclick="toggleRulers(event)">Rulers</li>
|
<li id="toggleRulers" data-tip="Rulers: click to toggle, drag to move, click on label to delete. Ctrl + click to edit layer style. Shortcut: = (equal)" class="buttonoff" onclick="toggleRulers(event)">Rulers</li>
|
||||||
<li id="toggleScaleBar" data-tip="Scale Bar: click to toggle, drag to move. Ctrl + click to edit style. Shortcut: - (minus)" onclick="toggleScaleBar(event)" class="solid">Scale Bar</li>
|
<li id="toggleScaleBar" data-tip="Scale Bar: click to toggle. Ctrl + click to edit style. Shortcut: - (minus)" onclick="toggleScaleBar(event)" class="solid">Scale Bar</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div id="viewMode" data-tip="Set view node">
|
<div id="viewMode" data-tip="Set view node">
|
||||||
|
|
@ -1520,14 +1520,14 @@
|
||||||
<p data-tip="Map generation settings. Generate a new map to apply the settings">Map settings (new map to apply):</p>
|
<p data-tip="Map generation settings. Generate a new map to apply the settings">Map settings (new map to apply):</p>
|
||||||
<table>
|
<table>
|
||||||
|
|
||||||
<tr data-tip="Canvas size in pixels. Defines map size on generation, then map size cannot be changed and canvas size changes only visible area. Keep canvas size equal to screen size or less to improve performance. The best aspect ratio for maps is 2:1">
|
<tr data-tip="Canvas width and height in pixels. Defines map size on generation, then map size cannot be changed and canvas size changes only visible area. Keep canvas size equal to screen size or less to improve performance. The best aspect ratio for maps is 2:1">
|
||||||
<td></td>
|
<td></td>
|
||||||
<td>Canvas size</td>
|
<td>Canvas size</td>
|
||||||
<td>
|
<td>
|
||||||
<span>width</span>
|
|
||||||
<input id="mapWidthInput" class="paired" type="number" min=240 value=960>
|
<input id="mapWidthInput" class="paired" type="number" min=240 value=960>
|
||||||
<span>height</span>
|
<span>x</span>
|
||||||
<input id="mapHeightInput" class="paired" type="number" min=135 value=540>
|
<input id="mapHeightInput" class="paired" type="number" min=135 value=540>
|
||||||
|
<span>px</span>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<i data-tip="Toggle between screen size and initial canvas size" id="toggleFullscreen" class="icon-resize-full-alt"></i>
|
<i data-tip="Toggle between screen size and initial canvas size" id="toggleFullscreen" class="icon-resize-full-alt"></i>
|
||||||
|
|
@ -1779,10 +1779,20 @@
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
<tr data-tip="Select language (please note not all languages are fully supported). Reload the page to apply">
|
||||||
|
<td></td>
|
||||||
|
<td>Language</td>
|
||||||
|
<td>
|
||||||
|
<select id="selectLanguage" data-stored="lang">
|
||||||
|
<option value="en" selected>English (100%)</option>
|
||||||
|
<option value="ru">Русский (1%)</option>
|
||||||
|
</select>
|
||||||
|
</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<button id="configureWorld" data-tip="Click to open world configurator to setup position on globe and world climate" onclick="editWorld()">Configure World</button>
|
<button id="configureWorld" data-tip="Click to open world configurator to setup map position on Globe and World climate" onclick="editWorld()">Configure World</button>
|
||||||
|
|
||||||
<button id="optionsReset" data-tip="Click to restore default options (page will be reloaded)" onclick="restoreDefaultOptions()">Reset to defaults</button>
|
<button id="optionsReset" data-tip="Click to restore default options (page will be reloaded)" onclick="restoreDefaultOptions()">Reset to defaults</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -1807,7 +1817,7 @@
|
||||||
<p>Click to overview:</p>
|
<p>Click to overview:</p>
|
||||||
<button id="overviewBurgsButton" data-tip="Click to open Burgs Overview. Shortcut: Shift + T">Burgs</button>
|
<button id="overviewBurgsButton" data-tip="Click to open Burgs Overview. Shortcut: Shift + T">Burgs</button>
|
||||||
<button id="overviewRiversButton" data-tip="Click to open Rivers Overview. Shortcut: Shift + V">Rivers</button>
|
<button id="overviewRiversButton" data-tip="Click to open Rivers Overview. Shortcut: Shift + V">Rivers</button>
|
||||||
<!-- <button id="overviewMilitaryButton" data-tip="Click to open Military Forces Overview. Shortcut: Shift + M">Military</button> -->
|
<button id="overviewMilitaryButton" data-tip="Click to open Military Forces Overview. Shortcut: Shift + M">Military</button>
|
||||||
<button id="overviewCellsButton" data-tip="Click to open Cell details view. Shortcut: Shift + E">Cells</button>
|
<button id="overviewCellsButton" data-tip="Click to open Cell details view. Shortcut: Shift + E">Cells</button>
|
||||||
<!-- <button id="overviewLandmassedButton" data-tip="Click to open Landmasses Overview. Shortcut: Shift + L">Landmasses</button> -->
|
<!-- <button id="overviewLandmassedButton" data-tip="Click to open Landmasses Overview. Shortcut: Shift + L">Landmasses</button> -->
|
||||||
<!-- <button id="overviewWaterbodiesButton" data-tip="Click to open Waterbodies Overview. Shortcut: Shift + W">Waterbodies</button> -->
|
<!-- <button id="overviewWaterbodiesButton" data-tip="Click to open Waterbodies Overview. Shortcut: Shift + W">Waterbodies</button> -->
|
||||||
|
|
@ -1879,7 +1889,7 @@
|
||||||
<div id="aboutContent" class="tabcontent">
|
<div id="aboutContent" class="tabcontent">
|
||||||
<p><a href="https://github.com/Azgaar/Fantasy-Map-Generator" target="_blank">Fantasy Map Generator</a> is a free <a href="https://github.com/Azgaar/Fantasy-Map-Generator/blob/master/LICENSE" target="_blank">open source</a> tool which procedurally generates fantasy maps. You may use auto-generated maps as they are, edit them or even create a new map from scratch. Check out the <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Quick-Start-Tutorial" target="_blank">quick start tutorial</a>, <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Q&A" target="_blank">Q&A</a> and <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Hotkeys" target="_blank">hotkeys</a> for guidance.</p>
|
<p><a href="https://github.com/Azgaar/Fantasy-Map-Generator" target="_blank">Fantasy Map Generator</a> is a free <a href="https://github.com/Azgaar/Fantasy-Map-Generator/blob/master/LICENSE" target="_blank">open source</a> tool which procedurally generates fantasy maps. You may use auto-generated maps as they are, edit them or even create a new map from scratch. Check out the <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Quick-Start-Tutorial" target="_blank">quick start tutorial</a>, <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Q&A" target="_blank">Q&A</a> and <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Hotkeys" target="_blank">hotkeys</a> for guidance.</p>
|
||||||
<p>Join our <a href='https://discordapp.com/invite/X7E84HU' target='_blank'>Discord server</a> and <a href="https://www.reddit.com/r/FantasyMapGenerator/" target="_blank">Reddit community</a> to ask questions, get help and share created maps. You may support the project on <a href='https://www.patreon.com/azgaar' target='_blank'>Patreon</a>.</p>
|
<p>Join our <a href='https://discordapp.com/invite/X7E84HU' target='_blank'>Discord server</a> and <a href="https://www.reddit.com/r/FantasyMapGenerator/" target="_blank">Reddit community</a> to ask questions, get help and share created maps. You may support the project on <a href='https://www.patreon.com/azgaar' target='_blank'>Patreon</a>.</p>
|
||||||
<p>The project is under active development. For older versions see the <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog" target="_blank">changelog</a>. To track the development progress see the <a href="https://trello.com/b/7x832DG4/fantasy-map-generator" target="_blank">devboard</a>. Please report bugs <a href="https://github.com/Azgaar/Fantasy-Map-Generator/issues" target="_blank">here</a>. You can also contact me directly via <a href="mailto:azgaar.fmg@yandex.by" target="_blank">email</a>.</p>
|
<p>The project is under active development. Creator and main maintainer: Azgaar. For older versions see the <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog" target="_blank">changelog</a>. To track the development progress see the <a href="https://trello.com/b/7x832DG4/fantasy-map-generator" target="_blank">devboard</a>. Please report bugs <a href="https://github.com/Azgaar/Fantasy-Map-Generator/issues" target="_blank">here</a>. You can also contact me directly via <a href="mailto:azgaar.fmg@yandex.by" target="_blank">email</a>.</p>
|
||||||
<p>Special thanks to all supporters on Patreon! <i data-tip="Click to see supporters names" class="collapsible icon-down-open pointer"></i></p>
|
<p>Special thanks to all supporters on Patreon! <i data-tip="Click to see supporters names" class="collapsible icon-down-open pointer"></i></p>
|
||||||
<p style="display:none">Patreon Supporters: Aaron Meyer, Ahmad Amerih, AstralJacks, aymeric, Billy Dean Goehring, Branndon Edwards,
|
<p style="display:none">Patreon Supporters: Aaron Meyer, Ahmad Amerih, AstralJacks, aymeric, Billy Dean Goehring, Branndon Edwards,
|
||||||
Chase Mayers, Curt Flood, cyninge, Dino Princip, E.M. White, es, Fondue, Fritjof Olsson, Gatsu, Johan Fröberg, Jonathan Moore,
|
Chase Mayers, Curt Flood, cyninge, Dino Princip, E.M. White, es, Fondue, Fritjof Olsson, Gatsu, Johan Fröberg, Jonathan Moore,
|
||||||
|
|
@ -1887,10 +1897,12 @@
|
||||||
Sasquatch, Shawn Spencer, Sizz_TV, Timothée CALLET, UTG community, Vlad Tomash, Wil Sisney, William Merriott, Xariun,
|
Sasquatch, Shawn Spencer, Sizz_TV, Timothée CALLET, UTG community, Vlad Tomash, Wil Sisney, William Merriott, Xariun,
|
||||||
Gun Metal Games, Scott Marner, Spencer Sherman, Valerii Matskevych, Alloyed Clavicle, Stewart Walsh, Ruthlyn Mollett (Javan),
|
Gun Metal Games, Scott Marner, Spencer Sherman, Valerii Matskevych, Alloyed Clavicle, Stewart Walsh, Ruthlyn Mollett (Javan),
|
||||||
Benjamin Mair-Pratt, Diagonath, Alexander Thomas, Ashley Wilson-Savoury, William Henry, Preston Brooks, JOSHUA QUALTIERI,
|
Benjamin Mair-Pratt, Diagonath, Alexander Thomas, Ashley Wilson-Savoury, William Henry, Preston Brooks, JOSHUA QUALTIERI,
|
||||||
Hilton Williams, Katharina Haase, Hisham Bedri, Ian arless, Karnat, Bird, Kevin, Jessica Thomas, Steve Hyatt, Logicspren,
|
Hilton Williams, Katharina Haase, Hisham Bedri, Ian arless, Karnat, Bird, Kevin, Jessica Thomas, Steve Hyatt, Logicspren,
|
||||||
Alfred García, Jonathan Killstring, John Ackley, Invad3r233, Norbert Žigmund, Jennifer, PoliticsBuff, _gfx_, Maggie,
|
Alfred García, Jonathan Killstring, John Ackley, Invad3r233, Norbert Žigmund, Jennifer, PoliticsBuff, _gfx_, Maggie,
|
||||||
Connor McMartin, Jared McDaris, BlastWind, Franc Casanova Ferrer, Dead & Devil, Michael Carmody, Valerie Elise, naikibens220,
|
Connor McMartin, Jared McDaris, BlastWind, Franc Casanova Ferrer, Dead & Devil, Michael Carmody, Valerie Elise, naikibens220,
|
||||||
Jordon Phillips, William Pucs, The Dungeon Masters, Brady R Rathbun, J, Shadow, Matthew Tiffany and many others!</p>
|
Jordon Phillips, William Pucs, The Dungeon Masters, Brady R Rathbun, J, Shadow, Matthew Tiffany, Huw Williams, Joseph Hamilton,
|
||||||
|
FlippantFeline, Tamashi Toh, kms, Stephen Herron, MidnightMoon, Whakomatic x, Barished, Aaron bateson, Brice Moss, Diklyquill,
|
||||||
|
PatronUser, Michael Greiner, Steven Bennett, Jacob Harrington and many others!</p>
|
||||||
|
|
||||||
<ul class="share-buttons">
|
<ul class="share-buttons">
|
||||||
<li><a href="https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fazgaar.github.io%2FFantasy-Map-Generator%2F"e=" data-tip="Share on Facebook" target="_blank"><img alt="Share on Facebook" src="images/Facebook.png" /></a></li>
|
<li><a href="https://www.facebook.com/sharer/sharer.php?u=https%3A%2F%2Fazgaar.github.io%2FFantasy-Map-Generator%2F"e=" data-tip="Share on Facebook" target="_blank"><img alt="Share on Facebook" src="images/Facebook.png" /></a></li>
|
||||||
|
|
@ -2292,9 +2304,9 @@
|
||||||
<span id="burgCapital" data-tip="Shows whether the burg is a state capital. Click to toggle" data-feature="capital" class="burgFeature icon-star"></span>
|
<span id="burgCapital" data-tip="Shows whether the burg is a state capital. Click to toggle" data-feature="capital" class="burgFeature icon-star"></span>
|
||||||
<span id="burgPort" data-tip="Shows whether the burg is a port. Click to toggle" data-feature="port" class="burgFeature icon-anchor"></span>
|
<span id="burgPort" data-tip="Shows whether the burg is a port. Click to toggle" data-feature="port" class="burgFeature icon-anchor"></span>
|
||||||
<span id="burgCitadel" data-tip="Shows whether the burg has a citadel (castle). Click to toggle" data-feature="citadel" class="burgFeature icon-chess-rook" style="font-size: 1.1em"></span>
|
<span id="burgCitadel" data-tip="Shows whether the burg has a citadel (castle). Click to toggle" data-feature="citadel" class="burgFeature icon-chess-rook" style="font-size: 1.1em"></span>
|
||||||
<span id="burgWalls" data-tip="Shows whether the burg has walls. Click to toggle" data-feature="walls" class="burgFeature icon-fort-awesome"></span>
|
<span id="burgWalls" data-tip="Shows whether the burg is walled. Click to toggle" data-feature="walls" class="burgFeature icon-fort-awesome"></span>
|
||||||
<span id="burgPlaza" data-tip="Shows whether the burg has a plaza (marketplace). Click to toggle" data-feature="plaza" class="burgFeature icon-store" style="font-size: 1em"></span>
|
<span id="burgPlaza" data-tip="Shows whether the burg is a trade center (has big marketplace). Click to toggle" data-feature="plaza" class="burgFeature icon-store" style="font-size: 1em"></span>
|
||||||
<span id="burgTemple" data-tip="Shows whether the burg has a place of worship. Click to toggle" data-feature="temple" class="burgFeature icon-bank" style="font-size: 1.1em; margin-left: 3px"></span>
|
<span id="burgTemple" data-tip="Shows whether the burg is a religious center. Click to toggle" data-feature="temple" class="burgFeature icon-chess-bishop" style="font-size: 1.1em; margin-left: 3px"></span>
|
||||||
<span id="burgShanty" data-tip="Shows whether the burg has a shanty town. Click to toggle" data-feature="shanty" class="burgFeature icon-campground" style="font-size: 1em"></span>
|
<span id="burgShanty" data-tip="Shows whether the burg has a shanty town. Click to toggle" data-feature="shanty" class="burgFeature icon-campground" style="font-size: 1em"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -2318,7 +2330,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button id="burgSeeInMFCG" data-tip="Open burg in the Medieval Fantasy City Generator by Watabou. Ctrl + click to change the seed" class="icon-map-o"></button>
|
<button id="burgSeeInMFCG" data-tip="Open burg in the Medieval Fantasy City Generator by Watabou. Ctrl + click to change the seed" class="icon-map-o"></button>
|
||||||
<button id="burgOpenCOA" data-tip="Open burg's COA in the Iron Arachne Heraldry Generator. Ctrl + click to change the seed" class="icon-fleur"></button>
|
<button id="burgOpenCOA" data-tip="Open burg's COA in the Iron Arachne Heraldry Generator. Ctrl + click to change the seed" class="icon-shield-alt"></button>
|
||||||
<button id="burgRelocate" data-tip="Relocate burg" class="icon-target"></button>
|
<button id="burgRelocate" data-tip="Relocate burg" class="icon-target"></button>
|
||||||
<button id="burglLegend" data-tip="Edit free text notes (legend) for this burg" class="icon-edit"></button>
|
<button id="burglLegend" data-tip="Edit free text notes (legend) for this burg" class="icon-edit"></button>
|
||||||
<button id="burgRemove" data-tip="Remove non-capital burg. Shortcut: Delete" class="icon-trash"></button>
|
<button id="burgRemove" data-tip="Remove non-capital burg. Shortcut: Delete" class="icon-trash"></button>
|
||||||
|
|
@ -2367,6 +2379,28 @@
|
||||||
<button id="markerRemove" data-tip="Remove the marker. Shortcut: Delete" class="icon-trash"></button>
|
<button id="markerRemove" data-tip="Remove the marker. Shortcut: Delete" class="icon-trash"></button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="regimentEditor" class="dialog" style="display: none">
|
||||||
|
|
||||||
|
<div id="regimentBody">
|
||||||
|
<div>
|
||||||
|
<button id="regimentType" data-tip="Regiment type (land or naval). Click to change"></button>
|
||||||
|
<input id="regimentName" data-tip="Type to rename the regiment" autocorrect="off" spellcheck="false" style="width: 13em">
|
||||||
|
<i id="regimentNameRestore" data-tip="Click to restore regiment's default name" class="icon-ccw pointer"></i>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="regimentComposition" style="padding: .1em"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="regimentBottom">
|
||||||
|
<button id="regimentAdd" data-tip="Create new regiment or fleet" class="icon-user-plus"></button>
|
||||||
|
<button id="regimentSplit" data-tip="Split regiment into 2 separate ones" class="icon-half"></button>
|
||||||
|
<button id="regimentAttach" data-tip="Attach regiment to another one (include this regiment to another one)" class="icon-attach"></button>
|
||||||
|
<button id="regimentRegenerateLegend" data-tip="Regenerate legend for this regiment" class="icon-retweet"></button>
|
||||||
|
<button id="regimentLegend" data-tip="Edit free text notes (legend) for this regiment" class="icon-edit"></button>
|
||||||
|
<button id="regimentRemove" data-tip="Remove regiment. Shortcut: Delete" class="icon-trash"></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="brushesPanel" class="dialog stable" style="display: none">
|
<div id="brushesPanel" class="dialog stable" style="display: none">
|
||||||
<div id="brushesButtons" style="display: inline-block">
|
<div id="brushesButtons" style="display: inline-block">
|
||||||
<button id="brushRaise" data-tip="Raise brush: increase height of cells in radius by Power value">
|
<button id="brushRaise" data-tip="Raise brush: increase height of cells in radius by Power value">
|
||||||
|
|
@ -2509,7 +2543,8 @@
|
||||||
<button id="templateRedo" data-tip="Redo the action" class="icon-cw" disabled></button>
|
<button id="templateRedo" data-tip="Redo the action" class="icon-cw" disabled></button>
|
||||||
<button id="templateSave" data-tip="Download the template as a text file" class="icon-download"></button>
|
<button id="templateSave" data-tip="Download the template as a text file" class="icon-download"></button>
|
||||||
<button id="templateLoad" data-tip="Open previously downloaded template" class="icon-upload"></button>
|
<button id="templateLoad" data-tip="Open previously downloaded template" class="icon-upload"></button>
|
||||||
<button id="templateCA" data-tip="Find or share custom template on Cartography Assets portal" class="icon-drafting-compass" onclick="openURL('https://www.cartographyassets.com/assets/categories/azgaar-generator.87')"></button>
|
<button id="templateCA" data-tip="Find or share custom template on Cartography Assets portal" class="icon-drafting-compass" onclick="openURL('https://www.cartographyassets.com/assets/categories/templates.89')"></button>
|
||||||
|
<button id="templateTutorial" data-tip="Open Template Editor Tutorial" class="icon-info" onclick="openURL('https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Heightmap-template-editor')"></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -2548,7 +2583,7 @@
|
||||||
|
|
||||||
<div id="biomesEditor" class="dialog stable" style="display: none">
|
<div id="biomesEditor" class="dialog stable" style="display: none">
|
||||||
<div id="biomesHeader" class="header">
|
<div id="biomesHeader" class="header">
|
||||||
<div style="left:1.4em" data-tip="Click to sort by biome name" class="sortable alphabetically" data-sortby="name">Biome </div>
|
<div style="left:1.8em" data-tip="Click to sort by biome name" class="sortable alphabetically" data-sortby="name">Biome </div>
|
||||||
<div style="left:12em" data-tip="Click to sort by biome habitability" class="sortable hide" data-sortby="habitability">Habitability </div>
|
<div style="left:12em" data-tip="Click to sort by biome habitability" class="sortable hide" data-sortby="habitability">Habitability </div>
|
||||||
<div style="left:19em" data-tip="Click to sort by biome cells number" class="sortable hide icon-sort-number-down" data-sortby="cells">Cells </div>
|
<div style="left:19em" data-tip="Click to sort by biome cells number" class="sortable hide icon-sort-number-down" data-sortby="cells">Cells </div>
|
||||||
<div style="left:25em" data-tip="Click to sort by biome area" class="sortable hide" data-sortby="area">Area </div>
|
<div style="left:25em" data-tip="Click to sort by biome area" class="sortable hide" data-sortby="area">Area </div>
|
||||||
|
|
@ -2588,11 +2623,11 @@
|
||||||
|
|
||||||
<div id="statesEditor" class="dialog stable" style="display: none">
|
<div id="statesEditor" class="dialog stable" style="display: none">
|
||||||
<div id="statesHeader" class="header">
|
<div id="statesHeader" class="header">
|
||||||
<div style="left:1.4em" data-tip="Click to sort by state name" class="sortable alphabetically" data-sortby="name">State </div>
|
<div style="left:1.8em" data-tip="Click to sort by state name" class="sortable alphabetically" data-sortby="name">State </div>
|
||||||
<div style="left:8.7em" data-tip="Click to sort by state form name" class="sortable alphabetically" data-sortby="form">Form </div>
|
<div style="left:9em" data-tip="Click to sort by state form name" class="sortable alphabetically" data-sortby="form">Form </div>
|
||||||
<div style="left:15.9em" data-tip="Click to sort by capital name" class="sortable alphabetically hide" data-sortby="capital">Capital </div>
|
<div style="left:16.3em" data-tip="Click to sort by capital name" class="sortable alphabetically hide" data-sortby="capital">Capital </div>
|
||||||
<div style="left:22.3em" data-tip="Click to sort by state dominant culture" class="sortable alphabetically hide" data-sortby="culture">Culture </div>
|
<div style="left:22.2em" data-tip="Click to sort by state dominant culture" class="sortable alphabetically hide" data-sortby="culture">Culture </div>
|
||||||
<div style="left:27em" data-tip="Click to sort by state burgs count" class="sortable hide" data-sortby="burgs">Burgs </div>
|
<div style="left:27.2em" data-tip="Click to sort by state burgs count" class="sortable hide" data-sortby="burgs">Burgs </div>
|
||||||
<div style="left:32.5em" data-tip="Click to sort by state area" class="sortable hide icon-sort-number-down" data-sortby="area">Area </div>
|
<div style="left:32.5em" data-tip="Click to sort by state area" class="sortable hide icon-sort-number-down" data-sortby="area">Area </div>
|
||||||
<div style="left:37em" data-tip="Click to sort by state population" class="sortable hide" data-sortby="population">Population </div>
|
<div style="left:37em" data-tip="Click to sort by state population" class="sortable hide" data-sortby="population">Population </div>
|
||||||
<div style="left:43.5em" data-tip="Click to sort by state type" class="sortable alphabetically hidden show hide" data-sortby="type">Type </div>
|
<div style="left:43.5em" data-tip="Click to sort by state type" class="sortable alphabetically hidden show hide" data-sortby="type">Type </div>
|
||||||
|
|
@ -2662,7 +2697,7 @@
|
||||||
|
|
||||||
<div style="padding: .1em" data-tip="Select form name">
|
<div style="padding: .1em" data-tip="Select form name">
|
||||||
<div data-tip="State form name" class="label">Form name:</div>
|
<div data-tip="State form name" class="label">Form name:</div>
|
||||||
<select id="stateNameEditorSelectForm" style="display: inline-block; width: 11.7em; height: 1.645em">
|
<select id="stateNameEditorSelectForm" style="width: 11em">
|
||||||
<option value="">blank</option>
|
<option value="">blank</option>
|
||||||
<option data-form="Monarchy" value="Beylik">Beylik</option>
|
<option data-form="Monarchy" value="Beylik">Beylik</option>
|
||||||
<option data-form="Theocracy" value="Caliphate">Caliphate</option>
|
<option data-form="Theocracy" value="Caliphate">Caliphate</option>
|
||||||
|
|
@ -2729,7 +2764,7 @@
|
||||||
|
|
||||||
<div id="provincesEditor" class="dialog stable" style="display: none">
|
<div id="provincesEditor" class="dialog stable" style="display: none">
|
||||||
<div id="provincesHeader" class="header">
|
<div id="provincesHeader" class="header">
|
||||||
<div style="left:1.4em" data-tip="Click to sort by province name" class="sortable alphabetically" data-sortby="name">Province </div>
|
<div style="left:1.8em" data-tip="Click to sort by province name" class="sortable alphabetically" data-sortby="name">Province </div>
|
||||||
<div style="left:8.5em" data-tip="Click to sort by province form name" class="sortable alphabetically hide" data-sortby="form">Form </div>
|
<div style="left:8.5em" data-tip="Click to sort by province form name" class="sortable alphabetically hide" data-sortby="form">Form </div>
|
||||||
<div style="left:15.9em" data-tip="Click to sort by province capital" class="sortable alphabetically hide" data-sortby="capital">Capital </div>
|
<div style="left:15.9em" data-tip="Click to sort by province capital" class="sortable alphabetically hide" data-sortby="capital">Capital </div>
|
||||||
<div style="left:22em" data-tip="Click to sort by province owner" class="sortable alphabetically" data-sortby="state">State </div>
|
<div style="left:22em" data-tip="Click to sort by province owner" class="sortable alphabetically" data-sortby="state">State </div>
|
||||||
|
|
@ -2781,7 +2816,7 @@
|
||||||
<div data-tip="Ally means states formed a defensive pact and will protect each other in case of third party aggression">Ally</div>
|
<div data-tip="Ally means states formed a defensive pact and will protect each other in case of third party aggression">Ally</div>
|
||||||
<div data-tip="State is friendly to anouther state when they share some common interests">Friendly</div>
|
<div data-tip="State is friendly to anouther state when they share some common interests">Friendly</div>
|
||||||
<div data-tip="Neutral means states relations are neither positive nor negative">Neutral</div>
|
<div data-tip="Neutral means states relations are neither positive nor negative">Neutral</div>
|
||||||
<div data-tip="Suspicion means shate has a cautious distrust of another state">Suspicion</div>
|
<div data-tip="Suspicion means state has a cautious distrust of another state">Suspicion</div>
|
||||||
<div data-tip="Enemies are states at war with each other">Enemy</div>
|
<div data-tip="Enemies are states at war with each other">Enemy</div>
|
||||||
<div data-tip="Relations are unknown if states do not have enought information about each other">Unknown</div>
|
<div data-tip="Relations are unknown if states do not have enought information about each other">Unknown</div>
|
||||||
<div data-tip="Rivalry is a state of competing for dominance in the region">Rival</div>
|
<div data-tip="Rivalry is a state of competing for dominance in the region">Rival</div>
|
||||||
|
|
@ -2848,11 +2883,11 @@
|
||||||
|
|
||||||
<div id="culturesEditor" class="dialog stable" style="display: none">
|
<div id="culturesEditor" class="dialog stable" style="display: none">
|
||||||
<div id="culturesHeader" class="header">
|
<div id="culturesHeader" class="header">
|
||||||
<div style="left:1.4em" data-tip="Click to sort by culture name" class="sortable alphabetically" data-sortby="name">Culture </div>
|
<div style="left:1.8em" data-tip="Click to sort by culture name" class="sortable alphabetically" data-sortby="name">Culture </div>
|
||||||
<div style="left:6.7em" data-tip="Click to sort by culture cells count" class="sortable hide" data-sortby="cells">Cells </div>
|
<div style="left:6.7em" data-tip="Click to sort by culture cells count" class="sortable hide" data-sortby="cells">Cells </div>
|
||||||
<div style="left:11em" data-tip="Click to sort by expansionism" class="sortable hide" data-sortby="expansionism">Expan. </div>
|
<div style="left:11.2em" data-tip="Click to sort by expansionism" class="sortable hide" data-sortby="expansionism">Expan. </div>
|
||||||
<div style="left:15.8em" data-tip="Click to sort by type" class="sortable alphabetically" data-sortby="type">Type </div>
|
<div style="left:16.6em" data-tip="Click to sort by type" class="sortable alphabetically" data-sortby="type">Type </div>
|
||||||
<div style="left:22.2em" data-tip="Click to sort by culture area" class="sortable hide" data-sortby="area">Area </div>
|
<div style="left:22.9em" data-tip="Click to sort by culture area" class="sortable hide" data-sortby="area">Area </div>
|
||||||
<div style="left:26.8em" data-tip="Click to sort by culture population" class="sortable hide icon-sort-number-down" data-sortby="population">Population </div>
|
<div style="left:26.8em" data-tip="Click to sort by culture population" class="sortable hide icon-sort-number-down" data-sortby="population">Population </div>
|
||||||
<div style="left:33.8em" data-tip="Click to sort by culture namesbase" class="sortable" data-sortby="base">Namesbase </div>
|
<div style="left:33.8em" data-tip="Click to sort by culture namesbase" class="sortable" data-sortby="base">Namesbase </div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -2930,7 +2965,7 @@
|
||||||
|
|
||||||
<div id="zonesEditor" class="dialog stable" style="display: none">
|
<div id="zonesEditor" class="dialog stable" style="display: none">
|
||||||
<div id="customHeader" class="header">
|
<div id="customHeader" class="header">
|
||||||
<div style="left:1.4em" data-tip="Zone description">Description </div>
|
<div style="left:1.8em" data-tip="Zone description">Description </div>
|
||||||
<div style="left:13em" data-tip="Zone cells count" class="hide">Cells </div>
|
<div style="left:13em" data-tip="Zone cells count" class="hide">Cells </div>
|
||||||
<div style="left:19em" data-tip="Zone area" class="hide">Area </div>
|
<div style="left:19em" data-tip="Zone area" class="hide">Area </div>
|
||||||
<div style="left:24em" data-tip="Zone population" class="hide">Population </div>
|
<div style="left:24em" data-tip="Zone population" class="hide">Population </div>
|
||||||
|
|
@ -2988,7 +3023,7 @@
|
||||||
|
|
||||||
<div id="religionsEditor" class="dialog stable" style="display: none">
|
<div id="religionsEditor" class="dialog stable" style="display: none">
|
||||||
<div id="religionsHeader" class="header">
|
<div id="religionsHeader" class="header">
|
||||||
<div style="left:1.4em" data-tip="Click to sort by religion name" class="sortable alphabetically" data-sortby="name">Religion </div>
|
<div style="left:1.8em" data-tip="Click to sort by religion name" class="sortable alphabetically" data-sortby="name">Religion </div>
|
||||||
<div style="left:12.6em" data-tip="Click to sort by religion type" class="sortable alphabetically icon-sort-name-down" data-sortby="type">Type </div>
|
<div style="left:12.6em" data-tip="Click to sort by religion type" class="sortable alphabetically icon-sort-name-down" data-sortby="type">Type </div>
|
||||||
<div style="left:18em" data-tip="Click to sort by religion form" class="sortable alphabetically hide" data-sortby="form">Form </div>
|
<div style="left:18em" data-tip="Click to sort by religion form" class="sortable alphabetically hide" data-sortby="form">Form </div>
|
||||||
<div style="left:25.1em" data-tip="Click to sort by supreme deity" class="sortable alphabetically hide" data-sortby="deity">Supreme Deity </div>
|
<div style="left:25.1em" data-tip="Click to sort by supreme deity" class="sortable alphabetically hide" data-sortby="deity">Supreme Deity </div>
|
||||||
|
|
@ -3073,7 +3108,7 @@
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div data-tip="Set height exponent, i.e. a value showing altitude change sharpness">
|
<div data-tip="Set height exponent, i.e. a value for altitude change sharpness. Altitude affects temperature and hence biomes">
|
||||||
<div>Exponent:</div>
|
<div>Exponent:</div>
|
||||||
<input id="heightExponentOutput" type="range" min=1.5 max=2.1 value=1.8 step=.01>
|
<input id="heightExponentOutput" type="range" min=1.5 max=2.1 value=1.8 step=.01>
|
||||||
<input id="heightExponentInput" data-stored="heightExponent" type="number" min=1.5 max=2.1 value=1.8 step=.01>
|
<input id="heightExponentInput" data-stored="heightExponent" type="number" min=1.5 max=2.1 value=1.8 step=.01>
|
||||||
|
|
@ -3122,8 +3157,8 @@
|
||||||
|
|
||||||
<div data-tip="Set position of the Scale bar bottom right corner in percents">
|
<div data-tip="Set position of the Scale bar bottom right corner in percents">
|
||||||
<div>Bar position:</div>
|
<div>Bar position:</div>
|
||||||
<i>x:</i><input id="barPosX" data-stored="barPosX" type="number" min=0 max=100 step=.1 value=99>
|
x:<input id="barPosX" data-stored="barPosX" type="number" min=0 max=100 step=.1 value=99>
|
||||||
<i>y:</i><input id="barPosY" data-stored="barPosY" type="number" min=0 max=100 step=.1 value=99>
|
y:<input id="barPosY" data-stored="barPosY" type="number" min=0 max=100 step=.1 value=99>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="unitsHeader">
|
<div class="unitsHeader">
|
||||||
|
|
@ -3133,8 +3168,8 @@
|
||||||
|
|
||||||
<div data-tip="Set how many people are in one population point">
|
<div data-tip="Set how many people are in one population point">
|
||||||
<div>1 population point =</div>
|
<div>1 population point =</div>
|
||||||
<input id="populationRateOutput" type="range" min=10 max=9990 step=10 value=1000>
|
<input id="populationRateOutput" type="range" min=10 max=9990 step=10 value=1000 style="width:6em">
|
||||||
<input id="populationRate" data-stored="populationRate" type="number" min=10 max=9990 step=10 value=1000 data-value=1000>
|
<input id="populationRate" data-stored="populationRate" type="number" min=10 max=9990 step=10 value=1000 data-value=1000 style="width:4.5em">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div data-tip="Set ubranization rate: burgs population relative to all population">
|
<div data-tip="Set ubranization rate: burgs population relative to all population">
|
||||||
|
|
@ -3155,7 +3190,7 @@
|
||||||
|
|
||||||
<div id="burgsOverview" class="dialog stable" style="display: none">
|
<div id="burgsOverview" class="dialog stable" style="display: none">
|
||||||
<div id="burgsHeader" class="header">
|
<div id="burgsHeader" class="header">
|
||||||
<div style="left:1.4em" data-tip="Click to sort by burg name" class="sortable alphabetically icon-sort-name-up" data-sortby="name">Burg </div>
|
<div style="left:1.8em" data-tip="Click to sort by burg name" class="sortable alphabetically icon-sort-name-up" data-sortby="name">Burg </div>
|
||||||
<div style="left:7.6em" data-tip="Click to sort by province name" class="sortable alphabetically" data-sortby="province">Province </div>
|
<div style="left:7.6em" data-tip="Click to sort by province name" class="sortable alphabetically" data-sortby="province">Province </div>
|
||||||
<div style="left:14em" data-tip="Click to sort by state name" class="sortable alphabetically" data-sortby="state">State </div>
|
<div style="left:14em" data-tip="Click to sort by state name" class="sortable alphabetically" data-sortby="state">State </div>
|
||||||
<div style="left:20.1em" data-tip="Click to sort by culture name" class="sortable alphabetically" data-sortby="culture">Culture </div>
|
<div style="left:20.1em" data-tip="Click to sort by culture name" class="sortable alphabetically" data-sortby="culture">Culture </div>
|
||||||
|
|
@ -3190,7 +3225,7 @@
|
||||||
|
|
||||||
<div id="riversOverview" class="dialog stable" style="display: none">
|
<div id="riversOverview" class="dialog stable" style="display: none">
|
||||||
<div id="riversHeader" class="header">
|
<div id="riversHeader" class="header">
|
||||||
<div style="left:1.4em" data-tip="Click to sort by river name" class="sortable alphabetically" data-sortby="name">River </div>
|
<div style="left:1.8em" data-tip="Click to sort by river name" class="sortable alphabetically" data-sortby="name">River </div>
|
||||||
<div style="left:7.7em" data-tip="Click to sort by river type name" class="sortable alphabetically" data-sortby="type">Type </div>
|
<div style="left:7.7em" data-tip="Click to sort by river type name" class="sortable alphabetically" data-sortby="type">Type </div>
|
||||||
<div style="left:12.9em" data-tip="Click to sort by river length" class="sortable icon-sort-number-down" data-sortby="length">Length </div>
|
<div style="left:12.9em" data-tip="Click to sort by river length" class="sortable icon-sort-number-down" data-sortby="length">Length </div>
|
||||||
<div style="left:18.2em" data-tip="Click to sort by river basin" class="sortable alphabetically" data-sortby="basin">Basin </div>
|
<div style="left:18.2em" data-tip="Click to sort by river basin" class="sortable alphabetically" data-sortby="basin">Basin </div>
|
||||||
|
|
@ -3214,29 +3249,48 @@
|
||||||
|
|
||||||
<div id="militaryOverview" class="dialog stable" style="display: none">
|
<div id="militaryOverview" class="dialog stable" style="display: none">
|
||||||
<div id="militaryHeader" class="header">
|
<div id="militaryHeader" class="header">
|
||||||
<div style="left:1.4em" data-tip="State name. Click to sort" class="sortable alphabetically" data-sortby="state">State </div>
|
<div data-tip="State name. Click to sort" style="left:1.8em; width: 7.4em;" class="sortable alphabetically" data-sortby="state">State </div>
|
||||||
<div style="left: 7.8em" data-tip="State infantry number. Click to sort" class="sortable" data-sortby="infantry">Infantry </div>
|
<div data-tip="Total military personnel (considering crew). Click to sort" id="militaryTotal" class="sortable icon-sort-number-down" data-sortby="total">Total </div>
|
||||||
<div style="left: 13em" data-tip="State archers number. Click to sort" class="sortable" data-sortby="archers">Archers </div>
|
<div data-tip="State population. Click to sort" style="width: 6.5em; margin-left: -1em" class="sortable" data-sortby="population">Population </div>
|
||||||
<div style="left: 18em" data-tip="State cavalry number. Click to sort" class="sortable" data-sortby="cavalry">Cavalry </div>
|
<div data-tip="Military personnel rate (% of state population). Depends on war alert. Click to sort" style="width: 3.7em" class="sortable" data-sortby="rate">Rate </div>
|
||||||
<div style="left: 23em" data-tip="Number of ships in navy. Click to sort" class="sortable" data-sortby="fleet">Fleet </div>
|
<div data-tip="War Alert. Modifier to military forces number, depends of political situation. Click to sort" class="sortable" data-sortby="alert">War Alert </div>
|
||||||
<div style="left: 27em" data-tip="Total military personnel (including ships crew). Click to sort" class="sortable icon-sort-number-down" data-sortby="total">Total </div>
|
|
||||||
<div style="left: 31.4em" data-tip="Military personnel rate (% of state population). Depends on diplomatic situation. Click to sort" class="sortable" data-sortby="rate">Rate </div>
|
|
||||||
<div style="left: 36em" data-tip="State manpower (reserve). Click to sort" class="sortable" data-sortby="reserve">Reserve </div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="militaryBody" class="table" data-type="absolute"></div>
|
<div id="militaryBody" class="table" data-type="absolute"></div>
|
||||||
|
|
||||||
<div id="militaryFooter" class="totalLine">
|
<div id="militaryFooter" class="totalLine">
|
||||||
<div data-tip="States number" style="margin-left: 4px">States: <span id="militaryFooterStates">0</span></div>
|
<div data-tip="States number" style="margin-left: 4px">States: <span id="militaryFooterStates">0</span></div>
|
||||||
<div data-tip="Average military per state" style="margin-left: 14px">Average military: <span id="militaryFooterAverage">0</span></div>
|
<div data-tip="Average military forces per state" style="margin-left: 14px">Average forces: <span id="militaryFooterForces">0</span></div>
|
||||||
|
<div data-tip="Average forces rate per state" style="margin-left: 14px">Average rate: <span id="militaryFooterRate">0%</span></div>
|
||||||
|
<div data-tip="Average War Alert" style="margin-left: 14px">Average alert: <span id="militaryFooterAlert">0</span></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="militaryBottom">
|
<div id="militaryBottom">
|
||||||
<button id="militaryOverviewRefresh" data-tip="Refresh the Editor" class="icon-cw"></button>
|
<button id="militaryOverviewRefresh" data-tip="Refresh the Editor" class="icon-cw"></button>
|
||||||
|
<button id="militaryOptionsButton" data-tip="Edit Military units" class="icon-cog"></button>
|
||||||
|
<button id="militaryOverviewRecalculate" data-tip="Recalculate military forces based on current options" class="icon-retweet"></button>
|
||||||
<button id="militaryExport" data-tip="Save military-related data as a text file (.csv)" class="icon-download"></button>
|
<button id="militaryExport" data-tip="Save military-related data as a text file (.csv)" class="icon-download"></button>
|
||||||
|
<button id="militaryVisualize" data-tip="Show armies on the map" class="icon-user-shield"></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="militaryOptions" class="dialog stable" style="display: none">
|
||||||
|
<table id="militaryOptionsTable">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th data-tip="Unit name. If name is changed for existing unit, old unit will be replaced">Military unit</th>
|
||||||
|
<th data-tip="Conscription percentage for rural population">Rural %</th>
|
||||||
|
<th data-tip="Conscription percentage for urban population">Urban %</th>
|
||||||
|
<th data-tip="Average number of people in crew">Crew</th>
|
||||||
|
<th data-tip="Unit type to apply special rules on forces recalculation">Type</th>
|
||||||
|
<th data-tip="Check if unit is separate and can be stacked only with units of the same type">S</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="styleSaver" class="dialog stable textual" style="display: none">
|
<div id="styleSaver" class="dialog stable textual" style="display: none">
|
||||||
<div id="styleSaverHeader" style="padding:2px 0">
|
<div id="styleSaverHeader" style="padding:2px 0">
|
||||||
<span>Preset name:</span>
|
<span>Preset name:</span>
|
||||||
|
|
@ -3266,7 +3320,8 @@
|
||||||
<p><b>Precipitation:</b> <span id="infoPrec">0</span></p>
|
<p><b>Precipitation:</b> <span id="infoPrec">0</span></p>
|
||||||
<p><b>River:</b> <span id="infoRiver">no</span></p>
|
<p><b>River:</b> <span id="infoRiver">no</span></p>
|
||||||
<p><b>Population:</b> <span id="infoPopulation">0</span></p>
|
<p><b>Population:</b> <span id="infoPopulation">0</span></p>
|
||||||
<p><b>Height:</b> <span id="infoHeight">0</span></p>
|
<p><b>Elevation:</b> <span id="infoEvelation">0</span></p>
|
||||||
|
<p><b>Depth:</b> <span id="infoDepth">0</span></p>
|
||||||
<p><b>Temperature:</b> <span id="infoTemp">0</span></p>
|
<p><b>Temperature:</b> <span id="infoTemp">0</span></p>
|
||||||
<p><b>Biome:</b> <span id="infoBiome">n/a</span></p>
|
<p><b>Biome:</b> <span id="infoBiome">n/a</span></p>
|
||||||
<p><b>State:</b> <span id="infoState">n/a</span></p>
|
<p><b>State:</b> <span id="infoState">n/a</span></p>
|
||||||
|
|
@ -3310,8 +3365,8 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div data-tip="Set sky and water color" id="options3dColorSection" style="display: none">
|
<div data-tip="Set sky and water color" id="options3dColorSection" style="display: none">
|
||||||
<span>Sky:</span><input id="options3dMeshSky" type="color" style="width: 4.4em; border: 0; padding: 0; margin: 0 .2em">
|
<span>Sky:</span><input id="options3dMeshSky" type="color" style="width: 4.4em; height: 1em; border: 0; padding: 0; margin: 0 .2em">
|
||||||
<span>Water:</span><input id="options3dMeshWater" type="color" style="width: 4.4em; border: 0; padding: 0; margin: 0 .2em">
|
<span>Water:</span><input id="options3dMeshWater" type="color" style="width: 4.4em; height: 1em; border: 0; padding: 0; margin: 0 .2em">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -3337,6 +3392,7 @@
|
||||||
|
|
||||||
<div id="options3dBottom" style="margin-top: .2em">
|
<div id="options3dBottom" style="margin-top: .2em">
|
||||||
<button id="options3dUpdate" data-tip="Update the scene" class="icon-cw"></button>
|
<button id="options3dUpdate" data-tip="Update the scene" class="icon-cw"></button>
|
||||||
|
<button data-tip="Configure world and map size and climate settings" onclick="editWorld()" class="icon-globe"></button>
|
||||||
<button id="options3dSave" data-tip="Save screenshot of the 3d scene" class="icon-download"></button>
|
<button id="options3dSave" data-tip="Save screenshot of the 3d scene" class="icon-download"></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -3392,6 +3448,7 @@
|
||||||
<input type="file" accept=".json" id="styleToLoad">
|
<input type="file" accept=".json" id="styleToLoad">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src="libs/translate.js"></script>
|
||||||
<script src="libs/jquery-3.1.1.min.js"></script>
|
<script src="libs/jquery-3.1.1.min.js"></script>
|
||||||
<script src="libs/d3.min.js"></script>
|
<script src="libs/d3.min.js"></script>
|
||||||
<script src="libs/priority-queue.min.js"></script>
|
<script src="libs/priority-queue.min.js"></script>
|
||||||
|
|
@ -3406,6 +3463,7 @@
|
||||||
<script src="modules/burgs-and-states.js"></script>
|
<script src="modules/burgs-and-states.js"></script>
|
||||||
<script src="modules/routes-generator.js"></script>
|
<script src="modules/routes-generator.js"></script>
|
||||||
<script src="modules/religions-generator.js"></script>
|
<script src="modules/religions-generator.js"></script>
|
||||||
|
<script src="modules/military-generator.js"></script>
|
||||||
<script src="libs/polylabel.min.js"></script>
|
<script src="libs/polylabel.min.js"></script>
|
||||||
<script src="libs/jquery-ui.min.js"></script>
|
<script src="libs/jquery-ui.min.js"></script>
|
||||||
<script src="libs/seedrandom.min.js"></script>
|
<script src="libs/seedrandom.min.js"></script>
|
||||||
|
|
@ -3441,7 +3499,8 @@
|
||||||
<script defer src="modules/ui/zones-editor.js"></script>
|
<script defer src="modules/ui/zones-editor.js"></script>
|
||||||
<script defer src="modules/ui/burgs-overview.js"></script>
|
<script defer src="modules/ui/burgs-overview.js"></script>
|
||||||
<script defer src="modules/ui/rivers-overview.js"></script>
|
<script defer src="modules/ui/rivers-overview.js"></script>
|
||||||
<!-- <script defer src="modules/ui/military-overview.js"></script> -->
|
<script defer src="modules/ui/military-overview.js"></script>
|
||||||
|
<script defer src="modules/ui/regiment-editor.js"></script>
|
||||||
<script defer src="modules/ui/editors.js"></script>
|
<script defer src="modules/ui/editors.js"></script>
|
||||||
<script defer src="modules/ui/3d.js"></script>
|
<script defer src="modules/ui/3d.js"></script>
|
||||||
<script defer src="libs/quantize.min.js"></script>
|
<script defer src="libs/quantize.min.js"></script>
|
||||||
|
|
|
||||||
511
lang/lang-en.js
Normal file
511
lang/lang-en.js
Normal file
|
|
@ -0,0 +1,511 @@
|
||||||
|
// Source translation file for FMG. This file is only used as a reference for tranlation. Version 0.01a
|
||||||
|
window.translation = {
|
||||||
|
titleFull: "Azgaar's Fantasy Map Generator",
|
||||||
|
titleName: "Azgaar's",
|
||||||
|
title: "Fantasy Map Generator",
|
||||||
|
version: "v. ",
|
||||||
|
loading: "Loading",
|
||||||
|
newMap: "New Map!",
|
||||||
|
layers: "Layers",
|
||||||
|
style: "Style",
|
||||||
|
options: "Options",
|
||||||
|
tools: "Tools",
|
||||||
|
about: "About",
|
||||||
|
tipOptionsTrigger: "Click to show options pane. Shortcut: Tab",
|
||||||
|
tipRegenerate: "Click to generate a new map. Shortcut: F2",
|
||||||
|
optionsDragTrigger: "Drag to move options pane",
|
||||||
|
optionsHide: "Click to hide options pane. Shortcut: Tab to close this or Esc to close all dialogs",
|
||||||
|
layersTab: "Click to change map layers",
|
||||||
|
styleTab: "Click to open style editor",
|
||||||
|
optionsTab: "Click to change generation and UI options",
|
||||||
|
toolsTab: "Click to open tools menu",
|
||||||
|
aboutTab: "Click to see Generator info"
|
||||||
|
};
|
||||||
|
|
||||||
|
return;
|
||||||
|
// list of tooltips from init DOM (dynamically added elements are not icluded)
|
||||||
|
const sourceDataForReference = {
|
||||||
|
optionsTrigger: "Click to show options pane. Shortcut: Tab",
|
||||||
|
NeedToAdd!: "Drag to move options pane",
|
||||||
|
optionsHide: "Click to hide options pane. Shortcut: Tab to close this or Esc to close all dialogs",
|
||||||
|
NeedToAdd!: "Select a map layers preset",
|
||||||
|
savePresetButton: "Click to save displayed layers as a new preset",
|
||||||
|
removePresetButton: "Click to remove current custom preset",
|
||||||
|
NeedToAdd!: "Click to toggle a layer, drag to raise or lower a layer. Ctrl + click to edit layer style",
|
||||||
|
toggleRulers: "Rulers: click to toggle, drag to move, click on label to delete. Ctrl + click to edit layer style. Shortcut: = (equal)",
|
||||||
|
toggleScaleBar: "Scale Bar: click to toggle. Ctrl + click to edit style. Shortcut: - (minus)",
|
||||||
|
viewStandard: "Standard view mode that allows to edit the map",
|
||||||
|
viewMesh: "Map presentation in 3D scene. Works best for heightmap. Cannot be used for editing",
|
||||||
|
viewGlobe: "Project map on globe. Cannot be used for editing",
|
||||||
|
NeedToAdd!: "Select a style preset",
|
||||||
|
addStyleButton: "Click to save current style as a new preset",
|
||||||
|
removeStyleButton: "Click to remove current custom style preset",
|
||||||
|
NeedToAdd!: "Select an element to edit its style",
|
||||||
|
styleIsOff: "The selected layer is not visible. See the buttons above to toggle it on",
|
||||||
|
NeedToAdd!: "Click and provide a URL to image to be set as a texture",
|
||||||
|
styleTextureShiftX: "Shift texture by x axis in pixels",
|
||||||
|
styleTextureShiftY: "Shift texture by y axis in pixels",
|
||||||
|
styleGridSizeFriendly: "Distance between two adjacent cells in map scale",
|
||||||
|
styleShiftX: "Shift by x axis in pixels",
|
||||||
|
styleShiftY: "Shift by y axis in pixels",
|
||||||
|
styleCompassShiftX: "Shift by x axis in pixels",
|
||||||
|
styleCompassShiftY: "Shift by y axis in pixels",
|
||||||
|
styleInputFont: "Provide a link to @font-face declaration or type Google font name",
|
||||||
|
styleFontAdd: "Add custom font from the web",
|
||||||
|
styleFontPlus: "Multiply font size by 1.1",
|
||||||
|
styleFontMinus: "Multiply font size by 0.9",
|
||||||
|
styleRadiusPlus: "Multiply radius by 1.1",
|
||||||
|
styleRadiusMinus: "Multiply radius by 1.1",
|
||||||
|
styleIconSizePlus: "Multiply size by 1.1",
|
||||||
|
styleIconSizeMinus: "Multiply size by 1.1",
|
||||||
|
NeedToAdd!: "Allow system to hide labels if their size in too small or too big on that scale",
|
||||||
|
NeedToAdd!: "Map generation settings. Generate a new map to apply the settings",
|
||||||
|
toggleFullscreen: "Toggle between screen size and initial canvas size",
|
||||||
|
optionsSeedGenerate: "Click to generate a map for this seed",
|
||||||
|
optionsMapHistory: "Show seed history to apply a previous seed",
|
||||||
|
optionsCopySeed: "Copy map seed as URL. It will produce the same map only if options are default or the same",
|
||||||
|
NeedToAdd!: "Regenerate map name",
|
||||||
|
NeedToAdd!: "Tool settings that don't affect maps. Changes are getting applied immediately",
|
||||||
|
NeedToAdd!: "Mimimal possible zoom level (should be > 0)",
|
||||||
|
zoomExtentMin: "Mimimal possible zoom level (should be > 0)",
|
||||||
|
NeedToAdd!: "Maximal possible zoom level (should be > 1)",
|
||||||
|
zoomExtentMax: "Maximal possible zoom level (should be > 1)",
|
||||||
|
zoomExtentDefault: "Restore default [1, 20] zoom extent",
|
||||||
|
configureWorld: "Click to open world configurator to setup map position on Globe and World climate",
|
||||||
|
optionsReset: "Click to restore default options (page will be reloaded)",
|
||||||
|
editHeightmapButton: "Click to open Heightmap customization menu. Shortcut: Shift + H",
|
||||||
|
editBiomesButton: "Click to open Biomes Editor. Shortcut: Shift + B",
|
||||||
|
editStatesButton: "Click to open States Editor. Shortcut: Shift + S",
|
||||||
|
editProvincesButton: "Click to open Provinces Editor. Shortcut: Shift + P",
|
||||||
|
editDiplomacyButton: "Click to open Diplomatical relationships Editor. Shortcut: Shift + D",
|
||||||
|
editCulturesButton: "Click to open Cultures Editor. Shortcut: Shift + C",
|
||||||
|
editNamesBaseButton: "Click to open Namesbase Editor. Shortcut: Shift + N",
|
||||||
|
editZonesButton: "Click to open Zones Editor. Shortcut: Shift + Z",
|
||||||
|
editReligions: "Click to open Religions Editor. Shortcut: Shift + R",
|
||||||
|
editUnitsButton: "Click to open Units Editor. Shortcut: Shift + Q",
|
||||||
|
editNotesButton: "Click to open Notes Editor. Shortcut: Shift + O",
|
||||||
|
overviewBurgsButton: "Click to open Burgs Overview. Shortcut: Shift + T",
|
||||||
|
overviewRiversButton: "Click to open Rivers Overview. Shortcut: Shift + V",
|
||||||
|
overviewMilitaryButton: "Click to open Military Forces Overview. Shortcut: Shift + M",
|
||||||
|
overviewCellsButton: "Click to open Cell details view. Shortcut: Shift + E",
|
||||||
|
regenerateStateLabels: "Click to update state labels placement based on current borders",
|
||||||
|
regenerateReliefIcons: "Click to regenerate all relief icons based on current cell biome and elevation",
|
||||||
|
regenerateRoutes: "Click to regenerate all routes",
|
||||||
|
regenerateRivers: "Click to regenerate all rivers (restore default state)",
|
||||||
|
regeneratePopulation: "Click to recalculate rural and urban population",
|
||||||
|
regenerateBurgs: "Click to regenerate all burgs and routes. States will remain as they are",
|
||||||
|
regenerateStates: "Click to select new capitals and regenerate states. Burgs will remain as they are",
|
||||||
|
regenerateProvinces: "Click to regenerate provinces. States will remain as they are",
|
||||||
|
regenerateReligions: "Click to regenerate religions",
|
||||||
|
regenerateMarkers: "Click to regenerate markers. Hold Ctrl and click to set markers number multiplier",
|
||||||
|
regenerateZones: "Click to regenerate zones. Hold Ctrl and click to set zones number multiplier",
|
||||||
|
addBurgTool: "Click on map to place a burg. Hold Shift to add multiple. Shortcut: Shift + 1",
|
||||||
|
addLabel: "Click on map to place label. Hold Shift to add multiple. Shortcut: Shift + 2",
|
||||||
|
addRiver: "Click on map to place a river. Hold Shift to add multiple. Shortcut: Shift + 3",
|
||||||
|
addRoute: "Click on map to place a route. Shortcut: Shift + 4",
|
||||||
|
addMarker: "Click on map to place a marker. Hold Shift to add multiple. Shortcut: Shift + 5",
|
||||||
|
paintBrushes: "Display brushes panel",
|
||||||
|
applyTemplate: "Open template editor",
|
||||||
|
convertImage: "Open Image Converter",
|
||||||
|
heightmapPreview: "Render heightmap data as a small monochrome image",
|
||||||
|
heightmap3DView: "Preview heightmap in 3D scene",
|
||||||
|
NeedToAdd!: "Click to see supporters names",
|
||||||
|
newMapButton: "Generate a new map based on options. Shortcut: F2",
|
||||||
|
saveButton: "Select format to save map",
|
||||||
|
loadButton: "Load fully functional map in a .map format",
|
||||||
|
zoomReset: "Reset map zoom. Shortcut: 0",
|
||||||
|
finalizeHeightmap: "Finalize the heightmap and exit the edit mode",
|
||||||
|
NeedToAdd!: "Length of Meridian. Almost half of the equator length",
|
||||||
|
meridianLength: "Length of Meridian in pixels",
|
||||||
|
meridianLengthFriendly: "Length of Meridian is friendly units (depends on user configuration)",
|
||||||
|
meridianLengthEarth: "Fantasy world Meridian length relative to real-world Earth (20k km)",
|
||||||
|
labelGroupShow: "Show the group selection",
|
||||||
|
labelGroupHide: "Hide the group selection",
|
||||||
|
labelGroupSelect: "Select a group for this label",
|
||||||
|
labelGroupInput: "Provide a name for the new group",
|
||||||
|
labelGroupNew: "Create new group for this label",
|
||||||
|
labelGroupRemove: "Remove the Group with all labels",
|
||||||
|
labelTextShow: "Show the edit label text section",
|
||||||
|
labelTextHide: "Hide the edit label text section",
|
||||||
|
labelText: "Type to change the label. Enter "|" to move to a new line",
|
||||||
|
labelTextRandom: "Generate random name",
|
||||||
|
labelEditStyle: "Edit label group style in Style Editor",
|
||||||
|
labelSizeShow: "Show the font size section",
|
||||||
|
labelSizeHide: "Hide the font size section",
|
||||||
|
labelStartOffset: "Set starting offset for the particular label",
|
||||||
|
labelRelativeSize: "Set relative size for the particular label",
|
||||||
|
labelAlign: "Turn text path into a straight line",
|
||||||
|
labelLegend: "Edit free text notes (legend) for this label",
|
||||||
|
labelRemoveSingle: "Remove the label. Shortcut: Delete",
|
||||||
|
riverNameShow: "Show river name section",
|
||||||
|
riverNameHide: "Hide the river name section",
|
||||||
|
riverName: "Change river proper name",
|
||||||
|
riverType: "Change river type name",
|
||||||
|
riverNameCulture: "Generate culture-specific name for the river",
|
||||||
|
riverNameRandom: "Generate random name for the river",
|
||||||
|
riverWidthShow: "Show river width and widening change section",
|
||||||
|
riverWidthHide: "Hide the river width and widening change section",
|
||||||
|
riverWidthInput: "Change river width",
|
||||||
|
riverIncrement: "Change river bed increment (widening speed)",
|
||||||
|
riverEditStyle: "Edit style for all rivers in Style Editor",
|
||||||
|
riverLength: "River length in selected units",
|
||||||
|
riverNew: "Create new river clicking on map",
|
||||||
|
riverLegend: "Edit free text notes (legend) for the river",
|
||||||
|
riverRemove: "Remove river. Shortcut: Delete",
|
||||||
|
routeGroupsShow: "Show the group selection",
|
||||||
|
routeGroupsHide: "Hide the group section",
|
||||||
|
routeGroup: "Select a group for this route",
|
||||||
|
routeGroupName: "Provide a name for the new group",
|
||||||
|
routeGroupAdd: "Create new group for this route",
|
||||||
|
routeGroupRemove: "Remove all routes of this group",
|
||||||
|
routeEditStyle: "Edit route group style in Style Editor",
|
||||||
|
routeLength: "Route length in selected units",
|
||||||
|
routeSplit: "Click on a control point to split the route",
|
||||||
|
routeLegend: "Edit free text notes (legend) for the route",
|
||||||
|
routeNew: "Create new route clicking on map",
|
||||||
|
routeRemove: "Remove route. Shortcut: Delete",
|
||||||
|
lakeGroupsShow: "Show the group selection",
|
||||||
|
lakeGroupsHide: "Hide the group section",
|
||||||
|
lakeGroup: "Select a group for this lake",
|
||||||
|
lakeGroupName: "Provide a name for the new group",
|
||||||
|
lakeGroupAdd: "Create new group for this lake",
|
||||||
|
lakeGroupRemove: "Remove the group",
|
||||||
|
lakeEditStyle: "Edit lake group style in Style Editor",
|
||||||
|
lakeArea: "Lake area in selected units",
|
||||||
|
lakeLegend: "Edit free text notes (legend) for the lake",
|
||||||
|
coastlineGroupsShow: "Show the group selection",
|
||||||
|
coastlineGroupsHide: "Hide the group section",
|
||||||
|
coastlineGroup: "Select a group for this coastline",
|
||||||
|
coastlineGroupName: "Provide a name for the new group",
|
||||||
|
coastlineGroupAdd: "Create new group for this coastline",
|
||||||
|
coastlineGroupRemove: "Remove the group",
|
||||||
|
coastlineEditStyle: "Edit coastline group style in Style Editor",
|
||||||
|
coastlineArea: "Lake area in selected units",
|
||||||
|
reliefIndividual: "Edit individual selected icon",
|
||||||
|
reliefBulkAdd: "Place icons in a bulk",
|
||||||
|
reliefBulkRemove: "Remove icons in a bulk",
|
||||||
|
reliefEditStyle: "Edit Relief Icons style in Style Editor",
|
||||||
|
reliefCopy: "Copy selected relief icon",
|
||||||
|
reliefMoveFront: "Move selected relief icon to front",
|
||||||
|
reliefMoveBack: "Move selected relief icon back",
|
||||||
|
reliefRemove: "Remove selected relief icon. Shortcut: Delete",
|
||||||
|
burgName: "Type to rename the burg",
|
||||||
|
burgNameReCulture: "Generate culture-specific name for the burg",
|
||||||
|
burgNameReRandom: "Generate random name for the burg",
|
||||||
|
burgPopulation: "Set burg population",
|
||||||
|
burgCapital: "Shows whether the burg is a state capital. Click to toggle",
|
||||||
|
burgPort: "Shows whether the burg is a port. Click to toggle",
|
||||||
|
burgCitadel: "Shows whether the burg has a citadel (castle). Click to toggle",
|
||||||
|
burgWalls: "Shows whether the burg is walled. Click to toggle",
|
||||||
|
burgPlaza: "Shows whether the burg is a trade center (has big marketplace). Click to toggle",
|
||||||
|
burgTemple: "Shows whether the burg is a religious center. Click to toggle",
|
||||||
|
burgShanty: "Shows whether the burg has a shanty town. Click to toggle",
|
||||||
|
burgGroupShow: "Show group change section",
|
||||||
|
burgGroupHide: "Hide group change section",
|
||||||
|
burgSelectGroup: "Select a group for this burg",
|
||||||
|
burgInputGroup: "Create new Group for the Burg",
|
||||||
|
burgAddGroup: "Create new group for the burg",
|
||||||
|
burgRemoveGroup: "Remove selected burg group",
|
||||||
|
burgStyleShow: "Show style edit section",
|
||||||
|
burgStyleHide: "Hide style edit section",
|
||||||
|
burgEditLabelStyle: "Edit label style for burg group in Style Editor",
|
||||||
|
burgEditIconStyle: "Edit icon style for burg group in Style Editor",
|
||||||
|
burgEditAnchorStyle: "Edit port icon (anchor) style for burg group in Style Editor",
|
||||||
|
burgSeeInMFCG: "Open burg in the Medieval Fantasy City Generator by Watabou. Ctrl + click to change the seed",
|
||||||
|
burgOpenCOA: "Open burg's COA in the Iron Arachne Heraldry Generator. Ctrl + click to change the seed",
|
||||||
|
burgRelocate: "Relocate burg",
|
||||||
|
burglLegend: "Edit free text notes (legend) for this burg",
|
||||||
|
burgRemove: "Remove non-capital burg. Shortcut: Delete",
|
||||||
|
markerGroup: "Change marker type",
|
||||||
|
markerSelectGroup: "Select type for the selected marker",
|
||||||
|
markerInputGroup: "Create new type for selected marker",
|
||||||
|
markerAddGroup: "Create new markers type",
|
||||||
|
markerRemoveGroup: "Remove all markers of that type",
|
||||||
|
markerIcon: "Change marker icon and edit positioning",
|
||||||
|
NeedToAdd!: "Change marker icon size",
|
||||||
|
markerIconSize: "Change marker icon size",
|
||||||
|
NeedToAdd!: "Change marker horizontal shift",
|
||||||
|
markerIconShiftX: "Change icon horizontal shift",
|
||||||
|
NeedToAdd!: "Change marker vertical shift",
|
||||||
|
markerIconShiftY: "Change vertical shift",
|
||||||
|
NeedToAdd!: "Paste custom unicode symbol to use as icon",
|
||||||
|
markerIconCustom: "Paste custom unicode symbol to use as icon",
|
||||||
|
markerStyle: "Change marker size and colors",
|
||||||
|
NeedToAdd!: "Change marker base (pin) style",
|
||||||
|
markerSize: "Change marker size",
|
||||||
|
markerBaseStroke: "Change pin stroke color",
|
||||||
|
markerBaseFill: "Change pin fill color",
|
||||||
|
NeedToAdd!: "Change marker icon style",
|
||||||
|
markerIconStrokeWidth: "Change icon stroke width",
|
||||||
|
markerIconStroke: "Change icon stroke color. Ensure icon stroke width is non-zero",
|
||||||
|
markerIconFill: "Change icon fill color",
|
||||||
|
markerToggleBubble: "Toggle pin (bubble) display",
|
||||||
|
markerLegendButton: "Edit place legend (free text notes)",
|
||||||
|
markerAdd: "Add additional marker of that type",
|
||||||
|
markerRemove: "Remove the marker. Shortcut: Delete",
|
||||||
|
undo: "Undo the latest action (Ctrl + Z)",
|
||||||
|
redo: "Redo the action (Ctrl + Y)",
|
||||||
|
rescaleShow: "Show rescaler slider",
|
||||||
|
rescaleCondShow: "Rescaler: change height if condition is fulfilled",
|
||||||
|
smoothHeights: "Smooth all heights a bit",
|
||||||
|
disruptHeights: "Disrupt (randomize) heights a bit",
|
||||||
|
brushClear: "Set height for all cells to 0 (erase the map)",
|
||||||
|
rescaleHide: "Hide rescaler slider",
|
||||||
|
rescaler: "Change height for all cells",
|
||||||
|
rescaleCondHide: "Hide rescaler",
|
||||||
|
rescaleExecute: "Click to perform an operation",
|
||||||
|
templateHill: "Hill: small blob",
|
||||||
|
templatePit: "Pit: round depression",
|
||||||
|
templateRange: "Range: elongated elevation",
|
||||||
|
templateTrough: "Trough: elongated depression",
|
||||||
|
templateStrait: "Strait: centered vertical or horizontal depression",
|
||||||
|
templateAdd: "Add or subtract value from all heights in range",
|
||||||
|
templateMultiply: "Multiply all heights in range by factor",
|
||||||
|
templateSmooth: "Smooth the map replacing cell heights by an average values of its neighbors",
|
||||||
|
NeedToAdd!: "Click to skip the step",
|
||||||
|
NeedToAdd!: "Remove the step",
|
||||||
|
NeedToAdd!: "Drag to reorder",
|
||||||
|
NeedToAdd!: "Placement range percentage along Y axis (minY-maxY)",
|
||||||
|
NeedToAdd!: "Placement range percentage along X axis (minX-maxX)",
|
||||||
|
NeedToAdd!: "Blob maximum height, use hyphen to get a random number in range",
|
||||||
|
NeedToAdd!: "Blobs to add, use hyphen to get a random number in range",
|
||||||
|
templateRun: "Apply current template",
|
||||||
|
templateUndo: "Undo the latest action",
|
||||||
|
templateRedo: "Redo the action",
|
||||||
|
templateSave: "Download the template as a text file",
|
||||||
|
templateLoad: "Open previously downloaded template",
|
||||||
|
templateCA: "Find or share custom template on Cartography Assets portal",
|
||||||
|
templateTutorial: "Open Template Editor Tutorial",
|
||||||
|
convertImageLoad: "Load image to convert",
|
||||||
|
convertAutoLum: "Auto-assign colors based on liminosity (good to monochrome images)",
|
||||||
|
convertAutoHue: "Auto-assign colors based on hue (good to colored images)",
|
||||||
|
convertColorsButton: "Set number of colors",
|
||||||
|
convertComplete: "Complete the assignment. All unassigned colors will be considered as ocean",
|
||||||
|
NeedToAdd!: "Click to sort by biome name",
|
||||||
|
NeedToAdd!: "Click to sort by biome habitability",
|
||||||
|
NeedToAdd!: "Click to sort by biome cells number",
|
||||||
|
NeedToAdd!: "Click to sort by biome area",
|
||||||
|
NeedToAdd!: "Click to sort by biome population",
|
||||||
|
biomesEditorRefresh: "Refresh the Editor",
|
||||||
|
biomesEditStyle: "Edit biomes style in Style Editor",
|
||||||
|
biomesLegend: "Toggle Legend box",
|
||||||
|
biomesPercentage: "Toggle percentage / absolute values views",
|
||||||
|
biomesManually: "Manually re-assign biomes to not follow the default moisture/temperature pattern",
|
||||||
|
biomesManuallyApply: "Apply current assignment",
|
||||||
|
biomesManuallyCancel: "Cancel assignment",
|
||||||
|
biomesAdd: "Add a custom biome",
|
||||||
|
biomesRestore: "Restore the defaults and re-define biomes based on current moisture and temperature",
|
||||||
|
biomesRegenerateReliefIcons: "Regenerate relief icons based on current biomes and elevation",
|
||||||
|
biomesExport: "Save biomes-related data as a text file (.csv)",
|
||||||
|
NeedToAdd!: "Click to sort by state name",
|
||||||
|
NeedToAdd!: "Click to sort by state form name",
|
||||||
|
NeedToAdd!: "Click to sort by capital name",
|
||||||
|
NeedToAdd!: "Click to sort by state dominant culture",
|
||||||
|
NeedToAdd!: "Click to sort by state burgs count",
|
||||||
|
NeedToAdd!: "Click to sort by state area",
|
||||||
|
NeedToAdd!: "Click to sort by state population",
|
||||||
|
NeedToAdd!: "Click to sort by state type",
|
||||||
|
NeedToAdd!: "Click to sort by state expansion value",
|
||||||
|
NeedToAdd!: "Click to sort by state cells count",
|
||||||
|
statesEditorRefresh: "Refresh the Editor",
|
||||||
|
statesEditStyle: "Edit states style in Style Editor",
|
||||||
|
statesLegend: "Toggle Legend box",
|
||||||
|
statesPercentage: "Toggle percentage / absolute values views",
|
||||||
|
statesChart: "Show states bubble chart",
|
||||||
|
statesRegenerate: "Show the regeneration menu and more data",
|
||||||
|
statesRegenerateBack: "Hide the regeneration menu",
|
||||||
|
statesRandomize: "Randomize states Expansion value and re-calculate states and provinces",
|
||||||
|
statesRecalculate: "Recalculate states based on current values of growth-related attributes",
|
||||||
|
statesManually: "Manually re-assign states",
|
||||||
|
statesManuallyApply: "Apply assignment",
|
||||||
|
statesManuallyCancel: "Cancel assignment",
|
||||||
|
statesAdd: "Add a new state. Hold Shift to add multiple",
|
||||||
|
statesExport: "Save state-related data as a text file (.csv)",
|
||||||
|
NeedToAdd!: "State short name",
|
||||||
|
stateNameEditorShort: "Type to change the short name",
|
||||||
|
stateNameEditorShortCulture: "Generate culture-specific name",
|
||||||
|
stateNameEditorShortRandom: "Generate random name",
|
||||||
|
NeedToAdd!: "State form name",
|
||||||
|
stateNameEditorCustomForm: "Create custom state form name",
|
||||||
|
stateNameEditorAddForm: "Click to add custom state form name to the list",
|
||||||
|
NeedToAdd!: "State full name",
|
||||||
|
stateNameEditorFull: "Type to change the full name",
|
||||||
|
stateNameEditorFullRegenerate: "Click to re-generate full name",
|
||||||
|
NeedToAdd!: "Click to sort by province name",
|
||||||
|
NeedToAdd!: "Click to sort by province form name",
|
||||||
|
NeedToAdd!: "Click to sort by province capital",
|
||||||
|
NeedToAdd!: "Click to sort by province owner",
|
||||||
|
NeedToAdd!: "Click to sort by province area",
|
||||||
|
NeedToAdd!: "Click to sort by province population",
|
||||||
|
provincesEditorRefresh: "Refresh the Editor",
|
||||||
|
provincesEditStyle: "Edit provinces style in Style Editor",
|
||||||
|
provincesPercentage: "Toggle percentage / absolute values views",
|
||||||
|
provincesChart: "Show provinces chart",
|
||||||
|
provincesToggleLabels: "Toggle province labels",
|
||||||
|
provincesExport: "Save provinces-related data as a text file (.csv)",
|
||||||
|
provincesManually: "Manually re-assign provinces",
|
||||||
|
provincesManuallyApply: "Apply assignment",
|
||||||
|
provincesManuallyCancel: "Cancel assignment",
|
||||||
|
provincesAdd: "Add a new province. Hold Shift to add multiple",
|
||||||
|
provincesRemoveAll: "Remove all provinces. States will remain as they are",
|
||||||
|
NeedToAdd!: "Click to sort by state name",
|
||||||
|
NeedToAdd!: "Click to sort by diplomatical relations",
|
||||||
|
NeedToAdd!: "Ally means states formed a defensive pact and will protect each other in case of third party aggression",
|
||||||
|
NeedToAdd!: "State is friendly to anouther state when they share some common interests",
|
||||||
|
NeedToAdd!: "Neutral means states relations are neither positive nor negative",
|
||||||
|
NeedToAdd!: "Suspicion means shate has a cautious distrust of another state",
|
||||||
|
NeedToAdd!: "Enemies are states at war with each other",
|
||||||
|
NeedToAdd!: "Relations are unknown if states do not have enought information about each other",
|
||||||
|
NeedToAdd!: "Rivalry is a state of competing for dominance in the region",
|
||||||
|
NeedToAdd!: "Vassal is a state having obligation to its suzerain",
|
||||||
|
NeedToAdd!: "Suzerain is a state having some control over its vassals",
|
||||||
|
diplomacyEditorRefresh: "Refresh the Editor",
|
||||||
|
diplomacyEditStyle: "Edit states (including diplomacy view) style in Style Editor",
|
||||||
|
diplomacyRegenerate: "Regenerate diplomatical relations",
|
||||||
|
diplomacyHistory: "Show relations history",
|
||||||
|
diplomacyMatrix: "Show relations matrix",
|
||||||
|
diplomacyExport: "Save state relations matrix as a text file (.csv)",
|
||||||
|
NeedToAdd!: "Province short name",
|
||||||
|
provinceNameEditorShort: "Type to change the short name",
|
||||||
|
provinceNameEditorShortCulture: "Generate culture-specific name",
|
||||||
|
provinceNameEditorShortRandom: "Generate random name",
|
||||||
|
NeedToAdd!: "Province form name",
|
||||||
|
provinceNameEditorCustomForm: "Create custom province form name",
|
||||||
|
provinceNameEditorAddForm: "Click to add custom province form name to the list",
|
||||||
|
NeedToAdd!: "Province full name",
|
||||||
|
provinceNameEditorFull: "Type to change the full name",
|
||||||
|
provinceNameEditorFullRegenerate: "Click to re-generate full name",
|
||||||
|
NeedToAdd!: "Click to sort by culture name",
|
||||||
|
NeedToAdd!: "Click to sort by culture cells count",
|
||||||
|
NeedToAdd!: "Click to sort by expansionism",
|
||||||
|
NeedToAdd!: "Click to sort by type",
|
||||||
|
NeedToAdd!: "Click to sort by culture area",
|
||||||
|
NeedToAdd!: "Click to sort by culture population",
|
||||||
|
NeedToAdd!: "Click to sort by culture namesbase",
|
||||||
|
culturesEditorRefresh: "Refresh the Editor",
|
||||||
|
culturesEditStyle: "Edit cultures style in Style Editor",
|
||||||
|
culturesLegend: "Toggle Legend box",
|
||||||
|
culturesPercentage: "Toggle percentage / absolute values display mode",
|
||||||
|
culturesHeirarchy: "Show cultures hierarchy tree",
|
||||||
|
culturesManually: "Manually re-assign cultures",
|
||||||
|
culturesManuallyApply: "Apply assignment",
|
||||||
|
culturesManuallyCancel: "Cancel assignment",
|
||||||
|
culturesEditNamesBase: "Edit a database used for names generation",
|
||||||
|
culturesAdd: "Add a new culture. Hold Shift to add multiple",
|
||||||
|
culturesExport: "Download cultures-related data",
|
||||||
|
culturesRecalculate: "Recalculate cultures based on current values of growth-related attributes",
|
||||||
|
namesbaseSelect: "Select base to edit",
|
||||||
|
namesbaseTextarea: "Names data: a comma separated list of source names used for names generation",
|
||||||
|
namesbaseName: "Type to change a base name",
|
||||||
|
namesbaseMin: "Recommended minimum name length",
|
||||||
|
namesbaseMax: "Recommended maximum name length",
|
||||||
|
namesbaseDouble: "Populate with letters that can used twice in a row",
|
||||||
|
namesbaseMulti: "Multi-word names rate. 1 - allow all multi-word names, 0 - all names should spelled as a single word",
|
||||||
|
namesbaseExamples: "Examples. Click to re-generate",
|
||||||
|
namesbaseUpdateExamples: "Re-generate examples based on provided data",
|
||||||
|
namesbaseAdd: "Add new namesbase",
|
||||||
|
namesbaseDefault: "Restore default namesbase",
|
||||||
|
namesbaseDownload: "Download namesbase to PC",
|
||||||
|
namesbaseUpload: "Upload a namesbase from PC",
|
||||||
|
namesbaseCA: "Find or share custom namesbase on Cartography Assets portal",
|
||||||
|
NeedToAdd!: "Zone description",
|
||||||
|
NeedToAdd!: "Zone cells count",
|
||||||
|
NeedToAdd!: "Zone area",
|
||||||
|
NeedToAdd!: "Zone population",
|
||||||
|
zonesEditorRefresh: "Refresh the Editor",
|
||||||
|
zonesEditStyle: "Edit zones style in Style Editor",
|
||||||
|
zonesLegend: "Toggle Legend box",
|
||||||
|
zonesPercentage: "Toggle percentage / absolute values views",
|
||||||
|
zonesManually: "Re-assign zones",
|
||||||
|
zonesManuallyApply: "Apply assignment",
|
||||||
|
zonesManuallyCancel: "Cancel assignment",
|
||||||
|
zonesRemove: "Click to toggle the removal mode on brush dragging. Shortcut: ctrl",
|
||||||
|
zonesAdd: "Add a new zone layer",
|
||||||
|
zonesExport: "Download zones-related data",
|
||||||
|
notesSelect: "Select object",
|
||||||
|
notesName: "Type to change object name",
|
||||||
|
notesText: "Type object description",
|
||||||
|
notesFocus: "Focus on selected object",
|
||||||
|
notesDownload: "Download notes to PC",
|
||||||
|
notesUpload: "Upload notes from PC",
|
||||||
|
notesRemove: "Remove this note",
|
||||||
|
NeedToAdd!: "Click to sort by religion name",
|
||||||
|
NeedToAdd!: "Click to sort by religion type",
|
||||||
|
NeedToAdd!: "Click to sort by religion form",
|
||||||
|
NeedToAdd!: "Click to sort by supreme deity",
|
||||||
|
NeedToAdd!: "Click to sort by religion area",
|
||||||
|
NeedToAdd!: "Click to sort by number of believers (religion area population)",
|
||||||
|
religionsEditorRefresh: "Refresh the Editor",
|
||||||
|
religionsEditStyle: "Edit religions style in Style Editor",
|
||||||
|
religionsLegend: "Toggle Legend box",
|
||||||
|
religionsPercentage: "Toggle percentage / absolute values display mode",
|
||||||
|
religionsHeirarchy: "Show religions hierarchy tree",
|
||||||
|
religionsExtinct: "Show/hide extinct religions (religions without cells)",
|
||||||
|
religionsManually: "Manually re-assign religions",
|
||||||
|
religionsManuallyApply: "Apply assignment",
|
||||||
|
religionsManuallyCancel: "Cancel assignment",
|
||||||
|
religionsAdd: "Add a new religion. Hold Shift to add multiple",
|
||||||
|
religionsExport: "Download religions-related data",
|
||||||
|
addLinearRuler: "Click to place a linear measurer (ruler)",
|
||||||
|
addOpisometer: "Drag to measure a curve length (opisometer)",
|
||||||
|
addPlanimeter: "Drag to measure a polygon area (planimeter)",
|
||||||
|
removeRulers: "Remove all rulers from the map. Click on ruler label to remove a ruler separately",
|
||||||
|
unitsRestore: "Restore default units settings",
|
||||||
|
NeedToAdd!: "Click to sort by burg name",
|
||||||
|
NeedToAdd!: "Click to sort by province name",
|
||||||
|
NeedToAdd!: "Click to sort by state name",
|
||||||
|
NeedToAdd!: "Click to sort by culture name",
|
||||||
|
NeedToAdd!: "Click to sort by burg population",
|
||||||
|
NeedToAdd!: "Click to sort by burg type",
|
||||||
|
burgsOverviewRefresh: "Refresh the Editor",
|
||||||
|
burgsChart: "Show burgs bubble chart",
|
||||||
|
regenerateBurgNames: "Regenerate burg names based on assigned culture",
|
||||||
|
addNewBurg: "Add a new burg. Hold Shift to add multiple",
|
||||||
|
burgsExport: "Save burgs-related data as a text file (.csv)",
|
||||||
|
burgNamesImport: "Rename burgs in bulk",
|
||||||
|
burgsRemoveAll: "Remove all burgs except for capitals. To remove a capital remove its state first",
|
||||||
|
NeedToAdd!: "Click to sort by river name",
|
||||||
|
NeedToAdd!: "Click to sort by river type name",
|
||||||
|
NeedToAdd!: "Click to sort by river length",
|
||||||
|
NeedToAdd!: "Click to sort by river basin",
|
||||||
|
riversOverviewRefresh: "Refresh the Editor",
|
||||||
|
addNewRiver: "Add a new river. Hold Shift to add multiple",
|
||||||
|
riversBasinHighlight: "Toggle basin highlight mode",
|
||||||
|
riversExport: "Save rivers-related data as a text file (.csv)",
|
||||||
|
riversRemoveAll: "Remove all rivers",
|
||||||
|
NeedToAdd!: "State name. Click to sort",
|
||||||
|
militaryTotal: "Total military personnel (considering crew). Click to sort",
|
||||||
|
NeedToAdd!: "State population. Click to sort",
|
||||||
|
NeedToAdd!: "Military personnel rate (% of state population). Depends on war alert. Click to sort",
|
||||||
|
NeedToAdd!: "War Alert. Modifier to military forces number, depends of political situation. Click to sort",
|
||||||
|
militaryOverviewRefresh: "Refresh the Editor",
|
||||||
|
militaryOptionsButton: "Military options. Click to customize units",
|
||||||
|
militaryOverviewRecalculate: "Recalculate military forces based on current options",
|
||||||
|
militaryExport: "Save military-related data as a text file (.csv)",
|
||||||
|
militaryVisualize: "Show armies on the map",
|
||||||
|
NeedToAdd!: "Unit name. If name is changed for existing unit, old unit will be replaced",
|
||||||
|
NeedToAdd!: "Conscription percentage for rural population",
|
||||||
|
NeedToAdd!: "Conscription percentage for urban population",
|
||||||
|
NeedToAdd!: "Average number of people in crew",
|
||||||
|
NeedToAdd!: "Unit type to apply special rules on forces recalculation",
|
||||||
|
styleSaverName: "Enter style preset name",
|
||||||
|
styleSaverTip: "Shows whether there is already a preset with this name",
|
||||||
|
styleSaverJSON: "Style JSON is getting formed based the current settings, but can be entered manually",
|
||||||
|
styleSaverSave: "Save current JSON as a new style preset",
|
||||||
|
styleSaverDownload: "Download the style as a .json file (can be opened in any text editor)",
|
||||||
|
styleSaverLoad: "Open previously downloaded style file",
|
||||||
|
styleSaverCA: "Find or share custom style preset on Cartography Assets portal",
|
||||||
|
NeedToAdd!: "Equirectangular projection is used: distortion is maximum on poles. Use map with aspect ratio 2:1 for best result",
|
||||||
|
options3dUpdate: "Update the scene",
|
||||||
|
NeedToAdd!: "Configure world and map size and climate settings",
|
||||||
|
options3dSave: "Save screenshot of the 3d scene",
|
||||||
|
NeedToAdd!: "Download the map as fully-functional .map file to your machine",
|
||||||
|
NeedToAdd!: "Download the map as vector image (open in browser or Inkscape)",
|
||||||
|
NeedToAdd!: "Download visible part of the map as .png (lossless compressed)",
|
||||||
|
NeedToAdd!: "Download visible part of the map as .jpeg (lossy compressed) image",
|
||||||
|
NeedToAdd!: "Download map data in GeoJSON format",
|
||||||
|
NeedToAdd!: "Save fully-functional map to browser storage. Shortcut: F6",
|
||||||
|
NeedToAdd!: "Load .map file from local disk",
|
||||||
|
NeedToAdd!: "Load .map file from URL (server should allow CORS)",
|
||||||
|
NeedToAdd!: "Load map from browser storage (if saved before)"
|
||||||
|
};
|
||||||
23
lang/lang-ru.js
Normal file
23
lang/lang-ru.js
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
// Файл перевода FMG на русский язык. Источник: lang-en.js. Версия 0.01a
|
||||||
|
window.translation = {
|
||||||
|
titleFull: "Генератор фэнтези карт",
|
||||||
|
titleName: " ",
|
||||||
|
title: "Генератор фэнтези карт",
|
||||||
|
version: "в. ",
|
||||||
|
loading: "ЗАГРУЗКА",
|
||||||
|
newMap: "Новая карта!",
|
||||||
|
layers: "Слои",
|
||||||
|
style: "Стиль",
|
||||||
|
options: "Опции",
|
||||||
|
tools: "Редактор",
|
||||||
|
about: "Справка",
|
||||||
|
tipOptionsTrigger: "Нажмите для открытия панели меню. Клавиша Tab",
|
||||||
|
tipRegenerate: "Нажмите, чтобы сгенерировать новую карту. Клавиша F2",
|
||||||
|
optionsDragTrigger: "Зажмите и тяните, чтобы переместить меню",
|
||||||
|
optionsHide: "Нажмите, чтобы скрыть меню. Клавиша: Tab (скрыть меню) Esc (скрыть меню и закрыть все окна)",
|
||||||
|
layersTab: "Нажмите, чтобы изменить слои карты, их видимость и порядок",
|
||||||
|
styleTab: "Нажмите, чтобы окрыть вкладку для работы над стилем",
|
||||||
|
optionsTab: "Нажмите, чтобы изменить настройки генерации карты и пользовательского интерейса",
|
||||||
|
toolsTab: "Нажмите, чтобы открыть вкладку инструментов",
|
||||||
|
aboutTab: "Нажмите, чтобы посмотреть информацию о Генераторе"
|
||||||
|
};
|
||||||
55
libs/translate.js
Normal file
55
libs/translate.js
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
// Translation module
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
void function() {
|
||||||
|
window.lang = "en"; // default language
|
||||||
|
|
||||||
|
if (localStorage.getItem("lang")) window.lang = localStorage.getItem("lang");
|
||||||
|
else {
|
||||||
|
const isSupported = ln => ["ru"].includes(ln); // list of supported languages with at least 50% support
|
||||||
|
const browserLang = navigator.language.split("-")[0];
|
||||||
|
if (isSupported(browserLang)) window.lang = browserLang;
|
||||||
|
}
|
||||||
|
|
||||||
|
selectLanguage.value = window.lang;
|
||||||
|
if (window.lang === "en") return; // no need to translate
|
||||||
|
initiateTranslation();
|
||||||
|
|
||||||
|
async function initiateTranslation() {
|
||||||
|
const loaded = await loadTranslation();
|
||||||
|
if (!loaded) {
|
||||||
|
tip(`Cannot load ${window.lang} translation, check files in lang folder`, false, "error", 4000);
|
||||||
|
window.lang == "en"; // set to default value
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadTranslation() {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.src = `lang/lang-${window.lang}.js`
|
||||||
|
document.head.append(script);
|
||||||
|
script.onload = () => resolve(true);
|
||||||
|
script.onerror = () => resolve(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (translation["titleFull"]) document.title = translation["titleFull"];
|
||||||
|
|
||||||
|
void function translateDOM() {
|
||||||
|
const tTags = Array.from(document.getElementsByTagName("t"));
|
||||||
|
tTags.forEach(t => {
|
||||||
|
const id = t.dataset.t;
|
||||||
|
const text = translation[id];
|
||||||
|
if (!text) return;
|
||||||
|
t.innerHTML = text;
|
||||||
|
});
|
||||||
|
}()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}()
|
||||||
|
|
||||||
|
function translate(id, originalEn) {
|
||||||
|
const text = translation[id];
|
||||||
|
return text ? text : originalEn;
|
||||||
|
}
|
||||||
129
main.js
129
main.js
|
|
@ -58,6 +58,7 @@ let labels = viewbox.append("g").attr("id", "labels");
|
||||||
let icons = viewbox.append("g").attr("id", "icons");
|
let icons = viewbox.append("g").attr("id", "icons");
|
||||||
let burgIcons = icons.append("g").attr("id", "burgIcons");
|
let burgIcons = icons.append("g").attr("id", "burgIcons");
|
||||||
let anchors = icons.append("g").attr("id", "anchors");
|
let anchors = icons.append("g").attr("id", "anchors");
|
||||||
|
let armies = viewbox.append("g").attr("id", "armies");
|
||||||
let markers = viewbox.append("g").attr("id", "markers").style("display", "none");
|
let markers = viewbox.append("g").attr("id", "markers").style("display", "none");
|
||||||
let fogging = viewbox.append("g").attr("id", "fogging-cont").attr("mask", "url(#fog)")
|
let fogging = viewbox.append("g").attr("id", "fogging-cont").attr("mask", "url(#fog)")
|
||||||
.append("g").attr("id", "fogging").style("display", "none");
|
.append("g").attr("id", "fogging").style("display", "none");
|
||||||
|
|
@ -101,8 +102,7 @@ let grid = {}; // initial grapg based on jittered square grid and data
|
||||||
let pack = {}; // packed graph and data
|
let pack = {}; // packed graph and data
|
||||||
let seed, mapHistory = [], elSelected, modules = {}, notes = [];
|
let seed, mapHistory = [], elSelected, modules = {}, notes = [];
|
||||||
let customization = 0; // 0 - no; 1 = heightmap draw; 2 - states draw; 3 - add state/burg; 4 - cultures draw
|
let customization = 0; // 0 - no; 1 = heightmap draw; 2 - states draw; 3 - add state/burg; 4 - cultures draw
|
||||||
let mapCoordinates = {}; // map coordinates on globe
|
|
||||||
let winds = [225, 45, 225, 315, 135, 315]; // default wind directions
|
|
||||||
let biomesData = applyDefaultBiomesSystem();
|
let biomesData = applyDefaultBiomesSystem();
|
||||||
let nameBases = Names.getNameBases(); // cultures-related data
|
let nameBases = Names.getNameBases(); // cultures-related data
|
||||||
const fonts = ["Almendra+SC", "Georgia", "Arial", "Times+New+Roman", "Comic+Sans+MS", "Lucida+Sans+Unicode", "Courier+New"]; // default web-safe fonts
|
const fonts = ["Almendra+SC", "Georgia", "Arial", "Times+New+Roman", "Comic+Sans+MS", "Lucida+Sans+Unicode", "Courier+New"]; // default web-safe fonts
|
||||||
|
|
@ -114,6 +114,23 @@ const lineGen = d3.line().curve(d3.curveBasis); // d3 line generator with defaul
|
||||||
let scale = 1, viewX = 0, viewY = 0;
|
let scale = 1, viewX = 0, viewY = 0;
|
||||||
const zoom = d3.zoom().scaleExtent([1, 20]).on("zoom", zoomed);
|
const zoom = d3.zoom().scaleExtent([1, 20]).on("zoom", zoomed);
|
||||||
|
|
||||||
|
// default options
|
||||||
|
let options = {}; // options object
|
||||||
|
let mapCoordinates = {}; // map coordinates on globe
|
||||||
|
options.winds = [225, 45, 225, 315, 135, 315]; // default wind directions
|
||||||
|
options.military = [
|
||||||
|
{name:"infantry", rural:.25, urban:.2, crew:1, type:"melee", separate:0},
|
||||||
|
{name:"archers", rural:.12, urban:.2, crew:1, type:"ranged", separate:0},
|
||||||
|
{name:"cavalry", rural:.12, urban:.03, crew:3, type:"mounted", separate:0},
|
||||||
|
{name:"artillery", rural:0, urban:.03, crew:8, type:"machinery", separate:0},
|
||||||
|
{name:"fleet", rural:0, urban:.015, crew:100, type:"naval", separate:1}
|
||||||
|
];
|
||||||
|
|
||||||
|
// woldbuilding options
|
||||||
|
options.year = rand(100, 2000); // current year
|
||||||
|
options.era = Names.getBaseShort(P(.7) ? 1 : rand(nameBases.length)) + " Era"; // current era name, global for all cultures
|
||||||
|
options.eraShort = options.era[0] + "E"; // short name for era
|
||||||
|
|
||||||
applyStoredOptions();
|
applyStoredOptions();
|
||||||
let graphWidth = +mapWidthInput.value, graphHeight = +mapHeightInput.value; // voronoi graph extention, cannot be changed arter generation
|
let graphWidth = +mapWidthInput.value, graphHeight = +mapHeightInput.value; // voronoi graph extention, cannot be changed arter generation
|
||||||
let svgWidth = graphWidth, svgHeight = graphHeight; // svg canvas resolution, can be changed
|
let svgWidth = graphWidth, svgHeight = graphHeight; // svg canvas resolution, can be changed
|
||||||
|
|
@ -516,7 +533,8 @@ function generate() {
|
||||||
BurgsAndStates.drawStateLabels();
|
BurgsAndStates.drawStateLabels();
|
||||||
|
|
||||||
Rivers.specify();
|
Rivers.specify();
|
||||||
//calculateMilitaryForces();
|
|
||||||
|
Military.generate();
|
||||||
addMarkers();
|
addMarkers();
|
||||||
addZones();
|
addZones();
|
||||||
Names.getMapName();
|
Names.getMapName();
|
||||||
|
|
@ -730,7 +748,8 @@ function calculateTemperatures() {
|
||||||
const lat = Math.abs(mapCoordinates.latN - y / graphHeight * mapCoordinates.latT);
|
const lat = Math.abs(mapCoordinates.latN - y / graphHeight * mapCoordinates.latT);
|
||||||
const initTemp = tEq - lat / 90 * tDelta;
|
const initTemp = tEq - lat / 90 * tDelta;
|
||||||
for (let i = r; i < r+grid.cellsX; i++) {
|
for (let i = r; i < r+grid.cellsX; i++) {
|
||||||
cells.temp[i] = initTemp - convertToFriendly(cells.h[i]);
|
const temp = initTemp - convertToFriendly(cells.h[i]);
|
||||||
|
cells.temp[i] = Math.max(Math.min(temp, 127), -128);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -772,10 +791,10 @@ function generatePrecipitation() {
|
||||||
const band = (Math.abs(lat) - 1) / 5 | 0;
|
const band = (Math.abs(lat) - 1) / 5 | 0;
|
||||||
const latMod = lalitudeModifier[band];
|
const latMod = lalitudeModifier[band];
|
||||||
const tier = Math.abs(lat - 89) / 30 | 0; // 30d tiers from 0 to 5 from N to S
|
const tier = Math.abs(lat - 89) / 30 | 0; // 30d tiers from 0 to 5 from N to S
|
||||||
if (winds[tier] > 40 && winds[tier] < 140) westerly.push([c, latMod, tier]);
|
if (options.winds[tier] > 40 && options.winds[tier] < 140) westerly.push([c, latMod, tier]);
|
||||||
else if (winds[tier] > 220 && winds[tier] < 320) easterly.push([c + cellsX -1, latMod, tier]);
|
else if (options.winds[tier] > 220 && options.winds[tier] < 320) easterly.push([c + cellsX -1, latMod, tier]);
|
||||||
if (winds[tier] > 100 && winds[tier] < 260) northerly++;
|
if (options.winds[tier] > 100 && options.winds[tier] < 260) northerly++;
|
||||||
else if (winds[tier] > 280 || winds[tier] < 80) southerly++;
|
else if (options.winds[tier] > 280 || options.winds[tier] < 80) southerly++;
|
||||||
});
|
});
|
||||||
|
|
||||||
// distribute winds by direction
|
// distribute winds by direction
|
||||||
|
|
@ -1057,7 +1076,7 @@ function reMarkFeatures() {
|
||||||
if (type === "lake") group = defineLakeGroup(start, cellNumber);
|
if (type === "lake") group = defineLakeGroup(start, cellNumber);
|
||||||
else if (type === "ocean") group = "ocean";
|
else if (type === "ocean") group = "ocean";
|
||||||
else if (type === "island") group = defineIslandGroup(start, cellNumber);
|
else if (type === "island") group = defineIslandGroup(start, cellNumber);
|
||||||
features.push({i, land, border, type, cells: cellNumber, firstCell: start, group});
|
features.push({i, land, border, type, cells: cellNumber, firstCell: start, group, ports:0});
|
||||||
queue[0] = cells.f.findIndex(f => !f); // find unmarked cell
|
queue[0] = cells.f.findIndex(f => !f); // find unmarked cell
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1163,88 +1182,11 @@ function rankCells() {
|
||||||
console.timeEnd('rankCells');
|
console.timeEnd('rankCells');
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate army and fleet based on state cells polulation
|
|
||||||
function calculateMilitaryForces() {
|
|
||||||
const cells = pack.cells, states = pack.states;
|
|
||||||
const valid = states.filter(s => s.i && !s.removed); // valid states
|
|
||||||
valid.forEach(s => s.military = {infantry:0, cavalry:0, archers:0, reserve:0, fleet:0});
|
|
||||||
|
|
||||||
for (const i of cells.i) {
|
|
||||||
const s = states[cells.state[i]]; // cell state
|
|
||||||
if (!s.i || s.removed) continue;
|
|
||||||
|
|
||||||
let m = cells.pop[i] / 100; // basic army is 1% of rural population
|
|
||||||
if (cells.culture[i] !== s.culture) m = s.form === "Union" ? m / 1.2 : m / 2; // non-dominant culture
|
|
||||||
if (cells.religion[i] !== cells.religion[s.center]) m = s.form === "Theocracy" ? m / 2.2 : m / 1.4; // non-dominant religion
|
|
||||||
if (cells.f[i] !== cells.f[s.center]) m = s.type === "Naval" ? m / 1.2 : m / 1.8; // different landmass
|
|
||||||
|
|
||||||
let infantry = m * .5; // basic infantry is 50% of army
|
|
||||||
let archers = m * .25; // basic archers is 25% of army
|
|
||||||
let cavalry = m * .25; // basic cavalry is 25% of army
|
|
||||||
|
|
||||||
if ([1, 2, 3, 4].includes(cells.biome[i])) {cavalry *= 3; infantry /= 5; archers /= 2;} else // "nomadic" biomes have lots of cavalry
|
|
||||||
if ([7, 8, 9, 12].includes(cells.biome[i])) {cavalry /= 2.5; infantry *= 1.2; archers *= 1.2;} // "wet" biomes have reduced number of cavalry
|
|
||||||
|
|
||||||
s.military.infantry += infantry;
|
|
||||||
s.military.archers += archers;
|
|
||||||
s.military.cavalry += cavalry;
|
|
||||||
s.military.reserve += m * 3 + cells.pop[i] * .02; // reserve is ~5% of population
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const b of pack.burgs) {
|
|
||||||
if (!b.i || b.removed || !b.state) continue;
|
|
||||||
const s = states[b.state]; // burg state
|
|
||||||
|
|
||||||
let m = b.population / 50; // basic army is 2% of urban population
|
|
||||||
if (b.capital) m *= 2; // capital has household troops
|
|
||||||
if (b.culture !== s.culture) m = s.form === "Union" ? m / 1.2 : m / 2; // non-dominant culture
|
|
||||||
if (cells.religion[b.cell] !== cells.religion[s.center]) m = s.form === "Theocracy" ? m / 2.2 : m / 1.4; // non-dominant religion
|
|
||||||
if (cells.f[b.cell] !== cells.f[s.center]) m = s.type === "Naval" ? m / 1.2 : m / 1.8; // different landmass
|
|
||||||
|
|
||||||
let infantry = m * .6; // basic infantry is 60% of army
|
|
||||||
let archers = m * .3; // basic archers is 3% of army
|
|
||||||
let cavalry = m * .1; // basic cavalry is 10% of army
|
|
||||||
|
|
||||||
const biome = cells.biome[b.cell]; // burg biome
|
|
||||||
if ([1, 2, 3, 4].includes(biome)) {cavalry *= 3; infantry /= 2;} else // "nomadic" biomes have lots of cavalry
|
|
||||||
if ([7, 8, 9, 12].includes(biome)) {cavalry /= 4; infantry *= 1.2; archers *= 1.4;} // "wet" biomes have reduced number of cavalry
|
|
||||||
|
|
||||||
s.military.infantry += infantry;
|
|
||||||
s.military.archers += archers;
|
|
||||||
s.military.cavalry += cavalry;
|
|
||||||
s.military.reserve += m * 2 + b.population * .01; // reserve is ~5% of population
|
|
||||||
|
|
||||||
if (!b.port) continue; // only ports have fleet
|
|
||||||
let ships = b.capital ? b.population / 3 : b.population / 5; // ~1 ship per 5 population points
|
|
||||||
if (s.type === "Naval") ships *= 1.8; // "naval" states have more ships
|
|
||||||
s.military.fleet += ~~ships + +P(ships % 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const expn = d3.sum(valid.map(s => s.expansionism)); // total expansion
|
|
||||||
const area = d3.sum(valid.map(s => s.area)); // total area
|
|
||||||
const rate = {x:0, Ally:-.2, Friendly:-.1, Neutral:0, Suspicion:.1, Enemy:1, Unknown:0, Rival:.5, Vassal:.5, Suzerain:-.5};
|
|
||||||
|
|
||||||
valid.forEach(s => {
|
|
||||||
const m = s.military, d = s.diplomacy;
|
|
||||||
const expansionRate = Math.min(Math.max((s.expansionism / expn) / (s.area / area), .25), 4); // how much state expansionism is relized
|
|
||||||
const diplomacyRate = d.some(d => d === "Enemy") ? 1 : d.some(d => d === "Rival") ? .8 : d.some(d => d === "Suspicion") ? .5 : .1; // peacefulness
|
|
||||||
const neighborsRate = Math.min(Math.max(s.neighbors.map(n => n ? pack.states[n].diplomacy[s.i] : "Suspicion").reduce((s, r) => s += rate[r], .5), .3), 3); // neighbors rate
|
|
||||||
m.alert = rn(expansionRate * diplomacyRate * neighborsRate, 2); // war alert rate (army modifier)
|
|
||||||
|
|
||||||
m.infantry = rn(m.infantry * m.alert, 3);
|
|
||||||
m.cavalry = rn(m.cavalry * m.alert, 3);
|
|
||||||
m.archers = rn(m.archers * m.alert, 3);
|
|
||||||
m.reserve = rn(m.reserve, 3);
|
|
||||||
});
|
|
||||||
|
|
||||||
console.table(valid.map(s=>[s.name, s.military.alert, s.military.infantry, s.military.archers, s.military.cavalry, s.military.reserve, rn(s.military.reserve/(s.urban+s.rural)*100,2)+"%", s.military.fleet]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate some markers
|
// generate some markers
|
||||||
function addMarkers(number = 1) {
|
function addMarkers(number = 1) {
|
||||||
if (!number) return;
|
if (!number) return;
|
||||||
console.time("addMarkers");
|
console.time("addMarkers");
|
||||||
const cells = pack.cells;
|
const cells = pack.cells, states = pack.states;
|
||||||
|
|
||||||
void function addVolcanoes() {
|
void function addVolcanoes() {
|
||||||
let mounts = Array.from(cells.i).filter(i => cells.h[i] > 70).sort((a, b) => cells.h[b] - cells.h[a]);
|
let mounts = Array.from(cells.i).filter(i => cells.h[i] > 70).sort((a, b) => cells.h[b] - cells.h[a]);
|
||||||
|
|
@ -1306,7 +1248,7 @@ function addMarkers(number = 1) {
|
||||||
.attr("data-size", 1).attr("width", 30).attr("height", 30);
|
.attr("data-size", 1).attr("width", 30).attr("height", 30);
|
||||||
const resource = rw(resources);
|
const resource = rw(resources);
|
||||||
const burg = pack.burgs[cells.burg[cell]];
|
const burg = pack.burgs[cells.burg[cell]];
|
||||||
const name = `${burg.name} - ${resource} mining town`;
|
const name = `${burg.name} — ${resource} mining town`;
|
||||||
const population = rn(burg.population * populationRate.value * urbanization.value);
|
const population = rn(burg.population * populationRate.value * urbanization.value);
|
||||||
const legend = `${burg.name} is a mining town of ${population} people just nearby the ${resource} mine`;
|
const legend = `${burg.name} is a mining town of ${population} people just nearby the ${resource} mine`;
|
||||||
notes.push({id, name, legend});
|
notes.push({id, name, legend});
|
||||||
|
|
@ -1412,9 +1354,8 @@ function addMarkers(number = 1) {
|
||||||
}()
|
}()
|
||||||
|
|
||||||
void function addBattlefields() {
|
void function addBattlefields() {
|
||||||
let battlefields = Array.from(cells.i).filter(i => cells.pop[i] > 2 && cells.h[i] < 50 && cells.h[i] > 25);
|
let battlefields = Array.from(cells.i).filter(i => cells.state[i] && cells.pop[i] > 2 && cells.h[i] < 50 && cells.h[i] > 25);
|
||||||
let count = battlefields.length < 100 ? 0 : Math.ceil(battlefields.length / 500 * number);
|
let count = battlefields.length < 100 ? 0 : Math.ceil(battlefields.length / 500 * number);
|
||||||
const era = Names.getCulture(0, 3, 7, "", 0) + " Era";
|
|
||||||
if (count) addMarker("battlefield", "⚔", 50, 50, 20);
|
if (count) addMarker("battlefield", "⚔", 50, 50, 20);
|
||||||
|
|
||||||
while (count) {
|
while (count) {
|
||||||
|
|
@ -1426,9 +1367,11 @@ function addMarkers(number = 1) {
|
||||||
.attr("data-x", x).attr("data-y", y).attr("x", x - 15).attr("y", y - 30)
|
.attr("data-x", x).attr("data-y", y).attr("x", x - 15).attr("y", y - 30)
|
||||||
.attr("data-size", 1).attr("width", 30).attr("height", 30);
|
.attr("data-size", 1).attr("width", 30).attr("height", 30);
|
||||||
|
|
||||||
|
const campaign = ra(states[cells.state[cell]].campaigns);
|
||||||
|
const date = generateDate(campaign.start, campaign.end);
|
||||||
const name = Names.getCulture(cells.culture[cell]) + " Battlefield";
|
const name = Names.getCulture(cells.culture[cell]) + " Battlefield";
|
||||||
const date = new Date(rand(100, 1000),rand(12),rand(31)).toLocaleDateString("en", {year:'numeric', month:'long', day:'numeric'}) + " " + era;
|
const legend = `A historical battle of the ${campaign.name}. \r\nDate: ${date} ${options.era}`;
|
||||||
notes.push({id, name, legend:`A historical battlefield spot. \r\nDate: ${date}`});
|
notes.push({id, name, legend});
|
||||||
count--;
|
count--;
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
@ -1800,7 +1743,7 @@ const regenerateMap = debounce(function() {
|
||||||
|
|
||||||
// clear the map
|
// clear the map
|
||||||
function undraw() {
|
function undraw() {
|
||||||
viewbox.selectAll("path, circle, polygon, line, text, use, #zones > g, #ruler > g").remove();
|
viewbox.selectAll("path, circle, polygon, line, text, use, #zones > g, #armies > g, #ruler > g").remove();
|
||||||
defs.selectAll("path, clipPath").remove();
|
defs.selectAll("path, clipPath").remove();
|
||||||
notes = [];
|
notes = [];
|
||||||
unfog();
|
unfog();
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@
|
||||||
collectStatistics();
|
collectStatistics();
|
||||||
assignColors();
|
assignColors();
|
||||||
|
|
||||||
|
generateCampaigns();
|
||||||
generateDiplomacy();
|
generateDiplomacy();
|
||||||
Routes.draw(capitalRoutes, townRoutes, oceanRoutes);
|
Routes.draw(capitalRoutes, townRoutes, oceanRoutes);
|
||||||
drawBurgs();
|
drawBurgs();
|
||||||
|
|
@ -145,15 +146,20 @@
|
||||||
if (!b.i) continue;
|
if (!b.i) continue;
|
||||||
const i = b.cell;
|
const i = b.cell;
|
||||||
|
|
||||||
// asign port status: capital with any harbor and towns with good harbors
|
// asign port status
|
||||||
const port = (b.capital && cells.harbor[i]) || cells.harbor[i] === 1;
|
if (cells.haven[i]) {
|
||||||
b.port = port ? cells.f[cells.haven[i]] : 0; // port is defined by feature id it lays on
|
const f = cells.f[cells.haven[i]]; // water body id
|
||||||
|
// port is a capital with any harbor OR town with good harbor
|
||||||
|
const port = pack.features[f].cells > 1 && ((b.capital && cells.harbor[i]) || cells.harbor[i] === 1);
|
||||||
|
b.port = port ? f : 0; // port is defined by water body id it lays on
|
||||||
|
if (port) {pack.features[f].ports += 1; pack.features[b.feature].ports += 1;}
|
||||||
|
} else b.port = 0;
|
||||||
|
|
||||||
// define burg population (keep urbanization at about 10% rate)
|
// define burg population (keep urbanization at about 10% rate)
|
||||||
b.population = rn(Math.max((cells.s[i] + cells.road[i]) / 8 + b.i / 1000 + i % 100 / 1000, .1), 3);
|
b.population = rn(Math.max((cells.s[i] + cells.road[i]) / 8 + b.i / 1000 + i % 100 / 1000, .1), 3);
|
||||||
if (b.capital) b.population = rn(b.population * 1.3, 3); // increase capital population
|
if (b.capital) b.population = rn(b.population * 1.3, 3); // increase capital population
|
||||||
|
|
||||||
if (port) {
|
if (b.port) {
|
||||||
b.population = b.population * 1.3; // increase port population
|
b.population = b.population * 1.3; // increase port population
|
||||||
const e = cells.v[i].filter(v => vertices.c[v].some(c => c === cells.haven[i])); // vertices of common edge
|
const e = cells.v[i].filter(v => vertices.c[v].some(c => c === cells.haven[i])); // vertices of common edge
|
||||||
b.x = rn((vertices.p[e[0]][0] + vertices.p[e[1]][0]) / 2, 2);
|
b.x = rn((vertices.p[e[0]][0] + vertices.p[e[1]][0]) / 2, 2);
|
||||||
|
|
@ -164,7 +170,7 @@
|
||||||
b.population = rn(b.population * gauss(2,3,.6,20,3), 3);
|
b.population = rn(b.population * gauss(2,3,.6,20,3), 3);
|
||||||
|
|
||||||
// shift burgs on rivers semi-randomly and just a bit
|
// shift burgs on rivers semi-randomly and just a bit
|
||||||
if (!port && cells.r[i]) {
|
if (!b.port && cells.r[i]) {
|
||||||
const shift = Math.min(cells.fl[i]/150, 1);
|
const shift = Math.min(cells.fl[i]/150, 1);
|
||||||
if (i%2) b.x = rn(b.x + shift, 2); else b.x = rn(b.x - shift, 2);
|
if (i%2) b.x = rn(b.x + shift, 2); else b.x = rn(b.x - shift, 2);
|
||||||
if (cells.r[i]%2) b.y = rn(b.y + shift, 2); else b.y = rn(b.y - shift, 2);
|
if (cells.r[i]%2) b.y = rn(b.y + shift, 2); else b.y = rn(b.y - shift, 2);
|
||||||
|
|
@ -173,11 +179,11 @@
|
||||||
|
|
||||||
// de-assign port status if it's the only one on feature
|
// de-assign port status if it's the only one on feature
|
||||||
for (const f of pack.features) {
|
for (const f of pack.features) {
|
||||||
if (!f.i || f.land) continue;
|
if (!f.i || f.land || f.ports !== 1) continue;
|
||||||
const onFeature = pack.burgs.filter(b => b.port === f.i);
|
const port = pack.burgs.find(b => b.port === f.i);
|
||||||
if (onFeature.length === 1) {
|
port.port = 0;
|
||||||
onFeature[0].port = 0;
|
f.port = 0;
|
||||||
}
|
pack.features[port.feature].ports -= 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.timeEnd("specifyBurgs");
|
console.timeEnd("specifyBurgs");
|
||||||
|
|
@ -589,6 +595,20 @@
|
||||||
console.timeEnd("assignColors");
|
console.timeEnd("assignColors");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// generate historical wars
|
||||||
|
const generateCampaigns = function() {
|
||||||
|
const wars = {"War":4, "Conflict":2, "Campaign":4, "Invasion":2, "Rebellion":2, "Conquest":2, "Intervention":1, "Expedition":1, "Crusade":1};
|
||||||
|
|
||||||
|
pack.states.forEach(s => {
|
||||||
|
if (!s.i || s.removed) return;
|
||||||
|
s.campaigns = (s.neighbors||[0]).map(i => {
|
||||||
|
const name = i && P(.8) ? pack.states[i].name : Names.getCultureShort(s.culture);
|
||||||
|
const start = gauss(options.year-100, 150, 1, options.year-6), end = start + gauss(4, 5, 1, options.year - start - 1);
|
||||||
|
return {name:getAdjective(name) + " " + rw(wars), start, end};
|
||||||
|
}).sort((a, b) => a.start - b.start);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// generate Diplomatic Relationships
|
// generate Diplomatic Relationships
|
||||||
const generateDiplomacy = function() {
|
const generateDiplomacy = function() {
|
||||||
console.time("generateDiplomacy");
|
console.time("generateDiplomacy");
|
||||||
|
|
@ -666,6 +686,9 @@
|
||||||
|
|
||||||
// start a war
|
// start a war
|
||||||
const war = [`${an}-${trimVowels(dn)}ian War`,`${an} declared a war on its rival ${dn}`];
|
const war = [`${an}-${trimVowels(dn)}ian War`,`${an} declared a war on its rival ${dn}`];
|
||||||
|
const start = options.year - gauss(2, 2, 0, 5);
|
||||||
|
states[attacker].campaigns.push({name: `${trimVowels(dn)}ian War`, start, end:options.year});
|
||||||
|
states[defender].campaigns.push({name: `${trimVowels(an)}ian War`, start, end:options.year});
|
||||||
|
|
||||||
// attacker vassals join the war
|
// attacker vassals join the war
|
||||||
ad.forEach((r, d) => {if (r === "Suzerain") {
|
ad.forEach((r, d) => {if (r === "Suzerain") {
|
||||||
|
|
@ -997,6 +1020,6 @@
|
||||||
|
|
||||||
return {generate, expandStates, normalizeStates, assignColors,
|
return {generate, expandStates, normalizeStates, assignColors,
|
||||||
drawBurgs, specifyBurgs, defineBurgFeatures, drawStateLabels, collectStatistics,
|
drawBurgs, specifyBurgs, defineBurgFeatures, drawStateLabels, collectStatistics,
|
||||||
generateDiplomacy, defineStateForms, getFullName, generateProvinces};
|
generateCampaigns, generateDiplomacy, defineStateForms, getFullName, generateProvinces};
|
||||||
|
|
||||||
})));
|
})));
|
||||||
|
|
|
||||||
241
modules/military-generator.js
Normal file
241
modules/military-generator.js
Normal file
|
|
@ -0,0 +1,241 @@
|
||||||
|
(function (global, factory) {
|
||||||
|
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||||
|
typeof define === 'function' && define.amd ? define(factory) :
|
||||||
|
(global.Military = factory());
|
||||||
|
}(this, (function () {'use strict';
|
||||||
|
|
||||||
|
let cells, p, states;
|
||||||
|
|
||||||
|
const generate = function() {
|
||||||
|
console.time("calculateMilitaryForces");
|
||||||
|
cells = pack.cells, p = cells.p, states = pack.states;
|
||||||
|
const valid = states.filter(s => s.i && !s.removed); // valid states
|
||||||
|
|
||||||
|
const expn = d3.sum(valid.map(s => s.expansionism)); // total expansion
|
||||||
|
const area = d3.sum(valid.map(s => s.area)); // total area
|
||||||
|
const rate = {x:0, Ally:-.2, Friendly:-.1, Neutral:0, Suspicion:.1, Enemy:1, Unknown:0, Rival:.5, Vassal:.5, Suzerain:-.5};
|
||||||
|
|
||||||
|
valid.forEach(s => {
|
||||||
|
const temp = s.temp = {}, d = s.diplomacy;
|
||||||
|
const expansionRate = Math.min(Math.max((s.expansionism / expn) / (s.area / area), .25), 4); // how much state expansionism is realized
|
||||||
|
const diplomacyRate = d.some(d => d === "Enemy") ? 1 : d.some(d => d === "Rival") ? .8 : d.some(d => d === "Suspicion") ? .5 : .1; // peacefulness
|
||||||
|
const neighborsRate = Math.min(Math.max(s.neighbors.map(n => n ? pack.states[n].diplomacy[s.i] : "Suspicion").reduce((s, r) => s += rate[r], .5), .3), 3); // neighbors rate
|
||||||
|
s.alert = rn(expansionRate * diplomacyRate * neighborsRate, 2); // war alert rate (army modifier)
|
||||||
|
temp.platoons = [];
|
||||||
|
|
||||||
|
// apply overall state modifiers for unit types based on state features
|
||||||
|
for (const unit of options.military) {
|
||||||
|
let modifier = 1;
|
||||||
|
|
||||||
|
if (unit.type === "mounted") {
|
||||||
|
if (s.type === "Naval") modifier /= 1.4;
|
||||||
|
if (s.form === "Horde") modifier *= 2;
|
||||||
|
} else if (unit.type === "ranged") {
|
||||||
|
if (s.type === "Hunting") modifier *= 1.4;
|
||||||
|
} else if (unit.type === "naval") {
|
||||||
|
if (s.type === "Naval") modifier *= 2; else
|
||||||
|
if (s.type === "River") modifier *= 1.2; else
|
||||||
|
if (s.type === "Nomadic") modifier /= 1.4;
|
||||||
|
if (s.form === "Republic") modifier *= 1.2;
|
||||||
|
}
|
||||||
|
temp[unit.name] = modifier * s.alert;
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
const portsMod = d3.max(pack.features.map(f => f.land ? 0 : f.ports)) * .75;
|
||||||
|
const normalizeNaval = ports => normalize(ports, 0, portsMod);
|
||||||
|
|
||||||
|
for (const i of cells.i) {
|
||||||
|
if (!cells.pop[i]) continue;
|
||||||
|
const s = states[cells.state[i]]; // cell state
|
||||||
|
if (!s.i || s.removed) continue;
|
||||||
|
|
||||||
|
let m = cells.pop[i] / 100; // basic rural army in percentages
|
||||||
|
if (cells.culture[i] !== s.culture) m = s.form === "Union" ? m / 1.2 : m / 2; // non-dominant culture
|
||||||
|
if (cells.religion[i] !== cells.religion[s.center]) m = s.form === "Theocracy" ? m / 2.2 : m / 1.4; // non-dominant religion
|
||||||
|
if (cells.f[i] !== cells.f[s.center]) m = s.type === "Naval" ? m / 1.2 : m / 1.8; // different landmass
|
||||||
|
|
||||||
|
const nomadic = [1, 2, 3, 4].includes(cells.biome[i]);
|
||||||
|
const wetland = [7, 8, 9, 12].includes(cells.biome[i]);
|
||||||
|
const highland = cells.h[i] >= 70;
|
||||||
|
|
||||||
|
for (const u of options.military) {
|
||||||
|
const perc = +u.rural;
|
||||||
|
if (isNaN(perc) || perc <= 0) continue;
|
||||||
|
|
||||||
|
let army = m * perc; // basic army for rural cell
|
||||||
|
if (nomadic) { // "nomadic" biomes special rules
|
||||||
|
if (u.type === "melee") army /= 5; else
|
||||||
|
if (u.type === "ranged") army /= 2; else
|
||||||
|
if (u.type === "mounted") army *= 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wetland) { // "wet" biomes special rules
|
||||||
|
if (u.type === "melee") army *= 1.2; else
|
||||||
|
if (u.type === "ranged") army *= 1.4; else
|
||||||
|
if (u.type === "mounted") army /= 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (highland) { // highlands special rules
|
||||||
|
if (u.type === "ranged") army *= 2; else
|
||||||
|
if (u.type === "mounted") army /= 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
const t = rn(army * s.temp[u.name] * populationRate.value);
|
||||||
|
if (!t) continue;
|
||||||
|
let x = p[i][0], y = p[i][1], n = 0;
|
||||||
|
if (u.type === "naval") {let haven = cells.haven[i]; x = p[haven][0], y = p[haven][1]; n = 1}; // place naval to sea
|
||||||
|
s.temp.platoons.push({cell: i, a:t, t, x, y, u:u.name, n, s:u.separate});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const b of pack.burgs) {
|
||||||
|
if (!b.i || b.removed || !b.state || !b.population) continue;
|
||||||
|
const s = states[b.state]; // burg state
|
||||||
|
|
||||||
|
let m = b.population * urbanization.value / 100; // basic urban army in percentages
|
||||||
|
if (b.capital) m *= 1.2; // capital has household troops
|
||||||
|
if (b.culture !== s.culture) m = s.form === "Union" ? m / 1.2 : m / 2; // non-dominant culture
|
||||||
|
if (cells.religion[b.cell] !== cells.religion[s.center]) m = s.form === "Theocracy" ? m / 2.2 : m / 1.4; // non-dominant religion
|
||||||
|
if (cells.f[b.cell] !== cells.f[s.center]) m = s.type === "Naval" ? m / 1.2 : m / 1.8; // different landmass
|
||||||
|
|
||||||
|
const biome = cells.biome[b.cell]; // burg biome
|
||||||
|
const nomadic = [1, 2, 3, 4].includes(biome);
|
||||||
|
const wetland = [7, 8, 9, 12].includes(biome);
|
||||||
|
const highland = cells.h[b.cell] >= 70;
|
||||||
|
|
||||||
|
for (const u of options.military) {
|
||||||
|
const perc = +u.urban;
|
||||||
|
if (isNaN(perc) || perc <= 0) continue;
|
||||||
|
let army = m * perc; // basic army for rural cell
|
||||||
|
|
||||||
|
if (u.type === "naval") {
|
||||||
|
if (!b.port) continue; // only ports have naval units
|
||||||
|
army *= normalizeNaval(pack.features[b.port].ports);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nomadic) { // "nomadic" biomes special rules
|
||||||
|
if (u.type === "melee") army /= 3; else
|
||||||
|
if (u.type === "machinery") army /= 2; else
|
||||||
|
if (u.type === "mounted") army *= 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wetland) { // "wet" biomes special rules
|
||||||
|
if (u.type === "melee") army *= 1.2; else
|
||||||
|
if (u.type === "ranged") army *= 1.4; else
|
||||||
|
if (u.type === "machinery") army *= 1.2; else
|
||||||
|
if (u.type === "mounted") army /= 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (highland) { // highlands special rules
|
||||||
|
if (u.type === "ranged") army *= 2; else
|
||||||
|
if (u.type === "naval") army /= 3; else
|
||||||
|
if (u.type === "mounted") army /= 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
const t = rn(army * s.temp[u.name] * populationRate.value);
|
||||||
|
if (!t) continue;
|
||||||
|
let x = p[b.cell][0], y = p[b.cell][1], n = 0;
|
||||||
|
if (u.type === "naval") {let haven = cells.haven[b.cell]; x = p[haven][0], y = p[haven][1]; n = 1}; // place naval to sea
|
||||||
|
s.temp.platoons.push({cell: b.cell, a:t, t, x, y, u:u.name, n, s:u.separate});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const expected = 3 * populationRate.value; // expected regiment size
|
||||||
|
const mergeable = (n, s) => (!n.s && !s.s) || n.u === s.u;
|
||||||
|
// get regiments for each state
|
||||||
|
valid.forEach(s => {
|
||||||
|
s.military = createRegiments(s.temp.platoons, s);
|
||||||
|
delete s.temp; // do not store temp data
|
||||||
|
drawRegiments(s.military, s.i, s.color);
|
||||||
|
});
|
||||||
|
|
||||||
|
function createRegiments(nodes, s) {
|
||||||
|
nodes.sort((a,b) => a.a - b.a);
|
||||||
|
const tree = d3.quadtree(nodes, d => d.x, d => d.y);
|
||||||
|
nodes.forEach(n => {
|
||||||
|
tree.remove(n);
|
||||||
|
const overlap = tree.find(n.x, n.y, 20);
|
||||||
|
if (overlap && overlap.t && mergeable(n, overlap)) {merge(n, overlap); return;}
|
||||||
|
if (n.t > expected) return;
|
||||||
|
const r = (expected - n.t) / (n.s?40:20); // search radius
|
||||||
|
const candidates = tree.findAll(n.x, n.y, r);
|
||||||
|
for (const c of candidates) {
|
||||||
|
if (c.t < expected && mergeable(n, c)) {merge(n, c); break;}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// add n0 to n1's ultimate parent
|
||||||
|
function merge(n0, n1) {
|
||||||
|
if (!n1.childen) n1.childen = [n0]; else n1.childen.push(n0);
|
||||||
|
if (n0.childen) n0.childen.forEach(n => n1.childen.push(n));
|
||||||
|
n1.t += n0.t;
|
||||||
|
n0.t = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse regiments data to easy-readable json
|
||||||
|
const regiments = nodes.filter(n => n.t).sort((a,b) => b.t - a.t).map((r, i) => {
|
||||||
|
const u = {}; u[r.u] = r.a;
|
||||||
|
(r.childen||[]).forEach(n => u[n.u] = u[n.u] ? u[n.u] += n.a : n.a);
|
||||||
|
return {i, a:r.t, cell:r.cell, x:r.x, y:r.y, u, n:r.n, name};
|
||||||
|
});
|
||||||
|
|
||||||
|
// generate name for regiments
|
||||||
|
regiments.forEach(r => {
|
||||||
|
r.name = getName(r, regiments);
|
||||||
|
generateNote(r, s);
|
||||||
|
});
|
||||||
|
|
||||||
|
return regiments;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.timeEnd("calculateMilitaryForces");
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawRegiments(regiments, s, color) {
|
||||||
|
const size = 3;
|
||||||
|
const army = armies.append("g").attr("id", "army"+s).attr("fill", color);
|
||||||
|
const g = army.selectAll("g").data(regiments).enter().append("g").attr("id", d => "regiment"+s+"-"+d.i);
|
||||||
|
g.append("rect").attr("data-name", d => d.name).attr("data-state", s).attr("data-id", d => d.i)
|
||||||
|
.attr("x", d => d.n ? d.x-size*2 : d.x-size*3).attr("y", d => d.y-size)
|
||||||
|
.attr("width", d => d.n ? size*4 : size*6).attr("height", size*2);
|
||||||
|
g.append("text").attr("x", d => d.x).attr("y", d => d.y).text(d => d.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
const drawRegiment = function(reg, s, x = reg.x, y = reg.y) {
|
||||||
|
const size = 3;
|
||||||
|
|
||||||
|
const g = armies.select("g#army"+s).append("g").attr("id", "regiment"+s+"-"+reg.i);
|
||||||
|
g.append("rect").attr("data-name", reg.name).attr("data-state", s).attr("data-id", reg.i)
|
||||||
|
.attr("x", reg.n ? x-size*2 : x-size*3).attr("y", y-size)
|
||||||
|
.attr("width", reg.n ? size*4 : size*6).attr("height", size*2);
|
||||||
|
g.append("text").attr("x", x).attr("y", y).text(reg.a);
|
||||||
|
}
|
||||||
|
|
||||||
|
const getName = function(r, regiments) {
|
||||||
|
const proper = r.n ? null :
|
||||||
|
cells.province[r.cell] ? pack.provinces[cells.province[r.cell]].name :
|
||||||
|
cells.burg[r.cell] ? pack.burgs[cells.burg[r.cell]].name : null
|
||||||
|
const number = nth(regiments.filter(reg => reg.n === r.n && reg.i < r.i).length+1);
|
||||||
|
const form = r.n ? "Fleet" : "Regiment";
|
||||||
|
return `${number}${proper?` (${proper}) `:` `}${form}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
const generateNote = function(r, s) {
|
||||||
|
const base = cells.burg[r.cell] ? pack.burgs[cells.burg[r.cell]].name :
|
||||||
|
cells.province[r.cell] ? pack.provinces[cells.province[r.cell]].fullName : null;
|
||||||
|
const station = base ? `${r.name} is ${r.n ? "based" : "stationed"} in ${base}. ` : null;
|
||||||
|
|
||||||
|
const composition = Object.keys(r.u).map(t => ` — ${t}: ${r.u[t]}`).join("\r\n");
|
||||||
|
const troops = `\r\n\r\nRegiment composition:\r\n${composition}.`;
|
||||||
|
|
||||||
|
const campaign = ra(s.campaigns);
|
||||||
|
const year = rand(campaign.start, campaign.end);
|
||||||
|
const legend = `Regiment was formed in ${year} ${options.era} during the ${campaign.name}. ${station}${troops}`;
|
||||||
|
notes.push({id:`regiment${s.i}-${r.i}`, name:r.name, legend});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {generate, getName, generateNote, drawRegiment};
|
||||||
|
|
||||||
|
})));
|
||||||
|
|
@ -117,8 +117,9 @@
|
||||||
const increment = rn(.8 + Math.random() * .6, 1); // river bed widening modifier
|
const increment = rn(.8 + Math.random() * .6, 1); // river bed widening modifier
|
||||||
const [path, length] = getPath(riverEnhanced, width, increment);
|
const [path, length] = getPath(riverEnhanced, width, increment);
|
||||||
riverPaths.push([r, path, width, increment]);
|
riverPaths.push([r, path, width, increment]);
|
||||||
const parent = riverSegments[0].parent || 0;
|
const source = riverSegments[0], mouth = riverSegments[riverSegments.length-2];
|
||||||
pack.rivers.push({i:r, parent, length, source:riverSegments[0].cell, mouth:last(riverSegments).cell});
|
const parent = source.parent || 0;
|
||||||
|
pack.rivers.push({i:r, parent, length, source:source.cell, mouth:mouth.cell});
|
||||||
} else {
|
} else {
|
||||||
// remove too short rivers
|
// remove too short rivers
|
||||||
riverSegments.filter(s => cells.r[s.cell] === r).forEach(s => cells.r[s.cell] = 0);
|
riverSegments.filter(s => cells.r[s.cell] === r).forEach(s => cells.r[s.cell] = 0);
|
||||||
|
|
@ -258,6 +259,7 @@
|
||||||
for (const r of pack.rivers) {
|
for (const r of pack.rivers) {
|
||||||
r.basin = getBasin(r.i, r.parent);
|
r.basin = getBasin(r.i, r.parent);
|
||||||
r.name = getName(r.mouth);
|
r.name = getName(r.mouth);
|
||||||
|
//debug.append("circle").attr("cx", pack.cells.p[r.mouth][0]).attr("cy", pack.cells.p[r.mouth][1]).attr("r", 2);
|
||||||
const small = r.length < smallLength;
|
const small = r.length < smallLength;
|
||||||
r.type = r.parent && !(r.i%6) ? small ? "Branch" : "Fork" : small ? rw(smallType) : "River";
|
r.type = r.parent && !(r.i%6) ? small ? "Branch" : "Fork" : small ? rw(smallType) : "River";
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -226,12 +226,12 @@ function getMapData() {
|
||||||
const dateString = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
|
const dateString = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
|
||||||
const license = "File can be loaded in azgaar.github.io/Fantasy-Map-Generator";
|
const license = "File can be loaded in azgaar.github.io/Fantasy-Map-Generator";
|
||||||
const params = [version, license, dateString, seed, graphWidth, graphHeight].join("|");
|
const params = [version, license, dateString, seed, graphWidth, graphHeight].join("|");
|
||||||
const options = [distanceUnitInput.value, distanceScaleInput.value, areaUnit.value,
|
const settings = [distanceUnitInput.value, distanceScaleInput.value, areaUnit.value,
|
||||||
heightUnit.value, heightExponentInput.value, temperatureScale.value,
|
heightUnit.value, heightExponentInput.value, temperatureScale.value,
|
||||||
barSize.value, barLabel.value, barBackOpacity.value, barBackColor.value,
|
barSize.value, barLabel.value, barBackOpacity.value, barBackColor.value,
|
||||||
barPosX.value, barPosY.value, populationRate.value, urbanization.value,
|
barPosX.value, barPosY.value, populationRate.value, urbanization.value,
|
||||||
mapSizeOutput.value, latitudeOutput.value, temperatureEquatorOutput.value,
|
mapSizeOutput.value, latitudeOutput.value, temperatureEquatorOutput.value,
|
||||||
temperaturePoleOutput.value, precOutput.value, JSON.stringify(winds),
|
temperaturePoleOutput.value, precOutput.value, JSON.stringify(options.winds),
|
||||||
mapName.value].join("|");
|
mapName.value].join("|");
|
||||||
const coords = JSON.stringify(mapCoordinates);
|
const coords = JSON.stringify(mapCoordinates);
|
||||||
const biomes = [biomesData.color, biomesData.habitability, biomesData.name].join("|");
|
const biomes = [biomesData.color, biomesData.habitability, biomesData.name].join("|");
|
||||||
|
|
@ -265,7 +265,7 @@ function getMapData() {
|
||||||
const pop = Array.from(pack.cells.pop).map(p => rn(p, 4));
|
const pop = Array.from(pack.cells.pop).map(p => rn(p, 4));
|
||||||
|
|
||||||
// data format as below
|
// data format as below
|
||||||
const data = [params, options, coords, biomes, notesData, svg_xml,
|
const data = [params, settings, coords, biomes, notesData, svg_xml,
|
||||||
gridGeneral, grid.cells.h, grid.cells.prec, grid.cells.f, grid.cells.t, grid.cells.temp,
|
gridGeneral, grid.cells.h, grid.cells.prec, grid.cells.f, grid.cells.t, grid.cells.temp,
|
||||||
features, cultures, states, burgs,
|
features, cultures, states, burgs,
|
||||||
pack.cells.biome, pack.cells.burg, pack.cells.conf, pack.cells.culture, pack.cells.fl,
|
pack.cells.biome, pack.cells.burg, pack.cells.conf, pack.cells.culture, pack.cells.fl,
|
||||||
|
|
@ -554,29 +554,29 @@ function parseLoadedData(data) {
|
||||||
|
|
||||||
console.group("Loaded Map " + seed);
|
console.group("Loaded Map " + seed);
|
||||||
|
|
||||||
void function parseOptions() {
|
void function parseSettings() {
|
||||||
const options = data[1].split("|");
|
const settings = data[1].split("|");
|
||||||
if (options[0]) applyOption(distanceUnitInput, options[0]);
|
if (settings[0]) applyOption(distanceUnitInput, settings[0]);
|
||||||
if (options[1]) distanceScaleInput.value = distanceScaleOutput.value = options[1];
|
if (settings[1]) distanceScaleInput.value = distanceScaleOutput.value = settings[1];
|
||||||
if (options[2]) areaUnit.value = options[2];
|
if (settings[2]) areaUnit.value = settings[2];
|
||||||
if (options[3]) applyOption(heightUnit, options[3]);
|
if (settings[3]) applyOption(heightUnit, settings[3]);
|
||||||
if (options[4]) heightExponentInput.value = heightExponentOutput.value = options[4];
|
if (settings[4]) heightExponentInput.value = heightExponentOutput.value = settings[4];
|
||||||
if (options[5]) temperatureScale.value = options[5];
|
if (settings[5]) temperatureScale.value = settings[5];
|
||||||
if (options[6]) barSize.value = barSizeOutput.value = options[6];
|
if (settings[6]) barSize.value = barSizeOutput.value = settings[6];
|
||||||
if (options[7] !== undefined) barLabel.value = options[7];
|
if (settings[7] !== undefined) barLabel.value = settings[7];
|
||||||
if (options[8] !== undefined) barBackOpacity.value = options[8];
|
if (settings[8] !== undefined) barBackOpacity.value = settings[8];
|
||||||
if (options[9]) barBackColor.value = options[9];
|
if (settings[9]) barBackColor.value = settings[9];
|
||||||
if (options[10]) barPosX.value = options[10];
|
if (settings[10]) barPosX.value = settings[10];
|
||||||
if (options[11]) barPosY.value = options[11];
|
if (settings[11]) barPosY.value = settings[11];
|
||||||
if (options[12]) populationRate.value = populationRateOutput.value = options[12];
|
if (settings[12]) populationRate.value = populationRateOutput.value = settings[12];
|
||||||
if (options[13]) urbanization.value = urbanizationOutput.value = options[13];
|
if (settings[13]) urbanization.value = urbanizationOutput.value = settings[13];
|
||||||
if (options[14]) mapSizeInput.value = mapSizeOutput.value = Math.max(Math.min(options[14], 100), 1);
|
if (settings[14]) mapSizeInput.value = mapSizeOutput.value = Math.max(Math.min(settings[14], 100), 1);
|
||||||
if (options[15]) latitudeInput.value = latitudeOutput.value = Math.max(Math.min(options[15], 100), 0);
|
if (settings[15]) latitudeInput.value = latitudeOutput.value = Math.max(Math.min(settings[15], 100), 0);
|
||||||
if (options[16]) temperatureEquatorInput.value = temperatureEquatorOutput.value = options[16];
|
if (settings[16]) temperatureEquatorInput.value = temperatureEquatorOutput.value = settings[16];
|
||||||
if (options[17]) temperaturePoleInput.value = temperaturePoleOutput.value = options[17];
|
if (settings[17]) temperaturePoleInput.value = temperaturePoleOutput.value = settings[17];
|
||||||
if (options[18]) precInput.value = precOutput.value = options[18];
|
if (settings[18]) precInput.value = precOutput.value = settings[18];
|
||||||
if (options[19]) winds = JSON.parse(options[19]);
|
if (settings[19]) options.winds = JSON.parse(settings[19]);
|
||||||
if (options[20]) mapName.value = options[20];
|
if (settings[20]) mapName.value = settings[20];
|
||||||
}()
|
}()
|
||||||
|
|
||||||
void function parseConfiguration() {
|
void function parseConfiguration() {
|
||||||
|
|
@ -931,6 +931,14 @@ function parseLoadedData(data) {
|
||||||
BurgsAndStates.collectStatistics();
|
BurgsAndStates.collectStatistics();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (version < 1.3) {
|
||||||
|
// v 1.3 added ports attribute to pack.features
|
||||||
|
for (const f of pack.features) {
|
||||||
|
if (!f.i) continue;
|
||||||
|
f.ports = pack.burgs.filter(b => !b.removed && b.port === f.i).length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
changeMapSize();
|
changeMapSize();
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ function restoreDefaultEvents() {
|
||||||
function clicked() {
|
function clicked() {
|
||||||
const el = d3.event.target;
|
const el = d3.event.target;
|
||||||
if (!el || !el.parentElement || !el.parentElement.parentElement) return;
|
if (!el || !el.parentElement || !el.parentElement.parentElement) return;
|
||||||
const parent = el.parentElement, grand = parent.parentElement;
|
const parent = el.parentElement, grand = parent.parentElement, great = grand.parentElement;
|
||||||
const p = d3.mouse(this);
|
const p = d3.mouse(this);
|
||||||
const i = findCell(p[0], p[1]);
|
const i = findCell(p[0], p[1]);
|
||||||
|
|
||||||
|
|
@ -27,8 +27,9 @@ function clicked() {
|
||||||
else if (grand.id === "burgLabels") editBurg();
|
else if (grand.id === "burgLabels") editBurg();
|
||||||
else if (grand.id === "burgIcons") editBurg();
|
else if (grand.id === "burgIcons") editBurg();
|
||||||
else if (parent.id === "terrain") editReliefIcon();
|
else if (parent.id === "terrain") editReliefIcon();
|
||||||
else if (parent.id === "markers") editMarker();
|
else if (parent.id === "markers") editMarker();
|
||||||
else if (grand.id === "coastline") editCoastline();
|
else if (grand.id === "coastline") editCoastline();
|
||||||
|
else if (great.id === "armies") editRegiment();
|
||||||
else if (pack.cells.t[i] === 1) {
|
else if (pack.cells.t[i] === 1) {
|
||||||
const node = document.getElementById("island_"+pack.cells.f[i]);
|
const node = document.getElementById("island_"+pack.cells.f[i]);
|
||||||
editCoastline(node);
|
editCoastline(node);
|
||||||
|
|
|
||||||
|
|
@ -39,10 +39,14 @@ function clearMainTip() {
|
||||||
tooltip.innerHTML = "";
|
tooltip.innerHTML = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// show tip at the bottom of the screen, consider possible translation
|
||||||
function showDataTip(e) {
|
function showDataTip(e) {
|
||||||
if (!e.target) return;
|
if (!e.target) return;
|
||||||
if (e.target.dataset.tip) {tip(e.target.dataset.tip); return;};
|
let dataTip = e.target.dataset.tip;
|
||||||
if (e.target.parentNode.dataset.tip) tip(e.target.parentNode.dataset.tip);
|
if (!dataTip && e.target.parentNode.dataset.tip) dataTip = e.target.parentNode.dataset.tip;
|
||||||
|
if (!dataTip) return;
|
||||||
|
const tooltip = lang === "en" ? dataTip : translate(e.target.dataset.t || e.target.parentNode.dataset.t, dataTip);
|
||||||
|
tip(tooltip);
|
||||||
}
|
}
|
||||||
|
|
||||||
function moved() {
|
function moved() {
|
||||||
|
|
@ -84,6 +88,7 @@ function showMapTooltip(point, e, i, g) {
|
||||||
const land = pack.cells.h[i] >= 20;
|
const land = pack.cells.h[i] >= 20;
|
||||||
|
|
||||||
// specific elements
|
// specific elements
|
||||||
|
if (group === "armies") {tip(e.target.dataset.name + ". Click to edit"); return;}
|
||||||
if (group === "rivers") {tip(getRiverName(e.target.id) + "Click to edit"); return;}
|
if (group === "rivers") {tip(getRiverName(e.target.id) + "Click to edit"); return;}
|
||||||
if (group === "routes") {tip("Click to edit the Route"); return;}
|
if (group === "routes") {tip("Click to edit the Route"); return;}
|
||||||
if (group === "terrain") {tip("Click to edit the Relief Icon"); return;}
|
if (group === "terrain") {tip("Click to edit the Relief Icon"); return;}
|
||||||
|
|
@ -132,14 +137,15 @@ function updateCellInfo(point, i, g) {
|
||||||
const cells = pack.cells;
|
const cells = pack.cells;
|
||||||
const x = infoX.innerHTML = rn(point[0]);
|
const x = infoX.innerHTML = rn(point[0]);
|
||||||
const y = infoY.innerHTML = rn(point[1]);
|
const y = infoY.innerHTML = rn(point[1]);
|
||||||
|
const f = cells.f[i];
|
||||||
infoLat.innerHTML = toDMS(mapCoordinates.latN - (y / graphHeight) * mapCoordinates.latT, "lat");
|
infoLat.innerHTML = toDMS(mapCoordinates.latN - (y / graphHeight) * mapCoordinates.latT, "lat");
|
||||||
infoLon.innerHTML = toDMS(mapCoordinates.lonW + (x / graphWidth) * mapCoordinates.lonT, "lon");
|
infoLon.innerHTML = toDMS(mapCoordinates.lonW + (x / graphWidth) * mapCoordinates.lonT, "lon");
|
||||||
|
|
||||||
infoCell.innerHTML = i;
|
infoCell.innerHTML = i;
|
||||||
const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value;
|
const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value;
|
||||||
infoArea.innerHTML = cells.area[i] ? si(cells.area[i] * distanceScaleInput.value ** 2) + unit : "n/a";
|
infoArea.innerHTML = cells.area[i] ? si(cells.area[i] * distanceScaleInput.value ** 2) + unit : "n/a";
|
||||||
const h = pack.cells.h[i] < 20 ? grid.cells.h[pack.cells.g[i]] : pack.cells.h[i];
|
infoEvelation.innerHTML = getElevation(pack.features[f], pack.cells.h[i]);
|
||||||
infoHeight.innerHTML = getFriendlyHeight(point) + " (" + h + ")";
|
infoDepth.innerHTML = getDepth(pack.features[f], pack.cells.h[i], point);
|
||||||
infoTemp.innerHTML = convertTemperature(grid.cells.temp[g]);
|
infoTemp.innerHTML = convertTemperature(grid.cells.temp[g]);
|
||||||
infoPrec.innerHTML = cells.h[i] >= 20 ? getFriendlyPrecipitation(i) : "n/a";
|
infoPrec.innerHTML = cells.h[i] >= 20 ? getFriendlyPrecipitation(i) : "n/a";
|
||||||
infoRiver.innerHTML = cells.h[i] >= 20 && cells.r[i] ? getRiverInfo(cells.r[i]) : "no";
|
infoRiver.innerHTML = cells.h[i] >= 20 && cells.r[i] ? getRiverInfo(cells.r[i]) : "no";
|
||||||
|
|
@ -149,7 +155,6 @@ function updateCellInfo(point, i, g) {
|
||||||
infoReligion.innerHTML = cells.religion[i] ? `${pack.religions[cells.religion[i]].name} (${cells.religion[i]})` : "no";
|
infoReligion.innerHTML = cells.religion[i] ? `${pack.religions[cells.religion[i]].name} (${cells.religion[i]})` : "no";
|
||||||
infoPopulation.innerHTML = getFriendlyPopulation(i);
|
infoPopulation.innerHTML = getFriendlyPopulation(i);
|
||||||
infoBurg.innerHTML = cells.burg[i] ? pack.burgs[cells.burg[i]].name + " (" + cells.burg[i] + ")" : "no";
|
infoBurg.innerHTML = cells.burg[i] ? pack.burgs[cells.burg[i]].name + " (" + cells.burg[i] + ")" : "no";
|
||||||
const f = cells.f[i];
|
|
||||||
infoFeature.innerHTML = f ? pack.features[f].group + " (" + f + ")" : "n/a";
|
infoFeature.innerHTML = f ? pack.features[f].group + " (" + f + ")" : "n/a";
|
||||||
infoBiome.innerHTML = biomesData.name[cells.biome[i]];
|
infoBiome.innerHTML = biomesData.name[cells.biome[i]];
|
||||||
}
|
}
|
||||||
|
|
@ -164,6 +169,26 @@ function toDMS(coord, c) {
|
||||||
return degrees + "° " + minutes + "′ " + seconds + "″ " + cardinal;
|
return degrees + "° " + minutes + "′ " + seconds + "″ " + cardinal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get surface elevation
|
||||||
|
function getElevation(f, h) {
|
||||||
|
if (f.land) return getHeight(h) + " (" + h + ")"; // land: usual height
|
||||||
|
if (f.border) return "0 " + heightUnit.value; // ocean: 0
|
||||||
|
|
||||||
|
// lake: lowest coast height - 1
|
||||||
|
const lakeCells = Array.from(pack.cells.i.filter(i => pack.cells.f[i] === f.i));
|
||||||
|
const heights = lakeCells.map(i => pack.cells.c[i].map(c => pack.cells.h[c])).flat().filter(h => h > 19);
|
||||||
|
const elevation = (d3.min(heights)||20) - 1;
|
||||||
|
return getHeight(elevation) + " (" + elevation + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
// get water depth
|
||||||
|
function getDepth(f, h, p) {
|
||||||
|
if (f.land) return "0 " + heightUnit.value; // land: 0
|
||||||
|
if (!f.border) return getHeight(h, "abs"); // lake: pack abs height
|
||||||
|
const gridH = grid.cells.h[findGridCell(p[0], p[1])];
|
||||||
|
return getHeight(gridH, "abs"); // ocean: grig height
|
||||||
|
}
|
||||||
|
|
||||||
// get user-friendly (real-world) height value from map data
|
// get user-friendly (real-world) height value from map data
|
||||||
function getFriendlyHeight(p) {
|
function getFriendlyHeight(p) {
|
||||||
const packH = pack.cells.h[findCell(p[0], p[1])];
|
const packH = pack.cells.h[findCell(p[0], p[1])];
|
||||||
|
|
@ -172,7 +197,7 @@ function getFriendlyHeight(p) {
|
||||||
return getHeight(h);
|
return getHeight(h);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getHeight(h) {
|
function getHeight(h, abs) {
|
||||||
const unit = heightUnit.value;
|
const unit = heightUnit.value;
|
||||||
let unitRatio = 3.281; // default calculations are in feet
|
let unitRatio = 3.281; // default calculations are in feet
|
||||||
if (unit === "m") unitRatio = 1; // if meter
|
if (unit === "m") unitRatio = 1; // if meter
|
||||||
|
|
@ -182,6 +207,7 @@ function getHeight(h) {
|
||||||
if (h >= 20) height = Math.pow(h - 18, +heightExponentInput.value);
|
if (h >= 20) height = Math.pow(h - 18, +heightExponentInput.value);
|
||||||
else if (h < 20 && h > 0) height = (h - 20) / h * 50;
|
else if (h < 20 && h > 0) height = (h - 20) / h * 50;
|
||||||
|
|
||||||
|
if (abs) height = Math.abs(height);
|
||||||
return rn(height * unitRatio) + " " + unit;
|
return rn(height * unitRatio) + " " + unit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -216,7 +242,7 @@ document.querySelectorAll("[data-locked]").forEach(function(e) {
|
||||||
else tip("Click to lock the option and always use the current value on new map generation");
|
else tip("Click to lock the option and always use the current value on new map generation");
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
});
|
});
|
||||||
|
|
||||||
e.addEventListener("click", function(event) {
|
e.addEventListener("click", function(event) {
|
||||||
const id = (this.id).slice(5);
|
const id = (this.id).slice(5);
|
||||||
if (this.className === "icon-lock") unlock(id);
|
if (this.className === "icon-lock") unlock(id);
|
||||||
|
|
@ -341,7 +367,7 @@ document.addEventListener("keyup", event => {
|
||||||
else if (shift && key === 79) editNotes(); // Shift + "O" to edit Notes
|
else if (shift && key === 79) editNotes(); // Shift + "O" to edit Notes
|
||||||
else if (shift && key === 84) overviewBurgs(); // Shift + "T" to open Burgs overview
|
else if (shift && key === 84) overviewBurgs(); // Shift + "T" to open Burgs overview
|
||||||
else if (shift && key === 86) overviewRivers(); // Shift + "V" to open Rivers overview
|
else if (shift && key === 86) overviewRivers(); // Shift + "V" to open Rivers overview
|
||||||
//else if (shift && key === 77) overviewMilitary(); // Shift + "M" to open Military overview
|
else if (shift && key === 77) overviewMilitary(); // Shift + "M" to open Military overview
|
||||||
else if (shift && key === 69) viewCellDetails(); // Shift + "E" to open Cell Details
|
else if (shift && key === 69) viewCellDetails(); // Shift + "E" to open Cell Details
|
||||||
|
|
||||||
else if (shift && key === 49) toggleAddBurg(); // Shift + "1" to click to add Burg
|
else if (shift && key === 49) toggleAddBurg(); // Shift + "1" to click to add Burg
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
|
|
||||||
function editHeightmap() {
|
function editHeightmap() {
|
||||||
void function selectEditMode() {
|
void function selectEditMode() {
|
||||||
alertMessage.innerHTML = `<p>Heightmap is a core element on which all other data (rivers, burgs, states etc) is based.
|
alertMessage.innerHTML = `<span>Heightmap is a core element on which all other data (rivers, burgs, states etc) is based.
|
||||||
So the best edit approach is to <i>erase</i> the secondary data and let the system automatically regenerate it on edit completion.</p>
|
So the best edit approach is to <i>erase</i> the secondary data and let the system automatically regenerate it on edit completion.</span>
|
||||||
<p>You can also <i>keep</i> all the data, but you won't be able to change the coastline.</p>
|
<p>You can also <i>keep</i> all the data, but you won't be able to change the coastline.</p>
|
||||||
<p>If you need to change the coastline and keep the data, you may try the <i>risk</i> edit option.
|
<p>If you need to change the coastline and keep the data, you may try the <i>risk</i> edit option.
|
||||||
The data will be restored as much as possible, but the coastline change can cause unexpected fluctuations and errors.</p>
|
The data will be restored as much as possible, but the coastline change can cause unexpected fluctuations and errors.</p>
|
||||||
|
|
@ -128,8 +128,7 @@ function editHeightmap() {
|
||||||
|
|
||||||
customization = 0;
|
customization = 0;
|
||||||
customizationMenu.style.display = "none";
|
customizationMenu.style.display = "none";
|
||||||
if (options.querySelector(".tab > button.active").id === "toolsTab")
|
if (document.getElementById("options").querySelector(".tab > button.active").id === "toolsTab") toolsContent.style.display = "block";
|
||||||
toolsContent.style.display = "block";
|
|
||||||
layersPreset.disabled = false;
|
layersPreset.disabled = false;
|
||||||
exitCustomization.style.display = "none"; // hide finalize button
|
exitCustomization.style.display = "none"; // hide finalize button
|
||||||
restoreDefaultEvents();
|
restoreDefaultEvents();
|
||||||
|
|
@ -195,6 +194,7 @@ function editHeightmap() {
|
||||||
BurgsAndStates.drawStateLabels();
|
BurgsAndStates.drawStateLabels();
|
||||||
|
|
||||||
Rivers.specify();
|
Rivers.specify();
|
||||||
|
Military.generate();
|
||||||
addMarkers();
|
addMarkers();
|
||||||
addZones();
|
addZones();
|
||||||
console.timeEnd("regenerateErasedData");
|
console.timeEnd("regenerateErasedData");
|
||||||
|
|
@ -307,6 +307,7 @@ function editHeightmap() {
|
||||||
|
|
||||||
for (const i of pack.cells.i) {
|
for (const i of pack.cells.i) {
|
||||||
const g = pack.cells.g[i];
|
const g = pack.cells.g[i];
|
||||||
|
if (pack.features[pack.cells.f[i]].group === "freshwater") pack.cells.h[i] = 19; // de-elevate lakes
|
||||||
const land = pack.cells.h[i] >= 20;
|
const land = pack.cells.h[i] >= 20;
|
||||||
|
|
||||||
// check biome
|
// check biome
|
||||||
|
|
|
||||||
|
|
@ -399,7 +399,7 @@ function drawPrec() {
|
||||||
const data = cells.i.filter(i => cells.h[i] >= 20 && cells.prec[i]);
|
const data = cells.i.filter(i => cells.h[i] >= 20 && cells.prec[i]);
|
||||||
prec.selectAll("circle").data(data).enter().append("circle")
|
prec.selectAll("circle").data(data).enter().append("circle")
|
||||||
.attr("cx", d => p[d][0]).attr("cy", d => p[d][1]).attr("r", 0)
|
.attr("cx", d => p[d][0]).attr("cy", d => p[d][1]).attr("r", 0)
|
||||||
.transition(show).attr("r", d => rn(Math.max(Math.sqrt(cells.prec[d] * .5), .8),2));
|
.transition(show).attr("r", d => rn(Math.max(Math.sqrt(cells.prec[d] * .5), .8),2));
|
||||||
}
|
}
|
||||||
|
|
||||||
function togglePopulation(event) {
|
function togglePopulation(event) {
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,12 @@ function overviewMilitary() {
|
||||||
if (!layerIsOn("toggleBorders")) toggleBorders();
|
if (!layerIsOn("toggleBorders")) toggleBorders();
|
||||||
|
|
||||||
const body = document.getElementById("militaryBody");
|
const body = document.getElementById("militaryBody");
|
||||||
militaryOverviewAddLines();
|
addLines();
|
||||||
$("#militaryOverview").dialog();
|
$("#militaryOverview").dialog();
|
||||||
|
|
||||||
if (modules.overviewMilitary) return;
|
if (modules.overviewMilitary) return;
|
||||||
modules.overviewMilitary = true;
|
modules.overviewMilitary = true;
|
||||||
|
updateHeaders();
|
||||||
|
|
||||||
$("#militaryOverview").dialog({
|
$("#militaryOverview").dialog({
|
||||||
title: "Military Overview", resizable: false, width: fitContent(),
|
title: "Military Overview", resizable: false, width: fitContent(),
|
||||||
|
|
@ -18,43 +19,58 @@ function overviewMilitary() {
|
||||||
});
|
});
|
||||||
|
|
||||||
// add listeners
|
// add listeners
|
||||||
document.getElementById("militaryOverviewRefresh").addEventListener("click", militaryOverviewAddLines);
|
document.getElementById("militaryOverviewRefresh").addEventListener("click", addLines);
|
||||||
|
document.getElementById("militaryOptionsButton").addEventListener("click", militaryCustomize);
|
||||||
|
document.getElementById("militaryOverviewRecalculate").addEventListener("click", militaryRecalculate);
|
||||||
document.getElementById("militaryExport").addEventListener("click", downloadMilitaryData);
|
document.getElementById("militaryExport").addEventListener("click", downloadMilitaryData);
|
||||||
|
|
||||||
// add line for each river
|
body.addEventListener("change", function(ev) {
|
||||||
function militaryOverviewAddLines() {
|
const el = ev.target, line = el.parentNode, state = +line.dataset.id, type = el.dataset.type;
|
||||||
body.innerHTML = "";
|
if (type && type !== "alert") changeForces(state, line, type, +el.value); else
|
||||||
let lines = "", militaryTotal = 0;
|
if (type === "alert") changeAlert(state, line, +el.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
// update military types in header and tooltips
|
||||||
|
function updateHeaders() {
|
||||||
|
const header = document.getElementById("militaryHeader");
|
||||||
|
header.querySelectorAll(".removable").forEach(el => el.remove());
|
||||||
|
const insert = html => document.getElementById("militaryTotal").insertAdjacentHTML("beforebegin", html);
|
||||||
|
for (const u of options.military) {
|
||||||
|
const label = capitalize(u.name.replace(/_/g, ' '));
|
||||||
|
insert(`<div data-tip="State ${u.name} units number. Click to sort" class="sortable removable" data-sortby="${u.name}">${label} </div>`);
|
||||||
|
}
|
||||||
|
header.querySelectorAll(".removable").forEach(function(e) {
|
||||||
|
e.addEventListener("click", function() {sortLines(this);});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// add line for each state
|
||||||
|
function addLines() {
|
||||||
|
body.innerHTML = "";
|
||||||
|
let lines = "";
|
||||||
const states = pack.states.filter(s => s.i && !s.removed);
|
const states = pack.states.filter(s => s.i && !s.removed);
|
||||||
const popRate = +populationRate.value;
|
|
||||||
|
|
||||||
for (const s of states) {
|
for (const s of states) {
|
||||||
const total = (s.military.infantry + s.military.cavalry + s.military.archers + s.military.fleet / 10);
|
const population = rn((s.rural + s.urban * urbanization.value) * populationRate.value);
|
||||||
const rate = total / (s.rural + s.urban * urbanization.value) * 100;
|
const getForces = u => s.military.reduce((s, r) => s+(r.u[u.name]||0), 0);
|
||||||
militaryTotal += total;
|
const total = options.military.reduce((s, u) => s + getForces(u) * u.crew, 0);
|
||||||
|
const rate = total / population * 100;
|
||||||
|
|
||||||
lines += `<div class="states" data-id=${s.i} data-state="${s.name}" data-infantry="${s.military.infantry}"
|
const sortData = options.military.map(u => `data-${u.name}="${getForces(u)}"`).join(" ");
|
||||||
data-archers="${s.military.archers}" data-cavalry="${s.military.cavalry}" data-reserve="${s.military.reserve}"
|
const lineData = options.military.map(u => `<input data-type="${u.name}" data-tip="State ${u.name} units number" type="number" min=0 step=1 value="${getForces(u)}">`).join(" ");
|
||||||
data-fleet="${s.military.fleet}" data-rate="${rate}" data-total="${total}">
|
|
||||||
<svg data-tip="State color" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${s.color}" class="fillRect"></svg>
|
|
||||||
<input data-tip="State name" class="stateName" value="${s.name}" readonly>
|
|
||||||
|
|
||||||
<input data-tip="State infantry number" type="number" class="militaryArmy" min=0 step=1 value="${rn(s.military.infantry * popRate)}">
|
lines += `<div class="states" data-id=${s.i} data-state="${s.name}" ${sortData} data-total="${total}" data-population="${population}" data-rate="${rate}" data-alert="${s.alert}">
|
||||||
<input data-tip="State archers number" type="number" class="militaryArmy" min=0 step=1 value="${rn(s.military.archers * popRate)}">
|
<svg data-tip="${s.fullName}" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${s.color}" class="fillRect"></svg>
|
||||||
<input data-tip="State cavalry number" type="number" class="militaryArmy" min=0 step=1 value="${rn(s.military.cavalry * popRate)}">
|
<input data-tip="${s.fullName}" style="width:6em" value="${s.name}" readonly>
|
||||||
<input data-tip="Number of ships in state navy" class="militaryFleet" type="number" min=0 step=1 value="${s.military.fleet}">
|
${lineData}
|
||||||
|
<div data-type="total" data-tip="Total state military personnel (considering crew)"><b>${si(total)}</b></div>
|
||||||
<div data-tip="Total military personnel (including ships crew)">${si(total * popRate)}</div>
|
<div data-tip="State population">${si(population)}</div>
|
||||||
<div data-tip="Armed forces personnel (% of state population). Depends on diplomatic situation">${rn(rate, 2)}%</div>
|
<div data-type="rate" data-tip="Military personnel rate (% of state population). Depends on war alert">${rn(rate, 2)}%</div>
|
||||||
<div data-tip="State manpower (reserve)">${si(s.military.reserve * popRate)}</div>
|
<input data-type="alert" data-tip="War Alert. Modifier to military forces number, depends of political situation" type="number" min=0 step=.01 value="${rn(s.alert, 2)}">
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
body.insertAdjacentHTML("beforeend", lines);
|
body.insertAdjacentHTML("beforeend", lines);
|
||||||
|
updateFooter();
|
||||||
// update footer
|
|
||||||
militaryFooterStates.innerHTML = states.length;
|
|
||||||
militaryFooterAverage.innerHTML = si(militaryTotal / states.length * popRate);
|
|
||||||
|
|
||||||
// add listeners
|
// add listeners
|
||||||
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseenter", ev => stateHighlightOn(ev)));
|
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseenter", ev => stateHighlightOn(ev)));
|
||||||
|
|
@ -62,6 +78,40 @@ function overviewMilitary() {
|
||||||
applySorting(militaryHeader);
|
applySorting(militaryHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function changeForces(state, line, type, value) {
|
||||||
|
const s = pack.states[state];
|
||||||
|
if (!s.military.alert) {tip("Value won't be applied as War Alert is 0. Change Alert value to positive first", false, "error"); return;}
|
||||||
|
|
||||||
|
line.dataset[type] = value;
|
||||||
|
s.military[type] = value / populationRate.value / s.military.alert;
|
||||||
|
updateTotal(s.military, line);
|
||||||
|
updateFooter();
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeAlert(state, line, alert) {
|
||||||
|
const s = pack.states[state];
|
||||||
|
s.military.alert = line.dataset.alert = alert;
|
||||||
|
const getForces = u => rn(s.military[u.name] * alert * populationRate.value)||0;
|
||||||
|
options.military.forEach(u => line.dataset[u.name] = line.querySelector(`input[data-type='${u.name}']`).value = getForces(u));
|
||||||
|
updateTotal(s.military, line);
|
||||||
|
updateFooter();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTotal(m, line) {
|
||||||
|
line.dataset.total = rn(d3.sum(options.military.map(u => (m[u.name]||0) * u.crew)) * m.alert * populationRate.value);
|
||||||
|
line.dataset.rate = line.dataset.total / line.dataset.population * 100;
|
||||||
|
line.querySelector("div[data-type='total']>b").innerHTML = si(line.dataset.total);
|
||||||
|
line.querySelector("div[data-type='rate']").innerHTML = rn(line.dataset.rate, 2) + "%";
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateFooter() {
|
||||||
|
const lines = Array.from(body.querySelectorAll(":scope > div"));
|
||||||
|
const statesNumber = militaryFooterStates.innerHTML = pack.states.filter(s => s.i && !s.removed).length;
|
||||||
|
militaryFooterForces.innerHTML = si(d3.sum(lines.map(el => el.dataset.total)) / statesNumber);
|
||||||
|
militaryFooterRate.innerHTML = rn(d3.sum(lines.map(el => el.dataset.rate)) / statesNumber, 2) + "%";
|
||||||
|
militaryFooterAlert.innerHTML = rn(d3.sum(lines.map(el => el.dataset.alert)) / statesNumber, 2);
|
||||||
|
}
|
||||||
|
|
||||||
function stateHighlightOn(event) {
|
function stateHighlightOn(event) {
|
||||||
if (!layerIsOn("toggleStates")) return;
|
if (!layerIsOn("toggleStates")) return;
|
||||||
const state = +event.target.dataset.id;
|
const state = +event.target.dataset.id;
|
||||||
|
|
@ -82,7 +132,7 @@ function overviewMilitary() {
|
||||||
const i = d3.interpolateString("0," + l, l + "," + l);
|
const i = d3.interpolateString("0," + l, l + "," + l);
|
||||||
return t => i(t);
|
return t => i(t);
|
||||||
}
|
}
|
||||||
|
|
||||||
function removePath(path) {
|
function removePath(path) {
|
||||||
path.transition().duration(1000).attr("opacity", 0).remove();
|
path.transition().duration(1000).attr("opacity", 0).remove();
|
||||||
}
|
}
|
||||||
|
|
@ -93,19 +143,92 @@ function overviewMilitary() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function militaryCustomize() {
|
||||||
|
const types = ["default", "melee", "ranged", "mounted", "machinery", "naval"];
|
||||||
|
const table = document.getElementById("militaryOptions").querySelector("tbody");
|
||||||
|
removeUnitLines();
|
||||||
|
options.military.map(u => addUnitLine(u));
|
||||||
|
|
||||||
|
$("#militaryOptions").dialog({
|
||||||
|
title: "Edit Military Units", resizable: false, width: fitContent(),
|
||||||
|
position: {my: "center", at: "center", of: "svg"},
|
||||||
|
buttons: {
|
||||||
|
Apply: function() {applyMilitaryOptions(); $(this).dialog("close");},
|
||||||
|
Add: () => addUnitLine({name: "custom", rural: 0.2, urban: 0.5, crew: 1, type: "default"}),
|
||||||
|
Restore: restoreDefaultUnits,
|
||||||
|
Cancel: function() {$(this).dialog("close");}
|
||||||
|
}, open: function() {
|
||||||
|
const buttons = $(this).dialog("widget").find(".ui-dialog-buttonset > button");
|
||||||
|
buttons[0].addEventListener("mousemove", () => tip("Apply military units settings. All forces will be recalculated!"));
|
||||||
|
buttons[1].addEventListener("mousemove", () => tip("Add new military unit to the table"));
|
||||||
|
buttons[2].addEventListener("mousemove", () => tip("Restore default military units and settings"));
|
||||||
|
buttons[3].addEventListener("mousemove", () => tip("Close the window without saving the changes"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function removeUnitLines() {
|
||||||
|
table.querySelectorAll("tr").forEach(el => el.remove());
|
||||||
|
}
|
||||||
|
|
||||||
|
function addUnitLine(u) {
|
||||||
|
const row = `<tr>
|
||||||
|
<td><input data-tip="Type unit name. If name is changed for existing unit, old unit will be replaced" value="${u.name}"></td>
|
||||||
|
<td><input data-tip="Enter conscription percentage for rural population" type="number" min=0 max=100 step=.01 value="${u.rural}"></td>
|
||||||
|
<td><input data-tip="Enter conscription percentage for urban population" type="number" min=0 max=100 step=.01 value="${u.urban}"></td>
|
||||||
|
<td><input data-tip="Enter average number of people in crew" type="number" min=1 step=1 value="${u.crew}"></td>
|
||||||
|
<td><select data-tip="Select unit type to apply special rules on forces recalculation">${types.map(t => `<option ${u.type === t ? "selected" : ""} value="${t}">${t}</option>`).join(" ")}</select></td>
|
||||||
|
<td data-tip="Check if unit is separate and can be stacked only with units of the same type">
|
||||||
|
<input id="${u.name}Separate" type="checkbox" class="checkbox" checked=${u.separate}>
|
||||||
|
<label for="${u.name}Separate" class="checkbox-label"></label>
|
||||||
|
</td>
|
||||||
|
<td data-tip="Remove the unit"><span data-tip="Remove unit type" class="icon-trash-empty pointer" onclick="this.parentElement.parentElement.remove();"></span></td>
|
||||||
|
</tr>`;
|
||||||
|
table.insertAdjacentHTML("beforeend", row);
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreDefaultUnits() {
|
||||||
|
removeUnitLines();
|
||||||
|
[{name:"infantry", rural:.25, urban:.2, crew:1, type:"melee", separate:0},
|
||||||
|
{name:"archers", rural:.12, urban:.2, crew:1, type:"ranged", separate:0},
|
||||||
|
{name:"cavalry", rural:.12, urban:.03, crew:3, type:"mounted", separate:0},
|
||||||
|
{name:"artillery", rural:0, urban:.03, crew:8, type:"machinery", separate:0},
|
||||||
|
{name:"fleet", rural:0, urban:.015, crew:100, type:"naval", separate:1}].map(u => addUnitLine(u));
|
||||||
|
}
|
||||||
|
|
||||||
|
function applyMilitaryOptions() {
|
||||||
|
options.military = Array.from(table.querySelectorAll("tr")).map(r => {
|
||||||
|
const [name, rural, urban, crew, type, separate] = Array.from(r.querySelectorAll("input, select")).map(d => d.value||d.checked);
|
||||||
|
return {name:name.replace(/[&\/\\#, +()$~%.'":*?<>{}]/g, '_'), rural:+rural||0, urban:+urban||0, crew:+crew||0, type, separate:+separate||0};
|
||||||
|
});
|
||||||
|
localStorage.setItem("military", JSON.stringify(options.military));
|
||||||
|
calculateMilitaryForces();
|
||||||
|
updateHeaders();
|
||||||
|
addLines();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function militaryRecalculate() {
|
||||||
|
calculateMilitaryForces();
|
||||||
|
addLines();
|
||||||
|
}
|
||||||
|
|
||||||
function downloadMilitaryData() {
|
function downloadMilitaryData() {
|
||||||
let data = "Id,River,Type,Length,Basin\n"; // headers
|
const units = options.military.map(u => u.name);
|
||||||
|
let data = "Id,State,"+units.map(u => capitalize(u)).join(",")+",Total,Population,Rate,War Alert\n"; // headers
|
||||||
|
|
||||||
body.querySelectorAll(":scope > div").forEach(function(el) {
|
body.querySelectorAll(":scope > div").forEach(function(el) {
|
||||||
data += el.dataset.id + ",";
|
data += el.dataset.id + ",";
|
||||||
data += el.dataset.name + ",";
|
data += el.dataset.state + ",";
|
||||||
data += el.dataset.type + ",";
|
data += units.map(u => el.dataset[u]).join(",") + ",";
|
||||||
data += el.querySelector(".biomeArea").innerHTML + ",";
|
data += el.dataset.total + ",";
|
||||||
data += el.dataset.basin + "\n";
|
data += el.dataset.population + ",";
|
||||||
|
data += el.dataset.rate + ",";
|
||||||
|
data += el.dataset.alert + "\n";
|
||||||
});
|
});
|
||||||
|
|
||||||
const name = getFileName("Military") + ".csv";
|
const name = getFileName("Military") + ".csv";
|
||||||
downloadFile(data, name);
|
downloadFile(data, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -20,7 +20,7 @@ function showOptions(event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
regenerate.style.display = "none";
|
regenerate.style.display = "none";
|
||||||
options.style.display = "block";
|
document.getElementById("options").style.display = "block";
|
||||||
optionsTrigger.style.display = "none";
|
optionsTrigger.style.display = "none";
|
||||||
|
|
||||||
if (event) event.stopPropagation();
|
if (event) event.stopPropagation();
|
||||||
|
|
@ -28,21 +28,21 @@ function showOptions(event) {
|
||||||
|
|
||||||
// Hide options pane on trigger click
|
// Hide options pane on trigger click
|
||||||
function hideOptions(event) {
|
function hideOptions(event) {
|
||||||
options.style.display = "none";
|
document.getElementById("options").style.display = "none";
|
||||||
optionsTrigger.style.display = "block";
|
optionsTrigger.style.display = "block";
|
||||||
if (event) event.stopPropagation();
|
if (event) event.stopPropagation();
|
||||||
}
|
}
|
||||||
|
|
||||||
// To toggle options on hotkey press
|
// To toggle options on hotkey press
|
||||||
function toggleOptions(event) {
|
function toggleOptions(event) {
|
||||||
if (options.style.display === "none") showOptions(event);
|
if (document.getElementById("options").style.display === "none") showOptions(event);
|
||||||
else hideOptions(event);
|
else hideOptions(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggle "New Map!" pane on hover
|
// Toggle "New Map!" pane on hover
|
||||||
optionsTrigger.addEventListener("mouseenter", function() {
|
optionsTrigger.addEventListener("mouseenter", function() {
|
||||||
if (optionsTrigger.classList.contains("glow")) return;
|
if (optionsTrigger.classList.contains("glow")) return;
|
||||||
if (options.style.display === "none") regenerate.style.display = "block";
|
if (document.getElementById("options").style.display === "none") regenerate.style.display = "block";
|
||||||
});
|
});
|
||||||
|
|
||||||
collapsible.addEventListener("mouseleave", function() {
|
collapsible.addEventListener("mouseleave", function() {
|
||||||
|
|
@ -50,15 +50,15 @@ collapsible.addEventListener("mouseleave", function() {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Activate options tab on click
|
// Activate options tab on click
|
||||||
options.querySelector("div.tab").addEventListener("click", function(event) {
|
document.getElementById("options").querySelector("div.tab").addEventListener("click", function(event) {
|
||||||
if (event.target.tagName !== "BUTTON") return;
|
if (event.target.tagName !== "BUTTON") return;
|
||||||
const id = event.target.id;
|
const id = event.target.id;
|
||||||
const active = options.querySelector(".tab > button.active");
|
const active = document.getElementById("options").querySelector(".tab > button.active");
|
||||||
if (active && id === active.id) return; // already active tab is clicked
|
if (active && id === active.id) return; // already active tab is clicked
|
||||||
|
|
||||||
if (active) active.classList.remove("active");
|
if (active) active.classList.remove("active");
|
||||||
document.getElementById(id).classList.add("active");
|
document.getElementById(id).classList.add("active");
|
||||||
options.querySelectorAll(".tabcontent").forEach(e => e.style.display = "none");
|
document.getElementById("options").querySelectorAll(".tabcontent").forEach(e => e.style.display = "none");
|
||||||
|
|
||||||
if (id === "layersTab") layersContent.style.display = "block"; else
|
if (id === "layersTab") layersContent.style.display = "block"; else
|
||||||
if (id === "styleTab") styleContent.style.display = "block"; else
|
if (id === "styleTab") styleContent.style.display = "block"; else
|
||||||
|
|
@ -69,7 +69,7 @@ options.querySelector("div.tab").addEventListener("click", function(event) {
|
||||||
if (id === "aboutTab") aboutContent.style.display = "block";
|
if (id === "aboutTab") aboutContent.style.display = "block";
|
||||||
});
|
});
|
||||||
|
|
||||||
options.querySelectorAll("i.collapsible").forEach(el => el.addEventListener("click", collapse));
|
document.getElementById("options").querySelectorAll("i.collapsible").forEach(el => el.addEventListener("click", collapse));
|
||||||
function collapse(e) {
|
function collapse(e) {
|
||||||
const trigger = e.target;
|
const trigger = e.target;
|
||||||
const section = trigger.parentElement.nextElementSibling;
|
const section = trigger.parentElement.nextElementSibling;
|
||||||
|
|
@ -309,7 +309,8 @@ function applyStoredOptions() {
|
||||||
if(stored.slice(0,5) === "style") applyOption(stylePreset, stored, stored.slice(5));
|
if(stored.slice(0,5) === "style") applyOption(stylePreset, stored, stored.slice(5));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (localStorage.getItem("winds")) winds = localStorage.getItem("winds").split(",").map(w => +w);
|
if (localStorage.getItem("winds")) options.winds = localStorage.getItem("winds").split(",").map(w => +w);
|
||||||
|
if (localStorage.getItem("military")) options.military = JSON.parse(localStorage.getItem("military"));
|
||||||
|
|
||||||
changeDialogsTransparency(localStorage.getItem("transparency") || 5);
|
changeDialogsTransparency(localStorage.getItem("transparency") || 5);
|
||||||
if (localStorage.getItem("tooltipSize")) changeTooltipSize(localStorage.getItem("tooltipSize"));
|
if (localStorage.getItem("tooltipSize")) changeTooltipSize(localStorage.getItem("tooltipSize"));
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ function editProvinces() {
|
||||||
const el = ev.target, cl = el.classList, line = el.parentNode, p = +line.dataset.id;
|
const el = ev.target, cl = el.classList, line = el.parentNode, p = +line.dataset.id;
|
||||||
if (cl.contains("fillRect")) changeFill(el); else
|
if (cl.contains("fillRect")) changeFill(el); else
|
||||||
if (cl.contains("name")) editProvinceName(p); else
|
if (cl.contains("name")) editProvinceName(p); else
|
||||||
if (cl.contains("icon-fleur")) provinceOpenCOA(ev, p); else
|
if (cl.contains("icon-coa")) provinceOpenCOA(ev, p); else
|
||||||
if (cl.contains("icon-star-empty")) capitalZoomIn(p); else
|
if (cl.contains("icon-star-empty")) capitalZoomIn(p); else
|
||||||
if (cl.contains("icon-flag-empty")) declareProvinceIndependence(p); else
|
if (cl.contains("icon-flag-empty")) declareProvinceIndependence(p); else
|
||||||
if (cl.contains("culturePopulation")) changePopulation(p); else
|
if (cl.contains("culturePopulation")) changePopulation(p); else
|
||||||
|
|
@ -116,7 +116,7 @@ function editProvinces() {
|
||||||
lines += `<div class="states" data-id=${p.i} data-name=${p.name} data-form=${p.formName} data-color="${p.color}" data-capital="${capital}" data-state="${stateName}" data-area=${area} data-population=${population}>
|
lines += `<div class="states" data-id=${p.i} data-name=${p.name} data-form=${p.formName} data-color="${p.color}" data-capital="${capital}" data-state="${stateName}" data-area=${area} data-population=${population}>
|
||||||
<svg data-tip="Province fill style. Click to change" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${p.color}" class="fillRect pointer"></svg>
|
<svg data-tip="Province fill style. Click to change" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${p.color}" class="fillRect pointer"></svg>
|
||||||
<input data-tip="Province name. Click to change" class="name pointer" value="${p.name}" readonly>
|
<input data-tip="Province name. Click to change" class="name pointer" value="${p.name}" readonly>
|
||||||
<span data-tip="Click to open province COA in the Iron Arachne Heraldry Generator. Ctrl + click to change the seed" class="icon-fleur pointer hide"></span>
|
<span data-tip="Click to open province COA in the Iron Arachne Heraldry Generator. Ctrl + click to change the seed" class="icon-coa pointer hide"></span>
|
||||||
<input data-tip="Province form name. Click to change" class="name pointer hide" value="${p.formName}" readonly>
|
<input data-tip="Province form name. Click to change" class="name pointer hide" value="${p.formName}" readonly>
|
||||||
<span data-tip="Province capital. Click to zoom into view" class="icon-star-empty pointer hide ${p.burg?'':'placeholder'}"></span>
|
<span data-tip="Province capital. Click to zoom into view" class="icon-star-empty pointer hide ${p.burg?'':'placeholder'}"></span>
|
||||||
<select data-tip="Province capital. Click to select from burgs within the state. No capital means the province is governed from the state capital" class="cultureBase hide ${p.burgs.length?'':'placeholder'}">${p.burgs.length ? getCapitalOptions(p.burgs, p.burg) : ''}</select>
|
<select data-tip="Province capital. Click to select from burgs within the state. No capital means the province is governed from the state capital" class="cultureBase hide ${p.burgs.length?'':'placeholder'}">${p.burgs.length ? getCapitalOptions(p.burgs, p.burg) : ''}</select>
|
||||||
|
|
|
||||||
277
modules/ui/regiment-editor.js
Normal file
277
modules/ui/regiment-editor.js
Normal file
|
|
@ -0,0 +1,277 @@
|
||||||
|
"use strict";
|
||||||
|
function editRegiment() {
|
||||||
|
if (customization) return;
|
||||||
|
closeDialogs(".stable");
|
||||||
|
// if (!layerIsOn("toggleArmies")) toggleArmies();
|
||||||
|
|
||||||
|
armies.selectAll(":scope > g").classed("draggable", true);
|
||||||
|
armies.selectAll(":scope > g > g").call(d3.drag().on("drag", dragRegiment));
|
||||||
|
elSelected = d3.event.target;
|
||||||
|
if (!pack.states[elSelected.dataset.state]) return;
|
||||||
|
if (!regiment()) return;
|
||||||
|
updateRegimentData(regiment());
|
||||||
|
drawBase();
|
||||||
|
|
||||||
|
$("#regimentEditor").dialog({
|
||||||
|
title: "Edit Regiment", resizable: false, close: closeEditor,
|
||||||
|
position: {my: "left top", at: "left+10 top+10", of: "#map"},
|
||||||
|
close: closeEditor
|
||||||
|
});
|
||||||
|
|
||||||
|
if (modules.editRegiment) return;
|
||||||
|
modules.editRegiment = true;
|
||||||
|
|
||||||
|
// add listeners
|
||||||
|
document.getElementById("regimentNameRestore").addEventListener("click", restoreName);
|
||||||
|
document.getElementById("regimentType").addEventListener("click", changeType);
|
||||||
|
document.getElementById("regimentName").addEventListener("change", changeName);
|
||||||
|
document.getElementById("regimentRegenerateLegend").addEventListener("click", regenerateLegend);
|
||||||
|
document.getElementById("regimentLegend").addEventListener("click", editLegend);
|
||||||
|
document.getElementById("regimentSplit").addEventListener("click", splitRegiment);
|
||||||
|
document.getElementById("regimentAdd").addEventListener("click", toggleAdd);
|
||||||
|
document.getElementById("regimentAttach").addEventListener("click", toggleAttach);
|
||||||
|
document.getElementById("regimentRemove").addEventListener("click", removeRegiment);
|
||||||
|
|
||||||
|
// get regiment data element
|
||||||
|
function regiment() {
|
||||||
|
return pack.states[elSelected.dataset.state].military.find(r => r.i == elSelected.dataset.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateRegimentData(regiment) {
|
||||||
|
document.getElementById("regimentType").className = regiment.n ? "icon-anchor" :"icon-users";
|
||||||
|
document.getElementById("regimentName").value = regiment.name;
|
||||||
|
const composition = document.getElementById("regimentComposition");
|
||||||
|
|
||||||
|
composition.innerHTML = options.military.map(u => {
|
||||||
|
return `<div data-tip="${capitalize(u.name)} number. Input to change">
|
||||||
|
<div class="label">${capitalize(u.name)}:</div>
|
||||||
|
<input data-u="${u.name}" type="number" min=0 step=1 value="${(regiment.u[u.name]||0)}">
|
||||||
|
<i>${u.type}</i></div>`
|
||||||
|
}).join("");
|
||||||
|
|
||||||
|
composition.querySelectorAll("input").forEach(el => el.addEventListener("change", changeUnit));
|
||||||
|
}
|
||||||
|
|
||||||
|
function drawBase() {
|
||||||
|
const reg = regiment();
|
||||||
|
const tr = parseTransform(elSelected.parentNode.getAttribute("transform"));
|
||||||
|
const tx = +tr[0], ty = +tr[1];
|
||||||
|
const x2 = +elSelected.nextSibling.getAttribute("x"), y2 = +elSelected.nextSibling.getAttribute("y");
|
||||||
|
|
||||||
|
const clr = pack.states[elSelected.dataset.state].color;
|
||||||
|
const base = viewbox.insert("g", "g#armies").attr("id", "regimentBase");
|
||||||
|
base.on("mouseenter", d => {tip("Regiment base. Drag to re-base the regiment", true);}).on("mouseleave", d => {tip('', true);});
|
||||||
|
|
||||||
|
base.append("line").attr("x1", reg.x).attr("y1", reg.y).attr("x2", x2+tx).attr("y2", y2+ty).attr("class", "dragLine");
|
||||||
|
base.append("circle").attr("cx", reg.x).attr("cy", reg.y).attr("r", 2).attr("fill", clr).call(d3.drag().on("drag", dragBase));
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeType() {
|
||||||
|
const reg = regiment();
|
||||||
|
reg.n = +!reg.n;
|
||||||
|
document.getElementById("regimentType").className = reg.n ? "icon-anchor" :"icon-users";
|
||||||
|
|
||||||
|
const size = 3;
|
||||||
|
elSelected.setAttribute("x", reg.n ? reg.x-size*2 : reg.x-size*3);
|
||||||
|
elSelected.setAttribute("width", reg.n ? size*4 : size*6);
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeName() {
|
||||||
|
elSelected.dataset.name = regiment().name = this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function restoreName() {
|
||||||
|
const reg = regiment(), regs = pack.states[elSelected.dataset.state].military;
|
||||||
|
const name = Military.getName(reg, regs);
|
||||||
|
elSelected.dataset.name = reg.name = document.getElementById("regimentName").value = name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function changeUnit() {
|
||||||
|
const u = this.dataset.u;
|
||||||
|
const reg = regiment();
|
||||||
|
reg.u[u] = (+this.value)||0;
|
||||||
|
reg.a = d3.sum(Object.values(reg.u));
|
||||||
|
elSelected.nextSibling.innerHTML = reg.a;
|
||||||
|
if (militaryOverviewRefresh.offsetParent) militaryOverviewRefresh.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
function splitRegiment() {
|
||||||
|
const reg = regiment(), u1 = reg.u;
|
||||||
|
const state = elSelected.dataset.state, military = pack.states[state].military;
|
||||||
|
const i = last(military).i + 1, u2 = Object.assign({}, u1); // u clone
|
||||||
|
|
||||||
|
Object.keys(u1).forEach(u => u1[u] = Math.ceil(u1[u]/2)); // halved old reg
|
||||||
|
Object.keys(u2).forEach(u => u2[u] = Math.floor(u2[u]/2)); // halved new reg
|
||||||
|
reg.a = d3.sum(Object.values(u1)); // old reg total
|
||||||
|
const a = d3.sum(Object.values(u2)); // new reg total
|
||||||
|
|
||||||
|
const newReg = {a, cell:reg.cell, i, n:reg.n, u:u2, x:reg.x, y:reg.y};
|
||||||
|
newReg.name = Military.getName(newReg, military);
|
||||||
|
military.push(newReg);
|
||||||
|
|
||||||
|
elSelected.parentNode.remove(); // undraw old reg
|
||||||
|
Military.drawRegiment(reg, state, reg.x, reg.y-6); // draw old reg above
|
||||||
|
Military.drawRegiment(newReg, state, reg.x, reg.y+6); // draw new reg below
|
||||||
|
|
||||||
|
$("#regimentEditor").dialog("close");
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleAdd() {
|
||||||
|
document.getElementById("regimentAdd").classList.toggle("pressed");
|
||||||
|
if (document.getElementById("regimentAdd").classList.contains("pressed")) {
|
||||||
|
viewbox.style("cursor", "crosshair").on("click", addRegimentOnClick);
|
||||||
|
tip("Click on map to create new regiment or fleet", true);
|
||||||
|
} else {
|
||||||
|
clearMainTip();
|
||||||
|
viewbox.on("click", clicked).style("cursor", "default");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function addRegimentOnClick() {
|
||||||
|
const point = d3.mouse(this);
|
||||||
|
const cell = findCell(point[0], point[1]);
|
||||||
|
const x = pack.cells.p[cell][0], y = pack.cells.p[cell][1];
|
||||||
|
const state = elSelected.dataset.state, military = pack.states[state].military;
|
||||||
|
const i = military.length ? last(military).i + 1 : 0;
|
||||||
|
const n = +(pack.cells.h[cell] < 20); // naval or land
|
||||||
|
const reg = {a:0, cell, i, n, u:{}, x, y};
|
||||||
|
reg.name = Military.getName(reg, military);
|
||||||
|
military.push(reg);
|
||||||
|
Military.drawRegiment(reg, state);
|
||||||
|
toggleAdd();
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleAttach() {
|
||||||
|
document.getElementById("regimentAttach").classList.toggle("pressed");
|
||||||
|
if (document.getElementById("regimentAttach").classList.contains("pressed")) {
|
||||||
|
viewbox.style("cursor", "crosshair").on("click", attachRegimentOnClick);
|
||||||
|
tip("Click on another regiment to unite both regiments. The current regiment will be removed", true);
|
||||||
|
armies.selectAll(":scope > g").classed("draggable", false);
|
||||||
|
} else {
|
||||||
|
clearMainTip();
|
||||||
|
armies.selectAll(":scope > g").classed("draggable", true);
|
||||||
|
viewbox.on("click", clicked).style("cursor", "default");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function attachRegimentOnClick() {
|
||||||
|
const target = d3.event.target, army = target.parentElement.parentElement;
|
||||||
|
|
||||||
|
if (army.parentElement.id !== "armies") {
|
||||||
|
tip("Please click on a regiment", false, "error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target === elSelected) {
|
||||||
|
tip("Cannot attach regiment to itself. Please click on another regiment", false, "error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (army !== elSelected.parentElement.parentElement) {
|
||||||
|
tip("Cannot attach this regiment to regiment of other state", false, "error");
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
const reg = regiment(); // reg to be attached
|
||||||
|
const sel = pack.states[target.dataset.state].military.find(r => r.i == target.dataset.id); // reg to attach to
|
||||||
|
|
||||||
|
for (const unit of options.military) {
|
||||||
|
const u = unit.name;
|
||||||
|
if (reg.u[u]) sel.u[u] ? sel.u[u] += reg.u[u] : sel.u[u] = reg.u[u];
|
||||||
|
}
|
||||||
|
sel.a = d3.sum(Object.values(sel.u)); // reg total
|
||||||
|
target.nextSibling.innerHTML = sel.a; // update selected reg total text
|
||||||
|
|
||||||
|
// remove attached regiment
|
||||||
|
const military = pack.states[elSelected.dataset.state].military;
|
||||||
|
military.splice(military.indexOf(reg), 1);
|
||||||
|
const index = notes.findIndex(n => n.id === elSelected.parentNode.id);
|
||||||
|
if (index != -1) notes.splice(index, 1);
|
||||||
|
elSelected.parentNode.remove();
|
||||||
|
|
||||||
|
$("#regimentEditor").dialog("close");
|
||||||
|
}
|
||||||
|
|
||||||
|
function regenerateLegend() {
|
||||||
|
const index = notes.findIndex(n => n.id === elSelected.parentNode.id);
|
||||||
|
if (index != -1) notes.splice(index, 1);
|
||||||
|
|
||||||
|
const s = pack.states[elSelected.dataset.state];
|
||||||
|
Military.generateNote(regiment(), s);
|
||||||
|
}
|
||||||
|
|
||||||
|
function editLegend() {
|
||||||
|
editNotes(elSelected.parentNode.id, regiment().name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function removeRegiment() {
|
||||||
|
alertMessage.innerHTML = "Are you sure you want to remove the regiment?";
|
||||||
|
$("#alert").dialog({resizable: false, title: "Remove regiment",
|
||||||
|
buttons: {
|
||||||
|
Remove: function() {
|
||||||
|
$(this).dialog("close");
|
||||||
|
const military = pack.states[elSelected.dataset.state].military;
|
||||||
|
const regIndex = military.indexOf(regiment());
|
||||||
|
if (regIndex === -1) return;
|
||||||
|
military.splice(regIndex, 1);
|
||||||
|
|
||||||
|
const index = notes.findIndex(n => n.id === elSelected.parentNode.id);
|
||||||
|
if (index != -1) notes.splice(index, 1);
|
||||||
|
elSelected.parentNode.remove();
|
||||||
|
|
||||||
|
if (militaryOverviewRefresh.offsetParent) militaryOverviewRefresh.click();
|
||||||
|
$("#regimentEditor").dialog("close");
|
||||||
|
},
|
||||||
|
Cancel: function() {$(this).dialog("close");}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function dragRegiment() {
|
||||||
|
const tr = parseTransform(this.getAttribute("transform"));
|
||||||
|
const dx = +tr[0] - d3.event.x, dy = +tr[1] - d3.event.y;
|
||||||
|
d3.select(this).raise();
|
||||||
|
d3.select(this.parentNode).raise();
|
||||||
|
|
||||||
|
const self = elSelected.parentNode === this;
|
||||||
|
const baseLine = viewbox.select("g#regimentBase > line");
|
||||||
|
const x2 = +elSelected.nextSibling.getAttribute("x");
|
||||||
|
const y2 = +elSelected.nextSibling.getAttribute("y");
|
||||||
|
|
||||||
|
d3.event.on("drag", function() {
|
||||||
|
const x = dx + d3.event.x, y = dy + d3.event.y;
|
||||||
|
this.setAttribute("transform", `translate(${(x)},${(y)})`);
|
||||||
|
if (self) baseLine.attr("x2", x2+x).attr("y2", y2+y);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function dragBase() {
|
||||||
|
const tr = parseTransform(this.getAttribute("transform"));
|
||||||
|
const dx = +tr[0] - d3.event.x, dy = +tr[1] - d3.event.y;
|
||||||
|
const baseLine = viewbox.select("g#regimentBase > line");
|
||||||
|
|
||||||
|
d3.event.on("drag", function() {
|
||||||
|
const x = dx + d3.event.x, y = dy + d3.event.y;
|
||||||
|
this.setAttribute("transform", `translate(${(x)},${(y)})`);
|
||||||
|
baseLine.attr("x1", d3.event.x).attr("y1", d3.event.y);
|
||||||
|
});
|
||||||
|
|
||||||
|
d3.event.on("end", function() {
|
||||||
|
const reg = regiment();
|
||||||
|
const x = d3.event.x, y = d3.event.y, cell = findCell(x, y);
|
||||||
|
reg.cell = cell, reg.x = x, reg.y = y;
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeEditor() {
|
||||||
|
armies.selectAll(":scope > g").classed("draggable", false);
|
||||||
|
armies.selectAll("g>g").call(d3.drag().on("drag", null));
|
||||||
|
viewbox.select("g#regimentBase").remove();
|
||||||
|
document.getElementById("regimentAdd").classList.remove("pressed");
|
||||||
|
document.getElementById("regimentAttach").classList.remove("pressed");
|
||||||
|
restoreDefaultEvents();
|
||||||
|
elSelected = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -42,7 +42,7 @@ function editStates() {
|
||||||
const el = ev.target, cl = el.classList, line = el.parentNode, state = +line.dataset.id;
|
const el = ev.target, cl = el.classList, line = el.parentNode, state = +line.dataset.id;
|
||||||
if (cl.contains("fillRect")) stateChangeFill(el); else
|
if (cl.contains("fillRect")) stateChangeFill(el); else
|
||||||
if (cl.contains("name")) editStateName(state); else
|
if (cl.contains("name")) editStateName(state); else
|
||||||
if (cl.contains("icon-fleur")) stateOpenCOA(ev, state); else
|
if (cl.contains("icon-coa")) stateOpenCOA(ev, state); else
|
||||||
if (cl.contains("icon-star-empty")) stateCapitalZoomIn(state); else
|
if (cl.contains("icon-star-empty")) stateCapitalZoomIn(state); else
|
||||||
if (cl.contains("culturePopulation")) changePopulation(state); else
|
if (cl.contains("culturePopulation")) changePopulation(state); else
|
||||||
if (cl.contains("icon-pin")) focusOnState(state, cl); else
|
if (cl.contains("icon-pin")) focusOnState(state, cl); else
|
||||||
|
|
@ -90,7 +90,7 @@ function editStates() {
|
||||||
data-population=${population} data-burgs=${s.burgs} data-color="" data-form="" data-capital="" data-culture="" data-type="" data-expansionism="">
|
data-population=${population} data-burgs=${s.burgs} data-color="" data-form="" data-capital="" data-culture="" data-type="" data-expansionism="">
|
||||||
<svg width="9" height="9" class="placeholder"></svg>
|
<svg width="9" height="9" class="placeholder"></svg>
|
||||||
<input data-tip="Neutral lands name. Click to change" class="stateName name pointer italic" value="${s.name}" readonly>
|
<input data-tip="Neutral lands name. Click to change" class="stateName name pointer italic" value="${s.name}" readonly>
|
||||||
<span class="icon-fleur placeholder hide"></span>
|
<span class="icon-coa placeholder hide"></span>
|
||||||
<input class="stateForm placeholder" value="none">
|
<input class="stateForm placeholder" value="none">
|
||||||
<span class="icon-star-empty placeholder hide"></span>
|
<span class="icon-star-empty placeholder hide"></span>
|
||||||
<input class="stateCapital placeholder hide">
|
<input class="stateCapital placeholder hide">
|
||||||
|
|
@ -114,7 +114,7 @@ function editStates() {
|
||||||
data-area=${area} data-population=${population} data-burgs=${s.burgs} data-culture=${pack.cultures[s.culture].name} data-type=${s.type} data-expansionism=${s.expansionism}>
|
data-area=${area} data-population=${population} data-burgs=${s.burgs} data-culture=${pack.cultures[s.culture].name} data-type=${s.type} data-expansionism=${s.expansionism}>
|
||||||
<svg data-tip="State fill style. Click to change" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${s.color}" class="fillRect pointer"></svg>
|
<svg data-tip="State fill style. Click to change" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${s.color}" class="fillRect pointer"></svg>
|
||||||
<input data-tip="State name. Click to change" class="stateName name pointer" value="${s.name}" readonly>
|
<input data-tip="State name. Click to change" class="stateName name pointer" value="${s.name}" readonly>
|
||||||
<span data-tip="Click to open state COA in the Iron Arachne Heraldry Generator. Ctrl + click to change the seed" class="icon-fleur pointer hide"></span>
|
<span data-tip="Click to open state COA in the Iron Arachne Heraldry Generator. Ctrl + click to change the seed" class="icon-coa pointer hide"></span>
|
||||||
<input data-tip="State form name. Click to change" class="stateForm name pointer" value="${s.formName}" readonly>
|
<input data-tip="State form name. Click to change" class="stateForm name pointer" value="${s.formName}" readonly>
|
||||||
<span data-tip="State capital. Click to zoom into view" class="icon-star-empty pointer hide"></span>
|
<span data-tip="State capital. Click to zoom into view" class="icon-star-empty pointer hide"></span>
|
||||||
<input data-tip="Capital name. Click and type to rename" class="stateCapital hide" value="${capital}" autocorrect="off" spellcheck="false"/>
|
<input data-tip="Capital name. Click and type to rename" class="stateCapital hide" value="${capital}" autocorrect="off" spellcheck="false"/>
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,13 @@ function editWorld() {
|
||||||
"Southern": () => applyPreset(33, 75),
|
"Southern": () => applyPreset(33, 75),
|
||||||
"Restore Winds": restoreDefaultWinds
|
"Restore Winds": restoreDefaultWinds
|
||||||
}, open: function() {
|
}, open: function() {
|
||||||
const buttons = $(this).dialog("widget").find(".ui-dialog-buttonset > button")
|
const buttons = $(this).dialog("widget").find(".ui-dialog-buttonset > button");
|
||||||
buttons[0].addEventListener("mousemove", () => tip("Click to set map size to cover the whole World"));
|
buttons[0].addEventListener("mousemove", () => tip("Click to set map size to cover the whole World"));
|
||||||
buttons[1].addEventListener("mousemove", () => tip("Click to set map size to cover the Northern latitudes"));
|
buttons[1].addEventListener("mousemove", () => tip("Click to set map size to cover the Northern latitudes"));
|
||||||
buttons[2].addEventListener("mousemove", () => tip("Click to set map size to cover the Tropical latitudes"));
|
buttons[2].addEventListener("mousemove", () => tip("Click to set map size to cover the Tropical latitudes"));
|
||||||
buttons[3].addEventListener("mousemove", () => tip("Click to set map size to cover the Southern latitudes"));
|
buttons[3].addEventListener("mousemove", () => tip("Click to set map size to cover the Southern latitudes"));
|
||||||
buttons[4].addEventListener("mousemove", () => tip("Click to restore default wind directions"));
|
buttons[4].addEventListener("mousemove", () => tip("Click to restore default wind directions"));
|
||||||
},
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const globe = d3.select("#globe");
|
const globe = d3.select("#globe");
|
||||||
|
|
@ -56,6 +56,7 @@ function editWorld() {
|
||||||
if (layerIsOn("togglePrec")) drawPrec();
|
if (layerIsOn("togglePrec")) drawPrec();
|
||||||
if (layerIsOn("toggleBiomes")) drawBiomes();
|
if (layerIsOn("toggleBiomes")) drawBiomes();
|
||||||
if (layerIsOn("toggleCoordinates")) drawCoordinates();
|
if (layerIsOn("toggleCoordinates")) drawCoordinates();
|
||||||
|
if (document.getElementById("canvas3d")) setTimeout(ThreeD.update(), 500);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateGlobePosition() {
|
function updateGlobePosition() {
|
||||||
|
|
@ -100,26 +101,26 @@ function editWorld() {
|
||||||
function updateWindDirections() {
|
function updateWindDirections() {
|
||||||
globe.select("#globeWindArrows").selectAll("path").each(function(d, i) {
|
globe.select("#globeWindArrows").selectAll("path").each(function(d, i) {
|
||||||
const tr = parseTransform(this.getAttribute("transform"));
|
const tr = parseTransform(this.getAttribute("transform"));
|
||||||
this.setAttribute("transform", `rotate(${winds[i]} ${tr[1]} ${tr[2]})`);
|
this.setAttribute("transform", `rotate(${options.winds[i]} ${tr[1]} ${tr[2]})`);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeWind() {
|
function changeWind() {
|
||||||
const arrow = d3.event.target.nextElementSibling;
|
const arrow = d3.event.target.nextElementSibling;
|
||||||
const tier = +arrow.dataset.tier;
|
const tier = +arrow.dataset.tier;
|
||||||
winds[tier] = (winds[tier] + 45) % 360;
|
options.winds[tier] = (options.winds[tier] + 45) % 360;
|
||||||
const tr = parseTransform(arrow.getAttribute("transform"));
|
const tr = parseTransform(arrow.getAttribute("transform"));
|
||||||
arrow.setAttribute("transform", `rotate(${winds[tier]} ${tr[1]} ${tr[2]})`);
|
arrow.setAttribute("transform", `rotate(${options.winds[tier]} ${tr[1]} ${tr[2]})`);
|
||||||
localStorage.setItem("winds", winds);
|
localStorage.setItem("winds", options.winds);
|
||||||
const mapTiers = d3.range(mapCoordinates.latN, mapCoordinates.latS, -30).map(c => (90-c) / 30 | 0);
|
const mapTiers = d3.range(mapCoordinates.latN, mapCoordinates.latS, -30).map(c => (90-c) / 30 | 0);
|
||||||
if (mapTiers.includes(tier)) updateWorld();
|
if (mapTiers.includes(tier)) updateWorld();
|
||||||
}
|
}
|
||||||
|
|
||||||
function restoreDefaultWinds() {
|
function restoreDefaultWinds() {
|
||||||
const defaultWinds = [225, 45, 225, 315, 135, 315];
|
const defaultWinds = [225, 45, 225, 315, 135, 315];
|
||||||
const mapTiers = d3.range(mapCoordinates.latN, mapCoordinates.latS, -30).map(c => (90-c) / 30 | 0);
|
const mapTiers = d3.range(mapCoordinates.latN, mapCoordinates.latS, -30).map(c => (90-c) / 30 | 0);
|
||||||
const update = mapTiers.some(t => winds[t] != defaultWinds[t]);
|
const update = mapTiers.some(t => options.winds[t] != defaultWinds[t]);
|
||||||
winds = defaultWinds;
|
options.winds = defaultWinds;
|
||||||
updateWindDirections();
|
updateWindDirections();
|
||||||
if (update) updateWorld();
|
if (update) updateWorld();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -232,6 +232,11 @@ function gauss(expected = 100, deviation = 30, min = 0, max = 300, round = 0) {
|
||||||
return rn(Math.max(Math.min(d3.randomNormal(expected, deviation)(), max), min), round);
|
return rn(Math.max(Math.min(d3.randomNormal(expected, deviation)(), max), min), round);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get integer from float as floor + P(fractional)
|
||||||
|
function Pint(float) {
|
||||||
|
return ~~float + +P(float % 1);
|
||||||
|
}
|
||||||
|
|
||||||
// round value to d decimals
|
// round value to d decimals
|
||||||
function rn(v, d = 0) {
|
function rn(v, d = 0) {
|
||||||
const m = Math.pow(10, d);
|
const m = Math.pow(10, d);
|
||||||
|
|
@ -403,6 +408,9 @@ function getAdjective(string) {
|
||||||
return trimVowels(string) + "ian";
|
return trimVowels(string) + "ian";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get ordinal out of integer: 1 => 1st
|
||||||
|
const nth = n => n+(["st","nd","rd"][((n+90)%100-10)%10-1]||"th");
|
||||||
|
|
||||||
// split string into 2 almost equal parts not breaking words
|
// split string into 2 almost equal parts not breaking words
|
||||||
function splitInTwo(str) {
|
function splitInTwo(str) {
|
||||||
const half = str.length / 2;
|
const half = str.length / 2;
|
||||||
|
|
@ -596,5 +604,9 @@ function isCtrlClick(event) {
|
||||||
return event.ctrlKey || event.metaKey;
|
return event.ctrlKey || event.metaKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function generateDate(from = 100, to = 1000) {
|
||||||
|
return new Date(rand(from, to),rand(12),rand(31)).toLocaleDateString("en", {year:'numeric', month:'long', day:'numeric'});
|
||||||
|
}
|
||||||
|
|
||||||
// localStorageDB
|
// localStorageDB
|
||||||
!function(){function e(t,o){return n?void(n.transaction("s").objectStore("s").get(t).onsuccess=function(e){var t=e.target.result&&e.target.result.v||null;o(t)}):void setTimeout(function(){e(t,o)},100)}var t=window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB;if(!t)return void console.error("indexDB not supported");var n,o={k:"",v:""},r=t.open("d2",1);r.onsuccess=function(e){n=this.result},r.onerror=function(e){console.error("indexedDB request error"),console.log(e)},r.onupgradeneeded=function(e){n=null;var t=e.target.result.createObjectStore("s",{keyPath:"k"});t.transaction.oncomplete=function(e){n=e.target.db}},window.ldb={get:e,set:function(e,t){o.k=e,o.v=t,n.transaction("s","readwrite").objectStore("s").put(o)}}}();
|
!function(){function e(t,o){return n?void(n.transaction("s").objectStore("s").get(t).onsuccess=function(e){var t=e.target.result&&e.target.result.v||null;o(t)}):void setTimeout(function(){e(t,o)},100)}var t=window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB;if(!t)return void console.error("indexDB not supported");var n,o={k:"",v:""},r=t.open("d2",1);r.onsuccess=function(e){n=this.result},r.onerror=function(e){console.error("indexedDB request error"),console.log(e)},r.onupgradeneeded=function(e){n=null;var t=e.target.result.createObjectStore("s",{keyPath:"k"});t.transaction.oncomplete=function(e){n=e.target.db}},window.ldb={get:e,set:function(e,t){o.k=e,o.v=t,n.transaction("s","readwrite").objectStore("s").put(o)}}}();
|
||||||
Loading…
Add table
Add a link
Reference in a new issue