mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 01:41:22 +01:00
v1.2
This commit is contained in:
parent
93c0c64cbd
commit
f6ec0251ec
11 changed files with 449 additions and 168 deletions
29
index.css
29
index.css
|
|
@ -584,6 +584,13 @@ button.active {
|
||||||
padding: 4px 0;
|
padding: 4px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#viewMode > button {
|
||||||
|
padding: .35em;
|
||||||
|
margin: .2em .3em;
|
||||||
|
float: left;
|
||||||
|
width: 30.7%;
|
||||||
|
}
|
||||||
|
|
||||||
fieldset {
|
fieldset {
|
||||||
border: 1px solid #5d4651;
|
border: 1px solid #5d4651;
|
||||||
}
|
}
|
||||||
|
|
@ -604,8 +611,9 @@ fieldset {
|
||||||
color: grey;
|
color: grey;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabcontent li:hover {
|
.tabcontent li:hover,
|
||||||
background-color: #a8879d;
|
.tabcontent button:hover {
|
||||||
|
box-shadow: 0 0 2px 2px #5d465117;
|
||||||
}
|
}
|
||||||
|
|
||||||
#optionsContainer span {
|
#optionsContainer span {
|
||||||
|
|
@ -1282,6 +1290,23 @@ div.states span.inactive:hover {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#diplomacySelect {
|
||||||
|
position: absolute;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border: 1px solid #1891ff;
|
||||||
|
width: 23%;
|
||||||
|
left: 70.5%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#diplomacySelect > div {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#diplomacySelect > div:hover {
|
||||||
|
background-color: #1891ff;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
#burgsFooterPopulation {
|
#burgsFooterPopulation {
|
||||||
border: 0;
|
border: 0;
|
||||||
width: 50px;
|
width: 50px;
|
||||||
|
|
|
||||||
49
index.html
49
index.html
|
|
@ -18,7 +18,7 @@
|
||||||
<link rel="canonical" href="https://azgaar.github.io/Fantasy-Map-Generator/">
|
<link rel="canonical" href="https://azgaar.github.io/Fantasy-Map-Generator/">
|
||||||
|
|
||||||
<style type="text/css">
|
<style type="text/css">
|
||||||
body {margin: 0; font-size: 11px;}
|
body {margin: 0; font-size: 11px; overflow: hidden;}
|
||||||
#map {position: absolute;}
|
#map {position: absolute;}
|
||||||
#initial {fill: none; stroke: black; pointer-events: none;}
|
#initial {fill: none; stroke: black; pointer-events: none;}
|
||||||
#init-rose {animation: 20s infinite spin; opacity: .7; transform-origin: center;}
|
#init-rose {animation: 20s infinite spin; opacity: .7; transform-origin: center;}
|
||||||
|
|
@ -34,11 +34,11 @@
|
||||||
#loading-text span:nth-child(3) {animation-delay: 2s;}
|
#loading-text span:nth-child(3) {animation-delay: 2s;}
|
||||||
@keyframes blink {0% {opacity: 0;} 20% {opacity: 1;} 100% {opacity: .1;}}
|
@keyframes blink {0% {opacity: 0;} 20% {opacity: 1;} 100% {opacity: .1;}}
|
||||||
</style>
|
</style>
|
||||||
<link rel="preload" href="index.css?version=1.1.09" as="style" onload="this.onload=null; this.rel='stylesheet'">
|
<link rel="preload" href="index.css?version=1.2" as="style">
|
||||||
<link rel="preload" href="icons.css?version=1.1.09" as="style" onload="this.onload=null; this.rel='stylesheet'">
|
<link rel="preload" href="icons.css?version=1.2" as="style">
|
||||||
<link rel="preload" href="libs/jquery-ui.css" as="style" onload="this.onload=null; this.rel='stylesheet'">
|
<link rel="preload" href="libs/jquery-ui.css" as="style">
|
||||||
<link rel="stylesheet" href="index.css?version=1.1.09">
|
<link rel="stylesheet" href="index.css?version=1.2">
|
||||||
<link rel="stylesheet" href="icons.css?version=1.1.09">
|
<link rel="stylesheet" href="icons.css?version=1.2">
|
||||||
<link rel="stylesheet" href="libs/jquery-ui.css">
|
<link rel="stylesheet" href="libs/jquery-ui.css">
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
@ -899,7 +899,7 @@
|
||||||
<div id="loading">
|
<div id="loading">
|
||||||
<div id="title_name">Azgaar's</div>
|
<div id="title_name">Azgaar's</div>
|
||||||
<div id="title">Fantasy Map Generator</div>
|
<div id="title">Fantasy Map Generator</div>
|
||||||
<div id="version">v. 1.1</div>
|
<div id="version">v. 1.2</div>
|
||||||
<p id="loading-text">LOADING<span>.</span><span>.</span><span>.</span></p>
|
<p id="loading-text">LOADING<span>.</span><span>.</span><span>.</span></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -931,6 +931,7 @@
|
||||||
<option value="provinces">Provinces map</option>
|
<option value="provinces">Provinces map</option>
|
||||||
<option value="biomes">Biomes map</option>
|
<option value="biomes">Biomes map</option>
|
||||||
<option value="heightmap">Heightmap</option>
|
<option value="heightmap">Heightmap</option>
|
||||||
|
<option value="physical">Physical map</option>
|
||||||
<option value="poi">Places of interest</option>
|
<option value="poi">Places of interest</option>
|
||||||
<option value="landmass">Pure landmass</option>
|
<option value="landmass">Pure landmass</option>
|
||||||
<option hidden value="custom">Custom (not saved)</option>
|
<option hidden value="custom">Custom (not saved)</option>
|
||||||
|
|
@ -965,6 +966,13 @@
|
||||||
<li id="toggleRulers" data-tip="Rulers: click to toggle, drag to move, click on label to delete. Ctrl + click to edit layer style. Shortcut: = (equal)" class="buttonoff" onclick="toggleRulers(event)">Rulers</li>
|
<li id="toggleRulers" data-tip="Rulers: click to toggle, drag to move, click on label to delete. Ctrl + click to edit layer style. Shortcut: = (equal)" class="buttonoff" onclick="toggleRulers(event)">Rulers</li>
|
||||||
<li id="toggleScaleBar" data-tip="Scale Bar: click to toggle, drag to move. Ctrl + click to edit style. Shortcut: - (minus)" onclick="toggleScaleBar(event)" class="solid">Scale Bar</li>
|
<li id="toggleScaleBar" data-tip="Scale Bar: click to toggle, drag to move. Ctrl + click to edit style. Shortcut: - (minus)" onclick="toggleScaleBar(event)" class="solid">Scale Bar</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
|
<div id="viewMode" data-tip="Set view node">
|
||||||
|
<p>View mode:</p>
|
||||||
|
<button data-tip="Standard view mode that allows to edit the map" id="viewStandard" class="pressed">Standard</button>
|
||||||
|
<button data-tip="Map presentation in 3D scene. Works best for heightmap. Cannot be used for editing" id="view3D">3D scene</button>
|
||||||
|
<button data-tip="Project map on globe as if it represents whole world. Cannot be used for editing" id="viewGlobe">Globe</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="styleContent" class="tabcontent">
|
<div id="styleContent" class="tabcontent">
|
||||||
|
|
@ -1745,9 +1753,9 @@
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr data-tip="Define relative size of a saved png image. Saving big images is slow and may cause a browser crash">
|
<tr data-tip="Define relative size of a saved png/jpeg image. Saving big images is slow and may cause a browser crash!">
|
||||||
<td></td>
|
<td></td>
|
||||||
<td>PNG resolution</td>
|
<td>PNG/JPEG size</td>
|
||||||
<td>
|
<td>
|
||||||
<input id="pngResolutionInput" data-stored="pngResolution" type="range" min=1 max=8 value=5>
|
<input id="pngResolutionInput" data-stored="pngResolution" type="range" min=1 max=8 value=5>
|
||||||
</td>
|
</td>
|
||||||
|
|
@ -1859,7 +1867,7 @@
|
||||||
<button data-tip="Open template editor" id="applyTemplate" style="display: none">Template Editor</button>
|
<button data-tip="Open template editor" id="applyTemplate" style="display: none">Template Editor</button>
|
||||||
<button data-tip="Open Image Converter" id="convertImage" style="display: none">Image Converter</button>
|
<button data-tip="Open Image Converter" id="convertImage" style="display: none">Image Converter</button>
|
||||||
<button data-tip="Render heightmap data as a small monochrome image" id="heightmapPreview">Heightmap Preview</button>
|
<button data-tip="Render heightmap data as a small monochrome image" id="heightmapPreview">Heightmap Preview</button>
|
||||||
<button data-tip="View heightmap data in 3D" id="heightmap3DView">3D</button>
|
<button data-tip="Preview heightmap in 3D scene" id="heightmap3DView">3D</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="customizeOptions">
|
<div id="customizeOptions">
|
||||||
|
|
@ -1920,7 +1928,8 @@
|
||||||
<div id="saveDropdown">
|
<div id="saveDropdown">
|
||||||
<div id="saveMap" data-tip="Download the map as fully functional .map file to your machine. Shortcut: Ctrl + M">.map</div>
|
<div id="saveMap" data-tip="Download the map as fully functional .map file to your machine. Shortcut: Ctrl + M">.map</div>
|
||||||
<div id="saveSVG" data-tip="Download the map as vector image (open in browser or Inkscape). Shortcut: Ctrl + S">.svg</div>
|
<div id="saveSVG" data-tip="Download the map as vector image (open in browser or Inkscape). Shortcut: Ctrl + S">.svg</div>
|
||||||
<div id="savePNG" data-tip="Download visible part of the map as .png image. Shortcut: Ctrl + P">.png</div>
|
<div id="savePNG" data-tip="Download visible part of the map as .png (lossless compressed) image. Shortcut: Ctrl + P">.png</div>
|
||||||
|
<div id="saveJPEG" data-tip="Download visible part of the map as .jpeg (lossy compressed) image. Shortcut: Ctrl + J">.jpeg</div>
|
||||||
<div id="saveGeo" data-tip="Download map data in GeoJSON format. Shortcut: Ctrl + G">.json</div>
|
<div id="saveGeo" data-tip="Download map data in GeoJSON format. Shortcut: Ctrl + G">.json</div>
|
||||||
<div id="quickSave" data-tip="Save map to browser storage. Shortcut: F6">storage</div>
|
<div id="quickSave" data-tip="Save map to browser storage. Shortcut: F6">storage</div>
|
||||||
<!-- <div id="saveDropbox" data-tip="Save fully functional .map file to Dropbox. Shortcut: Ctrl + B">Dropbox</div> -->
|
<!-- <div id="saveDropbox" data-tip="Save fully functional .map file to Dropbox. Shortcut: Ctrl + B">Dropbox</div> -->
|
||||||
|
|
@ -2608,8 +2617,6 @@
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="_3dpreviewEditor" class="dialog stable" style="display: none; padding: 0px"></div>
|
|
||||||
|
|
||||||
<div id="statesEditor" class="dialog stable" style="display: none">
|
<div id="statesEditor" class="dialog stable" style="display: none">
|
||||||
<div id="statesHeader" class="header">
|
<div id="statesHeader" class="header">
|
||||||
<div style="left:1.4em" data-tip="Click to sort by state name" class="sortable alphabetically" data-sortby="name">State </div>
|
<div style="left:1.4em" data-tip="Click to sort by state name" class="sortable alphabetically" data-sortby="name">State </div>
|
||||||
|
|
@ -2800,6 +2807,18 @@
|
||||||
<div style="left:12.4em" data-tip="Click to sort by diplomatical relations" class="sortable alphabetically" data-sortby="relations">Relations </div>
|
<div style="left:12.4em" data-tip="Click to sort by diplomatical relations" class="sortable alphabetically" data-sortby="relations">Relations </div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="diplomacySelect">
|
||||||
|
<div data-tip="Ally means states formed a defensive pact and will protect each other in case of third party aggression">Ally</div>
|
||||||
|
<div data-tip="State is friendly to anouther state when they share some common interests">Friendly</div>
|
||||||
|
<div data-tip="Neutral means states relations are neither positive nor negative">Neutral</div>
|
||||||
|
<div data-tip="Suspicion means shate has a cautious distrust of another state">Suspicion</div>
|
||||||
|
<div data-tip="Enemies are states at war with each other">Enemy</div>
|
||||||
|
<div data-tip="Relations are unknown if states do not have enought information about each other">Unknown</div>
|
||||||
|
<div data-tip="Rivalry is a state of competing for dominance in the region">Rival</div>
|
||||||
|
<div data-tip="Vassal is a state having obligation to its suzerain">Vassal</div>
|
||||||
|
<div data-tip="Suzerain is a state having some control over its vassals">Suzerain</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="diplomacyBodySection" class="table"></div>
|
<div id="diplomacyBodySection" class="table"></div>
|
||||||
|
|
||||||
<div id="diplomacyBottom" style="margin-top: .1em">
|
<div id="diplomacyBottom" style="margin-top: .1em">
|
||||||
|
|
@ -3219,6 +3238,8 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="preview3d" class="dialog stable" style="display: none; padding: 0px"></div>
|
||||||
|
|
||||||
<div id="alert" style="display: none" class="dialog">
|
<div id="alert" style="display: none" class="dialog">
|
||||||
<p id="alertMessage">Warning!</p>
|
<p id="alertMessage">Warning!</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -3296,7 +3317,7 @@
|
||||||
<script defer src="modules/ui/diplomacy-editor.js"></script>
|
<script defer src="modules/ui/diplomacy-editor.js"></script>
|
||||||
<script defer src="modules/ui/zones-editor.js"></script>
|
<script defer src="modules/ui/zones-editor.js"></script>
|
||||||
<script defer src="modules/ui/editors.js"></script>
|
<script defer src="modules/ui/editors.js"></script>
|
||||||
<script defer src="modules/ui/3dpreview.js"></script>
|
<script defer src="modules/ui/3d.js"></script>
|
||||||
<script defer src="libs/quantize.min.js"></script>
|
<script defer src="libs/quantize.min.js"></script>
|
||||||
<script defer src="libs/jquery.ui.touch-punch.min.js"></script>
|
<script defer src="libs/jquery.ui.touch-punch.min.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
17
main.js
17
main.js
|
|
@ -7,7 +7,7 @@
|
||||||
// See also https://github.com/Azgaar/Fantasy-Map-Generator/issues/153
|
// See also https://github.com/Azgaar/Fantasy-Map-Generator/issues/153
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
const version = "1.11"; // generator version
|
const version = "1.2"; // generator version
|
||||||
document.title += " v" + version;
|
document.title += " v" + version;
|
||||||
|
|
||||||
// if map version is not stored, clear localStorage and show a message
|
// if map version is not stored, clear localStorage and show a message
|
||||||
|
|
@ -315,7 +315,7 @@ function applyDefaultBiomesSystem() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function showWelcomeMessage() {
|
function showWelcomeMessage() {
|
||||||
const post = link("https://www.reddit.com/r/FantasyMapGenerator/comments/daf6g2/update_new_version_is_published_v_11", "Main changes:"); // announcement on Reddit
|
const post = link("https://www.reddit.com/r/FantasyMapGenerator/comments/daf6g2/update_new_version_is_published_v_12", "Main changes:"); // announcement on Reddit
|
||||||
const changelog = link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog", "previous version");
|
const changelog = link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog", "previous version");
|
||||||
const reddit = link("https://www.reddit.com/r/FantasyMapGenerator", "Reddit community");
|
const reddit = link("https://www.reddit.com/r/FantasyMapGenerator", "Reddit community");
|
||||||
const discord = link("https://discordapp.com/invite/X7E84HU", "Discord server");
|
const discord = link("https://discordapp.com/invite/X7E84HU", "Discord server");
|
||||||
|
|
@ -325,15 +325,8 @@ function showWelcomeMessage() {
|
||||||
This version is compatible with ${changelog}, loaded <i>.map</i> files will be auto-updated.
|
This version is compatible with ${changelog}, loaded <i>.map</i> files will be auto-updated.
|
||||||
|
|
||||||
<ul>${post}
|
<ul>${post}
|
||||||
<li>Lake Editor</li>
|
<li>3d scene</li>
|
||||||
<li>Coastline Editor</li>
|
<li>Globe view</li>
|
||||||
<li>New lake groups (types)</li>
|
|
||||||
<li>Culture presets</li>
|
|
||||||
<li>Provinces, states and burgs charts</li>
|
|
||||||
<li>Editable religions tree</li>
|
|
||||||
<li>Data export in geojson format</li>
|
|
||||||
<li>Map quick save and quick load</li>
|
|
||||||
<li>Map loading from URL</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>Join our ${reddit} and ${discord} to ask questions, share maps, discuss the Generator, report bugs and propose new features.</p>
|
<p>Join our ${reddit} and ${discord} to ask questions, share maps, discuss the Generator, report bugs and propose new features.</p>
|
||||||
|
|
@ -1686,6 +1679,8 @@ const regenerateMap = debounce(function() {
|
||||||
resetZoom(1000);
|
resetZoom(1000);
|
||||||
generate();
|
generate();
|
||||||
restoreLayers();
|
restoreLayers();
|
||||||
|
const canvas3d = document.getElementById("canvas3d");
|
||||||
|
if (canvas3d) update3dPreview(canvas3d);
|
||||||
if ($("#worldConfigurator").is(":visible")) editWorld();
|
if ($("#worldConfigurator").is(":visible")) editWorld();
|
||||||
}, 500);
|
}, 500);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ async function saveSVG() {
|
||||||
document.body.appendChild(link);
|
document.body.appendChild(link);
|
||||||
link.click();
|
link.click();
|
||||||
|
|
||||||
tip(`${link.download} is saved. Open "Downloads" screen (crtl + J) to check`, true, "warning", 5000);
|
tip(`${link.download} is saved. Open "Downloads" screen (crtl + J) to check`, true, "success", 5000);
|
||||||
console.timeEnd("saveSVG");
|
console.timeEnd("saveSVG");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -38,7 +38,7 @@ async function savePNG() {
|
||||||
window.setTimeout(function() {
|
window.setTimeout(function() {
|
||||||
canvas.remove();
|
canvas.remove();
|
||||||
window.URL.revokeObjectURL(link.href);
|
window.URL.revokeObjectURL(link.href);
|
||||||
tip(`${link.download} is saved. Open "Downloads" screen (crtl + J) to check`, true, "warning", 5000);
|
tip(`${link.download} is saved. Open "Downloads" screen (crtl + J) to check`, true, "success", 5000);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -46,6 +46,33 @@ async function savePNG() {
|
||||||
console.timeEnd("savePNG");
|
console.timeEnd("savePNG");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// download map as JPEG
|
||||||
|
async function saveJPEG() {
|
||||||
|
console.time("saveJPEG");
|
||||||
|
const url = await getMapURL("png");
|
||||||
|
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
canvas.width = svgWidth * pngResolutionInput.value;
|
||||||
|
canvas.height = svgHeight * pngResolutionInput.value;
|
||||||
|
const img = new Image();
|
||||||
|
img.src = url;
|
||||||
|
|
||||||
|
img.onload = async function() {
|
||||||
|
canvas.getContext("2d").drawImage(img, 0, 0, canvas.width, canvas.height);
|
||||||
|
const quality = Math.min(rn(1 - pngResolutionInput.value / 20, 2), .92);
|
||||||
|
const URL = await canvas.toDataURL("image/jpeg", quality);
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.download = getFileName() + ".jpeg";
|
||||||
|
link.href = URL;
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
tip(`${link.download} is saved. Open "Downloads" screen (CTRL + J) to check`, true, "success", 7000);
|
||||||
|
window.setTimeout(() => window.URL.revokeObjectURL(URL), 5000);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.timeEnd("saveJPEG");
|
||||||
|
}
|
||||||
|
|
||||||
// parse map svg to object url
|
// parse map svg to object url
|
||||||
async function getMapURL(type) {
|
async function getMapURL(type) {
|
||||||
const cloneEl = document.getElementById("map").cloneNode(true); // clone svg
|
const cloneEl = document.getElementById("map").cloneNode(true); // clone svg
|
||||||
|
|
@ -59,7 +86,7 @@ async function getMapURL(type) {
|
||||||
if (type === "mesh") clone.attr("width", graphWidth).attr("height", graphHeight);
|
if (type === "mesh") clone.attr("width", graphWidth).attr("height", graphHeight);
|
||||||
if (type !== "png") clone.select("#viewbox").attr("transform", null); // reset transform to show whole map
|
if (type !== "png") clone.select("#viewbox").attr("transform", null); // reset transform to show whole map
|
||||||
if (type === "svg") removeUnusedElements(clone);
|
if (type === "svg") removeUnusedElements(clone);
|
||||||
if (type === "mesh") updateMeshCells(clone);
|
if (customization && type === "mesh") updateMeshCells(clone);
|
||||||
inlineStyle(clone);
|
inlineStyle(clone);
|
||||||
|
|
||||||
const fontStyle = await GFontToDataURI(getFontsToLoad()); // load non-standard fonts
|
const fontStyle = await GFontToDataURI(getFontsToLoad()); // load non-standard fonts
|
||||||
|
|
@ -777,8 +804,8 @@ function parseLoadedData(data) {
|
||||||
if (!markers.selectAll("*").size()) {addMarkers(); turnButtonOn("toggleMarkers");}
|
if (!markers.selectAll("*").size()) {addMarkers(); turnButtonOn("toggleMarkers");}
|
||||||
|
|
||||||
// 1.0 add fogging layer (state focus)
|
// 1.0 add fogging layer (state focus)
|
||||||
let fogging = viewbox.insert("g", "#ruler").attr("id", "fogging-cont").attr("mask", "url(#fog)")
|
fogging = viewbox.insert("g", "#ruler").attr("id", "fogging-cont").attr("mask", "url(#fog)")
|
||||||
.append("g").attr("id", "fogging").attr("display", "none");
|
.append("g").attr("id", "fogging").style("display", "none");
|
||||||
fogging.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%");
|
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%")
|
defs.append("mask").attr("id", "fog").append("rect").attr("x", 0).attr("y", 0).attr("width", "100%")
|
||||||
.attr("height", "100%").attr("fill", "white");
|
.attr("height", "100%").attr("fill", "white");
|
||||||
|
|
@ -873,11 +900,14 @@ function parseLoadedData(data) {
|
||||||
|
|
||||||
// v 1.11 replaced "display" attribute by "display" style
|
// v 1.11 replaced "display" attribute by "display" style
|
||||||
viewbox.selectAll("g").each(function() {
|
viewbox.selectAll("g").each(function() {
|
||||||
if (this.hasAttribute("display")) this.removeAttribute("display");
|
if (this.hasAttribute("display")) {
|
||||||
fogging.style("display", "none");
|
this.removeAttribute("display");
|
||||||
prec.style("display", "none");
|
this.style.display = "none";
|
||||||
ruler.style("display", "none");
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// v 1.11 had an issue with fogging being displayed on load
|
||||||
|
unfog();
|
||||||
}
|
}
|
||||||
|
|
||||||
}()
|
}()
|
||||||
|
|
|
||||||
226
modules/ui/3d.js
Normal file
226
modules/ui/3d.js
Normal file
|
|
@ -0,0 +1,226 @@
|
||||||
|
"use strict";
|
||||||
|
let threeD = {}; // master object for 3d scane and parameters
|
||||||
|
let threeDscale = 50; // 3d scene scale
|
||||||
|
|
||||||
|
// start 3d view and heightmap edit preview
|
||||||
|
async function start3d(canvas) {
|
||||||
|
const loaded = await loadTHREE();
|
||||||
|
if (!loaded) {tip("Cannot load 3d library", false, "error", 4000); return false};
|
||||||
|
|
||||||
|
threeD.scene = new THREE.Scene();
|
||||||
|
//threeD.scene.background = new THREE.Color(0x53679f);
|
||||||
|
threeD.camera = new THREE.PerspectiveCamera(70, canvas.width / canvas.height, 0.1, 2000);
|
||||||
|
threeD.camera.position.x = 0;
|
||||||
|
threeD.camera.position.z = 350;
|
||||||
|
threeD.camera.position.y = 285;
|
||||||
|
threeD.Renderer = new THREE.WebGLRenderer({canvas, antialias: true, preserveDrawingBuffer: true});
|
||||||
|
threeD.controls = await OrbitControls(threeD.camera, threeD.Renderer.domElement);
|
||||||
|
threeD.controls.minDistance = 10; threeD.controls.maxDistance = 1000;
|
||||||
|
threeD.controls.maxPolarAngle = Math.PI/2;
|
||||||
|
threeD.controls.keys = {};
|
||||||
|
|
||||||
|
threeD.Renderer.setSize(canvas.width, canvas.height);
|
||||||
|
add3dMesh(graphWidth, graphHeight, grid.cellsX, grid.cellsY);
|
||||||
|
|
||||||
|
const ambientLight = new THREE.AmbientLight(0xcccccc, .7);
|
||||||
|
threeD.scene.add(ambientLight);
|
||||||
|
const spotLight = new THREE.SpotLight(0xcccccc, .8, 2000, .7, 0, 0);
|
||||||
|
spotLight.position.set(100, 600, 1000);
|
||||||
|
spotLight.castShadow = true;
|
||||||
|
threeD.scene.add(spotLight);
|
||||||
|
//threeD.scene.add(new THREE.SpotLightHelper(spotLight));
|
||||||
|
|
||||||
|
threeD.controls.addEventListener("change", render);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create a mesh from pixel data
|
||||||
|
async function add3dMesh(width, height, segmentsX, segmentsY) {
|
||||||
|
const geometry = new THREE.PlaneGeometry(width, height, segmentsX-1, segmentsY-1);
|
||||||
|
|
||||||
|
// generateTexture
|
||||||
|
//threeD.material = new THREE.MeshBasicMaterial();
|
||||||
|
//const texture = new THREE.CanvasTexture(generateTexture(grid.cells.h, grid.cellsX, grid.cellsY));
|
||||||
|
//threeD.material.map = texture;
|
||||||
|
|
||||||
|
const url = await getMapURL("mesh");
|
||||||
|
threeD.material = new THREE.MeshLambertMaterial();
|
||||||
|
const texture = new THREE.TextureLoader().load(url, render);
|
||||||
|
texture.needsUpdate = true;
|
||||||
|
threeD.material.map = texture;
|
||||||
|
|
||||||
|
geometry.vertices.forEach((v, i) => v.z = getMeshHeight(i));
|
||||||
|
geometry.computeVertexNormals(); // added
|
||||||
|
threeD.Renderer.shadowMap.enabled = true; // added
|
||||||
|
threeD.mesh = new THREE.Mesh(geometry, threeD.material);
|
||||||
|
threeD.mesh.rotation.x = -Math.PI / 2;
|
||||||
|
threeD.mesh.castShadow = true;
|
||||||
|
threeD.mesh.receiveShadow = true;
|
||||||
|
threeD.scene.add(threeD.mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getMeshHeight(i) {
|
||||||
|
const h = grid.cells.h[i];
|
||||||
|
return h < 20 ? 0 : (h - 18) / 82 * threeDscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateTexture(data, width, height) {
|
||||||
|
let context, image, imageData;
|
||||||
|
const vector3 = new THREE.Vector3(0, 0, 0);
|
||||||
|
const sun = new THREE.Vector3(1, 1, 1);
|
||||||
|
sun.normalize();
|
||||||
|
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
canvas.width = width;
|
||||||
|
canvas.height = height;
|
||||||
|
context = canvas.getContext('2d');
|
||||||
|
context.fillStyle = '#000';
|
||||||
|
context.fillRect(0, 0, width, height);
|
||||||
|
|
||||||
|
image = context.getImageData(0, 0, canvas.width, canvas.height);
|
||||||
|
imageData = image.data;
|
||||||
|
|
||||||
|
for (let i = 0, j = 0; i < imageData.length; i += 4, j ++) {
|
||||||
|
vector3.x = data[j - 2] - data[j + 2];
|
||||||
|
vector3.y = 2;
|
||||||
|
vector3.z = data[j - width * 2] - data[j + width * 2];
|
||||||
|
vector3.normalize();
|
||||||
|
|
||||||
|
const shade = vector3.dot(sun);
|
||||||
|
// initial: r 96 + shade * 128, g 32 + shade * 96, b shade * 96;
|
||||||
|
const clr = (shade * 255) * (.5 + data[j] * .007); // new: black and white
|
||||||
|
imageData[i] = imageData[i + 1] = imageData[i + 2] = clr;
|
||||||
|
}
|
||||||
|
context.putImageData(image, 0, 0);
|
||||||
|
|
||||||
|
const canvasScaled = document.createElement('canvas');
|
||||||
|
canvasScaled.width = width * 4;
|
||||||
|
canvasScaled.height = height * 4;
|
||||||
|
context = canvasScaled.getContext('2d');
|
||||||
|
context.scale(4, 4);
|
||||||
|
context.drawImage(canvas, 0, 0);
|
||||||
|
|
||||||
|
image = context.getImageData(0, 0, canvasScaled.width, canvasScaled.height);
|
||||||
|
imageData = image.data;
|
||||||
|
|
||||||
|
for (let i = 0; i < imageData.length; i += 4) {
|
||||||
|
const v = ~~(Math.random() * 5);
|
||||||
|
imageData[i] += v;
|
||||||
|
imageData[i + 1] += v;
|
||||||
|
imageData[i + 2] += v;
|
||||||
|
}
|
||||||
|
context.putImageData(image, 0, 0);
|
||||||
|
return canvasScaled;
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadTHREE() {
|
||||||
|
if (window.THREE) return Promise.resolve(true);
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.src = "libs/three.min.js"
|
||||||
|
document.head.append(script);
|
||||||
|
script.onload = () => resolve(true);
|
||||||
|
script.onerror = () => resolve(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function OrbitControls(camera, domElement) {
|
||||||
|
if (THREE.OrbitControls) new THREE.OrbitControls(camera, domElement);
|
||||||
|
|
||||||
|
return new Promise(resolve => {
|
||||||
|
const script = document.createElement('script');
|
||||||
|
script.src = "libs/orbitControls.min.js"
|
||||||
|
document.head.append(script);
|
||||||
|
script.onload = () => resolve(new THREE.OrbitControls(camera, domElement));
|
||||||
|
script.onerror = () => resolve(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function update3dPreview(canvas) {
|
||||||
|
threeD.scene.remove(threeD.mesh);
|
||||||
|
threeD.Renderer.setSize(canvas.width, canvas.height);
|
||||||
|
if (canvas.dataset.type === "viewGlobe") addGlobe3dMesh();
|
||||||
|
else add3dMesh(graphWidth, graphHeight, grid.cellsX, grid.cellsY);
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function update3d() {
|
||||||
|
const url = await getMapURL("mesh");
|
||||||
|
threeD.material.map = new THREE.TextureLoader().load(url, render);
|
||||||
|
}
|
||||||
|
|
||||||
|
function stop3d() {
|
||||||
|
if (!threeD.controls || !threeD.Renderer) return;
|
||||||
|
threeD.controls.dispose();
|
||||||
|
threeD.Renderer.dispose()
|
||||||
|
cancelAnimationFrame(threeD.animationFrame);
|
||||||
|
threeD = {};
|
||||||
|
}
|
||||||
|
|
||||||
|
async function startGlobe(canvas) {
|
||||||
|
const loaded = await loadTHREE();
|
||||||
|
if (!loaded) {tip("Cannot load 3d library", false, "error", 4000); return false};
|
||||||
|
|
||||||
|
threeD.scene = new THREE.Scene();
|
||||||
|
threeD.scene.background = new THREE.TextureLoader().load("https://i0.wp.com/azgaar.files.wordpress.com/2019/10/stars.png", render);
|
||||||
|
threeD.Renderer = new THREE.WebGLRenderer({canvas, antialias: true, preserveDrawingBuffer: true});
|
||||||
|
threeD.Renderer.setSize(canvas.width, canvas.height);
|
||||||
|
|
||||||
|
threeD.camera = new THREE.PerspectiveCamera(45, canvas.width / canvas.height, 0.1, 1000).translateZ(5);
|
||||||
|
threeD.controls = await OrbitControls(threeD.camera, threeD.Renderer.domElement);
|
||||||
|
threeD.controls.minDistance = 1.8; threeD.controls.maxDistance = 10;
|
||||||
|
threeD.controls.autoRotate = true;
|
||||||
|
threeD.controls.keys = {};
|
||||||
|
|
||||||
|
const ambientLight = new THREE.AmbientLight(0xcccccc, .9);
|
||||||
|
threeD.scene.add(ambientLight);
|
||||||
|
const spotLight = new THREE.SpotLight(0xcccccc, .6, 200, .7, .1, 0);
|
||||||
|
spotLight.position.set(700, 300, 200);
|
||||||
|
spotLight.castShadow = false;
|
||||||
|
threeD.scene.add(spotLight);
|
||||||
|
//threeD.scene.add(new THREE.SpotLightHelper(spotLight));
|
||||||
|
|
||||||
|
addGlobe3dMesh();
|
||||||
|
threeD.controls.addEventListener("change", render);
|
||||||
|
threeD.animationFrame = requestAnimationFrame(animate);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// create globe mesh just from svg
|
||||||
|
async function addGlobe3dMesh() {
|
||||||
|
threeD.material = new THREE.MeshLambertMaterial();
|
||||||
|
const url = await getMapURL("mesh");
|
||||||
|
threeD.material.map = new THREE.TextureLoader().load(url, render);
|
||||||
|
threeD.mesh = new THREE.Mesh(new THREE.SphereBufferGeometry(1, 64, 64), threeD.material);
|
||||||
|
threeD.scene.add(threeD.mesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
// render 3d scene and camera, do only on controls change
|
||||||
|
function render() {
|
||||||
|
threeD.Renderer.render(threeD.scene, threeD.camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
// animate 3d scene and camera
|
||||||
|
function animate() {
|
||||||
|
threeD.animationFrame = requestAnimationFrame(animate);
|
||||||
|
threeD.controls.update();
|
||||||
|
threeD.Renderer.render(threeD.scene, threeD.camera);
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleRotation() {
|
||||||
|
const rotate = threeD.controls.autoRotate = !threeD.controls.autoRotate;
|
||||||
|
rotate ? requestAnimationFrame(animate) : cancelAnimationFrame(threeD.animationFrame);
|
||||||
|
}
|
||||||
|
|
||||||
|
// download screenshot
|
||||||
|
async function saveScreenshot() {
|
||||||
|
const URL = threeD.Renderer.domElement.toDataURL("image/jpeg");
|
||||||
|
const link = document.createElement("a");
|
||||||
|
link.download = getFileName() + ".jpeg";
|
||||||
|
link.href = URL;
|
||||||
|
document.body.appendChild(link);
|
||||||
|
link.click();
|
||||||
|
tip(`Screenshot is saved. Open "Downloads" screen (CTRL + J) to check`, true, "success", 7000);
|
||||||
|
window.setTimeout(() => window.URL.revokeObjectURL(URL), 5000);
|
||||||
|
}
|
||||||
|
|
@ -1,91 +0,0 @@
|
||||||
"use strict";
|
|
||||||
|
|
||||||
// Define variables - these make it easy to work with from the console
|
|
||||||
let _3dpreviewScale = 70;
|
|
||||||
let _3dpreviewCamera = null;
|
|
||||||
let _3dpreviewScene = null;
|
|
||||||
let _3dpreviewRenderer = null;
|
|
||||||
let _3danimationFrame = null;
|
|
||||||
let _3dmaterial = null;
|
|
||||||
let _3dmesh = null;
|
|
||||||
|
|
||||||
// Create a mesh from pixel data
|
|
||||||
async function addMesh(width, height, segmentsX, segmentsY) {
|
|
||||||
const _3dgeometry = new THREE.PlaneGeometry(width, height, segmentsX-1, segmentsY-1);
|
|
||||||
const _3dmaterial = new THREE.MeshBasicMaterial({wireframe: false});
|
|
||||||
const url = await getMapURL("mesh");
|
|
||||||
_3dmaterial.map = new THREE.TextureLoader().load(url);
|
|
||||||
_3dgeometry.vertices.forEach((v, i) => v.z = getMeshHeight(i));
|
|
||||||
_3dmesh = new THREE.Mesh(_3dgeometry, _3dmaterial);
|
|
||||||
_3dmesh.rotation.x = -Math.PI / 2;
|
|
||||||
_3dpreviewScene.add(_3dmesh);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMeshHeight(i) {
|
|
||||||
const h = grid.cells.h[i];
|
|
||||||
return h < 20 ? 0 : (h - 18) / 82 * _3dpreviewScale;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Function to render scene and camera
|
|
||||||
function render() {
|
|
||||||
_3danimationFrame = requestAnimationFrame(render);
|
|
||||||
_3dpreviewRenderer.render(_3dpreviewScene, _3dpreviewCamera);
|
|
||||||
}
|
|
||||||
|
|
||||||
async function start3dpreview(canvas) {
|
|
||||||
const loaded = await loadTHREE();
|
|
||||||
if (!loaded) {
|
|
||||||
tip("Cannot load 3d library", false, "error", 4000);
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
_3dpreviewScene = new THREE.Scene();
|
|
||||||
_3dpreviewCamera = new THREE.PerspectiveCamera(70, canvas.width / canvas.height, 0.1, 100000);
|
|
||||||
_3dpreviewCamera.position.x = 0;
|
|
||||||
_3dpreviewCamera.position.z = 350;
|
|
||||||
_3dpreviewCamera.position.y = 285;
|
|
||||||
_3dpreviewRenderer = new THREE.WebGLRenderer({canvas});
|
|
||||||
OrbitControls(_3dpreviewCamera, _3dpreviewRenderer.domElement);
|
|
||||||
_3dpreviewRenderer.setSize(canvas.width, canvas.height);
|
|
||||||
addMesh(graphWidth, graphHeight, grid.cellsX, grid.cellsY);
|
|
||||||
_3danimationFrame = requestAnimationFrame(render);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadTHREE() {
|
|
||||||
if (window.THREE) return Promise.resolve(true);
|
|
||||||
|
|
||||||
return new Promise(resolve => {
|
|
||||||
const script = document.createElement('script');
|
|
||||||
script.src = "libs/three.min.js"
|
|
||||||
document.head.append(script);
|
|
||||||
script.onload = () => resolve(true);
|
|
||||||
script.onerror = () => resolve(false);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function OrbitControls(camera, domElement) {
|
|
||||||
if (THREE.OrbitControls) {
|
|
||||||
new THREE.OrbitControls(camera, domElement);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const script = document.createElement('script');
|
|
||||||
script.src = "libs/orbitControls.min.js"
|
|
||||||
document.head.append(script);
|
|
||||||
script.onload = () => new THREE.OrbitControls(camera, domElement);
|
|
||||||
}
|
|
||||||
|
|
||||||
function update3dpreview(canvas) {
|
|
||||||
_3dpreviewScene.remove(_3dmesh);
|
|
||||||
_3dpreviewRenderer.setSize(canvas.width, canvas.height);
|
|
||||||
addMesh(graphWidth, graphHeight, grid.cellsX, grid.cellsY);
|
|
||||||
}
|
|
||||||
|
|
||||||
function stop3dpreview() {
|
|
||||||
cancelAnimationFrame(_3danimationFrame);
|
|
||||||
_3danimationFrame = null;
|
|
||||||
_3dmesh = undefined;
|
|
||||||
_3dmaterial = undefined;
|
|
||||||
_3dpreviewScene = null;
|
|
||||||
_3dpreviewRenderer = null;
|
|
||||||
}
|
|
||||||
|
|
@ -39,6 +39,7 @@ function editDiplomacy() {
|
||||||
document.getElementById("diplomacyMatrix").addEventListener("click", showRelationsMatrix);
|
document.getElementById("diplomacyMatrix").addEventListener("click", showRelationsMatrix);
|
||||||
document.getElementById("diplomacyHistory").addEventListener("click", showRelationsHistory);
|
document.getElementById("diplomacyHistory").addEventListener("click", showRelationsHistory);
|
||||||
document.getElementById("diplomacyExport").addEventListener("click", downloadDiplomacyData);
|
document.getElementById("diplomacyExport").addEventListener("click", downloadDiplomacyData);
|
||||||
|
document.getElementById("diplomacySelect").addEventListener("click", diplomacyChangeRelations);
|
||||||
|
|
||||||
function refreshDiplomacyEditor() {
|
function refreshDiplomacyEditor() {
|
||||||
diplomacyEditorAddLines();
|
diplomacyEditorAddLines();
|
||||||
|
|
@ -52,6 +53,11 @@ function editDiplomacy() {
|
||||||
const sel = selectedLine ? +selectedLine.dataset.id : states.find(s => s.i && !s.removed).i;
|
const sel = selectedLine ? +selectedLine.dataset.id : states.find(s => s.i && !s.removed).i;
|
||||||
const selName = states[sel].fullName;
|
const selName = states[sel].fullName;
|
||||||
|
|
||||||
|
// move select drop-down back to initial place
|
||||||
|
const select = document.getElementById("diplomacySelect");
|
||||||
|
body.parentNode.insertBefore(select, body);
|
||||||
|
select.style.display = "none";
|
||||||
|
|
||||||
let lines = `<div class="states Self" data-id=${sel}>
|
let lines = `<div class="states Self" data-id=${sel}>
|
||||||
<div data-tip="List below shows relations to ${selName}" style="width: 100%">${selName}</div>
|
<div data-tip="List below shows relations to ${selName}" style="width: 100%">${selName}</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
@ -62,11 +68,15 @@ function editDiplomacy() {
|
||||||
const index = statuses.indexOf(relation);
|
const index = statuses.indexOf(relation);
|
||||||
const color = colors[index];
|
const color = colors[index];
|
||||||
const tip = s.fullName + description[index] + selName;
|
const tip = s.fullName + description[index] + selName;
|
||||||
|
const tipSelect = `${tip}. Click to see relations to ${s.name}`;
|
||||||
|
const tipChange = `${tip}. Click to change relations to ${selName}`;
|
||||||
|
|
||||||
lines += `<div class="states" data-id=${s.i} data-name="${s.fullName}" data-relations="${relation}">
|
lines += `<div class="states" data-id=${s.i} data-name="${s.fullName}" data-relations="${relation}">
|
||||||
<div data-tip="${tip}. Click to see relations to ${s.name}" style="width:12em">${s.fullName}</div>
|
<div data-tip="${tipSelect}" style="width:12em">${s.fullName}</div>
|
||||||
<input data-tip="${tip}. Click to see relations to ${s.name}" class="stateColor" type="color" value="${color}" disabled>
|
<svg data-tip="${tipChange}" width=".9em" height=".9em" style="margin-bottom:-1px" class="changeRelations">
|
||||||
<select data-tip="Click to change ${getAdjective(s.name)} relations to ${selName}" class="diplomacyRelations">${getRelations(relation)}</select>
|
<rect x="0" y="0" width="100%" height="100%" fill="${color}" class="zoneFill" style="pointer-events: none"></rect>
|
||||||
|
</svg>
|
||||||
|
<input data-tip="${tipChange}" class="changeRelations diplomacyRelations" value="${relation}" readonly/>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
body.innerHTML = lines;
|
body.innerHTML = lines;
|
||||||
|
|
@ -75,8 +85,7 @@ function editDiplomacy() {
|
||||||
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseenter", ev => stateHighlightOn(ev)));
|
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseenter", ev => stateHighlightOn(ev)));
|
||||||
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseleave", ev => stateHighlightOff(ev)));
|
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseleave", ev => stateHighlightOff(ev)));
|
||||||
body.querySelectorAll("div.states").forEach(el => el.addEventListener("click", selectStateOnLineClick));
|
body.querySelectorAll("div.states").forEach(el => el.addEventListener("click", selectStateOnLineClick));
|
||||||
body.querySelectorAll("div > select.diplomacyRelations").forEach(el => el.addEventListener("click", ev => ev.stopPropagation()));
|
body.querySelectorAll(".changeRelations").forEach(el => el.addEventListener("click", toggleDiplomacySelect));
|
||||||
body.querySelectorAll("div > select.diplomacyRelations").forEach(el => el.addEventListener("change", diplomacyChangeRelations));
|
|
||||||
|
|
||||||
applySorting(diplomacyHeader);
|
applySorting(diplomacyHeader);
|
||||||
$("#diplomacyEditor").dialog();
|
$("#diplomacyEditor").dialog();
|
||||||
|
|
@ -113,12 +122,6 @@ function editDiplomacy() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRelations(relations) {
|
|
||||||
let options = "";
|
|
||||||
statuses.forEach(s => options += `<option ${relations === s ? "selected" : ""} value="${s}">${s}</option>`);
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
function showStateRelations() {
|
function showStateRelations() {
|
||||||
const selectedLine = body.querySelector("div.Self");
|
const selectedLine = body.querySelector("div.Self");
|
||||||
const sel = selectedLine ? +selectedLine.dataset.id : pack.states.find(s => s.i && !s.removed).i;
|
const sel = selectedLine ? +selectedLine.dataset.id : pack.states.find(s => s.i && !s.removed).i;
|
||||||
|
|
@ -156,18 +159,33 @@ function editDiplomacy() {
|
||||||
refreshDiplomacyEditor();
|
refreshDiplomacyEditor();
|
||||||
}
|
}
|
||||||
|
|
||||||
function diplomacyChangeRelations() {
|
function toggleDiplomacySelect(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
const select = document.getElementById("diplomacySelect");
|
||||||
|
const show = select.style.display === "none";
|
||||||
|
if (!show) {select.style.display = "none"; return;}
|
||||||
|
event.target.parentNode.insertBefore(select, event.target);
|
||||||
|
select.style.display = "block";
|
||||||
|
}
|
||||||
|
|
||||||
|
function diplomacyChangeRelations(event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
const select = document.getElementById("diplomacySelect");
|
||||||
|
select.style.display = "none";
|
||||||
|
|
||||||
|
const subject = +event.target.parentElement.parentElement.dataset.id;
|
||||||
|
const rel = event.target.innerHTML;
|
||||||
|
body.parentNode.insertBefore(select, body);
|
||||||
|
|
||||||
const states = pack.states, chronicle = states[0].diplomacy;
|
const states = pack.states, chronicle = states[0].diplomacy;
|
||||||
const selectedLine = body.querySelector("div.Self");
|
const selectedLine = body.querySelector("div.Self");
|
||||||
const object = selectedLine ? +selectedLine.dataset.id : states.find(s => s.i && !s.removed).i;
|
const object = selectedLine ? +selectedLine.dataset.id : states.find(s => s.i && !s.removed).i;
|
||||||
if (!object) return;
|
if (!object) return;
|
||||||
const objectName = states[object].name; // object of relations change
|
const objectName = states[object].name; // object of relations change
|
||||||
|
|
||||||
const subject = +this.parentNode.dataset.id;
|
|
||||||
const subjectName = states[subject].name; // subject of relations change - actor
|
const subjectName = states[subject].name; // subject of relations change - actor
|
||||||
|
|
||||||
const oldRel = states[subject].diplomacy[object];
|
const oldRel = states[subject].diplomacy[object];
|
||||||
const rel = this.value;
|
if (rel === oldRel) return;
|
||||||
states[subject].diplomacy[object] = rel;
|
states[subject].diplomacy[object] = rel;
|
||||||
states[object].diplomacy[subject] = rel === "Vassal" ? "Suzerain" : rel === "Suzerain" ? "Vassal" : rel;
|
states[object].diplomacy[subject] = rel === "Vassal" ? "Suzerain" : rel === "Suzerain" ? "Vassal" : rel;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -279,13 +279,14 @@ document.addEventListener("keydown", event => {
|
||||||
|
|
||||||
// Hotkeys, see github.com/Azgaar/Fantasy-Map-Generator/wiki/Hotkeys
|
// Hotkeys, see github.com/Azgaar/Fantasy-Map-Generator/wiki/Hotkeys
|
||||||
document.addEventListener("keyup", event => {
|
document.addEventListener("keyup", event => {
|
||||||
const active = document.activeElement.tagName;
|
if (!window.closeDialogs) return; // not all modules are loaded
|
||||||
|
const canvas3d = document.getElementById("canvas3d"); // check if 3d mode is active
|
||||||
|
const active = canvas3d ? null : document.activeElement.tagName;
|
||||||
if (active === "INPUT" || active === "SELECT" || active === "TEXTAREA") return; // don't trigger if user inputs a text
|
if (active === "INPUT" || active === "SELECT" || active === "TEXTAREA") return; // don't trigger if user inputs a text
|
||||||
if (active === "DIV" && document.activeElement.contentEditable === "true") return; // don't trigger if user inputs a text
|
if (active === "DIV" && document.activeElement.contentEditable === "true") return; // don't trigger if user inputs a text
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
const key = event.keyCode, ctrl = event.ctrlKey, shift = event.shiftKey, meta = event.metaKey;
|
const key = event.keyCode, ctrl = event.ctrlKey, shift = event.shiftKey, meta = event.metaKey;
|
||||||
const tdMode = document.getElementById("_3dpreview");
|
|
||||||
|
|
||||||
if (key === 112) showInfo(); // "F1" to show info
|
if (key === 112) showInfo(); // "F1" to show info
|
||||||
else if (key === 113) regeneratePrompt(); // "F2" for new map
|
else if (key === 113) regeneratePrompt(); // "F2" for new map
|
||||||
|
|
@ -296,7 +297,12 @@ document.addEventListener("keyup", event => {
|
||||||
else if (key === 27) {closeDialogs(); hideOptions();} // Escape to close all dialogs
|
else if (key === 27) {closeDialogs(); hideOptions();} // Escape to close all dialogs
|
||||||
else if (key === 46) removeElementOnKey(); // "Delete" to remove the selected element
|
else if (key === 46) removeElementOnKey(); // "Delete" to remove the selected element
|
||||||
|
|
||||||
|
else if (key === 83 && canvas3d) saveScreenshot(); // "S" to save a screenshot
|
||||||
|
else if (key === 82 && canvas3d) toggleRotation(); // "R" to toggle 3d rotation
|
||||||
|
else if (key === 85 && canvas3d && customization !== 1) update3d(); // "U" to update 3d view
|
||||||
|
|
||||||
else if (ctrl && key === 80) savePNG(); // Ctrl + "P" to save as PNG
|
else if (ctrl && key === 80) savePNG(); // Ctrl + "P" to save as PNG
|
||||||
|
else if (ctrl && key === 71) saveJPEG(); // Ctrl + "J" to save as JPEG
|
||||||
else if (ctrl && key === 83) saveSVG(); // Ctrl + "S" to save as SVG
|
else if (ctrl && key === 83) saveSVG(); // Ctrl + "S" to save as SVG
|
||||||
else if (ctrl && key === 77) saveMap(); // Ctrl + "M" to save MAP file
|
else if (ctrl && key === 77) saveMap(); // Ctrl + "M" to save MAP file
|
||||||
else if (ctrl && key === 71) saveGeoJSON(); // Ctrl + "G" to save as GeoJSON
|
else if (ctrl && key === 71) saveGeoJSON(); // Ctrl + "G" to save as GeoJSON
|
||||||
|
|
@ -357,10 +363,10 @@ document.addEventListener("keyup", event => {
|
||||||
else if (key === 187) toggleRulers(); // Equal (=) to toggle Rulers
|
else if (key === 187) toggleRulers(); // Equal (=) to toggle Rulers
|
||||||
else if (key === 189) toggleScaleBar(); // Minus (-) to toggle Scale bar
|
else if (key === 189) toggleScaleBar(); // Minus (-) to toggle Scale bar
|
||||||
|
|
||||||
else if (key === 37 && !tdMode) zoom.translateBy(svg, 10, 0); // Left to scroll map left
|
else if (key === 37) zoom.translateBy(svg, 10, 0); // Left to scroll map left
|
||||||
else if (key === 39 && !tdMode) zoom.translateBy(svg, -10, 0); // Right to scroll map right
|
else if (key === 39) zoom.translateBy(svg, -10, 0); // Right to scroll map right
|
||||||
else if (key === 38 && !tdMode) zoom.translateBy(svg, 0, 10); // Up to scroll map up
|
else if (key === 38) zoom.translateBy(svg, 0, 10); // Up to scroll map up
|
||||||
else if (key === 40 && !tdMode) zoom.translateBy(svg, 0, -10); // Up to scroll map up
|
else if (key === 40) zoom.translateBy(svg, 0, -10); // Up to scroll map up
|
||||||
else if (key === 107 || key === 109) pressNumpadSign(key); // Numpad Plus/Minus to zoom map or change brush size
|
else if (key === 107 || key === 109) pressNumpadSign(key); // Numpad Plus/Minus to zoom map or change brush size
|
||||||
else if (key === 48 || key === 96) resetZoom(1000); // 0 to reset zoom
|
else if (key === 48 || key === 96) resetZoom(1000); // 0 to reset zoom
|
||||||
else if (key === 49 || key === 97) zoom.scaleTo(svg, 1); // 1 to zoom to 1
|
else if (key === 49 || key === 97) zoom.scaleTo(svg, 1); // 1 to zoom to 1
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,7 @@ function editHeightmap() {
|
||||||
|
|
||||||
restartHistory();
|
restartHistory();
|
||||||
if (document.getElementById("preview")) document.getElementById("preview").remove();
|
if (document.getElementById("preview")) document.getElementById("preview").remove();
|
||||||
if (document.getElementById("_3dpreview")) toggleHeightmap3dView();
|
if (document.getElementById("canvas3d")) toggleHeightmap3dView();
|
||||||
|
|
||||||
|
|
||||||
const mode = heightmapEditMode.innerHTML;
|
const mode = heightmapEditMode.innerHTML;
|
||||||
|
|
@ -415,7 +415,7 @@ function editHeightmap() {
|
||||||
if (!noStat) {
|
if (!noStat) {
|
||||||
updateStatistics();
|
updateStatistics();
|
||||||
if (document.getElementById("preview")) drawHeightmapPreview(); // update heightmap preview if opened
|
if (document.getElementById("preview")) drawHeightmapPreview(); // update heightmap preview if opened
|
||||||
if (document.getElementById("_3dpreview")) update3dpreview(_3dpreview); // update 3d heightmap preview if opened
|
if (document.getElementById("canvas3d")) update3dPreview(canvas3d); // update 3d heightmap preview if opened
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -430,7 +430,7 @@ function editHeightmap() {
|
||||||
updateStatistics();
|
updateStatistics();
|
||||||
|
|
||||||
if (document.getElementById("preview")) drawHeightmapPreview(); // update heightmap preview if opened
|
if (document.getElementById("preview")) drawHeightmapPreview(); // update heightmap preview if opened
|
||||||
if (document.getElementById("_3dpreview")) update3dpreview(_3dpreview); // update 3d heightmap preview if opened
|
if (document.getElementById("canvas3d")) update3dPreview(canvas3d); // update 3d heightmap preview if opened
|
||||||
}
|
}
|
||||||
|
|
||||||
// restart edits from 1st step
|
// restart edits from 1st step
|
||||||
|
|
@ -871,7 +871,7 @@ function editHeightmap() {
|
||||||
updateStatistics();
|
updateStatistics();
|
||||||
mockHeightmap();
|
mockHeightmap();
|
||||||
if (document.getElementById("preview")) drawHeightmapPreview(); // update heightmap preview if opened
|
if (document.getElementById("preview")) drawHeightmapPreview(); // update heightmap preview if opened
|
||||||
if (document.getElementById("_3dpreview")) update3dpreview(_3dpreview); // update 3d heightmap preview if opened
|
if (document.getElementById("canvas3d")) update3dPreview(canvas3d); // update 3d heightmap preview if opened
|
||||||
}
|
}
|
||||||
|
|
||||||
function downloadTemplate() {
|
function downloadTemplate() {
|
||||||
|
|
@ -1195,38 +1195,38 @@ function editHeightmap() {
|
||||||
|
|
||||||
// 3D previewer
|
// 3D previewer
|
||||||
async function toggleHeightmap3dView() {
|
async function toggleHeightmap3dView() {
|
||||||
if (document.getElementById("_3dpreview")) {
|
if (document.getElementById("canvas3d")) {
|
||||||
$("#_3dpreviewEditor").dialog("close");
|
$("#preview3d").dialog("close");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const canvas = document.createElement("canvas");
|
const canvas = document.createElement("canvas");
|
||||||
canvas.id = "_3dpreview";
|
canvas.id = "canvas3d";
|
||||||
canvas.style.display = "block";
|
canvas.style.display = "block";
|
||||||
canvas.width = parseFloat(_3dpreviewEditor.style.width) || graphWidth / 3;
|
canvas.width = parseFloat(preview3d.style.width) || graphWidth / 3;
|
||||||
canvas.height = canvas.width / (graphWidth / graphHeight);
|
canvas.height = canvas.width / (graphWidth / graphHeight);
|
||||||
const started = await start3dpreview(canvas);
|
const started = await start3d(canvas);
|
||||||
if (!started) return;
|
if (!started) return;
|
||||||
document.getElementById("_3dpreviewEditor").appendChild(canvas);
|
document.getElementById("preview3d").appendChild(canvas);
|
||||||
canvas.onmouseenter = () => {
|
canvas.onmouseenter = () => {
|
||||||
canvas.dataset.hovered ? tip("") : tip("Left mouse to change angle, middle mouse or mousewheel to zoom, right mouse to pan");
|
+canvas.dataset.hovered > 2 ? tip("") : tip("Left mouse to change angle, middle mouse or mousewheel to zoom, right mouse to pan. R to toggle rotation");
|
||||||
canvas.dataset.hovered = 1;
|
canvas.dataset.hovered = (+canvas.dataset.hovered|0) + 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
$("#_3dpreviewEditor").dialog({
|
$("#preview3d").dialog({
|
||||||
title: "3D Preview", resizable: true,
|
title: "3D Preview", resizable: true,
|
||||||
position: {my: "left bottom", at: "left+10 bottom-20", of: "svg"},
|
position: {my: "left bottom", at: "left+10 bottom-20", of: "svg"},
|
||||||
resizeStop: resize3dpreview, close: close3dPreview
|
resizeStop: resize3d, close: close3dPreview
|
||||||
});
|
});
|
||||||
|
|
||||||
function resize3dpreview() {
|
function resize3d() {
|
||||||
canvas.width = parseFloat(_3dpreviewEditor.style.width);
|
canvas.width = parseFloat(preview3d.style.width);
|
||||||
canvas.height = parseFloat(_3dpreviewEditor.style.height) - 2;
|
canvas.height = parseFloat(preview3d.style.height) - 2;
|
||||||
update3dpreview(canvas);
|
update3dPreview(canvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
function close3dPreview() {
|
function close3dPreview() {
|
||||||
stop3dpreview();
|
stop3d();
|
||||||
canvas.remove();
|
canvas.remove();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,8 @@ function getDefaultPresets() {
|
||||||
"religions": ["toggleBorders", "toggleIcons", "toggleLabels", "toggleReligions", "toggleRivers", "toggleRoutes", "toggleScaleBar"],
|
"religions": ["toggleBorders", "toggleIcons", "toggleLabels", "toggleReligions", "toggleRivers", "toggleRoutes", "toggleScaleBar"],
|
||||||
"provinces": ["toggleBorders", "toggleIcons", "toggleProvinces", "toggleRivers", "toggleScaleBar"],
|
"provinces": ["toggleBorders", "toggleIcons", "toggleProvinces", "toggleRivers", "toggleScaleBar"],
|
||||||
"biomes": ["toggleBiomes", "toggleRivers", "toggleScaleBar"],
|
"biomes": ["toggleBiomes", "toggleRivers", "toggleScaleBar"],
|
||||||
"heightmap": ["toggleHeight", "toggleRivers", "toggleScaleBar"],
|
"heightmap": ["toggleHeight", "toggleRivers"],
|
||||||
|
"physical": ["toggleCoordinates", "toggleHeight", "toggleRivers", "toggleScaleBar"],
|
||||||
"poi": ["toggleBorders", "toggleHeight", "toggleIcons", "toggleMarkers", "toggleRivers", "toggleRoutes", "toggleScaleBar"],
|
"poi": ["toggleBorders", "toggleHeight", "toggleIcons", "toggleMarkers", "toggleRivers", "toggleRoutes", "toggleScaleBar"],
|
||||||
"landmass": ["toggleScaleBar"]
|
"landmass": ["toggleScaleBar"]
|
||||||
}
|
}
|
||||||
|
|
@ -70,6 +71,7 @@ function changePreset(preset) {
|
||||||
const isDefault = getDefaultPresets()[preset];
|
const isDefault = getDefaultPresets()[preset];
|
||||||
removePresetButton.style.display = isDefault ? "none" : "inline-block";
|
removePresetButton.style.display = isDefault ? "none" : "inline-block";
|
||||||
savePresetButton.style.display = "none";
|
savePresetButton.style.display = "none";
|
||||||
|
if (document.getElementById("canvas3d")) setTimeout(update3d, 300);
|
||||||
}
|
}
|
||||||
|
|
||||||
function savePreset() {
|
function savePreset() {
|
||||||
|
|
@ -113,6 +115,11 @@ function getCurrentPreset() {
|
||||||
savePresetButton.style.display = "inline-block";
|
savePresetButton.style.display = "inline-block";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update 3d view is layer is toggled
|
||||||
|
document.getElementById("mapLayers").addEventListener("click", () => {
|
||||||
|
if (document.getElementById("canvas3d")) setTimeout(update3d, 300);
|
||||||
|
});
|
||||||
|
|
||||||
function toggleHeight(event) {
|
function toggleHeight(event) {
|
||||||
if (!terrs.selectAll("*").size()) {
|
if (!terrs.selectAll("*").size()) {
|
||||||
turnButtonOn("toggleHeight");
|
turnButtonOn("toggleHeight");
|
||||||
|
|
|
||||||
|
|
@ -348,6 +348,7 @@ document.getElementById("sticked").addEventListener("click", function(event) {
|
||||||
else if (id === "saveMap") saveMap();
|
else if (id === "saveMap") saveMap();
|
||||||
else if (id === "saveSVG") saveSVG();
|
else if (id === "saveSVG") saveSVG();
|
||||||
else if (id === "savePNG") savePNG();
|
else if (id === "savePNG") savePNG();
|
||||||
|
else if (id === "saveJPEG") saveJPEG();
|
||||||
else if (id === "saveGeo") saveGeoJSON();
|
else if (id === "saveGeo") saveGeoJSON();
|
||||||
else if (id === "saveDropbox") saveDropbox();
|
else if (id === "saveDropbox") saveDropbox();
|
||||||
if (id === "quickSave" || id === "saveMap" || id === "saveSVG" || id === "savePNG" || id === "saveGeo" || id === "saveDropbox") toggleSavePane();
|
if (id === "quickSave" || id === "saveMap" || id === "saveSVG" || id === "savePNG" || id === "saveGeo" || id === "saveDropbox") toggleSavePane();
|
||||||
|
|
@ -423,3 +424,46 @@ document.getElementById("mapToLoad").addEventListener("change", function() {
|
||||||
closeDialogs();
|
closeDialogs();
|
||||||
uploadMap(fileToLoad);
|
uploadMap(fileToLoad);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// View mode
|
||||||
|
viewMode.addEventListener("click", changeViewMode);
|
||||||
|
function changeViewMode(event) {
|
||||||
|
if (event.target.tagName !== "BUTTON") return;
|
||||||
|
const button = event.target;
|
||||||
|
|
||||||
|
enterStandardView();
|
||||||
|
if (button.classList.contains("pressed")) {
|
||||||
|
button.classList.remove("pressed");
|
||||||
|
viewStandard.classList.add("pressed");
|
||||||
|
} else {
|
||||||
|
viewMode.querySelectorAll(".pressed").forEach(button => button.classList.remove("pressed"));
|
||||||
|
button.classList.add("pressed");
|
||||||
|
if (button.id !== "viewStandard") enter3dView(button.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function enterStandardView() {
|
||||||
|
if (!document.getElementById("canvas3d")) return;
|
||||||
|
document.getElementById("canvas3d").remove();
|
||||||
|
stop3d();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function enter3dView(type) {
|
||||||
|
const canvas = document.createElement("canvas");
|
||||||
|
canvas.id = "canvas3d";
|
||||||
|
canvas.style.display = "block";
|
||||||
|
canvas.width = svgWidth;
|
||||||
|
canvas.height = svgHeight;
|
||||||
|
canvas.style.position = "absolute";
|
||||||
|
canvas.style.display = "none";
|
||||||
|
canvas.dataset.type = type;
|
||||||
|
const started = type === "viewGlobe" ? await startGlobe(canvas) : await start3d(canvas);
|
||||||
|
if (!started) return;
|
||||||
|
canvas.style.display = "block";
|
||||||
|
document.body.insertBefore(canvas, optionsContainer);
|
||||||
|
canvas.onmouseenter = () => {
|
||||||
|
const help = "Left mouse to change angle, middle mouse / mousewheel to zoom, right mouse to pan.\r\n<b>R</b> to toggle rotation. <b>U</b> to update. <b>S</b> to get a screenshot";
|
||||||
|
+canvas.dataset.hovered > 2 ? tip("") : tip(help);
|
||||||
|
canvas.dataset.hovered = (+canvas.dataset.hovered|0) + 1;
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue