This commit is contained in:
Azgaar 2020-04-07 22:46:16 +03:00
parent 2c6a8caf64
commit 6d0e106f1f
12 changed files with 181 additions and 66 deletions

View file

@ -566,6 +566,10 @@ input[type="color"]::-webkit-color-swatch-wrapper {
animation: glowing 2s infinite;
}
.glow {
animation: glowing 3s infinite;
}
@keyframes glowing {
0% {box-shadow: 0 0 -4px #ded2d8;}
50% {box-shadow: 0 0 8px #F44336;}
@ -2014,6 +2018,24 @@ svg.button {
border: dashed 1px #5d4651;
}
#prompt {
position: absolute;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
max-width: 21em;
background-color: #fff;
padding: 1.2em;
border: solid 1px #000;
font-size: 1.2em;
}
#promptTest {
padding: 0 0 .6em 0;
font-weight: bold;
font-family: sans-serif;
}
#mapOverlay {
position: absolute;
display: flex;

View file

@ -1022,6 +1022,7 @@
<option value="texture">Texture</option>
<option value="compass">Wind Rose</option>
<option value="zones">Zones</option>
<option value="seaIce">Ice</option>
</select>
<!-- <button id="restoreStyle" data-tip="Click to restore default style for all elements" class="icon-ccw styleButton" onclick="askToRestoreDefaultStyle()"></button> -->
@ -2584,7 +2585,8 @@
<button id="convertAutoHue" data-tip="Auto-assign colors based on hue (good to colored images)" class="icon-brush"></button>
<button id="convertColorsButton" data-tip="Set number of colors" class="icon-signal"></button>
<input id="convertColors" value="18" style="display: none"/>
<button id="convertComplete" data-tip="Complete the assignment. All unassigned colors will be considered as ocean" class="icon-check"></button>
<button id="convertComplete" data-tip="Complete the conversion. All unassigned colors will be considered as ocean" class="icon-check"></button>
<button id="convertCancel" data-tip="Cancel the conversion. Previous heoghtmap will be restored" class="icon-cancel"></button>
</div>
<div data-tip="Set opacity of the loaded image" style="padding-top: 4px"><i>Overlay opacity:</i><br>
@ -3473,14 +3475,14 @@
<p id="alertMessage">Warning!</p>
</div>
<dialog id="prompt" style="border: solid 1px #5e4fa2; user-select: none;">
<form method="dialog">
<div id="dialogText" style="padding-bottom: .4em; font-family: sans-serif"></div>
<div id="prompt" style="display: none" class="dialog">
<form id="promptForm">
<div id="promptTest"></div>
<input id="promptInput" type="number" step=.01 placeholder="type value" autocomplete="off" required>
<button type="submit" value="yes">Confirm</button>
<button value="no" formnovalidate>Cancel</button>
<button type="submit">Confirm</button>
<button type="button" id="promptCancel" formnovalidate>Cancel</button>
</form>
</dialog>
</div>
</div>

15
main.js
View file

@ -71,6 +71,7 @@ lakes.append("g").attr("id", "salt");
lakes.append("g").attr("id", "sinkhole");
lakes.append("g").attr("id", "frozen");
lakes.append("g").attr("id", "lava");
lakes.append("g").attr("id", "dry");
coastline.append("g").attr("id", "sea_island");
coastline.append("g").attr("id", "lake_island");
@ -526,6 +527,7 @@ function generate() {
elevateLakes();
Rivers.generate();
defineBiomes();
//drawSeaIce();
rankCells();
Cultures.generate();
@ -1091,6 +1093,7 @@ function reMarkFeatures() {
}
function defineLakeGroup(cell, number, temp) {
if (temp > 31) return "dry";
if (temp > 24) return "salt";
if (temp < -3) return "frozen";
const height = d3.max(cells.c[cell].map(c => cells.h[c]));
@ -1153,7 +1156,7 @@ function defineBiomes() {
}
function getBiomeId(moisture, temperature, height) {
if (temperature < -5) return 11; // permafrost biome
if (temperature < -5) return 11; // permafrost biome, including sea ice
if (height < 20) return 0; // liquid water cells have marine biome
if (moisture > 40 && height < 25 || moisture > 24 && height > 24) return 12; // wetland biome
const m = Math.min(moisture / 5 | 0, 4); // moisture band from 0 to 4
@ -1184,7 +1187,7 @@ function rankCells() {
if (type === "lake") {
// lake coast is valued
if (group === "freshwater") s += 30;
else if (group !== "lava") s += 10;
else if (group !== "lava" && group !== "dry") s += 10;
} else {
s += 5; // ocean coast is valued
if (cells.harbor[i] === 1) s += 20; // safe sea harbor is valued
@ -1210,7 +1213,7 @@ function addMarkers(number = 1) {
let count = mounts.length < 10 ? 0 : Math.ceil(mounts.length / 300 * number);
if (count) addMarker("volcano", "🌋", 52, 52, 17.5);
while (count) {
while (count && mounts.length) {
const cell = mounts.splice(biased(0, mounts.length-1, 5), 1);
const x = cells.p[cell][0], y = cells.p[cell][1];
const id = getNextId("markerElement");
@ -1231,7 +1234,7 @@ function addMarkers(number = 1) {
let count = springs.length < 30 ? 0 : Math.ceil(springs.length / 1000 * number);
if (count) addMarker("hot_springs", "♨", 50, 50, 19.5);
while (count) {
while (count && springs.length) {
const cell = springs.splice(biased(1, springs.length-1, 3), 1);
const x = cells.p[cell][0], y = cells.p[cell][1];
const id = getNextId("markerElement");
@ -1284,7 +1287,7 @@ function addMarkers(number = 1) {
let count = !bridges.length ? 0 : Math.ceil(bridges.length / 12 * number);
if (count) addMarker("bridge", "🌉", 50, 50, 16.5);
while (count) {
while (count && bridges.length) {
const cell = bridges.splice(0, 1);
const x = cells.p[cell][0], y = cells.p[cell][1];
const id = getNextId("markerElement");
@ -1375,7 +1378,7 @@ function addMarkers(number = 1) {
let count = battlefields.length < 100 ? 0 : Math.ceil(battlefields.length / 500 * number);
if (count) addMarker("battlefield", "⚔", 50, 50, 20);
while (count) {
while (count && battlefields.length) {
const cell = battlefields.splice(Math.floor(Math.random() * battlefields.length), 1);
const x = cells.p[cell][0], y = cells.p[cell][1];
const id = getNextId("markerElement");

View file

@ -953,6 +953,14 @@ function parseLoadedData(data) {
Military.generate();
}
if (version < 1.35) {
// v 1.35 added dry lakes
if (!lakes.select("#dry").size()) {
lakes.append("g").attr("id", "dry");
lakes.select("#dry").attr("opacity", 1).attr("fill", "#c9bfa7").attr("stroke", "#8e816f").attr("stroke-width", .7).attr("filter", null);
}
}
}()
void function checkDataIntegrity() {

View file

@ -283,9 +283,10 @@ function editBurg(id) {
const burg = pack.burgs[id];
const defSeed = +(seed + id.padStart(4, 0));
if (isCtrlClick(event)) {
prompt(`Please provide a Medieval Fantasy City Generator seed. <br>
Seed should be a number. Default seed is FMG map seed + burg id padded to 4 chars with zeros (${defSeed}). <br>
Please note that if seed is custom, "Overworld" button from MFCG will open a different map`, {default:burg.MFCG||defSeed, step:1, min:1, max:1e13-1}, v => {
prompt(`Please provide a Medieval Fantasy City Generator seed.
Seed should be a number. Default seed is FMG map seed + burg id padded to 4 chars with zeros (${defSeed}).
Please note that if seed is custom, "Overworld" button from MFCG will open a different map`,
{default:burg.MFCG||defSeed, step:1, min:1, max:1e13-1}, v => {
burg.MFCG = v;
openMFCG(v);
});

View file

@ -126,6 +126,11 @@ function editHeightmap() {
return;
}
if (document.getElementById("imageConverter").offsetParent) {
tip("Please exit the Image Conversion mode first", null, "error");
return;
}
customization = 0;
customizationMenu.style.display = "none";
if (document.getElementById("options").querySelector(".tab > button.active").id === "toolsTab") toolsContent.style.display = "block";
@ -960,8 +965,9 @@ function editHeightmap() {
$("#imageConverter").dialog({
title: "Image Converter", minHeight: "auto", width: "19.5em", resizable: false,
position: {my: "right top", at: "right-10 top+10", of: "svg"}
}).on('dialogclose', closeImageConverter);
position: {my: "right top", at: "right-10 top+10", of: "svg"},
beforeClose: closeImageConverter
});
// create canvas for image
const canvas = document.createElement("canvas");
@ -978,7 +984,7 @@ function editHeightmap() {
setOverlayOpacity(0);
document.getElementById("convertImageLoad").classList.add("glow"); // add glow effect
tip('Image Converter is opened. Upload the image and assign the colors to desired heights', true, "warn"); // main tip
tip('Image Converter is opened. Upload the image and assign the height for each of the colors', true, "warn"); // main tip
// remove all heights
grid.cells.h = new Uint8Array(grid.cells.i.length);
@ -1003,7 +1009,8 @@ function editHeightmap() {
document.getElementById("convertAutoLum").addEventListener("click", () => autoAssing("lum"));
document.getElementById("convertAutoHue").addEventListener("click", () => autoAssing("hue"));
document.getElementById("convertColorsButton").addEventListener("click", setConvertColorsNumber);
document.getElementById("convertComplete").addEventListener("click", () => $("#imageConverter").dialog("close"));
document.getElementById("convertComplete").addEventListener("click", applyConversion);
document.getElementById("convertCancel").addEventListener("click", cancelConversion);
document.getElementById("convertOverlay").addEventListener("input", function() {setOverlayOpacity(this.value)});
document.getElementById("convertOverlayNumber").addEventListener("input", function() {setOverlayOpacity(this.value)});
@ -1170,7 +1177,25 @@ function editHeightmap() {
document.getElementById("canvas").style.opacity = v;
}
function closeImageConverter() {
function applyConversion() {
viewbox.select("#heights").selectAll("polygon").each(function() {
const height = +this.dataset.height || 0;
const i = +this.id.slice(4);
grid.cells.h[i] = height;
});
viewbox.select("#heights").selectAll("polygon").remove();
updateHeightmap();
restoreImageConverterState();
}
function cancelConversion() {
restoreImageConverterState();
viewbox.select("#heights").selectAll("polygon").remove();
restoreHistory(edits.n-1);
}
function restoreImageConverterState() {
const canvas = document.getElementById("canvas");
if (canvas) canvas.remove(); else return;
const img = document.getElementById("image");
@ -1182,15 +1207,30 @@ function editHeightmap() {
colorsSelectValue.innerHTML = colorsSelectFriendly.innerHTML = 0;
viewbox.style("cursor", "default").on(".drag", null);
tip('Heightmap edit mode is active. Click on "Exit Customization" to finalize the heightmap', true);
$("#imageConverter").dialog("destroy");
}
viewbox.select("#heights").selectAll("polygon").each(function() {
const height = +this.dataset.height || 0;
const i = +this.id.slice(4);
grid.cells.h[i] = height;
});
function closeImageConverter(event) {
event.preventDefault();
event.stopPropagation();
alertMessage.innerHTML = 'Are you sure you want to close the Image Converter? Click "Cancel" to geck back to convertion. Click "Complete" to apply the conversion. Click "Close" to exit conversion mode and restore previous heightmap';
$("#alert").dialog({resizable: false, title: "Close Image Converter",
buttons: {
Cancel: function() {
$(this).dialog("close");
},
Complete: function() {
$(this).dialog("close");
applyConversion();
},
Close: function() {
$(this).dialog("close");
restoreImageConverterState();
viewbox.select("#heights").selectAll("polygon").remove();
updateHeightmap();
restoreHistory(edits.n-1);
}
}
});
}
}

View file

@ -127,7 +127,7 @@ function editLake() {
// just rename if only 1 element left
const oldGroup = elSelected.node().parentNode;
const basic = ["freshwater", "salt", "sinkhole", "frozen", "lava"].includes(oldGroup.id);
const basic = ["freshwater", "salt", "sinkhole", "frozen", "lava", "dry"].includes(oldGroup.id);
if (!basic && oldGroup.childElementCount === 1) {
document.getElementById("lakeGroup").selectedOptions[0].remove();
document.getElementById("lakeGroup").options.add(new Option(group, group, false, true));
@ -150,7 +150,7 @@ function editLake() {
function removeLakeGroup() {
const group = elSelected.node().parentNode.id;
if (["freshwater", "salt", "sinkhole", "frozen", "lava"].includes(group)) {
if (["freshwater", "salt", "sinkhole", "frozen", "lava", "dry"].includes(group)) {
tip("This is one of the default groups, it cannot be removed", false, "error");
return;
}

View file

@ -456,6 +456,41 @@ function drawCells() {
cells.append("path").attr("d", path);
}
function drawSeaIce() {
const seaIce = viewbox.append("g").attr("id", "seaIce").attr("fill", "#e8f0f6").attr("stroke", "#e8f0f6").attr("filter", "url(#dropShadow05)");//.attr("opacity", .8);
for (const i of grid.cells.i) {
const t = grid.cells.temp[i] ;
if (t > 2) continue;
if (t > -5 && grid.cells.h[i] >= 20) continue;
if (t < -5) drawpolygon(i);
if (P(normalize(t, -5.5, 2.5))) continue; // t[-5; 2]
const size = t < -14 ? 0 : t > -6 ? (7 + t) / 10 : (15 + t) / 100; // [0; 1], where 0 = full size, 1 = zero size
resizePolygon(i, rn(size * (.2 + rand() * .9), 2));
}
// -9: .06
// -8: .07
// -7: .08
// -6: .09
// -5: .2
// -4: .3
// -3: .4
// -2: .5
// -1: .6
// 0: .7
function drawpolygon(i) {
seaIce.append("polygon").attr("points", getGridPolygon(i));
}
function resizePolygon(i, s) {
const c = grid.points[i];
const points = getGridPolygon(i).map(p => [p[0] + (c[0]-p[0]) * s, p[1] + (c[1]-p[1]) * s]);
seaIce.append("polygon").attr("points", points);
}
}
function toggleCultures(event) {
const cultures = pack.cultures.filter(c => c.i && !c.removed);
const empty = !cults.selectAll("path").size();

View file

@ -896,12 +896,13 @@ function editStates() {
function downloadStatesData() {
const unit = areaUnit.value === "square" ? distanceUnitInput.value + "2" : areaUnit.value;
let data = "Id,State,Color,Capital,Culture,Type,Expansionism,Cells,Burgs,Area "+unit+",Total Population,Rural Population,Urban Population\n"; // headers
let data = "Id,State,Form,Color,Capital,Culture,Type,Expansionism,Cells,Burgs,Area "+unit+",Total Population,Rural Population,Urban Population\n"; // headers
body.querySelectorAll(":scope > div").forEach(function(el) {
const key = parseInt(el.dataset.id);
data += el.dataset.id + ",";
data += el.dataset.name + ",";
data += el.dataset.form + ",";
data += el.dataset.color + ",";
data += el.dataset.capital + ",";
data += el.dataset.culture + ",";

File diff suppressed because one or more lines are too long

View file

@ -250,12 +250,8 @@ function regenerateMilitary() {
}
function regenerateMarkers(event) {
if (isCtrlClick(event)) {
prompt("Please provide markers number multiplier", {default:1, step:.01, min:0, max:100}, v => {
if (v === null || v === "" || isNaN(+v)) return;
addNumberOfMarkers(Math.min(+v, 100));
});
} else addNumberOfMarkers(gauss(1, .5, .3, 5, 2));
if (isCtrlClick(event)) prompt("Please provide markers number multiplier", {default:1, step:.01, min:0, max:100}, v => addNumberOfMarkers(v));
else addNumberOfMarkers(gauss(1, .5, .3, 5, 2));
function addNumberOfMarkers(number) {
// remove existing markers and assigned notes
@ -270,12 +266,8 @@ function regenerateMarkers(event) {
}
function regenerateZones(event) {
if (isCtrlClick(event)) {
prompt("Please provide zones number multiplier", {default:1, step:.01, min:0, max:100}, v => {
if (v === null || v === "" || isNaN(+v)) return;
addNumberOfZones(Math.min(+v, 100));
});
} else addNumberOfZones(gauss(1, .5, .6, 5, 2));
if (isCtrlClick(event)) prompt("Please provide zones number multiplier", {default:1, step:.01, min:0, max:100}, v => addNumberOfZones(v));
else addNumberOfZones(gauss(1, .5, .6, 5, 2));
function addNumberOfZones(number) {
zones.selectAll("g").remove(); // remove existing zones

View file

@ -609,11 +609,14 @@ function generateDate(from = 100, to = 1000) {
}
// prompt replacer (prompt does not work in Electron)
window.prompt = function(dialogText = "Please provide an input", options = {default:1, step:.01, min:0, max:100}, callback) {
void function() {
const prompt = document.getElementById("prompt");
const form = prompt.querySelector("#promptForm");
window.prompt = function(promptTest = "Please provide an input", options = {default:1, step:.01, min:0, max:100}, callback) {
if (options.default === undefined) {console.error("Prompt: options object does not have default value defined"); return;}
const modal = document.getElementById("prompt");
const input = modal.querySelector("#promptInput");
modal.querySelector("#dialogText").innerHTML = dialogText;
const input = prompt.querySelector("#promptInput");
prompt.querySelector("#promptTest").innerHTML = promptTest;
const type = typeof(options.default) === "number" ? "number" : "text";
input.type = type;
if (options.step !== undefined) input.step = options.step;
@ -621,13 +624,19 @@ window.prompt = function(dialogText = "Please provide an input", options = {defa
if (options.max !== undefined) input.max = options.max;
input.placeholder = "type a " + type;
input.value = options.default;
modal.showModal();
prompt.style.display = "block";
modal.addEventListener("close", () => {
if (callback && modal.returnValue === "yes") callback(input.value);
input.value = modal.returnValue = "";
form.addEventListener("submit", event => {
prompt.style.display = "none";
const v = type === "number" ? +input.value : input.value;
event.preventDefault();
if (callback) callback(v);
}, {once: true});
}
}
const cancel = prompt.querySelector("#promptCancel");
cancel.addEventListener("click", () => prompt.style.display = "none");
}()
// localStorageDB
!function(){function e(t,o){return n?void(n.transaction("s").objectStore("s").get(t).onsuccess=function(e){var t=e.target.result&&e.target.result.v||null;o(t)}):void setTimeout(function(){e(t,o)},100)}var t=window.indexedDB||window.mozIndexedDB||window.webkitIndexedDB||window.msIndexedDB;if(!t)return void console.error("indexDB not supported");var n,o={k:"",v:""},r=t.open("d2",1);r.onsuccess=function(e){n=this.result},r.onerror=function(e){console.error("indexedDB request error"),console.log(e)},r.onupgradeneeded=function(e){n=null;var t=e.target.result.createObjectStore("s",{keyPath:"k"});t.transaction.oncomplete=function(e){n=e.target.db}},window.ldb={get:e,set:function(e,t){o.k=e,o.v=t,n.transaction("s","readwrite").objectStore("s").put(o)}}}();