mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-23 20:41:23 +01:00
Merge branch 'master' of https://github.com/Azgaar/Fantasy-Map-Generator into dev-submaps
This commit is contained in:
commit
b58674dddd
23 changed files with 850 additions and 522 deletions
83
index.css
83
index.css
|
|
@ -29,16 +29,19 @@ input:read-only {
|
||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input[type="radio"] {
|
||||||
|
vertical-align: bottom;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
padding: 2px;
|
padding: 3px;
|
||||||
text-indent: 1px;
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
iframe {
|
iframe {
|
||||||
border: 0;
|
border: 0;
|
||||||
pointer-events: none;
|
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -835,75 +838,79 @@ fieldset {
|
||||||
}
|
}
|
||||||
|
|
||||||
.matrix-table {
|
.matrix-table {
|
||||||
width: 100%;
|
max-height: 80vh;
|
||||||
font-size: smaller;
|
max-width: 85vw;
|
||||||
text-align: center;
|
scrollbar-width: thin;
|
||||||
border-collapse: collapse;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.matrix-table th,
|
.matrix-table > table {
|
||||||
table.matrix-table td {
|
text-align: center;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: smaller;
|
||||||
|
}
|
||||||
|
|
||||||
|
.matrix-table > table th,
|
||||||
|
.matrix-table > table td {
|
||||||
border: 1px solid var(--dark-solid);
|
border: 1px solid var(--dark-solid);
|
||||||
height: 2em;
|
height: 2em;
|
||||||
padding: 0.2em;
|
padding: 0.2em;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.matrix-table th {
|
.matrix-table > table th {
|
||||||
background-color: #302a2a;
|
background-color: #302a2a;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.matrix-table tr:hover th {
|
.matrix-table > table td:hover {
|
||||||
background: #3e3636;
|
|
||||||
}
|
|
||||||
|
|
||||||
table.matrix-table td:hover {
|
|
||||||
outline: 2px solid var(--dark-solid);
|
outline: 2px solid var(--dark-solid);
|
||||||
outline-offset: -1px;
|
outline-offset: -1px;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.matrix-table td.Ally {
|
.matrix-table > table td.Ally {
|
||||||
background-color: #73ec73;
|
background-color: #73ec73;
|
||||||
color: #000000;
|
color: #000000;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.matrix-table td.Friendly {
|
.matrix-table > table td.Friendly {
|
||||||
background-color: #d4f8aa;
|
background-color: #d4f8aa;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.matrix-table td.Neutral {
|
.matrix-table > table td.Neutral {
|
||||||
background-color: #d8d9d3;
|
background-color: #d8d9d3;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.matrix-table td.Suspicion {
|
.matrix-table > table td.Suspicion {
|
||||||
background-color: #eeafaa;
|
background-color: #eeafaa;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.matrix-table td.Enemy {
|
.matrix-table > table td.Enemy {
|
||||||
background-color: #ffa39c;
|
background-color: #ffa39c;
|
||||||
color: #af0d23;
|
color: #af0d23;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.matrix-table td.Unknown {
|
.matrix-table > table td.Unknown {
|
||||||
background-color: #c1bfbf;
|
background-color: #c1bfbf;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.matrix-table td.Rival {
|
.matrix-table > table td.Rival {
|
||||||
background-color: #bd845c;
|
background-color: #bd845c;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.matrix-table td.Vassal {
|
.matrix-table > table td.Vassal {
|
||||||
background-color: #87cefa;
|
background-color: #87cefa;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.matrix-table td.Suzerain {
|
.matrix-table > table td.Suzerain {
|
||||||
background-color: #8f8fe1;
|
background-color: #8f8fe1;
|
||||||
}
|
}
|
||||||
|
|
||||||
table.matrix-table td.x {
|
.matrix-table > table td.x {
|
||||||
background-color: #d4ca94;
|
background-color: #d4ca94;
|
||||||
|
cursor: initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
#sizeOutput {
|
#sizeOutput {
|
||||||
|
|
@ -1311,19 +1318,23 @@ div.slider .ui-slider-handle {
|
||||||
}
|
}
|
||||||
|
|
||||||
#alertMessage::-webkit-scrollbar,
|
#alertMessage::-webkit-scrollbar,
|
||||||
.table::-webkit-scrollbar {
|
.table::-webkit-scrollbar,
|
||||||
|
.matrix-table::-webkit-scrollbar {
|
||||||
width: 6px;
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
#alertMessage::-webkit-scrollbar-thumb,
|
#alertMessage::-webkit-scrollbar-thumb,
|
||||||
.table::-webkit-scrollbar-thumb {
|
.table::-webkit-scrollbar-thumb,
|
||||||
|
.matrix-table::-webkit-scrollbar-thumb {
|
||||||
background-color: #aaa;
|
background-color: #aaa;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#alertMessage::-webkit-scrollbar-thumb:hover,
|
#alertMessage::-webkit-scrollbar-thumb:hover,
|
||||||
.table::-webkit-scrollbar-thumb:hover {
|
.table::-webkit-scrollbar-thumb:hover,
|
||||||
|
.matrix-table::-webkit-scrollbar-thumb:hover {
|
||||||
background: #666;
|
background: #666;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1505,15 +1516,14 @@ div.states > .riverType {
|
||||||
width: 5em;
|
width: 5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
div.states > .coaIcon {
|
.coaIcon {
|
||||||
stroke-width: 3;
|
stroke-width: 3;
|
||||||
width: 1.4em;
|
width: 1.4em;
|
||||||
height: 1.4em;
|
height: 1.4em;
|
||||||
margin: -0.3em 0;
|
margin: -0.3em 0;
|
||||||
cursor: pointer;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
div.states > .coaIcon > use {
|
.coaIcon > use {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2001,7 +2011,7 @@ div.textual fieldset {
|
||||||
|
|
||||||
div.textual span,
|
div.textual span,
|
||||||
.textual legend {
|
.textual legend {
|
||||||
font-size: 0.8em;
|
font-size: 0.9em;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2120,8 +2130,9 @@ svg.button {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
#info-line {
|
.info-line {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
|
font-style: italic;
|
||||||
color: gray;
|
color: gray;
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
@ -2265,10 +2276,10 @@ svg.button {
|
||||||
left: 50%;
|
left: 50%;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
max-width: 22em;
|
max-width: 23em;
|
||||||
background-color: #fff;
|
|
||||||
padding: 1.2em;
|
padding: 1.2em;
|
||||||
border: solid 1px #000;
|
background-color: var(--bg-dialogs);
|
||||||
|
border: solid 1px var(--dark-solid);
|
||||||
font-size: 1.2em;
|
font-size: 1.2em;
|
||||||
z-index: 1000;
|
z-index: 1000;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
83
index.html
83
index.html
|
|
@ -947,7 +947,7 @@
|
||||||
|
|
||||||
<tr data-tip="Map seed number. Seed produces the same map only if canvas size and options are the same">
|
<tr data-tip="Map seed number. Seed produces the same map only if canvas size and options are the same">
|
||||||
<td>
|
<td>
|
||||||
<i data-tip="Show seed history to apply a previous seed" id="optionsMapHistory" class="icon-history"></i>
|
<i data-tip="Show seed history to apply a previous seed" id="optionsMapHistory" class="icon-hourglass-1"></i>
|
||||||
</td>
|
</td>
|
||||||
<td>Map seed</td>
|
<td>Map seed</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
@ -1062,7 +1062,7 @@
|
||||||
<input id="regionsInput" data-stored="regions" type="range" min=0 max=99 value=13>
|
<input id="regionsInput" data-stored="regions" type="range" min=0 max=99 value=13>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<input id="regionsOutput" data-stored="regions" type="number" min=0 max=99 value=13>
|
<input id="regionsOutput" data-stored="regions" type="number" min=0 max=999 value=13>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
|
|
@ -1451,9 +1451,21 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<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>
|
||||||
<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 maps.</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.
|
||||||
<p>The project is under active development. Creator and main maintainer: Azgaar. To track the development progress see the <a href="https://trello.com/b/7x832DG4/fantasy-map-generator" target="_blank">devboard</a>. For older versions see the <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog" target="_blank">changelog</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>
|
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</a>, <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Q&A" target="_blank">Q&A</a>, <a href="https://youtube.com/playlist?list=PLtgiuDC8iVR2gIG8zMTRn7T_L0arl9h1C" target="_blank">Video tutorial</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 maps.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The project is under active development. Creator and main maintainer: Azgaar. To track the development progress see the <a href="https://trello.com/b/7x832DG4/fantasy-map-generator" target="_blank">devboard</a>.
|
||||||
|
For older versions see the <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog" target="_blank">changelog</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>
|
||||||
|
|
||||||
<div style="background-color: #e85b46; padding: .4em; width: max-content; margin: .6em auto 0 auto; border: 1px solid #943838">
|
<div style="background-color: #e85b46; padding: .4em; width: max-content; margin: .6em auto 0 auto; border: 1px solid #943838">
|
||||||
<a href="https://www.patreon.com/azgaar" target="_blank" style="color: white; text-decoration: none; font-family: sans-serif">
|
<a href="https://www.patreon.com/azgaar" target="_blank" style="color: white; text-decoration: none; font-family: sans-serif">
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -1466,7 +1478,10 @@
|
||||||
</div>
|
</div>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<p>Special thanks to <a data-tip="Click to see list of supporters" onclick="showSupporters()">all supporters</a> on Patreon!</p>
|
|
||||||
|
<p>
|
||||||
|
Special thanks to <a data-tip="Click to see list of supporters" onclick="showSupporters()">all supporters</a> on Patreon!
|
||||||
|
</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>
|
||||||
|
|
@ -2038,12 +2053,15 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="mfcgPreviewSection" data-tip="Burg preview in the Medieval Fantasy City Generator. Default seed is a conbimation of map seed and burg id" style="display: flex; flex-direction: column">
|
<div id="mfcgPreviewSection" data-tip="Burg preview in the Medieval Fantasy City Generator. Default seed is a combination of map seed and burg id" style="display: flex; flex-direction: column">
|
||||||
<div>
|
<div>
|
||||||
See in <a id="mfcgLink" target="_blank">City Generator by Watabou</a>.
|
See in <a id="mfcgLink" target="_blank">City Generator by Watabou</a>.
|
||||||
|
<div id="mfcgBurgSeedSection">
|
||||||
Seed: <input id="mfcgBurgSeed" style="width: 10em" type="number" min=1 max="1e13" step="1" />
|
Seed: <input id="mfcgBurgSeed" style="width: 10em" type="number" min=1 max="1e13" step="1" />
|
||||||
<i id="regenerateMFCGBurgSeed" data-tip="Randomize Medieval Fantasy City Generator burg seed" class="icon-arrows-cw pointer" style="margin-left: .1em"></i>
|
<i id="regenerateMFCGBurgSeed" data-tip="Randomize Medieval Fantasy City Generator burg seed" class="icon-arrows-cw pointer" style="margin-left: .1em"></i>
|
||||||
</div>
|
</div>
|
||||||
|
<i id="addCustomMFCGBurgLink" data-tip="Provide custom link to the burg map" class="icon-pencil pointer" style="margin-left: .1em"></i>
|
||||||
|
</div>
|
||||||
<iframe id="mfcgPreview" sandbox="allow-scripts allow-same-origin"></iframe>
|
<iframe id="mfcgPreview" sandbox="allow-scripts allow-same-origin"></iframe>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -2623,8 +2641,10 @@
|
||||||
<option value="Sultanate">Sultanate</option>
|
<option value="Sultanate">Sultanate</option>
|
||||||
<option value="Tsardom">Tsardom</option>
|
<option value="Tsardom">Tsardom</option>
|
||||||
<option value="Ulus">Ulus</option>
|
<option value="Ulus">Ulus</option>
|
||||||
|
<option value="Viceroyalty">Viceroyalty</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
<optgroup label="Republic">
|
<optgroup label="Republic">
|
||||||
|
<option value="Chancellery">Chancellery</option>
|
||||||
<option value="City-state">City-state</option>
|
<option value="City-state">City-state</option>
|
||||||
<option value="Diarchy">Diarchy</option>
|
<option value="Diarchy">Diarchy</option>
|
||||||
<option value="Federation">Federation</option>
|
<option value="Federation">Federation</option>
|
||||||
|
|
@ -2662,8 +2682,10 @@
|
||||||
<option value="Divine Kingdom">Divine Kingdom</option>
|
<option value="Divine Kingdom">Divine Kingdom</option>
|
||||||
<option value="Divine Empire">Divine Empire</option>
|
<option value="Divine Empire">Divine Empire</option>
|
||||||
<option value="Eparchy">Eparchy</option>
|
<option value="Eparchy">Eparchy</option>
|
||||||
|
<option value="Exarchate">Exarchate</option>
|
||||||
<option value="Holy State">Holy State</option>
|
<option value="Holy State">Holy State</option>
|
||||||
<option value="Imamah">Imamah</option>
|
<option value="Imamah">Imamah</option>
|
||||||
|
<option value="Patriarchate">Patriarchate</option>
|
||||||
<option value="Theocracy">Theocracy</option>
|
<option value="Theocracy">Theocracy</option>
|
||||||
</optgroup>
|
</optgroup>
|
||||||
<optgroup label="Anarchy">
|
<optgroup label="Anarchy">
|
||||||
|
|
@ -2740,33 +2762,27 @@
|
||||||
<div id="diplomacyEditor" class="dialog stable" style="display: none">
|
<div id="diplomacyEditor" class="dialog stable" style="display: none">
|
||||||
<div id="diplomacyHeader" class="header">
|
<div id="diplomacyHeader" class="header">
|
||||||
<div style="left:.2em" data-tip="Click to sort by state name" class="sortable alphabetically" data-sortby="name">State </div>
|
<div style="left:.2em" data-tip="Click to sort by state name" class="sortable alphabetically" data-sortby="name">State </div>
|
||||||
<div style="left:13.4em" data-tip="Click to sort by diplomatical relations" class="sortable alphabetically" data-sortby="relations">Relations </div>
|
<div style="left:14.4em" data-tip="Click to sort by diplomatical relations" class="sortable alphabetically" data-sortby="relations">Relations </div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="diplomacyBodySection" class="table"></div>
|
<div id="diplomacyBodySection" class="table"></div>
|
||||||
|
<div class="info-line">Click on state name to see relations.<br>Click on relations name to change it</div>
|
||||||
<div id="diplomacySelect">
|
|
||||||
<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="Neutral means states relations are neither positive nor negative">Neutral</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="Relations are unknown if states do not have enough 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="Vassal is a state having obligation to its suzerain">Vassal</div>
|
|
||||||
<div data-tip="Suzerain is a state having some control over its vassals">Suzerain</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="diplomacyBottom" style="margin-top: .1em">
|
<div id="diplomacyBottom" style="margin-top: .1em">
|
||||||
<button id="diplomacyEditorRefresh" data-tip="Refresh the Editor" class="icon-cw"></button>
|
<button id="diplomacyEditorRefresh" data-tip="Refresh the Editor" class="icon-cw"></button>
|
||||||
<button id="diplomacyEditStyle" data-tip="Edit states (including diplomacy view) style in Style Editor" class="icon-adjust"></button>
|
<button id="diplomacyEditStyle" data-tip="Edit states (including diplomacy view) style in Style Editor" class="icon-adjust"></button>
|
||||||
<button id="diplomacyRegenerate" data-tip="Regenerate diplomatical relations" class="icon-retweet"></button>
|
<button id="diplomacyRegenerate" data-tip="Regenerate diplomatical relations" class="icon-retweet"></button>
|
||||||
|
<button id="diplomacyReset" data-tip="Reset diplomatical relations of selected state to Neutral" class="icon-eraser"></button>
|
||||||
<button id="diplomacyHistory" data-tip="Show relations history" class="icon-hourglass-1"></button>
|
<button id="diplomacyHistory" data-tip="Show relations history" class="icon-hourglass-1"></button>
|
||||||
<button id="diplomacyMatrix" data-tip="Show relations matrix" class="icon-list-bullet"></button>
|
<button id="diplomacyShowMatrix" data-tip="Show relations matrix" class="icon-list-bullet"></button>
|
||||||
<button id="diplomacyExport" data-tip="Save state relations matrix as a text file (.csv)" class="icon-download"></button>
|
<button id="diplomacyExport" data-tip="Save state relations matrix as a text file (.csv)" class="icon-download"></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="diplomacyMatrix" class="dialog" style="display: none">
|
||||||
|
<div id="diplomacyMatrixBody" class="matrix-table"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="provinceNameEditor" class="dialog" data-province="0" style="display: none">
|
<div id="provinceNameEditor" class="dialog" data-province="0" style="display: none">
|
||||||
<div>
|
<div>
|
||||||
<div data-tip="Province short name" class="label">Short name:</div>
|
<div data-tip="Province short name" class="label">Short name:</div>
|
||||||
|
|
@ -2784,6 +2800,8 @@
|
||||||
<option value="Autonomy">Autonomy</option>
|
<option value="Autonomy">Autonomy</option>
|
||||||
<option value="Barony">Barony</option>
|
<option value="Barony">Barony</option>
|
||||||
<option value="Canton">Canton</option>
|
<option value="Canton">Canton</option>
|
||||||
|
<option value="Captaincy">Captaincy</option>
|
||||||
|
<option value="Chiefdom">Chiefdom</option>
|
||||||
<option value="Clan">Clan</option>
|
<option value="Clan">Clan</option>
|
||||||
<option value="Colony">Colony</option>
|
<option value="Colony">Colony</option>
|
||||||
<option value="Council">Council</option>
|
<option value="Council">Council</option>
|
||||||
|
|
@ -2791,6 +2809,7 @@
|
||||||
<option value="Deanery">Deanery</option>
|
<option value="Deanery">Deanery</option>
|
||||||
<option value="Department">Department</option>
|
<option value="Department">Department</option>
|
||||||
<option value="Dependency">Dependency</option>
|
<option value="Dependency">Dependency</option>
|
||||||
|
<option value="Diaconate">Diaconate</option>
|
||||||
<option value="District">District</option>
|
<option value="District">District</option>
|
||||||
<option value="Earldom">Earldom</option>
|
<option value="Earldom">Earldom</option>
|
||||||
<option value="Governorate">Governorate</option>
|
<option value="Governorate">Governorate</option>
|
||||||
|
|
@ -2800,6 +2819,7 @@
|
||||||
<option value="Landgrave">Landgrave</option>
|
<option value="Landgrave">Landgrave</option>
|
||||||
<option value="Mandate">Mandate</option>
|
<option value="Mandate">Mandate</option>
|
||||||
<option value="Margrave">Margrave</option>
|
<option value="Margrave">Margrave</option>
|
||||||
|
<option value="Municipality">Municipality</option>
|
||||||
<option value="Occupation zone">Occupation zone</option>
|
<option value="Occupation zone">Occupation zone</option>
|
||||||
<option value="Parish">Parish</option>
|
<option value="Parish">Parish</option>
|
||||||
<option value="Prefecture">Prefecture</option>
|
<option value="Prefecture">Prefecture</option>
|
||||||
|
|
@ -2807,6 +2827,7 @@
|
||||||
<option value="Region">Region</option>
|
<option value="Region">Region</option>
|
||||||
<option value="Republic">Republic</option>
|
<option value="Republic">Republic</option>
|
||||||
<option value="Reservation">Reservation</option>
|
<option value="Reservation">Reservation</option>
|
||||||
|
<option value="Seneschalty">Seneschalty</option>
|
||||||
<option value="Shire">Shire</option>
|
<option value="Shire">Shire</option>
|
||||||
<option value="State">State</option>
|
<option value="State">State</option>
|
||||||
<option value="Territory">Territory</option>
|
<option value="Territory">Territory</option>
|
||||||
|
|
@ -2874,21 +2895,22 @@
|
||||||
<div id="namesbaseBasesTop">
|
<div id="namesbaseBasesTop">
|
||||||
<span>Select base: </span>
|
<span>Select base: </span>
|
||||||
<select id="namesbaseSelect" data-tip="Select base to edit" style="width: 12em" value="0"></select>
|
<select id="namesbaseSelect" data-tip="Select base to edit" style="width: 12em" value="0"></select>
|
||||||
|
<span style="margin-left: 2px">Names data: </span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="namesbaseBody">
|
<div id="namesbaseBody" style="margin-block: 2px">
|
||||||
<span>Names data:</span><br>
|
<textarea id="namesbaseTextarea" data-base="0" rows=13 data-tip="Names data: a comma separated list of source names used for names generation" placeholder="Provide a names data: a comma separated list of source names" autocorrect="off" spellcheck="false"></textarea>
|
||||||
<textarea id="namesbaseTextarea" data-base="0" rows=12 data-tip="Names data: a comma separated list of source names used for names generation" placeholder="Provide a names data: a comma separated list of source names" autocorrect="off" spellcheck="false"></textarea>
|
|
||||||
<br>
|
|
||||||
<div>
|
<div>
|
||||||
<span>Name: </span>
|
<span>Name: </span>
|
||||||
<input id="namesbaseName" data-tip="Type to change a base name" placeholder="Base name" autocorrect="off" spellcheck="false" style="width:12em"/>
|
<input id="namesbaseName" data-tip="Type to change a base name" placeholder="Base name" autocorrect="off" spellcheck="false" style="width:12em"/>
|
||||||
<span>Length: </span>
|
<span>Length: </span>
|
||||||
<input id="namesbaseMin" data-tip="Recommended minimum name length" type="number" min=2 max=100>
|
<input id="namesbaseMin" data-tip="Recommended minimum name length" type="number" min=2 max=100>
|
||||||
<input id="namesbaseMax" data-tip="Recommended maximum name length" type="number" min=2 value=10>
|
<input id="namesbaseMax" data-tip="Recommended maximum name length" type="number" min=2 value=10>
|
||||||
<span>Double: </span>
|
<span>Doubled: </span>
|
||||||
<input id="namesbaseDouble" data-tip="Populate with letters that can used twice in a row (geminates)" autocorrect="off" spellcheck="false" style="width:10em">
|
<input id="namesbaseDouble" data-tip="Populate with letters that can used twice in a row (geminates)" autocorrect="off" spellcheck="false" style="width:10em">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<legend>Generated examples: </legend>
|
<legend>Generated examples: </legend>
|
||||||
<div id="namesbaseExamples" data-tip="Examples. Click to re-generate"></div>
|
<div id="namesbaseExamples" data-tip="Examples. Click to re-generate"></div>
|
||||||
|
|
@ -2897,12 +2919,12 @@
|
||||||
|
|
||||||
<div id="namesbaseBottom">
|
<div id="namesbaseBottom">
|
||||||
<button id="namesbaseUpdateExamples" data-tip="Re-generate examples based on provided data" class="icon-arrows-cw"></button>
|
<button id="namesbaseUpdateExamples" data-tip="Re-generate examples based on provided data" class="icon-arrows-cw"></button>
|
||||||
<button id="namesbaseAnalize" data-tip="Analyze namesbase to get a validity and quality overview" class="icon-flask"></button>
|
|
||||||
<button id="namesbaseAdd" data-tip="Add new namesbase" class="icon-plus"></button>
|
<button id="namesbaseAdd" data-tip="Add new namesbase" class="icon-plus"></button>
|
||||||
<button id="namesbaseDefault" data-tip="Restore default namesbase" class="icon-cancel"></button>
|
<button id="namesbaseDefault" data-tip="Restore default namesbase" class="icon-cancel"></button>
|
||||||
<button id="namesbaseDownload" data-tip="Download namesbase to PC" class="icon-download"></button>
|
<button id="namesbaseDownload" data-tip="Download namesbase to PC" class="icon-download"></button>
|
||||||
<button id="namesbaseUpload" data-tip="Upload a namesbase from PC" class="icon-upload"></button>
|
<button id="namesbaseUpload" data-tip="Upload a namesbase from PC" class="icon-upload"></button>
|
||||||
<button id="namesbaseCA" data-tip="Find or share custom namesbase on Cartography Assets portal" class="icon-drafting-compass" onclick="openURL('https://cartographyassets.com/asset-category/specific-assets/azgaars-generator/namebases/')"></button>
|
<button id="namesbaseCA" data-tip="Find or share custom namesbase on Cartography Assets portal" class="icon-drafting-compass"></button>
|
||||||
|
<button id="namesbaseAnalyze" data-tip="Analyze namesbase to get a validity and quality overview" class="icon-flask"></button>
|
||||||
<button id="namesbaseSpeak" data-tip="Speak the examples. You can change voice and language in options" class="icon-voice"></button>
|
<button id="namesbaseSpeak" data-tip="Speak the examples. You can change voice and language in options" class="icon-voice"></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -3289,6 +3311,7 @@
|
||||||
<button id="addNewBurg" data-tip="Add a new burg. Hold Shift to add multiple" class="icon-plus"></button>
|
<button id="addNewBurg" data-tip="Add a new burg. Hold Shift to add multiple" class="icon-plus"></button>
|
||||||
<button id="burgsExport" data-tip="Save burgs-related data as a text file (.csv)" class="icon-download"></button>
|
<button id="burgsExport" data-tip="Save burgs-related data as a text file (.csv)" class="icon-download"></button>
|
||||||
<button id="burgNamesImport" data-tip="Rename burgs in bulk" class="icon-upload"></button>
|
<button id="burgNamesImport" data-tip="Rename burgs in bulk" class="icon-upload"></button>
|
||||||
|
<button id="burgsLockAll" data-tip="Lock or unlock all burgs" class="icon-lock"></button>
|
||||||
<button id="burgsRemoveAll" data-tip="Remove all unlocked burgs except for capitals. To remove a capital remove its state first" class="icon-trash"></button>
|
<button id="burgsRemoveAll" data-tip="Remove all unlocked burgs except for capitals. To remove a capital remove its state first" class="icon-trash"></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -3640,7 +3663,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div data-tip="Image scale relative to image size (e.g. 5x)" style="margin-bottom: .3em">
|
<div data-tip="Image scale relative to image size (e.g. 5x)" style="margin-bottom: .3em">
|
||||||
<div class="label">Scale:</div>
|
<div class="label">Scale:</div>
|
||||||
<input id="tileScaleInput" data-stored="tileScale" type="range" min=1 max=4 value=1 style="width: 11em">
|
<input id="tileScaleInput" data-stored="tileScale" type="range" min=1 max=4 value=1 style="width: 10em">
|
||||||
<input id="tileScaleOutput" data-stored="tileScale" type="number" min=1 value=1 >
|
<input id="tileScaleOutput" data-stored="tileScale" type="number" min=1 value=1 >
|
||||||
</div>
|
</div>
|
||||||
<div data-tip="Calculated size of image if combined" style="margin-bottom: .3em">
|
<div data-tip="Calculated size of image if combined" style="margin-bottom: .3em">
|
||||||
|
|
@ -3695,7 +3718,7 @@
|
||||||
<div id="prompt" style="display: none" class="dialog">
|
<div id="prompt" style="display: none" class="dialog">
|
||||||
<form id="promptForm">
|
<form id="promptForm">
|
||||||
<div id="promptText"></div>
|
<div id="promptText"></div>
|
||||||
<input id="promptInput" type="number" step=.01 placeholder="type value" autocomplete="off" required>
|
<input id="promptInput" type="number" step=.01 placeholder="type value" autocomplete="off">
|
||||||
<button type="submit">Confirm</button>
|
<button type="submit">Confirm</button>
|
||||||
<button type="button" id="promptCancel" formnovalidate>Cancel</button>
|
<button type="button" id="promptCancel" formnovalidate>Cancel</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
||||||
37
main.js
37
main.js
|
|
@ -109,7 +109,7 @@ scaleBar.on("mousemove", () => tip("Click to open Units Editor")).on("click", ()
|
||||||
legend.on("mousemove", () => tip("Drag to change the position. Click to hide the legend")).on("click", () => clearLegend());
|
legend.on("mousemove", () => tip("Drag to change the position. Click to hide the legend")).on("click", () => clearLegend());
|
||||||
|
|
||||||
// main data variables
|
// main data variables
|
||||||
let grid = {}; // initial grapg based on jittered square grid and data
|
let grid = {}; // initial graph based on jittered square grid and data
|
||||||
let pack = {}; // packed graph and data
|
let pack = {}; // packed graph and data
|
||||||
let seed;
|
let seed;
|
||||||
let mapId;
|
let mapId;
|
||||||
|
|
@ -161,7 +161,7 @@ let urbanDensity = +document.getElementById("urbanDensityInput").value;
|
||||||
|
|
||||||
applyStoredOptions();
|
applyStoredOptions();
|
||||||
|
|
||||||
// voronoi graph extention, cannot be changed arter generation
|
// voronoi graph extension, cannot be changed after generation
|
||||||
let graphWidth = +mapWidthInput.value;
|
let graphWidth = +mapWidthInput.value;
|
||||||
let graphHeight = +mapHeightInput.value;
|
let graphHeight = +mapHeightInput.value;
|
||||||
|
|
||||||
|
|
@ -493,7 +493,7 @@ function invokeActiveZooming() {
|
||||||
coastline.select("#sea_island").attr("filter", filter);
|
coastline.select("#sea_island").attr("filter", filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
// rescale lables on zoom
|
// rescale labels on zoom
|
||||||
if (labels.style("display") !== "none") {
|
if (labels.style("display") !== "none") {
|
||||||
labels.selectAll("g").each(function () {
|
labels.selectAll("g").each(function () {
|
||||||
if (this.id === "burgLabels") return;
|
if (this.id === "burgLabels") return;
|
||||||
|
|
@ -668,7 +668,7 @@ function generate() {
|
||||||
const parsedError = parseError(error);
|
const parsedError = parseError(error);
|
||||||
clearMainTip();
|
clearMainTip();
|
||||||
|
|
||||||
alertMessage.innerHTML = `An error is occured on map generation. Please retry.
|
alertMessage.innerHTML = `An error has occurred on map generation. Please retry.
|
||||||
<br>If error is critical, clear the stored data and try again.
|
<br>If error is critical, clear the stored data and try again.
|
||||||
<p id="errorBox">${parsedError}</p>`;
|
<p id="errorBox">${parsedError}</p>`;
|
||||||
$("#alert").dialog({
|
$("#alert").dialog({
|
||||||
|
|
@ -909,7 +909,7 @@ function defineMapSize() {
|
||||||
const template = document.getElementById("templateInput").value; // heightmap template
|
const template = document.getElementById("templateInput").value; // heightmap template
|
||||||
const part = grid.features.some(f => f.land && f.border); // if land goes over map borders
|
const part = grid.features.some(f => f.land && f.border); // if land goes over map borders
|
||||||
const max = part ? 80 : 100; // max size
|
const max = part ? 80 : 100; // max size
|
||||||
const lat = () => gauss(P(0.5) ? 40 : 60, 15, 25, 75); // latiture shift
|
const lat = () => gauss(P(0.5) ? 40 : 60, 15, 25, 75); // latitude shift
|
||||||
|
|
||||||
if (!part) {
|
if (!part) {
|
||||||
if (template === "Pangea") return [100, 50];
|
if (template === "Pangea") return [100, 50];
|
||||||
|
|
@ -981,7 +981,10 @@ function generatePrecipitation() {
|
||||||
prec.selectAll("*").remove();
|
prec.selectAll("*").remove();
|
||||||
const {cells, cellsX, cellsY} = grid;
|
const {cells, cellsX, cellsY} = grid;
|
||||||
cells.prec = new Uint8Array(cells.i.length); // precipitation array
|
cells.prec = new Uint8Array(cells.i.length); // precipitation array
|
||||||
const modifier = precInput.value / 100; // user's input
|
|
||||||
|
const cellsNumberModifier = (pointsInput.dataset.cells / 10000) ** 0.25;
|
||||||
|
const precInputModifier = precInput.value / 100;
|
||||||
|
const modifier = cellsNumberModifier * precInputModifier;
|
||||||
|
|
||||||
const westerly = [];
|
const westerly = [];
|
||||||
const easterly = [];
|
const easterly = [];
|
||||||
|
|
@ -997,14 +1000,14 @@ function generatePrecipitation() {
|
||||||
// x2 = 60-70 latitude: wet summer (rising zone), dry winter (sinking zone)
|
// x2 = 60-70 latitude: wet summer (rising zone), dry winter (sinking zone)
|
||||||
// x1 = 70-85 latitude: dry all year (sinking zone)
|
// x1 = 70-85 latitude: dry all year (sinking zone)
|
||||||
// x0.5 = 85-90 latitude: dry all year (sinking zone)
|
// x0.5 = 85-90 latitude: dry all year (sinking zone)
|
||||||
const lalitudeModifier = [4, 2, 2, 2, 1, 1, 2, 2, 2, 2, 3, 3, 2, 2, 1, 1, 1, 0.5];
|
const latitudeModifier = [4, 2, 2, 2, 1, 1, 2, 2, 2, 2, 3, 3, 2, 2, 1, 1, 1, 0.5];
|
||||||
const MAX_PASSABLE_ELEVATION = 85;
|
const MAX_PASSABLE_ELEVATION = 85;
|
||||||
|
|
||||||
// define wind directions based on cells latitude and prevailing winds there
|
// define wind directions based on cells latitude and prevailing winds there
|
||||||
d3.range(0, cells.i.length, cellsX).forEach(function (c, i) {
|
d3.range(0, cells.i.length, cellsX).forEach(function (c, i) {
|
||||||
const lat = mapCoordinates.latN - (i / cellsY) * mapCoordinates.latT;
|
const lat = mapCoordinates.latN - (i / cellsY) * mapCoordinates.latT;
|
||||||
const latBand = ((Math.abs(lat) - 1) / 5) | 0;
|
const latBand = ((Math.abs(lat) - 1) / 5) | 0;
|
||||||
const latMod = lalitudeModifier[latBand];
|
const latMod = latitudeModifier[latBand];
|
||||||
const windTier = (Math.abs(lat - 89) / 30) | 0; // 30d tiers from 0 to 5 from N to S
|
const windTier = (Math.abs(lat - 89) / 30) | 0; // 30d tiers from 0 to 5 from N to S
|
||||||
const {isWest, isEast, isNorth, isSouth} = getWindDirections(windTier);
|
const {isWest, isEast, isNorth, isSouth} = getWindDirections(windTier);
|
||||||
|
|
||||||
|
|
@ -1021,14 +1024,14 @@ function generatePrecipitation() {
|
||||||
const vertT = southerly + northerly;
|
const vertT = southerly + northerly;
|
||||||
if (northerly) {
|
if (northerly) {
|
||||||
const bandN = ((Math.abs(mapCoordinates.latN) - 1) / 5) | 0;
|
const bandN = ((Math.abs(mapCoordinates.latN) - 1) / 5) | 0;
|
||||||
const latModN = mapCoordinates.latT > 60 ? d3.mean(lalitudeModifier) : lalitudeModifier[bandN];
|
const latModN = mapCoordinates.latT > 60 ? d3.mean(latitudeModifier) : latitudeModifier[bandN];
|
||||||
const maxPrecN = (northerly / vertT) * 60 * modifier * latModN;
|
const maxPrecN = (northerly / vertT) * 60 * modifier * latModN;
|
||||||
passWind(d3.range(0, cellsX, 1), maxPrecN, cellsX, cellsY);
|
passWind(d3.range(0, cellsX, 1), maxPrecN, cellsX, cellsY);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (southerly) {
|
if (southerly) {
|
||||||
const bandS = ((Math.abs(mapCoordinates.latS) - 1) / 5) | 0;
|
const bandS = ((Math.abs(mapCoordinates.latS) - 1) / 5) | 0;
|
||||||
const latModS = mapCoordinates.latT > 60 ? d3.mean(lalitudeModifier) : lalitudeModifier[bandS];
|
const latModS = mapCoordinates.latT > 60 ? d3.mean(latitudeModifier) : latitudeModifier[bandS];
|
||||||
const maxPrecS = (southerly / vertT) * 60 * modifier * latModS;
|
const maxPrecS = (southerly / vertT) * 60 * modifier * latModS;
|
||||||
passWind(d3.range(cells.i.length - cellsX, cells.i.length, 1), maxPrecS, -cellsX, cellsY);
|
passWind(d3.range(cells.i.length - cellsX, cells.i.length, 1), maxPrecS, -cellsX, cellsY);
|
||||||
}
|
}
|
||||||
|
|
@ -1054,7 +1057,7 @@ function generatePrecipitation() {
|
||||||
}
|
}
|
||||||
|
|
||||||
let humidity = maxPrec - cells.h[first]; // initial water amount
|
let humidity = maxPrec - cells.h[first]; // initial water amount
|
||||||
if (humidity <= 0) continue; // if first cell in row is too elevated cosdired wind dry
|
if (humidity <= 0) continue; // if first cell in row is too elevated consider wind dry
|
||||||
|
|
||||||
for (let s = 0, current = first; s < steps; s++, current += next) {
|
for (let s = 0, current = first; s < steps; s++, current += next) {
|
||||||
if (cells.temp[current] < -5) continue; // no flux in permafrost
|
if (cells.temp[current] < -5) continue; // no flux in permafrost
|
||||||
|
|
@ -1182,7 +1185,7 @@ function reGraph() {
|
||||||
TIME && console.timeEnd("reGraph");
|
TIME && console.timeEnd("reGraph");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect and draw the coasline
|
// Detect and draw the coastline
|
||||||
function drawCoastline() {
|
function drawCoastline() {
|
||||||
TIME && console.time("drawCoastline");
|
TIME && console.time("drawCoastline");
|
||||||
reMarkFeatures();
|
reMarkFeatures();
|
||||||
|
|
@ -1191,7 +1194,7 @@ function drawCoastline() {
|
||||||
vertices = pack.vertices,
|
vertices = pack.vertices,
|
||||||
n = cells.i.length,
|
n = cells.i.length,
|
||||||
features = pack.features;
|
features = pack.features;
|
||||||
const used = new Uint8Array(features.length); // store conneted features
|
const used = new Uint8Array(features.length); // store connected features
|
||||||
const largestLand = d3.scan(
|
const largestLand = d3.scan(
|
||||||
features.map(f => (f.land ? f.cells : 0)),
|
features.map(f => (f.land ? f.cells : 0)),
|
||||||
(a, b) => b - a
|
(a, b) => b - a
|
||||||
|
|
@ -1395,6 +1398,12 @@ function reMarkFeatures() {
|
||||||
TIME && console.timeEnd("reMarkFeatures");
|
TIME && console.timeEnd("reMarkFeatures");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isWetLand(moisture, temperature, height) {
|
||||||
|
if (moisture > 40 && temperature > -2 && height < 25) return true; //near coast
|
||||||
|
if (moisture > 24 && temperature > -2 && height > 24 && height < 60) return true; //off coast
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// assign biome id for each cell
|
// assign biome id for each cell
|
||||||
function defineBiomes() {
|
function defineBiomes() {
|
||||||
TIME && console.time("defineBiomes");
|
TIME && console.time("defineBiomes");
|
||||||
|
|
@ -1427,7 +1436,7 @@ function defineBiomes() {
|
||||||
function getBiomeId(moisture, temperature, height) {
|
function getBiomeId(moisture, temperature, height) {
|
||||||
if (height < 20) return 0; // marine biome: all water cells
|
if (height < 20) return 0; // marine biome: all water cells
|
||||||
if (temperature < -5) return 11; // permafrost biome
|
if (temperature < -5) return 11; // permafrost biome
|
||||||
if (moisture > 40 && temperature > -2 && (height < 25 || (moisture > 24 && height > 24 && height < 60))) return 12; // wetland biome
|
if (isWetLand(moisture, temperature, height)) return 12; // wetland biome
|
||||||
|
|
||||||
const moistureBand = Math.min((moisture / 5) | 0, 4); // [0-4]
|
const moistureBand = Math.min((moisture / 5) | 0, 4); // [0-4]
|
||||||
const temperatureBand = Math.min(Math.max(20 - temperature, 0), 25); // [0-25]
|
const temperatureBand = Math.min(Math.max(20 - temperature, 0), 25); // [0-25]
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ window.BurgsAndStates = (function () {
|
||||||
|
|
||||||
function placeCapitals() {
|
function placeCapitals() {
|
||||||
TIME && console.time("placeCapitals");
|
TIME && console.time("placeCapitals");
|
||||||
let count = +regionsInput.value;
|
let count = +regionsOutput.value;
|
||||||
let burgs = [0];
|
let burgs = [0];
|
||||||
|
|
||||||
const rand = () => 0.5 + Math.random() * 0.5;
|
const rand = () => 0.5 + Math.random() * 0.5;
|
||||||
|
|
@ -240,7 +240,7 @@ window.BurgsAndStates = (function () {
|
||||||
b.citadel = b.capital || (pop > 50 && P(0.75)) || P(0.5) ? 1 : 0;
|
b.citadel = b.capital || (pop > 50 && P(0.75)) || P(0.5) ? 1 : 0;
|
||||||
b.plaza = pop > 50 || (pop > 30 && P(0.75)) || (pop > 10 && P(0.5)) || P(0.25) ? 1 : 0;
|
b.plaza = pop > 50 || (pop > 30 && P(0.75)) || (pop > 10 && P(0.5)) || P(0.25) ? 1 : 0;
|
||||||
b.walls = b.capital || pop > 30 || (pop > 20 && P(0.75)) || (pop > 10 && P(0.5)) || P(0.2) ? 1 : 0;
|
b.walls = b.capital || pop > 30 || (pop > 20 && P(0.75)) || (pop > 10 && P(0.5)) || P(0.2) ? 1 : 0;
|
||||||
b.shanty = pop > 30 || (pop > 20 && P(0.75)) || (b.walls && P(0.75)) ? 1 : 0;
|
b.shanty = pop > 60 || (pop > 40 && P(0.75)) || (pop > 20 && b.walls && P(0.4)) ? 1 : 0;
|
||||||
const religion = cells.religion[b.cell];
|
const religion = cells.religion[b.cell];
|
||||||
const theocracy = pack.states[b.state].form === "Theocracy";
|
const theocracy = pack.states[b.state].form === "Theocracy";
|
||||||
b.temple = (religion && theocracy) || pop > 50 || (pop > 35 && P(0.75)) || (pop > 20 && P(0.5)) ? 1 : 0;
|
b.temple = (religion && theocracy) || pop > 50 || (pop > 35 && P(0.75)) || (pop > 20 && P(0.5)) ? 1 : 0;
|
||||||
|
|
@ -726,7 +726,7 @@ window.BurgsAndStates = (function () {
|
||||||
TIME && console.time("assignColors");
|
TIME && console.time("assignColors");
|
||||||
const colors = ["#66c2a5", "#fc8d62", "#8da0cb", "#e78ac3", "#a6d854", "#ffd92f"]; // d3.schemeSet2;
|
const colors = ["#66c2a5", "#fc8d62", "#8da0cb", "#e78ac3", "#a6d854", "#ffd92f"]; // d3.schemeSet2;
|
||||||
|
|
||||||
// assin basic color using greedy coloring algorithm
|
// assign basic color using greedy coloring algorithm
|
||||||
pack.states.forEach(s => {
|
pack.states.forEach(s => {
|
||||||
if (!s.i || s.removed) return;
|
if (!s.i || s.removed) return;
|
||||||
const neibs = s.neighbors;
|
const neibs = s.neighbors;
|
||||||
|
|
@ -962,12 +962,12 @@ window.BurgsAndStates = (function () {
|
||||||
const republic = {
|
const republic = {
|
||||||
Republic: 75,
|
Republic: 75,
|
||||||
Federation: 4,
|
Federation: 4,
|
||||||
Oligarchy: 2,
|
"Trade Company": 4,
|
||||||
"Most Serene Republic": 2,
|
"Most Serene Republic": 2,
|
||||||
|
Oligarchy: 2,
|
||||||
Tetrarchy: 1,
|
Tetrarchy: 1,
|
||||||
Triumvirate: 1,
|
Triumvirate: 1,
|
||||||
Diarchy: 1,
|
Diarchy: 1,
|
||||||
"Trade Company": 4,
|
|
||||||
Junta: 1
|
Junta: 1
|
||||||
}; // weighted random
|
}; // weighted random
|
||||||
const union = {Union: 3, League: 4, Confederation: 1, "United Kingdom": 1, "United Republic": 1, "United Provinces": 2, Commonwealth: 1, Heptarchy: 1}; // weighted random
|
const union = {Union: 3, League: 4, Confederation: 1, "United Kingdom": 1, "United Republic": 1, "United Provinces": 2, Commonwealth: 1, Heptarchy: 1}; // weighted random
|
||||||
|
|
@ -997,7 +997,7 @@ window.BurgsAndStates = (function () {
|
||||||
const form = monarchy[tier];
|
const form = monarchy[tier];
|
||||||
// Default name depends on exponent tier, some culture bases have special names for tiers
|
// Default name depends on exponent tier, some culture bases have special names for tiers
|
||||||
if (s.diplomacy) {
|
if (s.diplomacy) {
|
||||||
if (form === "Duchy" && s.neighbors.length > 1 && rand(6) < s.neighbors.length && s.diplomacy.includes("Vassal")) return "Marches"; // some vassal dutchies on borderland
|
if (form === "Duchy" && s.neighbors.length > 1 && rand(6) < s.neighbors.length && s.diplomacy.includes("Vassal")) return "Marches"; // some vassal duchies on borderland
|
||||||
if (base === 1 && P(0.3) && s.diplomacy.includes("Vassal")) return "Dominion"; // English vassals
|
if (base === 1 && P(0.3) && s.diplomacy.includes("Vassal")) return "Dominion"; // English vassals
|
||||||
if (P(0.3) && s.diplomacy.includes("Vassal")) return "Protectorate"; // some vassals
|
if (P(0.3) && s.diplomacy.includes("Vassal")) return "Protectorate"; // some vassals
|
||||||
}
|
}
|
||||||
|
|
@ -1037,7 +1037,12 @@ window.BurgsAndStates = (function () {
|
||||||
if (tier < 2 && P(0.5)) return "Diocese";
|
if (tier < 2 && P(0.5)) return "Diocese";
|
||||||
if (tier < 2 && P(0.5)) return "Bishopric";
|
if (tier < 2 && P(0.5)) return "Bishopric";
|
||||||
}
|
}
|
||||||
if (tier < 2 && P(0.9) && [7, 5].includes(base)) return "Eparchy"; // Greek, Ruthenian
|
if (P(0.9) && [7, 5].includes(base)) {
|
||||||
|
// Greek, Ruthenian
|
||||||
|
if (tier < 2) return "Eparchy";
|
||||||
|
if (tier === 2) return "Exarchate";
|
||||||
|
if (tier > 2) return "Patriarchate";
|
||||||
|
}
|
||||||
if (P(0.9) && [21, 16].includes(base)) return "Imamah"; // Nigerian, Turkish
|
if (P(0.9) && [21, 16].includes(base)) return "Imamah"; // Nigerian, Turkish
|
||||||
if (tier > 2 && P(0.8) && [18, 17, 28].includes(base)) return "Caliphate"; // Arabic, Berber, Swahili
|
if (tier > 2 && P(0.8) && [18, 17, 28].includes(base)) return "Caliphate"; // Arabic, Berber, Swahili
|
||||||
return rw(theocracy);
|
return rw(theocracy);
|
||||||
|
|
@ -1093,7 +1098,7 @@ window.BurgsAndStates = (function () {
|
||||||
const max = percentage == 100 ? 1000 : gauss(20, 5, 5, 100) * percentage ** 0.5; // max growth
|
const max = percentage == 100 ? 1000 : gauss(20, 5, 5, 100) * percentage ** 0.5; // max growth
|
||||||
|
|
||||||
const forms = {
|
const forms = {
|
||||||
Monarchy: {County: 11, Earldom: 3, Shire: 1, Landgrave: 1, Margrave: 1, Barony: 1},
|
Monarchy: {County: 22, Earldom: 6, Shire: 2, Landgrave: 2, Margrave: 2, Barony: 2, Captaincy: 1, Seneschalty: 1},
|
||||||
Republic: {Province: 6, Department: 2, Governorate: 2, District: 1, Canton: 1, Prefecture: 1},
|
Republic: {Province: 6, Department: 2, Governorate: 2, District: 1, Canton: 1, Prefecture: 1},
|
||||||
Theocracy: {Parish: 3, Deanery: 1},
|
Theocracy: {Parish: 3, Deanery: 1},
|
||||||
Union: {Province: 1, State: 1, Canton: 1, Republic: 1, County: 1, Council: 1},
|
Union: {Province: 1, State: 1, Canton: 1, Republic: 1, County: 1, Council: 1},
|
||||||
|
|
|
||||||
|
|
@ -405,8 +405,8 @@ function saveGeoJSON_Cells() {
|
||||||
json.features.push(feature);
|
json.features.push(feature);
|
||||||
});
|
});
|
||||||
|
|
||||||
const name = getFileName("Cells") + ".geojson";
|
const fileName = getFileName("Cells") + ".geojson";
|
||||||
downloadFile(JSON.stringify(json), name, "application/json");
|
downloadFile(JSON.stringify(json), fileName, "application/json");
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveGeoJSON_Routes() {
|
function saveGeoJSON_Routes() {
|
||||||
|
|
@ -421,30 +421,25 @@ function saveGeoJSON_Routes() {
|
||||||
json.features.push(feature);
|
json.features.push(feature);
|
||||||
});
|
});
|
||||||
|
|
||||||
const name = getFileName("Routes") + ".geojson";
|
const fileName = getFileName("Routes") + ".geojson";
|
||||||
downloadFile(JSON.stringify(json), name, "application/json");
|
downloadFile(JSON.stringify(json), fileName, "application/json");
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveGeoJSON_Rivers() {
|
function saveGeoJSON_Rivers() {
|
||||||
const json = {type: "FeatureCollection", features: []};
|
const json = {type: "FeatureCollection", features: []};
|
||||||
|
|
||||||
rivers.selectAll("path").each(function () {
|
rivers.selectAll("path").each(function () {
|
||||||
const coordinates = getRiverPoints(this);
|
const river = pack.rivers.find(r => r.i === +this.id.slice(5));
|
||||||
const id = this.id;
|
if (!river) return;
|
||||||
const width = +this.dataset.increment;
|
|
||||||
const increment = +this.dataset.increment;
|
|
||||||
const river = pack.rivers.find(r => r.i === +id.slice(5));
|
|
||||||
const name = river ? river.name : "";
|
|
||||||
const type = river ? river.type : "";
|
|
||||||
const i = river ? river.i : "";
|
|
||||||
const basin = river ? river.basin : "";
|
|
||||||
|
|
||||||
const feature = {type: "Feature", geometry: {type: "LineString", coordinates}, properties: {id, i, basin, name, type, width, increment}};
|
const coordinates = getRiverPoints(this);
|
||||||
|
const properties = {...river, id: this.id};
|
||||||
|
const feature = {type: "Feature", geometry: {type: "LineString", coordinates}, properties};
|
||||||
json.features.push(feature);
|
json.features.push(feature);
|
||||||
});
|
});
|
||||||
|
|
||||||
const name = getFileName("Rivers") + ".geojson";
|
const fileName = getFileName("Rivers") + ".geojson";
|
||||||
downloadFile(JSON.stringify(json), name, "application/json");
|
downloadFile(JSON.stringify(json), fileName, "application/json");
|
||||||
}
|
}
|
||||||
|
|
||||||
function saveGeoJSON_Markers() {
|
function saveGeoJSON_Markers() {
|
||||||
|
|
|
||||||
|
|
@ -828,6 +828,7 @@ function parseLoadedData(data) {
|
||||||
// v 1.65 changed rivers data
|
// v 1.65 changed rivers data
|
||||||
d3.select("#rivers").attr("style", null); // remove style to unhide layer
|
d3.select("#rivers").attr("style", null); // remove style to unhide layer
|
||||||
const {cells, rivers} = pack;
|
const {cells, rivers} = pack;
|
||||||
|
const defaultWidthFactor = rn(1 / (pointsInput.dataset.cells / 10000) ** 0.25, 2);
|
||||||
|
|
||||||
for (const river of rivers) {
|
for (const river of rivers) {
|
||||||
const node = document.getElementById("river" + river.i);
|
const node = document.getElementById("river" + river.i);
|
||||||
|
|
@ -856,7 +857,7 @@ function parseLoadedData(data) {
|
||||||
river.points = riverPoints;
|
river.points = riverPoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
river.widthFactor = 1;
|
river.widthFactor = defaultWidthFactor;
|
||||||
|
|
||||||
cells.i.forEach(i => {
|
cells.i.forEach(i => {
|
||||||
const riverInWater = cells.r[i] && cells.h[i] < 20;
|
const riverInWater = cells.r[i] && cells.h[i] < 20;
|
||||||
|
|
@ -1013,6 +1014,31 @@ function parseLoadedData(data) {
|
||||||
ERROR && console.error("Data Integrity Check. Province", p.i, "is linked to removed state", p.state);
|
ERROR && console.error("Data Integrity Check. Province", p.i, "is linked to removed state", p.state);
|
||||||
p.removed = true; // remove incorrect province
|
p.removed = true; // remove incorrect province
|
||||||
});
|
});
|
||||||
|
|
||||||
|
{
|
||||||
|
const markerIds = [];
|
||||||
|
let nextId = last(pack.markers)?.i + 1 || 0;
|
||||||
|
|
||||||
|
pack.markers.forEach(marker => {
|
||||||
|
if (markerIds[marker.i]) {
|
||||||
|
ERROR && console.error("Data Integrity Check. Marker", marker.i, "has non-unique id. Changing to", nextId);
|
||||||
|
|
||||||
|
const domElements = document.querySelectorAll("#marker" + marker.i);
|
||||||
|
if (domElements[1]) domElements[1].id = "marker" + nextId; // rename 2nd dom element
|
||||||
|
|
||||||
|
const noteElements = notes.filter(note => note.id === "marker" + marker.i);
|
||||||
|
if (noteElements[1]) noteElements[1].id = "marker" + nextId; // rename 2nd note
|
||||||
|
|
||||||
|
marker.i = nextId;
|
||||||
|
nextId += 1;
|
||||||
|
} else {
|
||||||
|
markerIds[marker.i] = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// sort markers by index
|
||||||
|
pack.markers.sort((a, b) => a.i - b.i);
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
changeMapSize();
|
changeMapSize();
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ window.Markers = (function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
function addMarker({cell, type, icon, dx, dy, px}) {
|
function addMarker({cell, type, icon, dx, dy, px}) {
|
||||||
const i = pack.markers.length;
|
const i = last(pack.markers)?.i + 1 || 0;
|
||||||
const [x, y] = getMarkerCoordinates(cell);
|
const [x, y] = getMarkerCoordinates(cell);
|
||||||
const marker = {i, icon, type, x, y, cell};
|
const marker = {i, icon, type, x, y, cell};
|
||||||
if (dx) marker.dx = dx;
|
if (dx) marker.dx = dx;
|
||||||
|
|
|
||||||
|
|
@ -30,63 +30,59 @@ window.Religions = (function () {
|
||||||
const base = {
|
const base = {
|
||||||
number: ["One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve"],
|
number: ["One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve"],
|
||||||
being: [
|
being: [
|
||||||
"God",
|
|
||||||
"Goddess",
|
|
||||||
"Lord",
|
|
||||||
"Lady",
|
|
||||||
"Deity",
|
|
||||||
"Creator",
|
|
||||||
"Maker",
|
|
||||||
"Overlord",
|
|
||||||
"Ruler",
|
|
||||||
"Chief",
|
|
||||||
"Master",
|
|
||||||
"Spirit",
|
|
||||||
"Ancestor",
|
"Ancestor",
|
||||||
|
"Ancient",
|
||||||
|
"Brother",
|
||||||
|
"Chief",
|
||||||
|
"Council",
|
||||||
|
"Creator",
|
||||||
|
"Deity",
|
||||||
|
"Elder",
|
||||||
"Father",
|
"Father",
|
||||||
"Forebear",
|
"Forebear",
|
||||||
"Forefather",
|
"Forefather",
|
||||||
"Mother",
|
|
||||||
"Brother",
|
|
||||||
"Sister",
|
|
||||||
"Elder",
|
|
||||||
"Numen",
|
|
||||||
"Ancient",
|
|
||||||
"Virgin",
|
|
||||||
"Giver",
|
"Giver",
|
||||||
"Council",
|
"God",
|
||||||
|
"Goddess",
|
||||||
"Guardian",
|
"Guardian",
|
||||||
"Reaper"
|
"Lady",
|
||||||
|
"Lord",
|
||||||
|
"Maker",
|
||||||
|
"Master",
|
||||||
|
"Mother",
|
||||||
|
"Numen",
|
||||||
|
"Overlord",
|
||||||
|
"Reaper",
|
||||||
|
"Ruler",
|
||||||
|
"Sister",
|
||||||
|
"Spirit",
|
||||||
|
"Virgin"
|
||||||
],
|
],
|
||||||
animal: [
|
animal: [
|
||||||
"Dragon",
|
|
||||||
"Wyvern",
|
|
||||||
"Phoenix",
|
|
||||||
"Unicorn",
|
|
||||||
"Sphinx",
|
|
||||||
"Centaur",
|
|
||||||
"Pegasus",
|
|
||||||
"Kraken",
|
|
||||||
"Basilisk",
|
|
||||||
"Chimera",
|
|
||||||
"Cyclope",
|
|
||||||
"Antelope",
|
"Antelope",
|
||||||
"Ape",
|
"Ape",
|
||||||
"Badger",
|
"Badger",
|
||||||
|
"Basilisk",
|
||||||
"Bear",
|
"Bear",
|
||||||
"Beaver",
|
"Beaver",
|
||||||
"Bison",
|
"Bison",
|
||||||
"Boar",
|
"Boar",
|
||||||
"Buffalo",
|
"Buffalo",
|
||||||
|
"Camel",
|
||||||
"Cat",
|
"Cat",
|
||||||
|
"Centaur",
|
||||||
|
"Chimera",
|
||||||
"Cobra",
|
"Cobra",
|
||||||
"Crane",
|
"Crane",
|
||||||
"Crocodile",
|
"Crocodile",
|
||||||
"Crow",
|
"Crow",
|
||||||
|
"Cyclope",
|
||||||
"Deer",
|
"Deer",
|
||||||
"Dog",
|
"Dog",
|
||||||
|
"Dragon",
|
||||||
"Eagle",
|
"Eagle",
|
||||||
"Elk",
|
"Elk",
|
||||||
|
"Falcon",
|
||||||
"Fox",
|
"Fox",
|
||||||
"Goat",
|
"Goat",
|
||||||
"Goose",
|
"Goose",
|
||||||
|
|
@ -94,10 +90,12 @@ window.Religions = (function () {
|
||||||
"Hawk",
|
"Hawk",
|
||||||
"Heron",
|
"Heron",
|
||||||
"Horse",
|
"Horse",
|
||||||
|
"Hound",
|
||||||
"Hyena",
|
"Hyena",
|
||||||
"Ibis",
|
"Ibis",
|
||||||
"Jackal",
|
"Jackal",
|
||||||
"Jaguar",
|
"Jaguar",
|
||||||
|
"Kraken",
|
||||||
"Lark",
|
"Lark",
|
||||||
"Leopard",
|
"Leopard",
|
||||||
"Lion",
|
"Lion",
|
||||||
|
|
@ -107,177 +105,179 @@ window.Religions = (function () {
|
||||||
"Mule",
|
"Mule",
|
||||||
"Narwhal",
|
"Narwhal",
|
||||||
"Owl",
|
"Owl",
|
||||||
|
"Ox",
|
||||||
"Panther",
|
"Panther",
|
||||||
|
"Pegasus",
|
||||||
|
"Phoenix",
|
||||||
"Rat",
|
"Rat",
|
||||||
"Raven",
|
"Raven",
|
||||||
"Rook",
|
"Rook",
|
||||||
"Scorpion",
|
"Scorpion",
|
||||||
|
"Serpent",
|
||||||
"Shark",
|
"Shark",
|
||||||
"Sheep",
|
"Sheep",
|
||||||
"Snake",
|
"Snake",
|
||||||
|
"Sphinx",
|
||||||
"Spider",
|
"Spider",
|
||||||
"Swan",
|
"Swan",
|
||||||
"Tiger",
|
"Tiger",
|
||||||
"Turtle",
|
"Turtle",
|
||||||
|
"Unicorn",
|
||||||
"Viper",
|
"Viper",
|
||||||
"Vulture",
|
"Vulture",
|
||||||
"Walrus",
|
"Walrus",
|
||||||
"Wolf",
|
"Wolf",
|
||||||
"Wolverine",
|
"Wolverine",
|
||||||
"Worm",
|
"Worm",
|
||||||
"Camel",
|
"Wyvern"
|
||||||
"Falcon",
|
|
||||||
"Hound",
|
|
||||||
"Ox",
|
|
||||||
"Serpent"
|
|
||||||
],
|
],
|
||||||
adjective: [
|
adjective: [
|
||||||
"New",
|
|
||||||
"Good",
|
|
||||||
"High",
|
|
||||||
"Old",
|
|
||||||
"Great",
|
|
||||||
"Big",
|
|
||||||
"Young",
|
|
||||||
"Major",
|
|
||||||
"Strong",
|
|
||||||
"Happy",
|
|
||||||
"Last",
|
|
||||||
"Main",
|
|
||||||
"Huge",
|
|
||||||
"Far",
|
|
||||||
"Beautiful",
|
|
||||||
"Wild",
|
|
||||||
"Fair",
|
|
||||||
"Prime",
|
|
||||||
"Crazy",
|
|
||||||
"Ancient",
|
|
||||||
"Proud",
|
|
||||||
"Secret",
|
|
||||||
"Lucky",
|
|
||||||
"Sad",
|
|
||||||
"Silent",
|
|
||||||
"Latter",
|
|
||||||
"Severe",
|
|
||||||
"Fat",
|
|
||||||
"Holy",
|
|
||||||
"Pure",
|
|
||||||
"Aggressive",
|
"Aggressive",
|
||||||
"Honest",
|
"Almighty",
|
||||||
"Giant",
|
"Ancient",
|
||||||
"Mad",
|
"Beautiful",
|
||||||
"Pregnant",
|
"Benevolent",
|
||||||
"Distant",
|
"Big",
|
||||||
"Lost",
|
|
||||||
"Broken",
|
|
||||||
"Blind",
|
"Blind",
|
||||||
"Friendly",
|
"Blond",
|
||||||
"Unknown",
|
"Bloody",
|
||||||
"Sleeping",
|
"Brave",
|
||||||
"Slumbering",
|
"Broken",
|
||||||
"Loud",
|
"Brutal",
|
||||||
"Hungry",
|
"Burning",
|
||||||
"Wise",
|
"Calm",
|
||||||
"Worried",
|
"Cheerful",
|
||||||
"Sacred",
|
"Crazy",
|
||||||
"Magical",
|
"Cruel",
|
||||||
"Superior",
|
|
||||||
"Patient",
|
|
||||||
"Dead",
|
"Dead",
|
||||||
"Deadly",
|
"Deadly",
|
||||||
"Peaceful",
|
|
||||||
"Grateful",
|
|
||||||
"Frozen",
|
|
||||||
"Evil",
|
|
||||||
"Scary",
|
|
||||||
"Burning",
|
|
||||||
"Divine",
|
|
||||||
"Bloody",
|
|
||||||
"Dying",
|
|
||||||
"Waking",
|
|
||||||
"Brutal",
|
|
||||||
"Unhappy",
|
|
||||||
"Calm",
|
|
||||||
"Cruel",
|
|
||||||
"Favorable",
|
|
||||||
"Blond",
|
|
||||||
"Explicit",
|
|
||||||
"Disturbing",
|
|
||||||
"Devastating",
|
"Devastating",
|
||||||
"Brave",
|
"Distant",
|
||||||
"Sunny",
|
"Disturbing",
|
||||||
"Troubled",
|
"Divine",
|
||||||
"Flying",
|
"Dying",
|
||||||
"Sustainable",
|
|
||||||
"Marine",
|
|
||||||
"Fatal",
|
|
||||||
"Inherent",
|
|
||||||
"Selected",
|
|
||||||
"Naval",
|
|
||||||
"Cheerful",
|
|
||||||
"Almighty",
|
|
||||||
"Benevolent",
|
|
||||||
"Eternal",
|
"Eternal",
|
||||||
|
"Evil",
|
||||||
|
"Explicit",
|
||||||
|
"Fair",
|
||||||
|
"Far",
|
||||||
|
"Fat",
|
||||||
|
"Fatal",
|
||||||
|
"Favorable",
|
||||||
|
"Flying",
|
||||||
|
"Friendly",
|
||||||
|
"Frozen",
|
||||||
|
"Giant",
|
||||||
|
"Good",
|
||||||
|
"Grateful",
|
||||||
|
"Great",
|
||||||
|
"Happy",
|
||||||
|
"High",
|
||||||
|
"Holy",
|
||||||
|
"Honest",
|
||||||
|
"Huge",
|
||||||
|
"Hungry",
|
||||||
"Immutable",
|
"Immutable",
|
||||||
"Infallible"
|
"Infallible",
|
||||||
|
"Inherent",
|
||||||
|
"Last",
|
||||||
|
"Latter",
|
||||||
|
"Lost",
|
||||||
|
"Loud",
|
||||||
|
"Lucky",
|
||||||
|
"Mad",
|
||||||
|
"Magical",
|
||||||
|
"Main",
|
||||||
|
"Major",
|
||||||
|
"Marine",
|
||||||
|
"Naval",
|
||||||
|
"New",
|
||||||
|
"Old",
|
||||||
|
"Patient",
|
||||||
|
"Peaceful",
|
||||||
|
"Pregnant",
|
||||||
|
"Prime",
|
||||||
|
"Proud",
|
||||||
|
"Pure",
|
||||||
|
"Sacred",
|
||||||
|
"Sad",
|
||||||
|
"Scary",
|
||||||
|
"Secret",
|
||||||
|
"Selected",
|
||||||
|
"Severe",
|
||||||
|
"Silent",
|
||||||
|
"Sleeping",
|
||||||
|
"Slumbering",
|
||||||
|
"Strong",
|
||||||
|
"Sunny",
|
||||||
|
"Superior",
|
||||||
|
"Sustainable",
|
||||||
|
"Troubled",
|
||||||
|
"Unhappy",
|
||||||
|
"Unknown",
|
||||||
|
"Waking",
|
||||||
|
"Wild",
|
||||||
|
"Wise",
|
||||||
|
"Worried",
|
||||||
|
"Young"
|
||||||
],
|
],
|
||||||
genitive: [
|
genitive: [
|
||||||
"Day",
|
|
||||||
"Life",
|
|
||||||
"Death",
|
|
||||||
"Night",
|
|
||||||
"Home",
|
|
||||||
"Fog",
|
|
||||||
"Snow",
|
|
||||||
"Winter",
|
|
||||||
"Summer",
|
|
||||||
"Cold",
|
"Cold",
|
||||||
"Springs",
|
"Day",
|
||||||
"Gates",
|
"Death",
|
||||||
"Nature",
|
|
||||||
"Thunder",
|
|
||||||
"Lightning",
|
|
||||||
"War",
|
|
||||||
"Ice",
|
|
||||||
"Frost",
|
|
||||||
"Fire",
|
|
||||||
"Doom",
|
"Doom",
|
||||||
"Fate",
|
"Fate",
|
||||||
"Pain",
|
"Fire",
|
||||||
|
"Fog",
|
||||||
|
"Frost",
|
||||||
|
"Gates",
|
||||||
"Heaven",
|
"Heaven",
|
||||||
|
"Home",
|
||||||
|
"Ice",
|
||||||
"Justice",
|
"Justice",
|
||||||
|
"Life",
|
||||||
"Light",
|
"Light",
|
||||||
|
"Lightning",
|
||||||
"Love",
|
"Love",
|
||||||
|
"Nature",
|
||||||
|
"Night",
|
||||||
|
"Pain",
|
||||||
|
"Snow",
|
||||||
|
"Springs",
|
||||||
|
"Summer",
|
||||||
|
"Thunder",
|
||||||
"Time",
|
"Time",
|
||||||
"Victory"
|
"Victory",
|
||||||
|
"War",
|
||||||
|
"Winter"
|
||||||
],
|
],
|
||||||
theGenitive: [
|
theGenitive: [
|
||||||
"World",
|
|
||||||
"Word",
|
|
||||||
"South",
|
|
||||||
"West",
|
|
||||||
"North",
|
|
||||||
"East",
|
|
||||||
"Sun",
|
|
||||||
"Moon",
|
|
||||||
"Peak",
|
|
||||||
"Fall",
|
|
||||||
"Dawn",
|
|
||||||
"Eclipse",
|
|
||||||
"Abyss",
|
"Abyss",
|
||||||
"Blood",
|
"Blood",
|
||||||
"Tree",
|
"Dawn",
|
||||||
"Earth",
|
"Earth",
|
||||||
|
"East",
|
||||||
|
"Eclipse",
|
||||||
|
"Fall",
|
||||||
"Harvest",
|
"Harvest",
|
||||||
|
"Moon",
|
||||||
|
"North",
|
||||||
|
"Peak",
|
||||||
"Rainbow",
|
"Rainbow",
|
||||||
"Sea",
|
"Sea",
|
||||||
"Sky",
|
"Sky",
|
||||||
|
"South",
|
||||||
"Stars",
|
"Stars",
|
||||||
"Storm",
|
"Storm",
|
||||||
|
"Sun",
|
||||||
|
"Tree",
|
||||||
"Underworld",
|
"Underworld",
|
||||||
"Wild"
|
"West",
|
||||||
|
"Wild",
|
||||||
|
"Word",
|
||||||
|
"World"
|
||||||
],
|
],
|
||||||
color: ["Dark", "Light", "Bright", "Golden", "White", "Black", "Red", "Pink", "Purple", "Blue", "Green", "Yellow", "Amber", "Orange", "Brown", "Grey"]
|
color: ["Amber", "Black", "Blue", "Bright", "Brown", "Dark", "Golden", "Green", "Grey", "Light", "Orange", "Pink", "Purple", "Red", "White", "Yellow"]
|
||||||
};
|
};
|
||||||
|
|
||||||
const forms = {
|
const forms = {
|
||||||
|
|
@ -308,10 +308,10 @@ window.Religions = (function () {
|
||||||
Monotheism: {Religion: 1, Church: 1},
|
Monotheism: {Religion: 1, Church: 1},
|
||||||
"Non-theism": {Beliefs: 3, Spirits: 1},
|
"Non-theism": {Beliefs: 3, Spirits: 1},
|
||||||
|
|
||||||
Cult: {Cult: 4, Sect: 4, Worship: 1, Orden: 1, Coterie: 1, Arcanum: 1},
|
Cult: {Cult: 4, Sect: 4, Arcanum: 1, Coterie: 1, Order: 1, Worship: 1},
|
||||||
"Dark Cult": {Cult: 2, Sect: 2, Occultism: 1, Idols: 1, Coven: 1, Circle: 1, Blasphemy: 1},
|
"Dark Cult": {Cult: 2, Sect: 2, Blasphemy: 1, Circle: 1, Coven: 1, Idols: 1, Occultism: 1},
|
||||||
|
|
||||||
Heresy: {Heresy: 3, Sect: 2, Schism: 1, Dissenters: 1, Circle: 1, Brotherhood: 1, Society: 1, Iconoclasm: 1, Dissent: 1, Apostates: 1}
|
Heresy: {Heresy: 3, Sect: 2, Apostates: 1, Brotherhood: 1, Circle: 1, Dissent: 1, Dissenters: 1, Iconoclasm: 1, Schism: 1, Society: 1}
|
||||||
};
|
};
|
||||||
|
|
||||||
const generate = function () {
|
const generate = function () {
|
||||||
|
|
|
||||||
|
|
@ -23,10 +23,14 @@ window.Rivers = (function () {
|
||||||
resolveDepressions(h);
|
resolveDepressions(h);
|
||||||
drainWater();
|
drainWater();
|
||||||
defineRivers();
|
defineRivers();
|
||||||
|
|
||||||
calculateConfluenceFlux();
|
calculateConfluenceFlux();
|
||||||
Lakes.cleanupLakeData();
|
Lakes.cleanupLakeData();
|
||||||
|
|
||||||
if (allowErosion) cells.h = Uint8Array.from(h); // apply changed heights as basic one
|
if (allowErosion) {
|
||||||
|
cells.h = Uint8Array.from(h); // apply gradient
|
||||||
|
downcutRivers(); // downcut river beds
|
||||||
|
}
|
||||||
|
|
||||||
TIME && console.timeEnd("generateRivers");
|
TIME && console.timeEnd("generateRivers");
|
||||||
|
|
||||||
|
|
@ -34,6 +38,8 @@ window.Rivers = (function () {
|
||||||
const pixel2 = distanceScale * distanceScale
|
const pixel2 = distanceScale * distanceScale
|
||||||
//const MIN_FLUX_TO_FORM_RIVER = 10 * distanceScale;
|
//const MIN_FLUX_TO_FORM_RIVER = 10 * distanceScale;
|
||||||
const MIN_FLUX_TO_FORM_RIVER = 30;
|
const MIN_FLUX_TO_FORM_RIVER = 30;
|
||||||
|
const cellsNumberModifier = (pointsInput.dataset.cells / 10000) ** 0.25;
|
||||||
|
|
||||||
const prec = grid.cells.prec;
|
const prec = grid.cells.prec;
|
||||||
// const area = c => pack.cells.area[c] * pixel2;
|
// const area = c => pack.cells.area[c] * pixel2;
|
||||||
const area = pack.cells.area;
|
const area = pack.cells.area;
|
||||||
|
|
@ -41,7 +47,7 @@ window.Rivers = (function () {
|
||||||
const lakeOutCells = Lakes.setClimateData(h);
|
const lakeOutCells = Lakes.setClimateData(h);
|
||||||
|
|
||||||
land.forEach(function (i) {
|
land.forEach(function (i) {
|
||||||
cells.fl[i] += (prec[cells.g[i]] * area[i]) / 100; // add flux from precipitation
|
cells.fl[i] += prec[cells.g[i]] / cellsNumberModifier; // add flux from precipitation
|
||||||
|
|
||||||
// create lake outlet if lake is not in deep depression and flux > evaporation
|
// create lake outlet if lake is not in deep depression and flux > evaporation
|
||||||
const lakes = lakeOutCells[i] ? features.filter(feature => i === feature.outCell && feature.flux > feature.evaporation) : [];
|
const lakes = lakeOutCells[i] ? features.filter(feature => i === feature.outCell && feature.flux > feature.evaporation) : [];
|
||||||
|
|
@ -93,6 +99,15 @@ window.Rivers = (function () {
|
||||||
// cells is depressed
|
// cells is depressed
|
||||||
if (h[i] <= h[min]) return;
|
if (h[i] <= h[min]) return;
|
||||||
|
|
||||||
|
// debug
|
||||||
|
// .append("line")
|
||||||
|
// .attr("x1", pack.cells.p[i][0])
|
||||||
|
// .attr("y1", pack.cells.p[i][1])
|
||||||
|
// .attr("x2", pack.cells.p[min][0])
|
||||||
|
// .attr("y2", pack.cells.p[min][1])
|
||||||
|
// .attr("stroke", "#333")
|
||||||
|
// .attr("stroke-width", 0.2);
|
||||||
|
|
||||||
if (cells.fl[i] < MIN_FLUX_TO_FORM_RIVER) {
|
if (cells.fl[i] < MIN_FLUX_TO_FORM_RIVER) {
|
||||||
// flux is too small to operate as a river
|
// flux is too small to operate as a river
|
||||||
if (h[min] >= 20) cells.fl[min] += cells.fl[i];
|
if (h[min] >= 20) cells.fl[min] += cells.fl[i];
|
||||||
|
|
@ -152,6 +167,9 @@ window.Rivers = (function () {
|
||||||
cells.conf = new Uint16Array(cells.i.length);
|
cells.conf = new Uint16Array(cells.i.length);
|
||||||
pack.rivers = [];
|
pack.rivers = [];
|
||||||
|
|
||||||
|
const defaultWidthFactor = rn(1 / (pointsInput.dataset.cells / 10000) ** 0.25, 2);
|
||||||
|
const mainStemWidthFactor = defaultWidthFactor * 1.2;
|
||||||
|
|
||||||
for (const key in riversData) {
|
for (const key in riversData) {
|
||||||
const riverCells = riversData[key];
|
const riverCells = riversData[key];
|
||||||
if (riverCells.length < 3) continue; // exclude tiny rivers
|
if (riverCells.length < 3) continue; // exclude tiny rivers
|
||||||
|
|
@ -169,7 +187,7 @@ window.Rivers = (function () {
|
||||||
const mouth = riverCells[riverCells.length - 2];
|
const mouth = riverCells[riverCells.length - 2];
|
||||||
const parent = riverParents[key] || 0;
|
const parent = riverParents[key] || 0;
|
||||||
|
|
||||||
const widthFactor = (!parent || parent === riverId ? 1.2 : 1);
|
const widthFactor = !parent || parent === riverId ? mainStemWidthFactor : defaultWidthFactor;
|
||||||
const meanderedPoints = addMeandering(riverCells);
|
const meanderedPoints = addMeandering(riverCells);
|
||||||
const discharge = cells.fl[mouth]; // m3 in second
|
const discharge = cells.fl[mouth]; // m3 in second
|
||||||
const length = getApproximateLength(meanderedPoints);
|
const length = getApproximateLength(meanderedPoints);
|
||||||
|
|
@ -179,6 +197,22 @@ window.Rivers = (function () {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function downcutRivers() {
|
||||||
|
const MAX_DOWNCUT = 5;
|
||||||
|
|
||||||
|
for (const i of pack.cells.i) {
|
||||||
|
if (cells.h[i] < 35) continue; // don't donwcut lowlands
|
||||||
|
if (!cells.fl[i]) continue;
|
||||||
|
|
||||||
|
const higherCells = cells.c[i].filter(c => cells.h[c] > cells.h[i]);
|
||||||
|
const higherFlux = higherCells.reduce((acc, c) => acc + cells.fl[c], 0) / higherCells.length;
|
||||||
|
if (!higherFlux) continue;
|
||||||
|
|
||||||
|
const downcut = Math.floor(cells.fl[i] / higherFlux);
|
||||||
|
if (downcut) cells.h[i] -= Math.min(downcut, MAX_DOWNCUT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function calculateConfluenceFlux() {
|
function calculateConfluenceFlux() {
|
||||||
for (const i of cells.i) {
|
for (const i of cells.i) {
|
||||||
if (!cells.conf[i]) continue;
|
if (!cells.conf[i]) continue;
|
||||||
|
|
@ -347,14 +381,14 @@ window.Rivers = (function () {
|
||||||
const LENGTH_PROGRESSION = [1, 1, 2, 3, 5, 8, 13, 21, 34].map(n => n / LENGTH_FACTOR);
|
const LENGTH_PROGRESSION = [1, 1, 2, 3, 5, 8, 13, 21, 34].map(n => n / LENGTH_FACTOR);
|
||||||
const MAX_PROGRESSION = last(LENGTH_PROGRESSION);
|
const MAX_PROGRESSION = last(LENGTH_PROGRESSION);
|
||||||
|
|
||||||
const getOffset = (flux, pointNumber, widthFactor = 1, startingWidth = 0) => {
|
const getOffset = (flux, pointNumber, widthFactor, startingWidth = 0) => {
|
||||||
const fluxWidth = Math.min(flux ** 0.9 / FLUX_FACTOR, MAX_FLUX_WIDTH);
|
const fluxWidth = Math.min(flux ** 0.9 / FLUX_FACTOR, MAX_FLUX_WIDTH);
|
||||||
const lengthWidth = pointNumber * STEP_WIDTH + (LENGTH_PROGRESSION[pointNumber] || MAX_PROGRESSION);
|
const lengthWidth = pointNumber * STEP_WIDTH + (LENGTH_PROGRESSION[pointNumber] || MAX_PROGRESSION);
|
||||||
return widthFactor * (lengthWidth + fluxWidth) + startingWidth;
|
return widthFactor * (lengthWidth + fluxWidth) + startingWidth;
|
||||||
};
|
};
|
||||||
|
|
||||||
// build polygon from a list of points and calculated offset (width)
|
// build polygon from a list of points and calculated offset (width)
|
||||||
const getRiverPath = function (points, widthFactor = 1, startingWidth = 0) {
|
const getRiverPath = function (points, widthFactor, startingWidth = 0) {
|
||||||
const riverPointsLeft = [];
|
const riverPointsLeft = [];
|
||||||
const riverPointsRight = [];
|
const riverPointsRight = [];
|
||||||
|
|
||||||
|
|
@ -447,5 +481,20 @@ window.Rivers = (function () {
|
||||||
return getBasin(parent);
|
return getBasin(parent);
|
||||||
};
|
};
|
||||||
|
|
||||||
return {generate, alterHeights, resolveDepressions, addMeandering, getRiverPath, specify, getName, getType, getBasin, getWidth, getOffset, getApproximateLength, getRiverPoints, remove};
|
return {
|
||||||
|
generate,
|
||||||
|
alterHeights,
|
||||||
|
resolveDepressions,
|
||||||
|
addMeandering,
|
||||||
|
getRiverPath,
|
||||||
|
specify,
|
||||||
|
getName,
|
||||||
|
getType,
|
||||||
|
getBasin,
|
||||||
|
getWidth,
|
||||||
|
getOffset,
|
||||||
|
getApproximateLength,
|
||||||
|
getRiverPoints,
|
||||||
|
remove
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@ function editBurg(id) {
|
||||||
burgBody.querySelectorAll(".burgFeature").forEach(el => el.addEventListener("click", toggleFeature));
|
burgBody.querySelectorAll(".burgFeature").forEach(el => el.addEventListener("click", toggleFeature));
|
||||||
document.getElementById("mfcgBurgSeed").addEventListener("change", changeSeed);
|
document.getElementById("mfcgBurgSeed").addEventListener("change", changeSeed);
|
||||||
document.getElementById("regenerateMFCGBurgSeed").addEventListener("click", randomizeSeed);
|
document.getElementById("regenerateMFCGBurgSeed").addEventListener("click", randomizeSeed);
|
||||||
|
document.getElementById("addCustomMFCGBurgLink").addEventListener("click", addCustomMfcgLink);
|
||||||
|
|
||||||
document.getElementById("burgStyleShow").addEventListener("click", showStyleSection);
|
document.getElementById("burgStyleShow").addEventListener("click", showStyleSection);
|
||||||
document.getElementById("burgStyleHide").addEventListener("click", hideStyleSection);
|
document.getElementById("burgStyleHide").addEventListener("click", hideStyleSection);
|
||||||
|
|
@ -112,7 +113,13 @@ function editBurg(id) {
|
||||||
if (options.showMFCGMap) {
|
if (options.showMFCGMap) {
|
||||||
document.getElementById("mfcgPreviewSection").style.display = "block";
|
document.getElementById("mfcgPreviewSection").style.display = "block";
|
||||||
updateMFCGFrame(b);
|
updateMFCGFrame(b);
|
||||||
|
|
||||||
|
if (b.link) {
|
||||||
|
document.getElementById("mfcgBurgSeedSection").style.display = "none";
|
||||||
|
} else {
|
||||||
|
document.getElementById("mfcgBurgSeedSection").style.display = "inline-block";
|
||||||
document.getElementById("mfcgBurgSeed").value = getBurgSeed(b);
|
document.getElementById("mfcgBurgSeed").value = getBurgSeed(b);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
document.getElementById("mfcgPreviewSection").style.display = "none";
|
document.getElementById("mfcgPreviewSection").style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
@ -347,22 +354,25 @@ function editBurg(id) {
|
||||||
|
|
||||||
function toggleFeature() {
|
function toggleFeature() {
|
||||||
const id = +elSelected.attr("data-id");
|
const id = +elSelected.attr("data-id");
|
||||||
const b = pack.burgs[id];
|
const burg = pack.burgs[id];
|
||||||
const feature = this.dataset.feature;
|
const feature = this.dataset.feature;
|
||||||
const turnOn = this.classList.contains("inactive");
|
const turnOn = this.classList.contains("inactive");
|
||||||
if (feature === "port") togglePort(id);
|
if (feature === "port") togglePort(id);
|
||||||
else if (feature === "capital") toggleCapital(id);
|
else if (feature === "capital") toggleCapital(id);
|
||||||
else b[feature] = +turnOn;
|
else burg[feature] = +turnOn;
|
||||||
if (b[feature]) this.classList.remove("inactive");
|
if (burg[feature]) this.classList.remove("inactive");
|
||||||
else if (!b[feature]) this.classList.add("inactive");
|
else if (!burg[feature]) this.classList.add("inactive");
|
||||||
|
|
||||||
if (b.port) document.getElementById("burgEditAnchorStyle").style.display = "inline-block";
|
if (burg.port) document.getElementById("burgEditAnchorStyle").style.display = "inline-block";
|
||||||
else document.getElementById("burgEditAnchorStyle").style.display = "none";
|
else document.getElementById("burgEditAnchorStyle").style.display = "none";
|
||||||
|
updateMFCGFrame(burg);
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleBurgLockButton() {
|
function toggleBurgLockButton() {
|
||||||
const id = +elSelected.attr("data-id");
|
const id = +elSelected.attr("data-id");
|
||||||
toggleBurgLock(id);
|
const burg = pack.burgs[id];
|
||||||
|
burg.lock = !burg.lock;
|
||||||
|
|
||||||
updateBurgLockIcon();
|
updateBurgLockIcon();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -405,7 +415,7 @@ function editBurg(id) {
|
||||||
|
|
||||||
function updateMFCGFrame(burg) {
|
function updateMFCGFrame(burg) {
|
||||||
const mfcgURL = getMFCGlink(burg);
|
const mfcgURL = getMFCGlink(burg);
|
||||||
document.getElementById("mfcgPreview").setAttribute("src", mfcgURL);
|
document.getElementById("mfcgPreview").setAttribute("src", mfcgURL + "&preview=1");
|
||||||
document.getElementById("mfcgLink").setAttribute("href", mfcgURL);
|
document.getElementById("mfcgLink").setAttribute("href", mfcgURL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -426,6 +436,17 @@ function editBurg(id) {
|
||||||
document.getElementById("mfcgBurgSeed").value = burgSeed;
|
document.getElementById("mfcgBurgSeed").value = burgSeed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addCustomMfcgLink() {
|
||||||
|
const id = +elSelected.attr("data-id");
|
||||||
|
const burg = pack.burgs[id];
|
||||||
|
const message = "Enter custom link to the burg map. It can be a link to Medieval Fantasy City Generator or other tool. Keep empty to use MFCG seed";
|
||||||
|
prompt(message, {default: burg.link || "", required: false}, link => {
|
||||||
|
if (link) burg.link = link;
|
||||||
|
else delete burg.link;
|
||||||
|
updateMFCGFrame(burg);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function openEmblemEdit() {
|
function openEmblemEdit() {
|
||||||
const id = +elSelected.attr("data-id"),
|
const id = +elSelected.attr("data-id"),
|
||||||
burg = pack.burgs[id];
|
burg = pack.burgs[id];
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ function overviewBurgs() {
|
||||||
|
|
||||||
const body = document.getElementById("burgsBody");
|
const body = document.getElementById("burgsBody");
|
||||||
updateFilter();
|
updateFilter();
|
||||||
|
updateLockAllIcon();
|
||||||
burgsOverviewAddLines();
|
burgsOverviewAddLines();
|
||||||
$("#burgsOverview").dialog();
|
$("#burgsOverview").dialog();
|
||||||
|
|
||||||
|
|
@ -33,6 +34,7 @@ function overviewBurgs() {
|
||||||
document.getElementById("burgsListToLoad").addEventListener("change", function () {
|
document.getElementById("burgsListToLoad").addEventListener("change", function () {
|
||||||
uploadFile(this, importBurgNames);
|
uploadFile(this, importBurgNames);
|
||||||
});
|
});
|
||||||
|
document.getElementById("burgsLockAll").addEventListener("click", toggleLockAll);
|
||||||
document.getElementById("burgsRemoveAll").addEventListener("click", triggerAllBurgsRemove);
|
document.getElementById("burgsRemoveAll").addEventListener("click", triggerAllBurgsRemove);
|
||||||
document.getElementById("burgsInvertLock").addEventListener("click", invertLock);
|
document.getElementById("burgsInvertLock").addEventListener("click", invertLock);
|
||||||
|
|
||||||
|
|
@ -87,7 +89,7 @@ function overviewBurgs() {
|
||||||
<input data-tip="Burg name. Click and type to change" class="burgName" value="${b.name}" autocorrect="off" spellcheck="false">
|
<input data-tip="Burg name. Click and type to change" class="burgName" value="${b.name}" autocorrect="off" spellcheck="false">
|
||||||
<input data-tip="Burg province" class="burgState" value="${province}" disabled>
|
<input data-tip="Burg province" class="burgState" value="${province}" disabled>
|
||||||
<input data-tip="Burg state" class="burgState" value="${state}" disabled>
|
<input data-tip="Burg state" class="burgState" value="${state}" disabled>
|
||||||
<select data-tip="Dominant culture. Click to change burg culture (to change cell cultrure use Cultures Editor)" class="stateCulture">${getCultureOptions(
|
<select data-tip="Dominant culture. Click to change burg culture (to change cell culture use Cultures Editor)" class="stateCulture">${getCultureOptions(
|
||||||
b.culture
|
b.culture
|
||||||
)}</select>
|
)}</select>
|
||||||
<span data-tip="Burg population" class="icon-male"></span>
|
<span data-tip="Burg population" class="icon-male"></span>
|
||||||
|
|
@ -195,8 +197,11 @@ function overviewBurgs() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleBurgLockStatus() {
|
function toggleBurgLockStatus() {
|
||||||
const burg = +this.parentNode.dataset.id;
|
const burgId = +this.parentNode.dataset.id;
|
||||||
toggleBurgLock(burg);
|
|
||||||
|
const burg = pack.burgs[burgId];
|
||||||
|
burg.lock = !burg.lock;
|
||||||
|
|
||||||
if (this.classList.contains("icon-lock")) {
|
if (this.classList.contains("icon-lock")) {
|
||||||
this.classList.remove("icon-lock");
|
this.classList.remove("icon-lock");
|
||||||
this.classList.add("icon-lock-open");
|
this.classList.add("icon-lock-open");
|
||||||
|
|
@ -478,9 +483,9 @@ function overviewBurgs() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function renameBurgsInBulk() {
|
function renameBurgsInBulk() {
|
||||||
const message = `Download burgs list as a text file, make changes and re-upload the file.
|
alertMessage.innerHTML = `Download burgs list as a text file, make changes and re-upload the file.
|
||||||
|
Make sure the file is a plain text document with each name on its own line (the dilimiter is CRLF).
|
||||||
If you do not want to change the name, just leave it as is`;
|
If you do not want to change the name, just leave it as is`;
|
||||||
alertMessage.innerHTML = message;
|
|
||||||
|
|
||||||
$("#alert").dialog({
|
$("#alert").dialog({
|
||||||
title: "Burgs bulk renaming",
|
title: "Burgs bulk renaming",
|
||||||
|
|
@ -562,4 +567,21 @@ function overviewBurgs() {
|
||||||
pack.burgs = pack.burgs.map(burg => ({...burg, lock: !burg.lock}));
|
pack.burgs = pack.burgs.map(burg => ({...burg, lock: !burg.lock}));
|
||||||
burgsOverviewAddLines();
|
burgsOverviewAddLines();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleLockAll() {
|
||||||
|
const activeBurgs = pack.burgs.filter(b => b.i && !b.removed);
|
||||||
|
const allLocked = activeBurgs.every(burg => burg.lock);
|
||||||
|
|
||||||
|
pack.burgs.forEach(burg => {
|
||||||
|
burg.lock = !allLocked;
|
||||||
|
});
|
||||||
|
|
||||||
|
burgsOverviewAddLines();
|
||||||
|
document.getElementById("burgsLockAll").className = allLocked ? "icon-lock" : "icon-lock-open";
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateLockAllIcon() {
|
||||||
|
const allLocked = pack.burgs.every(({lock, i, removed}) => lock || !i || removed);
|
||||||
|
document.getElementById("burgsLockAll").className = allLocked ? "icon-lock-open" : "icon-lock";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,9 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
function editDiplomacy() {
|
function editDiplomacy() {
|
||||||
if (customization) return;
|
if (customization) return;
|
||||||
if (pack.states.filter(s => s.i && !s.removed).length < 2) {
|
if (pack.states.filter(s => s.i && !s.removed).length < 2) return tip("There should be at least 2 states to edit the diplomacy", false, "error");
|
||||||
tip("There should be at least 2 states to edit the diplomacy", false, "error");
|
|
||||||
return;
|
const body = document.getElementById("diplomacyBodySection");
|
||||||
}
|
|
||||||
|
|
||||||
closeDialogs("#diplomacyEditor, .stable");
|
closeDialogs("#diplomacyEditor, .stable");
|
||||||
if (!layerIsOn("toggleStates")) toggleStates();
|
if (!layerIsOn("toggleStates")) toggleStates();
|
||||||
|
|
@ -14,21 +13,29 @@ function editDiplomacy() {
|
||||||
if (layerIsOn("toggleBiomes")) toggleBiomes();
|
if (layerIsOn("toggleBiomes")) toggleBiomes();
|
||||||
if (layerIsOn("toggleReligions")) toggleReligions();
|
if (layerIsOn("toggleReligions")) toggleReligions();
|
||||||
|
|
||||||
const body = document.getElementById("diplomacyBodySection");
|
const relations = {
|
||||||
const statuses = ["Ally", "Friendly", "Neutral", "Suspicion", "Enemy", "Unknown", "Rival", "Vassal", "Suzerain"];
|
Ally: {inText: "is an ally of", color: "#00b300", tip: "Allies formed a defensive pact and protect each other in case of third party aggression"},
|
||||||
const description = [" is an ally of ", " is friendly to ", " is neutral to ", " is suspicious of ",
|
Friendly: {inText: "is friendly to", color: "#d4f8aa", tip: "State is friendly to anouther state when they share some common interests"},
|
||||||
" is at war with ", " does not know about ", " is a rival of ", " is a vassal of ", " is suzerain to "];
|
Neutral: {inText: "is neutral to", color: "#edeee8", tip: "Neutral means states relations are neither positive nor negative"},
|
||||||
const colors = ["#00b300", "#d4f8aa", "#edeee8", "#eeafaa", "#e64b40", "#a9a9a9", "#ad5a1f", "#87CEFA", "#00008B"];
|
Suspicion: {inText: "is suspicious of", color: "#eeafaa", tip: "Suspicion means state has a cautious distrust of another state"},
|
||||||
refreshDiplomacyEditor();
|
Enemy: {inText: "is at war with", color: "#e64b40", tip: "Enemies are states at war with each other"},
|
||||||
|
Unknown: {inText: "does not know about", color: "#a9a9a9", tip: "Relations are unknown if states do not have enough information about each other"},
|
||||||
|
Rival: {inText: "is a rival of", color: "#ad5a1f", tip: "Rivalry is a state of competing for dominance in the region"},
|
||||||
|
Vassal: {inText: "is a vassal of", color: "#87CEFA", tip: "Vassal is a state having obligation to its suzerain"},
|
||||||
|
Suzerain: {inText: "is suzerain to", color: "#00008B", tip: "Suzerain is a state having some control over its vassals"}
|
||||||
|
};
|
||||||
|
|
||||||
tip("Click on a state to see its diplomatic relations", false, "warning");
|
refreshDiplomacyEditor();
|
||||||
viewbox.style("cursor", "crosshair").on("click", selectStateOnMapClick);
|
viewbox.style("cursor", "crosshair").on("click", selectStateOnMapClick);
|
||||||
|
|
||||||
if (modules.editDiplomacy) return;
|
if (modules.editDiplomacy) return;
|
||||||
modules.editDiplomacy = true;
|
modules.editDiplomacy = true;
|
||||||
|
|
||||||
$("#diplomacyEditor").dialog({
|
$("#diplomacyEditor").dialog({
|
||||||
title: "Diplomacy Editor", resizable: false, width: fitContent(), close: closeDiplomacyEditor,
|
title: "Diplomacy Editor",
|
||||||
|
resizable: false,
|
||||||
|
width: fitContent(),
|
||||||
|
close: closeDiplomacyEditor,
|
||||||
position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}
|
position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -36,10 +43,30 @@ function editDiplomacy() {
|
||||||
document.getElementById("diplomacyEditorRefresh").addEventListener("click", refreshDiplomacyEditor);
|
document.getElementById("diplomacyEditorRefresh").addEventListener("click", refreshDiplomacyEditor);
|
||||||
document.getElementById("diplomacyEditStyle").addEventListener("click", () => editStyle("regions"));
|
document.getElementById("diplomacyEditStyle").addEventListener("click", () => editStyle("regions"));
|
||||||
document.getElementById("diplomacyRegenerate").addEventListener("click", regenerateRelations);
|
document.getElementById("diplomacyRegenerate").addEventListener("click", regenerateRelations);
|
||||||
document.getElementById("diplomacyMatrix").addEventListener("click", showRelationsMatrix);
|
document.getElementById("diplomacyReset").addEventListener("click", resetRelations);
|
||||||
|
document.getElementById("diplomacyShowMatrix").addEventListener("click", showRelationsMatrix);
|
||||||
document.getElementById("diplomacyHistory").addEventListener("click", showRelationsHistory);
|
document.getElementById("diplomacyHistory").addEventListener("click", showRelationsHistory);
|
||||||
document.getElementById("diplomacyExport").addEventListener("click", downloadDiplomacyData);
|
document.getElementById("diplomacyExport").addEventListener("click", downloadDiplomacyData);
|
||||||
document.getElementById("diplomacySelect").addEventListener("mouseup", diplomacyChangeRelations);
|
|
||||||
|
body.addEventListener("click", function (ev) {
|
||||||
|
const el = ev.target;
|
||||||
|
if (el.parentElement.classList.contains("Self")) return;
|
||||||
|
|
||||||
|
if (el.classList.contains("changeRelations")) {
|
||||||
|
const line = el.parentElement;
|
||||||
|
const subjectId = +line.dataset.id;
|
||||||
|
const objectId = +body.querySelector("div.Self").dataset.id;
|
||||||
|
const currentRelation = line.dataset.relations;
|
||||||
|
|
||||||
|
selectRelation(subjectId, objectId, currentRelation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// select state of clicked line
|
||||||
|
body.querySelector("div.Self").classList.remove("Self");
|
||||||
|
el.parentElement.classList.add("Self");
|
||||||
|
refreshDiplomacyEditor();
|
||||||
|
});
|
||||||
|
|
||||||
function refreshDiplomacyEditor() {
|
function refreshDiplomacyEditor() {
|
||||||
diplomacyEditorAddLines();
|
diplomacyEditorAddLines();
|
||||||
|
|
@ -50,33 +77,36 @@ function editDiplomacy() {
|
||||||
function diplomacyEditorAddLines() {
|
function diplomacyEditorAddLines() {
|
||||||
const states = pack.states;
|
const states = pack.states;
|
||||||
const selectedLine = body.querySelector("div.Self");
|
const selectedLine = body.querySelector("div.Self");
|
||||||
const sel = selectedLine ? +selectedLine.dataset.id : states.find(s => s.i && !s.removed).i;
|
const selectedId = selectedLine ? +selectedLine.dataset.id : states.find(s => s.i && !s.removed).i;
|
||||||
const selName = states[sel].fullName;
|
const selectedName = states[selectedId].name;
|
||||||
diplomacySelect.style.display = "none";
|
|
||||||
|
|
||||||
COArenderer.trigger("stateCOA"+sel, states[sel].coa);
|
COArenderer.trigger("stateCOA" + selectedId, states[selectedId].coa);
|
||||||
let lines = `<div class="states Self" data-id=${sel} data-tip="List below shows relations to ${selName}">
|
let lines = `<div class="states Self" data-id=${selectedId} data-tip="List below shows relations to ${selectedName}">
|
||||||
<div style="width: max-content">${selName}</div>
|
<div style="width: max-content">${states[selectedId].fullName}</div>
|
||||||
<svg class="coaIcon" viewBox="0 0 200 200"><use href="#stateCOA${sel}"></use></svg>
|
<svg class="coaIcon" viewBox="0 0 200 200"><use href="#stateCOA${selectedId}"></use></svg>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
for (const s of states) {
|
for (const state of states) {
|
||||||
if (!s.i || s.removed || s.i === sel) continue;
|
if (!state.i || state.removed || state.i === selectedId) continue;
|
||||||
const relation = s.diplomacy[sel];
|
const relation = state.diplomacy[selectedId];
|
||||||
const index = statuses.indexOf(relation);
|
const {color, inText} = relations[relation];
|
||||||
const color = colors[index];
|
|
||||||
const tip = s.fullName + description[index] + selName;
|
|
||||||
const tipSelect = `${tip}. Click to see relations to ${s.name}`;
|
|
||||||
const tipChange = `${tip}. Click to change relations to ${selName}`;
|
|
||||||
COArenderer.trigger("stateCOA"+s.i, s.coa);
|
|
||||||
|
|
||||||
lines += `<div class="states" data-id=${s.i} data-name="${s.fullName}" data-relations="${relation}">
|
const tip = `${state.name} ${inText} ${selectedName}`;
|
||||||
<svg data-tip="${tipSelect}" class="coaIcon" viewBox="0 0 200 200"><use href="#stateCOA${s.i}"></use></svg>
|
const tipSelect = `${tip}. Click to see relations to ${state.name}`;
|
||||||
<div data-tip="${tipSelect}" style="width:12em">${s.fullName}</div>
|
const tipChange = `Click to change relations. ${tip}`;
|
||||||
<svg data-tip="${tipChange}" width=".9em" height=".9em" style="margin-bottom:-1px" class="changeRelations">
|
|
||||||
<rect x="0" y="0" width="100%" height="100%" fill="${color}" class="fillRect pointer" style="pointer-events: none"></rect>
|
const name = state.fullName.length < 23 ? state.fullName : state.name;
|
||||||
|
COArenderer.trigger("stateCOA" + state.i, state.coa);
|
||||||
|
|
||||||
|
lines += `<div class="states" data-id=${state.i} data-name="${name}" data-relations="${relation}">
|
||||||
|
<svg data-tip="${tipSelect}" class="coaIcon" viewBox="0 0 200 200"><use href="#stateCOA${state.i}"></use></svg>
|
||||||
|
<div data-tip="${tipSelect}" style="width: 12em">${name}</div>
|
||||||
|
<div data-tip="${tipChange}" class="changeRelations pointer" style="width: 6em">
|
||||||
|
<svg width=".9em" height=".9em" style="margin-bottom:-1px">
|
||||||
|
<rect x="0" y="0" width="100%" height="100%" fill="${color}" class="fillRect"></rect>
|
||||||
</svg>
|
</svg>
|
||||||
<input data-tip="${tipChange}" class="changeRelations diplomacyRelations" value="${relation}" readonly/>
|
${relation}
|
||||||
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
body.innerHTML = lines;
|
body.innerHTML = lines;
|
||||||
|
|
@ -84,8 +114,6 @@ function editDiplomacy() {
|
||||||
// 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)));
|
||||||
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseleave", ev => stateHighlightOff(ev)));
|
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseleave", ev => stateHighlightOff(ev)));
|
||||||
body.querySelectorAll("div.states").forEach(el => el.addEventListener("click", selectStateOnLineClick));
|
|
||||||
body.querySelectorAll(".changeRelations").forEach(el => el.addEventListener("click", toggleDiplomacySelect));
|
|
||||||
|
|
||||||
applySorting(diplomacyHeader);
|
applySorting(diplomacyHeader);
|
||||||
$("#diplomacyEditor").dialog();
|
$("#diplomacyEditor").dialog();
|
||||||
|
|
@ -97,13 +125,25 @@ function editDiplomacy() {
|
||||||
if (customization || !state) return;
|
if (customization || !state) return;
|
||||||
const d = regions.select("#state" + state).attr("d");
|
const d = regions.select("#state" + state).attr("d");
|
||||||
|
|
||||||
const path = debug.append("path").attr("class", "highlight").attr("d", d)
|
const path = debug
|
||||||
.attr("fill", "none").attr("stroke", "red").attr("stroke-width", 1).attr("opacity", 1)
|
.append("path")
|
||||||
|
.attr("class", "highlight")
|
||||||
|
.attr("d", d)
|
||||||
|
.attr("fill", "none")
|
||||||
|
.attr("stroke", "red")
|
||||||
|
.attr("stroke-width", 1)
|
||||||
|
.attr("opacity", 1)
|
||||||
.attr("filter", "url(#blur1)");
|
.attr("filter", "url(#blur1)");
|
||||||
|
|
||||||
const l = path.node().getTotalLength(), dur = (l + 5000) / 2;
|
const l = path.node().getTotalLength(),
|
||||||
|
dur = (l + 5000) / 2;
|
||||||
const i = d3.interpolateString("0," + l, l + "," + l);
|
const i = d3.interpolateString("0," + l, l + "," + l);
|
||||||
path.transition().duration(dur).attrTween("stroke-dasharray", function() {return t => i(t)});
|
path
|
||||||
|
.transition()
|
||||||
|
.duration(dur)
|
||||||
|
.attrTween("stroke-dasharray", function () {
|
||||||
|
return t => i(t);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function stateHighlightOff(event) {
|
function stateHighlightOff(event) {
|
||||||
|
|
@ -121,19 +161,14 @@ function editDiplomacy() {
|
||||||
statesBody.selectAll("path").each(function () {
|
statesBody.selectAll("path").each(function () {
|
||||||
if (this.id.slice(0, 9) === "state-gap") return; // exclude state gap element
|
if (this.id.slice(0, 9) === "state-gap") return; // exclude state gap element
|
||||||
const id = +this.id.slice(5); // state id
|
const id = +this.id.slice(5); // state id
|
||||||
const index = statuses.indexOf(pack.states[id].diplomacy[sel]); // status index
|
|
||||||
const clr = index !== -1 ? colors[index] : "#4682b4"; // Self (bluish)
|
|
||||||
this.setAttribute("fill", clr);
|
|
||||||
statesBody.select("#state-gap"+id).attr("stroke", clr);
|
|
||||||
statesHalo.select("#state-border"+id).attr("stroke", d3.color(clr).darker().hex());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function selectStateOnLineClick() {
|
const relation = pack.states[id].diplomacy[sel];
|
||||||
if (this.classList.contains("Self")) return;
|
const color = relations[relation]?.color || "#4682b4";
|
||||||
body.querySelector("div.Self").classList.remove("Self");
|
|
||||||
this.classList.add("Self");
|
this.setAttribute("fill", color);
|
||||||
refreshDiplomacyEditor();
|
statesBody.select("#state-gap" + id).attr("stroke", color);
|
||||||
|
statesHalo.select("#state-border" + id).attr("stroke", d3.color(color).darker().hex());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function selectStateOnMapClick() {
|
function selectStateOnMapClick() {
|
||||||
|
|
@ -149,38 +184,59 @@ function editDiplomacy() {
|
||||||
refreshDiplomacyEditor();
|
refreshDiplomacyEditor();
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleDiplomacySelect(event) {
|
function selectRelation(subjectId, objectId, currentRelation) {
|
||||||
event.stopPropagation();
|
const states = pack.states;
|
||||||
const select = document.getElementById("diplomacySelect");
|
|
||||||
const show = select.style.display === "none";
|
const subject = states[subjectId];
|
||||||
if (!show) {select.style.display = "none"; return;}
|
const header = `<div style="margin-bottom: 0.3em"><svg class="coaIcon" viewBox="0 0 200 200"><use href="#stateCOA${subject.i}"></use></svg> <b>${subject.fullName}</b></div>`;
|
||||||
select.style.display = "block";
|
|
||||||
const input = event.target.closest("div").querySelector("input");
|
const options = Object.entries(relations)
|
||||||
select.style.left = input.getBoundingClientRect().left + "px";
|
.map(
|
||||||
select.style.top = input.getBoundingClientRect().bottom + "px";
|
([relation, {color, inText, tip}]) =>
|
||||||
body.dataset.state = event.target.closest("div.states").dataset.id;
|
`<div style="margin-block: 0.2em" data-tip="${tip}"><label class="pointer">
|
||||||
|
<input type="radio" name="relationSelect" value="${relation}" ${currentRelation === relation && "checked"} >
|
||||||
|
<svg width=".9em" height=".9em" style="margin-bottom:-1px">
|
||||||
|
<rect x="0" y="0" width="100%" height="100%" fill="${color}" class="fillRect" />
|
||||||
|
</svg>
|
||||||
|
${inText}
|
||||||
|
</label></div>`
|
||||||
|
)
|
||||||
|
.join("");
|
||||||
|
|
||||||
|
const object = states[objectId];
|
||||||
|
const footer = `<div style="margin-top: 0.7em"><svg class="coaIcon" viewBox="0 0 200 200"><use href="#stateCOA${object.i}"></use></svg> <b>${object.fullName}</b></div>`;
|
||||||
|
|
||||||
|
alertMessage.innerHTML = `<div style="overflow: hidden">${header} ${options} ${footer}</div>`;
|
||||||
|
|
||||||
|
$("#alert").dialog({
|
||||||
|
width: fitContent(),
|
||||||
|
title: `Change relations`,
|
||||||
|
buttons: {
|
||||||
|
Apply: function () {
|
||||||
|
const newRelation = document.querySelector('input[name="relationSelect"]:checked')?.value;
|
||||||
|
changeRelation(subjectId, objectId, currentRelation, newRelation);
|
||||||
|
$(this).dialog("close");
|
||||||
|
},
|
||||||
|
Cancel: function () {
|
||||||
|
$(this).dialog("close");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function diplomacyChangeRelations(event) {
|
function changeRelation(subjectId, objectId, oldRelation, newRelation) {
|
||||||
event.stopPropagation();
|
if (newRelation === oldRelation) return;
|
||||||
diplomacySelect.style.display = "none";
|
const states = pack.states;
|
||||||
const subject = +body.dataset.state;
|
const chronicle = states[0].diplomacy;
|
||||||
const rel = event.target.innerHTML;
|
|
||||||
|
|
||||||
const states = pack.states, chronicle = states[0].diplomacy;
|
const subjectName = states[subjectId].name;
|
||||||
const selectedLine = body.querySelector("div.Self");
|
const objectName = states[objectId].name;
|
||||||
const object = selectedLine ? +selectedLine.dataset.id : states.find(s => s.i && !s.removed).i;
|
|
||||||
if (!object) return;
|
|
||||||
const objectName = states[object].name; // object of relations change
|
|
||||||
const subjectName = states[subject].name; // subject of relations change - actor
|
|
||||||
|
|
||||||
const oldRel = states[subject].diplomacy[object];
|
states[subjectId].diplomacy[objectId] = newRelation;
|
||||||
if (rel === oldRel) return;
|
states[objectId].diplomacy[subjectId] = newRelation === "Vassal" ? "Suzerain" : newRelation === "Suzerain" ? "Vassal" : newRelation;
|
||||||
states[subject].diplomacy[object] = rel;
|
|
||||||
states[object].diplomacy[subject] = rel === "Vassal" ? "Suzerain" : rel === "Suzerain" ? "Vassal" : rel;
|
|
||||||
|
|
||||||
// update relation history
|
// update relation history
|
||||||
const change = () => [`Relations change`, `${subjectName}-${getAdjective(objectName)} relations changed to ${rel.toLowerCase()}`];
|
const change = () => [`Relations change`, `${subjectName}-${getAdjective(objectName)} relations changed to ${newRelation.toLowerCase()}`];
|
||||||
const ally = () => [`Defence pact`, `${subjectName} entered into defensive pact with ${objectName}`];
|
const ally = () => [`Defence pact`, `${subjectName} entered into defensive pact with ${objectName}`];
|
||||||
const vassal = () => [`Vassalization`, `${subjectName} became a vassal of ${objectName}`];
|
const vassal = () => [`Vassalization`, `${subjectName} became a vassal of ${objectName}`];
|
||||||
const suzerain = () => [`Vassalization`, `${subjectName} vassalized ${objectName}`];
|
const suzerain = () => [`Vassalization`, `${subjectName} vassalized ${objectName}`];
|
||||||
|
|
@ -189,24 +245,33 @@ function editDiplomacy() {
|
||||||
const war = () => [`War declaration`, `${subjectName} declared a war on its enemy ${objectName}`];
|
const war = () => [`War declaration`, `${subjectName} declared a war on its enemy ${objectName}`];
|
||||||
const peace = () => {
|
const peace = () => {
|
||||||
const treaty = `${subjectName} and ${objectName} agreed to cease fire and signed a peace treaty`;
|
const treaty = `${subjectName} and ${objectName} agreed to cease fire and signed a peace treaty`;
|
||||||
const changed = rel === "Ally" ? ally()
|
const changed =
|
||||||
: rel === "Vassal" ? vassal()
|
newRelation === "Ally"
|
||||||
: rel === "Suzerain" ? suzerain()
|
? ally()
|
||||||
: rel === "Unknown" ? unknown()
|
: newRelation === "Vassal"
|
||||||
|
? vassal()
|
||||||
|
: newRelation === "Suzerain"
|
||||||
|
? suzerain()
|
||||||
|
: newRelation === "Unknown"
|
||||||
|
? unknown()
|
||||||
: change();
|
: change();
|
||||||
return [`War termination`, treaty, changed[1]];
|
return [`War termination`, treaty, changed[1]];
|
||||||
}
|
};
|
||||||
|
|
||||||
if (oldRel === "Enemy") chronicle.push(peace()); else
|
if (oldRelation === "Enemy") chronicle.push(peace());
|
||||||
if (rel === "Enemy") chronicle.push(war()); else
|
else if (newRelation === "Enemy") chronicle.push(war());
|
||||||
if (rel === "Vassal") chronicle.push(vassal()); else
|
else if (newRelation === "Vassal") chronicle.push(vassal());
|
||||||
if (rel === "Suzerain") chronicle.push(suzerain()); else
|
else if (newRelation === "Suzerain") chronicle.push(suzerain());
|
||||||
if (rel === "Ally") chronicle.push(ally()); else
|
else if (newRelation === "Ally") chronicle.push(ally());
|
||||||
if (rel === "Unknown") chronicle.push(unknown()); else
|
else if (newRelation === "Unknown") chronicle.push(unknown());
|
||||||
if (rel === "Rival") chronicle.push(rival()); else
|
else if (newRelation === "Rival") chronicle.push(rival());
|
||||||
chronicle.push(change());
|
else chronicle.push(change());
|
||||||
|
|
||||||
refreshDiplomacyEditor();
|
refreshDiplomacyEditor();
|
||||||
|
if (diplomacyMatrix.offsetParent) {
|
||||||
|
document.getElementById("diplomacyMatrixBody").innerHTML = "";
|
||||||
|
showRelationsMatrix();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function regenerateRelations() {
|
function regenerateRelations() {
|
||||||
|
|
@ -214,28 +279,52 @@ function editDiplomacy() {
|
||||||
refreshDiplomacyEditor();
|
refreshDiplomacyEditor();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function resetRelations() {
|
||||||
|
const selectedId = +body.querySelector("div.Self")?.dataset?.id;
|
||||||
|
if (!selectedId) return;
|
||||||
|
const states = pack.states;
|
||||||
|
|
||||||
|
states[selectedId].diplomacy.forEach((relations, index) => {
|
||||||
|
if (relations !== "x") {
|
||||||
|
states[selectedId].diplomacy[index] = "Neutral";
|
||||||
|
states[index].diplomacy[selectedId] = "Neutral";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
refreshDiplomacyEditor();
|
||||||
|
}
|
||||||
|
|
||||||
function showRelationsHistory() {
|
function showRelationsHistory() {
|
||||||
const chronicle = pack.states[0].diplomacy;
|
const chronicle = pack.states[0].diplomacy;
|
||||||
if (!chronicle.length) {tip("Relations history is blank", false, "error"); return;}
|
if (!chronicle.length) return tip("Relations history is blank", false, "error");
|
||||||
|
|
||||||
let message = `<div autocorrect="off" spellcheck="false">`;
|
let message = `<div autocorrect="off" spellcheck="false">`;
|
||||||
chronicle.forEach((e, d) => {
|
chronicle.forEach((entry, d) => {
|
||||||
message += `<div>`;
|
message += `<div>`;
|
||||||
e.forEach((l, i) => message += `<div contenteditable="true" data-id="${d}-${i}"${i ? "" : " style='font-weight:bold'"}>${l}</div>`);
|
entry.forEach((l, i) => {
|
||||||
|
message += `<div contenteditable="true" data-id="${d}-${i}"${i ? "" : " style='font-weight:bold'"}>${l}</div>`;
|
||||||
|
});
|
||||||
message += `‍</div>`;
|
message += `‍</div>`;
|
||||||
});
|
});
|
||||||
alertMessage.innerHTML = message + `</div><i id="info-line">Type to edit. Press Enter to add a new line, empty the element to remove it</i>`;
|
alertMessage.innerHTML = message + `</div><div class="info-line">Type to edit. Press Enter to add a new line, empty the element to remove it</div>`;
|
||||||
alertMessage.querySelectorAll("div[contenteditable='true']").forEach(el => el.addEventListener("input", changeReliationsHistory));
|
alertMessage.querySelectorAll("div[contenteditable='true']").forEach(el => el.addEventListener("input", changeReliationsHistory));
|
||||||
|
|
||||||
$("#alert").dialog({title: "Relations history", position: {my: "center", at: "center", of: "svg"},
|
$("#alert").dialog({
|
||||||
|
title: "Relations history",
|
||||||
|
position: {my: "center", at: "center", of: "svg"},
|
||||||
buttons: {
|
buttons: {
|
||||||
Save: function () {
|
Save: function () {
|
||||||
const data = this.querySelector("div").innerText.split("\n").join("\r\n");
|
const data = this.querySelector("div").innerText.split("\n").join("\r\n");
|
||||||
const name = getFileName("Relations history") + ".txt";
|
const name = getFileName("Relations history") + ".txt";
|
||||||
downloadFile(data, name);
|
downloadFile(data, name);
|
||||||
},
|
},
|
||||||
Clear: function() {pack.states[0].diplomacy = []; $(this).dialog("close");},
|
Clear: function () {
|
||||||
Close: function() {$(this).dialog("close");}
|
pack.states[0].diplomacy = [];
|
||||||
|
$(this).dialog("close");
|
||||||
|
},
|
||||||
|
Close: function () {
|
||||||
|
$(this).dialog("close");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -251,22 +340,48 @@ function editDiplomacy() {
|
||||||
|
|
||||||
function showRelationsMatrix() {
|
function showRelationsMatrix() {
|
||||||
const states = pack.states.filter(s => s.i && !s.removed);
|
const states = pack.states.filter(s => s.i && !s.removed);
|
||||||
const valid = states.map(s => s.i);
|
const valid = states.map(state => state.i);
|
||||||
|
const diplomacyMatrixBody = document.getElementById("diplomacyMatrixBody");
|
||||||
|
|
||||||
let message = `<table class="matrix-table"><tr><th data-tip='‍'></th>`;
|
let table = `<table><thead><tr><th data-tip='‍'></th>`;
|
||||||
message += states.map(s => `<th data-tip='See relations to ${s.fullName}'>${s.name}</th>`).join("") + `</tr>`; // headers
|
table += states.map(state => `<th data-tip='Relations to ${state.fullName}'>${state.name}</th>`).join("") + `</tr>`;
|
||||||
states.forEach(s => {
|
table += `<tbody>`;
|
||||||
message += `<tr><th data-tip='See relations of ${s.fullName}'>${s.name}</th>` + s.diplomacy
|
|
||||||
.filter((v, i) => valid.includes(i)).map((r, i) => {
|
states.forEach(state => {
|
||||||
const desc = description[statuses.indexOf(r)];
|
table +=
|
||||||
const tip = desc ? s.fullName + desc + pack.states[valid[i]].fullName : '‍';
|
`<tr data-id=${state.i}><th data-tip='Relations of ${state.fullName}'>${state.name}</th>` +
|
||||||
return `<td data-tip='${tip}' class='${r}'>${r}</td>`
|
state.diplomacy
|
||||||
}).join("") + "</tr>";
|
.filter((v, i) => valid.includes(i))
|
||||||
|
.map((relation, index) => {
|
||||||
|
const relationObj = relations[relation];
|
||||||
|
if (!relationObj) return `<td class='${relation}'>${relation}</td>`;
|
||||||
|
|
||||||
|
const objectState = pack.states[valid[index]];
|
||||||
|
const tip = `${state.fullName} ${relationObj.inText} ${objectState.fullName}`;
|
||||||
|
return `<td data-id=${objectState.i} data-tip='${tip}' class='${relation}'>${relation}</td>`;
|
||||||
|
})
|
||||||
|
.join("") +
|
||||||
|
"</tr>";
|
||||||
});
|
});
|
||||||
message += `</table>`;
|
|
||||||
alertMessage.innerHTML = message;
|
|
||||||
|
|
||||||
$("#alert").dialog({title: "Relations matrix", width: fitContent(), position: {my: "center", at: "center", of: "svg"}, buttons: {}});
|
table += `</tbody></table>`;
|
||||||
|
diplomacyMatrixBody.innerHTML = table;
|
||||||
|
|
||||||
|
const tableEl = diplomacyMatrixBody.querySelector("table");
|
||||||
|
tableEl.addEventListener("click", function (event) {
|
||||||
|
const el = event.target;
|
||||||
|
if (el.tagName !== "TD") return;
|
||||||
|
|
||||||
|
const currentRelation = el.innerText;
|
||||||
|
if (!relations[currentRelation]) return;
|
||||||
|
|
||||||
|
const subjectId = +el.closest("tr")?.dataset?.id;
|
||||||
|
const objectId = +el?.dataset?.id;
|
||||||
|
|
||||||
|
selectRelation(subjectId, objectId, currentRelation);
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#diplomacyMatrix").dialog({title: "Relations matrix", position: {my: "center", at: "center", of: "svg"}, buttons: {}});
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadDiplomacyData() {
|
function downloadDiplomacyData() {
|
||||||
|
|
@ -288,7 +403,8 @@ function editDiplomacy() {
|
||||||
clearMainTip();
|
clearMainTip();
|
||||||
const selected = body.querySelector("div.Self");
|
const selected = body.querySelector("div.Self");
|
||||||
if (selected) selected.classList.remove("Self");
|
if (selected) selected.classList.remove("Self");
|
||||||
if (layerIsOn("toggleStates")) drawStates(); else toggleStates();
|
if (layerIsOn("toggleStates")) drawStates();
|
||||||
|
else toggleStates();
|
||||||
debug.selectAll(".highlight").remove();
|
debug.selectAll(".highlight").remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -265,41 +265,48 @@ function getBurgSeed(burg) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMFCGlink(burg) {
|
function getMFCGlink(burg) {
|
||||||
|
if (burg.link) return burg.link;
|
||||||
|
|
||||||
const {cells} = pack;
|
const {cells} = pack;
|
||||||
const {name, population, cell} = burg;
|
const {i, name, population: burgPopulation, cell} = burg;
|
||||||
const burgSeed = getBurgSeed(burg);
|
const seed = getBurgSeed(burg);
|
||||||
const sizeRaw = 2.13 * Math.pow((population * populationRate) / urbanDensity, 0.385);
|
|
||||||
|
const sizeRaw = 2.13 * Math.pow((burgPopulation * populationRate) / urbanDensity, 0.385);
|
||||||
const size = minmax(Math.ceil(sizeRaw), 6, 100);
|
const size = minmax(Math.ceil(sizeRaw), 6, 100);
|
||||||
const people = rn(population * populationRate * urbanization);
|
const population = rn(burgPopulation * populationRate * urbanization);
|
||||||
|
|
||||||
|
const river = cells.r[cell] ? 1 : 0;
|
||||||
|
const coast = Number(burg.port > 0);
|
||||||
|
const sea = coast && cells.haven[cell] ? getSeaDirections(cell) : null;
|
||||||
|
|
||||||
|
const biome = cells.biome[cell];
|
||||||
|
const arableBiomes = river ? [1, 2, 3, 4, 5, 6, 7, 8] : [5, 6, 7, 8];
|
||||||
|
const farms = +arableBiomes.includes(biome);
|
||||||
|
|
||||||
|
const citadel = +burg.citadel;
|
||||||
|
const urban_castle = +(citadel && each(2)(i));
|
||||||
|
|
||||||
const hub = +cells.road[cell] > 50;
|
const hub = +cells.road[cell] > 50;
|
||||||
const river = cells.r[cell] ? 1 : 0;
|
|
||||||
|
|
||||||
const coast = +burg.port;
|
|
||||||
const citadel = +burg.citadel;
|
|
||||||
const walls = +burg.walls;
|
const walls = +burg.walls;
|
||||||
const plaza = +burg.plaza;
|
const plaza = +burg.plaza;
|
||||||
const temple = +burg.temple;
|
const temple = +burg.temple;
|
||||||
const shanty = +burg.shanty;
|
const shantytown = +burg.shanty;
|
||||||
|
|
||||||
const sea = coast && cells.haven[cell] ? getSeaDirections(cell) : "";
|
|
||||||
function getSeaDirections(i) {
|
function getSeaDirections(i) {
|
||||||
const p1 = cells.p[i];
|
const p1 = cells.p[i];
|
||||||
const p2 = cells.p[cells.haven[i]];
|
const p2 = cells.p[cells.haven[i]];
|
||||||
let deg = (Math.atan2(p2[1] - p1[1], p2[0] - p1[0]) * 180) / Math.PI - 90;
|
let deg = (Math.atan2(p2[1] - p1[1], p2[0] - p1[0]) * 180) / Math.PI - 90;
|
||||||
if (deg < 0) deg += 360;
|
if (deg < 0) deg += 360;
|
||||||
const norm = rn(normalize(deg, 0, 360) * 2, 2); // 0 = south, 0.5 = west, 1 = north, 1.5 = east
|
return rn(normalize(deg, 0, 360) * 2, 2); // 0 = south, 0.5 = west, 1 = north, 1.5 = east
|
||||||
return "&sea=" + norm;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseURL = "https://watabou.github.io/city-generator/?random=0&continuous=0";
|
const parameters = {name, population, size, seed, river, coast, farms, citadel, urban_castle, hub, plaza, temple, walls, shantytown, gates: -1};
|
||||||
const url = `${baseURL}&name=${name}&population=${people}&size=${size}&seed=${burgSeed}&hub=${hub}&river=${river}&coast=${coast}&citadel=${citadel}&plaza=${plaza}&temple=${temple}&walls=${walls}&shantytown=${shanty}${sea}`;
|
const url = new URL("https://watabou.github.io/city-generator");
|
||||||
return url;
|
url.search = new URLSearchParams(parameters);
|
||||||
}
|
if (sea) url.searchParams.append("sea", sea);
|
||||||
|
|
||||||
function toggleBurgLock(burg) {
|
return url.toString();
|
||||||
const b = pack.burgs[burg];
|
|
||||||
b.lock = b.lock ? 0 : 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// draw legend box
|
// draw legend box
|
||||||
|
|
|
||||||
|
|
@ -460,32 +460,31 @@ function showInfo() {
|
||||||
const Discord = link("https://discordapp.com/invite/X7E84HU", "Discord");
|
const Discord = link("https://discordapp.com/invite/X7E84HU", "Discord");
|
||||||
const Reddit = link("https://www.reddit.com/r/FantasyMapGenerator", "Reddit");
|
const Reddit = link("https://www.reddit.com/r/FantasyMapGenerator", "Reddit");
|
||||||
const Patreon = link("https://www.patreon.com/azgaar", "Patreon");
|
const Patreon = link("https://www.patreon.com/azgaar", "Patreon");
|
||||||
const Trello = link("https://trello.com/b/7x832DG4/fantasy-map-generator", "Trello");
|
|
||||||
const Armoria = link("https://azgaar.github.io/Armoria", "Armoria");
|
const Armoria = link("https://azgaar.github.io/Armoria", "Armoria");
|
||||||
|
|
||||||
const QuickStart = link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Quick-Start-Tutorial", "Quick start tutorial");
|
const QuickStart = link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Quick-Start-Tutorial", "Quick start tutorial");
|
||||||
const QAA = link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Q&A", "Q&A page");
|
const QAA = link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Q&A", "Q&A page");
|
||||||
|
const VideoTutorial = link("https://youtube.com/playlist?list=PLtgiuDC8iVR2gIG8zMTRn7T_L0arl9h1C", "Video tutorial");
|
||||||
|
|
||||||
alertMessage.innerHTML = `
|
alertMessage.innerHTML = `
|
||||||
<b>Fantasy Map Generator</b> (FMG) is an open-source application, it means the code is published an anyone can use it.
|
<b>Fantasy Map Generator</b> (FMG) is a free open-source application.
|
||||||
In case of FMG is also means that you own all created maps and can use them as you wish, you can even sell them.
|
It means that you own all created maps and can use them as you wish.
|
||||||
|
|
||||||
<p>The development is supported by community, you can donate on ${Patreon}.
|
<p>The development is community-backed, you can donate on ${Patreon}.
|
||||||
You can also help creating overviews, tutorials and spreding the word about the Generator.</p>
|
You can also help creating overviews, tutorials and spreding the word about the Generator.</p>
|
||||||
|
|
||||||
<p>The best way to get help is to contact the community on ${Discord} and ${Reddit}.
|
<p>The best way to get help is to contact the community on ${Discord} and ${Reddit}.
|
||||||
Before asking questions, please check out the ${QuickStart} and the ${QAA}.</p>
|
Before asking questions, please check out the ${QuickStart}, the ${QAA}, and ${VideoTutorial}.</p>
|
||||||
|
|
||||||
<p>Track the development process on ${Trello}.</p>
|
<p>Check out our another project: ${Armoria} — heraldry generator and editor.</p>
|
||||||
|
|
||||||
<p>Check out our new project: ${Armoria}, heraldry generator and editor.</p>
|
|
||||||
|
|
||||||
<b>Links:</b>
|
|
||||||
<ul style="columns:2">
|
<ul style="columns:2">
|
||||||
<li>${link("https://github.com/Azgaar/Fantasy-Map-Generator", "GitHub repository")}</li>
|
<li>${link("https://github.com/Azgaar/Fantasy-Map-Generator", "GitHub repository")}</li>
|
||||||
<li>${link("https://github.com/Azgaar/Fantasy-Map-Generator/blob/master/LICENSE", "License")}</li>
|
<li>${link("https://github.com/Azgaar/Fantasy-Map-Generator/blob/master/LICENSE", "License")}</li>
|
||||||
<li>${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog", "Changelog")}</li>
|
<li>${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog", "Changelog")}</li>
|
||||||
<li>${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Hotkeys", "Hotkeys")}</li>
|
<li>${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Hotkeys", "Hotkeys")}</li>
|
||||||
|
<li>${link("https://trello.com/b/7x832DG4/fantasy-map-generator", "Devboard")}</li>
|
||||||
|
<li><a href="mailto:azgaar.fmg@yandex.by" target="_blank">Contact Azgaar</a></li>
|
||||||
</ul>`;
|
</ul>`;
|
||||||
|
|
||||||
$("#alert").dialog({
|
$("#alert").dialog({
|
||||||
|
|
|
||||||
|
|
@ -470,23 +470,26 @@ function togglePrec(event) {
|
||||||
|
|
||||||
function drawPrec() {
|
function drawPrec() {
|
||||||
prec.selectAll("circle").remove();
|
prec.selectAll("circle").remove();
|
||||||
const cells = grid.cells,
|
const {cells, points} = grid;
|
||||||
p = grid.points;
|
|
||||||
prec.style("display", "block");
|
prec.style("display", "block");
|
||||||
const show = d3.transition().duration(800).ease(d3.easeSinIn);
|
const show = d3.transition().duration(800).ease(d3.easeSinIn);
|
||||||
prec.selectAll("text").attr("opacity", 0).transition(show).attr("opacity", 1);
|
prec.selectAll("text").attr("opacity", 0).transition(show).attr("opacity", 1);
|
||||||
|
|
||||||
|
const cellsNumberModifier = (pointsInput.dataset.cells / 10000) ** 0.25;
|
||||||
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]);
|
||||||
|
const getRadius = prec => rn(Math.sqrt(prec / 4) / cellsNumberModifier, 2);
|
||||||
|
|
||||||
prec
|
prec
|
||||||
.selectAll("circle")
|
.selectAll("circle")
|
||||||
.data(data)
|
.data(data)
|
||||||
.enter()
|
.enter()
|
||||||
.append("circle")
|
.append("circle")
|
||||||
.attr("cx", d => p[d][0])
|
.attr("cx", d => points[d][0])
|
||||||
.attr("cy", d => p[d][1])
|
.attr("cy", d => points[d][1])
|
||||||
.attr("r", 0)
|
.attr("r", 0)
|
||||||
.transition(show)
|
.transition(show)
|
||||||
.attr("r", d => rn(Math.max(Math.sqrt(cells.prec[d] * 0.5), 0.8), 2));
|
.attr("r", d => getRadius(cells.prec[d]));
|
||||||
}
|
}
|
||||||
|
|
||||||
function togglePopulation(event) {
|
function togglePopulation(event) {
|
||||||
|
|
|
||||||
|
|
@ -17,18 +17,24 @@ function editNamesbase() {
|
||||||
document.getElementById("namesbaseMax").addEventListener("input", updateBaseMax);
|
document.getElementById("namesbaseMax").addEventListener("input", updateBaseMax);
|
||||||
document.getElementById("namesbaseDouble").addEventListener("input", updateBaseDublication);
|
document.getElementById("namesbaseDouble").addEventListener("input", updateBaseDublication);
|
||||||
document.getElementById("namesbaseAdd").addEventListener("click", namesbaseAdd);
|
document.getElementById("namesbaseAdd").addEventListener("click", namesbaseAdd);
|
||||||
document.getElementById("namesbaseAnalize").addEventListener("click", analizeNamesbase);
|
document.getElementById("namesbaseAnalyze").addEventListener("click", analyzeNamesbase);
|
||||||
document.getElementById("namesbaseDefault").addEventListener("click", namesbaseRestoreDefault);
|
document.getElementById("namesbaseDefault").addEventListener("click", namesbaseRestoreDefault);
|
||||||
document.getElementById("namesbaseDownload").addEventListener("click", namesbaseDownload);
|
document.getElementById("namesbaseDownload").addEventListener("click", namesbaseDownload);
|
||||||
document.getElementById("namesbaseUpload").addEventListener("click", () => namesbaseToLoad.click());
|
document.getElementById("namesbaseUpload").addEventListener("click", () => document.getElementById("namesbaseToLoad").click());
|
||||||
document.getElementById("namesbaseToLoad").addEventListener("change", function() {uploadFile(this, namesbaseUpload)});
|
document.getElementById("namesbaseToLoad").addEventListener("change", function () {
|
||||||
|
uploadFile(this, namesbaseUpload);
|
||||||
|
});
|
||||||
|
document.getElementById("namesbaseCA").addEventListener("click", () => {
|
||||||
|
openURL("https://cartographyassets.com/asset-category/specific-assets/azgaars-generator/namebases/");
|
||||||
|
});
|
||||||
document.getElementById("namesbaseSpeak").addEventListener("click", () => speak(namesbaseExamples.textContent));
|
document.getElementById("namesbaseSpeak").addEventListener("click", () => speak(namesbaseExamples.textContent));
|
||||||
|
|
||||||
createBasesList();
|
createBasesList();
|
||||||
updateInputs();
|
updateInputs();
|
||||||
|
|
||||||
$("#namesbaseEditor").dialog({
|
$("#namesbaseEditor").dialog({
|
||||||
title: "Namesbase Editor", width: "42.5em",
|
title: "Namesbase Editor",
|
||||||
|
width: "auto",
|
||||||
position: {my: "center", at: "center", of: "svg"}
|
position: {my: "center", at: "center", of: "svg"}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -40,7 +46,10 @@ function editNamesbase() {
|
||||||
|
|
||||||
function updateInputs() {
|
function updateInputs() {
|
||||||
const base = +document.getElementById("namesbaseSelect").value;
|
const base = +document.getElementById("namesbaseSelect").value;
|
||||||
if (!nameBases[base]) {tip(`Namesbase ${base} is not defined`, false, "error"); return;}
|
if (!nameBases[base]) {
|
||||||
|
tip(`Namesbase ${base} is not defined`, false, "error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
document.getElementById("namesbaseTextarea").value = nameBases[base].b;
|
document.getElementById("namesbaseTextarea").value = nameBases[base].b;
|
||||||
document.getElementById("namesbaseName").value = nameBases[base].name;
|
document.getElementById("namesbaseName").value = nameBases[base].name;
|
||||||
document.getElementById("namesbaseMin").value = nameBases[base].min;
|
document.getElementById("namesbaseMin").value = nameBases[base].min;
|
||||||
|
|
@ -84,13 +93,19 @@ function editNamesbase() {
|
||||||
|
|
||||||
function updateBaseMin() {
|
function updateBaseMin() {
|
||||||
const base = +document.getElementById("namesbaseSelect").value;
|
const base = +document.getElementById("namesbaseSelect").value;
|
||||||
if (+this.value > nameBases[base].max) {tip("Minimal length cannot be greater than maximal", false, "error"); return;}
|
if (+this.value > nameBases[base].max) {
|
||||||
|
tip("Minimal length cannot be greater than maximal", false, "error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
nameBases[base].min = +this.value;
|
nameBases[base].min = +this.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateBaseMax() {
|
function updateBaseMax() {
|
||||||
const base = +document.getElementById("namesbaseSelect").value;
|
const base = +document.getElementById("namesbaseSelect").value;
|
||||||
if (+this.value < nameBases[base].min) {tip("Maximal length should be greater than minimal", false, "error"); return;}
|
if (+this.value < nameBases[base].min) {
|
||||||
|
tip("Maximal length should be greater than minimal", false, "error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
nameBases[base].max = +this.value;
|
nameBases[base].max = +this.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -99,59 +114,70 @@ function editNamesbase() {
|
||||||
nameBases[base].d = this.value;
|
nameBases[base].d = this.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function analizeNamesbase() {
|
function analyzeNamesbase() {
|
||||||
const string = document.getElementById("namesbaseTextarea").value;
|
const namesSourceString = document.getElementById("namesbaseTextarea").value;
|
||||||
if (!string) {tip("Names data field should not be empty", false, "error"); return;}
|
const namesArray = namesSourceString.toLowerCase().split(",");
|
||||||
const base = string.toLowerCase();
|
const length = namesArray.length;
|
||||||
const array = base.split(",");
|
if (!namesSourceString || !length) return tip("Names data should not be empty", false, "error");
|
||||||
const l = array.length;
|
|
||||||
if (!l) {tip("Names data should not be empty", false, "error"); return;}
|
|
||||||
|
|
||||||
const wordsLength = array.map(n => n.length);
|
const chain = Names.calculateChain(namesSourceString);
|
||||||
const multi = rn(d3.mean(array.map(n => (n.match(/ /i)||[]).length)) * 100, 2);
|
const variety = rn(d3.mean(Object.values(chain).map(keyValue => keyValue.length)));
|
||||||
const geminate = array.map(name => name.match(/[^\w\s]|(.)(?=\1)/g)||[]).flat();
|
|
||||||
const doubled = ([...new Set(geminate)].filter(l => geminate.filter(d => d === l).length > 3)||["none"]).join("");
|
|
||||||
const chain = Names.calculateChain(string);
|
|
||||||
const depth = rn(d3.mean(Object.keys(chain).map(key => chain[key].filter(c => c !== " ").length)));
|
|
||||||
const nonLatin = (string.match(/[^\u0000-\u007f]/g)||["none"]).join("");
|
|
||||||
|
|
||||||
const lengthStat =
|
const wordsLength = namesArray.map(n => n.length);
|
||||||
l < 30 ? "<span style='color:red'>[not enough]</span>" :
|
|
||||||
l < 150 ? "<span style='color:darkred'>[low]</span>" :
|
|
||||||
l < 150 ? "<span style='color:orange'>[low]</span>" :
|
|
||||||
l < 400 ? "<span style='color:green'>[good]</span>" :
|
|
||||||
l < 600 ? "<span style='color:orange'>[overmuch]</span>" :
|
|
||||||
"<span style='color:darkred'>[overmuch]</span>";
|
|
||||||
|
|
||||||
const rangeStat =
|
const nonLatin = namesSourceString.match(/[^\u0000-\u007f]/g);
|
||||||
l < 10 ? "<span style='color:red'>[low]</span>" :
|
const nonBasicLatinChars = nonLatin
|
||||||
l < 15 ? "<span style='color:darkred'>[low]</span>" :
|
? unique(
|
||||||
l < 20 ? "<span style='color:orange'>[low]</span>" :
|
namesSourceString
|
||||||
"<span style='color:green'>[good]</span>";
|
.match(/[^\u0000-\u007f]/g)
|
||||||
|
.join("")
|
||||||
|
.toLowerCase()
|
||||||
|
).join("")
|
||||||
|
: "none";
|
||||||
|
|
||||||
const depthStat =
|
const geminate = namesArray.map(name => name.match(/[^\w\s]|(.)(?=\1)/g) || []).flat();
|
||||||
l < 15 ? "<span style='color:red'>[low]</span>" :
|
const doubled = unique(geminate).filter(char => geminate.filter(doudledChar => doudledChar === char).length > 3) || ["none"];
|
||||||
l < 20 ? "<span style='color:darkred'>[low]</span>" :
|
|
||||||
l < 25 ? "<span style='color:orange'>[low]</span>" :
|
const duplicates = unique(namesArray.filter((e, i, a) => a.indexOf(e) !== i)).join(", ") || "none";
|
||||||
"<span style='color:green'>[good]</span>";
|
const multiwordRate = d3.mean(namesArray.map(n => +n.includes(" ")));
|
||||||
|
|
||||||
|
const getLengthQuality = () => {
|
||||||
|
if (length < 30) return "<span data-tip='Namesbase contains < 30 names - not enough to generate reasonable data' style='color:red'>[not enough]</span>";
|
||||||
|
if (length < 100) return "<span data-tip='Namesbase contains < 100 names - not enough to generate good names' style='color:darkred'>[low]</span>";
|
||||||
|
if (length <= 400) return "<span data-tip='Namesbase contains a reasonable number of samples' style='color:green'>[good]</span>";
|
||||||
|
return "<span data-tip='Namesbase contains > 400 names. That is too much, try to reduce it to ~300 names' style='color:darkred'>[overmuch]</span>";
|
||||||
|
};
|
||||||
|
|
||||||
|
const getVarietyLevel = () => {
|
||||||
|
if (variety < 15) return "<span data-tip='Namesbase average variety < 15 - generated names will be too repetitive' style='color:red'>[low]</span>";
|
||||||
|
if (variety < 30) return "<span data-tip='Namesbase average variety < 30 - names can be too repetitive' style='color:orange'>[mean]</span>";
|
||||||
|
return "<span data-tip='Namesbase variety is good' style='color:green'>[good]</span>";
|
||||||
|
};
|
||||||
|
|
||||||
alertMessage.innerHTML = `<div style="line-height: 1.6em; max-width: 20em">
|
alertMessage.innerHTML = `<div style="line-height: 1.6em; max-width: 20em">
|
||||||
<div>Namesbase length: ${l} ${lengthStat}</div>
|
<div data-tip="Number of names provided">Namesbase length: ${length} ${getLengthQuality()}</div>
|
||||||
<div>Namesbase range: ${Object.keys(chain).length-1} ${rangeStat}</div>
|
<div data-tip="Average number of generation variants for each key in the chain">Namesbase variety: ${variety} ${getVarietyLevel()}</div>
|
||||||
<div>Namesbase depth: ${depth} ${depthStat}</div>
|
|
||||||
<div>Non-basic chars: ${nonLatin}</div>
|
|
||||||
<hr>
|
<hr>
|
||||||
<div>Min name length: ${d3.min(wordsLength)}</div>
|
<div data-tip="The shortest name length">Min name length: ${d3.min(wordsLength)}</div>
|
||||||
<div>Max name length: ${d3.max(wordsLength)}</div>
|
<div data-tip="The longest name length">Max name length: ${d3.max(wordsLength)}</div>
|
||||||
<div>Mean name length: ${rn(d3.mean(wordsLength), 1)}</div>
|
<div data-tip="Average name length">Mean name length: ${rn(d3.mean(wordsLength), 1)}</div>
|
||||||
<div>Median name length: ${d3.median(wordsLength)}</div>
|
<div data-tip="Common name length">Median name length: ${d3.median(wordsLength)}</div>
|
||||||
<div>Doubled chars: ${doubled}</div>
|
<hr>
|
||||||
<div>Multi-word names: ${multi}%</div>
|
<div data-tip="Characters outside of Basic Latin have bad font support">Non-basic chars: ${nonBasicLatinChars}</div>
|
||||||
|
<div data-tip="Characters that are frequently (more than 3 times) doubled">Doubled chars: ${doubled.join("")}</div>
|
||||||
|
<div data-tip="Names used more than one time">Duplicates: ${duplicates}</div>
|
||||||
|
<div data-tip="Percentage of names containing space character">Multi-word names: ${rn(multiwordRate * 100, 2)}%</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
$("#alert").dialog({
|
$("#alert").dialog({
|
||||||
resizable: false, title: "Data Analysis",
|
resizable: false,
|
||||||
|
title: "Data Analysis",
|
||||||
position: {my: "left top-30", at: "right+10 top", of: "#namesbaseEditor"},
|
position: {my: "left top-30", at: "right+10 top", of: "#namesbaseEditor"},
|
||||||
buttons: {OK: function() {$(this).dialog("close");}}
|
buttons: {
|
||||||
|
OK: function () {
|
||||||
|
$(this).dialog("close");
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -171,7 +197,9 @@ function editNamesbase() {
|
||||||
|
|
||||||
function namesbaseRestoreDefault() {
|
function namesbaseRestoreDefault() {
|
||||||
alertMessage.innerHTML = `Are you sure you want to restore default namesbase?`;
|
alertMessage.innerHTML = `Are you sure you want to restore default namesbase?`;
|
||||||
$("#alert").dialog({resizable: false, title: "Restore default data",
|
$("#alert").dialog({
|
||||||
|
resizable: false,
|
||||||
|
title: "Restore default data",
|
||||||
buttons: {
|
buttons: {
|
||||||
Restore: function () {
|
Restore: function () {
|
||||||
$(this).dialog("close");
|
$(this).dialog("close");
|
||||||
|
|
@ -180,7 +208,9 @@ function editNamesbase() {
|
||||||
createBasesList();
|
createBasesList();
|
||||||
updateInputs();
|
updateInputs();
|
||||||
},
|
},
|
||||||
Cancel: function() {$(this).dialog("close");}
|
Cancel: function () {
|
||||||
|
$(this).dialog("close");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -193,7 +223,10 @@ function editNamesbase() {
|
||||||
|
|
||||||
function namesbaseUpload(dataLoaded) {
|
function namesbaseUpload(dataLoaded) {
|
||||||
const data = dataLoaded.split("\r\n");
|
const data = dataLoaded.split("\r\n");
|
||||||
if (!data || !data[0]) {tip("Cannot load a namesbase. Please check the data format", false, "error"); return;}
|
if (!data || !data[0]) {
|
||||||
|
tip("Cannot load a namesbase. Please check the data format", false, "error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Names.clearChains();
|
Names.clearChains();
|
||||||
nameBases = [];
|
nameBases = [];
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,8 @@ function showSupporters() {
|
||||||
Dominick Ormsby,Linn Browning,Václav Švec,Alan Buehne,George J.Lekkas,Alexandre Boivin,Tommy Mayfield,Skylar Mangum-Turner,Karen Blythe,Stefan Gugerel,
|
Dominick Ormsby,Linn Browning,Václav Švec,Alan Buehne,George J.Lekkas,Alexandre Boivin,Tommy Mayfield,Skylar Mangum-Turner,Karen Blythe,Stefan Gugerel,
|
||||||
Mike Conley,Xavier privé,Hope You're Well,Mark Sprietsma,Robert Landry,Nick Mowry,steve hall,Markell,Josh Wren,Neutrix,BLRageQuit,Rocky,
|
Mike Conley,Xavier privé,Hope You're Well,Mark Sprietsma,Robert Landry,Nick Mowry,steve hall,Markell,Josh Wren,Neutrix,BLRageQuit,Rocky,
|
||||||
Dario Spadavecchia,Bas Kroot,John Patrick Callahan Jr,Alexandra Vesey,D,Exp1nt,james,Braxton Istace,w,Rurikid,AntiBlock,Redsauz,BigE0021,
|
Dario Spadavecchia,Bas Kroot,John Patrick Callahan Jr,Alexandra Vesey,D,Exp1nt,james,Braxton Istace,w,Rurikid,AntiBlock,Redsauz,BigE0021,
|
||||||
Jonathan Williams,ojacid .,Brian Wilson,A Patreon of the Ahts,Shubham Jakhotiya`;
|
Jonathan Williams,ojacid .,Brian Wilson,A Patreon of the Ahts,Shubham Jakhotiya,www15o,Jan Bundesmann,Angelique Badger,Joshua Xiong,Moist mongol,
|
||||||
|
Frank Fewkes,jason baldrick,Game Master Pro,Andrew Kircher,Preston Mitchell,Chris Kohut`;
|
||||||
|
|
||||||
const array = supporters
|
const array = supporters
|
||||||
.replace(/(?:\r\n|\r|\n)/g, "")
|
.replace(/(?:\r\n|\r|\n)/g, "")
|
||||||
|
|
@ -287,18 +288,16 @@ function generateMapWithSeed(source) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function showSeedHistoryDialog() {
|
function showSeedHistoryDialog() {
|
||||||
const alert = mapHistory
|
const lines = mapHistory.map((h, i) => {
|
||||||
.map(function (h, i) {
|
|
||||||
const created = new Date(h.created).toLocaleTimeString();
|
const created = new Date(h.created).toLocaleTimeString();
|
||||||
const button = `<i data-tip"Click to generate a map with this seed" onclick="restoreSeed(${i})" class="icon-history optionsSeedRestore"></i>`;
|
const button = `<i data-tip="Click to generate a map with this seed" onclick="restoreSeed(${i})" class="icon-history optionsSeedRestore"></i>`;
|
||||||
return `<div>${i + 1}. Seed: ${h.seed} ${button}. Size: ${h.width}x${h.height}. Template: ${h.template}. Created: ${created}</div>`;
|
return `<li>Seed: ${h.seed} ${button}. Size: ${h.width}x${h.height}. Template: ${h.template}. Created: ${created}</li>`;
|
||||||
})
|
});
|
||||||
.join("");
|
alertMessage.innerHTML = `<ol style="margin: 0; padding-left: 1.5em">${lines.join("")}</ol>`;
|
||||||
alertMessage.innerHTML = alert;
|
|
||||||
$("#alert").dialog({
|
$("#alert").dialog({
|
||||||
resizable: false,
|
resizable: false,
|
||||||
title: "Seed history",
|
title: "Seed history",
|
||||||
width: fitContent(),
|
|
||||||
position: {my: "center", at: "center", of: "svg"}
|
position: {my: "center", at: "center", of: "svg"}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,7 @@ function editProvinces() {
|
||||||
p.color
|
p.color
|
||||||
}" class="fillRect pointer"></svg>
|
}" 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>
|
||||||
<svg data-tip="Click to show and edit province emblem" class="coaIcon hide" viewBox="0 0 200 200"><use href="#provinceCOA${p.i}"></use></svg>
|
<svg data-tip="Click to show and edit province emblem" class="coaIcon pointer hide" viewBox="0 0 200 200"><use href="#provinceCOA${p.i}"></use></svg>
|
||||||
<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 ${
|
<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 ${
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,8 @@ function createRiver() {
|
||||||
const source = riverCells[0];
|
const source = riverCells[0];
|
||||||
const mouth = parent === riverId ? last(riverCells) : riverCells[riverCells.length - 2];
|
const mouth = parent === riverId ? last(riverCells) : riverCells[riverCells.length - 2];
|
||||||
const sourceWidth = 0.05;
|
const sourceWidth = 0.05;
|
||||||
const widthFactor = 1.2;
|
const defaultWidthFactor = rn(1 / (pointsInput.dataset.cells / 10000) ** 0.25, 2);
|
||||||
|
const widthFactor = 1.2 * defaultWidthFactor;
|
||||||
|
|
||||||
const meanderedPoints = addMeandering(riverCells);
|
const meanderedPoints = addMeandering(riverCells);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ function editStates() {
|
||||||
s.color
|
s.color
|
||||||
}" class="fillRect pointer"></svg>
|
}" 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>
|
||||||
<svg data-tip="Click to show and edit state emblem" class="coaIcon hide" viewBox="0 0 200 200"><use href="#stateCOA${s.i}"></use></svg>
|
<svg data-tip="Click to show and edit state emblem" class="coaIcon pointer hide" viewBox="0 0 200 200"><use href="#stateCOA${s.i}"></use></svg>
|
||||||
<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"/>
|
||||||
|
|
|
||||||
|
|
@ -831,7 +831,7 @@ function applyDefaultStyle() {
|
||||||
landmass.attr("opacity", 1).attr("fill", "#eef6fb").attr("filter", null);
|
landmass.attr("opacity", 1).attr("fill", "#eef6fb").attr("filter", null);
|
||||||
markers.attr("opacity", null).attr("rescale", 1).attr("filter", "url(#dropShadow01)");
|
markers.attr("opacity", null).attr("rescale", 1).attr("filter", "url(#dropShadow01)");
|
||||||
|
|
||||||
prec.attr("opacity", null).attr("stroke", "#000000").attr("stroke-width", 0.1).attr("fill", "#003dff").attr("filter", null);
|
prec.attr("opacity", null).attr("stroke", "#000000").attr("stroke-width", 0).attr("fill", "#003dff").attr("filter", null);
|
||||||
population.attr("opacity", null).attr("stroke-width", 1.6).attr("stroke-dasharray", null).attr("stroke-linecap", "butt").attr("filter", null);
|
population.attr("opacity", null).attr("stroke-width", 1.6).attr("stroke-dasharray", null).attr("stroke-linecap", "butt").attr("filter", null);
|
||||||
population.select("#rural").attr("stroke", "#0000ff");
|
population.select("#rural").attr("stroke", "#0000ff");
|
||||||
population.select("#urban").attr("stroke", "#ff0000");
|
population.select("#urban").attr("stroke", "#ff0000");
|
||||||
|
|
@ -938,7 +938,7 @@ function applyDefaultStyle() {
|
||||||
.attr("stroke-linecap", "round");
|
.attr("stroke-linecap", "round");
|
||||||
legend.select("#legendBox").attr("fill", "#ffffff").attr("fill-opacity", 0.8);
|
legend.select("#legendBox").attr("fill", "#ffffff").attr("fill-opacity", 0.8);
|
||||||
|
|
||||||
const citiesSize = Math.max(rn(8 - regionsInput.value / 20), 3);
|
const citiesSize = Math.max(rn(8 - regionsOutput.value / 20), 3);
|
||||||
burgLabels
|
burgLabels
|
||||||
.select("#cities")
|
.select("#cities")
|
||||||
.attr("fill", "#3e3e4b")
|
.attr("fill", "#3e3e4b")
|
||||||
|
|
@ -979,7 +979,7 @@ function applyDefaultStyle() {
|
||||||
.attr("stroke-linecap", "butt");
|
.attr("stroke-linecap", "butt");
|
||||||
anchors.select("#towns").attr("opacity", 1).attr("fill", "#ffffff").attr("stroke", "#3e3e4b").attr("stroke-width", 1.2).attr("size", 1);
|
anchors.select("#towns").attr("opacity", 1).attr("fill", "#ffffff").attr("stroke", "#3e3e4b").attr("stroke-width", 1.2).attr("size", 1);
|
||||||
|
|
||||||
const stateLabelSize = Math.max(rn(24 - regionsInput.value / 6), 6);
|
const stateLabelSize = Math.max(rn(24 - regionsOutput.value / 6), 6);
|
||||||
labels
|
labels
|
||||||
.select("#states")
|
.select("#states")
|
||||||
.attr("fill", "#3e3e4b")
|
.attr("fill", "#3e3e4b")
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,7 @@ function regenerateStates() {
|
||||||
const localSeed = Math.floor(Math.random() * 1e9); // new random seed
|
const localSeed = Math.floor(Math.random() * 1e9); // new random seed
|
||||||
Math.random = aleaPRNG(localSeed);
|
Math.random = aleaPRNG(localSeed);
|
||||||
|
|
||||||
const statesCount = +regionsInput.value;
|
const statesCount = +regionsOutput.value;
|
||||||
const burgs = pack.burgs.filter(b => b.i && !b.removed);
|
const burgs = pack.burgs.filter(b => b.i && !b.removed);
|
||||||
if (!burgs.length) return tip("There are no any burgs to generate states. Please create burgs first", false, "error");
|
if (!burgs.length) return tip("There are no any burgs to generate states. Please create burgs first", false, "error");
|
||||||
if (burgs.length < statesCount) tip(`Not enough burgs to generate ${statesCount} states. Will generate only ${burgs.length} states`, false, "warn");
|
if (burgs.length < statesCount) tip(`Not enough burgs to generate ${statesCount} states. Will generate only ${burgs.length} states`, false, "warn");
|
||||||
|
|
@ -624,7 +624,9 @@ function addRiverOnClick() {
|
||||||
|
|
||||||
const source = riverCells[0];
|
const source = riverCells[0];
|
||||||
const mouth = riverCells[riverCells.length - 2];
|
const mouth = riverCells[riverCells.length - 2];
|
||||||
const widthFactor = river?.widthFactor || (!parent || parent === riverId ? 1.2 : 1);
|
|
||||||
|
const defaultWidthFactor = rn(1 / (pointsInput.dataset.cells / 10000) ** 0.25, 2);
|
||||||
|
const widthFactor = river?.widthFactor || (!parent || parent === riverId ? defaultWidthFactor * 1.2 : defaultWidthFactor);
|
||||||
const meanderedPoints = addMeandering(riverCells);
|
const meanderedPoints = addMeandering(riverCells);
|
||||||
|
|
||||||
const discharge = cells.fl[mouth]; // m3 in second
|
const discharge = cells.fl[mouth]; // m3 in second
|
||||||
|
|
@ -739,7 +741,7 @@ function configMarkersGeneration() {
|
||||||
const inputId = `markerIconInput${index}`;
|
const inputId = `markerIconInput${index}`;
|
||||||
return `<tr>
|
return `<tr>
|
||||||
<td><input value="${type}" /></td>
|
<td><input value="${type}" /></td>
|
||||||
<td>
|
<td style="position: relative">
|
||||||
<input id="${inputId}" style="width: 5em" value="${icon}" />
|
<input id="${inputId}" style="width: 5em" value="${icon}" />
|
||||||
<i class="icon-edit pointer" style="position: absolute; margin:.4em 0 0 -1.4em; font-size:.85em"></i>
|
<i class="icon-edit pointer" style="position: absolute; margin:.4em 0 0 -1.4em; font-size:.85em"></i>
|
||||||
</td>
|
</td>
|
||||||
|
|
|
||||||
|
|
@ -154,18 +154,23 @@ void (function () {
|
||||||
const prompt = document.getElementById("prompt");
|
const prompt = document.getElementById("prompt");
|
||||||
const form = prompt.querySelector("#promptForm");
|
const form = prompt.querySelector("#promptForm");
|
||||||
|
|
||||||
window.prompt = function (promptText = "Please provide an input", options = {default: 1, step: 0.01, min: 0, max: 100}, callback) {
|
const defaultText = "Please provide an input";
|
||||||
if (options.default === undefined) {
|
const defaultOptions = {default: 1, step: 0.01, min: 0, max: 100, required: true};
|
||||||
ERROR && console.error("Prompt: options object does not have default value defined");
|
|
||||||
return;
|
window.prompt = function (promptText = defaultText, options = defaultOptions, callback) {
|
||||||
}
|
if (options.default === undefined) return ERROR && console.error("Prompt: options object does not have default value defined");
|
||||||
|
|
||||||
const input = prompt.querySelector("#promptInput");
|
const input = prompt.querySelector("#promptInput");
|
||||||
prompt.querySelector("#promptText").innerHTML = promptText;
|
prompt.querySelector("#promptText").innerHTML = promptText;
|
||||||
|
|
||||||
const type = typeof options.default === "number" ? "number" : "text";
|
const type = typeof options.default === "number" ? "number" : "text";
|
||||||
input.type = type;
|
input.type = type;
|
||||||
|
|
||||||
if (options.step !== undefined) input.step = options.step;
|
if (options.step !== undefined) input.step = options.step;
|
||||||
if (options.min !== undefined) input.min = options.min;
|
if (options.min !== undefined) input.min = options.min;
|
||||||
if (options.max !== undefined) input.max = options.max;
|
if (options.max !== undefined) input.max = options.max;
|
||||||
|
|
||||||
|
input.required = options.required === false ? false : true;
|
||||||
input.placeholder = "type a " + type;
|
input.placeholder = "type a " + type;
|
||||||
input.value = options.default;
|
input.value = options.default;
|
||||||
prompt.style.display = "block";
|
prompt.style.display = "block";
|
||||||
|
|
@ -173,9 +178,9 @@ void (function () {
|
||||||
form.addEventListener(
|
form.addEventListener(
|
||||||
"submit",
|
"submit",
|
||||||
event => {
|
event => {
|
||||||
|
event.preventDefault();
|
||||||
prompt.style.display = "none";
|
prompt.style.display = "none";
|
||||||
const v = type === "number" ? +input.value : input.value;
|
const v = type === "number" ? +input.value : input.value;
|
||||||
event.preventDefault();
|
|
||||||
if (callback) callback(v);
|
if (callback) callback(v);
|
||||||
},
|
},
|
||||||
{once: true}
|
{once: true}
|
||||||
|
|
@ -183,7 +188,9 @@ void (function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
const cancel = prompt.querySelector("#promptCancel");
|
const cancel = prompt.querySelector("#promptCancel");
|
||||||
cancel.addEventListener("click", () => (prompt.style.display = "none"));
|
cancel.addEventListener("click", () => {
|
||||||
|
prompt.style.display = "none";
|
||||||
|
});
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// indexedDB; ldb object
|
// indexedDB; ldb object
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue