Merge branch 'upstream' into dev-submaps

This commit is contained in:
Mészáros Gergely 2022-03-07 13:04:25 +01:00
commit 7e8f77d0b3
76 changed files with 5756 additions and 1688 deletions

2
.gitignore vendored
View file

@ -1,2 +1,4 @@
.bat
.vscode
.idea
.idea/Fantasy-Map-Generator.iml

74
components/fill-box.js Normal file
View file

@ -0,0 +1,74 @@
// fill-box cannot use Shadow DOM as it needs access to svg hatches
// append stylesheet
{
const style = `
fill-box:not([disabled]) {
cursor: pointer;
}
fill-box > svg {
vertical-align: middle;
pointer-events: none;
}
fill-box > svg > rect {
stroke: #666666;
stroke-width: 2;
}`;
const styleElement = document.createElement("style");
styleElement.setAttribute("type", "text/css");
styleElement.innerHTML = style;
document.head.appendChild(styleElement);
}
{
const template = document.createElement("template");
template.innerHTML = `
<svg>
<rect x="0" y="0" width="100%" height="100%">
</svg>
`;
class FillBox extends HTMLElement {
constructor() {
super();
this.appendChild(template.content.cloneNode(true));
this.querySelector("rect")?.setAttribute("fill", this.fill);
this.querySelector("svg")?.setAttribute("width", this.size);
this.querySelector("svg")?.setAttribute("height", this.size);
}
static showTip() {
tip(this.tip);
}
connectedCallback() {
this.addEventListener("mousemove", this.constructor.showTip);
}
disconnectedCallback() {
this.removeEventListener("mousemove", this.constructor.showTip);
}
get fill() {
return this.getAttribute("fill") || "#333";
}
set fill(newFill) {
this.setAttribute("fill", newFill);
this.querySelector("rect")?.setAttribute("fill", newFill);
}
get size() {
return this.getAttribute("size") || "1em";
}
get tip() {
return this.dataset.tip || "Fill style. Click to change";
}
}
customElements.define("fill-box", FillBox);
}

View file

@ -7,35 +7,34 @@
</head>
<body>
<script>
/*
open this page in a new window without query parameter to start auth
window.opener.setDropBoxToken(token) will be called on the opener
window.
*/
// open this page in a new window without query parameter to start auth
// setDropBoxToken(token) will be called on the opener window
const REDIRECT_URI = window.location.origin + window.location.pathname;
const dbxAuth = new Dropbox.DropboxAuth({clientId: "pdr9ae64ip0qno4"});
const auth = new Dropbox.DropboxAuth({clientId: "pdr9ae64ip0qno4"});
const spObj = new URLSearchParams(window.location.search);
const searchParams = Object.fromEntries(spObj.entries());
const params = new URLSearchParams(window.location.search);
const code = params.get("code");
const error = params.get("error");
if (searchParams.code) getToken();
else doAuth(); // start authentication
if (code) getToken();
else if (error) window.opener.Cloud.providers.dropbox.returnError(params.get("error_description"));
else startAuth();
function doAuth() {
dbxAuth
function startAuth() {
auth
.getAuthenticationUrl(REDIRECT_URI, undefined, "code", "offline", undefined, undefined, true)
.then(authUrl => {
window.sessionStorage.clear();
window.sessionStorage.setItem("codeVerifier", dbxAuth.codeVerifier);
window.sessionStorage.setItem("codeVerifier", auth.codeVerifier);
window.location.href = authUrl;
})
.catch(error => console.error(error));
}
function getToken() {
dbxAuth.setCodeVerifier(window.sessionStorage.getItem("codeVerifier"));
dbxAuth
.getAccessTokenFromCode(REDIRECT_URI, searchParams.code)
auth.setCodeVerifier(window.sessionStorage.getItem("codeVerifier"));
auth
.getAccessTokenFromCode(REDIRECT_URI, code)
.then(resp => {
const token = resp.result.access_token;
window.opener.Cloud.providers.dropbox.setDropBoxToken(token);

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

BIN
heightmaps/arabia.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

BIN
heightmaps/atlantics.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

BIN
heightmaps/britain.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
heightmaps/caribbean.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

BIN
heightmaps/east-asia.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

BIN
heightmaps/eurasia.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

BIN
heightmaps/europe-north.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 91 KiB

BIN
heightmaps/europe.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 KiB

BIN
heightmaps/greenland.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

BIN
heightmaps/hellenica.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
heightmaps/iceland.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

View file

@ -0,0 +1,8 @@
To get heightmap with correct height scale:
1. Open tangrams.github.io
2. Toggle off auto-exposure
3. Set max elevation to 2000
4. Set min elevation to -500
5. Find region you like
6. Render image
7. Optionally rescale image to a smaller size (e.g. 500x300px) as high resolution is not used

BIN
heightmaps/indian-ocean.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

BIN
heightmaps/middle-east.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

BIN
heightmaps/us-centric.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

BIN
heightmaps/us-mainland.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

BIN
heightmaps/world.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

BIN
images/Discord.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 869 B

View file

@ -1531,6 +1531,11 @@ div.states > .riverType {
cursor: pointer;
}
.changeRelations > * {
pointer-events: none;
cursor: pointer;
}
#diplomacySelect {
width: 5em;
margin: 0.1em 0 0 -0.3em;
@ -1668,11 +1673,6 @@ div.states > div.biomeArea {
width: 5em;
}
rect.fillRect {
stroke: #666666;
stroke-width: 2;
}
#militaryHeader > div,
#regimentsHeader > div {
width: 5.2em;
@ -2036,7 +2036,7 @@ div.textual span,
fill: none;
}
div#notes {
#notes {
display: none;
position: fixed;
width: 28vw;
@ -2046,22 +2046,47 @@ div#notes {
border: 1px solid #5e4fa2;
background: rgba(255, 250, 228, 0.7);
box-shadow: 2px 2px 5px -3px #3a2804;
white-space: pre-line;
}
div#notesHeader {
@media screen and (max-width: 600px) {
#notes {
width: 50vw;
}
}
#notesHeader {
font-weight: bold;
font-size: 1.3em;
padding: 0 0 4px 14px;
padding: 16px 0 4px 12px;
border-bottom: 1px solid #5e4fa2;
}
div#notesBody {
padding: 0 1em;
#notesBody {
padding: 14px 12px;
max-height: 80vh;
overflow: auto;
}
#notesBody > iframe {
pointer-events: none;
user-select: none;
}
#notesBody p {
margin: 4px;
}
#notesLegend {
height: 87%;
outline: 0;
overflow-y: auto;
padding: 0.6em;
font-family: Copperplate, monospace;
background-color: #fff;
border: 1px solid #dedede;
color: #000;
}
svg.button {
position: relative;
background-color: transparent;
@ -2310,7 +2335,7 @@ svg.button {
.epgrid line {
stroke: lightgrey;
stroke-opacity: 0.7;
stroke-opacity: 0.5;
shape-rendering: crispEdges;
}
@ -2318,45 +2343,6 @@ svg.button {
stroke-width: 0;
}
.pell {
border: 1px solid hsla(0, 0%, 4%, 0.1);
}
.pell,
.pell-content {
box-sizing: border-box;
}
.pell-content {
height: 14em;
outline: 0;
overflow-y: auto;
padding: 0.6em;
font-family: Copperplate, monospace;
background-color: #fff;
border: 1px solid #dedede;
}
.pell-actionbar {
background-color: #fff;
border: 1px solid #dedede;
border-bottom: 0;
}
.pell-button {
background-color: transparent;
border: none;
cursor: pointer;
height: 30px;
outline: 0;
width: 30px;
vertical-align: bottom;
}
.pell-button-selected {
background-color: #f0f0f0;
}
#debug {
font-size: 1px;
opacity: 0.8;

View file

@ -19,7 +19,7 @@
body {margin: 0; font-size: 10px; overflow: hidden;}
#map {position: absolute;}
#initial {fill: none; stroke: black; pointer-events: none;}
#init-rose {animation: 20s infinite spin; opacity: .7; transform-origin: center;}
#init-rose {opacity: .7; transform-origin: center; opacity: .7; animation: 20s infinite spin;}
@keyframes spin {0% {transform: rotate(0deg);} 100% {transform: rotate(359deg);}}
#loading {opacity:1; font-size: 11px; color:#fff5da; text-align:center; text-shadow:0px 1px 4px #4c3a35; width:80%; max-width:600px; position:fixed; top:50%; left:50%; transform:translate(-50%, -50%); pointer-events:none;}
#loading-text {font-size: 1.8em; margin: 0.2em 0 0 1em;}
@ -148,55 +148,6 @@
</filter>
</g>
<g id="hatching">
<pattern id="hatch0" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="0" y2="4" style="stroke:black; stroke-width:2"/>
</pattern>
<pattern id="hatch1" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="0" y2="4" style="stroke:black; stroke-width:2"/>
</pattern>
<pattern id="hatch2" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="4" y2="0" style="stroke:black; stroke-width:2"/>
</pattern>
<pattern id="hatch3" patternTransform="rotate(-45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="0" y2="4" style="stroke:black; stroke-width:2"/>
</pattern>
<pattern id="hatch4" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="0" y2="4" style="stroke:black; stroke-width:1.5"/>
<line x1="0" y1="0" x2="4" y2="0" style="stroke:black; stroke-width:1.5"/>
</pattern>
<pattern id="hatch5" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="0" y2="4" style="stroke:black; stroke-width:1.5"/>
<line x1="0" y1="0" x2="4" y2="0" style="stroke:black; stroke-width:1.5"/>
</pattern>
<pattern id="hatch6" patternUnits="userSpaceOnUse" width="5" height="5">
<circle cx="2.5" cy="2.5" r="1" style="fill: black"/>
</pattern>
<pattern id="hatch7" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="0" y2="3" transform="rotate(-45 0 0)" style="stroke:black; stroke-width:1.5" />
</pattern>
<pattern id="hatch8" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="0" y2="3" style="stroke:black; stroke-width:2.5"/>
</pattern>
<pattern id="hatch9" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="0" y2="3" style="stroke:black; stroke-width:2.5"/>
</pattern>
<pattern id="hatch10" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="3" y2="0" style="stroke:black; stroke-width:2.5"/>
</pattern>
<pattern id="hatch11" patternTransform="rotate(-45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="0" y2="3" style="stroke:black; stroke-width:2.5"/>
</pattern>
<pattern id="hatch12" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="1" x2="0" y2="3" style="stroke:black; stroke-width:1.5" />
<line x1="1" y1="0" x2="3" y2="0" style="stroke:black; stroke-width:1.5" />
</pattern>
<pattern id="hatch13" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="0" y2="3" style="stroke:black; stroke-width:1.5" />
<line x1="0" y1="0" x2="3" y2="0" style="stroke:black; stroke-width:1.5" />
</pattern>
</g>
<g id="deftemp">
<mask id="land"></mask>
<mask id="water">
@ -211,14 +162,14 @@
</g>
<pattern id="oceanic" width="100" height="100" patternUnits="userSpaceOnUse">
<image id="oceanicPattern" href="./images/pattern1.png"></image>
<image id="oceanicPattern" href="./images/pattern1.png" opacity="0.2"></image>
</pattern>
</defs>
<g id="viewbox"></g>
<g id="scaleBar"></g>
<g id="initial" opacity=1>
<rect x="-1%" y="-1%" width="102%" height="102%" fill="#466eab"/>
<rect x="-1%" y="-1%" width="102%" height="102%" fill="url(#oceanic)" opacity=".2"/>
<rect x="-1%" y="-1%" width="102%" height="102%" fill="url(#oceanic)"/>
<use href="#rose" id="init-rose" x="50%" y="50%"/>
</g>
</svg>
@ -226,7 +177,7 @@
<div id="loading">
<div id="titleName"><t data-t="titleName">Azgaar's</t></div>
<div id="title"><t data-t="title">Fantasy Map Generator</t></div>
<div id="version"><t data-t="version">v. </t>1.71</div>
<div id="version"><t data-t="version">v. </t>1.73</div>
<p id="loading-text"><t data-t="loading">LOADING</t><span>.</span><span>.</span><span>.</span></p>
</div>
@ -309,17 +260,9 @@
<div id="styleContent" class="tabcontent">
<p data-tip="Select a style preset. State labels may required regeneration if font is changed" style="display: inline-block">Style preset:</p>
<select data-tip="Select a style preset" id="stylePreset" onchange="changeStylePreset(this.value)" style="width:45%">
<option value="styleDefault" data-system=1 selected>Default</option>
<option value="styleAncient" data-system=1>Ancient</option>
<option value="styleGloom" data-system=1>Gloom</option>
<option value="styleClean" data-system=1>Clean</option>
<option value="styleLight" data-system=1>Light</option>
<option value="styleWatercolor" data-system=1>Watercolor</option>
<option value="styleMonochrome" data-system=1>Monochrome</option>
</select>
<select data-tip="Select a style preset" id="stylePreset" onchange="requestStylePresetChange(this.value)" style="width:45%; text-transform: capitalize;"></select>
<button id="addStyleButton" data-tip="Click to save current style as a new preset" class="icon-plus sideButton" style="display: inline-block" onclick="addStylePreset()"></button>
<button id="removeStyleButton" data-tip="Click to remove current custom style preset" class="icon-minus sideButton" style="display: none" onclick="removeStylePreset()"></button>
<button id="removeStyleButton" data-tip="Click to remove current custom style preset" class="icon-minus sideButton" style="display: none" onclick="requestRemoveStylePreset()"></button>
<p data-tip="Select an element to edit its style" style="display: inline-block;">Select element:</p>
<select data-tip="Select an element to edit its style (list is ordered alphabetically)" id="styleElementSelect" style="width:42%">
@ -996,13 +939,14 @@
</td>
</tr>
<tr data-tip="Select template to be used for a Heightmap generation">
<tr data-tip="Select heightmap generation template or precreated heightmap">
<td>
<i data-locked=0 id="lock_template" class="icon-lock-open"></i>
</td>
<td>Map template</td>
<td>Heightmap</td>
<td>
<select id="templateInput" data-stored="template">
<optgroup label="Template">
<option value="volcano">Volcano</option>
<option value="highIsland">High Island</option>
<option value="lowIsland">Low Island</option>
@ -1015,6 +959,32 @@
<option value="isthmus">Isthmus</option>
<option value="shattered">Shattered</option>
<option value="taklamakan">Taklamakan</option>
</optgroup>
<optgroup label="Specific">
<option value="africa-centric">Africa Centric</option>
<option value="arabia">Arabia</option>
<option value="atlantics">Atlantics</option>
<option value="britain">Britain</option>
<option value="caribbean">Caribbean</option>
<option value="east-asia">East Asia</option>
<option value="eurasia">Eurasia</option>
<option value="europe">Europe</option>
<option value="europe-accented">Europe Accented</option>
<option value="europe-and-central-asia">Europe and Central Asia</option>
<option value="europe-central">Europe Central</option>
<option value="europe-north">Europe North</option>
<option value="greenland">Greenland</option>
<option value="hellenica">Hellenica</option>
<option value="iceland">Iceland</option>
<option value="indian-ocean">Indian Ocean</option>
<option value="mediterranean-sea">Mediterranean Sea</option>
<option value="middle-east">Middle East</option>
<option value="north-america">North America</option>
<option value="us-centric">US-centric</option>
<option value="us-mainland">US Mainland</option>
<option value="world">World</option>
<option value="world-from-pacific">World from Pacific</option>
</optgroup>
</select>
</td>
<td></td>
@ -1489,6 +1459,7 @@
<li><a href="http://www.tumblr.com/share?v=3&u=https%3A%2F%2Fazgaar.github.io%2FFantasy-Map-Generator" target="_blank" data-tip="Post to Tumblr"><img alt="Post to Tumblr" src="images/Tumblr.png" /></a></li>
<li><a href="http://pinterest.com/pin/create/button/?url=https%3A%2F%2Fazgaar.github.io%2FFantasy-Map-Generator" target="_blank" data-tip="Pin it"><img alt="Pin it" src="images/Pinterest.png" /></a></li>
<li><a href="http://www.reddit.com/submit?url=https%3A%2F%2Fazgaar.github.io%2FFantasy-Map-Generator" target="_blank" data-tip="Submit to Reddit"><img alt="Submit to Reddit" src="images/Reddit.png" /></a></li>
<li><a href="https://discord.gg/X7E84HU" target="_blank" data-tip="Join Discord server"><img alt="Join Discord server" src="images/Discord.png" /></a></li>
</ul>
</div>
@ -1649,7 +1620,7 @@
<button id="labelSizeHide" data-tip="Hide the font size section" class="icon-text-height"></button>
<input id="labelStartOffset" data-tip="Set starting offset for the particular label" type="range" min=20 max=80 style="width:8em">
<i class="icon-text-height"></i>
<input id="labelRelativeSize" data-tip="Set relative size for the particular label" type="number" min=30 max=300 step=1 style="width:3.2em">
<input id="labelRelativeSize" data-tip="Set relative size for the particular label" type="number" min=30 max=300 step=1 style="width:4.5em">
</div>
<button id="labelAlign" data-tip="Turn text path into a straight line" class="icon-resize-horizontal"></button>
@ -1995,7 +1966,7 @@
<div id="burgBody" style="padding-bottom: .3em">
<div style="display: flex; align-items: center">
<svg data-tip="Burg emblem. Click to edit" class="pointer" viewBox="0 0 200 200" width="13em" height="13em"><use id="burgEmblem"></use></svg>
<div>
<div style="display: grid; grid-auto-rows: minmax(1.6em, auto)">
<div id="burgProvinceAndState" style="font-style: italic; max-width: 16em"></div>
<div>
@ -2044,6 +2015,7 @@
<div class="label">Temperature:</div>
<span id="burgTemperature"></span>, like in
<span id="burgTemperatureLikeIn"></span>
<i id="burgTemperatureGraph" data-tip="Show temperature graph for the burg" class="icon-chart-area pointer"></i>
</div>
<div data-tip="Burg height above mean sea level">
@ -2932,9 +2904,10 @@
<div id="zonesEditor" class="dialog stable" style="display: none">
<div id="customHeader" class="header">
<div style="left:1.8em" data-tip="Zone description">Description&nbsp;</div>
<div style="left:13em" data-tip="Zone cells count" class="hide">Cells&nbsp;</div>
<div style="left:19em" data-tip="Zone area" class="hide">Area&nbsp;</div>
<div style="left:24em" data-tip="Zone population" class="hide">Population&nbsp;</div>
<div style="left:13em" data-tip="Zone type">Type&nbsp;</div>
<div style="left:19em" data-tip="Zone cells count" class="hide">Cells&nbsp;</div>
<div style="left:23.6em" data-tip="Zone area" class="hide">Area&nbsp;</div>
<div style="left:30.6em" data-tip="Zone population" class="hide">Population&nbsp;</div>
</div>
<div id="zonesBodySection" class="table" data-type="absolute"></div>
@ -2955,35 +2928,35 @@
<button id="zonesManually" data-tip="Re-assign zones" class="icon-brush"></button>
<div id="zonesManuallyButtons" style="display: none">
<label data-tip="Change brush size. Shortcut: + (increase), (decrease)" class="italic">Brush:
<input id="zonesBrush" oninput="tip('Brush size: '+this.value); zonesBrushNumber.value = this.value" type="range" min=5 max=25 value=7 style="width:7em">
<input id="zonesBrushNumber" oninput="tip('Brush size: '+this.value); zonesBrush.value = this.value" type="number" min=5 max=25 value=7>
<input id="zonesBrush" oninput="tip('Brush size: '+this.value); zonesBrushNumber.value = this.value" type="range" min=5 max=50 value=7 style="width:9em">
<input id="zonesBrushNumber" oninput="tip('Brush size: '+this.value); zonesBrush.value = this.value" type="number" min=5 max=50 value=7>
</label><br>
<button id="zonesManuallyApply" data-tip="Apply assignment" class="icon-check"></button>
<button id="zonesManuallyCancel" data-tip="Cancel assignment" class="icon-cancel"></button>
<button id="zonesRemove" data-tip="Click to toggle the removal mode on brush dragging. Shortcut: ctrl" class="icon-eraser"></button>
</div>
<button id="zonesAdd" data-tip="Add a new zone layer" class="icon-plus"></button>
<button id="zonesAdd" data-tip="Add new zone layer" class="icon-plus"></button>
<button id="zonesExport" data-tip="Download zones-related data" class="icon-download"></button>
<div id="zonesFilters" data-tip="Show only zones of selected type" style="display: inline-block">Type: <select id="zonesFilterType"></select></div>
</div>
</div>
<div id="notesEditor" class="dialog stable textual" style="display: none">
<div>
<span>Select object: </span>
<select id="notesSelect" data-tip="Select object" style="width: 12em"></select>
<span>Object name: </span>
<input id="notesName" data-tip="Type to change object name" autocorrect="off" spellcheck="false" style="width: 16em">
<div id="notesEditor" class="dialog stable" style="display: none">
<div style="margin-bottom: 0.3em">
<strong>Element: </strong>
<select id="notesSelect" data-tip="Select element id" style="width: 12em"></select>
<strong>Element name: </strong>
<input id="notesName" data-tip="Set element name" autocorrect="off" spellcheck="false" style="width: 16em">
<span data-tip="Speak the name. You can change voice and language in options" class="speaker">🔊</span>
</div>
<div id="notesText" data-tip="Type and style object description" style="padding: .4em 0"></div>
<div>
<button id="notesSpeak" data-tip="Speak the note. You can change voice and language in options" class="icon-voice"></button>
<div id="notesLegend" contenteditable="true"></div>
<div style="margin-top: 0.3em">
<button id="notesFocus" data-tip="Focus on selected object" class="icon-target"></button>
<button id="notesPin" data-tip="Toggle notes box dispay: hide or do not hide the box on mouse move" class="icon-pin"></button>
<button id="notesDownload" data-tip="Download notes to PC" class="icon-download"></button>
<button id="notesUpload" data-tip="Upload notes from PC" class="icon-upload"></button>
<button id="notesClearStyle" data-tip="Remove all styling, get plain text only" class="icon-eraser"></button>
<button id="notesRemove" data-tip="Remove this note" class="icon-trash fastDelete"></button>
</div>
</div>
@ -3611,8 +3584,20 @@
</div>
<p>GeoJSON format is used in GIS tools such as QGIS. Check out <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/GIS-data-export" target="_blank">wiki-page</a> for guidance.</p>
<p>Generator uses pop-up window to download files. Please ensure your browser does not block popups.</p>
<div style="margin: 1em 0 .3em; font-weight: bold">Export To JSON</div>
<div>
<button onclick="exportToJson('Full')" data-tip="Download full data as in JSON format">full</button>
<button onclick="exportToJson('Minimal')" data-tip="Download minimal data as in JSON format">minimal</button>
<button onclick="exportToJson('PackCells')" data-tip="Download map metadata and pack cells data as in JSON format">pack cells</button>
<button onclick="exportToJson('GridCells')" data-tip="Download map metadata and grid cells data as in JSON format">grid cells</button>
</div>
<p>Export in JSON format can be used as an API replacement.</p>
<div>
<p>It's also possible to export map to <i>Foundry VTT</i>, see <a href="https://github.com/Ethck/azgaar-foundry" target="_blank">the module.</a></p>
</div>
</div>
<div id="saveMapData" style="display: none" class="dialog">
<div style="margin-top: .3em">
@ -3632,12 +3617,15 @@
<button onclick="quickLoad()" data-tip="Load map from browser storage (if saved before)">storage</button>
</div>
<div id="loadFromDropbox">
<p style="margin-bottom: .3em">From your Dropbox account</p>
<select id="loadFromDropboxSelect" style="width: 22em"></select>
<p style="margin-bottom: .3em">
From your Dropbox account
<button id="dropboxConnectButton" onclick="connectToDropbox()" data-tip="Connect your Dropbox account to be able to load maps from it">Connect</button>
</p>
<div id="loadFromDropboxButtons" style="margin-bottom: .3em">
<button onclick="loadFromDropbox()" data-tip="Load .map file from your Dropbox">Open</button>
<button onclick="createSharableDropboxLink()" data-tip="Select file and create a link to share with your friends">Create link</button>
<select id="loadFromDropboxSelect" style="width: 22em"></select>
<div id="loadFromDropboxButtons" style="margin-bottom: .6em">
<button onclick="loadFromDropbox()" data-tip="Load .map file from your Dropbox">Load</button>
<button onclick="createSharableDropboxLink()" data-tip="Select file and create a link to share with your friends">Share</button>
</div>
<div style="margin-top: .3em">
@ -4394,6 +4382,153 @@
<path d="M 43.4,0 36.2,12.5 43.4,25 M 21.7,12.5 H 36.2 Z M 0,0 H 14.5 L 21.7,12.5 14.5,25 H 0"/>
</pattern>
</g>
<g id="defs-hatching">
<pattern id="hatch0" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="0" y2="4" style="stroke:black; stroke-width:2"/>
</pattern>
<pattern id="hatch1" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="0" y2="4" style="stroke:black; stroke-width:2"/>
</pattern>
<pattern id="hatch2" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="4" y2="0" style="stroke:black; stroke-width:2"/>
</pattern>
<pattern id="hatch3" patternTransform="rotate(-45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="0" y2="4" style="stroke:black; stroke-width:2"/>
</pattern>
<pattern id="hatch4" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="0" y2="4" style="stroke:black; stroke-width:1.5"/>
<line x1="0" y1="0" x2="4" y2="0" style="stroke:black; stroke-width:1.5"/>
</pattern>
<pattern id="hatch5" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="0" y2="4" style="stroke:black; stroke-width:1.5"/>
<line x1="0" y1="0" x2="4" y2="0" style="stroke:black; stroke-width:1.5"/>
</pattern>
<pattern id="hatch6" patternUnits="userSpaceOnUse" width="5" height="5">
<circle cx="2.5" cy="2.5" r="1" style="fill: black"/>
</pattern>
<pattern id="hatch7" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="0" y2="3" transform="rotate(-45 0 0)" style="stroke:black; stroke-width:1.5" />
</pattern>
<pattern id="hatch8" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="0" y2="3" style="stroke:black; stroke-width:2.5"/>
</pattern>
<pattern id="hatch9" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="0" y2="3" style="stroke:black; stroke-width:2.5"/>
</pattern>
<pattern id="hatch10" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="3" y2="0" style="stroke:black; stroke-width:2.5"/>
</pattern>
<pattern id="hatch11" patternTransform="rotate(-45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="0" y2="3" style="stroke:black; stroke-width:2.5"/>
</pattern>
<pattern id="hatch12" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="1" x2="0" y2="3" style="stroke:black; stroke-width:1.5"/>
<line x1="1" y1="0" x2="3" y2="0" style="stroke:black; stroke-width:1.5"/>
</pattern>
<pattern id="hatch13" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="0" y2="3" style="stroke:black; stroke-width:1.5"/>
<line x1="0" y1="0" x2="3" y2="0" style="stroke:black; stroke-width:1.5"/>
</pattern>
<pattern id="hatch14" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="1" y1="0" x2="0" y2="4" style="stroke:black; stroke-width:2"/>
</pattern>
<pattern id="hatch15" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="1" y1="0" x2="0" y2="4" style="stroke:black; stroke-width:2"/>
</pattern>
<pattern id="hatch16" patternTransform="rotate(-45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="1" y1="0" x2="0" y2="4" style="stroke:black; stroke-width:2"/>
</pattern>
<pattern id="hatch17" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="1" y1="0" x2="0" y2="4" style="stroke:black; stroke-width:1.5"/>
<line x1="1" y1="0" x2="4" y2="0" style="stroke:black; stroke-width:1.5"/>
</pattern>
<pattern id="hatch18" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="1" y1="0" x2="0" y2="4" style="stroke:black; stroke-width:1.5"/>
<line x1="1" y1="0" x2="4" y2="0" style="stroke:black; stroke-width:1.5"/>
</pattern>
<pattern id="hatch19" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="2" y1="0" x2="0" y2="4" style="stroke:black; stroke-width:2"/>
</pattern>
<pattern id="hatch20" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="2" y1="0" x2="0" y2="4" style="stroke:black; stroke-width:2"/>
</pattern>
<pattern id="hatch21" patternTransform="rotate(-45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="2" y1="0" x2="0" y2="4" style="stroke:black; stroke-width:2"/>
</pattern>
<pattern id="hatch22" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="2" y1="0" x2="0" y2="4" style="stroke:black; stroke-width:1.5"/>
<line x1="2" y1="0" x2="4" y2="0" style="stroke:black; stroke-width:1.5"/>
</pattern>
<pattern id="hatch23" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="0" y2="3" style="stroke:black; stroke-width:1.5"/>
<line x1="0" y1="0" x2="3" y2="0" style="stroke:black; stroke-width:1.5"/>
</pattern>
<pattern id="hatch24" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="2" y1="0" x2="0" y2="4" style="stroke:black; stroke-width:1.5"/>
<line x1="2" y1="0" x2="4" y2="0" style="stroke:black; stroke-width:1.5"/>
</pattern>
<pattern id="hatch25" patternTransform="rotate(-45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="2" y1="0" x2="4" y2="0" style="stroke:black; stroke-width:2"/>
</pattern>
<pattern id="hatch26" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="2" y1="0" x2="4" y2="0" style="stroke:black; stroke-width:2"/>
</pattern>
<pattern id="hatch27" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="2" y1="0" x2="4" y2="0" style="stroke:black; stroke-width:2"/>
</pattern>
<pattern id="hatch28" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="2" y1="0" x2="0" y2="2" style="stroke:black; stroke-width:2"/>
</pattern>
<pattern id="hatch29" patternTransform="rotate(30 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="1" x2="0" y2="3" style="stroke:black; stroke-width:1.5"/>
<line x1="1" y1="0" x2="3" y2="0" style="stroke:black; stroke-width:1.5"/>
</pattern>
<pattern id="hatch30" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="1" y1="0" x2="0" y2="3" style="stroke:black; stroke-width:1.5"/>
<line x1="1" y1="0" x2="3" y2="0" style="stroke:black; stroke-width:1.5"/>
</pattern>
<pattern id="hatch31" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="1" y1="0" x2="0" y2="3" style="stroke:black; stroke-width:1.5"/>
<line x1="1" y1="0" x2="3" y2="0" style="stroke:black; stroke-width:1.5"/>
</pattern>
<pattern id="hatch32" patternUnits="userSpaceOnUse" width="5" height="5">
<circle cx="2.5" cy="2.5" r="0.5" style="fill: black"/>
</pattern>
<pattern id="hatch33" patternUnits="userSpaceOnUse" width="5" height="5">
<circle cx="2.5" cy="2.5" r="1.5" style="fill: black"/>
</pattern>
<pattern id="hatch34" patternUnits="userSpaceOnUse" width="5" height="5">
<circle cx="3" cy="3" r="1" style="fill: black"/>
<circle cx="1" cy="1" r="1" style="fill: black"/>
</pattern>
<pattern id="hatch35" patternUnits="userSpaceOnUse" width="5" height="5">
<circle cx="3" cy="3" r="1.5" style="fill: black"/>
<circle cx="1" cy="1" r="1.5" style="fill: black"/>
</pattern>
<pattern id="hatch36" patternTransform="rotate(-45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="0" y2="3" transform="rotate(-45 0 0)" style="stroke:black; stroke-width:1.5" />
</pattern>
<pattern id="hatch37" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="0" y2="3" transform="rotate(-45 0 0)" style="stroke:black; stroke-width:1.5" />
</pattern>
<pattern id="hatch38" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="4" y2="4" style="stroke:black; stroke-width:1.5"/>
<line x1="0" y1="0" x2="4" y2="0" style="stroke:black; stroke-width:1.5"/>
</pattern>
<pattern id="hatch39" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="4" y2="4" style="stroke:black; stroke-width:1.5"/>
<line x1="0" y1="0" x2="4" y2="0" style="stroke:black; stroke-width:1.5"/>
</pattern>
<pattern id="hatch40" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="3" y2="3" style="stroke:black; stroke-width:1.5"/>
<line x1="0" y1="0" x2="4" y2="0" style="stroke:black; stroke-width:1.5"/>
</pattern>
<pattern id="hatch41" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse" width="4" height="4">
<line x1="0" y1="0" x2="3" y2="3" style="stroke:black; stroke-width:1.5"/>
<line x1="0" y1="0" x2="4" y2="0" style="stroke:black; stroke-width:1.5"/>
</pattern>
</g>
</defs>
</svg>
@ -4436,18 +4571,15 @@
<script src="modules/fonts.js"></script>
<script src="modules/ui/layers.js"></script>
<script src="modules/ui/measurers.js"></script>
<script src="modules/ui/stylePresets.js"></script>
<!-- <script src="libs/umami.js"></script> -->
<script defer src="https://unpkg.com/dropbox@10.8.0/dist/Dropbox-sdk.min.js"></script>
<script defer src="modules/ui/general.js"></script>
<script defer src="modules/ui/options.js"></script>
<script defer src="modules/ui/style.js"></script>
<script defer src="modules/load.js"></script>
<script defer src="modules/cloud.js"></script>
<script defer src="main.js"></script>
<script defer src="modules/save.js"></script>
<script defer src="modules/export.js"></script>
<script src="modules/ui/general.js"></script>
<script src="modules/ui/options.js"></script>
<script src="main.js"></script>
<script defer src="modules/relief-icons.js"></script>
<script defer src="modules/ui/style.js"></script>
<script defer src="modules/ui/tools.js"></script>
<script defer src="modules/ui/world-configurator.js"></script>
<script defer src="modules/ui/editors.js"></script>
@ -4458,6 +4590,7 @@
<script defer src="modules/ui/cultures-editor.js"></script>
<script defer src="modules/ui/namesbase-editor.js"></script>
<script defer src="modules/ui/elevation-profile.js"></script>
<script defer src="modules/ui/temperature-graph.js"></script>
<script defer src="modules/ui/routes-editor.js"></script>
<script defer src="modules/ui/ice-editor.js"></script>
<script defer src="modules/ui/lakes-editor.js"></script>
@ -4467,7 +4600,6 @@
<script defer src="modules/ui/rivers-creator.js"></script>
<script defer src="modules/ui/relief-editor.js"></script>
<script defer src="modules/ui/religions-editor.js"></script>
<script defer src="modules/ui/markers-editor.js"></script>
<script defer src="modules/ui/burg-editor.js"></script>
<script defer src="modules/ui/units-editor.js"></script>
<script defer src="modules/ui/notes-editor.js"></script>
@ -4480,14 +4612,23 @@
<script defer src="modules/ui/markers-overview.js"></script>
<script defer src="modules/ui/regiment-editor.js"></script>
<script defer src="modules/ui/battle-screen.js"></script>
<script defer src="modules/coa-renderer.js"></script>
<script defer src="modules/ui/emblems-editor.js"></script>
<script defer src="modules/ui/markers-editor.js"></script>
<script defer src="modules/ui/3d.js"></script>
<script defer src="modules/ui/submap.js"></script>
<script defer src="modules/ui/hotkeys.js"></script>
<script defer src="modules/coa-renderer.js"></script>
<script defer src="libs/rgbquant.min.js"></script>
<script defer src="libs/jquery.ui.touch-punch.min.js"></script>
<script defer src="libs/pell.min.js"></script>
<script defer src="libs/jszip.min.js"></script>
<script defer src="modules/io/save.js"></script>
<script defer src="modules/io/load.js"></script>
<script defer src="modules/io/cloud.js"></script>
<script defer src="modules/io/export.js"></script>
<script defer src="modules/io/export-json.js"></script>
<!-- Web Components -->
<script defer src="components/fill-box.js"></script>
</body>
</html>

2
libs/pell.min.js vendored
View file

@ -1,2 +0,0 @@
// https://github.com/jaredreich/pell, MIT License
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Pell=t()}(this,function(){"use strict";const e=(e,t,n)=>e.addEventListener(t,n),t=(e,t)=>e.appendChild(t),n=e=>document.createElement(e),i=e=>document.queryCommandState(e),o=(e,t=null)=>document.execCommand(e,!1,t),l={bold:{icon:"<b>B</b>",title:"Bold",state:()=>i("bold"),result:()=>o("bold")},italic:{icon:"<i>I</i>",title:"Italic",state:()=>i("italic"),result:()=>o("italic")},underline:{icon:"<u>U</u>",title:"Underline",state:()=>i("underline"),result:()=>o("underline")},strikethrough:{icon:"<strike>S</strike>",title:"Strike-through",state:()=>i("strikeThrough"),result:()=>o("strikeThrough")},heading1:{icon:"<b>H<sub>1</sub></b>",title:"Heading 1",result:()=>o("formatBlock","<h1>")},heading2:{icon:"<b>H<sub>2</sub></b>",title:"Heading 2",result:()=>o("formatBlock","<h2>")},paragraph:{icon:"&#182;",title:"Paragraph",result:()=>o("formatBlock","<p>")},quote:{icon:"&#8220; &#8221;",title:"Quote",result:()=>o("formatBlock","<blockquote>")},olist:{icon:"&#35;",title:"Ordered List",result:()=>o("insertOrderedList")},ulist:{icon:"&#8226;",title:"Unordered List",result:()=>o("insertUnorderedList")},code:{icon:"&lt;/&gt;",title:"Code",result:()=>o("formatBlock","<pre>")},line:{icon:"&#8213;",title:"Horizontal Line",result:()=>o("insertHorizontalRule")},link:{icon:"&#128279;",title:"Link",result:()=>navigator.clipboard.readText().then(e=>o("createLink",e))},image:{icon:"&#128247;",title:"Image",result:()=>{navigator.clipboard.readText().then(e=>o("insertImage",e)),o("enableObjectResizing")}}},r={actionbar:"pell-actionbar",button:"pell-button",content:"pell-content",selected:"pell-button-selected"};return{exec:o,init:i=>{const a=i.actions?i.actions.map(e=>"string"==typeof e?l[e]:l[e.name]?{...l[e.name],...e}:e):Object.keys(l).map(e=>l[e]),s={...r,...i.classes},c=i.defaultParagraphSeparator||"div",u=n("div");u.className=s.actionbar,t(i.element,u);const d=i.element.content=n("div");return d.contentEditable=!0,d.className=s.content,d.oninput=(({target:{firstChild:e}})=>{e&&3===e.nodeType?o("formatBlock",`<${c}>`):"<br>"===d.innerHTML&&(d.innerHTML=""),i.onChange(d.innerHTML)}),d.onkeydown=(e=>{"Enter"===e.key&&"blockquote"===(e=>document.queryCommandValue(e))("formatBlock")&&setTimeout(()=>o("formatBlock",`<${c}>`),0)}),t(i.element,d),a.forEach(i=>{const o=n("button");if(o.className=s.button,o.innerHTML=i.icon,o.title=i.title,o.setAttribute("type","button"),o.onclick=(()=>i.result()&&d.focus()),i.state){const t=()=>o.classList[i.state()?"add":"remove"](s.selected);e(d,"keyup",t),e(d,"mouseup",t),e(o,"click",t)}t(u,o)}),i.styleWithCSS&&o("styleWithCSS"),o("defaultParagraphSeparator",c),i.element}}});

163
main.js
View file

@ -1,11 +1,11 @@
// Azgaar (azgaar.fmg@yandex.com). Minsk, 2017-2021. MIT License
// Azgaar (azgaar.fmg@yandex.com). Minsk, 2017-2022. MIT License
// https://github.com/Azgaar/Fantasy-Map-Generator
"use strict";
const version = "1.71"; // generator version
const version = "1.732"; // generator version
document.title += " v" + version;
// Switches to disable/enable logging features
// switches to disable/enable logging features
const PRODUCTION = location.hostname && location.hostname !== "localhost" && location.hostname !== "127.0.0.1";
const DEBUG = localStorage.getItem("debug");
const INFO = DEBUG || !PRODUCTION;
@ -173,14 +173,46 @@ landmass.append("rect").attr("x", 0).attr("y", 0).attr("width", graphWidth).attr
oceanPattern.append("rect").attr("fill", "url(#oceanic)").attr("x", 0).attr("y", 0).attr("width", graphWidth).attr("height", graphHeight);
oceanLayers.append("rect").attr("id", "oceanBase").attr("x", 0).attr("y", 0).attr("width", graphWidth).attr("height", graphHeight);
// remove loading screen
d3.select("#loading").transition().duration(4000).style("opacity", 0).remove();
d3.select("#initial").transition().duration(4000).attr("opacity", 0).remove();
d3.select("#optionsContainer").transition().duration(3000).style("opacity", 1);
d3.select("#tooltip").transition().duration(4000).style("opacity", 1);
if (!location.hostname) {
const wiki = "https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Run-FMG-locally";
alertMessage.innerHTML = `Fantasy Map Generator cannot run serverless.
Follow the <a href="${wiki}" target="_blank">instructions</a> on how you can easily run a local web-server`;
$("#alert").dialog({
resizable: false,
title: "Loading error",
width: "28em",
position: {my: "center center-4em", at: "center", of: "svg"},
buttons: {
OK: function () {
$(this).dialog("close");
}
}
});
d3.select("#loading-text").transition().duration(1000).style("opacity", 0);
d3.select("#init-rose").transition().duration(4000).style("opacity", 0);
} else {
hideLoading();
checkLoadParameters();
}
function hideLoading() {
d3.select("#loading").transition().duration(4000).style("opacity", 0);
d3.select("#initial").transition().duration(4000).attr("opacity", 0);
d3.select("#optionsContainer").transition().duration(3000).style("opacity", 1);
d3.select("#tooltip").transition().duration(4000).style("opacity", 1);
}
function showLoading() {
d3.select("#loading").transition().duration(200).style("opacity", 1);
d3.select("#initial").transition().duration(200).attr("opacity", 1);
d3.select("#optionsContainer").transition().duration(100).style("opacity", 0);
d3.select("#tooltip").transition().duration(200).style("opacity", 0);
}
// decide which map should be loaded or generated on page load
void (function checkLoadParameters() {
function checkLoadParameters() {
const url = new URL(window.location.href);
const params = url.searchParams;
@ -191,7 +223,9 @@ void (function checkLoadParameters() {
const pattern = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/;
const valid = pattern.test(maplink);
if (valid) {
setTimeout(() => {
loadMapFromURL(maplink, 1);
}, 1000);
return;
} else showUploadErrorMessage("Map link is not a valid URL", maplink);
}
@ -225,11 +259,11 @@ void (function checkLoadParameters() {
WARN && console.warn("Generate random map");
generateMapOnLoad();
})();
}
function generateMapOnLoad() {
applyStyleOnLoad(); // apply default or previously selected style
generate(); // generate map
async function generateMapOnLoad() {
await applyStyleOnLoad(); // apply previously selected default or custom style
await generate(); // generate map
focusOn(); // based on searchParams focus on point, cell or burg from MFCG
applyPreset(); // apply saved layers preset
}
@ -410,22 +444,22 @@ function showWelcomeMessage() {
const discord = link("https://discordapp.com/invite/X7E84HU", "Discord server");
const patreon = link("https://www.patreon.com/azgaar", "Patreon");
alertMessage.innerHTML = `The Fantasy Map Generator is updated up to version <b>${version}</b>.
alertMessage.innerHTML = `The Fantasy Map Generator is updated up to version <strong>${version}</strong>.
This version is compatible with ${changelog}, loaded <i>.map</i> files will be auto-updated.
<ul>Main changes:
<li>Ability to limit military units by biome, state, culture and religion</li>
<li>New marker types</li>
<li>New markers editor</li>
<li>Markers overview screen</li>
<li>Markers regeneration menu</li>
<li>Burg editor update</li>
<li>Editable theme color</li>
<li>Add font dialog</li>
<li>Save to Dropbox</li>
<ul><strong>Latest changes:</strong>
<li>Pre-defined heightmaps</li>
<li>Advanced notes editor</li>
<li>Zones editor: filter by type</li>
<li>Color picker: new hatchings</li>
<li>New style presets: Cyberpunk and Atlas</li>
<li>Burg temperature graph</li>
<li>4 new textures</li>
<li>Province capture logic rework</li>
<li>Button to release all provinces</li>
</ul>
<p>Join our ${discord} and ${reddit} to ask questions, share maps, discuss the Generator and Worlbuilding, report bugs and propose new features.</p>
<span>Thanks for all supporters on <a href="https://www.patreon.com/azgaar" target="_blank">Patreon</a>!</i></span>`;
<span><i>Thanks for all supporters on ${patreon}!</i></span>`;
$("#alert").dialog({
resizable: false,
@ -448,7 +482,7 @@ function doWorkOnZoom(isScaleChanged, isPositionChanged) {
if (isScaleChanged) {
invokeActiveZooming();
drawScaleBar();
drawScaleBar(scale);
}
// zoom image converter overlay
@ -609,7 +643,7 @@ void (function addDragToUpload() {
});
})();
function generate() {
async function generate() {
try {
const timeStart = performance.now();
invokeActiveZooming();
@ -619,8 +653,8 @@ function generate() {
randomizeOptions();
placePoints();
calculateVoronoi(grid, grid.points);
drawScaleBar();
HeightmapGenerator.generate();
drawScaleBar(scale);
await HeightmapGenerator.generate();
markFeatures();
markupGridOcean();
addLakesInDeepDepressions();
@ -907,6 +941,31 @@ function defineMapSize() {
function getSizeAndLatitude() {
const template = document.getElementById("templateInput").value; // heightmap template
if (template === "africa-centric") return [45, 53];
if (template === "arabia") return [20, 35];
if (template === "atlantics") return [42, 23];
if (template === "britain") return [7, 20];
if (template === "caribbean") return [15, 40];
if (template === "east-asia") return [11, 28];
if (template === "eurasia") return [38, 19];
if (template === "europe") return [20, 16];
if (template === "europe-accented") return [14, 22];
if (template === "europe-and-central-asia") return [25, 10];
if (template === "europe-central") return [11, 22];
if (template === "europe-north") return [7, 18];
if (template === "greenland") return [22, 7];
if (template === "hellenica") return [8, 27];
if (template === "iceland") return [2, 15];
if (template === "indian-ocean") return [45, 55];
if (template === "mediterranean-sea") return [10, 29];
if (template === "middle-east") return [8, 31];
if (template === "north-america") return [37, 17];
if (template === "us-centric") return [66, 27];
if (template === "us-mainland") return [16, 30];
if (template === "world") return [78, 27];
if (template === "world-from-pacific") return [75, 32];
const part = grid.features.some(f => f.land && f.border); // if land goes over map borders
const max = part ? 80 : 100; // max size
const lat = () => gauss(P(0.5) ? 40 : 60, 15, 25, 75); // latitude shift
@ -1485,14 +1544,12 @@ function rankCells() {
TIME && console.timeEnd("rankCells");
}
// regenerate some zones
// generate zones
function addZones(number = 1) {
TIME && console.time("addZones");
const data = [],
cells = pack.cells,
states = pack.states,
burgs = pack.burgs;
const {cells, states, burgs} = pack;
const used = new Uint8Array(cells.i.length); // to store used cells
const zonesData = [];
for (let i = 0; i < rn(Math.random() * 1.8 * number); i++) addInvasion(); // invasion of enemy lands
for (let i = 0; i < rn(Math.random() * 1.6 * number); i++) addRebels(); // rebels along a state border
@ -1506,6 +1563,8 @@ function addZones(number = 1) {
for (let i = 0; i < rn(Math.random() * 1.4 * number); i++) addFlood(); // flood on river banks
for (let i = 0; i < rn(Math.random() * 1.2 * number); i++) addTsunami(); // tsunami starting near coast
drawZones();
function addInvasion() {
const atWar = states.filter(s => s.diplomacy && s.diplomacy.some(d => d === "Enemy"));
if (!atWar.length) return;
@ -1546,7 +1605,7 @@ function addZones(number = 1) {
Intervention: 1
});
const name = getAdjective(invader.name) + " " + invasion;
data.push({name, type: "Invasion", cells: cellsArray, fill: "url(#hatch1)"});
zonesData.push({name, type: "Invasion", cells: cellsArray, fill: "url(#hatch1)"});
}
function addRebels() {
@ -1582,7 +1641,7 @@ function addZones(number = 1) {
const rebels = rw({Rebels: 5, Insurgents: 2, Mutineers: 1, Rioters: 1, Separatists: 1, Secessionists: 1, Insurrection: 2, Rebellion: 1, Conspiracy: 2});
const name = getAdjective(states[neib].name) + " " + rebels;
data.push({name, type: "Rebels", cells: cellsArray, fill: "url(#hatch3)"});
zonesData.push({name, type: "Rebels", cells: cellsArray, fill: "url(#hatch3)"});
}
function addProselytism() {
@ -1612,7 +1671,7 @@ function addZones(number = 1) {
}
const name = getAdjective(organized.name.split(" ")[0]) + " Proselytism";
data.push({name, type: "Proselytism", cells: cellsArray, fill: "url(#hatch6)"});
zonesData.push({name, type: "Proselytism", cells: cellsArray, fill: "url(#hatch6)"});
}
function addCrusade() {
@ -1624,7 +1683,7 @@ function addZones(number = 1) {
cellsArray.forEach(i => (used[i] = 1));
const name = getAdjective(heresy.name.split(" ")[0]) + " Crusade";
data.push({name, type: "Crusade", cells: cellsArray, fill: "url(#hatch6)"});
zonesData.push({name, type: "Crusade", cells: cellsArray, fill: "url(#hatch6)"});
}
function addDisease() {
@ -1661,7 +1720,7 @@ function addZones(number = 1) {
const type = rw({Fever: 5, Pestilence: 2, Flu: 2, Pox: 2, Smallpox: 2, Plague: 4, Cholera: 2, Dropsy: 1, Leprosy: 2});
const name = rw({[color()]: 4, [animal()]: 2, [adjective()]: 1}) + " " + type;
data.push({name, type: "Disease", cells: cellsArray, fill: "url(#hatch12)"});
zonesData.push({name, type: "Disease", cells: cellsArray, fill: "url(#hatch12)"});
}
function addDisaster() {
@ -1693,7 +1752,7 @@ function addZones(number = 1) {
const type = rw({Famine: 5, Dearth: 1, Drought: 3, Earthquake: 3, Tornadoes: 1, Wildfires: 1});
const name = getAdjective(burg.name) + " " + type;
data.push({name, type: "Disaster", cells: cellsArray, fill: "url(#hatch5)"});
zonesData.push({name, type: "Disaster", cells: cellsArray, fill: "url(#hatch5)"});
}
function addEruption() {
@ -1724,7 +1783,7 @@ function addZones(number = 1) {
});
}
data.push({name, type: "Disaster", cells: cellsArray, fill: "url(#hatch7)"});
zonesData.push({name, type: "Disaster", cells: cellsArray, fill: "url(#hatch7)"});
}
function addAvalanche() {
@ -1749,7 +1808,7 @@ function addZones(number = 1) {
const proper = getAdjective(Names.getCultureShort(cells.culture[cell]));
const name = proper + " Avalanche";
data.push({name, type: "Disaster", cells: cellsArray, fill: "url(#hatch5)"});
zonesData.push({name, type: "Disaster", cells: cellsArray, fill: "url(#hatch5)"});
}
function addFault() {
@ -1774,7 +1833,7 @@ function addZones(number = 1) {
const proper = getAdjective(Names.getCultureShort(cells.culture[cell]));
const name = proper + " Fault";
data.push({name, type: "Disaster", cells: cellsArray, fill: "url(#hatch2)"});
zonesData.push({name, type: "Disaster", cells: cellsArray, fill: "url(#hatch2)"});
}
function addFlood() {
@ -1804,7 +1863,7 @@ function addZones(number = 1) {
}
const name = getAdjective(burgs[cells.burg[cell]].name) + " Flood";
data.push({name, type: "Disaster", cells: cellsArray, fill: "url(#hatch13)"});
zonesData.push({name, type: "Disaster", cells: cellsArray, fill: "url(#hatch13)"});
}
function addTsunami() {
@ -1832,13 +1891,13 @@ function addZones(number = 1) {
const proper = getAdjective(Names.getCultureShort(cells.culture[cell]));
const name = proper + " Tsunami";
data.push({name, type: "Disaster", cells: cellsArray, fill: "url(#hatch13)"});
zonesData.push({name, type: "Disaster", cells: cellsArray, fill: "url(#hatch13)"});
}
void (function drawZones() {
function drawZones() {
zones
.selectAll("g")
.data(data)
.data(zonesData)
.enter()
.append("g")
.attr("id", (d, i) => "zone" + i)
@ -1854,7 +1913,7 @@ function addZones(number = 1) {
.attr("id", function (d) {
return this.parentNode.id + "_" + d;
});
})();
}
TIME && console.timeEnd("addZones");
}
@ -1881,16 +1940,18 @@ function showStatistics() {
INFO && console.log(stats);
}
const regenerateMap = debounce(function () {
const regenerateMap = debounce(async function () {
WARN && console.warn("Generate new random map");
showLoading();
closeDialogs("#worldConfigurator, #options3d");
customization = 0;
undraw();
resetZoom(1000);
generate();
undraw();
await generate();
restoreLayers();
if (ThreeD.options.isOn) ThreeD.redraw();
if ($("#worldConfigurator").is(":visible")) editWorld();
hideLoading();
}, 1000);
// clear the map

View file

@ -595,7 +595,7 @@ window.BurgsAndStates = (function () {
g.select("#stateLabel" + id).remove();
}
const path = p[1].length > 1 ? lineGen(p[1]) : `M${p[1][0][0] - 50},${p[1][0][1]}h${100}`;
const path = p[1].length > 1 ? round(lineGen(p[1])) : `M${p[1][0][0] - 50},${p[1][0][1]}h${100}`;
const textPath = t
.append("path")
.attr("d", path)
@ -621,7 +621,7 @@ window.BurgsAndStates = (function () {
const spans = lines.map((l, d) => {
example.text(l);
const left = example.node().getBBox().width / -2; // x offset
return `<tspan x="${left}px" dy="${d ? 1 : top}em">${l}</tspan>`;
return `<tspan x=${rn(left, 1)} dy="${d ? 1 : top}em">${l}</tspan>`;
});
const el = g

View file

@ -976,7 +976,7 @@ window.COA = (function () {
if (emblemShape.value === "state" && state && pack.states[state].coa) return pack.states[state].coa.shield;
if (pack.cultures[culture].shield) return pack.cultures[culture].shield;
console.error("Shield shape is not defined on culture level", pack.cultures[culture]);
ERROR && console.error("Shield shape is not defined on culture level", pack.cultures[culture]);
return "heater";
};

View file

@ -1938,7 +1938,9 @@ window.COArenderer = (function () {
g.setAttribute("id", charge + "_" + id);
return g.outerHTML;
})
.catch(err => console.error(err));
.catch(err => {
ERROR && console.error(err);
});
return fetched;
}

View file

@ -14,12 +14,14 @@ const fonts = [
{
family: "Almendra SC",
src: "url(https://fonts.gstatic.com/s/almendrasc/v13/Iure6Yx284eebowr7hbyTaZOrLQ.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
unicodeRange:
"U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
},
{
family: "Amatic SC",
src: "url(https://fonts.gstatic.com/s/amaticsc/v11/TUZ3zwprpvBS1izr_vOMscGKfrUC.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
unicodeRange:
"U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
},
{
family: "Architects Daughter",
@ -34,7 +36,8 @@ const fonts = [
{
family: "Caesar Dressing",
src: "url(https://fonts.gstatic.com/s/caesardressing/v6/yYLx0hLa3vawqtwdswbotmK4vrRHdrz7.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
unicodeRange:
"U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
},
{
family: "Cinzel",
@ -49,7 +52,8 @@ const fonts = [
{
family: "Fredericka the Great",
src: "url(https://fonts.gstatic.com/s/frederickathegreat/v6/9Bt33CxNwt7aOctW2xjbCstzwVKsIBVV--Sjxbc.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
unicodeRange:
"U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
},
{
family: "Gloria Hallelujah",
@ -74,12 +78,14 @@ const fonts = [
{
family: "MedievalSharp",
src: "url(https://fonts.gstatic.com/s/medievalsharp/v9/EvOJzAlL3oU5AQl2mP5KdgptMqhwMg.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
unicodeRange:
"U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
},
{
family: "Metamorphous",
src: "url(https://fonts.gstatic.com/s/metamorphous/v7/Wnz8HA03aAXcC39ZEX5y133EOyqs.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
unicodeRange:
"U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
},
{
family: "Montez",
@ -89,7 +95,8 @@ const fonts = [
{
family: "Nova Script",
src: "url(https://fonts.gstatic.com/s/novascript/v10/7Au7p_IpkSWSTWaFWkumvlQKGFw.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
unicodeRange:
"U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
},
{
family: "Orbitron",
@ -109,12 +116,14 @@ const fonts = [
{
family: "Uncial Antiqua",
src: "url(https://fonts.gstatic.com/s/uncialantiqua/v5/N0bM2S5WOex4OUbESzoESK-i-MfWQZQ.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
unicodeRange:
"U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
},
{
family: "Underdog",
src: "url(https://fonts.gstatic.com/s/underdog/v6/CHygV-jCElj7diMroWSlWV8.woff2)",
unicodeRange: "U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
unicodeRange:
"U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD"
},
{
family: "Yellowtail",
@ -243,7 +252,7 @@ async function addGoogleFont(family) {
})
.catch(err => {
tip(`Failed to load Google font ${family}`, true, "error", 4000);
console.error(err);
ERROR && console.error(err);
});
}

View file

@ -3,13 +3,44 @@
window.HeightmapGenerator = (function () {
let cells, p;
const generate = function () {
TIME && console.time("generateHeightmap");
const generate = async function () {
cells = grid.cells;
p = grid.points;
cells.h = new Uint8Array(grid.points.length);
const template = document.getElementById("templateInput").value;
const input = document.getElementById("templateInput");
const selectedId = input.selectedIndex >= 0 ? input.selectedIndex : 0;
const type = input.options[selectedId]?.parentElement?.label;
if (type === "Specific") {
// pre-defined heightmap
TIME && console.time("defineHeightmap");
return new Promise(resolve => {
// create canvas where 1px correcponds to a cell
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const {cellsX, cellsY} = grid;
canvas.width = cellsX;
canvas.height = cellsY;
// load heightmap into image and render to canvas
const img = new Image();
img.src = `./heightmaps/${input.value}.png`;
img.onload = function () {
ctx.drawImage(img, 0, 0, cellsX, cellsY);
const imageData = ctx.getImageData(0, 0, cellsX, cellsY);
assignColorsToHeight(imageData.data);
canvas.remove();
img.remove();
TIME && console.timeEnd("defineHeightmap");
resolve();
};
});
}
// heightmap template
TIME && console.time("generateHeightmap");
const template = input.value;
const templateString = HeightmapTemplates[template];
const steps = templateString.split("\n");
@ -79,8 +110,8 @@ window.HeightmapGenerator = (function () {
function addOneHill() {
const change = new Uint8Array(cells.h.length);
let limit = 0,
start;
let limit = 0;
let start;
let h = lim(getNumberInRange(height));
do {
@ -410,5 +441,13 @@ window.HeightmapGenerator = (function () {
return rand(min * length, max * length);
}
function assignColorsToHeight(imageData) {
for (let i = 0; i < cells.i.length; i++) {
const lightness = imageData[i * 4] / 255;
const powered = lightness < 0.2 ? lightness : 0.2 + (lightness - 0.2) ** 0.8;
cells.h[i] = minmax(Math.floor(powered * 100), 0, 100);
}
}
return {generate, addHill, addRange, addTrough, addStrait, addPit, smooth, modify};
})();

523
modules/io/auto-update.js Normal file
View file

@ -0,0 +1,523 @@
"use strict";
// update old .map version to the current one
export function resolveVersionConflicts(version) {
if (version < 1) {
// v1.0 added a new religions layer
relig = viewbox.insert("g", "#terrain").attr("id", "relig");
Religions.generate();
// v1.0 added a legend box
legend = svg.append("g").attr("id", "legend");
legend
.attr("font-family", "Almendra SC")
.attr("font-size", 13)
.attr("data-size", 13)
.attr("data-x", 99)
.attr("data-y", 93)
.attr("stroke-width", 2.5)
.attr("stroke", "#812929")
.attr("stroke-dasharray", "0 4 10 4")
.attr("stroke-linecap", "round");
// v1.0 separated drawBorders fron drawStates()
stateBorders = borders.append("g").attr("id", "stateBorders");
provinceBorders = borders.append("g").attr("id", "provinceBorders");
borders
.attr("opacity", null)
.attr("stroke", null)
.attr("stroke-width", null)
.attr("stroke-dasharray", null)
.attr("stroke-linecap", null)
.attr("filter", null);
stateBorders.attr("opacity", 0.8).attr("stroke", "#56566d").attr("stroke-width", 1).attr("stroke-dasharray", "2").attr("stroke-linecap", "butt");
provinceBorders.attr("opacity", 0.8).attr("stroke", "#56566d").attr("stroke-width", 0.5).attr("stroke-dasharray", "1").attr("stroke-linecap", "butt");
// v1.0 added state relations, provinces, forms and full names
provs = viewbox.insert("g", "#borders").attr("id", "provs").attr("opacity", 0.6);
BurgsAndStates.collectStatistics();
BurgsAndStates.generateCampaigns();
BurgsAndStates.generateDiplomacy();
BurgsAndStates.defineStateForms();
drawStates();
BurgsAndStates.generateProvinces();
drawBorders();
if (!layerIsOn("toggleBorders")) $("#borders").fadeOut();
if (!layerIsOn("toggleStates")) regions.attr("display", "none").selectAll("path").remove();
// v1.0 added zones layer
zones = viewbox.insert("g", "#borders").attr("id", "zones").attr("display", "none");
zones.attr("opacity", 0.6).attr("stroke", null).attr("stroke-width", 0).attr("stroke-dasharray", null).attr("stroke-linecap", "butt");
addZones();
if (!markers.selectAll("*").size()) {
Markers.generate();
turnButtonOn("toggleMarkers");
}
// v1.0 add fogging layer (state focus)
fogging = viewbox.insert("g", "#ruler").attr("id", "fogging-cont").attr("mask", "url(#fog)").append("g").attr("id", "fogging").style("display", "none");
fogging.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%");
defs.append("mask").attr("id", "fog").append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%").attr("fill", "white");
// v1.0 changes states opacity bask to regions level
if (statesBody.attr("opacity")) {
regions.attr("opacity", statesBody.attr("opacity"));
statesBody.attr("opacity", null);
}
// v1.0 changed labels to multi-lined
labels.selectAll("textPath").each(function () {
const text = this.textContent;
const shift = this.getComputedTextLength() / -1.5;
this.innerHTML = `<tspan x="${shift}">${text}</tspan>`;
});
// v1.0 added new biome - Wetland
biomesData.name.push("Wetland");
biomesData.color.push("#0b9131");
biomesData.habitability.push(12);
}
if (version < 1.1) {
// v1.0 initial code had a bug with religion layer id
if (!relig.size()) relig = viewbox.insert("g", "#terrain").attr("id", "relig");
// v1.0 initially has Sympathy status then relaced with Friendly
for (const s of pack.states) {
if (!s.diplomacy) continue;
s.diplomacy = s.diplomacy.map(r => (r === "Sympathy" ? "Friendly" : r));
}
// labels should be toggled via style attribute, so remove display attribute
labels.attr("display", null);
// v1.0 added religions heirarchy tree
if (pack.religions[1] && !pack.religions[1].code) {
pack.religions
.filter(r => r.i)
.forEach(r => {
r.origin = 0;
r.code = r.name.slice(0, 2);
});
}
if (!document.getElementById("freshwater")) {
lakes.append("g").attr("id", "freshwater");
lakes.select("#freshwater").attr("opacity", 0.5).attr("fill", "#a6c1fd").attr("stroke", "#5f799d").attr("stroke-width", 0.7).attr("filter", null);
}
if (!document.getElementById("salt")) {
lakes.append("g").attr("id", "salt");
lakes.select("#salt").attr("opacity", 0.5).attr("fill", "#409b8a").attr("stroke", "#388985").attr("stroke-width", 0.7).attr("filter", null);
}
// v1.1 added new lake and coast groups
if (!document.getElementById("sinkhole")) {
lakes.append("g").attr("id", "sinkhole");
lakes.append("g").attr("id", "frozen");
lakes.append("g").attr("id", "lava");
lakes.select("#sinkhole").attr("opacity", 1).attr("fill", "#5bc9fd").attr("stroke", "#53a3b0").attr("stroke-width", 0.7).attr("filter", null);
lakes.select("#frozen").attr("opacity", 0.95).attr("fill", "#cdd4e7").attr("stroke", "#cfe0eb").attr("stroke-width", 0).attr("filter", null);
lakes.select("#lava").attr("opacity", 0.7).attr("fill", "#90270d").attr("stroke", "#f93e0c").attr("stroke-width", 2).attr("filter", "url(#crumpled)");
coastline.append("g").attr("id", "sea_island");
coastline.append("g").attr("id", "lake_island");
coastline.select("#sea_island").attr("opacity", 0.5).attr("stroke", "#1f3846").attr("stroke-width", 0.7).attr("filter", "url(#dropShadow)");
coastline.select("#lake_island").attr("opacity", 1).attr("stroke", "#7c8eaf").attr("stroke-width", 0.35).attr("filter", null);
}
// v1.1 features stores more data
defs.select("#land").selectAll("path").remove();
defs.select("#water").selectAll("path").remove();
coastline.selectAll("path").remove();
lakes.selectAll("path").remove();
drawCoastline();
}
if (version < 1.11) {
// v1.11 added new attributes
terrs.attr("scheme", "bright").attr("terracing", 0).attr("skip", 5).attr("relax", 0).attr("curve", 0);
svg.select("#oceanic > *").attr("id", "oceanicPattern");
oceanLayers.attr("layers", "-6,-3,-1");
gridOverlay.attr("type", "pointyHex").attr("size", 10);
// v1.11 added cultures heirarchy tree
if (pack.cultures[1] && !pack.cultures[1].code) {
pack.cultures
.filter(c => c.i)
.forEach(c => {
c.origin = 0;
c.code = c.name.slice(0, 2);
});
}
// v1.11 had an issue with fogging being displayed on load
unfog();
// v1.2 added new terrain attributes
if (!terrain.attr("set")) terrain.attr("set", "simple");
if (!terrain.attr("size")) terrain.attr("size", 1);
if (!terrain.attr("density")) terrain.attr("density", 0.4);
}
if (version < 1.21) {
// v1.11 replaced "display" attribute by "display" style
viewbox.selectAll("g").each(function () {
if (this.hasAttribute("display")) {
this.removeAttribute("display");
this.style.display = "none";
}
});
// v1.21 added rivers data to pack
pack.rivers = []; // rivers data
rivers.selectAll("path").each(function () {
const i = +this.id.slice(5);
const length = this.getTotalLength() / 2;
const s = this.getPointAtLength(length),
e = this.getPointAtLength(0);
const source = findCell(s.x, s.y),
mouth = findCell(e.x, e.y);
const name = Rivers.getName(mouth);
const type = length < 25 ? rw({Creek: 9, River: 3, Brook: 3, Stream: 1}) : "River";
pack.rivers.push({i, parent: 0, length, source, mouth, basin: i, name, type});
});
}
if (version < 1.22) {
// v1.22 changed state neighbors from Set object to array
BurgsAndStates.collectStatistics();
}
if (version < 1.3) {
// v1.3 added global options object
const winds = options.slice(); // previostly wind was saved in settings[19]
const year = rand(100, 2000);
const era = Names.getBaseShort(P(0.7) ? 1 : rand(nameBases.length)) + " Era";
const eraShort = era[0] + "E";
const military = Military.getDefaultOptions();
options = {winds, year, era, eraShort, military};
// v1.3 added campaings data for all states
BurgsAndStates.generateCampaigns();
// v1.3 added militry layer
armies = viewbox.insert("g", "#icons").attr("id", "armies");
armies.attr("opacity", 1).attr("fill-opacity", 1).attr("font-size", 6).attr("box-size", 3).attr("stroke", "#000").attr("stroke-width", 0.3);
turnButtonOn("toggleMilitary");
Military.generate();
}
if (version < 1.4) {
// v1.35 added dry lakes
if (!lakes.select("#dry").size()) {
lakes.append("g").attr("id", "dry");
lakes.select("#dry").attr("opacity", 1).attr("fill", "#c9bfa7").attr("stroke", "#8e816f").attr("stroke-width", 0.7).attr("filter", null);
}
// v1.4 added ice layer
ice = viewbox.insert("g", "#coastline").attr("id", "ice").style("display", "none");
ice.attr("opacity", null).attr("fill", "#e8f0f6").attr("stroke", "#e8f0f6").attr("stroke-width", 1).attr("filter", "url(#dropShadow05)");
drawIce();
// v1.4 added icon and power attributes for units
for (const unit of options.military) {
if (!unit.icon) unit.icon = getUnitIcon(unit.type);
if (!unit.power) unit.power = unit.crew;
}
function getUnitIcon(type) {
if (type === "naval") return "🌊";
if (type === "ranged") return "🏹";
if (type === "mounted") return "🐴";
if (type === "machinery") return "💣";
if (type === "armored") return "🐢";
if (type === "aviation") return "🦅";
if (type === "magical") return "🔮";
else return "⚔️";
}
// v1.4 added state reference for regiments
pack.states.filter(s => s.military).forEach(s => s.military.forEach(r => (r.state = s.i)));
}
if (version < 1.5) {
// not need to store default styles from v 1.5
localStorage.removeItem("styleClean");
localStorage.removeItem("styleGloom");
localStorage.removeItem("styleAncient");
localStorage.removeItem("styleMonochrome");
// v1.5 cultures has shield attribute
pack.cultures.forEach(culture => {
if (culture.removed) return;
culture.shield = Cultures.getRandomShield();
});
// v1.5 added burg type value
pack.burgs.forEach(burg => {
if (!burg.i || burg.removed) return;
burg.type = BurgsAndStates.getType(burg.cell, burg.port);
});
// v1.5 added emblems
defs.append("g").attr("id", "defs-emblems");
emblems = viewbox.insert("g", "#population").attr("id", "emblems").style("display", "none");
emblems.append("g").attr("id", "burgEmblems");
emblems.append("g").attr("id", "provinceEmblems");
emblems.append("g").attr("id", "stateEmblems");
regenerateEmblems();
toggleEmblems();
// v1.5 changed releif icons data
terrain.selectAll("use").each(function () {
const type = this.getAttribute("data-type") || this.getAttribute("xlink:href");
this.removeAttribute("xlink:href");
this.removeAttribute("data-type");
this.removeAttribute("data-size");
this.setAttribute("href", type);
});
}
if (version < 1.6) {
// v1.6 changed rivers data
for (const river of pack.rivers) {
const el = document.getElementById("river" + river.i);
if (el) {
river.widthFactor = +el.getAttribute("data-width");
el.removeAttribute("data-width");
el.removeAttribute("data-increment");
river.discharge = pack.cells.fl[river.mouth] || 1;
river.width = rn(river.length / 100, 2);
river.sourceWidth = 0.1;
} else {
Rivers.remove(river.i);
}
}
// v1.6 changed lakes data
for (const f of pack.features) {
if (f.type !== "lake") continue;
if (f.evaporation) continue;
f.flux = f.flux || f.cells * 3;
f.temp = grid.cells.temp[pack.cells.g[f.firstCell]];
f.height = f.height || d3.min(pack.cells.c[f.firstCell].map(c => pack.cells.h[c]).filter(h => h >= 20));
const height = (f.height - 18) ** heightExponentInput.value;
const evaporation = ((700 * (f.temp + 0.006 * height)) / 50 + 75) / (80 - f.temp);
f.evaporation = rn(evaporation * f.cells);
f.name = f.name || Lakes.getName(f);
delete f.river;
}
}
if (version < 1.61) {
// v1.61 changed rulers data
ruler.style("display", null);
rulers = new Rulers();
ruler.selectAll(".ruler > .white").each(function () {
const x1 = +this.getAttribute("x1");
const y1 = +this.getAttribute("y1");
const x2 = +this.getAttribute("x2");
const y2 = +this.getAttribute("y2");
if (isNaN(x1) || isNaN(y1) || isNaN(x2) || isNaN(y2)) return;
const points = [
[x1, y1],
[x2, y2]
];
rulers.create(Ruler, points);
});
ruler.selectAll("g.opisometer").each(function () {
const pointsString = this.dataset.points;
if (!pointsString) return;
const points = JSON.parse(pointsString);
rulers.create(Opisometer, points);
});
ruler.selectAll("path.planimeter").each(function () {
const length = this.getTotalLength();
if (length < 30) return;
const step = length > 1000 ? 40 : length > 400 ? 20 : 10;
const increment = length / Math.ceil(length / step);
const points = [];
for (let i = 0; i <= length; i += increment) {
const point = this.getPointAtLength(i);
points.push([point.x | 0, point.y | 0]);
}
rulers.create(Planimeter, points);
});
ruler.selectAll("*").remove();
if (rulers.data.length) {
turnButtonOn("toggleRulers");
rulers.draw();
} else turnButtonOff("toggleRulers");
// 1.61 changed oceanicPattern from rect to image
const pattern = document.getElementById("oceanic");
const filter = pattern.firstElementChild.getAttribute("filter");
const href = filter ? "./images/" + filter.replace("url(#", "").replace(")", "") + ".png" : "";
pattern.innerHTML = `<image id="oceanicPattern" href=${href} width="100" height="100" opacity="0.2"></image>`;
}
if (version < 1.62) {
// v1.62 changed grid data
gridOverlay.attr("size", null);
}
if (version < 1.63) {
// v1.63 changed ocean pattern opacity element
const oceanPattern = document.getElementById("oceanPattern");
if (oceanPattern) oceanPattern.removeAttribute("opacity");
const oceanicPattern = document.getElementById("oceanicPattern");
if (!oceanicPattern.getAttribute("opacity")) oceanicPattern.setAttribute("opacity", 0.2);
// v 1.63 moved label text-shadow from css to editable inline style
burgLabels.select("#cities").style("text-shadow", "white 0 0 4px");
burgLabels.select("#towns").style("text-shadow", "white 0 0 4px");
labels.select("#states").style("text-shadow", "white 0 0 4px");
labels.select("#addedLabels").style("text-shadow", "white 0 0 4px");
}
if (version < 1.64) {
// v1.64 change states style
const opacity = regions.attr("opacity");
const filter = regions.attr("filter");
statesBody.attr("opacity", opacity).attr("filter", filter);
statesHalo.attr("opacity", opacity).attr("filter", "blur(5px)");
regions.attr("opacity", null).attr("filter", null);
}
if (version < 1.65) {
// v1.65 changed rivers data
d3.select("#rivers").attr("style", null); // remove style to unhide layer
const {cells, rivers} = pack;
const defaultWidthFactor = rn(1 / (pointsInput.dataset.cells / 10000) ** 0.25, 2);
for (const river of rivers) {
const node = document.getElementById("river" + river.i);
if (node && !river.cells) {
const riverCells = [];
const riverPoints = [];
const length = node.getTotalLength() / 2;
if (!length) continue;
const segments = Math.ceil(length / 6);
const increment = length / segments;
for (let i = 0; i <= segments; i++) {
const shift = increment * i;
const {x: x1, y: y1} = node.getPointAtLength(length + shift);
const {x: x2, y: y2} = node.getPointAtLength(length - shift);
const x = rn((x1 + x2) / 2, 1);
const y = rn((y1 + y2) / 2, 1);
const cell = findCell(x, y);
riverPoints.push([x, y]);
riverCells.push(cell);
}
river.cells = riverCells;
river.points = riverPoints;
}
river.widthFactor = defaultWidthFactor;
cells.i.forEach(i => {
const riverInWater = cells.r[i] && cells.h[i] < 20;
if (riverInWater) cells.r[i] = 0;
});
}
}
if (version < 1.652) {
// remove style to unhide layers
rivers.attr("style", null);
borders.attr("style", null);
}
if (version < 1.7) {
// v1.7 changed markers data
const defs = document.getElementById("defs-markers");
const markersGroup = document.getElementById("markers");
if (defs && markersGroup) {
const markerElements = markersGroup.querySelectorAll("use");
const rescale = +markersGroup.getAttribute("rescale");
pack.markers = Array.from(markerElements).map((el, i) => {
const id = el.getAttribute("id");
const note = notes.find(note => note.id === id);
if (note) note.id = `marker${i}`;
let x = +el.dataset.x;
let y = +el.dataset.y;
const transform = el.getAttribute("transform");
if (transform) {
const [dx, dy] = parseTransform(transform);
if (dx) x += +dx;
if (dy) y += +dy;
}
const cell = findCell(x, y);
const size = rn(rescale ? el.dataset.size * 30 : el.getAttribute("width"), 1);
const href = el.href.baseVal;
const type = href.replace("#marker_", "");
const symbol = defs?.querySelector(`symbol${href}`);
const text = symbol?.querySelector("text");
const circle = symbol?.querySelector("circle");
const icon = text?.innerHTML;
const px = text && Number(text.getAttribute("font-size")?.replace("px", ""));
const dx = text && Number(text.getAttribute("x")?.replace("%", ""));
const dy = text && Number(text.getAttribute("y")?.replace("%", ""));
const fill = circle && circle.getAttribute("fill");
const stroke = circle && circle.getAttribute("stroke");
const marker = {i, icon, type, x, y, size, cell};
if (size && size !== 30) marker.size = size;
if (!isNaN(px) && px !== 12) marker.px = px;
if (!isNaN(dx) && dx !== 50) marker.dx = dx;
if (!isNaN(dy) && dy !== 50) marker.dy = dy;
if (fill && fill !== "#ffffff") marker.fill = fill;
if (stroke && stroke !== "#000000") marker.stroke = stroke;
if (circle?.getAttribute("opacity") === "0") marker.pin = "no";
return marker;
});
markersGroup.style.display = null;
defs?.remove();
markerElements.forEach(el => el.remove());
if (layerIsOn("markers")) drawMarkers();
}
}
if (version < 1.72) {
// v1.72 renamed custom style presets
const storedStyles = Object.keys(localStorage).filter(key => key.startsWith("style"));
storedStyles.forEach(styleName => {
const style = localStorage.getItem(styleName);
const newStyleName = styleName.replace(/^style/, customPresetPrefix);
localStorage.setItem(newStyleName, style);
localStorage.removeItem(styleName);
});
}
if (version < 1.73) {
// v1.73 moved the hatching patterns out of the user's SVG
document.getElementById("hatching")?.remove();
// v1.73 added zone type to UI, ensure type is populated
const zones = Array.from(document.querySelectorAll("#zones > g"));
zones.forEach(zone => {
if (!zone.dataset.type) zone.dataset.type = "Unknown";
});
}
}

View file

@ -12,7 +12,6 @@ async load(filename): load filename from provider
async list(): list available filenames at provider
async getLink(filePath): get shareable link for file
restore(): restore access tokens from storage if possible
*/
window.Cloud = (function () {
@ -32,38 +31,40 @@ window.Cloud = (function () {
token: null, // Access token
api: null,
restore() {
this.token = getToken(this.name);
if (this.token) this.connect(this.token);
},
async call(name, param) {
try {
if (!this.api) await this.initialize();
return await this.api[name](param);
} catch (e) {
if (e.name !== "DropboxResponseError") throw e;
// retry with auth
await this.auth();
await this.auth(); // retry with auth
return await this.api[name](param);
}
},
connect(token) {
const clientId = this.clientId;
const auth = new Dropbox.DropboxAuth({clientId});
initialize() {
const token = getToken(this.name);
if (token) {
return this.connect(token);
} else {
return this.auth();
}
},
async connect(token) {
await import("https://unpkg.com/dropbox@10.8.0/dist/Dropbox-sdk.min.js");
const auth = new Dropbox.DropboxAuth({clientId: this.clientId});
auth.setAccessToken(token);
this.api = new Dropbox.Dropbox({auth});
},
async save(fileName, contents) {
if (!this.api) await this.auth();
const resp = this.call("filesUpload", {path: "/" + fileName, contents});
const resp = await this.call("filesUpload", {path: "/" + fileName, contents});
DEBUG && console.log("Dropbox response:", resp);
return true;
},
async load(path) {
if (!this.api) await this.auth();
const resp = await this.call("filesDownload", {path});
const blob = resp.result.fileBlob;
if (!blob) throw new Error("Invalid response from dropbox.");
@ -71,22 +72,23 @@ window.Cloud = (function () {
},
async list() {
if (!this.api) return null;
const resp = await this.call("filesListFolder", {path: ""});
return resp.result.entries.map(e => ({name: e.name, path: e.path_lower}));
},
auth() {
const url = window.location.origin + window.location.pathname + "dropbox.html";
this.authWindow = window.open(url, "auth", "width=640,height=480");
// child window expected to call
// window.opener.Cloud.providers.dropbox.setDropBoxToken (see below)
const width = 640;
const height = 480;
const left = window.innerWidth / 2 - width / 2;
const top = window.innerHeight / 2 - height / 2.5;
this.authWindow = window.open("./dropbox.html", "auth", `width=640, height=${height}, top=${top}, left=${left}}`);
return new Promise((resolve, reject) => {
const watchDog = () => {
const watchDog = setTimeout(() => {
this.authWindow.close();
reject(new Error("Timeout. No auth for dropbox."));
};
setTimeout(watchDog, 120 * 1000);
reject(new Error("Timeout. No auth for Dropbox"));
}, 120 * 1000);
window.addEventListener("dropboxauth", e => {
clearTimeout(watchDog);
resolve();
@ -94,46 +96,34 @@ window.Cloud = (function () {
});
},
// Callback function for auth window.
setDropBoxToken(token) {
// Callback function for auth window
async setDropBoxToken(token) {
DEBUG && console.log("Access token:", token);
setToken(this.name, token);
this.connect(token);
await this.connect(token);
this.authWindow.close();
window.dispatchEvent(new Event("dropboxauth"));
},
returnError(errorDescription) {
console.error(errorDescription);
tip(errorDescription.replaceAll("+", " "), true, "error", 4000);
this.authWindow.close();
},
async getLink(path) {
if (!this.api) await this.auth();
let resp;
// return existitng shared link
const sharedLinks = await this.call("sharingListSharedLinks", {path});
if (sharedLinks.result.links.length) return resp.result.links[0].url;
// already exists?
resp = await this.call("sharingListSharedLinks", {path});
if (resp.result.links.length) return resp.result.links[0].url;
// create new
resp = await this.call("sharingCreateSharedLinkWithSettings", {
path,
settings: {
require_password: false,
audience: "public",
access: "viewer",
requested_visibility: "public",
allow_download: true
}
});
// create new shared link
const settings = {require_password: false, audience: "public", access: "viewer", requested_visibility: "public", allow_download: true};
const resp = await this.call("sharingCreateSharedLinkWithSettings", {path, settings});
DEBUG && console.log("Dropbox link object:", resp.result);
return resp.result.url;
}
};
// register providers here:
const providers = {
dropbox: DBP
};
// restore all providers at startup
for (const p of Object.values(providers)) p.restore();
const providers = {dropbox: DBP};
return {providers};
})();

209
modules/io/export-json.js Normal file
View file

@ -0,0 +1,209 @@
function exportToJson(type) {
if (customization) return tip("Map cannot be saved when edit mode is active, please exit the mode and retry", false, "error");
closeDialogs("#alert");
const typeMap = {
Full: getFullDataJson,
Minimal: getMinimalDataJson,
PackCells: getPackCellsDataJson,
GridCells: getGridCellsDataJson,
};
const mapData = typeMap[type]();
const blob = new Blob([mapData], {type: "application/json"});
const URL = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.download = getFileName(type) + ".json";
link.href = URL;
link.click();
tip(`${link.download} is saved. Open "Downloads" screen (CTRL + J) to check`, true, "success", 7000);
window.URL.revokeObjectURL(URL);
}
function getMapInfo() {
const info = {
version,
description: "Azgaar's Fantasy Map Generator output: azgaar.github.io/Fantasy-map-generator",
exportedAt: new Date().toISOString(),
mapName: mapName.value,
seed,
mapId
};
return info;
}
function getSettings() {
const settings = {
distanceUnit: distanceUnitInput.value,
distanceScale: distanceScaleInput.value,
areaUnit: areaUnit.value,
heightUnit: heightUnit.value,
heightExponent: heightExponentInput.value,
temperatureScale: temperatureScale.value,
barSize: barSizeInput.value,
barLabel: barLabel.value,
barBackOpacity: barBackOpacity.value,
barBackColor: barBackColor.value,
barPosX: barPosX.value,
barPosY: barPosY.value,
populationRate: populationRate,
urbanization: urbanization,
mapSize: mapSizeOutput.value,
latitudeO: latitudeOutput.value,
temperatureEquator: temperatureEquatorOutput.value,
temperaturePole: temperaturePoleOutput.value,
prec: precOutput.value,
options: options,
mapName: mapName.value,
hideLabels: hideLabels.checked,
stylePreset: stylePreset.value,
rescaleLabels: rescaleLabels.checked,
urbanDensity: urbanDensity
};
return settings;
}
function getPackCellsData() {
const cellConverted = {
i: Array.from(pack.cells.i),
v: pack.cells.v,
c: pack.cells.c,
p: pack.cells.p,
g: Array.from(pack.cells.g),
h: Array.from(pack.cells.h),
area: Array.from(pack.cells.area),
f: Array.from(pack.cells.f),
t: Array.from(pack.cells.t),
haven: Array.from(pack.cells.haven),
harbor: Array.from(pack.cells.harbor),
fl: Array.from(pack.cells.fl),
r: Array.from(pack.cells.r),
conf: Array.from(pack.cells.conf),
biome: Array.from(pack.cells.biome),
s: Array.from(pack.cells.s),
pop: Array.from(pack.cells.pop),
culture: Array.from(pack.cells.culture),
burg: Array.from(pack.cells.burg),
road: Array.from(pack.cells.road),
crossroad: Array.from(pack.cells.crossroad),
state: Array.from(pack.cells.state),
religion: Array.from(pack.cells.religion),
province: Array.from(pack.cells.province)
};
const cellObjArr = [];
{
cellConverted.i.forEach(value => {
const cellobj = {
i: value,
v: cellConverted.v[value],
c: cellConverted.c[value],
p: cellConverted.p[value],
g: cellConverted.g[value],
h: cellConverted.h[value],
area: cellConverted.area[value],
f: cellConverted.f[value],
t: cellConverted.t[value],
haven: cellConverted.haven[value],
harbor: cellConverted.harbor[value],
fl: cellConverted.fl[value],
r: cellConverted.r[value],
conf: cellConverted.conf[value],
biome: cellConverted.biome[value],
s: cellConverted.s[value],
pop: cellConverted.pop[value],
culture: cellConverted.culture[value],
burg: cellConverted.burg[value],
road: cellConverted.road[value],
crossroad: cellConverted.crossroad[value],
state: cellConverted.state[value],
religion: cellConverted.religion[value],
province: cellConverted.province[value]
};
cellObjArr.push(cellobj);
});
}
const cellsData = {
cells: cellObjArr,
features: pack.features,
cultures: pack.cultures,
burgs: pack.burgs,
states: pack.states,
provinces: pack.provinces,
religions: pack.religions,
rivers: pack.rivers,
markers: pack.markers
};
return cellsData;
}
//data only containing graphical appearance
function getGridCellsData(){
const gridData = {
spacing: grid.spacing,
cellsY: grid.cellsY,
cellsX: grid.cellsX,
points: grid.points,
boundary: grid.boundary
}
return gridData
}
function getFullDataJson() {
TIME && console.time("getFullDataJson");
const info = getMapInfo();
const settings = getSettings();
const cells = getPackCellsData();
const exportData = {info, settings, coords: mapCoordinates, cells, biomes: biomesData, notes, nameBases};
TIME && console.timeEnd("getFullDataJson");
return JSON.stringify(exportData);
}
// data excluding cells
function getMinimalDataJson() {
TIME && console.time("getMinimalDataJson");
const info = getMapInfo();
const settings = getSettings();
const packData = {
features: pack.features,
cultures: pack.cultures,
burgs: pack.burgs,
states: pack.states,
provinces: pack.provinces,
religions: pack.religions,
rivers: pack.rivers,
markers: pack.markers
};
const exportData = {info, settings, coords: mapCoordinates, pack: packData, biomes: biomesData, notes, nameBases};
TIME && console.timeEnd("getMinimalDataJson");
return JSON.stringify(exportData);
}
function getPackCellsDataJson() {
TIME && console.time("getCellsDataJson");
const info = getMapInfo();
const cells = getPackCellsData();
const exportData = {info, cells};
TIME && console.timeEnd("getCellsDataJson");
return JSON.stringify(exportData);
}
function getGridCellsDataJson() {
TIME && console.time("getGridCellsDataJson");
const info = getMapInfo();
const gridCells = getGridCellsData()
const exportData = {info,gridCells};
TIME && console.log("getGridCellsDataJson");
return JSON.stringify(exportData);
}

View file

@ -262,19 +262,19 @@ async function getMapURL(type, options = {}) {
if (pattern) cloneDefs.appendChild(pattern.cloneNode(true));
}
if (!cloneEl.getElementById("hatching").children.length) cloneEl.getElementById("hatching")?.remove(); // remove unused hatching group
if (!cloneEl.getElementById("fogging-cont")) cloneEl.getElementById("fog")?.remove(); // remove unused fog
if (!cloneEl.getElementById("regions")) cloneEl.getElementById("statePaths")?.remove(); // removed unused statePaths
if (!cloneEl.getElementById("labels")) cloneEl.getElementById("textPaths")?.remove(); // removed unused textPaths
// add armies style
if (cloneEl.getElementById("armies"))
if (cloneEl.getElementById("armies")) {
cloneEl.insertAdjacentHTML(
"afterbegin",
"<style>#armies text {stroke: none; fill: #fff; text-shadow: 0 0 4px #000; dominant-baseline: central; text-anchor: middle; font-family: Helvetica; fill-opacity: 1;}#armies text.regimentIcon {font-size: .8em;}</style>"
);
}
// add xlink: for href to support svg1.1
// add xlink: for href to support svg 1.1
if (type === "svg") {
cloneEl.querySelectorAll("[href]").forEach(el => {
const href = el.getAttribute("href");
@ -283,6 +283,16 @@ async function getMapURL(type, options = {}) {
});
}
// add hatchings
const hatchingUsers = cloneEl.querySelectorAll(`[fill^='url(#hatch']`);
const hatchingFills = unique(Array.from(hatchingUsers).map(el => el.getAttribute("fill")));
const hatchingIds = hatchingFills.map(fill => fill.slice(5, -1));
for (const hatchingId of hatchingIds) {
const hatching = svgDefs.getElementById(hatchingId);
if (hatching) cloneDefs.appendChild(hatching.cloneNode(true));
}
// load fonts
const usedFonts = getUsedFonts(cloneEl);
const fontsToLoad = usedFonts.filter(font => font.src);
if (fontsToLoad.length) {

View file

@ -145,7 +145,7 @@ function parseLoadedResult(result) {
const mapVersion = parseFloat(mapData[0].split("|")[0] || mapData[0]);
return [mapData, mapVersion];
} catch (error) {
console.error(error);
ERROR && console.error(error);
return [null, null];
}
}
@ -182,18 +182,15 @@ function showUploadMessage(type, mapData, mapVersion) {
$("#alert").dialog({title, buttons});
}
function parseLoadedData(data) {
async function parseLoadedData(data) {
try {
// exit customization
if (window.closeDialogs) closeDialogs();
customization = 0;
if (customizationMenu.offsetParent) styleTab.click();
const reliefIcons = document.getElementById("defs-relief").innerHTML; // save relief icons
const hatching = document.getElementById("hatching").cloneNode(true); // save hatching
void (function parseParameters() {
const params = data[0].split("|");
void (function parseParameters() {
if (params[3]) {
seed = params[3];
optionsSeed.value = seed;
@ -423,514 +420,13 @@ function parseLoadedData(data) {
legend.on("mousemove", () => tip("Drag to change the position. Click to hide the legend")).on("click", () => clearLegend());
})();
void (function resolveVersionConflicts() {
const version = parseFloat(data[0].split("|")[0]);
if (version < 0.9) {
// 0.9 has additional relief icons to be included into older maps
document.getElementById("defs-relief").innerHTML = reliefIcons;
{
// dynamically import and run auto-udpdate script
const version = parseFloat(params[0]);
const {resolveVersionConflicts} = await import("./auto-update.js");
resolveVersionConflicts(version);
}
if (version < 1) {
// 1.0 adds a new religions layer
relig = viewbox.insert("g", "#terrain").attr("id", "relig");
Religions.generate();
// 1.0 adds a legend box
legend = svg.append("g").attr("id", "legend");
legend
.attr("font-family", "Almendra SC")
.attr("font-size", 13)
.attr("data-size", 13)
.attr("data-x", 99)
.attr("data-y", 93)
.attr("stroke-width", 2.5)
.attr("stroke", "#812929")
.attr("stroke-dasharray", "0 4 10 4")
.attr("stroke-linecap", "round");
// 1.0 separated drawBorders fron drawStates()
stateBorders = borders.append("g").attr("id", "stateBorders");
provinceBorders = borders.append("g").attr("id", "provinceBorders");
borders
.attr("opacity", null)
.attr("stroke", null)
.attr("stroke-width", null)
.attr("stroke-dasharray", null)
.attr("stroke-linecap", null)
.attr("filter", null);
stateBorders.attr("opacity", 0.8).attr("stroke", "#56566d").attr("stroke-width", 1).attr("stroke-dasharray", "2").attr("stroke-linecap", "butt");
provinceBorders.attr("opacity", 0.8).attr("stroke", "#56566d").attr("stroke-width", 0.5).attr("stroke-dasharray", "1").attr("stroke-linecap", "butt");
// 1.0 adds state relations, provinces, forms and full names
provs = viewbox.insert("g", "#borders").attr("id", "provs").attr("opacity", 0.6);
BurgsAndStates.collectStatistics();
BurgsAndStates.generateCampaigns();
BurgsAndStates.generateDiplomacy();
BurgsAndStates.defineStateForms();
drawStates();
BurgsAndStates.generateProvinces();
drawBorders();
if (!layerIsOn("toggleBorders")) $("#borders").fadeOut();
if (!layerIsOn("toggleStates")) regions.attr("display", "none").selectAll("path").remove();
// 1.0 adds hatching
document.getElementsByTagName("defs")[0].appendChild(hatching);
// 1.0 adds zones layer
zones = viewbox.insert("g", "#borders").attr("id", "zones").attr("display", "none");
zones.attr("opacity", 0.6).attr("stroke", null).attr("stroke-width", 0).attr("stroke-dasharray", null).attr("stroke-linecap", "butt");
addZones();
if (!markers.selectAll("*").size()) {
Markers.generate();
turnButtonOn("toggleMarkers");
}
// 1.0 add fogging layer (state focus)
fogging = viewbox.insert("g", "#ruler").attr("id", "fogging-cont").attr("mask", "url(#fog)").append("g").attr("id", "fogging").style("display", "none");
fogging.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%");
defs.append("mask").attr("id", "fog").append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%").attr("fill", "white");
// 1.0 changes states opacity bask to regions level
if (statesBody.attr("opacity")) {
regions.attr("opacity", statesBody.attr("opacity"));
statesBody.attr("opacity", null);
}
// 1.0 changed labels to multi-lined
labels.selectAll("textPath").each(function () {
const text = this.textContent;
const shift = this.getComputedTextLength() / -1.5;
this.innerHTML = `<tspan x="${shift}">${text}</tspan>`;
});
// 1.0 added new biome - Wetland
biomesData.name.push("Wetland");
biomesData.color.push("#0b9131");
biomesData.habitability.push(12);
}
if (version < 1.1) {
// v 1.0 initial code had a bug with religion layer id
if (!relig.size()) relig = viewbox.insert("g", "#terrain").attr("id", "relig");
// v 1.0 initially has Sympathy status then relaced with Friendly
for (const s of pack.states) {
if (!s.diplomacy) continue;
s.diplomacy = s.diplomacy.map(r => (r === "Sympathy" ? "Friendly" : r));
}
// labels should be toggled via style attribute, so remove display attribute
labels.attr("display", null);
// v 1.0 added religions heirarchy tree
if (pack.religions[1] && !pack.religions[1].code) {
pack.religions
.filter(r => r.i)
.forEach(r => {
r.origin = 0;
r.code = r.name.slice(0, 2);
});
}
if (!document.getElementById("freshwater")) {
lakes.append("g").attr("id", "freshwater");
lakes.select("#freshwater").attr("opacity", 0.5).attr("fill", "#a6c1fd").attr("stroke", "#5f799d").attr("stroke-width", 0.7).attr("filter", null);
}
if (!document.getElementById("salt")) {
lakes.append("g").attr("id", "salt");
lakes.select("#salt").attr("opacity", 0.5).attr("fill", "#409b8a").attr("stroke", "#388985").attr("stroke-width", 0.7).attr("filter", null);
}
// v 1.1 added new lake and coast groups
if (!document.getElementById("sinkhole")) {
lakes.append("g").attr("id", "sinkhole");
lakes.append("g").attr("id", "frozen");
lakes.append("g").attr("id", "lava");
lakes.select("#sinkhole").attr("opacity", 1).attr("fill", "#5bc9fd").attr("stroke", "#53a3b0").attr("stroke-width", 0.7).attr("filter", null);
lakes.select("#frozen").attr("opacity", 0.95).attr("fill", "#cdd4e7").attr("stroke", "#cfe0eb").attr("stroke-width", 0).attr("filter", null);
lakes.select("#lava").attr("opacity", 0.7).attr("fill", "#90270d").attr("stroke", "#f93e0c").attr("stroke-width", 2).attr("filter", "url(#crumpled)");
coastline.append("g").attr("id", "sea_island");
coastline.append("g").attr("id", "lake_island");
coastline.select("#sea_island").attr("opacity", 0.5).attr("stroke", "#1f3846").attr("stroke-width", 0.7).attr("filter", "url(#dropShadow)");
coastline.select("#lake_island").attr("opacity", 1).attr("stroke", "#7c8eaf").attr("stroke-width", 0.35).attr("filter", null);
}
// v 1.1 features stores more data
defs.select("#land").selectAll("path").remove();
defs.select("#water").selectAll("path").remove();
coastline.selectAll("path").remove();
lakes.selectAll("path").remove();
drawCoastline();
}
if (version < 1.11) {
// v 1.11 added new attributes
terrs.attr("scheme", "bright").attr("terracing", 0).attr("skip", 5).attr("relax", 0).attr("curve", 0);
svg.select("#oceanic > *").attr("id", "oceanicPattern");
oceanLayers.attr("layers", "-6,-3,-1");
gridOverlay.attr("type", "pointyHex").attr("size", 10);
// v 1.11 added cultures heirarchy tree
if (pack.cultures[1] && !pack.cultures[1].code) {
pack.cultures
.filter(c => c.i)
.forEach(c => {
c.origin = 0;
c.code = c.name.slice(0, 2);
});
}
// v 1.11 had an issue with fogging being displayed on load
unfog();
// v 1.2 added new terrain attributes
if (!terrain.attr("set")) terrain.attr("set", "simple");
if (!terrain.attr("size")) terrain.attr("size", 1);
if (!terrain.attr("density")) terrain.attr("density", 0.4);
}
if (version < 1.21) {
// v 1.11 replaced "display" attribute by "display" style
viewbox.selectAll("g").each(function () {
if (this.hasAttribute("display")) {
this.removeAttribute("display");
this.style.display = "none";
}
});
// v 1.21 added rivers data to pack
pack.rivers = []; // rivers data
rivers.selectAll("path").each(function () {
const i = +this.id.slice(5);
const length = this.getTotalLength() / 2;
const s = this.getPointAtLength(length),
e = this.getPointAtLength(0);
const source = findCell(s.x, s.y),
mouth = findCell(e.x, e.y);
const name = Rivers.getName(mouth);
const type = length < 25 ? rw({Creek: 9, River: 3, Brook: 3, Stream: 1}) : "River";
pack.rivers.push({i, parent: 0, length, source, mouth, basin: i, name, type});
});
}
if (version < 1.22) {
// v 1.22 changed state neighbors from Set object to array
BurgsAndStates.collectStatistics();
}
if (version < 1.3) {
// v 1.3 added global options object
const winds = options.slice(); // previostly wind was saved in settings[19]
const year = rand(100, 2000);
const era = Names.getBaseShort(P(0.7) ? 1 : rand(nameBases.length)) + " Era";
const eraShort = era[0] + "E";
const military = Military.getDefaultOptions();
options = {winds, year, era, eraShort, military};
// v 1.3 added campaings data for all states
BurgsAndStates.generateCampaigns();
// v 1.3 added militry layer
armies = viewbox.insert("g", "#icons").attr("id", "armies");
armies.attr("opacity", 1).attr("fill-opacity", 1).attr("font-size", 6).attr("box-size", 3).attr("stroke", "#000").attr("stroke-width", 0.3);
turnButtonOn("toggleMilitary");
Military.generate();
}
if (version < 1.4) {
// v 1.35 added dry lakes
if (!lakes.select("#dry").size()) {
lakes.append("g").attr("id", "dry");
lakes.select("#dry").attr("opacity", 1).attr("fill", "#c9bfa7").attr("stroke", "#8e816f").attr("stroke-width", 0.7).attr("filter", null);
}
// v 1.4 added ice layer
ice = viewbox.insert("g", "#coastline").attr("id", "ice").style("display", "none");
ice.attr("opacity", null).attr("fill", "#e8f0f6").attr("stroke", "#e8f0f6").attr("stroke-width", 1).attr("filter", "url(#dropShadow05)");
drawIce();
// v 1.4 added icon and power attributes for units
for (const unit of options.military) {
if (!unit.icon) unit.icon = getUnitIcon(unit.type);
if (!unit.power) unit.power = unit.crew;
}
function getUnitIcon(type) {
if (type === "naval") return "🌊";
if (type === "ranged") return "🏹";
if (type === "mounted") return "🐴";
if (type === "machinery") return "💣";
if (type === "armored") return "🐢";
if (type === "aviation") return "🦅";
if (type === "magical") return "🔮";
else return "⚔️";
}
// 1.4 added state reference for regiments
pack.states.filter(s => s.military).forEach(s => s.military.forEach(r => (r.state = s.i)));
}
if (version < 1.5) {
// not need to store default styles from v 1.5
localStorage.removeItem("styleClean");
localStorage.removeItem("styleGloom");
localStorage.removeItem("styleAncient");
localStorage.removeItem("styleMonochrome");
// v 1.5 cultures has shield attribute
pack.cultures.forEach(culture => {
if (culture.removed) return;
culture.shield = Cultures.getRandomShield();
});
// v 1.5 added burg type value
pack.burgs.forEach(burg => {
if (!burg.i || burg.removed) return;
burg.type = BurgsAndStates.getType(burg.cell, burg.port);
});
// v 1.5 added emblems
defs.append("g").attr("id", "defs-emblems");
emblems = viewbox.insert("g", "#population").attr("id", "emblems").style("display", "none");
emblems.append("g").attr("id", "burgEmblems");
emblems.append("g").attr("id", "provinceEmblems");
emblems.append("g").attr("id", "stateEmblems");
regenerateEmblems();
toggleEmblems();
// v 1.5 changed releif icons data
terrain.selectAll("use").each(function () {
const type = this.getAttribute("data-type") || this.getAttribute("xlink:href");
this.removeAttribute("xlink:href");
this.removeAttribute("data-type");
this.removeAttribute("data-size");
this.setAttribute("href", type);
});
}
if (version < 1.6) {
// v 1.6 changed rivers data
for (const river of pack.rivers) {
const el = document.getElementById("river" + river.i);
if (el) {
river.widthFactor = +el.getAttribute("data-width");
el.removeAttribute("data-width");
el.removeAttribute("data-increment");
river.discharge = pack.cells.fl[river.mouth] || 1;
river.width = rn(river.length / 100, 2);
river.sourceWidth = 0.1;
} else {
Rivers.remove(river.i);
}
}
// v 1.6 changed lakes data
for (const f of pack.features) {
if (f.type !== "lake") continue;
if (f.evaporation) continue;
f.flux = f.flux || f.cells * 3;
f.temp = grid.cells.temp[pack.cells.g[f.firstCell]];
f.height = f.height || d3.min(pack.cells.c[f.firstCell].map(c => pack.cells.h[c]).filter(h => h >= 20));
const height = (f.height - 18) ** heightExponentInput.value;
const evaporation = ((700 * (f.temp + 0.006 * height)) / 50 + 75) / (80 - f.temp);
f.evaporation = rn(evaporation * f.cells);
f.name = f.name || Lakes.getName(f);
delete f.river;
}
}
if (version < 1.61) {
// v 1.61 changed rulers data
ruler.style("display", null);
rulers = new Rulers();
ruler.selectAll(".ruler > .white").each(function () {
const x1 = +this.getAttribute("x1");
const y1 = +this.getAttribute("y1");
const x2 = +this.getAttribute("x2");
const y2 = +this.getAttribute("y2");
if (isNaN(x1) || isNaN(y1) || isNaN(x2) || isNaN(y2)) return;
const points = [
[x1, y1],
[x2, y2]
];
rulers.create(Ruler, points);
});
ruler.selectAll("g.opisometer").each(function () {
const pointsString = this.dataset.points;
if (!pointsString) return;
const points = JSON.parse(pointsString);
rulers.create(Opisometer, points);
});
ruler.selectAll("path.planimeter").each(function () {
const length = this.getTotalLength();
if (length < 30) return;
const step = length > 1000 ? 40 : length > 400 ? 20 : 10;
const increment = length / Math.ceil(length / step);
const points = [];
for (let i = 0; i <= length; i += increment) {
const point = this.getPointAtLength(i);
points.push([point.x | 0, point.y | 0]);
}
rulers.create(Planimeter, points);
});
ruler.selectAll("*").remove();
if (rulers.data.length) {
turnButtonOn("toggleRulers");
rulers.draw();
} else turnButtonOff("toggleRulers");
// 1.61 changed oceanicPattern from rect to image
const pattern = document.getElementById("oceanic");
const filter = pattern.firstElementChild.getAttribute("filter");
const href = filter ? "./images/" + filter.replace("url(#", "").replace(")", "") + ".png" : "";
pattern.innerHTML = `<image id="oceanicPattern" href=${href} width="100" height="100" opacity="0.2"></image>`;
}
if (version < 1.62) {
// v 1.62 changed grid data
gridOverlay.attr("size", null);
}
if (version < 1.63) {
// v.1.63 changed ocean pattern opacity element
const oceanPattern = document.getElementById("oceanPattern");
if (oceanPattern) oceanPattern.removeAttribute("opacity");
const oceanicPattern = document.getElementById("oceanicPattern");
if (!oceanicPattern.getAttribute("opacity")) oceanicPattern.setAttribute("opacity", 0.2);
// v 1.63 moved label text-shadow from css to editable inline style
burgLabels.select("#cities").style("text-shadow", "white 0 0 4px");
burgLabels.select("#towns").style("text-shadow", "white 0 0 4px");
labels.select("#states").style("text-shadow", "white 0 0 4px");
labels.select("#addedLabels").style("text-shadow", "white 0 0 4px");
}
if (version < 1.64) {
// v.1.64 change states style
const opacity = regions.attr("opacity");
const filter = regions.attr("filter");
statesBody.attr("opacity", opacity).attr("filter", filter);
statesHalo.attr("opacity", opacity).attr("filter", "blur(5px)");
regions.attr("opacity", null).attr("filter", null);
}
if (version < 1.65) {
// v 1.65 changed rivers data
d3.select("#rivers").attr("style", null); // remove style to unhide layer
const {cells, rivers} = pack;
const defaultWidthFactor = rn(1 / (pointsInput.dataset.cells / 10000) ** 0.25, 2);
for (const river of rivers) {
const node = document.getElementById("river" + river.i);
if (node && !river.cells) {
const riverCells = [];
const riverPoints = [];
const length = node.getTotalLength() / 2;
if (!length) continue;
const segments = Math.ceil(length / 6);
const increment = length / segments;
for (let i = 0; i <= segments; i++) {
const shift = increment * i;
const {x: x1, y: y1} = node.getPointAtLength(length + shift);
const {x: x2, y: y2} = node.getPointAtLength(length - shift);
const x = rn((x1 + x2) / 2, 1);
const y = rn((y1 + y2) / 2, 1);
const cell = findCell(x, y);
riverPoints.push([x, y]);
riverCells.push(cell);
}
river.cells = riverCells;
river.points = riverPoints;
}
river.widthFactor = defaultWidthFactor;
cells.i.forEach(i => {
const riverInWater = cells.r[i] && cells.h[i] < 20;
if (riverInWater) cells.r[i] = 0;
});
}
}
if (version < 1.652) {
// remove style to unhide layers
rivers.attr("style", null);
borders.attr("style", null);
}
if (version < 1.7) {
// v 1.7 changed markers data
const defs = document.getElementById("defs-markers");
const markersGroup = document.getElementById("markers");
if (defs && markersGroup) {
const markerElements = markersGroup.querySelectorAll("use");
const rescale = +markersGroup.getAttribute("rescale");
pack.markers = Array.from(markerElements).map((el, i) => {
const id = el.getAttribute("id");
const note = notes.find(note => note.id === id);
if (note) note.id = `marker${i}`;
let x = +el.dataset.x;
let y = +el.dataset.y;
const transform = el.getAttribute("transform");
if (transform) {
const [dx, dy] = parseTransform(transform);
if (dx) x += +dx;
if (dy) y += +dy;
}
const cell = findCell(x, y);
const size = rn(rescale ? el.dataset.size * 30 : el.getAttribute("width"), 1);
const href = el.href.baseVal;
const type = href.replace("#marker_", "");
const symbol = defs?.querySelector(`symbol${href}`);
const text = symbol?.querySelector("text");
const circle = symbol?.querySelector("circle");
const icon = text?.innerHTML;
const px = text && Number(text.getAttribute("font-size")?.replace("px", ""));
const dx = text && Number(text.getAttribute("x")?.replace("%", ""));
const dy = text && Number(text.getAttribute("y")?.replace("%", ""));
const fill = circle && circle.getAttribute("fill");
const stroke = circle && circle.getAttribute("stroke");
const marker = {i, icon, type, x, y, size, cell};
if (size && size !== 30) marker.size = size;
if (!isNaN(px) && px !== 12) marker.px = px;
if (!isNaN(dx) && dx !== 50) marker.dx = dx;
if (!isNaN(dy) && dy !== 50) marker.dy = dy;
if (fill && fill !== "#ffffff") marker.fill = fill;
if (stroke && stroke !== "#000000") marker.stroke = stroke;
if (circle?.getAttribute("opacity") === "0") marker.pin = "no";
return marker;
});
markersGroup.style.display = null;
defs?.remove();
markerElements.forEach(el => el.remove());
if (layerIsOn("markers")) drawMarkers();
}
}
})();
void (function checkDataIntegrity() {
const cells = pack.cells;
@ -1070,6 +566,7 @@ function parseLoadedData(data) {
alertMessage.innerHTML = `An error is occured on map loading. Select a different file to load,
<br>generate a new random map or cancel the loading
<p id="errorBox">${parseError(error)}</p>`;
$("#alert").dialog({
resizable: false,
title: "Loading error",

View file

@ -145,7 +145,7 @@ async function saveToDropbox() {
await Cloud.providers.dropbox.save(filename, mapData);
tip("Map is saved to your Dropbox", true, "success", 8000);
} catch (msg) {
console.error(msg);
ERROR && console.error(msg);
tip("Cannot save .map to your Dropbox", true, "error", 8000);
}
}

View file

@ -519,7 +519,7 @@ window.Markers = (function () {
const dungeonSeed = `${seed}${cell}`;
const name = "Dungeon";
const legend = `<div>Undiscovered dungeon. See <a href="https://watabou.github.io/one-page-dungeon/?seed=${dungeonSeed}" target="_blank">One page dungeon</a></div><iframe style="height: 33vh" src="https://watabou.github.io/one-page-dungeon/?seed=${dungeonSeed}" sandbox="allow-scripts allow-same-origin"></iframe>`;
const legend = `<div>Undiscovered dungeon. See <a href="https://watabou.github.io/one-page-dungeon/?seed=${dungeonSeed}" target="_blank">One page dungeon</a></div><iframe src="https://watabou.github.io/one-page-dungeon/?seed=${dungeonSeed}" sandbox="allow-scripts allow-same-origin"></iframe>`;
notes.push({id, name, legend});
quantity--;
}

View file

@ -141,8 +141,8 @@ class Battle {
const state = pack.states[regiment.state];
const distance = (Math.hypot(this.y - regiment.by, this.x - regiment.bx) * distanceScaleInput.value) | 0; // distance between regiment and its base
const color = state.color[0] === "#" ? state.color : "#999";
const icon = `<svg width="1.4em" height="1.4em" style="margin-bottom: -.6em">
<rect x="0" y="0" width="100%" height="100%" fill="${color}" class="fillRect"></rect>
const icon = `<svg width="1.4em" height="1.4em" style="margin-bottom: -.6em; stroke: #333">
<rect x="0" y="0" width="100%" height="100%" fill="${color}"></rect>
<text x="0" y="1.04em" style="">${regiment.icon}</text></svg>`;
const body = `<tbody id="battle${state.i}-${regiment.i}">`;
@ -183,7 +183,7 @@ class Battle {
dist = added ? "0 " + distanceUnitInput.value : distance(r);
return `<div ${added ? "class='inactive'" : ""} data-s=${s.i} data-i=${r.i} data-state=${s.name} data-regiment=${r.name}
data-total=${r.a} data-distance=${dist} data-tip="Click to select regiment">
<svg width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${s.color}" class="fillRect"></svg>
<svg width=".9em" height=".9em" style="margin-bottom:-1px; stroke: #333"><rect x="0" y="0" width="100%" height="100%" fill="${s.color}" ></svg>
<div style="width:6em">${s.name.slice(0, 11)}</div>
<div style="width:1.2em">${r.icon}</div>
<div style="width:13em">${r.name.slice(0, 24)}</div>

View file

@ -37,9 +37,9 @@ function editBiomes() {
document.getElementById("biomesExport").addEventListener("click", downloadBiomesData);
body.addEventListener("click", function (ev) {
const el = ev.target,
cl = el.classList;
if (cl.contains("fillRect")) biomeChangeColor(el);
const el = ev.target;
const cl = el.classList;
if (el.tagName === "FILL-BOX") biomeChangeColor(el);
else if (cl.contains("icon-info-circled")) openWiki(el);
else if (cl.contains("icon-trash-empty")) removeCustomBiome(el);
if (customization === 6) selectBiomeOnLineClick(el);
@ -94,9 +94,7 @@ function editBiomes() {
lines += `<div class="states biomes" data-id="${i}" data-name="${b.name[i]}" data-habitability="${b.habitability[i]}"
data-cells=${b.cells[i]} data-area=${area} data-population=${population} data-color=${b.color[i]}>
<svg data-tip="Biomes fill style. Click to change" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${
b.color[i]
}" class="fillRect pointer"></svg>
<fill-box fill="${b.color[i]}"></fill-box>
<input data-tip="Biome name. Click and type to change" class="biomeName" value="${b.name[i]}" autocorrect="off" spellcheck="false">
<span data-tip="Biome habitability percent" class="hide">%</span>
<input data-tip="Biome habitability percent. Click and set new value to change" type="number" min=0 max=9999 class="biomeHabitability hide" value=${
@ -158,15 +156,15 @@ function editBiomes() {
function biomeChangeColor(el) {
const currentFill = el.getAttribute("fill");
const biome = +el.parentNode.parentNode.dataset.id;
const biome = +el.parentNode.dataset.id;
const callback = function (fill) {
el.setAttribute("fill", fill);
biomesData.color[biome] = fill;
const callback = newFill => {
el.fill = newFill;
biomesData.color[biome] = newFill;
biomes
.select("#biome" + biome)
.attr("fill", fill)
.attr("stroke", fill);
.attr("fill", newFill)
.attr("stroke", newFill);
};
openPicker(currentFill, callback);
@ -270,7 +268,7 @@ function editBiomes() {
const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value;
const line = `<div class="states biomes" data-id="${i}" data-name="${b.name[i]}" data-habitability=${b.habitability[i]} data-cells=0 data-area=0 data-population=0 data-color=${b.color[i]}>
<svg data-tip="Biomes fill style. Click to change" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${b.color[i]}" class="fillRect pointer"></svg>
<fill-box fill="${b.color[i]}"></fill-box>
<input data-tip="Biome name. Click and type to change" class="biomeName" value="${b.name[i]}" autocorrect="off" spellcheck="false">
<span data-tip="Biome habitability percent" class="hide">%</span>
<input data-tip="Biome habitability percent. Click and set new value to change" type="number" min=0 max=9999 step=1 class="biomeHabitability hide" value=${b.habitability[i]}>

View file

@ -52,6 +52,7 @@ function editBurg(id) {
document.getElementById("burglLegend").addEventListener("click", editBurgLegend);
document.getElementById("burgLock").addEventListener("click", toggleBurgLockButton);
document.getElementById("burgRemove").addEventListener("click", removeSelectedBurg);
document.getElementById("burgTemperatureGraph").addEventListener("click", showTemperatureGraph);
function updateBurgValues() {
const id = +elSelected.attr("data-id");
@ -543,6 +544,11 @@ function editBurg(id) {
editNotes("burg" + id, name);
}
function showTemperatureGraph() {
const id = elSelected.attr("data-id");
showBurgTemperatureGraph(id);
}
function removeSelectedBurg() {
const id = +elSelected.attr("data-id");
if (pack.burgs[id].capital) {

View file

@ -108,9 +108,7 @@ function editCultures() {
lines += `<div class="states cultures" data-id=${c.i} data-name="${c.name}" data-color="${c.color}" data-cells=${c.cells}
data-area=${area} data-population=${population} data-base=${c.base} data-type=${c.type} data-expansionism=${c.expansionism} data-emblems="${c.shield}">
<svg data-tip="Culture fill style. Click to change" width=".9em" height=".9em" style="margin-bottom:-1px">
<rect x="0" y="0" width="100%" height="100%" fill="${c.color}" class="fillRect pointer">
</svg>
<fill-box fill="${c.color}"></fill-box>
<input data-tip="Culture name. Click and type to change" class="cultureName" value="${c.name}" autocorrect="off" spellcheck="false">
<span data-tip="Regenerate culture name" class="icon-cw hiddenIcon" style="visibility: hidden"></span>
<span data-tip="Cells count" class="icon-check-empty hide"></span>
@ -148,7 +146,7 @@ function editCultures() {
body.querySelectorAll("div.cultures").forEach(el => el.addEventListener("mouseenter", ev => cultureHighlightOn(ev)));
body.querySelectorAll("div.cultures").forEach(el => el.addEventListener("mouseleave", ev => cultureHighlightOff(ev)));
body.querySelectorAll("div.states").forEach(el => el.addEventListener("click", selectCultureOnLineClick));
body.querySelectorAll("rect.fillRect").forEach(el => el.addEventListener("click", cultureChangeColor));
body.querySelectorAll("fill-box").forEach(el => el.addEventListener("click", cultureChangeColor));
body.querySelectorAll("div > input.cultureName").forEach(el => el.addEventListener("input", cultureChangeName));
body.querySelectorAll("div > span.icon-cw").forEach(el => el.addEventListener("click", cultureRegenerateName));
body.querySelectorAll("div > input.statePower").forEach(el => el.addEventListener("input", cultureChangeExpansionism));
@ -248,16 +246,16 @@ function editCultures() {
function cultureChangeColor() {
const el = this;
const currentFill = el.getAttribute("fill");
const culture = +el.parentNode.parentNode.dataset.id;
const culture = +el.parentNode.dataset.id;
const callback = function (fill) {
el.setAttribute("fill", fill);
pack.cultures[culture].color = fill;
const callback = newFill => {
el.fill = newFill;
pack.cultures[culture].color = newFill;
cults
.select("#culture" + culture)
.attr("fill", fill)
.attr("stroke", fill);
debug.select("#cultureCenter" + culture).attr("fill", fill);
.attr("fill", newFill)
.attr("stroke", newFill);
debug.select("#cultureCenter" + culture).attr("fill", newFill);
};
openPicker(currentFill, callback);

View file

@ -101,10 +101,8 @@ function editDiplomacy() {
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>
<div data-tip="${tipChange}" class="changeRelations" style="width: 6em">
<fill-box fill="${color}" size=".9em"></fill-box>
${relation}
</div>
</div>`;
@ -195,9 +193,7 @@ function editDiplomacy() {
([relation, {color, inText, tip}]) =>
`<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>
<fill-box fill="${color}" size=".8em"></fill-box>
${inText}
</label></div>`
)

View file

@ -425,10 +425,10 @@ function clearLegend() {
function createPicker() {
const pos = () => tip("Drag to change the picker position");
const cl = () => tip("Click to close the picker");
const closePicker = () => contaiter.style("display", "none");
const closePicker = () => container.style("display", "none");
const contaiter = d3.select("body").append("svg").attr("id", "pickerContainer").attr("width", "100%").attr("height", "100%");
contaiter
const container = d3.select("body").append("svg").attr("id", "pickerContainer").attr("width", "100%").attr("height", "100%");
container
.append("rect")
.attr("x", 0)
.attr("y", 0)
@ -437,7 +437,7 @@ function createPicker() {
.attr("opacity", 0.2)
.on("mousemove", cl)
.on("click", closePicker);
const picker = contaiter
const picker = container
.append("g")
.attr("id", "picker")
.call(
@ -494,7 +494,7 @@ function createPicker() {
const colors = picker.append("g").attr("id", "pickerColors").attr("stroke", "#333333");
const hatches = picker.append("g").attr("id", "pickerHatches").attr("stroke", "#333333");
const hatching = d3.selectAll("g#hatching > pattern");
const hatching = d3.selectAll("g#defs-hatching > pattern");
const number = hatching.size();
const clr = d3.range(number).map(i => d3.hsl((i / number) * 360, 0.7, 0.7).hex());
@ -504,8 +504,8 @@ function createPicker() {
.attr("id", "picker_" + d)
.attr("fill", d)
.attr("class", i ? "" : "selected")
.attr("x", i * 22 + 4)
.attr("y", 40)
.attr("x", (i % 14) * 22 + 4)
.attr("y", 40 + Math.floor(i / 14)*20)
.attr("width", 16)
.attr("height", 16);
});
@ -515,20 +515,20 @@ function createPicker() {
.append("rect")
.attr("id", "picker_" + this.id)
.attr("fill", "url(#" + this.id + ")")
.attr("x", i * 22 + 4)
.attr("y", 61)
.attr("x", (i % 14) * 22 + 4)
.attr("y", Math.floor(i / 14)*20 + 20 + (number * 2))
.attr("width", 16)
.attr("height", 16);
.attr("height", 16)
});
colors
.selectAll("rect")
.on("click", pickerFillClicked)
.on("mousemove", () => tip("Click to fill with the color"));
.on("mouseover", () => tip("Click to fill with the color"));
hatches
.selectAll("rect")
.on("click", pickerFillClicked)
.on("mousemove", () => tip("Click to fill with the hatching"));
.on("mouseover", function() { tip("Click to fill with the hatching " + this.id) });
// append box
const bbox = picker.node().getBBox();
@ -544,10 +544,10 @@ function createPicker() {
.attr("fill", "#ffffff")
.attr("stroke", "#5d4651")
.on("mousemove", pos);
picker.insert("text", ":first-child").attr("x", 291).attr("y", -10).attr("id", "pickerCloseText").text("✕");
picker.insert("text", ":first-child").attr("x", width-20).attr("y", -10).attr("id", "pickerCloseText").text("✕");
picker
.insert("rect", ":first-child")
.attr("x", 288)
.attr("x", width-23)
.attr("y", -21)
.attr("id", "pickerCloseRect")
.attr("width", 14)

View file

@ -70,7 +70,8 @@ function mouseMove() {
const point = d3.mouse(this);
const i = findCell(point[0], point[1]); // pack cell id
if (i === undefined) return;
showNotes(d3.event, i);
showNotes(d3.event);
const g = findGridCell(point[0], point[1]); // grid cell id
if (tooltip.dataset.main) showMainTip();
else showMapTooltip(point, d3.event, i, g);
@ -78,7 +79,7 @@ function mouseMove() {
}
// show note box on hover (if any)
function showNotes(e, i) {
function showNotes(e) {
if (notesEditor.offsetParent) return;
let id = e.target.id || e.target.parentNode.id || e.target.parentNode.parentNode.id;
if (e.target.parentNode.parentNode.id === "burgLabels") id = "burg" + e.target.dataset.id;

View file

@ -913,11 +913,9 @@ function editHeightmap() {
function uploadTemplate(dataLoaded) {
const steps = dataLoaded.split("\r\n");
if (!steps.length) {
tip("Cannot parse the template, please check the file", false, "error");
return;
}
if (!steps.length) return tip("Cannot parse the template, please check the file", false, "error");
templateBody.innerHTML = "";
for (const s of steps) {
const step = s.split(" ");
if (step.length !== 5) {
@ -1318,10 +1316,10 @@ function editHeightmap() {
img.onload = function () {
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
canvas.width = svgWidth;
canvas.height = svgHeight;
canvas.width = graphWidth;
canvas.height = graphHeight;
document.body.insertBefore(canvas, optionsContainer);
ctx.drawImage(img, 0, 0, svgWidth, svgHeight);
ctx.drawImage(img, 0, 0, graphWidth, graphHeight);
const imgBig = canvas.toDataURL("image/png");
const link = document.createElement("a");
link.download = getFileName("Heightmap") + ".png";

View file

@ -536,12 +536,11 @@ class Planimeter extends Measurer {
}
// Scale bar
function drawScaleBar(requestedScale) {
function drawScaleBar(scaleLevel) {
if (scaleBar.style("display") === "none") return; // no need to re-draw hidden element
scaleBar.selectAll("*").remove(); // fully redraw every time
const scaleLevel = requestedScale || scale;
const distanceScale = distanceScaleInput.value;
const distanceScale = +distanceScaleInput.value;
const unit = distanceUnitInput.value;
const size = +barSizeInput.value;

View file

@ -75,12 +75,9 @@ function overviewMilitary() {
const sortData = options.military.map(u => `data-${u.name}="${getForces(u)}"`).join(" ");
const lineData = options.military.map(u => `<div data-type="${u.name}" data-tip="State ${u.name} units number">${getForces(u)}</div>`).join(" ");
lines += `<div class="states" data-id=${s.i} data-state="${
s.name
}" ${sortData} data-total="${total}" data-population="${population}" data-rate="${rate}" data-alert="${s.alert}">
<svg data-tip="${s.fullName}" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${
s.color
}" class="fillRect"></svg>
lines += `<div class="states" data-id=${s.i} data-state="${s.name}" ${sortData} data-total="${total}"
data-population="${population}" data-rate="${rate}" data-alert="${s.alert}">
<fill-box data-tip="${s.fullName}" fill="${s.color}" disabled></fill-box>
<input data-tip="${s.fullName}" style="width:6em" value="${s.name}" readonly>
${lineData}
<div data-type="total" data-tip="Total state military personnel (considering crew)" style="font-weight: bold">${si(total)}</div>

View file

@ -1,25 +1,22 @@
"use strict";
function editNotes(id, name) {
// elements
const notesLegend = document.getElementById("notesLegend");
const notesName = document.getElementById("notesName");
const notesSelect = document.getElementById("notesSelect");
const notesPin = document.getElementById("notesPin");
// update list of objects
const select = document.getElementById("notesSelect");
select.options.length = 0;
notesSelect.options.length = 0;
for (const note of notes) {
select.options.add(new Option(note.id, note.id));
notesSelect.options.add(new Option(note.id, note.id));
}
// initiate pell (html editor)
const notesText = document.getElementById("notesText");
notesText.innerHTML = "";
const editor = Pell.init({
element: notesText,
onChange: html => {
const note = notes.find(note => note.id === select.value);
if (!note) return;
note.legend = html;
showNote(note);
}
});
// update pin notes icon
const notesArePinned = options.pinNotes;
if (notesArePinned) notesPin.classList.add("pressed");
else notesPin.classList.remove("pressed");
// select an object
if (notes.length || id) {
@ -29,136 +26,161 @@ function editNotes(id, name) {
if (!name) name = id;
note = {id, name, legend: ""};
notes.push(note);
select.options.add(new Option(id, id));
}
select.value = id;
notesName.value = note.name;
editor.content.innerHTML = note.legend;
showNote(note);
} else {
editor.content.innerHTML = "There are no added notes. Click on element (e.g. label) and add a free text note";
document.getElementById("notesName").value = "";
notesSelect.options.add(new Option(id, id));
}
notesSelect.value = id;
notesName.value = note.name;
notesLegend.innerHTML = note.legend;
initEditor();
updateNotesBox(note);
} else {
// if notes array is empty
notesName.value = "";
notesLegend.innerHTML = "No notes added. Click on an element (e.g. label or marker) and add a free text note";
}
// open a dialog
$("#notesEditor").dialog({
title: "Notes Editor",
minWidth: "40em",
width: "50vw",
position: {my: "center", at: "center", of: "svg"}
width: "70vw",
height: window.innerHeight * 0.75,
position: {my: "center", at: "center", of: "svg"},
close: removeEditor
});
$("[aria-describedby='notesEditor']").css("top", "10vh");
if (modules.editNotes) return;
modules.editNotes = true;
// add listeners
document.getElementById("notesSelect").addEventListener("change", changeObject);
document.getElementById("notesSelect").addEventListener("change", changeElement);
document.getElementById("notesName").addEventListener("input", changeName);
document.getElementById("notesPin").addEventListener("click", () => (options.pinNotes = !options.pinNotes));
document.getElementById("notesSpeak").addEventListener("click", () => speak(editor.content.innerHTML));
document.getElementById("notesLegend").addEventListener("blur", updateLegend);
document.getElementById("notesPin").addEventListener("click", toggleNotesPin);
document.getElementById("notesFocus").addEventListener("click", validateHighlightElement);
document.getElementById("notesDownload").addEventListener("click", downloadLegends);
document.getElementById("notesUpload").addEventListener("click", () => legendsToLoad.click());
document.getElementById("legendsToLoad").addEventListener("change", function () {
uploadFile(this, uploadLegends);
});
document.getElementById("notesClearStyle").addEventListener("click", clearStyle);
document.getElementById("notesRemove").addEventListener("click", triggerNotesRemove);
function showNote(note) {
document.getElementById("notes").style.display = "block";
async function initEditor() {
if (!window.tinymce) {
const url = "https://cdn.tiny.cloud/1/4i6a79ymt2y0cagke174jp3meoi28vyecrch12e5puyw3p9a/tinymce/5/tinymce.min.js";
try {
await import(url);
} catch (error) {
// error may be caused by failed request being cached, try again with random hash
try {
const hash = Math.random().toString(36).substring(2, 15);
await import(`${url}#${hash}`);
} catch (error) {
console.error(error);
}
}
}
if (window.tinymce) {
tinymce.init({
selector: "#notesLegend",
height: "90%",
menubar: false,
plugins: `autolink lists link charmap print formatpainter casechange code fullscreen image link media table paste hr checklist wordcount`,
toolbar: `code | undo redo | bold italic strikethrough | forecolor backcolor | formatpainter removeformat | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image media table | fontselect fontsizeselect | blockquote hr casechange checklist charmap | print fullscreen`,
media_alt_source: false,
media_poster: false,
setup: editor => {
editor.on("Change", updateLegend);
}
});
}
}
function updateLegend() {
const note = notes.find(note => note.id === notesSelect.value);
if (!note) return tip("Note element is not found", true, "error", 4000);
const isTinyEditorActive = window.tinymce?.activeEditor;
note.legend = isTinyEditorActive ? tinymce.activeEditor.getContent() : notesLegend.innerHTML;
updateNotesBox(note);
}
function updateNotesBox(note) {
document.getElementById("notesHeader").innerHTML = note.name;
document.getElementById("notesBody").innerHTML = note.legend;
}
function changeObject() {
function changeElement() {
const note = notes.find(note => note.id === this.value);
if (!note) return;
if (!note) return tip("Note element is not found", true, "error", 4000);
notesName.value = note.name;
editor.content.innerHTML = note.legend;
notesLegend.innerHTML = note.legend;
updateNotesBox(note);
if (window.tinymce) tinymce.activeEditor.setContent(note.legend);
}
function changeName() {
const id = document.getElementById("notesSelect").value;
const note = notes.find(note => note.id === id);
if (!note) return;
const note = notes.find(note => note.id === notesSelect.value);
if (!note) return tip("Note element is not found", true, "error", 4000);
note.name = this.value;
showNote(note);
}
function validateHighlightElement() {
const select = document.getElementById("notesSelect");
const element = document.getElementById(select.value);
const element = document.getElementById(notesSelect.value);
if (element) return highlightElement(element, 3);
// if element is not found
if (element === null) {
alertMessage.innerHTML = "Related element is not found. Would you like to remove the note?";
$("#alert").dialog({
resizable: false,
confirmationDialog({
title: "Element not found",
buttons: {
Remove: function () {
$(this).dialog("close");
removeLegend();
},
Keep: function () {
$(this).dialog("close");
}
}
message: "Note element is not found. Would you like to remove the note?",
confirm: "Remove",
cancel: "Keep",
onConfirm: removeLegend
});
return;
}
highlightElement(element, 3); // if element is found
}
function downloadLegends() {
const data = JSON.stringify(notes);
const notesData = JSON.stringify(notes);
const name = getFileName("Notes") + ".txt";
downloadFile(data, name);
downloadFile(notesData, name);
}
function uploadLegends(dataLoaded) {
if (!dataLoaded) {
tip("Cannot load the file. Please check the data format", false, "error");
return;
}
if (!dataLoaded) return tip("Cannot load the file. Please check the data format", false, "error");
notes = JSON.parse(dataLoaded);
document.getElementById("notesSelect").options.length = 0;
notesSelect.options.length = 0;
editNotes(notes[0].id, notes[0].name);
}
function clearStyle() {
editor.content.innerHTML = editor.content.textContent;
}
function triggerNotesRemove() {
alertMessage.innerHTML = "Are you sure you want to remove the selected note?";
$("#alert").dialog({
resizable: false,
confirmationDialog({
title: "Remove note",
buttons: {
Remove: function () {
$(this).dialog("close");
removeLegend();
},
Keep: function () {
$(this).dialog("close");
}
}
message: "Are you sure you want to remove the selected note? There is no way to undo this action",
confirm: "Remove",
onConfirm: removeLegend
});
}
function removeLegend() {
const select = document.getElementById("notesSelect");
const index = notes.findIndex(n => n.id === select.value);
const index = notes.findIndex(n => n.id === notesSelect.value);
notes.splice(index, 1);
select.options.length = 0;
notesSelect.options.length = 0;
if (!notes.length) {
$("#notesEditor").dialog("close");
return;
}
notesText.innerHTML = "";
editNotes(notes[0].id, notes[0].name);
}
function toggleNotesPin() {
options.pinNotes = !options.pinNotes;
this.classList.toggle("pressed");
}
function removeEditor() {
if (window.tinymce) tinymce.remove();
}
}

View file

@ -103,7 +103,9 @@ function showSupporters() {
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,
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`;
Frank Fewkes,jason baldrick,Game Master Pro,Andrew Kircher,Preston Mitchell,Chris Kohut,Emarandzeb,Trentin Bergeron,Damon Gallaty,Pleaseworkforonce,
Jordan,William Markus,Sidr Dim,Alexander Whittaker,The Next Level,Patrick Valverde,Markus Peham,Daniel Cooper,the Beagles of Neorbus,Marley Moule,
Maximilian Schielke,Johnathan Xavier Hutchinson,Ele,Rita`;
const array = supporters
.replace(/(?:\r\n|\r|\n)/g, "")
@ -304,10 +306,8 @@ function showSeedHistoryDialog() {
// generate map with historical seed
function restoreSeed(id) {
if (mapHistory[id].seed == seed) {
tip("The current map is already generated with this seed", null, "error");
return;
}
if (mapHistory[id].seed == seed) return tip("The current map is already generated with this seed", null, "error");
optionsSeed.value = mapHistory[id].seed;
mapWidthInput.value = mapHistory[id].width;
mapHeightInput.value = mapHistory[id].height;
@ -545,7 +545,7 @@ function randomizeOptions() {
// 'Options' settings
if (randomize || !locked("template")) randomizeHeightmapTemplate();
if (randomize || !locked("regions")) regionsInput.value = regionsOutput.value = gauss(15, 3, 2, 30);
if (randomize || !locked("regions")) regionsInput.value = regionsOutput.value = gauss(18, 5, 2, 30);
if (randomize || !locked("provinces")) provincesInput.value = provincesOutput.value = gauss(20, 10, 20, 100);
if (randomize || !locked("manors")) {
manorsInput.value = 1000;
@ -736,8 +736,14 @@ async function showLoadPane() {
}
});
// already connected to Dropbox: list saved maps
if (Cloud.providers.dropbox.api) {
document.getElementById("dropboxConnectButton").style.display = "none";
document.getElementById("loadFromDropboxSelect").style.display = "block";
const loadFromDropboxButtons = document.getElementById("loadFromDropboxButtons");
const fileSelect = document.getElementById("loadFromDropboxSelect");
fileSelect.innerHTML = `<option value="" disabled selected>Loading...</option>`;
const files = await Cloud.providers.dropbox.list();
if (!files) {
@ -754,6 +760,19 @@ async function showLoadPane() {
opt.value = file.path;
fileSelect.appendChild(opt);
});
return;
}
// not connected to Dropbox: show connect button
document.getElementById("dropboxConnectButton").style.display = "inline-block";
document.getElementById("loadFromDropboxButtons").style.display = "none";
document.getElementById("loadFromDropboxSelect").style.display = "none";
}
async function connectToDropbox() {
await Cloud.providers.dropbox.initialize();
if (Cloud.providers.dropbox.api) showLoadPane();
}
function loadURL() {

View file

@ -44,7 +44,8 @@ function editProvinces() {
cl = el.classList,
line = el.parentNode,
p = +line.dataset.id;
if (cl.contains("fillRect")) changeFill(el);
if (el.tagName === "FILL-BOX") changeFill(el);
else if (cl.contains("name")) editProvinceName(p);
else if (cl.contains("coaIcon")) editEmblem("province", "provinceCOA" + p, pack.provinces[p]);
else if (cl.contains("icon-star-empty")) capitalZoomIn(p);
@ -133,9 +134,7 @@ function editProvinces() {
lines += `<div class="states" data-id=${p.i} data-name="${p.name}" data-form="${p.formName}" data-color="${
p.color
}" data-capital="${capital}" data-state="${stateName}" data-area=${area} data-population=${population}>
<svg data-tip="Province fill style. Click to change" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${
p.color
}" class="fillRect pointer"></svg>
<fill-box fill="${p.color}"></fill-box>
<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 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>
@ -215,14 +214,14 @@ function editProvinces() {
function changeFill(el) {
const currentFill = el.getAttribute("fill");
const p = +el.parentNode.parentNode.dataset.id;
const p = +el.parentNode.dataset.id;
const callback = function (fill) {
el.setAttribute("fill", fill);
pack.provinces[p].color = fill;
const callback = newFill => {
el.fill = newFill;
pack.provinces[p].color = newFill;
const g = provs.select("#provincesBody");
g.select("#province" + p).attr("fill", fill);
g.select("#province-gap" + p).attr("stroke", fill);
g.select("#province" + p).attr("fill", newFill);
g.select("#province-gap" + p).attr("stroke", newFill);
};
openPicker(currentFill, callback);

View file

@ -14,7 +14,9 @@ function overviewRegiments(state) {
updateHeaders();
$("#regimentsOverview").dialog({
title: "Regiments Overview", resizable: false, width: fitContent(),
title: "Regiments Overview",
resizable: false,
width: fitContent(),
position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}
});
@ -31,11 +33,13 @@ function overviewRegiments(state) {
header.querySelectorAll(".removable").forEach(el => el.remove());
const insert = html => document.getElementById("regimentsTotal").insertAdjacentHTML("beforebegin", html);
for (const u of options.military) {
const label = capitalize(u.name.replace(/_/g, ' '));
const label = capitalize(u.name.replace(/_/g, " "));
insert(`<div data-tip="Regiment ${u.name} units number. Click to sort" class="sortable removable" data-sortby="${u.name}">${label}&nbsp;</div>`);
}
header.querySelectorAll(".removable").forEach(function(e) {
e.addEventListener("click", function() {sortLines(this);});
header.querySelectorAll(".removable").forEach(function (e) {
e.addEventListener("click", function () {
sortLines(this);
});
});
}
@ -51,11 +55,13 @@ function overviewRegiments(state) {
if (state !== -1 && s.i !== state) continue; // specific state is selected
for (const r of s.military) {
const sortData = options.military.map(u => `data-${u.name}=${r.u[u.name]||0}`).join(" ");
const lineData = options.military.map(u => `<div data-type="${u.name}" data-tip="${capitalize(u.name)} units number">${r.u[u.name]||0}</div>`).join(" ");
const sortData = options.military.map(u => `data-${u.name}=${r.u[u.name] || 0}`).join(" ");
const lineData = options.military
.map(u => `<div data-type="${u.name}" data-tip="${capitalize(u.name)} units number">${r.u[u.name] || 0}</div>`)
.join(" ");
lines += `<div class="states" data-id=${r.i} data-s="${s.i}" data-state="${s.name}" data-name="${r.name}" ${sortData} data-total="${r.a}">
<svg data-tip="${s.fullName}" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${s.color}" class="fillRect"></svg>
<fill-box data-tip="${s.fullName}" fill="${s.color}" disabled></fill-box>
<input data-tip="${s.fullName}" style="width:6em" value="${s.name}" readonly>
<span data-tip="Regiment's emblem" style="width:1em">${r.icon}</span>
<input data-tip="Regiment's name" style="width:13em" value="${r.name}" readonly>
@ -70,12 +76,15 @@ function overviewRegiments(state) {
lines += `<div id="regimentsTotalLine" class="totalLine" data-tip="Total of all displayed regiments">
<div style="width: 21em; margin-left: 1em">Regiments: ${regiments.length}</div>
${options.military.map(u => `<div style="width:5em">${si(d3.sum(regiments.map(r => r.u[u.name]||0)))}</div>`).join(" ")}
${options.military.map(u => `<div style="width:5em">${si(d3.sum(regiments.map(r => r.u[u.name] || 0)))}</div>`).join(" ")}
<div style="width:5em">${si(d3.sum(regiments.map(r => r.a)))}</div>
</div>`;
body.insertAdjacentHTML("beforeend", lines);
if (body.dataset.type === "percentage") {body.dataset.type = "absolute"; togglePercentageMode();}
if (body.dataset.type === "percentage") {
body.dataset.type = "absolute";
togglePercentageMode();
}
applySorting(regimentsHeader);
// add listeners
@ -87,7 +96,7 @@ function overviewRegiments(state) {
const filter = document.getElementById("regimentsFilter");
filter.options.length = 0; // remove all options
filter.options.add(new Option(`all`, -1, false, state === -1));
const statesSorted = pack.states.filter(s => s.i && !s.removed).sort((a, b) => (a.name > b.name) ? 1 : -1);
const statesSorted = pack.states.filter(s => s.i && !s.removed).sort((a, b) => (a.name > b.name ? 1 : -1));
statesSorted.forEach(s => filter.options.add(new Option(s.name, s.i, false, s.i == state)));
}
@ -108,19 +117,20 @@ function overviewRegiments(state) {
if (body.dataset.type === "absolute") {
body.dataset.type = "percentage";
const lines = body.querySelectorAll(":scope > div:not(.totalLine)");
const array = Array.from(lines), cache = [];
const array = Array.from(lines),
cache = [];
const total = function(type) {
const total = function (type) {
if (cache[type]) cache[type];
cache[type] = d3.sum(array.map(el => +el.dataset[type]));
return cache[type];
}
};
lines.forEach(function(el) {
el.querySelectorAll("div").forEach(function(div) {
lines.forEach(function (el) {
el.querySelectorAll("div").forEach(function (div) {
const type = div.dataset.type;
if (type === "rate") return;
div.textContent = total(type) ? rn(+el.dataset[type] / total(type) * 100) + "%" : "0%";
div.textContent = total(type) ? rn((+el.dataset[type] / total(type)) * 100) + "%" : "0%";
});
});
} else {
@ -145,15 +155,19 @@ function overviewRegiments(state) {
function addRegimentOnClick() {
const state = +regimentsFilter.value;
if (state === -1) {tip("Please select state from the list", false, "error"); return;}
if (state === -1) {
tip("Please select state from the list", false, "error");
return;
}
const point = d3.mouse(this);
const cell = findCell(point[0], point[1]);
const x = pack.cells.p[cell][0], y = pack.cells.p[cell][1];
const x = pack.cells.p[cell][0],
y = pack.cells.p[cell][1];
const military = pack.states[state].military;
const i = military.length ? last(military).i + 1 : 0;
const n = +(pack.cells.h[cell] < 20); // naval or land
const reg = {a:0, cell, i, n, u:{}, x, y, bx:x, by:y, state, icon:"🛡️"};
const reg = {a: 0, cell, i, n, u: {}, x, y, bx: x, by: y, state, icon: "🛡️"};
reg.name = Military.getName(reg, military);
military.push(reg);
Military.generateNote(reg, pack.states[state]); // add legend
@ -163,9 +177,9 @@ function overviewRegiments(state) {
function downloadRegimentsData() {
const units = options.military.map(u => u.name);
let data = "State,Id,Name,"+units.map(u => capitalize(u)).join(",")+",Total\n"; // headers
let data = "State,Id,Name," + units.map(u => capitalize(u)).join(",") + ",Total\n"; // headers
body.querySelectorAll(":scope > div:not(.totalLine)").forEach(function(el) {
body.querySelectorAll(":scope > div:not(.totalLine)").forEach(function (el) {
data += el.dataset.state + ",";
data += el.dataset.id + ",";
data += el.dataset.name + ",";
@ -176,5 +190,4 @@ function overviewRegiments(state) {
const name = getFileName("Regiments") + ".csv";
downloadFile(data, name);
}
}

View file

@ -79,7 +79,7 @@ function editReligions() {
if (r.i) {
lines += `<div class="states religions" data-id=${r.i} data-name="${r.name}" data-color="${r.color}" data-area=${area}
data-population=${population} data-type=${r.type} data-form=${r.form} data-deity="${r.deity ? r.deity : ""}" data-expansionism=${r.expansionism}>
<svg data-tip="Religion fill style. Click to change" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${r.color}" class="fillRect pointer"></svg>
<fill-box fill="${r.color}"></fill-box>
<input data-tip="Religion name. Click and type to change" class="religionName" value="${r.name}" autocorrect="off" spellcheck="false">
<select data-tip="Religion type" class="religionType">${getTypeOptions(r.type)}</select>
<input data-tip="Religion form" class="religionForm hide" value="${r.form}" autocorrect="off" spellcheck="false">
@ -93,7 +93,9 @@ function editReligions() {
</div>`;
} else {
// No religion (neutral) line
lines += `<div class="states" data-id=${r.i} data-name="${r.name}" data-color="" data-area=${area} data-population=${population} data-type="" data-form="" data-deity="" data-expansionism="">
lines += `<div class="states" data-id=${r.i} data-name="${
r.name
}" data-color="" data-area=${area} data-population=${population} data-type="" data-form="" data-deity="" data-expansionism="">
<svg width="9" height="9" class="placeholder"></svg>
<input data-tip="Religion name. Click and type to change" class="religionName italic" value="${r.name}" autocorrect="off" spellcheck="false">
<select data-tip="Religion type" class="religionType placeholder">${getTypeOptions(r.type)}</select>
@ -124,7 +126,7 @@ function editReligions() {
body.querySelectorAll("div.religions").forEach(el => el.addEventListener("mouseenter", ev => religionHighlightOn(ev)));
body.querySelectorAll("div.religions").forEach(el => el.addEventListener("mouseleave", ev => religionHighlightOff(ev)));
body.querySelectorAll("div.states").forEach(el => el.addEventListener("click", selectReligionOnLineClick));
body.querySelectorAll("rect.fillRect").forEach(el => el.addEventListener("click", religionChangeColor));
body.querySelectorAll("fill-box").forEach(el => el.addEventListener("click", religionChangeColor));
body.querySelectorAll("div > input.religionName").forEach(el => el.addEventListener("input", religionChangeName));
body.querySelectorAll("div > select.religionType").forEach(el => el.addEventListener("change", religionChangeType));
body.querySelectorAll("div > input.religionForm").forEach(el => el.addEventListener("input", religionChangeForm));
@ -215,13 +217,13 @@ function editReligions() {
function religionChangeColor() {
const el = this;
const currentFill = el.getAttribute("fill");
const religion = +el.parentNode.parentNode.dataset.id;
const religion = +el.parentNode.dataset.id;
const callback = function (fill) {
el.setAttribute("fill", fill);
pack.religions[religion].color = fill;
relig.select("#religion" + religion).attr("fill", fill);
debug.select("#religionsCenter" + religion).attr("fill", fill);
const callback = newFill => {
el.fill = newFill;
pack.religions[religion].color = newFill;
relig.select("#religion" + religion).attr("fill", newFill);
debug.select("#religionsCenter" + religion).attr("fill", newFill);
};
openPicker(currentFill, callback);
@ -459,7 +461,13 @@ function editReligions() {
// prepare svg
alertMessage.innerHTML = "<div id='religionInfo' class='chartInfo'>&#8205;</div>";
const svg = d3.select("#alertMessage").insert("svg", "#religionInfo").attr("id", "hierarchy").attr("width", width).attr("height", height).style("text-anchor", "middle");
const svg = d3
.select("#alertMessage")
.insert("svg", "#religionInfo")
.attr("id", "hierarchy")
.attr("width", width)
.attr("height", height)
.style("text-anchor", "middle");
const graph = svg.append("g").attr("transform", `translate(10, -45)`);
const links = graph.append("g").attr("fill", "none").attr("stroke", "#aaaaaa");
const nodes = graph.append("g");
@ -473,7 +481,24 @@ function editReligions() {
.enter()
.append("path")
.attr("d", d => {
return "M" + d.source.x + "," + d.source.y + "C" + d.source.x + "," + (d.source.y * 3 + d.target.y) / 4 + " " + d.target.x + "," + (d.source.y * 2 + d.target.y) / 3 + " " + d.target.x + "," + d.target.y;
return (
"M" +
d.source.x +
"," +
d.source.y +
"C" +
d.source.x +
"," +
(d.source.y * 3 + d.target.y) / 4 +
" " +
d.target.x +
"," +
(d.source.y * 2 + d.target.y) / 3 +
" " +
d.target.x +
"," +
d.target.y
);
});
const node = nodes
@ -578,7 +603,11 @@ function editReligions() {
$("#religionsEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg"}});
tip("Click on religion to select, drag the circle to change religion", true);
viewbox.style("cursor", "crosshair").on("click", selectReligionOnMapClick).call(d3.drag().on("start", dragReligionBrush)).on("touchmove mousemove", moveReligionBrush);
viewbox
.style("cursor", "crosshair")
.on("click", selectReligionOnMapClick)
.call(d3.drag().on("start", dragReligionBrush))
.on("touchmove mousemove", moveReligionBrush);
body.querySelector("div").classList.add("selected");
}

View file

@ -45,7 +45,7 @@ function editStates() {
cl = el.classList,
line = el.parentNode,
state = +line.dataset.id;
if (cl.contains("fillRect")) stateChangeFill(el);
if (el.tagName === "FILL-BOX") stateChangeFill(el);
else if (cl.contains("name")) editStateName(state);
else if (cl.contains("coaIcon")) editEmblem("state", "stateCOA" + state, pack.states[state]);
else if (cl.contains("icon-star-empty")) stateCapitalZoomIn(state);
@ -102,7 +102,7 @@ function editStates() {
// Neutral line
lines += `<div class="states" data-id=${s.i} data-name="${s.name}" data-cells=${s.cells} data-area=${area}
data-population=${population} data-burgs=${s.burgs} data-color="" data-form="" data-capital="" data-culture="" data-type="" data-expansionism="">
<svg width="9" height="9" class="placeholder"></svg>
<svg width="1em" height="1em" class="placeholder"></svg>
<input data-tip="Neutral lands name. Click to change" class="stateName name pointer italic" value="${s.name}" readonly>
<svg class="coaIcon placeholder hide"></svg>
<input class="stateForm placeholder" value="none">
@ -126,15 +126,10 @@ function editStates() {
const capital = pack.burgs[s.capital].name;
COArenderer.trigger("stateCOA" + s.i, s.coa);
lines += `<div class="states" data-id=${s.i} data-name="${s.name}" data-form="${s.formName}" data-capital="${capital}" data-color="${
s.color
}" data-cells=${s.cells}
data-area=${area} data-population=${population} data-burgs=${s.burgs} data-culture=${pack.cultures[s.culture].name} data-type=${
s.type
} data-expansionism=${s.expansionism}>
<svg data-tip="State fill style. Click to change" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${
s.color
}" class="fillRect pointer"></svg>
lines += `<div class="states" data-id=${s.i} data-name="${s.name}" data-form="${s.formName}" data-capital="${capital}"
data-color="${s.color}" data-cells=${s.cells} data-area=${area} data-population=${population} data-burgs=${s.burgs}
data-culture=${pack.cultures[s.culture].name} data-type=${s.type} data-expansionism=${s.expansionism}>
<fill-box fill="${s.color}"></fill-box>
<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 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>
@ -149,9 +144,8 @@ function editStates() {
<div data-tip="${populationTip}" class="culturePopulation hide">${si(population)}</div>
<select data-tip="State type. Defines growth model. Click to change" class="cultureType ${hidden} show hide">${getTypeOptions(s.type)}</select>
<span data-tip="State expansionism" class="icon-resize-full ${hidden} show hide"></span>
<input data-tip="Expansionism (defines competitive size). Change to re-calculate states based on new value" class="statePower ${hidden} show hide" type="number" min=0 max=99 step=.1 value=${
s.expansionism
}>
<input data-tip="Expansionism (defines competitive size). Change to re-calculate states based on new value"
class="statePower ${hidden} show hide" type="number" min=0 max=99 step=.1 value=${s.expansionism}>
<span data-tip="Cells count" class="icon-check-empty ${hidden} show hide"></span>
<div data-tip="Cells count" class="stateCells ${hidden} show hide">${s.cells}</div>
<span data-tip="Toggle state focus" class="icon-pin ${focused ? "" : " inactive"} hide"></span>
@ -237,18 +231,18 @@ function editStates() {
function stateChangeFill(el) {
const currentFill = el.getAttribute("fill");
const state = +el.parentNode.parentNode.dataset.id;
const state = +el.parentNode.dataset.id;
const callback = function (fill) {
el.setAttribute("fill", fill);
pack.states[state].color = fill;
statesBody.select("#state" + state).attr("fill", fill);
statesBody.select("#state-gap" + state).attr("stroke", fill);
const halo = d3.color(fill) ? d3.color(fill).darker().hex() : "#666666";
const callback = function (newFill) {
el.fill = newFill;
pack.states[state].color = newFill;
statesBody.select("#state" + state).attr("fill", newFill);
statesBody.select("#state-gap" + state).attr("stroke", newFill);
const halo = d3.color(newFill) ? d3.color(newFill).darker().hex() : "#666666";
statesHalo.select("#state-border" + state).attr("stroke", halo);
// recolor regiments
const solidColor = fill[0] === "#" ? fill : "#999";
const solidColor = newFill[0] === "#" ? newFill : "#999";
const darkerColor = d3.color(solidColor).darker().hex();
armies.select("#army" + state).attr("fill", solidColor);
armies

File diff suppressed because one or more lines are too long

311
modules/ui/stylePresets.js Normal file
View file

@ -0,0 +1,311 @@
// UI module to control the style presets
"use strict";
const systemPresets = ["default", "ancient", "gloom", "light", "watercolor", "clean", "atlas", "cyberpunk", "monochrome"];
const customPresetPrefix = "fmgStyle_";
// add style presets to list
{
const systemOptions = systemPresets.map(styleName => `<option value="${styleName}">${styleName}</option>`);
const storedStyles = Object.keys(localStorage).filter(key => key.startsWith(customPresetPrefix));
const customOptions = storedStyles.map(styleName => `<option value="${styleName}">${styleName.replace(customPresetPrefix, "")} [custom]</option>`);
const options = systemOptions.join("") + customOptions.join("");
document.getElementById("stylePreset").innerHTML = options;
}
async function applyStyleOnLoad() {
const desiredPreset = localStorage.getItem("presetStyle") || "default";
const styleData = await getStylePreset(desiredPreset);
const [appliedPreset, style] = styleData;
applyStyle(style);
updateMapFilter();
stylePreset.value = stylePreset.dataset.old = appliedPreset;
setPresetRemoveButtonVisibiliy();
}
async function getStylePreset(desiredPreset) {
let presetToLoad = desiredPreset;
const isCustom = !systemPresets.includes(desiredPreset);
if (isCustom) {
const storedStyleJSON = localStorage.getItem(desiredPreset);
if (!storedStyleJSON) {
ERROR && console.error(`Custom style ${desiredPreset} in not found in localStorage. Applying default style`);
presetToLoad = "default";
} else {
const isValid = JSON.isValid(storedStyleJSON);
if (isValid) return [desiredPreset, JSON.parse(storedStyleJSON)];
ERROR && console.error(`Custom style ${desiredPreset} stored in localStorage is not valid. Applying default style`);
presetToLoad = "default";
}
}
const style = await fetchSystemPreset(presetToLoad);
return [presetToLoad, style];
}
async function fetchSystemPreset(preset) {
const style = await fetch(`./styles/${preset}.json`)
.then(res => res.json())
.catch(err => {
ERROR && console.error("Error on loading style preset", preset, err);
return null;
});
if (!style) throw new Error("Cannot fetch style preset", preset);
return style;
}
function applyStyle(style) {
for (const selector in style) {
const el = document.querySelector(selector);
if (!el) continue;
for (const attribute in style[selector]) {
const value = style[selector][attribute];
if (value === "null" || value === null) {
el.removeAttribute(attribute);
continue;
}
if (attribute === "text-shadow") {
el.style[attribute] = value;
} else {
el.setAttribute(attribute, value);
}
}
}
}
function requestStylePresetChange(preset) {
const isConfirmed = sessionStorage.getItem("styleChangeConfirmed");
if (isConfirmed) {
changeStyle(preset);
return;
}
confirmationDialog({
title: "Change style preset",
message: "Are you sure you want to change the style preset? All unsaved style changes will be lost",
confirm: "Change",
onConfirm: () => {
sessionStorage.setItem("styleChangeConfirmed", true);
changeStyle(preset);
},
onCancel: () => {
stylePreset.value = stylePreset.dataset.old;
}
});
}
async function changeStyle(desiredPreset) {
const styleData = await getStylePreset(desiredPreset);
const [appliedPreset, style] = styleData;
localStorage.setItem("presetStyle", appliedPreset);
applyStyleWithUiRefresh(style);
}
function applyStyleWithUiRefresh(style) {
applyStyle(style);
updateElements();
selectStyleElement(); // re-select element to trigger values update
updateMapFilter();
stylePreset.dataset.old = stylePreset.value;
invokeActiveZooming();
setPresetRemoveButtonVisibiliy();
}
function addStylePreset() {
$("#styleSaver").dialog({title: "Style Saver", width: "26em", position: {my: "center", at: "center", of: "svg"}});
const styleName = stylePreset.value.replace(customPresetPrefix, "");
document.getElementById("styleSaverName").value = styleName;
styleSaverJSON.value = JSON.stringify(collectStyleData(), null, 2);
checkName();
if (modules.saveStyle) return;
modules.saveStyle = true;
// add listeners
document.getElementById("styleSaverName").addEventListener("input", checkName);
document.getElementById("styleSaverSave").addEventListener("click", saveStyle);
document.getElementById("styleSaverDownload").addEventListener("click", styleDownload);
document.getElementById("styleSaverLoad").addEventListener("click", () => styleToLoad.click());
document.getElementById("styleToLoad").addEventListener("change", loadStyleFile);
function collectStyleData() {
const style = {};
const attributes = {
"#map": ["background-color", "filter", "data-filter"],
"#armies": ["font-size", "box-size", "stroke", "stroke-width", "fill-opacity", "filter"],
"#biomes": ["opacity", "filter", "mask"],
"#stateBorders": ["opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter"],
"#provinceBorders": ["opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter"],
"#cells": ["opacity", "stroke", "stroke-width", "filter", "mask"],
"#gridOverlay": ["opacity", "scale", "dx", "dy", "type", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "transform", "filter", "mask"],
"#coordinates": ["opacity", "data-size", "font-size", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter", "mask"],
"#compass": ["opacity", "transform", "filter", "mask", "shape-rendering"],
"#rose": ["transform"],
"#relig": ["opacity", "stroke", "stroke-width", "filter"],
"#cults": ["opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter"],
"#landmass": ["opacity", "fill", "filter"],
"#markers": ["opacity", "rescale", "filter"],
"#prec": ["opacity", "stroke", "stroke-width", "fill", "filter"],
"#population": ["opacity", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter"],
"#rural": ["stroke"],
"#urban": ["stroke"],
"#freshwater": ["opacity", "fill", "stroke", "stroke-width", "filter"],
"#salt": ["opacity", "fill", "stroke", "stroke-width", "filter"],
"#sinkhole": ["opacity", "fill", "stroke", "stroke-width", "filter"],
"#frozen": ["opacity", "fill", "stroke", "stroke-width", "filter"],
"#lava": ["opacity", "fill", "stroke", "stroke-width", "filter"],
"#dry": ["opacity", "fill", "stroke", "stroke-width", "filter"],
"#sea_island": ["opacity", "stroke", "stroke-width", "filter", "auto-filter"],
"#lake_island": ["opacity", "stroke", "stroke-width", "filter"],
"#terrain": ["opacity", "set", "size", "density", "filter", "mask"],
"#rivers": ["opacity", "filter", "fill"],
"#ruler": ["opacity", "filter"],
"#roads": ["opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter", "mask"],
"#trails": ["opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter", "mask"],
"#searoutes": ["opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter", "mask"],
"#statesBody": ["opacity", "filter"],
"#statesHalo": ["opacity", "data-width", "stroke-width", "filter"],
"#provs": ["opacity", "fill", "font-size", "font-family", "filter"],
"#temperature": ["opacity", "font-size", "fill", "fill-opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter"],
"#ice": ["opacity", "fill", "stroke", "stroke-width", "filter"],
"#emblems": ["opacity", "stroke-width", "filter"],
"#texture": ["opacity", "filter", "mask"],
"#textureImage": ["x", "y"],
"#zones": ["opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter", "mask"],
"#oceanLayers": ["filter", "layers"],
"#oceanBase": ["fill"],
"#oceanicPattern": ["href", "opacity"],
"#terrs": ["opacity", "scheme", "terracing", "skip", "relax", "curve", "filter", "mask"],
"#legend": ["data-size", "font-size", "font-family", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "data-x", "data-y", "data-columns"],
"#legendBox": ["fill", "fill-opacity"],
"#burgLabels > #cities": ["opacity", "fill", "text-shadow", "data-size", "font-size", "font-family"],
"#burgIcons > #cities": ["opacity", "fill", "fill-opacity", "size", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap"],
"#anchors > #cities": ["opacity", "fill", "size", "stroke", "stroke-width"],
"#burgLabels > #towns": ["opacity", "fill", "text-shadow", "data-size", "font-size", "font-family"],
"#burgIcons > #towns": ["opacity", "fill", "fill-opacity", "size", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap"],
"#anchors > #towns": ["opacity", "fill", "size", "stroke", "stroke-width"],
"#labels > #states": ["opacity", "fill", "stroke", "stroke-width", "text-shadow", "data-size", "font-size", "font-family", "filter"],
"#labels > #addedLabels": ["opacity", "fill", "stroke", "stroke-width", "text-shadow", "data-size", "font-size", "font-family", "filter"],
"#fogging": ["opacity", "fill", "filter"]
};
for (const selector in attributes) {
const el = document.querySelector(selector);
if (!el) continue;
style[selector] = {};
for (const attr of attributes[selector]) {
let value = el.style[attr] || el.getAttribute(attr);
if (attr === "font-size" && el.hasAttribute("data-size")) value = el.getAttribute("data-size");
style[selector][attr] = parseValue(value);
}
}
function parseValue(value) {
if (value === "null" || value === null) return null;
if (value === "") return "";
if (!isNaN(+value)) return +value;
return value;
}
return style;
}
function checkName() {
const styleName = customPresetPrefix + styleSaverName.value;
const isSystem = systemPresets.includes(styleName) || systemPresets.includes(styleSaverName.value);
if (isSystem) return (styleSaverTip.innerHTML = "default");
const isExisting = Array.from(stylePreset.options).some(option => option.value == styleName);
if (isExisting) return (styleSaverTip.innerHTML = "existing");
styleSaverTip.innerHTML = "new";
}
function saveStyle() {
const styleJSON = styleSaverJSON.value;
const desiredName = styleSaverName.value;
if (!styleJSON) return tip("Please provide a style JSON", false, "error");
if (!JSON.isValid(styleJSON)) return tip("JSON string is not valid, please check the format", false, "error");
if (!desiredName) return tip("Please provide a preset name", false, "error");
if (styleSaverTip.innerHTML === "default") return tip("You cannot overwrite default preset, please change the name", false, "error");
const presetName = customPresetPrefix + desiredName;
applyOption(stylePreset, presetName, desiredName + " [custom]");
localStorage.setItem("presetStyle", presetName);
localStorage.setItem(presetName, styleJSON);
applyStyleWithUiRefresh(JSON.parse(styleJSON));
tip("Style preset is saved and applied", false, "success", 4000);
$("#styleSaver").dialog("close");
}
function styleDownload() {
const styleJSON = styleSaverJSON.value;
const styleName = styleSaverName.value;
if (!styleJSON) return tip("Please provide a style JSON", false, "error");
if (!JSON.isValid(styleJSON)) return tip("JSON string is not valid, please check the format", false, "error");
if (!styleName) return tip("Please provide a preset name", false, "error");
downloadFile(styleJSON, styleName + ".json", "application/json");
}
function loadStyleFile() {
const fileName = this.files[0]?.name.replace(/\.[^.]*$/, "");
uploadFile(this, styleUpload);
function styleUpload(dataLoaded) {
if (!dataLoaded) return tip("Cannot load the file. Please check the data format", false, "error");
const isValid = JSON.isValid(dataLoaded);
if (!isValid) return tip("Loaded data is not a valid JSON, please check the format", false, "error");
styleSaverJSON.value = JSON.stringify(JSON.parse(dataLoaded), null, 2);
styleSaverName.value = fileName;
checkName();
tip("Style preset is uploaded", false, "success", 4000);
}
}
}
function requestRemoveStylePreset() {
const isDefault = systemPresets.includes(stylePreset.value);
if (isDefault) return tip("Cannot remove system preset", false, "error");
confirmationDialog({
title: "Remove style preset",
message: "Are you sure you want to remove the style preset? This action cannot be undone.",
confirm: "Remove",
onConfirm: removeStylePreset
});
}
function removeStylePreset() {
localStorage.removeItem("presetStyle");
localStorage.removeItem(stylePreset.value);
stylePreset.selectedOptions[0].remove();
changeStyle("default");
}
function updateMapFilter() {
const filter = svg.attr("data-filter");
mapFilters.querySelectorAll(".pressed").forEach(button => button.classList.remove("pressed"));
if (!filter) return;
mapFilters.querySelector("#" + filter).classList.add("pressed");
}
function setPresetRemoveButtonVisibiliy() {
const isDefault = systemPresets.includes(stylePreset.value);
removeStyleButton.style.display = isDefault ? "none" : "inline-block";
}

View file

@ -0,0 +1,161 @@
"use strict";
function showBurgTemperatureGraph(id) {
const b = pack.burgs[id];
const lat = mapCoordinates.latN - (b.y / graphHeight) * mapCoordinates.latT;
const burgTemp = grid.cells.temp[pack.cells.g[b.cell]];
const prec = grid.cells.prec[pack.cells.g[b.cell]];
// prettier-ignore
const weights = [
[
[10.782752257744338, 2.7100404240962126], [-2.8226802110591462, 51.62920138583541], [-6.6250956268643835, 4.427939197315455], [-59.64690518541339, 41.89084162654791], [-1.3302059550553835, -3.6964487738450913],
[-2.5844898544535497, 0.09879268612455298], [-5.58528252533573, -0.23426224364501905], [26.94531337690372, 20.898158905988907], [3.816397481634785, -0.19045424064580757], [-4.835697931609101, -10.748232783636434]
],
[
[-2.478952081870123, 0.6405800134306895, -7.136785640930911, -0.2186529024764509, 3.6568435212735424, 31.446026153530838, -19.91005187482281, 0.2543395274783306, -7.036924569659988, -0.7721371621651565],
[-197.10583739743538, 6.889921141533474, 0.5058941504631129, 7.7667203434606416, -53.74180550086929, -15.717331715167001, -61.32068414155791, -2.259728220978728, 35.84049189540032, 94.6157364730977],
[-5.312011591880851, -0.09923148954215096, -1.7132477487917586, -22.55559652066422, 0.4806107280554336, -26.5583974109492, 2.0558257347014863, 25.815645234787432, -18.569029876991156, -2.6792003366730035],
[20.706518520569514, 18.344297403881875, 99.52244671131733, -58.53124969563653, -60.74384321042212, -80.57540534651835, 7.884792406540866, -144.33871131678563, 80.134199744324, 20.50745285622448],
[-52.88299538575159, -15.782505343805528, 16.63316001054924, 88.09475330556671, -17.619552086641818, -19.943999528182427, -120.46286026828177, 19.354752020806302, 43.49422099308949, 28.733924806541363],
[-2.4621368711159897, -1.2074759925679757, -1.5133898639835084, 2.173715352424188, -5.988707597991683, 3.0234147182203843, 3.3284199340000797, -1.8805161326360575, 5.151910934121654, -1.2540553911612116]
],
[
[-0.3357437479474717, 0.01430651794222215, -0.7927524256670906, 0.2121636229648523, 1.0587803023358318, -3.759288325505095],
[-1.1988028704442968, 1.3768997508052783, -3.8480086358278816, 0.5289387340947143, 0.5769459339961177, -1.2528318145750772],
[1.0074966649240946, 1.155301164699459, -2.974254371052421, 0.47408176553219467, 0.5939042688615264, -0.7631976947131744]
]
];
// From (-∞, ∞) to ~[-1, 1]
const In1 = [(Math.abs(lat) - 26.950680212887473) / 48.378128506956, (prec - 12.229929140832644) / 29.94402033696607];
let lastIn = In1;
let lstOut = [];
for (let levelN = 0; levelN < weights.length; levelN++) {
const layerN = weights[levelN];
for (let i = 0; i < layerN.length; i++) {
lstOut[i] = 0;
for (let j = 0; j < layerN[i].length; j++) {
lstOut[i] = lstOut[i] + lastIn[j] * layerN[i][j];
}
// sigmoid
lstOut[i] = 1 / (1 + Math.exp(-lstOut[i]));
}
lastIn = lstOut.slice(0);
}
// Standard deviation for average temperature for the year from [0, 1] to [min, max]
const yearSig = lstOut[0] * 62.9466411977018 + 0.28613807855649165;
// Standard deviation for the difference between the minimum and maximum temperatures for the year
const yearDelTmpSig = lstOut[1] * 13.541688670361175 + 0.1414213562373084 > yearSig ? yearSig : lstOut[1] * 13.541688670361175 + 0.1414213562373084;
// Expected value for the difference between the minimum and maximum temperatures for the year
const yearDelTmpMu = lstOut[2] * 15.266666666666667 + 0.6416666666666663;
// Temperature change shape
const delT = yearDelTmpMu / 2 + (0.5 * yearDelTmpSig) / 2;
const minT = burgTemp - Math.max(yearSig + delT, 15);
const maxT = burgTemp + (burgTemp - minT);
const chartWidth = Math.max(window.innerWidth / 2, 580);
const chartHeight = 300;
// drawing starting point from top-left (y = 0) of SVG
const xOffset = 60;
const yOffset = 10;
const year = new Date().getFullYear(); // use current year
const startDate = new Date(year, 0, 1);
const endDate = new Date(year, 11, 31);
const months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
const xscale = d3.scaleTime().domain([startDate, endDate]).range([0, chartWidth]);
const yscale = d3.scaleLinear().domain([minT, maxT]).range([chartHeight, 0]);
const tempMean = [];
const tempMin = [];
const tempMax = [];
months.forEach((month, index) => {
const rate = index / 11;
let formTmp = Math.cos(rate * 2 * Math.PI) / 2;
if (lat > 0) formTmp = -formTmp;
const x = rate * chartWidth + xOffset;
const tempAverage = formTmp * yearSig + burgTemp;
const tempDelta = yearDelTmpMu / 2 + (formTmp * yearDelTmpSig) / 2;
tempMean.push([x, yscale(tempAverage) + yOffset]);
tempMin.push([x, yscale(tempAverage - tempDelta) + yOffset]);
tempMax.push([x, yscale(tempAverage + tempDelta) + yOffset]);
});
drawGraph();
$("#alert").dialog({title: "Annual temperature in " + b.name, width: "auto", position: {my: "center", at: "center", of: "svg"}});
function drawGraph() {
alertMessage.innerHTML = "";
const getCurve = data => round(d3.line().curve(d3.curveBasis)(data), 2);
const legendSize = 60;
const chart = d3
.select("#alertMessage")
.append("svg")
.attr("width", chartWidth + 120)
.attr("height", chartHeight + yOffset + legendSize);
const legend = chart.append("g");
const legendY = chartHeight + yOffset + legendSize * 0.8;
const legendX = n => (chartWidth * n) / 4;
const legendTextX = n => legendX(n) + 10;
legend.append("circle").attr("cx", legendX(1)).attr("cy", legendY).attr("r", 4).style("fill", "red");
legend.append("text").attr("x", legendTextX(1)).attr("y", legendY).attr("alignment-baseline", "central").text("Day temperature");
legend.append("circle").attr("cx", legendX(2)).attr("cy", legendY).attr("r", 4).style("fill", "orange");
legend.append("text").attr("x", legendTextX(2)).attr("y", legendY).attr("alignment-baseline", "central").text("Mean temperature");
legend.append("circle").attr("cx", legendX(3)).attr("cy", legendY).attr("r", 4).style("fill", "blue");
legend.append("text").attr("x", legendTextX(3)).attr("y", legendY).attr("alignment-baseline", "central").text("Night temperature");
const xGrid = d3.axisBottom(xscale).ticks().tickSize(-chartHeight);
const yGrid = d3.axisLeft(yscale).ticks(5).tickSize(-chartWidth);
const grid = chart.append("g").attr("class", "epgrid").attr("stroke-dasharray", "4 1");
grid.append("g").attr("transform", `translate(${xOffset}, ${chartHeight + yOffset})`).call(xGrid); // prettier-ignore
grid.append("g").attr("transform", `translate(${xOffset}, ${yOffset})`).call(yGrid);
grid.selectAll("text").remove();
// add zero degree line
if (minT < 0 && maxT > 0) {
grid
.append("line")
.attr("x1", xOffset)
.attr("y1", yscale(0) + yOffset)
.attr("x2", chartWidth + xOffset)
.attr("y2", yscale(0) + yOffset)
.attr("stroke", "gray");
}
const xAxis = d3.axisBottom(xscale).ticks().tickFormat(d3.timeFormat("%B"));
const yAxis = d3.axisLeft(yscale).ticks(5).tickFormat(convertTemperature);
const axis = chart.append("g");
axis
.append("g")
.attr("transform", `translate(${xOffset}, ${chartHeight + yOffset})`)
.call(xAxis);
axis.append("g").attr("transform", `translate(${xOffset}, ${yOffset})`).call(yAxis);
axis.select("path.domain").attr("d", `M0.5,0.5 H${chartWidth + 0.5}`);
const curves = chart.append("g").attr("fill", "none").style("stroke-width", 2.5);
curves.append("path").attr("d", getCurve(tempMean)).attr("data-type", "daily").attr("stroke", "orange").on("mousemove", printVal);
curves.append("path").attr("d", getCurve(tempMin)).attr("data-type", "night").attr("stroke", "blue").on("mousemove", printVal);
curves.append("path").attr("d", getCurve(tempMax)).attr("data-type", "day").attr("stroke", "red").on("mousemove", printVal);
function printVal() {
const [x, y] = d3.mouse(this);
const type = this.getAttribute("data-type");
const temp = convertTemperature(yscale.invert(y - yOffset));
const month = months[rn(((x - xOffset) / chartWidth) * 12)] || months[0];
tip(`Average ${type} temperature in ${month}: ${temp}`);
}
}
}

View file

@ -11,6 +11,8 @@ function editUnits() {
position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}
});
const drawBar = () => drawScaleBar(scale);
// add listeners
document.getElementById("distanceUnitInput").addEventListener("change", changeDistanceUnit);
document.getElementById("distanceScaleOutput").addEventListener("input", changeDistanceScale);
@ -19,9 +21,9 @@ function editUnits() {
document.getElementById("heightExponentInput").addEventListener("input", changeHeightExponent);
document.getElementById("heightExponentOutput").addEventListener("input", changeHeightExponent);
document.getElementById("temperatureScale").addEventListener("change", changeTemperatureScale);
document.getElementById("barSizeOutput").addEventListener("input", drawScaleBar);
document.getElementById("barSizeInput").addEventListener("input", drawScaleBar);
document.getElementById("barLabel").addEventListener("input", drawScaleBar);
document.getElementById("barSizeOutput").addEventListener("input", drawBar);
document.getElementById("barSizeInput").addEventListener("input", drawBar);
document.getElementById("barLabel").addEventListener("input", drawBar);
document.getElementById("barPosX").addEventListener("input", fitScaleBar);
document.getElementById("barPosY").addEventListener("input", fitScaleBar);
document.getElementById("barBackOpacity").addEventListener("input", changeScaleBarOpacity);
@ -46,19 +48,18 @@ function editUnits() {
prompt("Provide a custom name for a distance unit", {default: ""}, custom => {
this.options.add(new Option(custom, custom, false, true));
lock("distanceUnit");
drawScaleBar();
drawScaleBar(scale);
calculateFriendlyGridSize();
});
return;
}
drawScaleBar();
drawScaleBar(scale);
calculateFriendlyGridSize();
}
function changeDistanceScale() {
distanceScale = +document.getElementById("distanceScaleInput").value;
drawScaleBar();
drawScaleBar(scale);
calculateFriendlyGridSize();
}
@ -138,7 +139,7 @@ function editUnits() {
localStorage.removeItem("barBackColor");
localStorage.removeItem("barPosX");
localStorage.removeItem("barPosY");
drawScaleBar();
drawScaleBar(scale);
// population
populationRate = populationRateOutput.value = populationRateInput.value = 1000;

View file

@ -4,6 +4,8 @@ function editZones() {
closeDialogs();
if (!layerIsOn("toggleZones")) toggleZones();
const body = document.getElementById("zonesBodySection");
updateFilters();
zonesEditorAddLines();
if (modules.editZones) return;
@ -18,6 +20,8 @@ function editZones() {
});
// add listeners
document.getElementById("zonesFilterType").addEventListener("click", updateFilters);
document.getElementById("zonesFilterType").addEventListener("change", filterZonesByType);
document.getElementById("zonesEditorRefresh").addEventListener("click", zonesEditorAddLines);
document.getElementById("zonesEditStyle").addEventListener("click", () => editStyle("zones"));
document.getElementById("zonesLegend").addEventListener("click", toggleLegend);
@ -33,55 +37,60 @@ function editZones() {
const el = ev.target,
cl = el.classList,
zone = el.parentNode.dataset.id;
if (cl.contains("culturePopulation")) {
changePopulation(zone);
return;
}
if (cl.contains("icon-trash-empty")) {
zoneRemove(zone);
return;
}
if (cl.contains("icon-eye")) {
toggleVisibility(el);
return;
}
if (cl.contains("icon-pin")) {
toggleFog(zone, cl);
return;
}
if (cl.contains("fillRect")) {
changeFill(el);
return;
}
if (el.tagName === "FILL-BOX") changeFill(el);
else if (cl.contains("culturePopulation")) changePopulation(zone);
else if (cl.contains("icon-trash-empty")) zoneRemove(zone);
else if (cl.contains("icon-eye")) toggleVisibility(el);
else if (cl.contains("icon-pin")) toggleFog(zone, cl);
if (customization) selectZone(el);
});
body.addEventListener("input", function (ev) {
const el = ev.target,
zone = el.parentNode.dataset.id;
if (el.classList.contains("religionName")) zones.select("#" + zone).attr("data-description", el.value);
const el = ev.target;
const zone = zones.select("#" + el.parentNode.dataset.id);
if (el.classList.contains("zoneName")) zone.attr("data-description", el.value);
else if (el.classList.contains("zoneType")) zone.attr("data-type", el.value);
});
// update type filter with a list of used types
function updateFilters() {
const zones = Array.from(document.querySelectorAll("#zones > g"));
const types = unique(zones.map(zone => zone.dataset.type));
const filterSelect = document.getElementById("zonesFilterType");
const typeToFilterBy = types.includes(zonesFilterType.value) ? zonesFilterType.value : "all";
filterSelect.innerHTML = "<option value='all'>all</option>" + types.map(type => `<option value="${type}">${type}</option>`).join("");
filterSelect.value = typeToFilterBy;
}
// add line for each zone
function zonesEditorAddLines() {
const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value;
let lines = "";
zones.selectAll("g").each(function () {
const c = this.dataset.cells ? this.dataset.cells.split(",").map(c => +c) : [];
const description = this.dataset.description;
const fill = this.getAttribute("fill");
const typeToFilterBy = document.getElementById("zonesFilterType").value;
const zones = Array.from(document.querySelectorAll("#zones > g"));
const filteredZones = typeToFilterBy === "all" ? zones : zones.filter(zone => zone.dataset.type === typeToFilterBy);
const lines = filteredZones.map(zoneEl => {
const c = zoneEl.dataset.cells ? zoneEl.dataset.cells.split(",").map(c => +c) : [];
const description = zoneEl.dataset.description;
const type = zoneEl.dataset.type;
const fill = zoneEl.getAttribute("fill");
const area = d3.sum(c.map(i => pack.cells.area[i])) * distanceScaleInput.value ** 2;
const rural = d3.sum(c.map(i => pack.cells.pop[i])) * populationRate;
const urban = d3.sum(c.map(i => pack.cells.burg[i]).map(b => pack.burgs[b].population)) * populationRate * urbanization;
const population = rural + urban;
const populationTip = `Total population: ${si(population)}; Rural population: ${si(rural)}; Urban population: ${si(urban)}. Click to change`;
const inactive = this.style.display === "none";
const focused = defs.select("#fog #focus" + this.id).size();
const inactive = zoneEl.style.display === "none";
const focused = defs.select("#fog #focus" + zoneEl.id).size();
lines += `<div class="states" data-id="${this.id}" data-fill="${fill}" data-description="${description}" data-cells=${c.length} data-area=${area} data-population=${population}>
<svg data-tip="Zone fill style. Click to change" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${fill}" class="fillRect pointer"></svg>
<input data-tip="Zone description. Click and type to change" class="religionName" value="${description}" autocorrect="off" spellcheck="false">
return `<div class="states" data-id="${zoneEl.id}" data-fill="${fill}" data-description="${description}"
data-type="${type}" data-cells=${c.length} data-area=${area} data-population=${population}>
<fill-box fill="${fill}"></fill-box>
<input data-tip="Zone description. Click and type to change" style="width: 11em" class="zoneName" value="${description}" autocorrect="off" spellcheck="false">
<input data-tip="Zone type. Click and type to change" class="zoneType" value="${type}">
<span data-tip="Cells count" class="icon-check-empty hide"></span>
<div data-tip="Cells count" class="stateCells hide">${c.length}</div>
<span data-tip="Zone area" style="padding-right:4px" class="icon-map-o hide"></span>
@ -95,13 +104,13 @@ function editZones() {
</div>`;
});
body.innerHTML = lines;
body.innerHTML = lines.join("");
// update footer
const totalArea = (zonesFooterArea.dataset.area = graphWidth * graphHeight * distanceScaleInput.value ** 2);
const totalPop = (d3.sum(pack.cells.pop) + d3.sum(pack.burgs.filter(b => !b.removed).map(b => b.population)) * urbanization) * populationRate;
zonesFooterPopulation.dataset.population = totalPop;
zonesFooterNumber.innerHTML = zones.selectAll("g").size();
zonesFooterNumber.innerHTML = `${filteredZones.length} of ${zones.length}`;
zonesFooterCells.innerHTML = pack.cells.i.length;
zonesFooterArea.innerHTML = si(totalArea) + unit;
zonesFooterPopulation.innerHTML = si(totalPop);
@ -127,6 +136,19 @@ function editZones() {
zones.select("#" + zone).style("outline", null);
}
function filterZonesByType() {
const typeToFilterBy = this.value;
const zones = Array.from(document.querySelectorAll("#zones > g"));
for (const zone of zones) {
const type = zone.dataset.type;
const visible = typeToFilterBy === "all" || type === typeToFilterBy;
zone.style.display = visible ? "block" : "none";
}
zonesEditorAddLines();
}
$(body).sortable({items: "div.states", handle: ".icon-resize-vertical", containment: "parent", axis: "y", update: movezone});
function movezone(ev, ui) {
const zone = $("#" + ui.item.attr("data-id"));
@ -142,7 +164,7 @@ function editZones() {
function enterZonesManualAssignent() {
if (!layerIsOn("toggleZones")) toggleZones();
customization = 10;
document.querySelectorAll("#zonesBottom > button").forEach(el => (el.style.display = "none"));
document.querySelectorAll("#zonesBottom > *").forEach(el => (el.style.display = "none"));
document.getElementById("zonesManuallyButtons").style.display = "inline-block";
zonesEditor.querySelectorAll(".hide").forEach(el => el.classList.add("hidden"));
@ -256,7 +278,7 @@ function editZones() {
function exitZonesManualAssignment(close) {
customization = 0;
removeCircle();
document.querySelectorAll("#zonesBottom > button").forEach(el => (el.style.display = "inline-block"));
document.querySelectorAll("#zonesBottom > *").forEach(el => (el.style.display = "inline-block"));
document.getElementById("zonesManuallyButtons").style.display = "none";
zonesEditor.querySelectorAll(".hide:not(.show)").forEach(el => el.classList.remove("hidden"));
@ -275,9 +297,9 @@ function editZones() {
function changeFill(el) {
const fill = el.getAttribute("fill");
const callback = function (fill) {
el.setAttribute("fill", fill);
document.getElementById(el.parentNode.parentNode.dataset.id).setAttribute("fill", fill);
const callback = newFill => {
el.fill = newFill;
document.getElementById(el.parentNode.dataset.id).setAttribute("fill", newFill);
};
openPicker(fill, callback);
@ -344,37 +366,22 @@ function editZones() {
function addZonesLayer() {
const id = getNextId("zone");
const description = "Unknown zone";
const fill = "url(#hatch" + (id.slice(4) % 14) + ")";
zones.append("g").attr("id", id).attr("data-description", description).attr("data-cells", "").attr("fill", fill);
const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value;
const type = "Unknown";
const fill = "url(#hatch" + (id.slice(4) % 42) + ")";
zones.append("g").attr("id", id).attr("data-description", description).attr("data-type", type).attr("data-cells", "").attr("fill", fill);
const line = `<div class="states" data-id="${id}" data-fill="${fill}" data-description="${description}" data-cells=0 data-area=0 data-population=0>
<svg data-tip="Zone fill style. Click to change" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${fill}" class="fillRect pointer"></svg>
<input data-tip="Zone description. Click and type to change" class="religionName" value="${description}" autocorrect="off" spellcheck="false">
<span data-tip="Cells count" class="icon-check-empty hide"></span>
<div data-tip="Cells count" class="stateCells hide">0</div>
<span data-tip="Zone area" style="padding-right:4px" class="icon-map-o hide"></span>
<div data-tip="Zone area" class="biomeArea hide">0 ${unit}</div>
<span class="icon-male hide"></span>
<div class="culturePopulation hide">0</div>
<span data-tip="Drag to raise or lower the zone" class="icon-resize-vertical hide"></span>
<span data-tip="Toggle zone focus" class="icon-pin inactive hide placeholder"></span>
<span data-tip="Toggle zone visibility" class="icon-eye hide placeholder"></span>
<span data-tip="Remove zone" class="icon-trash-empty hide"></span>
</div>`;
body.insertAdjacentHTML("beforeend", line);
zonesFooterNumber.innerHTML = zones.selectAll("g").size();
zonesEditorAddLines();
}
function downloadZonesData() {
const unit = areaUnit.value === "square" ? distanceUnitInput.value + "2" : areaUnit.value;
let data = "Id,Fill,Description,Cells,Area " + unit + ",Population\n"; // headers
let data = "Id,Fill,Description,Type,Cells,Area " + unit + ",Population\n"; // headers
body.querySelectorAll(":scope > div").forEach(function (el) {
data += el.dataset.id + ",";
data += el.dataset.fill + ",";
data += el.dataset.description + ",";
data += el.dataset.type + ",";
data += el.dataset.cells + ",";
data += el.dataset.area + ",";
data += el.dataset.population + "\n";

View file

@ -1,3 +0,0 @@
start chrome.exe http://localhost:8000/
@echo off
php -S localhost:8000

389
styles/ancient.json Normal file
View file

@ -0,0 +1,389 @@
{
"#map": {
"background-color": "#000000",
"filter": null,
"data-filter": null
},
"#armies": {
"font-size": 8,
"box-size": 4,
"stroke": "#000",
"stroke-width": 0.2,
"fill-opacity": 1,
"filter": null
},
"#biomes": {
"opacity": null,
"filter": null,
"mask": "url(#land)"
},
"#stateBorders": {
"opacity": 0.8,
"stroke": "#56566d",
"stroke-width": 1,
"stroke-dasharray": 2,
"stroke-linecap": "butt",
"filter": null
},
"#provinceBorders": {
"opacity": 0.8,
"stroke": "#56566d",
"stroke-width": 0.2,
"stroke-dasharray": 1,
"stroke-linecap": "butt",
"filter": null
},
"#cells": {
"opacity": null,
"stroke": "#808080",
"stroke-width": 0.1,
"filter": null,
"mask": null
},
"#gridOverlay": {
"opacity": 0.8,
"scale": 1,
"dx": 0,
"dy": 0,
"type": "pointyHex",
"stroke": "#808080",
"stroke-width": 0.5,
"stroke-dasharray": null,
"stroke-linecap": null,
"transform": null,
"filter": null,
"mask": null
},
"#coordinates": {
"opacity": 1,
"data-size": 12,
"font-size": 12,
"stroke": "#d4d4d4",
"stroke-width": 1,
"stroke-dasharray": 5,
"stroke-linecap": null,
"filter": null,
"mask": null
},
"#compass": {
"opacity": 0.5,
"transform": null,
"filter": "url(#filter-sepia)",
"mask": "url(#water)",
"shape-rendering": "optimizespeed"
},
"#rose": {
"transform": "translate(80 80) scale(.25)"
},
"#relig": {
"opacity": 0.7,
"stroke": "#404040",
"stroke-width": 0.7,
"filter": null
},
"#cults": {
"opacity": 0.6,
"stroke": "#777777",
"stroke-width": 0.5,
"stroke-dasharray": null,
"stroke-linecap": null,
"filter": null
},
"#landmass": {
"opacity": 1,
"fill": "#e3dfce",
"filter": null
},
"#markers": {
"opacity": null,
"rescale": 1,
"filter": ""
},
"#prec": {
"opacity": null,
"stroke": "#000000",
"stroke-width": 0.1,
"fill": "#003dff",
"filter": null
},
"#population": {
"opacity": null,
"stroke-width": 1.6,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"filter": null
},
"#rural": {
"stroke": "#0000ff"
},
"#urban": {
"stroke": "#ff0000"
},
"#freshwater": {
"opacity": 0.6,
"fill": "#c8d6e0",
"stroke": "#968d6e",
"stroke-width": 0.7,
"filter": null
},
"#salt": {
"opacity": 0.5,
"fill": "#339482",
"stroke": "#836a34",
"stroke-width": 0.7,
"filter": null
},
"#sinkhole": {
"opacity": 1,
"fill": "#c3d6df",
"stroke": "#b29062",
"stroke-width": 0.7,
"filter": null
},
"#frozen": {
"opacity": 0.95,
"fill": "#cdd4e7",
"stroke": "#cfe0eb",
"stroke-width": 0,
"filter": null
},
"#lava": {
"opacity": 0.7,
"fill": "#a04e18",
"stroke": "#835520",
"stroke-width": 2,
"filter": "url(#paper)"
},
"#dry": {
"opacity": 0.7,
"fill": "#c6b795",
"stroke": "#8e816f",
"stroke-width": 0.7,
"filter": null
},
"#sea_island": {
"opacity": 0.5,
"stroke": "#1f3846",
"stroke-width": 0.7,
"filter": "url(#dropShadow)",
"auto-filter": 1
},
"#lake_island": {
"opacity": 1,
"stroke": "#7c8eaf",
"stroke-width": 0.35,
"filter": null
},
"#terrain": {
"opacity": 1,
"set": "simple",
"size": 1,
"density": 0.4,
"filter": null,
"mask": null
},
"#rivers": {
"opacity": null,
"filter": "",
"fill": "#a69b7d"
},
"#ruler": {
"opacity": null,
"filter": null
},
"#roads": {
"opacity": 0.7,
"stroke": "#8d502a",
"stroke-width": 1,
"stroke-dasharray": 3,
"stroke-linecap": "inherit",
"filter": "",
"mask": null
},
"#trails": {
"opacity": 0.7,
"stroke": "#924217",
"stroke-width": 0.5,
"stroke-dasharray": "1 2",
"stroke-linecap": "butt",
"filter": null,
"mask": null
},
"#searoutes": {
"opacity": 0.8,
"stroke": "#b16925",
"stroke-width": 0.8,
"stroke-dasharray": "1 2",
"stroke-linecap": "round",
"filter": null,
"mask": null
},
"#statesBody": {
"opacity": 0.2,
"filter": "url(#filter-sepia)"
},
"#statesHalo": {
"opacity": 0.4,
"data-width": 10,
"stroke-width": 10,
"filter": "blur(6px)"
},
"#provs": {
"opacity": 0.7,
"fill": "#000000",
"font-size": 10,
"font-family": "Georgia",
"filter": null
},
"#temperature": {
"opacity": null,
"font-size": "8px",
"fill": "#000000",
"fill-opacity": 0.3,
"stroke": null,
"stroke-width": 1.8,
"stroke-dasharray": null,
"stroke-linecap": null,
"filter": null
},
"#ice": {
"opacity": 0.35,
"fill": "#e8f0f6",
"stroke": "#e8f0f6",
"stroke-width": 3,
"filter": "url(#dropShadow05)"
},
"#emblems": {
"opacity": 0.8,
"stroke-width": 0.8,
"filter": "url(#dropShadow05)"
},
"#texture": {
"opacity": 0.6,
"filter": "",
"mask": ""
},
"#textureImage": {
"x": 0,
"y": 0
},
"#zones": {
"opacity": 0.6,
"stroke": "#333333",
"stroke-width": 0,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"filter": null,
"mask": null
},
"#oceanLayers": {
"filter": "",
"layers": "-6,-4,-2"
},
"#oceanBase": {
"fill": "#c99f64"
},
"#oceanicPattern": {
"href": "./images/kiwiroo.png",
"opacity": 0.4
},
"#terrs": {
"opacity": null,
"scheme": "bright",
"terracing": 0,
"skip": 2,
"relax": 1,
"curve": 0,
"filter": "url(#blur3)",
"mask": "url(#land)"
},
"#legend": {
"data-size": 13,
"font-size": 13,
"font-family": "Almendra SC",
"stroke": "#812929",
"stroke-width": 2.5,
"stroke-dasharray": "0 4 10 4",
"stroke-linecap": "round",
"data-x": 99,
"data-y": 93,
"data-columns": 8
},
"#burgLabels > #cities": {
"opacity": 1,
"fill": "#3e3e4b",
"text-shadow": "white 0px 0px 4px",
"data-size": 12,
"font-size": 12,
"font-family": "Great Vibes"
},
"#burgIcons > #cities": {
"opacity": 1,
"fill": "#fdfab9",
"fill-opacity": 0.7,
"size": 1,
"stroke": "#6f4e1f",
"stroke-width": 0.3,
"stroke-dasharray": ".3 .4",
"stroke-linecap": "butt"
},
"#anchors > #cities": {
"opacity": 1,
"fill": "#ffffff",
"size": 2,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > #towns": {
"opacity": 1,
"fill": "#3e3e4b",
"text-shadow": "white 0px 0px 4px",
"data-size": 5,
"font-size": 5,
"font-family": "Great Vibes"
},
"#burgIcons > #towns": {
"opacity": 1,
"fill": "#fef4d8",
"fill-opacity": 0.7,
"size": 0.5,
"stroke": "#72472c",
"stroke-width": 0.12,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #towns": {
"opacity": 1,
"fill": "#ffffff",
"size": 1,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#labels > #states": {
"opacity": 1,
"fill": "#3e3e4b",
"stroke": "#3a3a3a",
"stroke-width": 0,
"text-shadow": "white 0px 0px 4px",
"data-size": 22,
"font-size": 22,
"font-family": "Great Vibes",
"filter": "url(#filter-sepia)"
},
"#labels > #addedLabels": {
"opacity": 1,
"fill": "#3e3e4b",
"stroke": "#3a3a3a",
"stroke-width": 0,
"text-shadow": "white 0px 0px 4px",
"data-size": 18,
"font-size": 18,
"font-family": "Times New Roman",
"filter": "url(#filter-sepia)"
},
"#fogging": {
"opacity": 0.98,
"fill": "#30426f",
"filter": null
}
}

385
styles/atlas.json Normal file
View file

@ -0,0 +1,385 @@
{
"#map": {
"background-color": "#000000",
"filter": null,
"data-filter": null
},
"#armies": {
"font-size": 6,
"box-size": 3,
"stroke": "#000",
"stroke-width": 0.3,
"fill-opacity": 1,
"filter": null
},
"#biomes": {
"opacity": null,
"filter": null,
"mask": "url(#land)"
},
"#stateBorders": {
"opacity": 1,
"stroke": "#000000",
"stroke-width": 1.01,
"stroke-dasharray": 0,
"stroke-linecap": "butt",
"filter": null
},
"#provinceBorders": {
"opacity": 0.8,
"stroke": "#000000",
"stroke-width": 0.69,
"stroke-dasharray": 0,
"stroke-linecap": "round",
"filter": null
},
"#cells": {
"opacity": null,
"stroke": "#808080",
"stroke-width": 0.1,
"filter": null,
"mask": null
},
"#gridOverlay": {
"opacity": 1,
"scale": 7.99,
"dx": -2,
"dy": 3,
"type": "square",
"stroke": "#000000",
"stroke-width": 0.05,
"stroke-dasharray": null,
"stroke-linecap": null,
"transform": null,
"filter": null,
"mask": null
},
"#coordinates": {
"opacity": 1,
"data-size": 12,
"font-size": 12,
"stroke": "#d4d4d4",
"stroke-width": 1,
"stroke-dasharray": 5,
"stroke-linecap": null,
"filter": null,
"mask": null
},
"#compass": {
"opacity": 0.8,
"transform": null,
"filter": null,
"mask": "url(#water)",
"shape-rendering": "optimizespeed"
},
"#rose": {
"transform": "translate(80 80) scale(.25)"
},
"#relig": {
"opacity": 0.7,
"stroke": "#777777",
"stroke-width": 0,
"filter": null
},
"#cults": {
"opacity": 0.6,
"stroke": "#777777",
"stroke-width": 0.5,
"stroke-dasharray": null,
"stroke-linecap": null,
"filter": null
},
"#landmass": {
"opacity": 1,
"fill": "#eef6fb",
"filter": null
},
"#markers": {
"opacity": null,
"rescale": 1,
"filter": "url(#dropShadow01)"
},
"#prec": {
"opacity": null,
"stroke": "#000000",
"stroke-width": 0.1,
"fill": "#003dff",
"filter": null
},
"#population": {
"opacity": null,
"stroke-width": 1.6,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"filter": null
},
"#rural": {
"stroke": "#0000ff"
},
"#urban": {
"stroke": "#ff0000"
},
"#freshwater": {
"opacity": 1,
"fill": "#cae3f7",
"stroke": "#0089ca",
"stroke-width": 1.01,
"filter": null
},
"#salt": {
"opacity": 1,
"fill": "#cae3f7",
"stroke": "#0089ca",
"stroke-width": 1.01,
"filter": null
},
"#sinkhole": {
"opacity": 1,
"fill": "#cae3f7",
"stroke": "#0089ca",
"stroke-width": 1.01,
"filter": null
},
"#frozen": {
"opacity": 0.95,
"fill": "#cdd4e7",
"stroke": "#cfe0eb",
"stroke-width": 0,
"filter": null
},
"#lava": {
"opacity": 0.7,
"fill": "#90270d",
"stroke": "#f93e0c",
"stroke-width": 2,
"filter": "url(#crumpled)"
},
"#dry": {
"opacity": 1,
"fill": "#c9bfa7",
"stroke": "#8e816f",
"stroke-width": 0.7,
"filter": null
},
"#sea_island": {
"opacity": 1,
"stroke": "#028ac9",
"stroke-width": 1.01,
"filter": "",
"auto-filter": 0
},
"#lake_island": {
"opacity": 1,
"stroke": "#7c8eaf",
"stroke-width": 0.35,
"filter": null
},
"#terrain": {
"opacity": null,
"set": "simple",
"size": 1,
"density": 0.4,
"filter": null,
"mask": null
},
"#rivers": {
"opacity": null,
"filter": null,
"fill": "#0089ca"
},
"#ruler": {
"opacity": null,
"filter": null
},
"#roads": {
"opacity": 1,
"stroke": "#ff2c2c",
"stroke-width": 1.05,
"stroke-dasharray": 0,
"stroke-linecap": "butt",
"filter": null,
"mask": null
},
"#trails": {
"opacity": 1,
"stroke": "#9f5122",
"stroke-width": 0.43,
"stroke-dasharray": 0,
"stroke-linecap": "butt",
"filter": null,
"mask": null
},
"#searoutes": {
"opacity": 1,
"stroke": "#0089ca",
"stroke-width": 0.45,
"stroke-dasharray": "1 2",
"stroke-linecap": "round",
"filter": null,
"mask": null
},
"#statesBody": {
"opacity": 0.49,
"filter": null
},
"#statesHalo": {
"opacity": 0.4,
"data-width": 10,
"stroke-width": 10,
"filter": "blur(5px)"
},
"#provs": {
"opacity": 1,
"fill": "#000000",
"font-size": 10,
"font-family": "Georgia",
"filter": null
},
"#temperature": {
"opacity": null,
"font-size": "8px",
"fill": "#000000",
"fill-opacity": 0.3,
"stroke": null,
"stroke-width": 1.8,
"stroke-dasharray": null,
"stroke-linecap": null,
"filter": null
},
"#ice": {
"opacity": 0.9,
"fill": "#e8f0f6",
"stroke": "#e8f0f6",
"stroke-width": 1,
"filter": "url(#dropShadow05)"
},
"#emblems": {
"opacity": 0.9,
"stroke-width": 1,
"filter": null
},
"#texture": {
"opacity": null,
"filter": null,
"mask": "url(#land)"
},
"#zones": {
"opacity": 0.6,
"stroke": "#333333",
"stroke-width": 0,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"filter": null,
"mask": null
},
"#oceanLayers": {
"filter": "",
"layers": "none"
},
"#oceanBase": {
"fill": "#b4d2f3"
},
"#oceanicPattern": {
"href": "",
"opacity": 1
},
"#terrs": {
"opacity": null,
"scheme": "bright",
"terracing": 0,
"skip": 0,
"relax": 0,
"curve": 0,
"filter": null,
"mask": "url(#land)"
},
"#legend": {
"data-size": 13,
"font-size": 13,
"font-family": "Almendra SC",
"stroke": "#812929",
"stroke-width": 2.5,
"stroke-dasharray": "0 4 10 4",
"stroke-linecap": "round",
"data-x": 99,
"data-y": 93,
"data-columns": 8
},
"#burgLabels > #cities": {
"opacity": 1,
"fill": "#000000",
"text-shadow": "white 0px 0px 4px",
"data-size": 5,
"font-size": 5,
"font-family": "Questrial"
},
"#burgIcons > #cities": {
"opacity": 1,
"fill": "#000000",
"fill-opacity": 0.7,
"size": 1,
"stroke": "#000000",
"stroke-width": 0.24,
"stroke-dasharray": "",
"stroke-linecap": "round"
},
"#anchors > #cities": {
"opacity": 1,
"fill": "#ffffff",
"size": 2,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > #towns": {
"opacity": 1,
"fill": "#000000",
"text-shadow": "white 0px 0px 4px",
"data-size": 4,
"font-size": 4,
"font-family": "Questrial"
},
"#burgIcons > #towns": {
"opacity": 1,
"fill": "#000000",
"fill-opacity": 0.7,
"size": 0.5,
"stroke": "#000000",
"stroke-width": 0.12,
"stroke-dasharray": "",
"stroke-linecap": "round"
},
"#anchors > #towns": {
"opacity": 1,
"fill": "#ffffff",
"size": 1,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#labels > #states": {
"opacity": 0,
"fill": "#000000",
"stroke": "#000000",
"stroke-width": 0,
"text-shadow": "white 0px 0px 4px",
"data-size": 21,
"font-size": 21,
"font-family": "Questrial",
"filter": null
},
"#labels > #addedLabels": {
"opacity": 1,
"fill": "#000000",
"stroke": "#000000",
"stroke-width": 0,
"text-shadow": "white 0px 0px 4px",
"data-size": 18,
"font-size": 18,
"font-family": "Questrial",
"filter": null
},
"#fogging": {
"opacity": 0.98,
"fill": "#30426f",
"filter": null
}
}

388
styles/clean.json Normal file
View file

@ -0,0 +1,388 @@
{
"#map": {
"background-color": "#000000",
"filter": null,
"data-filter": null
},
"#armies": {
"font-size": 6,
"box-size": 3,
"stroke": "#000",
"stroke-width": 0,
"opacity": 1,
"fill-opacity": 1,
"filter": null
},
"#biomes": {
"opacity": 0.5,
"filter": "url(#blur7)",
"mask": "url(#land)"
},
"#stateBorders": {
"opacity": 0.8,
"stroke": "#414141",
"stroke-width": 0.7,
"stroke-dasharray": 0,
"stroke-linecap": "butt",
"filter": null
},
"#provinceBorders": {
"opacity": 0.8,
"stroke": "#414141",
"stroke-width": 0.45,
"stroke-dasharray": 1,
"stroke-linecap": "butt",
"filter": null
},
"#cells": {
"opacity": null,
"stroke": "#808080",
"stroke-width": 0.09,
"filter": null,
"mask": "url(#land)"
},
"#gridOverlay": {
"opacity": 0.8,
"scale": 1,
"dx": 0,
"dy": "0",
"type": "pointyHex",
"stroke": "#808080",
"stroke-width": 0.5,
"stroke-dasharray": null,
"stroke-linecap": null,
"transform": null,
"filter": null,
"mask": null
},
"#coordinates": {
"opacity": 1,
"data-size": 12,
"font-size": 12,
"stroke": "#414141",
"stroke-width": 0.45,
"stroke-dasharray": 3,
"stroke-linecap": null,
"filter": null,
"mask": null
},
"#compass": {
"opacity": 0.8,
"transform": null,
"filter": null,
"mask": "url(#water)",
"shape-rendering": "optimizespeed"
},
"#rose": {
"transform": null
},
"#relig": {
"opacity": 0.7,
"stroke": "#404040",
"stroke-width": 0.7,
"filter": null
},
"#cults": {
"opacity": 0.6,
"stroke": "#777777",
"stroke-width": 0.5,
"stroke-dasharray": null,
"stroke-linecap": null,
"filter": null
},
"#landmass": {
"opacity": 1,
"fill": "#eeedeb",
"filter": null
},
"#markers": {
"opacity": null,
"rescale": null,
"filter": "url(#dropShadow01)"
},
"#prec": {
"opacity": null,
"stroke": "#000000",
"stroke-width": 0,
"fill": "#0080ff",
"filter": null
},
"#population": {
"opacity": null,
"stroke-width": 2.58,
"stroke-dasharray": 0,
"stroke-linecap": "butt",
"filter": "url(#blur3)"
},
"#rural": {
"stroke": "#ff0000"
},
"#urban": {
"stroke": "#800000"
},
"#freshwater": {
"opacity": 0.5,
"fill": "#aadaff",
"stroke": "#5f799d",
"stroke-width": 0,
"filter": null
},
"#salt": {
"opacity": 0.5,
"fill": "#409b8a",
"stroke": "#388985",
"stroke-width": 0.7,
"filter": null
},
"#sinkhole": {
"opacity": 1,
"fill": "#5bc9fd",
"stroke": "#53a3b0",
"stroke-width": 0.7,
"filter": null
},
"#frozen": {
"opacity": 0.95,
"fill": "#cdd4e7",
"stroke": "#cfe0eb",
"stroke-width": 0,
"filter": null
},
"#lava": {
"opacity": 0.7,
"fill": "#90270d",
"stroke": "#f93e0c",
"stroke-width": 2,
"filter": "url(#crumpled)"
},
"#dry": {
"opacity": 0.7,
"fill": "#c9bfa7",
"stroke": "#8e816f",
"stroke-width": 0.7,
"filter": null
},
"#sea_island": {
"opacity": 0.6,
"stroke": "#595959",
"stroke-width": 0.4,
"filter": null,
"auto-filter": 0
},
"#lake_island": {
"opacity": 0,
"stroke": "#7c8eaf",
"stroke-width": 0,
"filter": null
},
"#terrain": {
"opacity": 1,
"set": "simple",
"size": 1,
"density": 0.4,
"filter": null,
"mask": null
},
"#rivers": {
"opacity": null,
"filter": null,
"fill": "#aadaff"
},
"#ruler": {
"opacity": null,
"filter": null
},
"#roads": {
"opacity": 0.9,
"stroke": "#f6d068",
"stroke-width": 0.7,
"stroke-dasharray": 0,
"stroke-linecap": "inherit",
"filter": null,
"mask": null
},
"#trails": {
"opacity": 1,
"stroke": "#ffffff",
"stroke-width": 0.25,
"stroke-dasharray": "",
"stroke-linecap": "round",
"filter": null,
"mask": null
},
"#searoutes": {
"opacity": 0.8,
"stroke": "#4f82c6",
"stroke-width": 0.45,
"stroke-dasharray": 2,
"stroke-linecap": "butt",
"filter": null,
"mask": "url(#water)"
},
"#statesBody": {
"opacity": 0.3,
"filter": null
},
"#statesHalo": {
"opacity": 0.5,
"data-width": 1,
"stroke-width": 1,
"filter": null
},
"#provs": {
"opacity": 0.7,
"fill": "#000000",
"data-size": 10,
"font-size": 10,
"font-family": "Georgia",
"filter": null
},
"#temperature": {
"opacity": null,
"font-size": "8px",
"fill": "#000000",
"fill-opacity": 0.3,
"stroke": null,
"stroke-width": 1.8,
"stroke-dasharray": null,
"stroke-linecap": null,
"filter": null
},
"#ice": {
"opacity": 0.9,
"fill": "#e8f0f6",
"stroke": "#e8f0f6",
"stroke-width": 1,
"filter": "url(#dropShadow01)"
},
"#emblems": {
"opacity": 1,
"stroke-width": 1,
"filter": null
},
"#texture": {
"opacity": null,
"filter": null,
"mask": "url(#land)"
},
"#textureImage": {},
"#zones": {
"opacity": 0.7,
"stroke": "#ff6262",
"stroke-width": 0,
"stroke-dasharray": "",
"stroke-linecap": "butt",
"filter": null,
"mask": null
},
"#oceanLayers": {
"filter": null,
"layers": "none"
},
"#oceanBase": {
"fill": "#aadaff"
},
"#oceanicPattern": {
"href": "",
"opacity": 0.2
},
"#terrs": {
"opacity": 0.5,
"scheme": "bright",
"terracing": 0,
"skip": 5,
"relax": 0,
"curve": 0,
"filter": null,
"mask": "url(#land)"
},
"#legend": {
"data-size": 12.74,
"font-size": 12.74,
"font-family": "Arial",
"stroke": "#909090",
"stroke-width": 1.13,
"stroke-dasharray": 0,
"stroke-linecap": "round",
"data-x": 98.39,
"data-y": 12.67,
"data-columns": null
},
"#legendBox": {},
"#burgLabels > #cities": {
"opacity": 1,
"fill": "#414141",
"text-shadow": "white 0 0 4px",
"data-size": 7,
"font-size": 7,
"font-family": "Arial"
},
"#burgIcons > #cities": {
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 1,
"stroke": "#3e3e4b",
"stroke-width": 0.24,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #cities": {
"opacity": 1,
"fill": "#ffffff",
"size": 2,
"stroke": "#303030",
"stroke-width": 1.7
},
"#burgLabels > #towns": {
"opacity": 1,
"fill": "#414141",
"data-size": 3,
"font-size": 3,
"font-family": "Arial"
},
"#burgIcons > #towns": {
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 0.5,
"stroke": "#3e3e4b",
"stroke-width": 0.12,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #towns": {
"opacity": 1,
"fill": "#ffffff",
"size": 1,
"stroke": "#3e3e4b",
"stroke-width": 1.06
},
"#labels > #states": {
"opacity": 1,
"fill": "#292929",
"stroke": "#303030",
"stroke-width": 0,
"text-shadow": "white 0 0 2px",
"data-size": 10,
"font-size": 10,
"font-family": "Arial",
"filter": null
},
"#labels > #addedLabels": {
"opacity": 1,
"fill": "#414141",
"stroke": "#3a3a3a",
"stroke-width": 0,
"text-shadow": "white 0 0 4px",
"data-size": 18,
"font-size": 18,
"font-family": "Arial",
"filter": null
},
"#fogging": {
"opacity": 1,
"fill": "#ffffff",
"filter": null
}
}

385
styles/cyberpunk.json Normal file
View file

@ -0,0 +1,385 @@
{
"#map": {
"background-color": "#000000",
"filter": null,
"data-filter": null
},
"#armies": {
"font-size": 8,
"box-size": 4,
"stroke": "#000000",
"stroke-width": 0.6,
"fill-opacity": 1,
"filter": null
},
"#biomes": {
"opacity": 0.7,
"filter": "",
"mask": "url(#land)"
},
"#stateBorders": {
"opacity": 1,
"stroke": "#ffffff",
"stroke-width": 1,
"stroke-dasharray": 3,
"stroke-linecap": "round",
"filter": ""
},
"#provinceBorders": {
"opacity": 0.5,
"stroke": "#ffffff",
"stroke-width": 0.3,
"stroke-dasharray": 1,
"stroke-linecap": "round",
"filter": ""
},
"#cells": {
"opacity": null,
"stroke": "#808080",
"stroke-width": 0.1,
"filter": null,
"mask": null
},
"#gridOverlay": {
"opacity": 0.8,
"scale": 1,
"dx": 0,
"dy": 0,
"type": "pointyHex",
"stroke": "#808080",
"stroke-width": 0.5,
"stroke-dasharray": null,
"stroke-linecap": null,
"transform": null,
"filter": null,
"mask": null
},
"#coordinates": {
"opacity": 1,
"data-size": 14,
"font-size": 14,
"stroke": "#4a4a4a",
"stroke-width": 1,
"stroke-dasharray": 6,
"stroke-linecap": null,
"filter": null,
"mask": null
},
"#compass": {
"opacity": 0.9,
"transform": null,
"filter": null,
"mask": "",
"shape-rendering": "optimizespeed"
},
"#rose": {
"transform": null
},
"#relig": {
"opacity": 0.5,
"stroke": "#404040",
"stroke-width": 2,
"filter": "url(#splotch)"
},
"#cults": {
"opacity": 0.35,
"stroke": "#777777",
"stroke-width": 2,
"stroke-dasharray": null,
"stroke-linecap": null,
"filter": "url(#splotch)"
},
"#landmass": {
"opacity": 1,
"fill": "#04011e",
"filter": null
},
"#markers": {
"opacity": 0.8,
"rescale": 1,
"filter": "url(#dropShadow05)"
},
"#prec": {
"opacity": null,
"stroke": "#000000",
"stroke-width": 0.1,
"fill": "#003dff",
"filter": null
},
"#population": {
"opacity": null,
"stroke-width": 1.6,
"stroke-dasharray": null,
"stroke-linecap": "square",
"filter": null
},
"#rural": {
"stroke": "#5294ff"
},
"#urban": {
"stroke": "#5cdeff"
},
"#freshwater": {
"opacity": 0.9,
"fill": "#381579",
"stroke": "#47228c",
"stroke-width": 3,
"filter": null
},
"#salt": {
"opacity": 0.5,
"fill": "#409b8a",
"stroke": "#388985",
"stroke-width": 0.7,
"filter": null
},
"#sinkhole": {
"opacity": 1,
"fill": "#5bc9fd",
"stroke": "#53a3b0",
"stroke-width": 0.7,
"filter": null
},
"#frozen": {
"opacity": 0.95,
"fill": "#cdd4e7",
"stroke": "#cfe0eb",
"stroke-width": 0,
"filter": null
},
"#lava": {
"opacity": 0.7,
"fill": "#90270d",
"stroke": "#f93e0c",
"stroke-width": 2,
"filter": "url(#crumpled)"
},
"#dry": {
"opacity": 0.7,
"fill": "#c9bfa7",
"stroke": "#8e816f",
"stroke-width": 0.7,
"filter": null
},
"#sea_island": {
"opacity": 0.6,
"stroke": "#1f3846",
"stroke-width": 0.7,
"filter": "url(#dropShadow)",
"auto-filter": 1
},
"#lake_island": {
"opacity": 1,
"stroke": "#7c8eaf",
"stroke-width": 0.35,
"filter": null
},
"#terrain": {
"opacity": 0.9,
"set": "simple",
"size": 1,
"density": 0.4,
"filter": null,
"mask": null
},
"#rivers": {
"opacity": null,
"filter": null,
"fill": "#6738bc"
},
"#ruler": {
"opacity": null,
"filter": null
},
"#roads": {
"opacity": 1,
"stroke": "#c44ac0",
"stroke-width": 0.9,
"stroke-dasharray": "2 3",
"stroke-linecap": "round",
"filter": null,
"mask": null
},
"#trails": {
"opacity": 1,
"stroke": "#df2654",
"stroke-width": 0.2,
"stroke-dasharray": ".5 1",
"stroke-linecap": "round",
"filter": null,
"mask": null
},
"#searoutes": {
"opacity": 0.8,
"stroke": "#a890c6",
"stroke-width": 0.6,
"stroke-dasharray": "1.2 2.4",
"stroke-linecap": "round",
"filter": null,
"mask": null
},
"#statesBody": {
"opacity": 0,
"filter": null
},
"#statesHalo": {
"opacity": 1,
"data-width": 13,
"stroke-width": 13,
"filter": "blur(8.25px)"
},
"#provs": {
"opacity": 0.2,
"fill": "#933e3e",
"font-size": 8,
"font-family": "Orbitron",
"filter": ""
},
"#temperature": {
"opacity": 0.8,
"font-size": "22px",
"fill": "#551282",
"fill-opacity": 0.3,
"stroke": null,
"stroke-width": 3,
"stroke-dasharray": 2,
"stroke-linecap": null,
"filter": null
},
"#ice": {
"opacity": 0.3,
"fill": "#919191",
"stroke": "#949494",
"stroke-width": 0,
"filter": "url(#dropShadow05)"
},
"#emblems": {
"opacity": 0.75,
"stroke-width": 0.5,
"filter": ""
},
"#texture": {
"opacity": 0.14,
"filter": null,
"mask": "url(#water)"
},
"#zones": {
"opacity": 0.7,
"stroke": "#ffffff",
"stroke-width": 0.3,
"stroke-dasharray": null,
"stroke-linecap": "inherit",
"filter": "url(#dropShadow05)",
"mask": null
},
"#oceanLayers": {
"filter": "",
"layers": "-6,-3,-1"
},
"#oceanBase": {
"fill": "#05001f"
},
"#oceanicPattern": {
"href": "",
"opacity": 0.15
},
"#terrs": {
"opacity": 1,
"scheme": "monochrome",
"terracing": 6,
"skip": 0,
"relax": 2,
"curve": 0,
"filter": "",
"mask": "url(#land)"
},
"#legend": {
"data-size": 13,
"font-size": 13,
"font-family": "Almendra SC",
"stroke": "#812929",
"stroke-width": 2.5,
"stroke-dasharray": "0 4 10 4",
"stroke-linecap": "round",
"data-x": 99,
"data-y": 93,
"data-columns": 8
},
"#burgLabels > #cities": {
"opacity": 1,
"fill": "#ffffff",
"text-shadow": "white 0px 0px 4px",
"data-size": 8,
"font-size": 8,
"font-family": "Orbitron"
},
"#burgIcons > #cities": {
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 2,
"stroke": "#444444",
"stroke-width": 0.25,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #cities": {
"opacity": 1,
"fill": "#ffffff",
"size": 4,
"stroke": "#3e3e4b",
"stroke-width": 1
},
"#burgLabels > #towns": {
"opacity": 1,
"fill": "#ffffff",
"text-shadow": "white 0px 0px 4px",
"data-size": 3,
"font-size": 3,
"font-family": "Orbitron"
},
"#burgIcons > #towns": {
"opacity": 0.95,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 0.8,
"stroke": "#3e3e4b",
"stroke-width": 0.2,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #towns": {
"opacity": 1,
"fill": "#ffffff",
"size": 1.6,
"stroke": "#3e3e4b",
"stroke-width": 1
},
"#labels > #states": {
"opacity": 1,
"fill": "#ffffff",
"stroke": "#000000",
"stroke-width": 0,
"text-shadow": "white 0px 0px 4px",
"data-size": 18,
"font-size": 18,
"font-family": "Orbitron",
"filter": ""
},
"#labels > #addedLabels": {
"opacity": 1,
"fill": "#ffffff",
"stroke": "#000000",
"stroke-width": 0,
"text-shadow": "white 0px 0px 4px",
"data-size": 18,
"font-size": 18,
"font-family": "Almendra SC",
"filter": null
},
"#fogging": {
"opacity": 0.98,
"fill": "#1b1423",
"filter": null
}
}

385
styles/default.json Normal file
View file

@ -0,0 +1,385 @@
{
"#map": {
"background-color": "#000000",
"filter": null,
"data-filter": null
},
"#armies": {
"font-size": 6,
"box-size": 3,
"stroke": "#000",
"stroke-width": 0.3,
"fill-opacity": 1,
"filter": null
},
"#biomes": {
"opacity": null,
"filter": null,
"mask": "url(#land)"
},
"#stateBorders": {
"opacity": 0.8,
"stroke": "#56566d",
"stroke-width": 1,
"stroke-dasharray": 2,
"stroke-linecap": "butt",
"filter": null
},
"#provinceBorders": {
"opacity": 0.8,
"stroke": "#56566d",
"stroke-width": 0.5,
"stroke-dasharray": "0 2",
"stroke-linecap": "round",
"filter": null
},
"#cells": {
"opacity": null,
"stroke": "#808080",
"stroke-width": 0.1,
"filter": null,
"mask": null
},
"#gridOverlay": {
"opacity": 0.8,
"scale": 1,
"dx": 0,
"dy": 0,
"type": "pointyHex",
"stroke": "#777777",
"stroke-width": 0.5,
"stroke-dasharray": null,
"stroke-linecap": null,
"transform": null,
"filter": null,
"mask": null
},
"#coordinates": {
"opacity": 1,
"data-size": 12,
"font-size": 12,
"stroke": "#d4d4d4",
"stroke-width": 1,
"stroke-dasharray": 5,
"stroke-linecap": null,
"filter": null,
"mask": null
},
"#compass": {
"opacity": 0.8,
"transform": null,
"filter": null,
"mask": "url(#water)",
"shape-rendering": "optimizespeed"
},
"#rose": {
"transform": null
},
"#relig": {
"opacity": 0.7,
"stroke": "#777777",
"stroke-width": 0,
"filter": null
},
"#cults": {
"opacity": 0.6,
"stroke": "#777777",
"stroke-width": 0.5,
"stroke-dasharray": null,
"stroke-linecap": null,
"filter": null
},
"#landmass": {
"opacity": 1,
"fill": "#eef6fb",
"filter": null
},
"#markers": {
"opacity": null,
"rescale": 1,
"filter": "url(#dropShadow01)"
},
"#prec": {
"opacity": null,
"stroke": "#000000",
"stroke-width": 0,
"fill": "#003dff",
"filter": null
},
"#population": {
"opacity": null,
"stroke-width": 1.6,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"filter": null
},
"#rural": {
"stroke": "#0000ff"
},
"#urban": {
"stroke": "#ff0000"
},
"#freshwater": {
"opacity": 0.5,
"fill": "#a6c1fd",
"stroke": "#5f799d",
"stroke-width": 0.7,
"filter": null
},
"#salt": {
"opacity": 0.5,
"fill": "#409b8a",
"stroke": "#388985",
"stroke-width": 0.7,
"filter": null
},
"#sinkhole": {
"opacity": 1,
"fill": "#5bc9fd",
"stroke": "#53a3b0",
"stroke-width": 0.7,
"filter": null
},
"#frozen": {
"opacity": 0.95,
"fill": "#cdd4e7",
"stroke": "#cfe0eb",
"stroke-width": 0,
"filter": null
},
"#lava": {
"opacity": 0.7,
"fill": "#90270d",
"stroke": "#f93e0c",
"stroke-width": 2,
"filter": "url(#crumpled)"
},
"#dry": {
"opacity": 1,
"fill": "#c9bfa7",
"stroke": "#8e816f",
"stroke-width": 0.7,
"filter": null
},
"#sea_island": {
"opacity": 0.5,
"stroke": "#1f3846",
"stroke-width": 0.7,
"filter": "url(#dropShadow)",
"auto-filter": 1
},
"#lake_island": {
"opacity": 1,
"stroke": "#7c8eaf",
"stroke-width": 0.35,
"filter": null
},
"#terrain": {
"opacity": null,
"set": "simple",
"size": 1,
"density": 0.4,
"filter": null,
"mask": null
},
"#rivers": {
"opacity": null,
"filter": null,
"fill": "#5d97bb"
},
"#ruler": {
"opacity": null,
"filter": null
},
"#roads": {
"opacity": 0.9,
"stroke": "#d06324",
"stroke-width": 0.7,
"stroke-dasharray": 2,
"stroke-linecap": "butt",
"filter": null,
"mask": null
},
"#trails": {
"opacity": 0.9,
"stroke": "#d06324",
"stroke-width": 0.25,
"stroke-dasharray": ".8 1.6",
"stroke-linecap": "butt",
"filter": null,
"mask": null
},
"#searoutes": {
"opacity": 0.8,
"stroke": "#ffffff",
"stroke-width": 0.45,
"stroke-dasharray": "1 2",
"stroke-linecap": "round",
"filter": null,
"mask": null
},
"#statesBody": {
"opacity": 0.4,
"filter": null
},
"#statesHalo": {
"opacity": 0.4,
"data-width": 10,
"stroke-width": 10,
"filter": "blur(5px)"
},
"#provs": {
"opacity": 0.7,
"fill": "#000000",
"font-size": 10,
"font-family": "Georgia",
"filter": null
},
"#temperature": {
"opacity": null,
"font-size": "8px",
"fill": "#000000",
"fill-opacity": 0.3,
"stroke": null,
"stroke-width": 1.8,
"stroke-dasharray": null,
"stroke-linecap": null,
"filter": null
},
"#ice": {
"opacity": 0.9,
"fill": "#e8f0f6",
"stroke": "#e8f0f6",
"stroke-width": 1,
"filter": "url(#dropShadow05)"
},
"#emblems": {
"opacity": 0.9,
"stroke-width": 1,
"filter": null
},
"#texture": {
"opacity": null,
"filter": null,
"mask": "url(#land)"
},
"#zones": {
"opacity": 0.6,
"stroke": "#333333",
"stroke-width": 0,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"filter": null,
"mask": null
},
"#oceanLayers": {
"filter": null,
"layers": "-6,-3,-1"
},
"#oceanBase": {
"fill": "#466eab"
},
"#oceanicPattern": {
"href": "./images/pattern1.png",
"opacity": 0.2
},
"#terrs": {
"opacity": null,
"scheme": "bright",
"terracing": 0,
"skip": 5,
"relax": 0,
"curve": 0,
"filter": null,
"mask": "url(#land)"
},
"#legend": {
"data-size": 13,
"font-size": 13,
"font-family": "Almendra SC",
"stroke": "#812929",
"stroke-width": 2.5,
"stroke-dasharray": "0 4 10 4",
"stroke-linecap": "round",
"data-x": 99,
"data-y": 93,
"data-columns": 8
},
"#burgLabels > #cities": {
"opacity": 1,
"fill": "#3e3e4b",
"text-shadow": "white 0px 0px 4px",
"data-size": 7,
"font-size": 7,
"font-family": "Almendra SC"
},
"#burgIcons > #cities": {
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 1,
"stroke": "#3e3e4b",
"stroke-width": 0.24,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #cities": {
"opacity": 1,
"fill": "#ffffff",
"size": 2,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > #towns": {
"opacity": 1,
"fill": "#3e3e4b",
"text-shadow": "white 0px 0px 4px",
"data-size": 4,
"font-size": 4,
"font-family": "Almendra SC"
},
"#burgIcons > #towns": {
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 0.5,
"stroke": "#3e3e4b",
"stroke-width": 0.12,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #towns": {
"opacity": 1,
"fill": "#ffffff",
"size": 1,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#labels > #states": {
"opacity": 1,
"fill": "#3e3e4b",
"stroke": "#3a3a3a",
"stroke-width": 0,
"text-shadow": "white 0px 0px 4px",
"data-size": 22,
"font-size": 22,
"font-family": "Almendra SC",
"filter": null
},
"#labels > #addedLabels": {
"opacity": 1,
"fill": "#3e3e4b",
"stroke": "#3a3a3a",
"stroke-width": 0,
"text-shadow": "white 0px 0px 4px",
"data-size": 18,
"font-size": 18,
"font-family": "Almendra SC",
"filter": null
},
"#fogging": {
"opacity": 0.98,
"fill": "#30426f",
"filter": null
}
}

391
styles/gloom.json Normal file
View file

@ -0,0 +1,391 @@
{
"#map": {
"background-color": "#000000",
"filter": null,
"data-filter": null
},
"#armies": {
"font-size": 6,
"box-size": 3,
"stroke": "#000",
"stroke-width": 0.3,
"opacity": 1,
"fill-opacity": 1,
"filter": null
},
"#biomes": {
"opacity": null,
"filter": "url(#blur5)",
"mask": "url(#land)"
},
"#stateBorders": {
"opacity": 1,
"stroke": "#56566d",
"stroke-width": 1,
"stroke-dasharray": 2,
"stroke-linecap": "butt",
"filter": null
},
"#provinceBorders": {
"opacity": 1,
"stroke": "#56566d",
"stroke-width": 0.3,
"stroke-dasharray": ".7 1",
"stroke-linecap": "butt",
"filter": null
},
"#cells": {
"opacity": null,
"stroke": "#808080",
"stroke-width": 0.1,
"filter": null,
"mask": null
},
"#gridOverlay": {
"opacity": 0.8,
"scale": 1,
"dx": 0,
"dy": "0",
"type": "pointyHex",
"stroke": "#808080",
"stroke-width": 0.5,
"stroke-dasharray": null,
"stroke-linecap": null,
"transform": null,
"filter": null,
"mask": null
},
"#coordinates": {
"opacity": 1,
"data-size": 14,
"font-size": 14,
"stroke": "#4a4a4a",
"stroke-width": 1,
"stroke-dasharray": 6,
"stroke-linecap": null,
"filter": null,
"mask": null
},
"#compass": {
"opacity": 0.6,
"transform": null,
"filter": null,
"mask": "url(#water)",
"shape-rendering": "optimizespeed"
},
"#rose": {
"transform": "translate(100 100) scale(0.3)"
},
"#relig": {
"opacity": 0.7,
"stroke": "#404040",
"stroke-width": 1,
"filter": null
},
"#cults": {
"opacity": 0.7,
"stroke": "#777777",
"stroke-width": 1.5,
"stroke-dasharray": null,
"stroke-linecap": null,
"filter": null
},
"#landmass": {
"opacity": 1,
"fill": "#e0e0e0",
"filter": null
},
"#markers": {
"opacity": 0.8,
"rescale": 1,
"filter": "url(#dropShadow05)"
},
"#prec": {
"opacity": null,
"stroke": "#000000",
"stroke-width": 0.1,
"fill": "#003dff",
"filter": null
},
"#population": {
"opacity": null,
"stroke-width": 1.6,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"filter": null
},
"#rural": {
"stroke": "#0000aa"
},
"#urban": {
"stroke": "#9d0000"
},
"#freshwater": {
"opacity": 0.5,
"fill": "#a6c1fd",
"stroke": "#5f799d",
"stroke-width": 0.7,
"filter": null
},
"#salt": {
"opacity": 0.5,
"fill": "#409b8a",
"stroke": "#388985",
"stroke-width": 0.7,
"filter": null
},
"#sinkhole": {
"opacity": 1,
"fill": "#5bc9fd",
"stroke": "#53a3b0",
"stroke-width": 0.7,
"filter": null
},
"#frozen": {
"opacity": 0.95,
"fill": "#cdd4e7",
"stroke": "#cfe0eb",
"stroke-width": 0,
"filter": null
},
"#lava": {
"opacity": 0.7,
"fill": "#90270d",
"stroke": "#f93e0c",
"stroke-width": 2,
"filter": "url(#crumpled)"
},
"#dry": {
"opacity": 0.7,
"fill": "#c9bfa7",
"stroke": "#8e816f",
"stroke-width": 0.7,
"filter": null
},
"#sea_island": {
"opacity": 0.6,
"stroke": "#1f3846",
"stroke-width": 0.7,
"filter": "url(#dropShadow)",
"auto-filter": 1
},
"#lake_island": {
"opacity": 1,
"stroke": "#7c8eaf",
"stroke-width": 0.35,
"filter": null
},
"#terrain": {
"opacity": 0.9,
"set": "simple",
"size": 1,
"density": 0.4,
"filter": null,
"mask": null
},
"#rivers": {
"opacity": null,
"filter": null,
"fill": "#779582"
},
"#ruler": {
"opacity": null,
"filter": null
},
"#roads": {
"opacity": 1,
"stroke": "#8b4418",
"stroke-width": 0.9,
"stroke-dasharray": "2 3",
"stroke-linecap": "round",
"filter": null,
"mask": null
},
"#trails": {
"opacity": 1,
"stroke": "#844017",
"stroke-width": 0.2,
"stroke-dasharray": ".5 1",
"stroke-linecap": "round",
"filter": null,
"mask": null
},
"#searoutes": {
"opacity": 0.8,
"stroke": "#5e1865",
"stroke-width": 0.6,
"stroke-dasharray": "1.2 2.4",
"stroke-linecap": "round",
"filter": null,
"mask": null
},
"#statesBody": {
"opacity": 0.4,
"filter": null
},
"#statesHalo": {
"opacity": 0.5,
"data-width": 12,
"stroke-width": 12,
"filter": "blur(10px)"
},
"#provs": {
"opacity": 0.7,
"fill": "#000000",
"data-size": 10,
"font-size": 10,
"font-family": "Georgia",
"filter": null
},
"#temperature": {
"opacity": 1,
"font-size": "11px",
"fill": "#62001b",
"fill-opacity": 0.3,
"stroke": null,
"stroke-width": 2,
"stroke-dasharray": 2,
"stroke-linecap": null,
"filter": null
},
"#ice": {
"opacity": 0.9,
"fill": "#e8f0f6",
"stroke": "#e8f0f6",
"stroke-width": 1,
"filter": "url(#dropShadow05)"
},
"#emblems": {
"opacity": 0.6,
"stroke-width": 0.5,
"filter": null
},
"#texture": {
"opacity": null,
"filter": null,
"mask": "url(#land)"
},
"#textureImage": {
"x": 0,
"y": 0
},
"#zones": {
"opacity": 0.5,
"stroke": "#333333",
"stroke-width": 0,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"filter": "url(#dropShadow01)",
"mask": null
},
"#oceanLayers": {
"filter": null,
"layers": "-6,-4,-2"
},
"#oceanBase": {
"fill": "#4e6964"
},
"#oceanicPattern": {
"href": "./images/pattern3.png",
"opacity": 0.2
},
"#terrs": {
"opacity": 1,
"scheme": "bright",
"terracing": 0,
"skip": 0,
"relax": 1,
"curve": 1,
"filter": "url(#filter-grayscale)",
"mask": "url(#land)"
},
"#legend": {
"data-size": 13,
"font-size": 13,
"font-family": "Almendra SC",
"stroke": "#812929",
"stroke-width": 2.5,
"stroke-dasharray": "0 4 10 4",
"stroke-linecap": "round",
"data-x": 99,
"data-y": 93,
"data-columns": 8
},
"#legendBox": {},
"#burgLabels > #cities": {
"opacity": 1,
"fill": "#3e3e4b",
"text-shadow": "white 0 0 4px",
"data-size": 7,
"font-size": 7,
"font-family": "Bitter"
},
"#burgIcons > #cities": {
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 2,
"stroke": "#444444",
"stroke-width": 0.25,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #cities": {
"opacity": 0.8,
"fill": "#ffffff",
"size": 4,
"stroke": "#3e3e4b",
"stroke-width": 1
},
"#burgLabels > #towns": {
"opacity": 1,
"fill": "#3e3e4b",
"data-size": 3,
"font-size": 3,
"font-family": "Bitter"
},
"#burgIcons > #towns": {
"opacity": 0.95,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 0.8,
"stroke": "#3e3e4b",
"stroke-width": 0.2,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #towns": {
"opacity": 1,
"fill": "#ffffff",
"size": 1.6,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#labels > #states": {
"opacity": 1,
"fill": "#4e4e4e",
"stroke": "#b5b5b5",
"stroke-width": 0,
"text-shadow": "white 0 0 4px",
"data-size": 22,
"font-size": 22,
"font-family": "Almendra SC",
"filter": null
},
"#labels > #addedLabels": {
"opacity": 1,
"fill": "#3e3e4b",
"stroke": "#3a3a3a",
"stroke-width": 0,
"text-shadow": "white 0 0 4px",
"data-size": 18,
"font-size": 18,
"font-family": "Almendra SC",
"filter": null
},
"#fogging": {
"opacity": 0.98,
"fill": "#1b1423",
"filter": null
}
}

385
styles/light.json Normal file
View file

@ -0,0 +1,385 @@
{
"#map": {
"background-color": "#000000",
"filter": null,
"data-filter": null
},
"#armies": {
"font-size": 8,
"box-size": 4,
"stroke": "#000",
"stroke-width": 0.02,
"fill-opacity": 0.8,
"filter": null
},
"#biomes": {
"opacity": 0.5,
"filter": null,
"mask": "url(#land)"
},
"#stateBorders": {
"opacity": 0.8,
"stroke": "#4c483e",
"stroke-width": 1,
"stroke-dasharray": 2,
"stroke-linecap": "square",
"filter": null
},
"#provinceBorders": {
"opacity": 0.8,
"stroke": "#56566d",
"stroke-width": 0.2,
"stroke-dasharray": 1,
"stroke-linecap": "butt",
"filter": null
},
"#cells": {
"opacity": null,
"stroke": "#808080",
"stroke-width": 0.1,
"filter": null,
"mask": null
},
"#gridOverlay": {
"opacity": 0.5,
"scale": 1,
"dx": 0,
"dy": 0,
"type": "pointyHex",
"stroke": "#808080",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": null,
"transform": null,
"filter": null,
"mask": null
},
"#coordinates": {
"opacity": 0.7,
"data-size": 15,
"font-size": 15,
"stroke": "#734d37",
"stroke-width": 1.5,
"stroke-dasharray": 5,
"stroke-linecap": "square",
"filter": null,
"mask": ""
},
"#compass": {
"opacity": 0.6,
"transform": null,
"filter": null,
"mask": "url(#water)",
"shape-rendering": "optimizespeed"
},
"#rose": {
"transform": null
},
"#relig": {
"opacity": 0.5,
"stroke": null,
"stroke-width": 0,
"filter": null
},
"#cults": {
"opacity": 0.5,
"stroke": "#777777",
"stroke-width": 0,
"stroke-dasharray": null,
"stroke-linecap": null,
"filter": null
},
"#landmass": {
"opacity": 1,
"fill": "#f9f2ea",
"filter": null
},
"#markers": {
"opacity": null,
"rescale": 1,
"filter": null
},
"#prec": {
"opacity": null,
"stroke": "#000000",
"stroke-width": 0.1,
"fill": "#2554ef",
"filter": null
},
"#population": {
"opacity": null,
"stroke-width": 1.6,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"filter": null
},
"#rural": {
"stroke": "#0000ff"
},
"#urban": {
"stroke": "#ff0000"
},
"#freshwater": {
"opacity": 1,
"fill": "#98cdc4",
"stroke": "#719892",
"stroke-width": 0.46,
"filter": "url(#dropShadow05)"
},
"#salt": {
"opacity": 0.5,
"fill": "#409b8a",
"stroke": "#388985",
"stroke-width": 0.7,
"filter": null
},
"#sinkhole": {
"opacity": 1,
"fill": "#5bc9fd",
"stroke": "#53a3b0",
"stroke-width": 0.7,
"filter": null
},
"#frozen": {
"opacity": 0.95,
"fill": "#cdd4e7",
"stroke": "#cfe0eb",
"stroke-width": 0,
"filter": null
},
"#lava": {
"opacity": 0.7,
"fill": "#90270d",
"stroke": "#f93e0c",
"stroke-width": 2,
"filter": "url(#crumpled)"
},
"#dry": {
"opacity": 1,
"fill": "#c9bfa7",
"stroke": "#8e816f",
"stroke-width": 0.7,
"filter": null
},
"#sea_island": {
"opacity": 1,
"stroke": "#5e5e5e",
"stroke-width": 0.4,
"filter": "url(#dropShadow)",
"auto-filter": 1
},
"#lake_island": {
"opacity": 1,
"stroke": "#7c8eaf",
"stroke-width": 0.35,
"filter": null
},
"#terrain": {
"opacity": 0.6,
"set": "colored",
"size": 1,
"density": 0.3,
"filter": null,
"mask": ""
},
"#rivers": {
"opacity": null,
"filter": null,
"fill": "#6d94ba"
},
"#ruler": {
"opacity": null,
"filter": null
},
"#roads": {
"opacity": 0.9,
"stroke": "#3c1d0b",
"stroke-width": 1.37,
"stroke-dasharray": 2,
"stroke-linecap": "inherit",
"filter": null,
"mask": null
},
"#trails": {
"opacity": 0.9,
"stroke": "#95481a",
"stroke-width": 0.88,
"stroke-dasharray": ".8 1.6",
"stroke-linecap": "butt",
"filter": null,
"mask": null
},
"#searoutes": {
"opacity": 0.8,
"stroke": "#ffffff",
"stroke-width": 0.45,
"stroke-dasharray": "1 2",
"stroke-linecap": "round",
"filter": null,
"mask": null
},
"#statesBody": {
"opacity": 0.2,
"filter": null
},
"#statesHalo": {
"opacity": 0.3,
"data-width": 25,
"stroke-width": 25,
"filter": "blur(5px)"
},
"#provs": {
"opacity": 0.4,
"fill": "#000000",
"font-size": 5,
"font-family": "IM Fell English",
"filter": null
},
"#temperature": {
"opacity": null,
"font-size": "8px",
"fill": "#000000",
"fill-opacity": 0.3,
"stroke": null,
"stroke-width": 1.8,
"stroke-dasharray": null,
"stroke-linecap": null,
"filter": null
},
"#ice": {
"opacity": 0.5,
"fill": "#e8f0f6",
"stroke": "#e8f0f6",
"stroke-width": 1.5,
"filter": "url(#dropShadow05)"
},
"#emblems": {
"opacity": 0.9,
"stroke-width": 1,
"filter": null
},
"#texture": {
"opacity": 0.39,
"filter": null,
"mask": ""
},
"#zones": {
"opacity": 0.6,
"stroke": "#333333",
"stroke-width": 0,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"filter": null,
"mask": null
},
"#oceanLayers": {
"filter": "url(#dropShadow05)",
"layers": "-6,-3,-1"
},
"#oceanBase": {
"fill": "#8dc1c8"
},
"#oceanicPattern": {
"href": "./images/pattern1.png",
"opacity": 0.2
},
"#terrs": {
"opacity": 0.4,
"scheme": "light",
"terracing": 10,
"skip": 5,
"relax": 0,
"curve": 0,
"filter": "url(#turbulence)",
"mask": "url(#land)"
},
"#legend": {
"data-size": 13,
"font-size": 13,
"font-family": "Almendra SC",
"stroke": "#812929",
"stroke-width": 2.5,
"stroke-dasharray": "0 4 10 4",
"stroke-linecap": "round",
"data-x": 54.73,
"data-y": 62.98,
"data-columns": 8
},
"#burgLabels > #cities": {
"opacity": 1,
"fill": "#3a3a3a",
"text-shadow": "white 0px 0px 4px",
"data-size": 8,
"font-size": 8,
"font-family": "IM Fell English"
},
"#burgIcons > #cities": {
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 3,
"stroke": "#3e3e4b",
"stroke-width": 0.4,
"stroke-dasharray": "0.5 0.25",
"stroke-linecap": "butt"
},
"#anchors > #cities": {
"opacity": 1,
"fill": "#ffffff",
"size": 5.5,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > #towns": {
"opacity": 1,
"fill": "#3e3e4b",
"text-shadow": "white 0px 0px 4px",
"data-size": 4,
"font-size": 4,
"font-family": "IM Fell English"
},
"#burgIcons > #towns": {
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 1.2,
"stroke": "#3e3e4b",
"stroke-width": 0.2,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #towns": {
"opacity": 1,
"fill": "#ffffff",
"size": 2.2,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#labels > #states": {
"opacity": 1,
"fill": "#3e3e3e",
"stroke": "#000000",
"stroke-width": 0.3,
"text-shadow": "white 0px 0px 6px",
"data-size": 14,
"font-size": 14,
"font-family": "IM Fell English",
"filter": null
},
"#labels > #addedLabels": {
"opacity": 1,
"fill": "#f24706",
"stroke": "#701b05",
"stroke-width": 0.1,
"text-shadow": "white 0px 0px 4px",
"data-size": 6,
"font-size": 6,
"font-family": "IM Fell English",
"filter": null
},
"#fogging": {
"opacity": 1,
"fill": "#30426f",
"filter": null
}
}

381
styles/monochrome.json Normal file
View file

@ -0,0 +1,381 @@
{
"#map": {
"background-color": "#000000",
"filter": "url(#filter-grayscale)",
"data-filter": "grayscale"
},
"#armies": {
"font-size": 6,
"box-size": 3,
"stroke": "#000",
"stroke-width": 0.3,
"opacity": 1,
"fill-opacity": 1,
"filter": null
},
"#biomes": {
"opacity": null,
"filter": "url(#blur5)",
"mask": "url(#land)"
},
"#stateBorders": {
"opacity": 1,
"stroke": "#56566d",
"stroke-width": 1,
"stroke-dasharray": 2,
"stroke-linecap": "butt",
"filter": null
},
"#provinceBorders": {
"opacity": 1,
"stroke": "#56566d",
"stroke-width": 0.4,
"stroke-dasharray": 1,
"stroke-linecap": "butt",
"filter": null
},
"#cells": {
"opacity": null,
"stroke": "#808080",
"stroke-width": 0.1,
"filter": null,
"mask": null
},
"#gridOverlay": {
"opacity": 0.8,
"scale": 1,
"dx": 0,
"dy": "0",
"type": "pointyHex",
"stroke": "#808080",
"stroke-width": 0.5,
"stroke-dasharray": null,
"stroke-linecap": null,
"transform": null,
"filter": null,
"mask": null
},
"#coordinates": {
"opacity": 1,
"data-size": 12,
"font-size": 12,
"stroke": "#d4d4d4",
"stroke-width": 1,
"stroke-dasharray": 5,
"stroke-linecap": null,
"filter": null,
"mask": null
},
"#compass": {
"opacity": 0.8,
"transform": null,
"filter": null,
"mask": "url(#water)",
"shape-rendering": "optimizespeed"
},
"#rose": {
"transform": null
},
"#relig": {
"opacity": 0.7,
"stroke": "#404040",
"stroke-width": 0.7,
"filter": null
},
"#cults": {
"opacity": 0.6,
"stroke": "#777777",
"stroke-width": 0.5,
"stroke-dasharray": null,
"stroke-linecap": null,
"filter": null
},
"#landmass": {
"opacity": 1,
"fill": "#000000",
"filter": null
},
"#markers": {
"opacity": null,
"rescale": 1,
"filter": "url(#dropShadow01)"
},
"#prec": {
"opacity": null,
"stroke": "#000000",
"stroke-width": 0.1,
"fill": "#003dff",
"filter": null
},
"#population": {
"opacity": null,
"stroke-width": 1.6,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"filter": null
},
"#rural": {
"stroke": "#0000ff"
},
"#urban": {
"stroke": "#ff0000"
},
"#freshwater": {
"opacity": 1,
"fill": "#000000",
"stroke": "#515151",
"stroke-width": 0,
"filter": null
},
"#salt": {
"opacity": 1,
"fill": "#000000",
"stroke": "#484848",
"stroke-width": 0,
"filter": null
},
"#sinkhole": {
"opacity": 1,
"fill": "#000000",
"stroke": "#5f5f5f",
"stroke-width": 0.5,
"filter": null
},
"#frozen": {
"opacity": 1,
"fill": "#000000",
"stroke": "#6f6f6f",
"stroke-width": 0,
"filter": null
},
"#lava": {
"opacity": 1,
"fill": "#000000",
"stroke": "#5d5d5d",
"stroke-width": 0,
"filter": null
},
"#sea_island": {
"opacity": 1,
"stroke": "#1f3846",
"stroke-width": 0,
"filter": null,
"auto-filter": 0
},
"#lake_island": {
"opacity": 0,
"stroke": "#7c8eaf",
"stroke-width": 0,
"filter": null
},
"#terrain": {
"opacity": null,
"set": "simple",
"size": 1,
"density": 0.4,
"filter": null,
"mask": null
},
"#rivers": {
"opacity": 0.2,
"filter": "url(#blur1)",
"fill": "#000000"
},
"#ruler": {
"opacity": null,
"filter": null
},
"#roads": {
"opacity": 0.9,
"stroke": "#d06324",
"stroke-width": 0.7,
"stroke-dasharray": 2,
"stroke-linecap": "butt",
"filter": null,
"mask": null
},
"#trails": {
"opacity": 0.9,
"stroke": "#d06324",
"stroke-width": 0.25,
"stroke-dasharray": ".8 1.6",
"stroke-linecap": "butt",
"filter": null,
"mask": null
},
"#searoutes": {
"opacity": 0.8,
"stroke": "#ffffff",
"stroke-width": 0.45,
"stroke-dasharray": "1 2",
"stroke-linecap": "round",
"filter": null,
"mask": null
},
"#statesBody": {
"opacity": 0.4,
"filter": null
},
"#statesHalo": {
"opacity": 0.4,
"data-width": 10,
"stroke-width": 10,
"filter": "blur(5px)"
},
"#provs": {
"opacity": 0.7,
"fill": "#000000",
"data-size": 10,
"font-size": 10,
"font-family": "Georgia",
"filter": null
},
"#temperature": {
"opacity": null,
"font-size": "8px",
"fill": "#000000",
"fill-opacity": 0.3,
"stroke": null,
"stroke-width": 1.8,
"stroke-dasharray": null,
"stroke-linecap": null,
"filter": null
},
"#ice": {
"opacity": 0.9,
"fill": "#e8f0f6",
"stroke": "#e8f0f6",
"stroke-width": 1,
"filter": "url(#dropShadow05)"
},
"#texture": {
"opacity": 1,
"filter": null,
"mask": "url(#land)"
},
"#emblems": {
"opacity": 0.5,
"stroke-width": 0.5,
"filter": null
},
"#textureImage": {},
"#zones": {
"opacity": 0.6,
"stroke": "#333333",
"stroke-width": 0,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"filter": null,
"mask": null
},
"#oceanLayers": {
"filter": null,
"layers": "none"
},
"#oceanBase": {
"fill": "#000000"
},
"#oceanicPattern": {
"href": "",
"opacity": 0.2
},
"#terrs": {
"opacity": 1,
"scheme": "monochrome",
"terracing": 0,
"skip": 5,
"relax": 0,
"curve": 0,
"filter": "url(#blur3)",
"mask": "url(#land)"
},
"#legend": {
"data-size": 13,
"font-size": 13,
"font-family": "Almendra SC",
"stroke": "#812929",
"stroke-width": 2.5,
"stroke-dasharray": "0 4 10 4",
"stroke-linecap": "round",
"data-x": 99,
"data-y": 93,
"data-columns": 8
},
"#legendBox": {},
"#burgLabels > #cities": {
"opacity": 1,
"fill": "#3e3e4b",
"text-shadow": "white 0 0 4px",
"data-size": 7,
"font-size": 7,
"font-family": "Almendra SC"
},
"#burgIcons > #cities": {
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 1,
"stroke": "#3e3e4b",
"stroke-width": 0.24,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #cities": {
"opacity": 1,
"fill": "#ffffff",
"size": 2,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > #towns": {
"opacity": 1,
"fill": "#3e3e4b",
"data-size": 4,
"font-size": 4,
"font-family": "Almendra SC"
},
"#burgIcons > #towns": {
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 0.5,
"stroke": "#3e3e4b",
"stroke-width": 0.12,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #towns": {
"opacity": 1,
"fill": "#ffffff",
"size": 1,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#labels > #states": {
"opacity": 1,
"fill": "#3e3e4b",
"stroke": "#3a3a3a",
"stroke-width": 0,
"text-shadow": "white 0 0 4px",
"data-size": 22,
"font-size": 22,
"font-family": "Almendra SC",
"filter": null
},
"#labels > #addedLabels": {
"opacity": 1,
"fill": "#3e3e4b",
"stroke": "#3a3a3a",
"stroke-width": 0,
"text-shadow": "white 0 0 4px",
"data-size": 18,
"font-size": 18,
"font-family": "Almendra SC",
"filter": null
},
"#fogging": {
"opacity": 0.98,
"fill": "#30426f",
"filter": null
}
}

385
styles/watercolor.json Normal file
View file

@ -0,0 +1,385 @@
{
"#map": {
"background-color": "#000000",
"filter": null,
"data-filter": null
},
"#armies": {
"font-size": 8,
"box-size": 4,
"stroke": "#000",
"stroke-width": 0.2,
"fill-opacity": 1,
"filter": null
},
"#biomes": {
"opacity": 0.6,
"filter": null,
"mask": "url(#land)"
},
"#stateBorders": {
"opacity": 0.6,
"stroke": "#56566d",
"stroke-width": 1,
"stroke-dasharray": 3,
"stroke-linecap": "butt",
"filter": null
},
"#provinceBorders": {
"opacity": 0.5,
"stroke": "#56566d",
"stroke-width": 0.5,
"stroke-dasharray": "0 2",
"stroke-linecap": "round",
"filter": null
},
"#cells": {
"opacity": null,
"stroke": "#808080",
"stroke-width": 0.1,
"filter": null,
"mask": null
},
"#gridOverlay": {
"opacity": 0.8,
"scale": 1,
"dx": 0,
"dy": 0,
"type": "pointyHex",
"stroke": "#777777",
"stroke-width": 0.5,
"stroke-dasharray": null,
"stroke-linecap": null,
"transform": null,
"filter": null,
"mask": null
},
"#coordinates": {
"opacity": 1,
"data-size": 12,
"font-size": 12,
"stroke": "#d4d4d4",
"stroke-width": 1,
"stroke-dasharray": 5,
"stroke-linecap": null,
"filter": null,
"mask": null
},
"#compass": {
"opacity": 0.8,
"transform": null,
"filter": null,
"mask": "url(#water)",
"shape-rendering": "optimizespeed"
},
"#rose": {
"transform": "translate(80 80) scale(.25)"
},
"#relig": {
"opacity": 0.7,
"stroke": "#777777",
"stroke-width": 0,
"filter": "url(#bluredSplotch)"
},
"#cults": {
"opacity": 0.6,
"stroke": "#777777",
"stroke-width": 0.5,
"stroke-dasharray": null,
"stroke-linecap": null,
"filter": "url(#splotch)"
},
"#landmass": {
"opacity": 1,
"fill": "#eef6fb",
"filter": null
},
"#markers": {
"opacity": null,
"rescale": 1,
"filter": "url(#dropShadow01)"
},
"#prec": {
"opacity": null,
"stroke": "#000000",
"stroke-width": 0.1,
"fill": "#003dff",
"filter": null
},
"#population": {
"opacity": null,
"stroke-width": 1.6,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"filter": null
},
"#rural": {
"stroke": "#0000ff"
},
"#urban": {
"stroke": "#ff0000"
},
"#freshwater": {
"opacity": 0.5,
"fill": "#a6c1fd",
"stroke": "#5f799d",
"stroke-width": 0.7,
"filter": null
},
"#salt": {
"opacity": 0.5,
"fill": "#409b8a",
"stroke": "#388985",
"stroke-width": 0.7,
"filter": null
},
"#sinkhole": {
"opacity": 1,
"fill": "#5bc9fd",
"stroke": "#53a3b0",
"stroke-width": 0.7,
"filter": null
},
"#frozen": {
"opacity": 0.95,
"fill": "#cdd4e7",
"stroke": "#cfe0eb",
"stroke-width": 0,
"filter": null
},
"#lava": {
"opacity": 0.7,
"fill": "#90270d",
"stroke": "#f93e0c",
"stroke-width": 2,
"filter": "url(#crumpled)"
},
"#dry": {
"opacity": 1,
"fill": "#c9bfa7",
"stroke": "#8e816f",
"stroke-width": 0.7,
"filter": null
},
"#sea_island": {
"opacity": 0.5,
"stroke": "#1f3846",
"stroke-width": 0.7,
"filter": "url(#dropShadow)",
"auto-filter": 1
},
"#lake_island": {
"opacity": 1,
"stroke": "#7c8eaf",
"stroke-width": 0.35,
"filter": null
},
"#terrain": {
"opacity": 1,
"set": "gray",
"size": 1,
"density": 0.4,
"filter": null,
"mask": null
},
"#rivers": {
"opacity": null,
"filter": null,
"fill": "#2e89c2"
},
"#ruler": {
"opacity": null,
"filter": null
},
"#roads": {
"opacity": 0.9,
"stroke": "#969696",
"stroke-width": 0.7,
"stroke-dasharray": "",
"stroke-linecap": "butt",
"filter": null,
"mask": null
},
"#trails": {
"opacity": 0.9,
"stroke": "#969696",
"stroke-width": 0.4,
"stroke-dasharray": "",
"stroke-linecap": "butt",
"filter": null,
"mask": null
},
"#searoutes": {
"opacity": 0.9,
"stroke": "#969696",
"stroke-width": 0.7,
"stroke-dasharray": "",
"stroke-linecap": "round",
"filter": null,
"mask": null
},
"#statesBody": {
"opacity": 0.05,
"filter": null
},
"#statesHalo": {
"opacity": 0.4,
"data-width": 8,
"stroke-width": 8,
"filter": "blur(2px)"
},
"#provs": {
"opacity": 0.7,
"fill": "#000000",
"font-size": 4,
"font-family": "Comfortaa",
"filter": null
},
"#temperature": {
"opacity": null,
"font-size": "8px",
"fill": "#000000",
"fill-opacity": 0.3,
"stroke": null,
"stroke-width": 1.8,
"stroke-dasharray": null,
"stroke-linecap": null,
"filter": null
},
"#ice": {
"opacity": 0.7,
"fill": "#dfe8ec",
"stroke": "#000000",
"stroke-width": 0,
"filter": "url(#dropShadow05)"
},
"#emblems": {
"opacity": 0.95,
"stroke-width": 1,
"filter": null
},
"#texture": {
"opacity": 0.2,
"filter": null,
"mask": "url(#land)"
},
"#zones": {
"opacity": 0.6,
"stroke": "#333333",
"stroke-width": 0,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"filter": null,
"mask": null
},
"#oceanLayers": {
"filter": null,
"layers": "-6,-4,-2"
},
"#oceanBase": {
"fill": "#2d788b"
},
"#oceanicPattern": {
"href": "./images/kiwiroo.png",
"opacity": 0.5
},
"#terrs": {
"opacity": 0.5,
"scheme": "light",
"terracing": 0,
"skip": 5,
"relax": 1,
"curve": 0,
"filter": null,
"mask": "url(#land)"
},
"#legend": {
"data-size": 13,
"font-size": 13,
"font-family": "Almendra SC",
"stroke": "#812929",
"stroke-width": 2.5,
"stroke-dasharray": "0 4 10 4",
"stroke-linecap": "round",
"data-x": 99,
"data-y": 93,
"data-columns": 8
},
"#burgLabels > #cities": {
"opacity": 1,
"fill": "#043449",
"text-shadow": "white 0px 0px 2px",
"data-size": 5,
"font-size": 5,
"font-family": "Comfortaa"
},
"#burgIcons > #cities": {
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 1,
"stroke": "#3e3e4b",
"stroke-width": 0.24,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #cities": {
"opacity": 1,
"fill": "#ffffff",
"size": 2,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > #towns": {
"opacity": 1,
"fill": "#3e3e4b",
"text-shadow": "white 0px 0px 4px",
"data-size": 3,
"font-size": 3,
"font-family": "Comfortaa"
},
"#burgIcons > #towns": {
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 0.5,
"stroke": "#3e3e4b",
"stroke-width": 0.12,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #towns": {
"opacity": 1,
"fill": "#ffffff",
"size": 1,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#labels > #states": {
"opacity": 1,
"fill": "#ffffff",
"stroke": "#000000",
"stroke-width": 0.15,
"text-shadow": "black 1px 1px 2px",
"data-size": 20,
"font-size": 20,
"font-family": "Gloria Hallelujah",
"filter": null
},
"#labels > #addedLabels": {
"opacity": 1,
"fill": "#3e3e4b",
"stroke": "#3a3a3a",
"stroke-width": 0,
"text-shadow": "white 0px 0px 4px",
"data-size": 18,
"font-size": 18,
"font-family": "Comfortaa",
"filter": null
},
"#fogging": {
"opacity": 0.97,
"fill": "#8398ce",
"filter": null
}
}

View file

@ -5,7 +5,7 @@
function convertTemperature(temp) {
switch (temperatureScale.value) {
case "°C":
return temp + "°C";
return rn(temp) + "°C";
case "°F":
return rn((temp * 9) / 5 + 32) + "°F";
case "K":
@ -21,7 +21,7 @@ function convertTemperature(temp) {
case "°Rø":
return rn((temp * 21) / 40 + 7.5) + "°Rø";
default:
return temp + "°C";
return rn(temp) + "°C";
}
}