latest version

This commit is contained in:
howlingsails 2022-08-16 18:26:34 -07:00
parent 41432ac944
commit c477c8dfcd
36 changed files with 3979 additions and 832 deletions

View file

@ -7,89 +7,170 @@ function editBurg(id) {
const burg = id || d3.event.target.dataset.id;
elSelected = burgLabels.select("[data-id='" + burg + "']");
burgLabels.selectAll("text").call(d3.drag().on("start", dragBurgLabel)).classed("draggable", true);
burgLabels
.selectAll("text")
.call(d3.drag().on("start", dragBurgLabel))
.classed("draggable", true);
updateBurgValues();
$("#burgEditor").dialog({
title: "Edit Burg",
resizable: false,
close: closeBurgEditor,
position: {my: "left top", at: "left+10 top+10", of: "svg", collision: "fit"}
position: {
my: "left top",
at: "left+10 top+10",
of: "svg",
collision: "fit",
},
});
if (modules.editBurg) return;
modules.editBurg = true;
// add listeners
document.getElementById("burgGroupShow").addEventListener("click", showGroupSection);
document.getElementById("burgGroupHide").addEventListener("click", hideGroupSection);
document.getElementById("burgSelectGroup").addEventListener("change", changeGroup);
document.getElementById("burgInputGroup").addEventListener("change", createNewGroup);
document.getElementById("burgAddGroup").addEventListener("click", toggleNewGroupInput);
document.getElementById("burgRemoveGroup").addEventListener("click", removeBurgsGroup);
document
.getElementById("burgGroupShow")
.addEventListener("click", showGroupSection);
document
.getElementById("burgGroupHide")
.addEventListener("click", hideGroupSection);
document
.getElementById("burgSelectGroup")
.addEventListener("change", changeGroup);
document
.getElementById("burgInputGroup")
.addEventListener("change", createNewGroup);
document
.getElementById("burgAddGroup")
.addEventListener("click", toggleNewGroupInput);
document
.getElementById("burgRemoveGroup")
.addEventListener("click", removeBurgsGroup);
document.getElementById("burgName").addEventListener("input", changeName);
document.getElementById("burgNameReRandom").addEventListener("click", generateNameRandom);
document
.getElementById("burgNameReRandom")
.addEventListener("click", generateNameRandom);
document.getElementById("burgType").addEventListener("input", changeType);
document.getElementById("burgCulture").addEventListener("input", changeCulture);
document.getElementById("burgNameReCulture").addEventListener("click", generateNameCulture);
document.getElementById("burgPopulation").addEventListener("change", changePopulation);
burgBody.querySelectorAll(".burgFeature").forEach(el => el.addEventListener("click", toggleFeature));
document.getElementById("mfcgBurgSeed").addEventListener("change", changeSeed);
document.getElementById("regenerateMFCGBurgSeed").addEventListener("click", randomizeSeed);
document
.getElementById("burgCulture")
.addEventListener("input", changeCulture);
document
.getElementById("burgNameReCulture")
.addEventListener("click", generateNameCulture);
document
.getElementById("burgPopulation")
.addEventListener("change", changePopulation);
burgBody
.querySelectorAll(".burgFeature")
.forEach((el) => el.addEventListener("click", toggleFeature));
document
.getElementById("mfcgBurgSeed")
.addEventListener("change", changeSeed);
document
.getElementById("regenerateMFCGBurgSeed")
.addEventListener("click", randomizeSeed);
document.getElementById("burgStyleShow").addEventListener("click", showStyleSection);
document.getElementById("burgStyleHide").addEventListener("click", hideStyleSection);
document.getElementById("burgEditLabelStyle").addEventListener("click", editGroupLabelStyle);
document.getElementById("burgEditIconStyle").addEventListener("click", editGroupIconStyle);
document.getElementById("burgEditAnchorStyle").addEventListener("click", editGroupAnchorStyle);
document
.getElementById("burgStyleShow")
.addEventListener("click", showStyleSection);
document
.getElementById("burgStyleHide")
.addEventListener("click", hideStyleSection);
document
.getElementById("burgEditLabelStyle")
.addEventListener("click", editGroupLabelStyle);
document
.getElementById("burgEditIconStyle")
.addEventListener("click", editGroupIconStyle);
document
.getElementById("burgEditAnchorStyle")
.addEventListener("click", editGroupAnchorStyle);
document.getElementById("burgEmblem").addEventListener("click", openEmblemEdit);
document.getElementById("burgToggleMFCGMap").addEventListener("click", toggleMFCGMap);
document.getElementById("burgEditEmblem").addEventListener("click", openEmblemEdit);
document.getElementById("burgRelocate").addEventListener("click", toggleRelocateBurg);
document.getElementById("burglLegend").addEventListener("click", editBurgLegend);
document.getElementById("burgLock").addEventListener("click", toggleBurgLockButton);
document.getElementById("burgRemove").addEventListener("click", removeSelectedBurg);
document
.getElementById("burgEmblem")
.addEventListener("click", openEmblemEdit);
document
.getElementById("burgToggleMFCGMap")
.addEventListener("click", toggleMFCGMap);
document
.getElementById("burgEditEmblem")
.addEventListener("click", openEmblemEdit);
document
.getElementById("burgRelocate")
.addEventListener("click", toggleRelocateBurg);
document
.getElementById("burglLegend")
.addEventListener("click", editBurgLegend);
document
.getElementById("burgLock")
.addEventListener("click", toggleBurgLockButton);
document
.getElementById("burgRemove")
.addEventListener("click", removeSelectedBurg);
function updateBurgValues() {
const id = +elSelected.attr("data-id");
const b = pack.burgs[id];
const province = pack.cells.province[b.cell];
const provinceName = province ? pack.provinces[province].fullName + ", " : "";
const stateName = pack.states[b.state].fullName || pack.states[b.state].name;
document.getElementById("burgProvinceAndState").innerHTML = provinceName + stateName;
const provinceName = province
? pack.provinces[province].fullName + ", "
: "";
const stateName =
pack.states[b.state].fullName || pack.states[b.state].name;
document.getElementById("burgProvinceAndState").innerHTML =
provinceName + stateName;
document.getElementById("burgName").value = b.name;
document.getElementById("burgType").value = b.type || "Generic";
document.getElementById("burgPopulation").value = rn(b.population * populationRate * urbanization);
document.getElementById("burgEditAnchorStyle").style.display = +b.port ? "inline-block" : "none";
document.getElementById("burgPopulation").value = rn(
b.population * populationRate * urbanization
);
document.getElementById("burgEditAnchorStyle").style.display = +b.port
? "inline-block"
: "none";
// update list and select culture
const cultureSelect = document.getElementById("burgCulture");
cultureSelect.options.length = 0;
const cultures = pack.cultures.filter(c => !c.removed);
cultures.forEach(c => cultureSelect.options.add(new Option(c.name, c.i, false, c.i === b.culture)));
const cultures = pack.cultures.filter((c) => !c.removed);
cultures.forEach((c) =>
cultureSelect.options.add(
new Option(c.name, c.i, false, c.i === b.culture)
)
);
const temperature = grid.cells.temp[pack.cells.g[b.cell]];
document.getElementById("burgTemperature").innerHTML = convertTemperature(temperature);
document.getElementById("burgTemperatureLikeIn").innerHTML = getTemperatureLikeness(temperature);
document.getElementById("burgElevation").innerHTML = getHeight(pack.cells.h[b.cell]);
document.getElementById("burgTemperature").innerHTML =
convertTemperature(temperature);
document.getElementById("burgTemperatureLikeIn").innerHTML =
getTemperatureLikeness(temperature);
document.getElementById("burgElevation").innerHTML = getHeight(
pack.cells.h[b.cell]
);
// toggle features
if (b.capital) document.getElementById("burgCapital").classList.remove("inactive");
if (b.capital)
document.getElementById("burgCapital").classList.remove("inactive");
else document.getElementById("burgCapital").classList.add("inactive");
if (b.port) document.getElementById("burgPort").classList.remove("inactive");
if (b.port)
document.getElementById("burgPort").classList.remove("inactive");
else document.getElementById("burgPort").classList.add("inactive");
if (b.citadel) document.getElementById("burgCitadel").classList.remove("inactive");
if (b.citadel)
document.getElementById("burgCitadel").classList.remove("inactive");
else document.getElementById("burgCitadel").classList.add("inactive");
if (b.walls) document.getElementById("burgWalls").classList.remove("inactive");
if (b.walls)
document.getElementById("burgWalls").classList.remove("inactive");
else document.getElementById("burgWalls").classList.add("inactive");
if (b.plaza) document.getElementById("burgPlaza").classList.remove("inactive");
if (b.plaza)
document.getElementById("burgPlaza").classList.remove("inactive");
else document.getElementById("burgPlaza").classList.add("inactive");
if (b.temple) document.getElementById("burgTemple").classList.remove("inactive");
if (b.temple)
document.getElementById("burgTemple").classList.remove("inactive");
else document.getElementById("burgTemple").classList.add("inactive");
if (b.shanty) document.getElementById("burgShanty").classList.remove("inactive");
if (b.shanty)
document.getElementById("burgShanty").classList.remove("inactive");
else document.getElementById("burgShanty").classList.add("inactive");
//toggle lock
@ -101,7 +182,9 @@ function editBurg(id) {
select.options.length = 0; // remove all options
burgLabels.selectAll("g").each(function () {
select.options.add(new Option(this.id, this.id, false, this.id === group));
select.options.add(
new Option(this.id, this.id, false, this.id === group)
);
});
// set emlem image
@ -157,7 +240,7 @@ function editBurg(id) {
"Mogadishu",
"Bangkok",
"Aden",
"Khartoum"
"Khartoum",
]; // 21 - 30
if (temperature > 30) return "Mecca";
return cities[temperature + 5] || null;
@ -172,17 +255,25 @@ function editBurg(id) {
const x = d3.event.x,
y = d3.event.y;
this.setAttribute("transform", `translate(${dx + x},${dy + y})`);
tip('Use dragging for fine-tuning only, to actually move burg use "Relocate" button', false, "warning");
tip(
'Use dragging for fine-tuning only, to actually move burg use "Relocate" button',
false,
"warning"
);
});
}
function showGroupSection() {
document.querySelectorAll("#burgBottom > button").forEach(el => (el.style.display = "none"));
document
.querySelectorAll("#burgBottom > button")
.forEach((el) => (el.style.display = "none"));
document.getElementById("burgGroupSection").style.display = "inline-block";
}
function hideGroupSection() {
document.querySelectorAll("#burgBottom > button").forEach(el => (el.style.display = "inline-block"));
document
.querySelectorAll("#burgBottom > button")
.forEach((el) => (el.style.display = "inline-block"));
document.getElementById("burgGroupSection").style.display = "none";
document.getElementById("burgInputGroup").style.display = "none";
document.getElementById("burgInputGroup").value = "";
@ -216,7 +307,11 @@ function editBurg(id) {
.replace(/[^\w\s]/gi, "");
if (document.getElementById(group)) {
tip("Element with this id already exists. Please provide a unique name", false, "error");
tip(
"Element with this id already exists. Please provide a unique name",
false,
"error"
);
return;
}
@ -244,7 +339,9 @@ function editBurg(id) {
const count = elSelected.node().parentNode.childElementCount;
if (oldGroup !== "cities" && oldGroup !== "towns" && count === 1) {
document.getElementById("burgSelectGroup").selectedOptions[0].remove();
document.getElementById("burgSelectGroup").options.add(new Option(group, group, false, true));
document
.getElementById("burgSelectGroup")
.options.add(new Option(group, group, false, true));
toggleNewGroupInput();
document.getElementById("burgInputGroup").value = "";
labelG.id = group;
@ -254,16 +351,24 @@ function editBurg(id) {
}
// create new groups
document.getElementById("burgSelectGroup").options.add(new Option(group, group, false, true));
document
.getElementById("burgSelectGroup")
.options.add(new Option(group, group, false, true));
toggleNewGroupInput();
document.getElementById("burgInputGroup").value = "";
const newLabelG = document.querySelector("#burgLabels").appendChild(labelG.cloneNode(false));
const newLabelG = document
.querySelector("#burgLabels")
.appendChild(labelG.cloneNode(false));
newLabelG.id = group;
const newIconG = document.querySelector("#burgIcons").appendChild(iconG.cloneNode(false));
const newIconG = document
.querySelector("#burgIcons")
.appendChild(iconG.cloneNode(false));
newIconG.id = group;
if (anchor) {
const newAnchorG = document.querySelector("#anchors").appendChild(anchorG.cloneNode(false));
const newAnchorG = document
.querySelector("#anchors")
.appendChild(anchorG.cloneNode(false));
newAnchorG.id = group;
}
moveBurgToGroup(id, group);
@ -277,11 +382,17 @@ function editBurg(id) {
for (let i = 0; i < group.children.length; i++) {
burgsInGroup.push(+group.children[i].dataset.id);
}
const burgsToRemove = burgsInGroup.filter(b => !(pack.burgs[b].capital || pack.burgs[b].lock));
const burgsToRemove = burgsInGroup.filter(
(b) => !(pack.burgs[b].capital || pack.burgs[b].lock)
);
const capital = burgsToRemove.length < burgsInGroup.length;
alertMessage.innerHTML = `Are you sure you want to remove
${basic || capital ? "all unlocked elements in the group" : "the entire burg group"}?
${
basic || capital
? "all unlocked elements in the group"
: "the entire burg group"
}?
<br>Please note that capital or locked burgs will not be deleted.
<br><br>Burgs to be removed: ${burgsToRemove.length}`;
$("#alert").dialog({
@ -292,7 +403,7 @@ function editBurg(id) {
$(this).dialog("close");
$("#burgEditor").dialog("close");
hideGroupSection();
burgsToRemove.forEach(b => removeBurg(b));
burgsToRemove.forEach((b) => removeBurg(b));
if (!basic && !capital) {
// entirely remove group
@ -306,8 +417,8 @@ function editBurg(id) {
},
Cancel: function () {
$(this).dialog("close");
}
}
},
},
});
}
@ -342,7 +453,10 @@ function editBurg(id) {
function changePopulation() {
const id = +elSelected.attr("data-id");
pack.burgs[id].population = rn(burgPopulation.value / populationRate / urbanization, 4);
pack.burgs[id].population = rn(
burgPopulation.value / populationRate / urbanization,
4
);
}
function toggleFeature() {
@ -356,7 +470,9 @@ function editBurg(id) {
if (b[feature]) this.classList.remove("inactive");
else if (!b[feature]) this.classList.add("inactive");
if (b.port) document.getElementById("burgEditAnchorStyle").style.display = "inline-block";
if (b.port)
document.getElementById("burgEditAnchorStyle").style.display =
"inline-block";
else document.getElementById("burgEditAnchorStyle").style.display = "none";
}
@ -379,12 +495,16 @@ function editBurg(id) {
}
function showStyleSection() {
document.querySelectorAll("#burgBottom > button").forEach(el => (el.style.display = "none"));
document
.querySelectorAll("#burgBottom > button")
.forEach((el) => (el.style.display = "none"));
document.getElementById("burgStyleSection").style.display = "inline-block";
}
function hideStyleSection() {
document.querySelectorAll("#burgBottom > button").forEach(el => (el.style.display = "inline-block"));
document
.querySelectorAll("#burgBottom > button")
.forEach((el) => (el.style.display = "inline-block"));
document.getElementById("burgStyleSection").style.display = "none";
}
@ -408,21 +528,25 @@ function editBurg(id) {
const burgGeneratorURL = getBurgLink(burg);
document.getElementById("mfcgPreview").setAttribute("src", mfcgURL);
document.getElementById("mfcgLink").setAttribute("href", mfcgURL);
document.getElementById("burgGenerator").setAttribute("href", burgGeneratorURL);
debug
document
.getElementById("burgGenerator")
.setAttribute("href", burgGeneratorURL);
debug;
}
function getBurgSeed(burg) {
return burg.MFCG || Number(`${seed}${String(burg.i).padStart(4, 0)}`);
}
function getBurgLink(burg) {
const {cells} = pack;
let burgCulture = pack.cultures[burg.culture].name.split('(')[1].split(')')[0];
const { cells } = pack;
let burgCulture = pack.cultures[burg.culture].name
.split("(")[1]
.split(")")[0];
const {name, population, cell} = burg;
const { name, population, cell } = burg;
const burgSeed = getBurgSeed(burg);
const sizeRaw = 0.43 * Math.pow((population * populationRate) / urbanDensity, 0.385);
const sizeRaw =
0.43 * Math.pow((population * populationRate) / urbanDensity, 0.385);
const size = minmax(Math.ceil(sizeRaw), 2, 40);
const people = rn(population * populationRate * urbanization);
const hub = +cells.road[cell] > 50;
@ -435,6 +559,7 @@ function editBurg(id) {
const temple = +burg.temple;
const shanty = +burg.shanty;
const worldName = mapName.value;
const sea = coast && cells.haven[cell] ? getSeaDirections(cell) : "";
function getSeaDirections(i) {
const p1 = cells.p[i];
@ -445,24 +570,23 @@ function editBurg(id) {
return "&sea=" + norm;
}
let townSize = "tiny";
if (people > 200) townSize = 'small';
if (people > 900) townSize = 'medium';
if (people > 2_000) townSize = 'average';
if (people > 5_500) townSize = 'big';
if (people > 200) townSize = "small";
if (people > 900) townSize = "medium";
if (people > 2_000) townSize = "average";
if (people > 5_500) townSize = "big";
const baseURL = "http://localhost:9090/town/";
const url = `${baseURL}${name}-${burgCulture}/${townSize}`;
const url = `${baseURL}${worldName}_${name}-${burgCulture}/${townSize}`;
return url;
}
function getMFCGlink(burg) {
const {cells} = pack;
const {name, population, cell} = burg;
const { cells } = pack;
const { name, population, cell } = burg;
const burgSeed = getBurgSeed(burg);
const sizeRaw = 0.43 * Math.pow((population * populationRate) / urbanDensity, 0.385);
const size = minmax(Math.ceil(sizeRaw), 2, 40)*3;
const sizeRaw =
0.43 * Math.pow((population * populationRate) / urbanDensity, 0.385);
const size = minmax(Math.ceil(sizeRaw), 2, 40) * 3;
const people = rn(population * populationRate * urbanization);
const hub = +cells.road[cell] > 50;
@ -485,7 +609,8 @@ function editBurg(id) {
return "&sea=" + norm;
}
const baseURL = "https://watabou.github.io/city-generator/?random=0&continuous=0";
const baseURL =
"https://watabou.github.io/city-generator/?random=0&continuous=0";
const url = `${baseURL}&name=${name}&population=${people}&size=${size}&seed=${burgSeed}&hub=${hub}&river=${river}&coast=${coast}&citadel=${citadel}&plaza=${plaza}&temple=${temple}&walls=${walls}&shantytown=${shanty}${sea}`;
return url;
}
@ -515,8 +640,11 @@ function editBurg(id) {
function toggleMFCGMap() {
options.showMFCGMap = !options.showMFCGMap;
document.getElementById("mfcgPreviewSection").style.display = options.showMFCGMap ? "block" : "none";
document.getElementById("burgToggleMFCGMap").className = options.showMFCGMap ? "icon-map" : "icon-map-o";
document.getElementById("mfcgPreviewSection").style.display =
options.showMFCGMap ? "block" : "none";
document.getElementById("burgToggleMFCGMap").className = options.showMFCGMap
? "icon-map"
: "icon-map-o";
}
function toggleRelocateBurg() {
@ -524,7 +652,10 @@ function editBurg(id) {
document.getElementById("burgRelocate").classList.toggle("pressed");
if (document.getElementById("burgRelocate").classList.contains("pressed")) {
viewbox.style("cursor", "crosshair").on("click", relocateBurgOnClick);
tip("Click on map to relocate burg. Hold Shift for continuous move", true);
tip(
"Click on map to relocate burg. Hold Shift for continuous move",
true
);
if (!layerIsOn("toggleCells")) {
toggleCells();
toggler.dataset.forced = true;
@ -547,12 +678,20 @@ function editBurg(id) {
const burg = pack.burgs[id];
if (cells.h[cell] < 20) {
tip("Cannot place burg into the water! Select a land cell", false, "error");
tip(
"Cannot place burg into the water! Select a land cell",
false,
"error"
);
return;
}
if (cells.burg[cell] && cells.burg[cell] !== id) {
tip("There is already a burg in this cell. Please select a free cell", false, "error");
tip(
"There is already a burg in this cell. Please select a free cell",
false,
"error"
);
return;
}
@ -614,8 +753,8 @@ function editBurg(id) {
buttons: {
Ok: function () {
$(this).dialog("close");
}
}
},
},
});
} else {
alertMessage.innerHTML = "Are you sure you want to remove the burg?";
@ -630,15 +769,18 @@ function editBurg(id) {
},
Cancel: function () {
$(this).dialog("close");
}
}
},
},
});
}
}
function closeBurgEditor() {
document.getElementById("burgRelocate").classList.remove("pressed");
burgLabels.selectAll("text").call(d3.drag().on("drag", null)).classed("draggable", false);
burgLabels
.selectAll("text")
.call(d3.drag().on("drag", null))
.classed("draggable", false);
unselect();
}
}

View file

@ -7,7 +7,11 @@ restoreDefaultEvents(); // apply default viewbox events on load
// restore default viewbox events
function restoreDefaultEvents() {
svg.call(zoom);
viewbox.style("cursor", "default").on(".drag", null).on("click", clicked).on("touchmove mousemove", moved);
viewbox
.style("cursor", "default")
.on(".drag", null)
.on("click", clicked)
.on("touchmove mousemove", moved);
legend.call(d3.drag().on("start", dragLegendBox));
}
@ -24,7 +28,11 @@ function clicked() {
if (grand.id === "emblems") editEmblem();
else if (parent.id === "rivers") editRiver(el.id);
else if (grand.id === "routes") editRoute();
else if (el.tagName === "tspan" && grand.parentNode.parentNode.id === "labels") editLabel();
else if (
el.tagName === "tspan" &&
grand.parentNode.parentNode.id === "labels"
)
editLabel();
else if (grand.id === "burgLabels") editBurg();
else if (grand.id === "burgIcons") editBurg();
else if (parent.id === "ice") editIce();
@ -71,7 +79,8 @@ function moveCircle(x, y, r = 20) {
}
function removeCircle() {
if (document.getElementById("brushCircle")) document.getElementById("brushCircle").remove();
if (document.getElementById("brushCircle"))
document.getElementById("brushCircle").remove();
}
// get browser-defined fit-content
@ -92,8 +101,8 @@ function sortLines(header) {
if (!header.className.includes("icon-sort") && type === "name") order = "-up";
const headers = header.parentNode;
headers.querySelectorAll("div.sortable").forEach(e => {
e.classList.forEach(c => {
headers.querySelectorAll("div.sortable").forEach((e) => {
e.classList.forEach((c) => {
if (c.includes("icon-sort")) e.classList.remove(c);
});
});
@ -116,7 +125,7 @@ function applySorting(headers) {
const bn = name ? b.dataset[sortby] : +b.dataset[sortby];
return (an > bn ? 1 : an < bn ? -1 : 0) * desc;
})
.forEach(line => list.appendChild(line));
.forEach((line) => list.appendChild(line));
}
function addBurg(point) {
@ -131,7 +140,10 @@ function addBurg(point) {
const feature = cells.f[cell];
const temple = pack.states[state].form === "Theocracy";
const population = Math.max((cells.s[cell] + cells.road[cell]) / 3 + i / 1000 + (cell % 100) / 1000, 0.1);
const population = Math.max(
(cells.s[cell] + cells.road[cell]) / 3 + i / 1000 + (cell % 100) / 1000,
0.1
);
const type = BurgsAndStates.getType(cell, false);
// generate emblem
@ -139,7 +151,22 @@ function addBurg(point) {
coa.shield = COA.getShield(culture, state);
COArenderer.add("burg", i, coa, x, y);
pack.burgs.push({name, cell, x, y, state, i, culture, feature, capital: 0, port: 0, temple, population, coa, type});
pack.burgs.push({
name,
cell,
x,
y,
state,
i,
culture,
feature,
capital: 0,
port: 0,
temple,
population,
coa,
type,
});
cells.burg[cell] = i;
const townSize = burgIcons.select("#towns").attr("size") || 0.5;
@ -219,7 +246,11 @@ function toggleCapital(burg) {
return;
}
if (pack.burgs[burg].capital) {
tip("To change capital please assign a capital status to another burg of this state", false, "error");
tip(
"To change capital please assign a capital status to another burg of this state",
false,
"error"
);
return;
}
const old = pack.states[state].capital;
@ -244,7 +275,12 @@ function togglePort(burg) {
const haven = pack.cells.haven[b.cell];
const port = haven ? pack.cells.f[haven] : -1;
if (!haven) tip("Port haven is not found, system won't be able to make a searoute", false, "warn");
if (!haven)
tip(
"Port haven is not found, system won't be able to make a searoute",
false,
"warn"
);
b.port = port;
const g = b.capital ? "cities" : "towns";
@ -281,13 +317,22 @@ function drawLegend(name, data) {
const vOffset = fontSize / 2;
// append items
const boxes = legend.append("g").attr("stroke-width", 0.5).attr("stroke", "#111111").attr("stroke-dasharray", "none");
const labels = legend.append("g").attr("fill", "#000000").attr("stroke", "none");
const boxes = legend
.append("g")
.attr("stroke-width", 0.5)
.attr("stroke", "#111111")
.attr("stroke-dasharray", "none");
const labels = legend
.append("g")
.attr("fill", "#000000")
.attr("stroke", "none");
const columns = Math.ceil(data.length / itemsInCol);
for (let column = 0, i = 0; column < columns; column++) {
const linesInColumn = Math.ceil(data.length / columns);
const offset = column ? colOffset * 2 + legend.node().getBBox().width : colOffset;
const offset = column
? colOffset * 2 + legend.node().getBBox().width
: colOffset;
for (let l = 0; l < linesInColumn && data[i]; l++, i++) {
boxes
@ -354,7 +399,7 @@ function redrawLegend() {
const data = legend
.attr("data")
.split("|")
.map(l => l.split(","));
.map((l) => l.split(","));
drawLegend(name, data);
}
@ -383,7 +428,12 @@ function createPicker() {
const cl = () => tip("Click to close the picker");
const closePicker = () => contaiter.style("display", "none");
const contaiter = d3.select("body").append("svg").attr("id", "pickerContainer").attr("width", "100%").attr("height", "100%");
const contaiter = d3
.select("body")
.append("svg")
.attr("id", "pickerContainer")
.attr("width", "100%")
.attr("height", "100%");
contaiter
.append("rect")
.attr("x", 0)
@ -407,19 +457,39 @@ function createPicker() {
const h = controls.append("g");
h.append("text").attr("x", 4).attr("y", 14).text("H:");
h.append("line").attr("x1", 18).attr("y1", 10).attr("x2", 107).attr("y2", 10);
h.append("circle").attr("cx", 75).attr("cy", 10).attr("r", 5).attr("id", "pickerH");
h.append("circle")
.attr("cx", 75)
.attr("cy", 10)
.attr("r", 5)
.attr("id", "pickerH");
h.on("mousemove", () => tip("Set palette hue"));
const s = controls.append("g");
s.append("text").attr("x", 113).attr("y", 14).text("S:");
s.append("line").attr("x1", 124).attr("y1", 10).attr("x2", 206).attr("y2", 10);
s.append("circle").attr("cx", 181.4).attr("cy", 10).attr("r", 5).attr("id", "pickerS");
s.append("line")
.attr("x1", 124)
.attr("y1", 10)
.attr("x2", 206)
.attr("y2", 10);
s.append("circle")
.attr("cx", 181.4)
.attr("cy", 10)
.attr("r", 5)
.attr("id", "pickerS");
s.on("mousemove", () => tip("Set palette saturation"));
const l = controls.append("g");
l.append("text").attr("x", 213).attr("y", 14).text("L:");
l.append("line").attr("x1", 226).attr("y1", 10).attr("x2", 306).attr("y2", 10);
l.append("circle").attr("cx", 282).attr("cy", 10).attr("r", 5).attr("id", "pickerL");
l.append("line")
.attr("x1", 226)
.attr("y1", 10)
.attr("x2", 306)
.attr("y2", 10);
l.append("circle")
.attr("cx", 282)
.attr("cy", 10)
.attr("r", 5)
.attr("id", "pickerL");
l.on("mousemove", () => tip("Set palette lightness"));
controls.selectAll("line").on("click", clickPickerControl);
@ -432,7 +502,9 @@ function createPicker() {
.attr("y", 20)
.attr("width", 303)
.attr("height", 20)
.on("mousemove", () => tip("Color value in different color spaces. Edit to change"));
.on("mousemove", () =>
tip("Color value in different color spaces. Edit to change")
);
const html = `
<label style="margin-right: 6px">HSL:
<input type="number" id="pickerHSL_H" data-space="hsl" min=0 max=360 value="231">,
@ -448,12 +520,20 @@ function createPicker() {
spaces.node().insertAdjacentHTML("beforeend", html);
spaces.selectAll("input").on("change", changePickerSpace);
const colors = picker.append("g").attr("id", "pickerColors").attr("stroke", "#333333");
const hatches = picker.append("g").attr("id", "pickerHatches").attr("stroke", "#333333");
const colors = picker
.append("g")
.attr("id", "pickerColors")
.attr("stroke", "#333333");
const hatches = picker
.append("g")
.attr("id", "pickerHatches")
.attr("stroke", "#333333");
const hatching = d3.selectAll("g#hatching > pattern");
const number = hatching.size();
const clr = d3.range(number).map(i => d3.hsl((i / number) * 360, 0.7, 0.7).hex());
const clr = d3
.range(number)
.map((i) => d3.hsl((i / number) * 360, 0.7, 0.7).hex());
clr.forEach(function (d, i) {
colors
.append("rect")
@ -500,7 +580,12 @@ function createPicker() {
.attr("fill", "#ffffff")
.attr("stroke", "#5d4651")
.on("mousemove", pos);
picker.insert("text", ":first-child").attr("x", 291).attr("y", -10).attr("id", "pickerCloseText").text("✕");
picker
.insert("text", ":first-child")
.attr("x", 291)
.attr("y", -10)
.attr("id", "pickerCloseText")
.text("✕");
picker
.insert("rect", ":first-child")
.attr("x", 288)
@ -510,13 +595,32 @@ function createPicker() {
.attr("height", 14)
.on("mousemove", cl)
.on("click", closePicker);
picker.insert("text", ":first-child").attr("x", 12).attr("y", -10).attr("id", "pickerLabel").text("Color Picker").on("mousemove", pos);
picker.insert("rect", ":first-child").attr("x", 0).attr("y", -30).attr("width", width).attr("height", 30).attr("id", "pickerHeader").on("mousemove", pos);
picker.attr("transform", `translate(${(svgWidth - width) / 2},${(svgHeight - height) / 2})`);
picker
.insert("text", ":first-child")
.attr("x", 12)
.attr("y", -10)
.attr("id", "pickerLabel")
.text("Color Picker")
.on("mousemove", pos);
picker
.insert("rect", ":first-child")
.attr("x", 0)
.attr("y", -30)
.attr("width", width)
.attr("height", 30)
.attr("id", "pickerHeader")
.on("mousemove", pos);
picker.attr(
"transform",
`translate(${(svgWidth - width) / 2},${(svgHeight - height) / 2})`
);
}
function updateSelectedRect(fill) {
document.getElementById("picker").querySelector("rect.selected").classList.remove("selected");
document
.getElementById("picker")
.querySelector("rect.selected")
.classList.remove("selected");
document
.getElementById("picker")
.querySelector("rect[fill='" + fill.toLowerCase() + "']")
@ -574,7 +678,9 @@ function openPicker(fill, callback) {
updateSelectedRect(fill);
openPicker.updateFill = function () {
const selected = document.getElementById("picker").querySelector("rect.selected");
const selected = document
.getElementById("picker")
.querySelector("rect.selected");
if (!selected) return;
callback(selected.getAttribute("fill"));
};
@ -649,8 +755,15 @@ function changePickerSpace() {
}
const space = this.dataset.space;
const i = Array.from(this.parentNode.querySelectorAll("input")).map(input => input.value); // inputs
const fill = space === "hex" ? d3.rgb(this.value) : space === "rgb" ? d3.rgb(i[0], i[1], i[2]) : d3.hsl(i[0], i[1] / 100, i[2] / 100);
const i = Array.from(this.parentNode.querySelectorAll("input")).map(
(input) => input.value
); // inputs
const fill =
space === "hex"
? d3.rgb(this.value)
: space === "rgb"
? d3.rgb(i[0], i[1], i[2])
: d3.hsl(i[0], i[1] / 100, i[2] / 100);
const hsl = d3.hsl(fill);
if (isNaN(hsl.l)) {
@ -671,11 +784,27 @@ function fog(id, path) {
if (defs.select("#fog #" + id).size()) return;
const fadeIn = d3.transition().duration(2000).ease(d3.easeSinInOut);
if (defs.select("#fog path").size()) {
defs.select("#fog").append("path").attr("d", path).attr("id", id).attr("opacity", 0).transition(fadeIn).attr("opacity", 1);
defs
.select("#fog")
.append("path")
.attr("d", path)
.attr("id", id)
.attr("opacity", 0)
.transition(fadeIn)
.attr("opacity", 1);
} else {
defs.select("#fog").append("path").attr("d", path).attr("id", id).attr("opacity", 1);
defs
.select("#fog")
.append("path")
.attr("d", path)
.attr("id", id)
.attr("opacity", 1);
const opacity = fogging.attr("opacity");
fogging.style("display", "block").attr("opacity", 0).transition(fadeIn).attr("opacity", opacity);
fogging
.style("display", "block")
.attr("opacity", 0)
.transition(fadeIn)
.attr("opacity", opacity);
}
}
@ -689,7 +818,7 @@ function unfog(id) {
}
function getFileName(dataType) {
const formatTime = time => (time < 10 ? "0" + time : time);
const formatTime = (time) => (time < 10 ? "0" + time : time);
const name = mapName.value;
const type = dataType ? dataType + " " : "";
const date = new Date();
@ -699,11 +828,13 @@ function getFileName(dataType) {
const hour = formatTime(date.getHours());
const minutes = formatTime(date.getMinutes());
const dateString = [year, month, day, hour, minutes].join("-");
return name + " " + type + dateString;
//return name + " " + type + dateString;
return name;
}
function downloadFile(data, name, type = "text/plain") {
const dataBlob = new Blob([data], {type});
const dataBlob = new Blob([data], { type });
const url = window.URL.createObjectURL(dataBlob);
const link = document.createElement("a");
link.download = name;
@ -716,7 +847,7 @@ function uploadFile(el, callback) {
const fileReader = new FileReader();
fileReader.readAsText(el.files[0], "UTF-8");
el.value = "";
fileReader.onload = loaded => callback(loaded.target.result);
fileReader.onload = (loaded) => callback(loaded.target.result);
}
function getBBox(element) {
@ -724,7 +855,7 @@ function getBBox(element) {
const y = +element.getAttribute("y");
const width = +element.getAttribute("width");
const height = +element.getAttribute("height");
return {x, y, width, height};
return { x, y, width, height };
}
function highlightElement(element, zoom) {
@ -734,9 +865,20 @@ function highlightElement(element, zoom) {
const enter = d3.transition().duration(1000).ease(d3.easeBounceOut);
const exit = d3.transition().duration(500).ease(d3.easeLinear);
const highlight = debug.append("rect").attr("x", box.x).attr("y", box.y).attr("width", box.width).attr("height", box.height);
const highlight = debug
.append("rect")
.attr("x", box.x)
.attr("y", box.y)
.attr("width", box.width)
.attr("height", box.height);
highlight.classed("highlighted", 1).attr("transform", transform);
highlight.transition(enter).style("outline-offset", "0px").transition(exit).style("outline-color", "transparent").delay(1000).remove();
highlight
.transition(enter)
.style("outline-offset", "0px")
.transition(exit)
.style("outline-color", "transparent")
.delay(1000)
.remove();
if (zoom) {
const tr = parseTransform(transform);
@ -944,7 +1086,7 @@ function selectIcon(initial, callback) {
"🍻",
"🍺",
"🍲",
"🍷"
"🍷",
];
let row = "";
@ -955,15 +1097,16 @@ function selectIcon(initial, callback) {
}
}
input.oninput = e => callback(input.value);
table.onclick = e => {
input.oninput = (e) => callback(input.value);
table.onclick = (e) => {
if (e.target.tagName === "TD") {
input.value = e.target.innerHTML;
callback(input.value);
}
};
table.onmouseover = e => {
if (e.target.tagName === "TD") tip(`Click to select ${e.target.innerHTML} icon`);
table.onmouseover = (e) => {
if (e.target.tagName === "TD")
tip(`Click to select ${e.target.innerHTML} icon`);
};
$("#iconSelector").dialog({
@ -977,8 +1120,8 @@ function selectIcon(initial, callback) {
Close: function () {
callback(initial);
$(this).dialog("close");
}
}
},
},
});
}
@ -989,7 +1132,7 @@ function confirmationDialog(options) {
cancel = "Cancel",
confirm = "Continue",
onCancel,
onConfirm
onConfirm,
} = options;
const buttons = {
@ -1000,11 +1143,11 @@ function confirmationDialog(options) {
[cancel]: function () {
if (onCancel) onCancel();
$(this).dialog("close");
}
},
};
document.getElementById("alertMessage").innerHTML = message;
$("#alert").dialog({resizable: false, title, buttons});
$("#alert").dialog({ resizable: false, title, buttons });
}
// add and register event listeners to clean up on editor closure
@ -1016,12 +1159,19 @@ function listen(element, event, handler) {
// Calls the refresh functionality on all editors currently open.
function refreshAllEditors() {
TIME && console.time("refreshAllEditors");
if (document.getElementById("culturesEditorRefresh").offsetParent) culturesEditorRefresh.click();
if (document.getElementById("biomesEditorRefresh").offsetParent) biomesEditorRefresh.click();
if (document.getElementById("diplomacyEditorRefresh").offsetParent) diplomacyEditorRefresh.click();
if (document.getElementById("provincesEditorRefresh").offsetParent) provincesEditorRefresh.click();
if (document.getElementById("religionsEditorRefresh").offsetParent) religionsEditorRefresh.click();
if (document.getElementById("statesEditorRefresh").offsetParent) statesEditorRefresh.click();
if (document.getElementById("zonesEditorRefresh").offsetParent) zonesEditorRefresh.click();
if (document.getElementById("culturesEditorRefresh").offsetParent)
culturesEditorRefresh.click();
if (document.getElementById("biomesEditorRefresh").offsetParent)
biomesEditorRefresh.click();
if (document.getElementById("diplomacyEditorRefresh").offsetParent)
diplomacyEditorRefresh.click();
if (document.getElementById("provincesEditorRefresh").offsetParent)
provincesEditorRefresh.click();
if (document.getElementById("religionsEditorRefresh").offsetParent)
religionsEditorRefresh.click();
if (document.getElementById("statesEditorRefresh").offsetParent)
statesEditorRefresh.click();
if (document.getElementById("zonesEditorRefresh").offsetParent)
zonesEditorRefresh.click();
TIME && console.timeEnd("refreshAllEditors");
}

View file

@ -3,13 +3,18 @@
// fit full-screen map if window is resized
window.addEventListener("resize", function (e) {
if (localStorage.getItem("mapWidth") && localStorage.getItem("mapHeight")) return;
if (localStorage.getItem("mapWidth") && localStorage.getItem("mapHeight"))
return;
mapWidthInput.value = window.innerWidth;
mapHeightInput.value = window.innerHeight;
changeMapSize();
});
if (location.hostname && location.hostname !== "localhost" && location.hostname !== "127.0.0.1") {
if (
location.hostname &&
location.hostname !== "localhost" &&
location.hostname !== "127.0.0.1"
) {
window.onbeforeunload = () => "Are you sure you want to navigate away?";
}
@ -18,15 +23,26 @@ const tooltip = document.getElementById("tooltip");
// show tip for non-svg elemets with data-tip
document.getElementById("dialogs").addEventListener("mousemove", showDataTip);
document.getElementById("optionsContainer").addEventListener("mousemove", showDataTip);
document.getElementById("exitCustomization").addEventListener("mousemove", showDataTip);
document
.getElementById("optionsContainer")
.addEventListener("mousemove", showDataTip);
document
.getElementById("exitCustomization")
.addEventListener("mousemove", showDataTip);
function tip(tip = "Tip is undefined", main, type, time) {
tooltip.innerHTML = tip;
tooltip.style.background = "linear-gradient(0.1turn, #ffffff00, #5e5c5c80, #ffffff00)";
if (type === "error") tooltip.style.background = "linear-gradient(0.1turn, #ffffff00, #e11d1dcc, #ffffff00)";
else if (type === "warn") tooltip.style.background = "linear-gradient(0.1turn, #ffffff00, #be5d08cc, #ffffff00)";
else if (type === "success") tooltip.style.background = "linear-gradient(0.1turn, #ffffff00, #127912cc, #ffffff00)";
tooltip.style.background =
"linear-gradient(0.1turn, #ffffff00, #5e5c5c80, #ffffff00)";
if (type === "error")
tooltip.style.background =
"linear-gradient(0.1turn, #ffffff00, #e11d1dcc, #ffffff00)";
else if (type === "warn")
tooltip.style.background =
"linear-gradient(0.1turn, #ffffff00, #be5d08cc, #ffffff00)";
else if (type === "success")
tooltip.style.background =
"linear-gradient(0.1turn, #ffffff00, #127912cc, #ffffff00)";
if (main) {
tooltip.dataset.main = tip;
@ -50,7 +66,8 @@ function clearMainTip() {
function showDataTip(e) {
if (!e.target) return;
let dataTip = e.target.dataset.tip;
if (!dataTip && e.target.parentNode.dataset.tip) dataTip = e.target.parentNode.dataset.tip;
if (!dataTip && e.target.parentNode.dataset.tip)
dataTip = e.target.parentNode.dataset.tip;
if (!dataTip) return;
//const tooltip = lang === "en" ? dataTip : translate(e.target.dataset.t || e.target.parentNode.dataset.t, dataTip);
tip(dataTip);
@ -59,9 +76,13 @@ function showDataTip(e) {
function showElementLockTip(event) {
const locked = event?.target?.classList?.contains("icon-lock");
if (locked) {
tip("Click to unlock the element and allow it to be changed by regeneration tools");
tip(
"Click to unlock the element and allow it to be changed by regeneration tools"
);
} else {
tip("Click to lock the element and prevent changes to it by regeneration tools");
tip(
"Click to lock the element and prevent changes to it by regeneration tools"
);
}
}
@ -80,11 +101,14 @@ function mouseMove() {
// show note box on hover (if any)
function showNotes(e, i) {
if (notesEditor.offsetParent) return;
let id = e.target.id || e.target.parentNode.id || e.target.parentNode.parentNode.id;
if (e.target.parentNode.parentNode.id === "burgLabels") id = "burg" + e.target.dataset.id;
else if (e.target.parentNode.parentNode.id === "burgIcons") id = "burg" + e.target.dataset.id;
let id =
e.target.id || e.target.parentNode.id || e.target.parentNode.parentNode.id;
if (e.target.parentNode.parentNode.id === "burgLabels")
id = "burg" + e.target.dataset.id;
else if (e.target.parentNode.parentNode.id === "burgIcons")
id = "burg" + e.target.dataset.id;
const note = notes.find(note => note.id === id);
const note = notes.find((note) => note.id === id);
if (note !== undefined && note.legend !== "") {
document.getElementById("notes").style.display = "block";
document.getElementById("notesHeader").innerHTML = note.name;
@ -96,6 +120,34 @@ function showNotes(e, i) {
}
}
function createMetaTip(point, e, i, g) {
const biome = pack.cells.biome[i];
const religion = pack.cells.religion[i];
const r = pack.religions[religion];
const religionType =
r.type === "Cult" || r.type == "Heresy" ? r.type : r.type + " religion";
const state = pack.cells.state[i];
const stateCulture = pack.cells.state[i].culture;
const stateName = pack.states[state].fullName;
const province = pack.cells.province[i];
const prov = province ? pack.provinces[province].fullName + ", " : "";
const culture = pack.cells.culture[i];
tip("Culture: " + pack.cultures[culture].name);
let tipText =
pack.cultures[culture].name +
" | " +
religionType +
": " +
r.name +
" | " +
stateName +
" - " +
prov +
" | Biome: " +
biomesData.name[biome];
tip(tipText);
}
// show viewbox tooltip if main tooltip is blank
function showMapTooltip(point, e, i, g) {
tip(""); // clear tip
@ -106,12 +158,17 @@ function showMapTooltip(point, e, i, g) {
const land = pack.cells.h[i] >= 20;
// specific elements
if (group === "armies") return tip(e.target.parentNode.dataset.name + ". Click to edit");
if (group === "armies")
return tip(e.target.parentNode.dataset.name + ". Click to edit");
if (group === "emblems" && e.target.tagName === "use") {
const parent = e.target.parentNode;
const [g, type] =
parent.id === "burgEmblems" ? [pack.burgs, "burg"] : parent.id === "provinceEmblems" ? [pack.provinces, "province"] : [pack.states, "state"];
parent.id === "burgEmblems"
? [pack.burgs, "burg"]
: parent.id === "provinceEmblems"
? [pack.provinces, "province"]
: [pack.states, "state"];
const i = +e.target.dataset.i;
if (event.shiftKey) highlightEmblemElement(type, g[i]);
@ -119,16 +176,19 @@ function showMapTooltip(point, e, i, g) {
d3.select(parent).raise();
const name = g[i].fullName || g[i].name;
tip(`${name} ${type} emblem. Click to edit. Hold Shift to show associated area or place`);
tip(
`${name} ${type} emblem. Click to edit. Hold Shift to show associated area or place`
);
return;
}
if (group === "rivers") {
const river = +e.target.id.slice(5);
const r = pack.rivers.find(r => r.i === river);
const r = pack.rivers.find((r) => r.i === river);
const name = r ? r.name + " " + r.type : "";
tip(name + ". Click to edit");
if (riversOverview.offsetParent) highlightEditorLine(riversOverview, river, 5000);
if (riversOverview.offsetParent)
highlightEditorLine(riversOverview, river, 5000);
return;
}
@ -141,22 +201,31 @@ function showMapTooltip(point, e, i, g) {
const b = pack.burgs[burg];
const population = si(b.population * populationRate * urbanization);
tip(`${b.name}. Population: ${population}. Click to edit`);
if (burgsOverview.offsetParent) highlightEditorLine(burgsOverview, burg, 5000);
if (burgsOverview.offsetParent)
highlightEditorLine(burgsOverview, burg, 5000);
return;
}
if (group === "labels") return tip("Click to edit the Label");
if (group === "markers") return tip("Click to edit the Marker and pin the marker note");
if (group === "markers")
return tip("Click to edit the Marker and pin the marker note");
if (group === "ruler") {
const tag = e.target.tagName;
const className = e.target.getAttribute("class");
if (tag === "circle" && className === "edge") return tip("Drag to adjust. Hold Ctrl and drag to add a point. Click to remove the point");
if (tag === "circle" && className === "control") return tip("Drag to adjust. Hold Shift and drag to keep axial direction. Click to remove the point");
if (tag === "circle" && className === "edge")
return tip(
"Drag to adjust. Hold Ctrl and drag to add a point. Click to remove the point"
);
if (tag === "circle" && className === "control")
return tip(
"Drag to adjust. Hold Shift and drag to keep axial direction. Click to remove the point"
);
if (tag === "circle") return tip("Drag to adjust the measurer");
if (tag === "polyline") return tip("Click on drag to add a control point");
if (tag === "path") return tip("Drag to move the measurer");
if (tag === "text") return tip("Drag to move, click to remove the measurer");
if (tag === "text")
return tip("Drag to move, click to remove the measurer");
}
if (subgroup === "burgIcons") return tip("Click to edit the Burg");
@ -175,46 +244,55 @@ function showMapTooltip(point, e, i, g) {
if (group === "zones") {
const zone = path[path.length - 8];
tip(zone.dataset.description);
if (zonesEditor.offsetParent) highlightEditorLine(zonesEditor, zone.id, 5000);
if (zonesEditor.offsetParent)
highlightEditorLine(zonesEditor, zone.id, 5000);
return;
}
if (group === "ice") return tip("Click to edit the Ice");
// covering elements
if (layerIsOn("togglePrec") && land) tip("Annual Precipitation: " + getFriendlyPrecipitation(i));
if (layerIsOn("togglePrec") && land)
tip("Annual Precipitation: " + getFriendlyPrecipitation(i));
else if (layerIsOn("togglePopulation")) tip(getPopulationTip(i));
else if (layerIsOn("toggleTemp")) tip("Temperature: " + convertTemperature(grid.cells.temp[g]));
else if (layerIsOn("toggleTemp"))
tip("Temperature: " + convertTemperature(grid.cells.temp[g]));
else if (layerIsOn("toggleBiomes") && pack.cells.biome[i]) {
const biome = pack.cells.biome[i];
tip("Biome: " + biomesData.name[biome]);
createMetaTip(point, e, i, g);
if (biomesEditor.offsetParent) highlightEditorLine(biomesEditor, biome);
} else if (layerIsOn("toggleReligions") && pack.cells.religion[i]) {
const religion = pack.cells.religion[i];
const r = pack.religions[religion];
const type = r.type === "Cult" || r.type == "Heresy" ? r.type : r.type + " religion";
tip(type + ": " + r.name);
if (religionsEditor.offsetParent) highlightEditorLine(religionsEditor, religion);
} else if (pack.cells.state[i] && (layerIsOn("toggleProvinces") || layerIsOn("toggleStates"))) {
const state = pack.cells.state[i];
const stateName = pack.states[state].fullName;
const province = pack.cells.province[i];
const prov = province ? pack.provinces[province].fullName + ", " : "";
tip(prov + stateName);
createMetaTip(point, e, i, g);
if (religionsEditor.offsetParent)
highlightEditorLine(religionsEditor, religion);
} else if (
pack.cells.state[i] &&
(layerIsOn("toggleProvinces") || layerIsOn("toggleStates"))
) {
createMetaTip(point, e, i, g);
if (statesEditor.offsetParent) highlightEditorLine(statesEditor, state);
if (diplomacyEditor.offsetParent) highlightEditorLine(diplomacyEditor, state);
if (militaryOverview.offsetParent) highlightEditorLine(militaryOverview, state);
if (provincesEditor.offsetParent) highlightEditorLine(provincesEditor, province);
if (diplomacyEditor.offsetParent)
highlightEditorLine(diplomacyEditor, state);
if (militaryOverview.offsetParent)
highlightEditorLine(militaryOverview, state);
if (provincesEditor.offsetParent)
highlightEditorLine(provincesEditor, province);
} else if (layerIsOn("toggleCultures") && pack.cells.culture[i]) {
const culture = pack.cells.culture[i];
tip("Culture: " + pack.cultures[culture].name);
if (culturesEditor.offsetParent) highlightEditorLine(culturesEditor, culture);
} else if (layerIsOn("toggleHeight")) tip("Height: " + getFriendlyHeight(point));
createMetaTip(point, e, i, g);
if (culturesEditor.offsetParent) {
highlightEditorLine(culturesEditor, culture);
}
} else if (layerIsOn("toggleHeight"))
tip("Height: " + getFriendlyHeight(point));
}
function highlightEditorLine(editor, id, timeout = 15000) {
Array.from(editor.getElementsByClassName("states hovered")).forEach(el => el.classList.remove("hovered")); // clear all hovered
const hovered = Array.from(editor.querySelectorAll("div")).find(el => el.dataset.id == id);
Array.from(editor.getElementsByClassName("states hovered")).forEach((el) =>
el.classList.remove("hovered")
); // clear all hovered
const hovered = Array.from(editor.querySelectorAll("div")).find(
(el) => el.dataset.id == id
);
if (hovered) hovered.classList.add("hovered"); // add hovered class
if (timeout)
setTimeout(() => {
@ -228,23 +306,48 @@ function updateCellInfo(point, i, g) {
const x = (infoX.innerHTML = rn(point[0]));
const y = (infoY.innerHTML = rn(point[1]));
const f = cells.f[i];
infoLat.innerHTML = toDMS(mapCoordinates.latN - (y / graphHeight) * mapCoordinates.latT, "lat");
infoLon.innerHTML = toDMS(mapCoordinates.lonW + (x / graphWidth) * mapCoordinates.lonT, "lon");
infoLat.innerHTML = toDMS(
mapCoordinates.latN - (y / graphHeight) * mapCoordinates.latT,
"lat"
);
infoLon.innerHTML = toDMS(
mapCoordinates.lonW + (x / graphWidth) * mapCoordinates.lonT,
"lon"
);
infoCell.innerHTML = i;
const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value;
infoArea.innerHTML = cells.area[i] ? si(cells.area[i] * distanceScaleInput.value ** 2) + unit : "n/a";
const unit =
areaUnit.value === "square"
? " " + distanceUnitInput.value + "²"
: " " + areaUnit.value;
infoArea.innerHTML = cells.area[i]
? si(cells.area[i] * distanceScaleInput.value ** 2) + unit
: "n/a";
infoEvelation.innerHTML = getElevation(pack.features[f], pack.cells.h[i]);
infoDepth.innerHTML = getDepth(pack.features[f], pack.cells.h[i], point);
infoTemp.innerHTML = convertTemperature(grid.cells.temp[g]);
infoPrec.innerHTML = cells.h[i] >= 20 ? getFriendlyPrecipitation(i) : "n/a";
infoRiver.innerHTML = cells.h[i] >= 20 && cells.r[i] ? getRiverInfo(cells.r[i]) : "no";
infoState.innerHTML = cells.h[i] >= 20 ? (cells.state[i] ? `${pack.states[cells.state[i]].fullName} (${cells.state[i]})` : "neutral lands (0)") : "no";
infoProvince.innerHTML = cells.province[i] ? `${pack.provinces[cells.province[i]].fullName} (${cells.province[i]})` : "no";
infoCulture.innerHTML = cells.culture[i] ? `${pack.cultures[cells.culture[i]].name} (${cells.culture[i]})` : "no";
infoReligion.innerHTML = cells.religion[i] ? `${pack.religions[cells.religion[i]].name} (${cells.religion[i]})` : "no";
infoRiver.innerHTML =
cells.h[i] >= 20 && cells.r[i] ? getRiverInfo(cells.r[i]) : "no";
infoState.innerHTML =
cells.h[i] >= 20
? cells.state[i]
? `${pack.states[cells.state[i]].fullName} (${cells.state[i]})`
: "neutral lands (0)"
: "no";
infoProvince.innerHTML = cells.province[i]
? `${pack.provinces[cells.province[i]].fullName} (${cells.province[i]})`
: "no";
infoCulture.innerHTML = cells.culture[i]
? `${pack.cultures[cells.culture[i]].name} (${cells.culture[i]})`
: "no";
infoReligion.innerHTML = cells.religion[i]
? `${pack.religions[cells.religion[i]].name} (${cells.religion[i]})`
: "no";
infoPopulation.innerHTML = getFriendlyPopulation(i);
infoBurg.innerHTML = cells.burg[i] ? pack.burgs[cells.burg[i]].name + " (" + cells.burg[i] + ")" : "no";
infoBurg.innerHTML = cells.burg[i]
? pack.burgs[cells.burg[i]].name + " (" + cells.burg[i] + ")"
: "no";
infoFeature.innerHTML = f ? pack.features[f].group + " (" + f + ")" : "n/a";
infoBiome.innerHTML = biomesData.name[cells.biome[i]];
}
@ -255,7 +358,8 @@ function toDMS(coord, c) {
const minutesNotTruncated = (Math.abs(coord) - degrees) * 60;
const minutes = Math.floor(minutesNotTruncated);
const seconds = Math.floor((minutesNotTruncated - minutes) * 60);
const cardinal = c === "lat" ? (coord >= 0 ? "N" : "S") : coord >= 0 ? "E" : "W";
const cardinal =
c === "lat" ? (coord >= 0 ? "N" : "S") : coord >= 0 ? "E" : "W";
return degrees + "° " + minutes + " " + seconds + "″ " + cardinal;
}
@ -310,13 +414,15 @@ function getFriendlyPrecipitation(i) {
}
function getRiverInfo(id) {
const r = pack.rivers.find(r => r.i == id);
const r = pack.rivers.find((r) => r.i == id);
return r ? `${r.name} ${r.type} (${id})` : "n/a";
}
function getCellPopulation(i) {
const rural = pack.cells.pop[i] * populationRate;
const urban = pack.cells.burg[i] ? pack.burgs[pack.cells.burg[i]].population * populationRate * urbanization : 0;
const urban = pack.cells.burg[i]
? pack.burgs[pack.cells.burg[i]].population * populationRate * urbanization
: 0;
return [rural, urban];
}
@ -328,7 +434,9 @@ function getFriendlyPopulation(i) {
function getPopulationTip(i) {
const [rural, urban] = getCellPopulation(i);
return `Cell population: ${si(rural + urban)}; Rural: ${si(rural)}; Urban: ${si(urban)}`;
return `Cell population: ${si(rural + urban)}; Rural: ${si(
rural
)}; Urban: ${si(urban)}`;
}
function highlightEmblemElement(type, el) {
@ -337,7 +445,7 @@ function highlightEmblemElement(type, el) {
const animation = d3.transition().duration(1000).ease(d3.easeSinIn);
if (type === "burg") {
const {x, y} = el;
const { x, y } = el;
debug
.append("circle")
.attr("cx", x)
@ -357,11 +465,13 @@ function highlightEmblemElement(type, el) {
const [x, y] = el.pole || pack.cells.p[el.center];
const obj = type === "state" ? cells.state : cells.province;
const borderCells = cells.i.filter(id => obj[id] === i && cells.c[id].some(n => obj[n] !== i));
const borderCells = cells.i.filter(
(id) => obj[id] === i && cells.c[id].some((n) => obj[n] !== i)
);
const data = Array.from(borderCells)
.filter((c, i) => !(i % 2))
.map(i => cells.p[i])
.map(i => [i[0], i[1], Math.hypot(i[0] - x, i[1] - y)]);
.map((i) => cells.p[i])
.map((i) => [i[0], i[1], Math.hypot(i[0] - x, i[1] - y)]);
debug
.selectAll("line")
@ -370,19 +480,19 @@ function highlightEmblemElement(type, el) {
.append("line")
.attr("x1", x)
.attr("y1", y)
.attr("x2", d => d[0])
.attr("y2", d => d[1])
.attr("x2", (d) => d[0])
.attr("y2", (d) => d[1])
.attr("stroke", "#d0240f")
.attr("stroke-width", 0.5)
.attr("opacity", 0.2)
.attr("stroke-dashoffset", d => d[2])
.attr("stroke-dasharray", d => d[2])
.attr("stroke-dashoffset", (d) => d[2])
.attr("stroke-dasharray", (d) => d[2])
.transition(animation)
.attr("stroke-dashoffset", 0)
.attr("opacity", 1)
.transition(animation)
.delay(1000)
.attr("stroke-dashoffset", d => d[2])
.attr("stroke-dashoffset", (d) => d[2])
.attr("opacity", 0)
.remove();
}
@ -390,8 +500,14 @@ function highlightEmblemElement(type, el) {
// assign lock behavior
document.querySelectorAll("[data-locked]").forEach(function (e) {
e.addEventListener("mouseover", function (event) {
if (this.className === "icon-lock") tip("Click to unlock the option and allow it to be randomized on new map generation");
else tip("Click to lock the option and always use the current value on new map generation");
if (this.className === "icon-lock")
tip(
"Click to unlock the option and allow it to be randomized on new map generation"
);
else
tip(
"Click to lock the option and always use the current value on new map generation"
);
event.stopPropagation();
});
@ -433,7 +549,7 @@ function stored(option) {
}
// assign skeaker behaviour
Array.from(document.getElementsByClassName("speaker")).forEach(el => {
Array.from(document.getElementsByClassName("speaker")).forEach((el) => {
const input = el.previousElementSibling;
el.addEventListener("click", () => speak(input.value));
});
@ -450,7 +566,7 @@ function speak(text) {
// apply drop-down menu option. If the value is not in options, add it
function applyOption(select, id, name = id) {
const custom = !Array.from(select.options).some(o => o.value == id);
const custom = !Array.from(select.options).some((o) => o.value == id);
if (custom) select.options.add(new Option(name, id));
select.value = id;
}
@ -460,11 +576,20 @@ function showInfo() {
const Discord = link("https://discordapp.com/invite/X7E84HU", "Discord");
const Reddit = link("https://www.reddit.com/r/FantasyMapGenerator", "Reddit");
const Patreon = link("https://www.patreon.com/azgaar", "Patreon");
const Trello = link("https://trello.com/b/7x832DG4/fantasy-map-generator", "Trello");
const Trello = link(
"https://trello.com/b/7x832DG4/fantasy-map-generator",
"Trello"
);
const Armoria = link("https://azgaar.github.io/Armoria", "Armoria");
const QuickStart = link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Quick-Start-Tutorial", "Quick start tutorial");
const QAA = link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Q&A", "Q&A page");
const QuickStart = link(
"https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Quick-Start-Tutorial",
"Quick start tutorial"
);
const QAA = link(
"https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Q&A",
"Q&A page"
);
alertMessage.innerHTML = `
<b>Fantasy Map Generator</b> (FMG) is an open-source application, it means the code is published an anyone can use it.
@ -482,10 +607,22 @@ function showInfo() {
<b>Links:</b>
<ul style="columns:2">
<li>${link("https://github.com/Azgaar/Fantasy-Map-Generator", "GitHub repository")}</li>
<li>${link("https://github.com/Azgaar/Fantasy-Map-Generator/blob/master/LICENSE", "License")}</li>
<li>${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog", "Changelog")}</li>
<li>${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Hotkeys", "Hotkeys")}</li>
<li>${link(
"https://github.com/Azgaar/Fantasy-Map-Generator",
"GitHub repository"
)}</li>
<li>${link(
"https://github.com/Azgaar/Fantasy-Map-Generator/blob/master/LICENSE",
"License"
)}</li>
<li>${link(
"https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog",
"Changelog"
)}</li>
<li>${link(
"https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Hotkeys",
"Hotkeys"
)}</li>
</ul>`;
$("#alert").dialog({
@ -495,8 +632,8 @@ function showInfo() {
buttons: {
OK: function () {
$(this).dialog("close");
}
},
},
position: {my: "center", at: "center", of: "svg"}
position: { my: "center", at: "center", of: "svg" },
});
}

File diff suppressed because it is too large Load diff