mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 09:41:24 +01:00
Vignette layer (#1021)
* feat: vignette (non-editable yet) * feat: vignette style control * feat: vignette layer * feat: vignette - save styles * feat: vignette - style presets * feat: vignette - style presets * feat: vignette - style presets --------- Co-authored-by: Azgaar <azgaar.fmg@yandex.com>
This commit is contained in:
parent
537abf1223
commit
24ecd1dbf7
21 changed files with 396 additions and 60 deletions
|
|
@ -169,6 +169,7 @@ t,
|
||||||
#temperature,
|
#temperature,
|
||||||
#texture,
|
#texture,
|
||||||
#landmass,
|
#landmass,
|
||||||
|
#vignette,
|
||||||
#fogging {
|
#fogging {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
79
index.html
79
index.html
|
|
@ -359,9 +359,17 @@
|
||||||
<pattern id="oceanic" width="100" height="100" patternUnits="userSpaceOnUse">
|
<pattern id="oceanic" width="100" height="100" patternUnits="userSpaceOnUse">
|
||||||
<image id="oceanicPattern" href="./images/pattern1.png" opacity="0.2"></image>
|
<image id="oceanicPattern" href="./images/pattern1.png" opacity="0.2"></image>
|
||||||
</pattern>
|
</pattern>
|
||||||
|
|
||||||
|
<mask id="vignette-mask">
|
||||||
|
<rect x="0" y="0" width="100%" height="100%" fill="white"></rect>
|
||||||
|
<rect id="vignette-rect" fill="black"></rect>
|
||||||
|
</mask>
|
||||||
</defs>
|
</defs>
|
||||||
<g id="viewbox"></g>
|
<g id="viewbox"></g>
|
||||||
<g id="scaleBar"></g>
|
<g id="scaleBar"></g>
|
||||||
|
<g id="vignette" mask="url(#vignette-mask)">
|
||||||
|
<rect x="0" y="0" width="100%" height="100%" />
|
||||||
|
</g>
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<div id="loading">
|
<div id="loading">
|
||||||
|
|
@ -703,6 +711,15 @@
|
||||||
>
|
>
|
||||||
Scale Bar
|
Scale Bar
|
||||||
</li>
|
</li>
|
||||||
|
<li
|
||||||
|
id="toggleVignette"
|
||||||
|
data-tip="Vignette (border fading): click to toggle. Ctrl + click to edit style"
|
||||||
|
data-shortcut="[ (left bracket)"
|
||||||
|
onclick="toggleVignette(event)"
|
||||||
|
class="solid"
|
||||||
|
>
|
||||||
|
Vignette
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<div id="viewMode" data-tip="Set view node">
|
<div id="viewMode" data-tip="Set view node">
|
||||||
|
|
@ -785,6 +802,7 @@
|
||||||
<option value="regions" selected>States</option>
|
<option value="regions" selected>States</option>
|
||||||
<option value="temperature">Temperature</option>
|
<option value="temperature">Temperature</option>
|
||||||
<option value="texture">Texture</option>
|
<option value="texture">Texture</option>
|
||||||
|
<option value="vignette">Vignette</option>
|
||||||
<option value="compass">Wind Rose</option>
|
<option value="compass">Wind Rose</option>
|
||||||
<option value="zones">Zones</option>
|
<option value="zones">Zones</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
@ -909,6 +927,51 @@
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
||||||
|
<tbody id="styleVignette">
|
||||||
|
<tr data-tip="Select precreated vignette">
|
||||||
|
<td>Preset</td>
|
||||||
|
<td>
|
||||||
|
<select id="styleVignettePreset"></select>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr data-tip="Vignette rectangle position (in percents)">
|
||||||
|
<td>Position</td>
|
||||||
|
<td style="display: flex; flex-direction: column; gap: 2px">
|
||||||
|
<div>
|
||||||
|
<span>x </span>
|
||||||
|
<input id="styleVignetteX" type="number" min="0" max="100" step="0.1" style="width: 5em" />
|
||||||
|
<span>width </span>
|
||||||
|
<input id="styleVignetteWidth" type="number" min="0" max="100" step="0.1" style="width: 5em" />
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span>y </span>
|
||||||
|
<input id="styleVignetteY" type="number" min="0" max="100" step="0.1" style="width: 5em" />
|
||||||
|
<span>height </span>
|
||||||
|
<input id="styleVignetteHeight" type="number" min="0" max="100" step="0.1" style="width: 5em" />
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr data-tip="Set vignette X and Y radius (in percents)">
|
||||||
|
<td>Radius</td>
|
||||||
|
<td>
|
||||||
|
<span>x </span>
|
||||||
|
<input id="styleVignetteRx" type="number" min="0" max="50" style="width: 5em" />
|
||||||
|
<span>y </span>
|
||||||
|
<input id="styleVignetteRy" type="number" min="0" max="50" style="width: 5em" />
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
<tr data-tip="Set vignette blue propagation (in pixels)">
|
||||||
|
<td>Blur</td>
|
||||||
|
<td>
|
||||||
|
<input id="styleVignetteBlur" type="range" min="0" max="400" step="1" value="50" />
|
||||||
|
<output id="styleVignetteBlurOutput">50</output>px
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
|
||||||
<tbody id="styleOcean">
|
<tbody id="styleOcean">
|
||||||
<tr data-tip="Select ocean pattern">
|
<tr data-tip="Select ocean pattern">
|
||||||
<td>Pattern</td>
|
<td>Pattern</td>
|
||||||
|
|
@ -990,16 +1053,6 @@
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
||||||
<tbody id="styleShift">
|
|
||||||
<tr data-tip="Shift the element by axes">
|
|
||||||
<td>Shift by axes</td>
|
|
||||||
<td>
|
|
||||||
<input id="styleShiftX" type="number" data-tip="Shift by x axis in pixels" />
|
|
||||||
<input id="styleShiftY" type="number" data-tip="Shift by y axis in pixels" />
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
|
|
||||||
<tbody id="styleCompass">
|
<tbody id="styleCompass">
|
||||||
<tr data-tip="Set wind (compass) rose size">
|
<tr data-tip="Set wind (compass) rose size">
|
||||||
<td>Size</td>
|
<td>Size</td>
|
||||||
|
|
@ -7923,7 +7976,7 @@
|
||||||
<script src="config/heightmap-templates.js"></script>
|
<script src="config/heightmap-templates.js"></script>
|
||||||
<script src="config/precreated-heightmaps.js"></script>
|
<script src="config/precreated-heightmaps.js"></script>
|
||||||
<script src="modules/heightmap-generator.js?v=1.88.00"></script>
|
<script src="modules/heightmap-generator.js?v=1.88.00"></script>
|
||||||
<script src="modules/ocean-layers.js?v=1.93.07"></script>
|
<script src="modules/ocean-layers.js?v=1.95.00"></script>
|
||||||
<script src="modules/river-generator.js?v=1.89.13"></script>
|
<script src="modules/river-generator.js?v=1.89.13"></script>
|
||||||
<script src="modules/lakes.js"></script>
|
<script src="modules/lakes.js"></script>
|
||||||
<script src="modules/biomes.js"></script>
|
<script src="modules/biomes.js"></script>
|
||||||
|
|
@ -7984,12 +8037,12 @@
|
||||||
<script defer src="modules/ui/markers-editor.js"></script>
|
<script defer src="modules/ui/markers-editor.js"></script>
|
||||||
<script defer src="modules/ui/3d.js?v=1.94.03"></script>
|
<script defer src="modules/ui/3d.js?v=1.94.03"></script>
|
||||||
<script defer src="modules/ui/submap.js?v=1.94.03"></script>
|
<script defer src="modules/ui/submap.js?v=1.94.03"></script>
|
||||||
<script defer src="modules/ui/hotkeys.js?v=1.93.00"></script>
|
<script defer src="modules/ui/hotkeys.js?v=1.95.00"></script>
|
||||||
<script defer src="modules/coa-renderer.js?v=1.94.00"></script>
|
<script defer src="modules/coa-renderer.js?v=1.94.00"></script>
|
||||||
<script defer src="libs/rgbquant.min.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/jquery.ui.touch-punch.min.js"></script>
|
||||||
<script defer src="modules/io/save.js?v=1.93.02"></script>
|
<script defer src="modules/io/save.js?v=1.93.02"></script>
|
||||||
<script defer src="modules/io/load.js?v=1.94.05"></script>
|
<script defer src="modules/io/load.js?v=1.95.00"></script>
|
||||||
<script defer src="modules/io/cloud.js?v=1.94.04"></script>
|
<script defer src="modules/io/cloud.js?v=1.94.04"></script>
|
||||||
<script defer src="modules/io/export.js?v=1.94.03"></script>
|
<script defer src="modules/io/export.js?v=1.94.03"></script>
|
||||||
<script defer src="modules/io/formats.js"></script>
|
<script defer src="modules/io/formats.js"></script>
|
||||||
|
|
|
||||||
4
main.js
4
main.js
|
|
@ -1,7 +1,7 @@
|
||||||
// Azgaar (azgaar.fmg@yandex.com). Minsk, 2017-2022. MIT License
|
"use strict";
|
||||||
|
// Azgaar (azgaar.fmg@yandex.com). Minsk, 2017-2023. MIT License
|
||||||
// https://github.com/Azgaar/Fantasy-Map-Generator
|
// https://github.com/Azgaar/Fantasy-Map-Generator
|
||||||
|
|
||||||
"use strict";
|
|
||||||
// set debug options
|
// set debug options
|
||||||
const PRODUCTION = location.hostname && location.hostname !== "localhost" && location.hostname !== "127.0.0.1";
|
const PRODUCTION = location.hostname && location.hostname !== "localhost" && location.hostname !== "127.0.0.1";
|
||||||
const DEBUG = localStorage.getItem("debug");
|
const DEBUG = localStorage.getItem("debug");
|
||||||
|
|
|
||||||
|
|
@ -710,4 +710,30 @@ export function resolveVersionConflicts(version) {
|
||||||
drawTexture();
|
drawTexture();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (version < 1.95) {
|
||||||
|
// v1.95.00 added vignette visual layer
|
||||||
|
const mask = defs.append("mask").attr("id", "vignette-mask");
|
||||||
|
mask.append("rect").attr("fill", "white").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%");
|
||||||
|
mask
|
||||||
|
.append("rect")
|
||||||
|
.attr("id", "vignette-rect")
|
||||||
|
.attr("fill", "black")
|
||||||
|
.attr("x", "0.3%")
|
||||||
|
.attr("y", "0.4%")
|
||||||
|
.attr("width", "99.4%")
|
||||||
|
.attr("height", "99.2%")
|
||||||
|
.attr("rx", "5%")
|
||||||
|
.attr("ry", "5%")
|
||||||
|
.attr("filter", "blur(20px)");
|
||||||
|
|
||||||
|
const vignette = svg
|
||||||
|
.append("g")
|
||||||
|
.attr("id", "vignette")
|
||||||
|
.attr("mask", "url(#vignette-mask)")
|
||||||
|
.attr("opacity", 0.3)
|
||||||
|
.attr("fill", "#000000")
|
||||||
|
.style("display", "none");
|
||||||
|
vignette.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ async function quickLoad() {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadFromDropbox() {
|
async function loadFromDropbox() {
|
||||||
const mapPath = document.getElementById("loadFromDropboxSelect")?.value;
|
const mapPath = byId("loadFromDropboxSelect")?.value;
|
||||||
|
|
||||||
DEBUG && console.log("Loading map from Dropbox:", mapPath);
|
DEBUG && console.log("Loading map from Dropbox:", mapPath);
|
||||||
const blob = await Cloud.providers.dropbox.load(mapPath);
|
const blob = await Cloud.providers.dropbox.load(mapPath);
|
||||||
|
|
@ -19,8 +19,8 @@ async function loadFromDropbox() {
|
||||||
|
|
||||||
async function createSharableDropboxLink() {
|
async function createSharableDropboxLink() {
|
||||||
const mapFile = document.querySelector("#loadFromDropbox select").value;
|
const mapFile = document.querySelector("#loadFromDropbox select").value;
|
||||||
const sharableLink = document.getElementById("sharableLink");
|
const sharableLink = byId("sharableLink");
|
||||||
const sharableLinkContainer = document.getElementById("sharableLinkContainer");
|
const sharableLinkContainer = byId("sharableLinkContainer");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const previewLink = await Cloud.providers.dropbox.getLink(mapFile);
|
const previewLink = await Cloud.providers.dropbox.getLink(mapFile);
|
||||||
|
|
@ -110,7 +110,7 @@ function uploadMap(file, callback) {
|
||||||
const fileReader = new FileReader();
|
const fileReader = new FileReader();
|
||||||
fileReader.onloadend = async function (fileLoadedEvent) {
|
fileReader.onloadend = async function (fileLoadedEvent) {
|
||||||
if (callback) callback();
|
if (callback) callback();
|
||||||
document.getElementById("coas").innerHTML = ""; // remove auto-generated emblems
|
byId("coas").innerHTML = ""; // remove auto-generated emblems
|
||||||
const result = fileLoadedEvent.target.result;
|
const result = fileLoadedEvent.target.result;
|
||||||
const [mapData, mapVersion] = await parseLoadedResult(result);
|
const [mapData, mapVersion] = await parseLoadedResult(result);
|
||||||
|
|
||||||
|
|
@ -408,15 +408,14 @@ async function parseLoadedData(data) {
|
||||||
})();
|
})();
|
||||||
|
|
||||||
void (function restoreLayersState() {
|
void (function restoreLayersState() {
|
||||||
// helper functions
|
const isVisible = selection => selection.node() && selection.style("display") !== "none";
|
||||||
const notHidden = selection => selection.node() && selection.style("display") !== "none";
|
const isVisibleNode = node => node && node.style.display !== "none";
|
||||||
const hasChildren = selection => selection.node()?.hasChildNodes();
|
const hasChildren = selection => selection.node()?.hasChildNodes();
|
||||||
const hasChild = (selection, selector) => selection.node()?.querySelector(selector);
|
const hasChild = (selection, selector) => selection.node()?.querySelector(selector);
|
||||||
const turnOn = el => document.getElementById(el).classList.remove("buttonoff");
|
const turnOn = el => byId(el).classList.remove("buttonoff");
|
||||||
|
|
||||||
// turn all layers off
|
// turn all layers off
|
||||||
document
|
byId("mapLayers")
|
||||||
.getElementById("mapLayers")
|
|
||||||
.querySelectorAll("li")
|
.querySelectorAll("li")
|
||||||
.forEach(el => el.classList.add("buttonoff"));
|
.forEach(el => el.classList.add("buttonoff"));
|
||||||
|
|
||||||
|
|
@ -427,27 +426,28 @@ async function parseLoadedData(data) {
|
||||||
if (hasChildren(cells)) turnOn("toggleCells");
|
if (hasChildren(cells)) turnOn("toggleCells");
|
||||||
if (hasChildren(gridOverlay)) turnOn("toggleGrid");
|
if (hasChildren(gridOverlay)) turnOn("toggleGrid");
|
||||||
if (hasChildren(coordinates)) turnOn("toggleCoordinates");
|
if (hasChildren(coordinates)) turnOn("toggleCoordinates");
|
||||||
if (notHidden(compass) && hasChild(compass, "use")) turnOn("toggleCompass");
|
if (isVisible(compass) && hasChild(compass, "use")) turnOn("toggleCompass");
|
||||||
if (hasChildren(rivers)) turnOn("toggleRivers");
|
if (hasChildren(rivers)) turnOn("toggleRivers");
|
||||||
if (notHidden(terrain) && hasChildren(terrain)) turnOn("toggleRelief");
|
if (isVisible(terrain) && hasChildren(terrain)) turnOn("toggleRelief");
|
||||||
if (hasChildren(relig)) turnOn("toggleReligions");
|
if (hasChildren(relig)) turnOn("toggleReligions");
|
||||||
if (hasChildren(cults)) turnOn("toggleCultures");
|
if (hasChildren(cults)) turnOn("toggleCultures");
|
||||||
if (hasChildren(statesBody)) turnOn("toggleStates");
|
if (hasChildren(statesBody)) turnOn("toggleStates");
|
||||||
if (hasChildren(provs)) turnOn("toggleProvinces");
|
if (hasChildren(provs)) turnOn("toggleProvinces");
|
||||||
if (hasChildren(zones) && notHidden(zones)) turnOn("toggleZones");
|
if (hasChildren(zones) && isVisible(zones)) turnOn("toggleZones");
|
||||||
if (notHidden(borders) && hasChild(borders, "path")) turnOn("toggleBorders");
|
if (isVisible(borders) && hasChild(borders, "path")) turnOn("toggleBorders");
|
||||||
if (notHidden(routes) && hasChild(routes, "path")) turnOn("toggleRoutes");
|
if (isVisible(routes) && hasChild(routes, "path")) turnOn("toggleRoutes");
|
||||||
if (hasChildren(temperature)) turnOn("toggleTemp");
|
if (hasChildren(temperature)) turnOn("toggleTemp");
|
||||||
if (hasChild(population, "line")) turnOn("togglePopulation");
|
if (hasChild(population, "line")) turnOn("togglePopulation");
|
||||||
if (hasChildren(ice)) turnOn("toggleIce");
|
if (hasChildren(ice)) turnOn("toggleIce");
|
||||||
if (hasChild(prec, "circle")) turnOn("togglePrec");
|
if (hasChild(prec, "circle")) turnOn("togglePrec");
|
||||||
if (notHidden(emblems) && hasChild(emblems, "use")) turnOn("toggleEmblems");
|
if (isVisible(emblems) && hasChild(emblems, "use")) turnOn("toggleEmblems");
|
||||||
if (notHidden(labels)) turnOn("toggleLabels");
|
if (isVisible(labels)) turnOn("toggleLabels");
|
||||||
if (notHidden(icons)) turnOn("toggleIcons");
|
if (isVisible(icons)) turnOn("toggleIcons");
|
||||||
if (hasChildren(armies) && notHidden(armies)) turnOn("toggleMilitary");
|
if (hasChildren(armies) && isVisible(armies)) turnOn("toggleMilitary");
|
||||||
if (hasChildren(markers)) turnOn("toggleMarkers");
|
if (hasChildren(markers)) turnOn("toggleMarkers");
|
||||||
if (notHidden(ruler)) turnOn("toggleRulers");
|
if (isVisible(ruler)) turnOn("toggleRulers");
|
||||||
if (notHidden(scaleBar)) turnOn("toggleScaleBar");
|
if (isVisible(scaleBar)) turnOn("toggleScaleBar");
|
||||||
|
if (isVisibleNode(byId("vignette"))) turnOn("toggleVignette");
|
||||||
|
|
||||||
getCurrentPreset();
|
getCurrentPreset();
|
||||||
})();
|
})();
|
||||||
|
|
@ -462,7 +462,7 @@ async function parseLoadedData(data) {
|
||||||
{
|
{
|
||||||
// dynamically import and run auto-update script
|
// dynamically import and run auto-update script
|
||||||
const versionNumber = parseFloat(params[0]);
|
const versionNumber = parseFloat(params[0]);
|
||||||
const {resolveVersionConflicts} = await import("../dynamic/auto-update.js?v=1.94.00");
|
const {resolveVersionConflicts} = await import("../dynamic/auto-update.js?v=1.95.00");
|
||||||
resolveVersionConflicts(versionNumber);
|
resolveVersionConflicts(versionNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,6 +91,7 @@ function handleKeyup(event) {
|
||||||
else if (code === "KeyK") toggleMarkers();
|
else if (code === "KeyK") toggleMarkers();
|
||||||
else if (code === "Equal" && !customization) toggleRulers();
|
else if (code === "Equal" && !customization) toggleRulers();
|
||||||
else if (code === "Slash") toggleScaleBar();
|
else if (code === "Slash") toggleScaleBar();
|
||||||
|
else if (code === "BracketLeft") toggleVignette();
|
||||||
else if (code === "ArrowLeft") zoom.translateBy(svg, 10, 0);
|
else if (code === "ArrowLeft") zoom.translateBy(svg, 10, 0);
|
||||||
else if (code === "ArrowRight") zoom.translateBy(svg, -10, 0);
|
else if (code === "ArrowRight") zoom.translateBy(svg, -10, 0);
|
||||||
else if (code === "ArrowUp") zoom.translateBy(svg, 0, 10);
|
else if (code === "ArrowUp") zoom.translateBy(svg, 0, 10);
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@ function getDefaultPresets() {
|
||||||
"toggleRivers",
|
"toggleRivers",
|
||||||
"toggleRoutes",
|
"toggleRoutes",
|
||||||
"toggleScaleBar",
|
"toggleScaleBar",
|
||||||
"toggleStates"
|
"toggleStates",
|
||||||
|
"toggleVignette"
|
||||||
],
|
],
|
||||||
cultural: [
|
cultural: [
|
||||||
"toggleBorders",
|
"toggleBorders",
|
||||||
|
|
@ -23,7 +24,8 @@ function getDefaultPresets() {
|
||||||
"toggleLabels",
|
"toggleLabels",
|
||||||
"toggleRivers",
|
"toggleRivers",
|
||||||
"toggleRoutes",
|
"toggleRoutes",
|
||||||
"toggleScaleBar"
|
"toggleScaleBar",
|
||||||
|
"toggleVignette"
|
||||||
],
|
],
|
||||||
religions: [
|
religions: [
|
||||||
"toggleBorders",
|
"toggleBorders",
|
||||||
|
|
@ -32,12 +34,13 @@ function getDefaultPresets() {
|
||||||
"toggleReligions",
|
"toggleReligions",
|
||||||
"toggleRivers",
|
"toggleRivers",
|
||||||
"toggleRoutes",
|
"toggleRoutes",
|
||||||
"toggleScaleBar"
|
"toggleScaleBar",
|
||||||
|
"toggleVignette"
|
||||||
],
|
],
|
||||||
provinces: ["toggleBorders", "toggleIcons", "toggleProvinces", "toggleRivers", "toggleScaleBar"],
|
provinces: ["toggleBorders", "toggleIcons", "toggleProvinces", "toggleRivers", "toggleScaleBar", "toggleVignette"],
|
||||||
biomes: ["toggleBiomes", "toggleIce", "toggleRivers", "toggleScaleBar"],
|
biomes: ["toggleBiomes", "toggleIce", "toggleRivers", "toggleScaleBar", "toggleVignette"],
|
||||||
heightmap: ["toggleHeight", "toggleRivers"],
|
heightmap: ["toggleHeight", "toggleRivers", "toggleVignette"],
|
||||||
physical: ["toggleCoordinates", "toggleHeight", "toggleIce", "toggleRivers", "toggleScaleBar"],
|
physical: ["toggleCoordinates", "toggleHeight", "toggleIce", "toggleRivers", "toggleScaleBar", "toggleVignette"],
|
||||||
poi: [
|
poi: [
|
||||||
"toggleBorders",
|
"toggleBorders",
|
||||||
"toggleHeight",
|
"toggleHeight",
|
||||||
|
|
@ -46,7 +49,8 @@ function getDefaultPresets() {
|
||||||
"toggleMarkers",
|
"toggleMarkers",
|
||||||
"toggleRivers",
|
"toggleRivers",
|
||||||
"toggleRoutes",
|
"toggleRoutes",
|
||||||
"toggleScaleBar"
|
"toggleScaleBar",
|
||||||
|
"toggleVignette"
|
||||||
],
|
],
|
||||||
military: [
|
military: [
|
||||||
"toggleBorders",
|
"toggleBorders",
|
||||||
|
|
@ -56,7 +60,8 @@ function getDefaultPresets() {
|
||||||
"toggleRivers",
|
"toggleRivers",
|
||||||
"toggleRoutes",
|
"toggleRoutes",
|
||||||
"toggleScaleBar",
|
"toggleScaleBar",
|
||||||
"toggleStates"
|
"toggleStates",
|
||||||
|
"toggleVignette"
|
||||||
],
|
],
|
||||||
emblems: [
|
emblems: [
|
||||||
"toggleBorders",
|
"toggleBorders",
|
||||||
|
|
@ -66,7 +71,8 @@ function getDefaultPresets() {
|
||||||
"toggleRivers",
|
"toggleRivers",
|
||||||
"toggleRoutes",
|
"toggleRoutes",
|
||||||
"toggleScaleBar",
|
"toggleScaleBar",
|
||||||
"toggleStates"
|
"toggleStates",
|
||||||
|
"toggleVignette"
|
||||||
],
|
],
|
||||||
landmass: ["toggleScaleBar"]
|
landmass: ["toggleScaleBar"]
|
||||||
};
|
};
|
||||||
|
|
@ -1862,6 +1868,18 @@ function drawEmblems() {
|
||||||
TIME && console.timeEnd("drawEmblems");
|
TIME && console.timeEnd("drawEmblems");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function toggleVignette(event) {
|
||||||
|
if (!layerIsOn("toggleVignette")) {
|
||||||
|
turnButtonOn("toggleVignette");
|
||||||
|
$("#vignette").fadeIn();
|
||||||
|
if (event && isCtrlClick(event)) editStyle("vignette");
|
||||||
|
} else {
|
||||||
|
if (event && isCtrlClick(event)) return editStyle("vignette");
|
||||||
|
$("#vignette").fadeOut();
|
||||||
|
turnButtonOff("toggleVignette");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function layerIsOn(el) {
|
function layerIsOn(el) {
|
||||||
const buttonoff = document.getElementById(el).classList.contains("buttonoff");
|
const buttonoff = document.getElementById(el).classList.contains("buttonoff");
|
||||||
return !buttonoff;
|
return !buttonoff;
|
||||||
|
|
|
||||||
|
|
@ -100,7 +100,7 @@ function selectStyleElement() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// fill
|
// fill
|
||||||
if (["rivers", "lakes", "landmass", "prec", "ice", "fogging"].includes(styleElement)) {
|
if (["rivers", "lakes", "landmass", "prec", "ice", "fogging", "vignette"].includes(styleElement)) {
|
||||||
styleFill.style.display = "block";
|
styleFill.style.display = "block";
|
||||||
styleFillInput.value = styleFillOutput.value = el.attr("fill");
|
styleFillInput.value = styleFillOutput.value = el.attr("fill");
|
||||||
}
|
}
|
||||||
|
|
@ -355,6 +355,22 @@ function selectStyleElement() {
|
||||||
const auto = (styleCoastlineAuto.checked = coastline.select("#sea_island").attr("auto-filter"));
|
const auto = (styleCoastlineAuto.checked = coastline.select("#sea_island").attr("auto-filter"));
|
||||||
if (auto) styleFilter.style.display = "none";
|
if (auto) styleFilter.style.display = "none";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (styleElement === "vignette") {
|
||||||
|
styleVignette.style.display = "block";
|
||||||
|
|
||||||
|
const maskRect = byId("vignette-rect");
|
||||||
|
if (maskRect) {
|
||||||
|
const digit = str => str.replace(/[^\d.]/g, "");
|
||||||
|
styleVignetteX.value = digit(maskRect.getAttribute("x"));
|
||||||
|
styleVignetteY.value = digit(maskRect.getAttribute("y"));
|
||||||
|
styleVignetteWidth.value = digit(maskRect.getAttribute("width"));
|
||||||
|
styleVignetteHeight.value = digit(maskRect.getAttribute("height"));
|
||||||
|
styleVignetteRx.value = digit(maskRect.getAttribute("rx"));
|
||||||
|
styleVignetteRy.value = digit(maskRect.getAttribute("ry"));
|
||||||
|
styleVignetteBlur.value = styleVignetteBlurOutput.value = digit(maskRect.getAttribute("filter"));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle style inputs change
|
// Handle style inputs change
|
||||||
|
|
@ -471,15 +487,6 @@ styleGridShiftY.addEventListener("input", function () {
|
||||||
if (layerIsOn("toggleGrid")) drawGrid();
|
if (layerIsOn("toggleGrid")) drawGrid();
|
||||||
});
|
});
|
||||||
|
|
||||||
styleShiftX.addEventListener("input", shiftElement);
|
|
||||||
styleShiftY.addEventListener("input", shiftElement);
|
|
||||||
|
|
||||||
function shiftElement() {
|
|
||||||
const x = styleShiftX.value || 0;
|
|
||||||
const y = styleShiftY.value || 0;
|
|
||||||
getEl().attr("transform", `translate(${x},${y})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
styleRescaleMarkers.addEventListener("change", function () {
|
styleRescaleMarkers.addEventListener("change", function () {
|
||||||
markers.attr("rescale", +this.checked);
|
markers.attr("rescale", +this.checked);
|
||||||
invokeActiveZooming();
|
invokeActiveZooming();
|
||||||
|
|
@ -961,6 +968,81 @@ function fetchTextureURL(url) {
|
||||||
img.src = url;
|
img.src = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const vignettePresets = {
|
||||||
|
default: `{ "#vignette": { "opacity": 0.3, "fill": "#000000", "filter": null }, "#vignette-rect": { "x": "0.3%", "y": "0.4%", "width": "99.6%", "height": "99.2%", "rx": "5%", "ry": "5%", "filter": "blur(20px)" } }`,
|
||||||
|
neon: `{ "#vignette": { "opacity": 0.5, "fill": "#7300ff", "filter": null }, "#vignette-rect": { "x": "0.3%", "y": "0.4%", "width": "99.6%", "height": "99.2%", "rx": "0%", "ry": "0%", "filter": "blur(15px)" } }`,
|
||||||
|
smoke: `{ "#vignette": { "opacity": 1, "fill": "#000000", "filter": "url(#splotch)" }, "#vignette-rect": { "x": "3%", "y": "5%", "width": "96%", "height": "90%", "rx": "10%", "ry": "10%", "filter": "blur(100px)" } }`,
|
||||||
|
wound: `{ "#vignette": { "opacity": 0.8, "fill": "#ff0000", "filter": "url(#paper)"}, "#vignette-rect": {"x": "0.5%", "y": "1%", "width": "99%", "height": "98%", "rx": "5%", "ry": "5%", "filter": "blur(50px)" } }`,
|
||||||
|
paper: `{ "#vignette": { "opacity": 1, "fill": "#000000", "filter": "url(#paper)" }, "#vignette-rect": { "x": "0.3%", "y": "0.4%", "width": "99.6%", "height": "99.2%", "rx": "20%", "ry": "20%", "filter": "blur(150px)" } }`,
|
||||||
|
granite: `{ "#vignette": { "opacity": 0.95, "fill": "#231b1b", "filter": "url(#crumpled)" }, "#vignette-rect": { "x": "3%", "y": "5%", "width": "94%", "height": "90%", "rx": "20%", "ry": "20%", "filter": "blur(150px)" } }`,
|
||||||
|
spotlight: `{ "#vignette": { "opacity": 0.96, "fill": "#000000", "filter": null }, "#vignette-rect": { "x": "20%", "y": "30%", "width": "24%", "height": "30%", "rx": "50%", "ry": "50%", "filter": "blur(30px) "} }`
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.keys(vignettePresets).forEach(preset => {
|
||||||
|
styleVignettePreset.options.add(new Option(preset, preset, false, false));
|
||||||
|
});
|
||||||
|
|
||||||
|
styleVignettePreset.addEventListener("change", function () {
|
||||||
|
const attributes = JSON.parse(vignettePresets[this.value]);
|
||||||
|
|
||||||
|
for (const selector in attributes) {
|
||||||
|
const el = document.querySelector(selector);
|
||||||
|
if (!el) continue;
|
||||||
|
for (const attr in attributes[selector]) {
|
||||||
|
const value = attributes[selector][attr];
|
||||||
|
el.setAttribute(attr, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const vignette = byId("vignette");
|
||||||
|
if (vignette) {
|
||||||
|
styleOpacityInput.value = styleOpacityOutput.value = vignette.getAttribute("opacity");
|
||||||
|
styleFillInput.value = styleFillOutput.value = vignette.getAttribute("fill");
|
||||||
|
styleFilterInput.value = vignette.getAttribute("filter");
|
||||||
|
}
|
||||||
|
|
||||||
|
const maskRect = byId("vignette-rect");
|
||||||
|
if (maskRect) {
|
||||||
|
const digit = str => str.replace(/[^\d.]/g, "");
|
||||||
|
styleVignetteX.value = digit(maskRect.getAttribute("x"));
|
||||||
|
styleVignetteY.value = digit(maskRect.getAttribute("y"));
|
||||||
|
styleVignetteWidth.value = digit(maskRect.getAttribute("width"));
|
||||||
|
styleVignetteHeight.value = digit(maskRect.getAttribute("height"));
|
||||||
|
styleVignetteRx.value = digit(maskRect.getAttribute("rx"));
|
||||||
|
styleVignetteRy.value = digit(maskRect.getAttribute("ry"));
|
||||||
|
styleVignetteBlur.value = styleVignetteBlurOutput.value = digit(maskRect.getAttribute("filter"));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
styleVignetteX.addEventListener("input", function () {
|
||||||
|
byId("vignette-rect")?.setAttribute("x", `${this.value}%`);
|
||||||
|
});
|
||||||
|
|
||||||
|
styleVignetteWidth.addEventListener("input", function () {
|
||||||
|
byId("vignette-rect")?.setAttribute("width", `${this.value}%`);
|
||||||
|
});
|
||||||
|
|
||||||
|
styleVignetteY.addEventListener("input", function () {
|
||||||
|
byId("vignette-rect")?.setAttribute("y", `${this.value}%`);
|
||||||
|
});
|
||||||
|
|
||||||
|
styleVignetteHeight.addEventListener("input", function () {
|
||||||
|
byId("vignette-rect")?.setAttribute("height", `${this.value}%`);
|
||||||
|
});
|
||||||
|
|
||||||
|
styleVignetteRx.addEventListener("input", function () {
|
||||||
|
byId("vignette-rect")?.setAttribute("rx", `${this.value}%`);
|
||||||
|
});
|
||||||
|
|
||||||
|
styleVignetteRy.addEventListener("input", function () {
|
||||||
|
byId("vignette-rect")?.setAttribute("ry", `${this.value}%`);
|
||||||
|
});
|
||||||
|
|
||||||
|
styleVignetteBlur.addEventListener("input", function () {
|
||||||
|
styleVignetteBlurOutput.value = this.value;
|
||||||
|
byId("vignette-rect")?.setAttribute("filter", `blur(${this.value}px)`);
|
||||||
|
});
|
||||||
|
|
||||||
function updateElements() {
|
function updateElements() {
|
||||||
// burgIcons to desired size
|
// burgIcons to desired size
|
||||||
burgIcons.selectAll("g").each(function () {
|
burgIcons.selectAll("g").each(function () {
|
||||||
|
|
|
||||||
|
|
@ -299,7 +299,9 @@ function addStylePreset() {
|
||||||
"font-family",
|
"font-family",
|
||||||
"filter"
|
"filter"
|
||||||
],
|
],
|
||||||
"#fogging": ["opacity", "fill", "filter"]
|
"#fogging": ["opacity", "fill", "filter"],
|
||||||
|
"#vignette": ["opacity", "fill", "filter"],
|
||||||
|
"#vignette-rect": ["x", "y", "width", "height", "rx", "ry", "filter"]
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const selector in attributes) {
|
for (const selector in attributes) {
|
||||||
|
|
|
||||||
|
|
@ -384,5 +384,19 @@
|
||||||
"opacity": 0.98,
|
"opacity": 0.98,
|
||||||
"fill": "#30426f",
|
"fill": "#30426f",
|
||||||
"filter": null
|
"filter": null
|
||||||
|
},
|
||||||
|
"#vignette": {
|
||||||
|
"opacity": 0.2,
|
||||||
|
"fill": "#000000",
|
||||||
|
"filter": ""
|
||||||
|
},
|
||||||
|
"#vignette-rect": {
|
||||||
|
"x": "0%",
|
||||||
|
"y": "0%",
|
||||||
|
"width": "100%",
|
||||||
|
"height": "100%",
|
||||||
|
"rx": "0%",
|
||||||
|
"ry": "0%",
|
||||||
|
"filter": "blur(50px)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -384,5 +384,19 @@
|
||||||
"opacity": 0.98,
|
"opacity": 0.98,
|
||||||
"fill": "#30426f",
|
"fill": "#30426f",
|
||||||
"filter": null
|
"filter": null
|
||||||
|
},
|
||||||
|
"#vignette": {
|
||||||
|
"opacity": 0.2,
|
||||||
|
"fill": "#000000",
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#vignette-rect": {
|
||||||
|
"x": "0.2%",
|
||||||
|
"y": "0.3%",
|
||||||
|
"width": "99.8%",
|
||||||
|
"height": "99.4%",
|
||||||
|
"rx": "5%",
|
||||||
|
"ry": "5%",
|
||||||
|
"filter": "blur(30px)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -386,5 +386,19 @@
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"fill": "#ffffff",
|
"fill": "#ffffff",
|
||||||
"filter": null
|
"filter": null
|
||||||
|
},
|
||||||
|
"#vignette": {
|
||||||
|
"opacity": 0,
|
||||||
|
"fill": "#000000",
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#vignette-rect": {
|
||||||
|
"x": "0.3%",
|
||||||
|
"y": "0.4%",
|
||||||
|
"width": "99.6%",
|
||||||
|
"height": "99.2%",
|
||||||
|
"rx": "5%",
|
||||||
|
"ry": "5%",
|
||||||
|
"filter": "blur(20px)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -384,5 +384,19 @@
|
||||||
"opacity": 0.98,
|
"opacity": 0.98,
|
||||||
"fill": "#1b1423",
|
"fill": "#1b1423",
|
||||||
"filter": null
|
"filter": null
|
||||||
|
},
|
||||||
|
"#vignette": {
|
||||||
|
"opacity": 0,
|
||||||
|
"fill": "#000000",
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#vignette-rect": {
|
||||||
|
"x": "0.3%",
|
||||||
|
"y": "0.4%",
|
||||||
|
"width": "99.6%",
|
||||||
|
"height": "99.2%",
|
||||||
|
"rx": "5%",
|
||||||
|
"ry": "5%",
|
||||||
|
"filter": "blur(20px)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -384,5 +384,19 @@
|
||||||
"opacity": 0.98,
|
"opacity": 0.98,
|
||||||
"fill": "#30426f",
|
"fill": "#30426f",
|
||||||
"filter": null
|
"filter": null
|
||||||
|
},
|
||||||
|
"#vignette": {
|
||||||
|
"opacity": 0.3,
|
||||||
|
"fill": "#000000",
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#vignette-rect": {
|
||||||
|
"x": "0.3%",
|
||||||
|
"y": "0.4%",
|
||||||
|
"width": "99.6%",
|
||||||
|
"height": "99.2%",
|
||||||
|
"rx": "5%",
|
||||||
|
"ry": "5%",
|
||||||
|
"filter": "blur(20px)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -386,5 +386,19 @@
|
||||||
"opacity": 0.98,
|
"opacity": 0.98,
|
||||||
"fill": "#1b1423",
|
"fill": "#1b1423",
|
||||||
"filter": null
|
"filter": null
|
||||||
|
},
|
||||||
|
"#vignette": {
|
||||||
|
"opacity": 0.5,
|
||||||
|
"fill": "#042603",
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#vignette-rect": {
|
||||||
|
"x": "0.1%",
|
||||||
|
"y": "0.2%",
|
||||||
|
"width": "99.8%",
|
||||||
|
"height": "99.6%",
|
||||||
|
"rx": "10%",
|
||||||
|
"ry": "10%",
|
||||||
|
"filter": "blur(30px)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -384,5 +384,19 @@
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"fill": "#30426f",
|
"fill": "#30426f",
|
||||||
"filter": null
|
"filter": null
|
||||||
|
},
|
||||||
|
"#vignette": {
|
||||||
|
"opacity": 0.15,
|
||||||
|
"fill": "#000000",
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#vignette-rect": {
|
||||||
|
"x": "0.2%",
|
||||||
|
"y": "0.3%",
|
||||||
|
"width": "99.8%",
|
||||||
|
"height": "99.4%",
|
||||||
|
"rx": "5%",
|
||||||
|
"ry": "5%",
|
||||||
|
"filter": "blur(20px)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -380,5 +380,19 @@
|
||||||
"opacity": 0.98,
|
"opacity": 0.98,
|
||||||
"fill": "#30426f",
|
"fill": "#30426f",
|
||||||
"filter": null
|
"filter": null
|
||||||
|
},
|
||||||
|
"#vignette": {
|
||||||
|
"opacity": 0.3,
|
||||||
|
"fill": "#000000",
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#vignette-rect": {
|
||||||
|
"x": "0.3%",
|
||||||
|
"y": "0.4%",
|
||||||
|
"width": "99.6%",
|
||||||
|
"height": "99.2%",
|
||||||
|
"rx": "5%",
|
||||||
|
"ry": "5%",
|
||||||
|
"filter": "blur(20px)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -384,5 +384,19 @@
|
||||||
"opacity": 0.8,
|
"opacity": 0.8,
|
||||||
"fill": "#000000",
|
"fill": "#000000",
|
||||||
"filter": null
|
"filter": null
|
||||||
|
},
|
||||||
|
"#vignette": {
|
||||||
|
"opacity": 0,
|
||||||
|
"fill": "#000000",
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#vignette-rect": {
|
||||||
|
"x": "0.3%",
|
||||||
|
"y": "0.4%",
|
||||||
|
"width": "99.6%",
|
||||||
|
"height": "99.2%",
|
||||||
|
"rx": "5%",
|
||||||
|
"ry": "5%",
|
||||||
|
"filter": "blur(20px)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -384,5 +384,19 @@
|
||||||
"opacity": 1,
|
"opacity": 1,
|
||||||
"fill": "#30426f",
|
"fill": "#30426f",
|
||||||
"filter": null
|
"filter": null
|
||||||
|
},
|
||||||
|
"#vignette": {
|
||||||
|
"opacity": 0.2,
|
||||||
|
"fill": "#000000",
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#vignette-rect": {
|
||||||
|
"x": "0.3%",
|
||||||
|
"y": "0.4%",
|
||||||
|
"width": "99.6%",
|
||||||
|
"height": "99.2%",
|
||||||
|
"rx": "5%",
|
||||||
|
"ry": "5%",
|
||||||
|
"filter": "blur(30px)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -384,5 +384,19 @@
|
||||||
"opacity": 0.97,
|
"opacity": 0.97,
|
||||||
"fill": "#8398ce",
|
"fill": "#8398ce",
|
||||||
"filter": null
|
"filter": null
|
||||||
|
},
|
||||||
|
"#vignette": {
|
||||||
|
"opacity": 0.2,
|
||||||
|
"fill": "#000000",
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#vignette-rect": {
|
||||||
|
"x": "0.3%",
|
||||||
|
"y": "0.4%",
|
||||||
|
"width": "99.6%",
|
||||||
|
"height": "99.2%",
|
||||||
|
"rx": "5%",
|
||||||
|
"ry": "5%",
|
||||||
|
"filter": "blur(20px)"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// version and caching control
|
// version and caching control
|
||||||
const version = "1.94.06"; // generator version, update each time
|
const version = "1.95.00"; // generator version, update each time
|
||||||
|
|
||||||
{
|
{
|
||||||
document.title += " v" + version;
|
document.title += " v" + version;
|
||||||
|
|
@ -28,6 +28,7 @@ const version = "1.94.06"; // generator version, update each time
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<strong>Latest changes:</strong>
|
<strong>Latest changes:</strong>
|
||||||
|
<li>Vignette visual layer and vignette styling options</li>
|
||||||
<li>Ability to define custom heightmap color scheme</li>
|
<li>Ability to define custom heightmap color scheme</li>
|
||||||
<li>New style preset Night and new heightmap color schemes</li>
|
<li>New style preset Night and new heightmap color schemes</li>
|
||||||
<li>Random encounter markers (integration with <a href="https://deorum.vercel.app/" target="_blank">Deorum</a>)</li>
|
<li>Random encounter markers (integration with <a href="https://deorum.vercel.app/" target="_blank">Deorum</a>)</li>
|
||||||
|
|
@ -39,8 +40,6 @@ const version = "1.94.06"; // generator version, update each time
|
||||||
<li>New 3D scene options and improvements</li>
|
<li>New 3D scene options and improvements</li>
|
||||||
<li>Autosave feature (in Options)</li>
|
<li>Autosave feature (in Options)</li>
|
||||||
<li>Google translation support (in Options)</li>
|
<li>Google translation support (in Options)</li>
|
||||||
<li>Religions can be edited and redrawn like cultures</li>
|
|
||||||
<li>Lock states, provinces, cultures, and religions from regeneration</li>
|
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>Join our <a href="${discord}" target="_blank">Discord server</a> and <a href="${reddit}" target="_blank">Reddit community</a> to ask questions, share maps, discuss the Generator and Worlbuilding, report bugs and propose new features.</p>
|
<p>Join our <a href="${discord}" target="_blank">Discord server</a> and <a href="${reddit}" target="_blank">Reddit community</a> to ask questions, share maps, discuss the Generator and Worlbuilding, report bugs and propose new features.</p>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue