Merge branch 'master' of https://github.com/Azgaar/Fantasy-Map-Generator into ai-assistant

This commit is contained in:
Azgaar 2024-09-22 13:24:42 +02:00
commit dadb0cad8c
61 changed files with 2831 additions and 2928 deletions

View file

@ -373,7 +373,7 @@ window.ThreeD = (function () {
}
// icons
if (layerIsOn("toggleIcons")) {
if (layerIsOn("toggleBurgIcons")) {
const geometry = isCity ? city_icon_geometry : town_icon_geometry;
const material = isCity ? city_icon_material : town_icon_material;
const iconMesh = new THREE.Mesh(geometry, material);
@ -444,6 +444,7 @@ window.ThreeD = (function () {
const url = await getMapURL("mesh", {
noLabels: options.labels3d,
noWater: options.extendedWater,
noViewbox: true,
fullMap: true
});
const canvas = document.createElement("canvas");
@ -623,7 +624,7 @@ window.ThreeD = (function () {
material.map = texture;
if (addMesh) addGlobe3dMesh();
};
img2.src = await getMapURL("mesh", {noScaleBar: true, fullMap: true});
img2.src = await getMapURL("mesh", {noScaleBar: true, fullMap: true, noVignette: true});
}
function addGlobe3dMesh() {

View file

@ -277,7 +277,7 @@ class Battle {
const shift = side === "attackers" ? attackers.length * -8 : (defenders.length - 1) * 8;
regiment.px = regiment.x;
regiment.py = regiment.y;
Military.moveRegiment(regiment, defenders[0].x, defenders[0].y + shift);
moveRegiment(regiment, defenders[0].x, defenders[0].y + shift);
});
}
@ -909,7 +909,7 @@ class Battle {
cancelResults() {
// move regiments back to initial positions
this.attackers.regiments.concat(this.defenders.regiments).forEach(r => Military.moveRegiment(r, r.px, r.py));
this.attackers.regiments.concat(this.defenders.regiments).forEach(r => moveRegiment(r, r.px, r.py));
$("#battleScreen").dialog("close");
this.cleanData();
}

View file

@ -2,7 +2,7 @@
function editBurg(id) {
if (customization) return;
closeDialogs(".stable");
if (!layerIsOn("toggleIcons")) toggleIcons();
if (!layerIsOn("toggleBurgIcons")) toggleBurgIcons();
if (!layerIsOn("toggleLabels")) toggleLabels();
const burg = id || d3.event.target.dataset.id;
@ -47,6 +47,7 @@ function editBurg(id) {
byId("burgEmblem").addEventListener("click", openEmblemEdit);
byId("burgTogglePreview").addEventListener("click", toggleBurgPreview);
byId("burgEditEmblem").addEventListener("click", openEmblemEdit);
byId("burgLocate").addEventListener("click", zoomIntoBurg);
byId("burgRelocate").addEventListener("click", toggleRelocateBurg);
byId("burglLegend").addEventListener("click", editBurgLegend);
byId("burgLock").addEventListener("click", toggleBurgLockButton);
@ -397,6 +398,14 @@ function editBurg(id) {
byId("burgTogglePreview").className = options.showBurgPreview ? "icon-map" : "icon-map-o";
}
function zoomIntoBurg() {
const id = +elSelected.attr("data-id");
const burg = pack.burgs[id];
const x = burg.x;
const y = burg.y;
zoomTo(x, y, 8, 2000);
}
function toggleRelocateBurg() {
const toggler = byId("toggleCells");
byId("burgRelocate").classList.toggle("pressed");

View file

@ -2,7 +2,7 @@
function overviewBurgs(settings = {stateId: null, cultureId: null}) {
if (customization) return;
closeDialogs("#burgsOverview, .stable");
if (!layerIsOn("toggleIcons")) toggleIcons();
if (!layerIsOn("toggleBurgIcons")) toggleBurgIcons();
if (!layerIsOn("toggleLabels")) toggleLabels();
const body = byId("burgsBody");
@ -154,9 +154,9 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) {
}
function burgHighlightOn(event) {
if (!layerIsOn("toggleLabels")) toggleLabels();
const burg = +event.target.dataset.id;
burgLabels.select("[data-id='" + burg + "']").classed("drag", true);
const label = burgLabels.select("[data-id='" + burg + "']");
if (label.size()) label.classed("drag", true);
}
function burgHighlightOff() {

View file

@ -1,5 +1,6 @@
"use strict";
function editCoastline(node = d3.event.target) {
function editCoastline() {
if (customization) return;
closeDialogs(".stable");
if (layerIsOn("toggleCells")) toggleCells();
@ -12,6 +13,7 @@ function editCoastline(node = d3.event.target) {
});
debug.append("g").attr("id", "vertices");
const node = d3.event.target;
elSelected = d3.select(node);
selectCoastlineGroup(node);
drawCoastlineVertices();
@ -21,93 +23,98 @@ function editCoastline(node = d3.event.target) {
modules.editCoastline = true;
// add listeners
document.getElementById("coastlineGroupsShow").addEventListener("click", showGroupSection);
document.getElementById("coastlineGroup").addEventListener("change", changeCoastlineGroup);
document.getElementById("coastlineGroupAdd").addEventListener("click", toggleNewGroupInput);
document.getElementById("coastlineGroupName").addEventListener("change", createNewGroup);
document.getElementById("coastlineGroupRemove").addEventListener("click", removeCoastlineGroup);
document.getElementById("coastlineGroupsHide").addEventListener("click", hideGroupSection);
document.getElementById("coastlineEditStyle").addEventListener("click", editGroupStyle);
byId("coastlineGroupsShow").on("click", showGroupSection);
byId("coastlineGroup").on("change", changeCoastlineGroup);
byId("coastlineGroupAdd").on("click", toggleNewGroupInput);
byId("coastlineGroupName").on("change", createNewGroup);
byId("coastlineGroupRemove").on("click", removeCoastlineGroup);
byId("coastlineGroupsHide").on("click", hideGroupSection);
byId("coastlineEditStyle").on("click", editGroupStyle);
function drawCoastlineVertices() {
const f = +elSelected.attr("data-f"); // feature id
const v = pack.features[f].vertices; // coastline outer vertices
const featureId = +elSelected.attr("data-f");
const {vertices, area} = pack.features[featureId];
const l = pack.cells.i.length;
const c = [...new Set(v.map(v => pack.vertices.c[v]).flat())].filter(c => c < l);
const cellsNumber = pack.cells.i.length;
const neibCells = unique(vertices.map(v => pack.vertices.c[v]).flat()).filter(cellId => cellId < cellsNumber);
debug
.select("#vertices")
.selectAll("polygon")
.data(c)
.data(neibCells)
.enter()
.append("polygon")
.attr("points", d => getPackPolygon(d))
.attr("points", getPackPolygon)
.attr("data-c", d => d);
debug
.select("#vertices")
.selectAll("circle")
.data(v)
.data(vertices)
.enter()
.append("circle")
.attr("cx", d => pack.vertices.p[d][0])
.attr("cy", d => pack.vertices.p[d][1])
.attr("r", 0.4)
.attr("data-v", d => d)
.call(d3.drag().on("drag", dragVertex))
.on("mousemove", () => tip("Drag to move the vertex, please use for fine-tuning only. Edit heightmap to change actual cell heights"));
.call(d3.drag().on("drag", handleVertexDrag).on("end", handleVertexDragEnd))
.on("mousemove", () =>
tip("Drag to move the vertex. Please use for fine-tuning only. Edit heightmap to change actual cell heights!")
);
const area = pack.features[f].area;
coastlineArea.innerHTML = si(getArea(area)) + " " + getAreaUnit();
}
function dragVertex() {
const x = rn(d3.event.x, 2),
y = rn(d3.event.y, 2);
function handleVertexDrag() {
const {vertices, features} = pack;
const x = rn(d3.event.x, 2);
const y = rn(d3.event.y, 2);
this.setAttribute("cx", x);
this.setAttribute("cy", y);
const v = +this.dataset.v;
pack.vertices.p[v] = [x, y];
debug
.select("#vertices")
.selectAll("polygon")
.attr("points", d => getPackPolygon(d));
redrawCoastline();
const vertexId = d3.select(this).datum();
vertices.p[vertexId] = [x, y];
const featureId = +elSelected.attr("data-f");
const feature = features[featureId];
// change coastline path
defs.select("#featurePaths > path#feature_" + featureId).attr("d", getFeaturePath(feature));
// update area
const points = feature.vertices.map(vertex => vertices.p[vertex]);
feature.area = Math.abs(d3.polygonArea(points));
coastlineArea.innerHTML = si(getArea(feature.area)) + " " + getAreaUnit();
// update cell
debug.select("#vertices").selectAll("polygon").attr("points", getPackPolygon);
}
function redrawCoastline() {
lineGen.curve(d3.curveBasisClosed);
const f = +elSelected.attr("data-f");
const vertices = pack.features[f].vertices;
const points = clipPoly(
vertices.map(v => pack.vertices.p[v]),
1
);
const d = round(lineGen(points));
elSelected.attr("d", d);
defs.select("mask#land > path#land_" + f).attr("d", d); // update land mask
defs.select("mask#water > path#water_" + f).attr("d", d); // update water mask
const area = Math.abs(d3.polygonArea(points));
coastlineArea.innerHTML = si(getArea(area)) + " " + getAreaUnit();
function handleVertexDragEnd() {
if (layerIsOn("toggleStates")) drawStates();
if (layerIsOn("toggleProvinces")) drawProvinces();
if (layerIsOn("toggleBorders")) drawBorders();
if (layerIsOn("toggleBiomes")) drawBiomes();
if (layerIsOn("toggleReligions")) drawReligions();
if (layerIsOn("toggleCultures")) drawCultures();
}
function showGroupSection() {
document.querySelectorAll("#coastlineEditor > button").forEach(el => (el.style.display = "none"));
document.getElementById("coastlineGroupsSelection").style.display = "inline-block";
byId("coastlineGroupsSelection").style.display = "inline-block";
}
function hideGroupSection() {
document.querySelectorAll("#coastlineEditor > button").forEach(el => (el.style.display = "inline-block"));
document.getElementById("coastlineGroupsSelection").style.display = "none";
document.getElementById("coastlineGroupName").style.display = "none";
document.getElementById("coastlineGroupName").value = "";
document.getElementById("coastlineGroup").style.display = "inline-block";
byId("coastlineGroupsSelection").style.display = "none";
byId("coastlineGroupName").style.display = "none";
byId("coastlineGroupName").value = "";
byId("coastlineGroup").style.display = "inline-block";
}
function selectCoastlineGroup(node) {
const group = node.parentNode.id;
const select = document.getElementById("coastlineGroup");
const select = byId("coastlineGroup");
select.options.length = 0; // remove all options
coastline.selectAll("g").each(function () {
@ -116,7 +123,7 @@ function editCoastline(node = d3.event.target) {
}
function changeCoastlineGroup() {
document.getElementById(this.value).appendChild(elSelected.node());
byId(this.value).appendChild(elSelected.node());
}
function toggleNewGroupInput() {
@ -131,54 +138,44 @@ function editCoastline(node = d3.event.target) {
}
function createNewGroup() {
if (!this.value) {
tip("Please provide a valid group name");
return;
}
if (!this.value) return tip("Please provide a valid group name");
const group = this.value
.toLowerCase()
.replace(/ /g, "_")
.replace(/[^\w\s]/gi, "");
if (document.getElementById(group)) {
tip("Element with this id already exists. Please provide a unique name", false, "error");
return;
}
if (byId(group)) return tip("Element with this id already exists. Please provide a unique name", false, "error");
if (Number.isFinite(+group.charAt(0))) {
tip("Group name should start with a letter", false, "error");
return;
}
if (Number.isFinite(+group.charAt(0))) return tip("Group name should start with a letter", false, "error");
// just rename if only 1 element left
const oldGroup = elSelected.node().parentNode;
const basic = ["sea_island", "lake_island"].includes(oldGroup.id);
if (!basic && oldGroup.childElementCount === 1) {
document.getElementById("coastlineGroup").selectedOptions[0].remove();
document.getElementById("coastlineGroup").options.add(new Option(group, group, false, true));
byId("coastlineGroup").selectedOptions[0].remove();
byId("coastlineGroup").options.add(new Option(group, group, false, true));
oldGroup.id = group;
toggleNewGroupInput();
document.getElementById("coastlineGroupName").value = "";
byId("coastlineGroupName").value = "";
return;
}
// create a new group
const newGroup = elSelected.node().parentNode.cloneNode(false);
document.getElementById("coastline").appendChild(newGroup);
byId("coastline").appendChild(newGroup);
newGroup.id = group;
document.getElementById("coastlineGroup").options.add(new Option(group, group, false, true));
document.getElementById(group).appendChild(elSelected.node());
byId("coastlineGroup").options.add(new Option(group, group, false, true));
byId(group).appendChild(elSelected.node());
toggleNewGroupInput();
document.getElementById("coastlineGroupName").value = "";
byId("coastlineGroupName").value = "";
}
function removeCoastlineGroup() {
const group = elSelected.node().parentNode.id;
if (["sea_island", "lake_island"].includes(group)) {
tip("This is one of the default groups, it cannot be removed", false, "error");
return;
}
if (["sea_island", "lake_island"].includes(group))
return tip("This is one of the default groups, it cannot be removed", false, "error");
const count = elSelected.node().parentNode.childElementCount;
alertMessage.innerHTML = /* html */ `Are you sure you want to remove the group? All coastline elements of the group (${count}) will be moved under
@ -190,14 +187,14 @@ function editCoastline(node = d3.event.target) {
buttons: {
Remove: function () {
$(this).dialog("close");
const sea = document.getElementById("sea_island");
const groupEl = document.getElementById(group);
const sea = byId("sea_island");
const groupEl = byId(group);
while (groupEl.childNodes.length) {
sea.appendChild(groupEl.childNodes[0]);
}
groupEl.remove();
document.getElementById("coastlineGroup").selectedOptions[0].remove();
document.getElementById("coastlineGroup").value = "sea_island";
byId("coastlineGroup").selectedOptions[0].remove();
byId("coastlineGroup").value = "sea_island";
},
Cancel: function () {
$(this).dialog("close");

View file

@ -10,31 +10,27 @@ function restoreDefaultEvents() {
legend.call(d3.drag().on("start", dragLegendBox));
}
// on viewbox click event - run function based on target
// handle viewbox click
function clicked() {
const el = d3.event.target;
if (!el || !el.parentElement || !el.parentElement.parentElement) return;
const parent = el.parentElement;
const grand = parent.parentElement;
const great = grand.parentElement;
const p = d3.mouse(this);
const i = findCell(p[0], p[1]);
const parent = el?.parentElement;
const grand = parent?.parentElement;
const great = grand?.parentElement;
const ancestor = great?.parentElement;
if (!ancestor) return;
if (grand.id === "emblems") editEmblem();
else if (parent.id === "rivers") editRiver(el.id);
else if (grand.id === "routes") editRoute(el.id);
else if (el.tagName === "tspan" && grand.parentNode.parentNode.id === "labels") editLabel();
else if (ancestor.id === "labels" && el.tagName === "tspan") editLabel();
else if (grand.id === "burgLabels") editBurg();
else if (grand.id === "burgIcons") editBurg();
else if (parent.id === "ice") editIce();
else if (parent.id === "terrain") editReliefIcon();
else if (grand.id === "markers" || great.id === "markers") editMarker();
else if (grand.id === "coastline") editCoastline();
else if (grand.id === "lakes") editLake();
else if (great.id === "armies") editRegiment();
else if (pack.cells.t[i] === 1) {
const node = byId("island_" + pack.cells.f[i]);
editCoastline(node);
} else if (grand.id === "lakes") editLake();
}
// clear elSelected variable
@ -397,12 +393,12 @@ function createVillageGeneratorLink(burg) {
else if (cells.r[cell]) tags.push("river");
else if (pop < 200 && each(4)(cell)) tags.push("pond");
const connections = pack.cells.routes[cell] || {};
const roads = Object.values(connections).filter(routeId => {
const route = pack.routes[routeId];
const roadsNumber = Object.values(pack.cells.routes[cell] || {}).filter(routeId => {
const route = pack.routes.find(route => route.i === routeId);
if (!route) return false;
return route.group === "roads" || route.group === "trails";
}).length;
tags.push(roads > 1 ? "highway" : roads === 1 ? "dead end" : "isolated");
tags.push(roadsNumber > 1 ? "highway" : roadsNumber === 1 ? "dead end" : "isolated");
const biome = cells.biome[cell];
const arableBiomes = cells.r[cell] ? [1, 2, 3, 4, 5, 6, 7, 8] : [5, 6, 7, 8];
@ -1252,18 +1248,18 @@ function refreshAllEditors() {
// dynamically loaded editors
async function editStates() {
if (customization) return;
const Editor = await import("../dynamic/editors/states-editor.js?v=1.99.05");
const Editor = await import("../dynamic/editors/states-editor.js?v=1.104.0");
Editor.open();
}
async function editCultures() {
if (customization) return;
const Editor = await import("../dynamic/editors/cultures-editor.js?v=1.99.05");
const Editor = await import("../dynamic/editors/cultures-editor.js?v=1.104.0");
Editor.open();
}
async function editReligions() {
if (customization) return;
const Editor = await import("../dynamic/editors/religions-editor.js?v=1.99.05");
const Editor = await import("../dynamic/editors/religions-editor.js?v=1.104.0");
Editor.open();
}

View file

@ -153,20 +153,24 @@ function showMapTooltip(point, e, i, g) {
if (group === "routes") {
const routeId = +e.target.id.slice(5);
const name = pack.routes[routeId]?.name;
if (name) return tip(`${name}. Click to edit the Route`);
return tip("Click to edit the Route");
const route = pack.routes.find(route => route.i === routeId);
if (route) {
if (route.name) return tip(`${route.name}. Click to edit the Route`);
return tip("Click to edit the Route");
}
}
if (group === "terrain") return tip("Click to edit the Relief Icon");
if (subgroup === "burgLabels" || subgroup === "burgIcons") {
const burg = +path[path.length - 10].dataset.id;
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);
return;
const burgId = +path[path.length - 10].dataset.id;
if (burgId) {
const burg = pack.burgs[burgId];
const population = si(burg.population * populationRate * urbanization);
tip(`${burg.name}. Population: ${population}. Click to edit`);
if (burgsOverview?.offsetParent) highlightEditorLine(burgsOverview, burgId, 5000);
return;
}
}
if (group === "labels") return tip("Click to edit the Label");
@ -211,9 +215,9 @@ function showMapTooltip(point, e, i, g) {
if (group === "ice") return tip("Click to edit the Ice");
// covering elements
if (layerIsOn("togglePrec") && land) tip("Annual Precipitation: " + getFriendlyPrecipitation(i));
if (layerIsOn("togglePrecipitation") && 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("toggleTemperature")) 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]);
@ -259,10 +263,11 @@ function updateCellInfo(point, i, g) {
const f = cells.f[i];
infoLat.innerHTML = toDMS(getLatitude(y, 4), "lat");
infoLon.innerHTML = toDMS(getLongitude(x, 4), "lon");
infoGeozone.innerHTML = getGeozone(getLatitude(y, 4));
infoCell.innerHTML = i;
infoArea.innerHTML = cells.area[i] ? si(getArea(cells.area[i])) + " " + getAreaUnit() : "n/a";
infoEvelation.innerHTML = getElevation(pack.features[f], pack.cells.h[i]);
infoElevation.innerHTML = getElevation(pack.features[f], pack.cells.h[i]);
infoDepth.innerHTML = getDepth(pack.features[f], point);
infoTemp.innerHTML = convertTemperature(grid.cells.temp[g]);
infoPrec.innerHTML = cells.h[i] >= 20 ? getFriendlyPrecipitation(i) : "n/a";
@ -286,6 +291,18 @@ function updateCellInfo(point, i, g) {
infoBiome.innerHTML = biomesData.name[cells.biome[i]];
}
function getGeozone(latitude) {
if (latitude > 66.5) return "Arctic";
if (latitude > 35) return "Temperate North";
if (latitude > 23.5) return "Subtropical North";
if (latitude > 1) return "Tropical North";
if (latitude > -1) return "Equatorial";
if (latitude > -23.5) return "Tropical South";
if (latitude > -35) return "Subtropical South";
if (latitude > -66.5) return "Temperate South";
return "Antarctic";
}
// convert coordinate to DMS format
function toDMS(coord, c) {
const degrees = Math.floor(Math.abs(coord));
@ -429,17 +446,17 @@ function highlightEmblemElement(type, el) {
// assign lock behavior
document.querySelectorAll("[data-locked]").forEach(function (e) {
e.addEventListener("mouseover", function (event) {
e.addEventListener("mouseover", function (e) {
e.stopPropagation();
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();
});
e.addEventListener("click", function () {
const id = this.id.slice(5);
if (this.className === "icon-lock") unlock(id);
else lock(id);
const ids = this.dataset.ids ? this.dataset.ids.split(",") : [this.id.slice(5)];
const fn = this.className === "icon-lock" ? unlock : lock;
ids.forEach(fn);
});
});

View file

@ -75,7 +75,8 @@ function editHeightmap(options) {
changeOnlyLand.checked = true;
} else if (mode === "risk") {
defs.selectAll("#land, #water").selectAll("path").remove();
viewbox.selectAll("#coastline path, #lakes path, #oceanLayers path").remove();
defs.select("#featurePaths").selectAll("path").remove();
viewbox.selectAll("#coastline use, #lakes path, #oceanLayers path").remove();
changeOnlyLand.checked = false;
}
@ -157,11 +158,7 @@ function editHeightmap(options) {
// Exit customization mode
function finalizeHeightmap() {
if (viewbox.select("#heights").selectAll("*").size() < 200)
return tip(
"Insufficient land area! There should be at least 200 land cells to finalize the heightmap",
null,
"error"
);
return tip("Insufficient land area. There should be at least 200 land cells!", null, "error");
if (byId("imageConverter").offsetParent) return tip("Please exit the Image Conversion mode first", null, "error");
delete window.edits; // remove global variable
@ -173,6 +170,7 @@ function editHeightmap(options) {
if (byId("options").querySelector(".tab > button.active").id === "toolsTab") toolsContent.style.display = "block";
layersPreset.disabled = false;
exitCustomization.style.display = "none"; // hide finalize button
restoreDefaultEvents();
clearMainTip();
closeDialogs();
@ -187,6 +185,7 @@ function editHeightmap(options) {
else if (mode === "risk") restoreRiskedData();
// restore initial layers
drawFeatures();
byId("heights").remove();
turnButtonOff("toggleHeight");
document
@ -215,8 +214,7 @@ function editHeightmap(options) {
pack.religions = [];
const erosionAllowed = allowErosion.checked;
markFeatures();
markupGridOcean();
Features.markupGrid();
if (erosionAllowed) {
addLakesInDeepDepressions();
openNearSeaLakes();
@ -225,7 +223,7 @@ function editHeightmap(options) {
calculateTemperatures();
generatePrecipitation();
reGraph();
drawCoastline();
Features.markupPack();
Rivers.generate(erosionAllowed);
@ -237,8 +235,6 @@ function editHeightmap(options) {
}
}
drawRivers();
Lakes.defineGroup();
Biomes.define();
rankCells();
@ -249,15 +245,12 @@ function editHeightmap(options) {
Routes.generate();
Religions.generate();
BurgsAndStates.defineStateForms();
BurgsAndStates.generateProvinces();
Provinces.generate();
Provinces.getPoles();
BurgsAndStates.defineBurgFeatures();
drawStates();
drawBorders();
drawStateLabels();
Rivers.specify();
Lakes.generateName();
Features.specify();
Military.generate();
Markers.generate();
@ -338,14 +331,13 @@ function editHeightmap(options) {
zone.selectAll("*").remove();
});
markFeatures();
markupGridOcean();
Features.markupGrid();
if (erosionAllowed) addLakesInDeepDepressions();
OceanLayers();
calculateTemperatures();
generatePrecipitation();
reGraph();
drawCoastline();
Features.markupPack();
if (erosionAllowed) Rivers.generate(true);
@ -439,13 +431,9 @@ function editHeightmap(options) {
c.center = findCell(c.x, c.y);
}
drawStateLabels();
drawStates();
drawBorders();
if (erosionAllowed) {
Rivers.specify();
Lakes.generateName();
Features.specify();
}
// restore zones from grid
@ -489,10 +477,14 @@ function editHeightmap(options) {
updateHistory();
}
function getColor(value, scheme = getColorScheme()) {
return scheme(1 - (value < 20 ? value - 5 : value) / 100);
}
// draw or update heightmap
function mockHeightmap() {
const data = renderOcean.checked ? grid.cells.i : grid.cells.i.filter(i => grid.cells.h[i] >= 20);
const scheme = getColorScheme();
viewbox
.select("#heights")
.selectAll("polygon")
@ -500,13 +492,12 @@ function editHeightmap(options) {
.join("polygon")
.attr("points", d => getGridPolygon(d))
.attr("id", d => "cell" + d)
.attr("fill", d => getColor(grid.cells.h[d], scheme));
.attr("fill", d => getColor(grid.cells.h[d]));
}
// draw or update heightmap for a selection of cells
function mockHeightmapSelection(selection) {
const ocean = renderOcean.checked;
const scheme = getColorScheme();
selection.forEach(function (i) {
let cell = viewbox.select("#heights").select("#cell" + i);
@ -518,7 +509,7 @@ function editHeightmap(options) {
.append("polygon")
.attr("points", getGridPolygon(i))
.attr("id", "cell" + i);
cell.attr("fill", getColor(grid.cells.h[i], scheme));
cell.attr("fill", getColor(grid.cells.h[i]));
});
}
@ -1349,7 +1340,7 @@ function editHeightmap(options) {
return lum | 0; // land
};
const scheme = d3.range(101).map(i => getColor(i, color()));
const scheme = d3.range(101).map(i => getColor(i));
const hues = scheme.map(rgb => d3.hsl(rgb).h | 0);
const getHeightByScheme = function (color) {
let height = scheme.indexOf(color);

View file

@ -75,13 +75,13 @@ function handleKeyup(event) {
else if (code === "KeyD") toggleBorders();
else if (code === "KeyR") toggleReligions();
else if (code === "KeyU") toggleRoutes();
else if (code === "KeyT") toggleTemp();
else if (code === "KeyT") toggleTemperature();
else if (code === "KeyN") togglePopulation();
else if (code === "KeyJ") toggleIce();
else if (code === "KeyA") togglePrec();
else if (code === "KeyA") togglePrecipitation();
else if (code === "KeyY") toggleEmblems();
else if (code === "KeyL") toggleLabels();
else if (code === "KeyI") toggleIcons();
else if (code === "KeyI") toggleBurgIcons();
else if (code === "KeyM") toggleMilitary();
else if (code === "KeyK") toggleMarkers();
else if (code === "Equal" && !customization) toggleRulers();

View file

@ -23,17 +23,15 @@ function editLake() {
modules.editLake = true;
// add listeners
document.getElementById("lakeName").addEventListener("input", changeName);
document.getElementById("lakeNameCulture").addEventListener("click", generateNameCulture);
document.getElementById("lakeNameRandom").addEventListener("click", generateNameRandom);
document.getElementById("lakeGroup").addEventListener("change", changeLakeGroup);
document.getElementById("lakeGroupAdd").addEventListener("click", toggleNewGroupInput);
document.getElementById("lakeGroupName").addEventListener("change", createNewGroup);
document.getElementById("lakeGroupRemove").addEventListener("click", removeLakeGroup);
document.getElementById("lakeEditStyle").addEventListener("click", editGroupStyle);
document.getElementById("lakeLegend").addEventListener("click", editLakeLegend);
byId("lakeName").on("input", changeName);
byId("lakeNameCulture").on("click", generateNameCulture);
byId("lakeNameRandom").on("click", generateNameRandom);
byId("lakeGroup").on("change", changeLakeGroup);
byId("lakeGroupAdd").on("click", toggleNewGroupInput);
byId("lakeGroupName").on("change", createNewGroup);
byId("lakeGroupRemove").on("click", removeLakeGroup);
byId("lakeEditStyle").on("click", editGroupStyle);
byId("lakeLegend").on("click", editLakeLegend);
function getLake() {
const lakeId = +elSelected.attr("data-f");
@ -41,85 +39,91 @@ function editLake() {
}
function updateLakeValues() {
const cells = pack.cells;
const {cells, vertices, rivers} = pack;
const l = getLake();
document.getElementById("lakeName").value = l.name;
document.getElementById("lakeArea").value = si(getArea(l.area)) + " " + getAreaUnit();
byId("lakeName").value = l.name;
byId("lakeArea").value = si(getArea(l.area)) + " " + getAreaUnit();
const length = d3.polygonLength(l.vertices.map(v => pack.vertices.p[v]));
document.getElementById("lakeShoreLength").value = si(length * distanceScale) + " " + distanceUnitInput.value;
const length = d3.polygonLength(l.vertices.map(v => vertices.p[v]));
byId("lakeShoreLength").value = si(length * distanceScale) + " " + distanceUnitInput.value;
const lakeCells = Array.from(cells.i.filter(i => cells.f[i] === l.i));
const heights = lakeCells.map(i => cells.h[i]);
document.getElementById("lakeElevation").value = getHeight(l.height);
document.getElementById("lakeAverageDepth").value = getHeight(d3.mean(heights), "abs");
document.getElementById("lakeMaxDepth").value = getHeight(d3.min(heights), "abs");
byId("lakeElevation").value = getHeight(l.height);
byId("lakeAverageDepth").value = getHeight(d3.mean(heights), "abs");
byId("lakeMaxDepth").value = getHeight(d3.min(heights), "abs");
document.getElementById("lakeFlux").value = l.flux;
document.getElementById("lakeEvaporation").value = l.evaporation;
byId("lakeFlux").value = l.flux;
byId("lakeEvaporation").value = l.evaporation;
const inlets = l.inlets && l.inlets.map(inlet => pack.rivers.find(river => river.i === inlet)?.name);
const outlet = l.outlet ? pack.rivers.find(river => river.i === l.outlet)?.name : "no";
document.getElementById("lakeInlets").value = inlets ? inlets.length : "no";
document.getElementById("lakeInlets").title = inlets ? inlets.join(", ") : "";
document.getElementById("lakeOutlet").value = outlet;
const inlets = l.inlets && l.inlets.map(inlet => rivers.find(river => river.i === inlet)?.name);
const outlet = l.outlet ? rivers.find(river => river.i === l.outlet)?.name : "no";
byId("lakeInlets").value = inlets ? inlets.length : "no";
byId("lakeInlets").title = inlets ? inlets.join(", ") : "";
byId("lakeOutlet").value = outlet;
}
function drawLakeVertices() {
const v = getLake().vertices; // lake outer vertices
const vertices = getLake().vertices;
const c = [...new Set(v.map(v => pack.vertices.c[v]).flat())];
const neibCells = unique(vertices.map(v => pack.vertices.c[v]).flat());
debug
.select("#vertices")
.selectAll("polygon")
.data(c)
.data(neibCells)
.enter()
.append("polygon")
.attr("points", d => getPackPolygon(d))
.attr("points", getPackPolygon)
.attr("data-c", d => d);
debug
.select("#vertices")
.selectAll("circle")
.data(v)
.data(vertices)
.enter()
.append("circle")
.attr("cx", d => pack.vertices.p[d][0])
.attr("cy", d => pack.vertices.p[d][1])
.attr("r", 0.4)
.attr("data-v", d => d)
.call(d3.drag().on("drag", dragVertex))
.call(d3.drag().on("drag", handleVertexDrag).on("end", handleVertexDragEnd))
.on("mousemove", () =>
tip("Drag to move the vertex, please use for fine-tuning only. Edit heightmap to change actual cell heights")
tip("Drag to move the vertex. Please use for fine-tuning only! Edit heightmap to change actual cell heights")
);
}
function dragVertex() {
const x = rn(d3.event.x, 2),
y = rn(d3.event.y, 2);
function handleVertexDrag() {
const x = rn(d3.event.x, 2);
const y = rn(d3.event.y, 2);
this.setAttribute("cx", x);
this.setAttribute("cy", y);
const v = +this.dataset.v;
pack.vertices.p[v] = [x, y];
debug
.select("#vertices")
.selectAll("polygon")
.attr("points", d => getPackPolygon(d));
redrawLake();
const vertexId = d3.select(this).datum();
pack.vertices.p[vertexId] = [x, y];
const feature = getLake();
// update lake path
defs.select("#featurePaths > path#feature_" + feature.i).attr("d", getFeaturePath(feature));
// update area
const points = feature.vertices.map(vertex => pack.vertices.p[vertex]);
feature.area = Math.abs(d3.polygonArea(points));
byId("lakeArea").value = si(getArea(feature.area)) + " " + getAreaUnit();
// update cell
debug.select("#vertices").selectAll("polygon").attr("points", getPackPolygon);
}
function redrawLake() {
lineGen.curve(d3.curveBasisClosed);
const feature = getLake();
const points = feature.vertices.map(v => pack.vertices.p[v]);
const d = round(lineGen(points));
elSelected.attr("d", d);
defs.select("mask#land > path#land_" + feature.i).attr("d", d); // update land mask
feature.area = Math.abs(d3.polygonArea(points));
document.getElementById("lakeArea").value = si(getArea(feature.area)) + " " + getAreaUnit();
function handleVertexDragEnd() {
if (layerIsOn("toggleStates")) drawStates();
if (layerIsOn("toggleProvinces")) drawProvinces();
if (layerIsOn("toggleBorders")) drawBorders();
if (layerIsOn("toggleBiomes")) drawBiomes();
if (layerIsOn("toggleReligions")) drawReligions();
if (layerIsOn("toggleCultures")) drawCultures();
}
function changeName() {
@ -138,7 +142,7 @@ function editLake() {
function selectLakeGroup(node) {
const group = node.parentNode.id;
const select = document.getElementById("lakeGroup");
const select = byId("lakeGroup");
select.options.length = 0; // remove all options
lakes.selectAll("g").each(function () {
@ -147,7 +151,7 @@ function editLake() {
}
function changeLakeGroup() {
document.getElementById(this.value).appendChild(elSelected.node());
byId(this.value).appendChild(elSelected.node());
getLake().group = this.value;
}
@ -172,7 +176,7 @@ function editLake() {
.replace(/ /g, "_")
.replace(/[^\w\s]/gi, "");
if (document.getElementById(group)) {
if (byId(group)) {
tip("Element with this id already exists. Please provide a unique name", false, "error");
return;
}
@ -186,23 +190,23 @@ function editLake() {
const oldGroup = elSelected.node().parentNode;
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));
byId("lakeGroup").selectedOptions[0].remove();
byId("lakeGroup").options.add(new Option(group, group, false, true));
oldGroup.id = group;
toggleNewGroupInput();
document.getElementById("lakeGroupName").value = "";
byId("lakeGroupName").value = "";
return;
}
// create a new group
const newGroup = elSelected.node().parentNode.cloneNode(false);
document.getElementById("lakes").appendChild(newGroup);
byId("lakes").appendChild(newGroup);
newGroup.id = group;
document.getElementById("lakeGroup").options.add(new Option(group, group, false, true));
document.getElementById(group).appendChild(elSelected.node());
byId("lakeGroup").options.add(new Option(group, group, false, true));
byId(group).appendChild(elSelected.node());
toggleNewGroupInput();
document.getElementById("lakeGroupName").value = "";
byId("lakeGroupName").value = "";
}
function removeLakeGroup() {
@ -221,14 +225,14 @@ function editLake() {
buttons: {
Remove: function () {
$(this).dialog("close");
const freshwater = document.getElementById("freshwater");
const groupEl = document.getElementById(group);
const freshwater = byId("freshwater");
const groupEl = byId(group);
while (groupEl.childNodes.length) {
freshwater.appendChild(groupEl.childNodes[0]);
}
groupEl.remove();
document.getElementById("lakeGroup").selectedOptions[0].remove();
document.getElementById("lakeGroup").value = "freshwater";
byId("lakeGroup").selectedOptions[0].remove();
byId("lakeGroup").value = "freshwater";
},
Cancel: function () {
$(this).dialog("close");

File diff suppressed because it is too large Load diff

View file

@ -530,3 +530,32 @@ class Planimeter extends Measurer {
this.el.select("text").attr("x", c[0]).attr("y", c[1]).text(area);
}
}
function createDefaultRuler() {
TIME && console.time("createDefaultRuler");
const {features, vertices} = pack;
const areas = features.map(f => (f.land ? f.area || 0 : -Infinity));
const largestLand = areas.indexOf(Math.max(...areas));
const featureVertices = features[largestLand].vertices;
const MIN_X = 100;
const MAX_X = graphWidth - 100;
const MIN_Y = 100;
const MAX_Y = graphHeight - 100;
let leftmostVertex = [graphWidth - MIN_X, graphHeight / 2];
let rightmostVertex = [MIN_X, graphHeight / 2];
for (const vertex of featureVertices) {
const [x, y] = vertices.p[vertex];
if (y < MIN_Y || y > MAX_Y) continue;
if (x < leftmostVertex[0] && x >= MIN_X) leftmostVertex = [x, y];
if (x > rightmostVertex[0] && x <= MAX_X) rightmostVertex = [x, y];
}
rulers = new Rulers();
rulers.create(Ruler, [leftmostVertex, rightmostVertex]);
TIME && console.timeEnd("createDefaultRuler");
}

View file

@ -251,8 +251,7 @@ const voiceInterval = setInterval(function () {
select.options.add(new Option(voice.name, i, false));
});
if (stored("speakerVoice")) select.value = stored("speakerVoice");
// se voice to store
else select.value = voices.findIndex(voice => voice.lang === "en-US"); // or to first found English-US
else select.value = voices.findIndex(voice => voice.lang === "en-US");
}, 1000);
function testSpeaker() {
@ -704,12 +703,6 @@ async function openTemplateSelectionDialog() {
HeightmapSelectionDialog.open();
}
// remove all saved data from LocalStorage and reload the page
function restoreDefaultOptions() {
localStorage.clear();
location.reload();
}
// Sticked menu Options listeners
byId("sticked").addEventListener("click", function (event) {
const id = event.target.id;

View file

@ -8,7 +8,7 @@ function editProvinces() {
if (layerIsOn("toggleCultures")) toggleCultures();
provs.selectAll("text").call(d3.drag().on("drag", dragLabel)).classed("draggable", true);
const body = document.getElementById("provincesBodySection");
const body = byId("provincesBodySection");
refreshProvincesEditor();
if (modules.editProvinces) return;
@ -23,22 +23,22 @@ function editProvinces() {
});
// add listeners
document.getElementById("provincesEditorRefresh").addEventListener("click", refreshProvincesEditor);
document.getElementById("provincesEditStyle").addEventListener("click", () => editStyle("provs"));
document.getElementById("provincesFilterState").addEventListener("change", provincesEditorAddLines);
document.getElementById("provincesPercentage").addEventListener("click", togglePercentageMode);
document.getElementById("provincesChart").addEventListener("click", showChart);
document.getElementById("provincesToggleLabels").addEventListener("click", toggleLabels);
document.getElementById("provincesExport").addEventListener("click", downloadProvincesData);
document.getElementById("provincesRemoveAll").addEventListener("click", removeAllProvinces);
document.getElementById("provincesManually").addEventListener("click", enterProvincesManualAssignent);
document.getElementById("provincesManuallyApply").addEventListener("click", applyProvincesManualAssignent);
document.getElementById("provincesManuallyCancel").addEventListener("click", () => exitProvincesManualAssignment());
document.getElementById("provincesRelease").addEventListener("click", triggerProvincesRelease);
document.getElementById("provincesAdd").addEventListener("click", enterAddProvinceMode);
document.getElementById("provincesRecolor").addEventListener("click", recolorProvinces);
byId("provincesEditorRefresh").on("click", refreshProvincesEditor);
byId("provincesEditStyle").on("click", () => editStyle("provs"));
byId("provincesFilterState").on("change", provincesEditorAddLines);
byId("provincesPercentage").on("click", togglePercentageMode);
byId("provincesChart").on("click", showChart);
byId("provincesToggleLabels").on("click", toggleLabels);
byId("provincesExport").on("click", downloadProvincesData);
byId("provincesRemoveAll").on("click", removeAllProvinces);
byId("provincesManually").on("click", enterProvincesManualAssignent);
byId("provincesManuallyApply").on("click", applyProvincesManualAssignent);
byId("provincesManuallyCancel").on("click", () => exitProvincesManualAssignment());
byId("provincesRelease").on("click", triggerProvincesRelease);
byId("provincesAdd").on("click", enterAddProvinceMode);
byId("provincesRecolor").on("click", recolorProvinces);
body.addEventListener("click", function (ev) {
body.on("click", function (ev) {
if (customization) return;
const el = ev.target,
cl = el.classList,
@ -58,7 +58,7 @@ function editProvinces() {
else if (cl.contains("icon-lock") || cl.contains("icon-lock-open")) updateLockStatus(p, cl);
});
body.addEventListener("change", function (ev) {
body.on("change", function (ev) {
const el = ev.target,
cl = el.classList,
line = el.parentNode,
@ -100,7 +100,7 @@ function editProvinces() {
}
function updateFilter() {
const stateFilter = document.getElementById("provincesFilterState");
const stateFilter = byId("provincesFilterState");
const selectedState = stateFilter.value || 1;
stateFilter.options.length = 0; // remove all options
stateFilter.options.add(new Option(`all`, -1, false, selectedState == -1));
@ -111,7 +111,7 @@ function editProvinces() {
// add line for each province
function provincesEditorAddLines() {
const unit = " " + getAreaUnit();
const selectedState = +document.getElementById("provincesFilterState").value;
const selectedState = +byId("provincesFilterState").value;
let filtered = pack.provinces.filter(p => p.i && !p.removed); // all valid burgs
if (selectedState != -1) filtered = filtered.filter(p => p.state === selectedState); // filtered by state
body.innerHTML = "";
@ -194,9 +194,9 @@ function editProvinces() {
byId("provincesFooterPopulation").dataset.population = totalPopulation;
body.querySelectorAll("div.states").forEach(el => {
el.addEventListener("click", selectProvinceOnLineClick);
el.addEventListener("mouseenter", ev => provinceHighlightOn(ev));
el.addEventListener("mouseleave", ev => provinceHighlightOff(ev));
el.on("click", selectProvinceOnLineClick);
el.on("mouseenter", ev => provinceHighlightOn(ev));
el.on("mouseleave", ev => provinceHighlightOff(ev));
});
if (body.dataset.type === "percentage") {
@ -306,7 +306,7 @@ function editProvinces() {
const {cell: center, culture} = burgs[burgId];
const color = getRandomColor();
const coa = province.coa;
const coaEl = document.getElementById("provinceCOA" + provinceId);
const coaEl = byId("provinceCOA" + provinceId);
if (coaEl) coaEl.id = "stateCOA" + newStateId;
emblems.select(`#provinceEmblems > use[data-i='${provinceId}']`).remove();
@ -454,6 +454,7 @@ function editProvinces() {
p.burgs.forEach(b => (pack.burgs[b].population = population));
}
if (layerIsOn("togglePopulation")) drawPopulation();
refreshProvincesEditor();
}
}
@ -482,7 +483,7 @@ function editProvinces() {
unfog("focusProvince" + p);
const coaId = "provinceCOA" + p;
if (document.getElementById(coaId)) document.getElementById(coaId).remove();
if (byId(coaId)) byId(coaId).remove();
emblems.select(`#provinceEmblems > use[data-i='${p}']`).remove();
pack.provinces[p] = {i: p, removed: true};
@ -490,8 +491,7 @@ function editProvinces() {
const g = provs.select("#provincesBody");
g.select("#province" + p).remove();
g.select("#province-gap" + p).remove();
if (!layerIsOn("toggleBorders")) toggleBorders();
else drawBorders();
if (layerIsOn("toggleBorders")) drawBorders();
refreshProvincesEditor();
$(this).dialog("close");
},
@ -504,13 +504,13 @@ function editProvinces() {
function editProvinceName(province) {
const p = pack.provinces[province];
document.getElementById("provinceNameEditor").dataset.province = province;
document.getElementById("provinceNameEditorShort").value = p.name;
byId("provinceNameEditor").dataset.province = province;
byId("provinceNameEditorShort").value = p.name;
applyOption(provinceNameEditorSelectForm, p.formName);
document.getElementById("provinceNameEditorFull").value = p.fullName;
byId("provinceNameEditorFull").value = p.fullName;
const cultureId = pack.cells.culture[p.center];
document.getElementById("provinceCultureDisplay").innerText = pack.cultures[cultureId].name;
byId("provinceCultureDisplay").innerText = pack.cultures[cultureId].name;
$("#provinceNameEditor").dialog({
resizable: false,
@ -531,22 +531,22 @@ function editProvinces() {
modules.editProvinceName = true;
// add listeners
document.getElementById("provinceNameEditorShortCulture").addEventListener("click", regenerateShortNameCulture);
document.getElementById("provinceNameEditorShortRandom").addEventListener("click", regenerateShortNameRandom);
document.getElementById("provinceNameEditorAddForm").addEventListener("click", addCustomForm);
document.getElementById("provinceNameEditorFullRegenerate").addEventListener("click", regenerateFullName);
byId("provinceNameEditorShortCulture").on("click", regenerateShortNameCulture);
byId("provinceNameEditorShortRandom").on("click", regenerateShortNameRandom);
byId("provinceNameEditorAddForm").on("click", addCustomForm);
byId("provinceNameEditorFullRegenerate").on("click", regenerateFullName);
function regenerateShortNameCulture() {
const province = +provinceNameEditor.dataset.province;
const culture = pack.cells.culture[pack.provinces[province].center];
const name = Names.getState(Names.getCultureShort(culture), culture);
document.getElementById("provinceNameEditorShort").value = name;
byId("provinceNameEditorShort").value = name;
}
function regenerateShortNameRandom() {
const base = rand(nameBases.length - 1);
const name = Names.getState(Names.getBase(base), undefined, base);
document.getElementById("provinceNameEditorShort").value = name;
byId("provinceNameEditorShort").value = name;
}
function addCustomForm() {
@ -558,9 +558,9 @@ function editProvinces() {
}
function regenerateFullName() {
const short = document.getElementById("provinceNameEditorShort").value;
const form = document.getElementById("provinceNameEditorSelectForm").value;
document.getElementById("provinceNameEditorFull").value = getFullName();
const short = byId("provinceNameEditorShort").value;
const form = byId("provinceNameEditorSelectForm").value;
byId("provinceNameEditorFull").value = getFullName();
function getFullName() {
if (!form) return short;
@ -570,9 +570,9 @@ function editProvinces() {
}
function applyNameChange(p) {
p.name = document.getElementById("provinceNameEditorShort").value;
p.formName = document.getElementById("provinceNameEditorSelectForm").value;
p.fullName = document.getElementById("provinceNameEditorFull").value;
p.name = byId("provinceNameEditorShort").value;
p.formName = byId("provinceNameEditorSelectForm").value;
p.fullName = byId("provinceNameEditorFull").value;
provs.select("#provinceLabel" + p.i).text(p.name);
refreshProvincesEditor();
}
@ -651,7 +651,7 @@ function editProvinces() {
.attr("height", height)
.attr("font-size", "10px");
const graph = svg.append("g").attr("transform", `translate(10, 0)`);
document.getElementById("provincesTreeType").addEventListener("change", updateChart);
byId("provincesTreeType").on("change", updateChart);
treeLayout(root);
@ -688,7 +688,7 @@ function editProvinces() {
function hideInfo(ev) {
provinceHighlightOff(ev);
if (!document.getElementById("provinceInfo")) return;
if (!byId("provinceInfo")) return;
provinceInfo.innerHTML = "&#8205;";
d3.select(ev.target).select("rect").classed("selected", 0);
}
@ -816,7 +816,7 @@ function editProvinces() {
stateBorders.select("path").attr("stroke", "#000").attr("stroke-width", 1.2);
customization = 11;
provs.select("g#provincesBody").append("g").attr("id", "temp");
provs.select("g#provincesBody").append("g").attr("id", "temp").attr("stroke-width", 0.3);
provs
.select("g#provincesBody")
.append("g")
@ -826,7 +826,7 @@ function editProvinces() {
.attr("stroke-width", 1);
document.querySelectorAll("#provincesBottom > *").forEach(el => (el.style.display = "none"));
document.getElementById("provincesManuallyButtons").style.display = "inline-block";
byId("provincesManuallyButtons").style.display = "inline-block";
provincesEditor.querySelectorAll(".hide").forEach(el => el.classList.add("hidden"));
provincesHeader.querySelector("div[data-sortby='state']").style.left = "7.7em";
@ -950,10 +950,10 @@ function editProvinces() {
pack.cells.province[i] = +this.dataset.province;
});
if (!layerIsOn("toggleBorders")) toggleBorders();
else drawBorders();
if (!layerIsOn("toggleProvinces")) toggleProvinces();
else drawProvinces();
Provinces.getPoles();
if (layerIsOn("toggleBorders")) drawBorders();
if (layerIsOn("toggleProvinces")) drawProvinces();
exitProvincesManualAssignment();
refreshProvincesEditor();
}
@ -970,7 +970,7 @@ function editProvinces() {
debug.selectAll("path.selected").remove();
document.querySelectorAll("#provincesBottom > *").forEach(el => (el.style.display = "inline-block"));
document.getElementById("provincesManuallyButtons").style.display = "none";
byId("provincesManuallyButtons").style.display = "none";
provincesEditor.querySelectorAll(".hide:not(.show)").forEach(el => el.classList.remove("hidden"));
provincesHeader.querySelector("div[data-sortby='state']").style.left = "22em";
@ -1044,12 +1044,11 @@ function editProvinces() {
cells.province[c] = province;
});
if (!layerIsOn("toggleBorders")) toggleBorders();
else drawBorders();
if (!layerIsOn("toggleProvinces")) toggleProvinces();
else drawProvinces();
if (layerIsOn("toggleBorders")) drawBorders();
if (layerIsOn("toggleProvinces")) drawProvinces();
collectStatistics();
document.getElementById("provincesFilterState").value = state;
byId("provincesFilterState").value = state;
provincesEditorAddLines();
}
@ -1062,7 +1061,7 @@ function editProvinces() {
}
function recolorProvinces() {
const state = +document.getElementById("provincesFilterState").value;
const state = +byId("provincesFilterState").value;
pack.provinces.forEach(p => {
if (!p || p.removed) return;
@ -1120,8 +1119,7 @@ function editProvinces() {
pack.states.forEach(s => (s.provinces = []));
unfog();
if (!layerIsOn("toggleBorders")) toggleBorders();
else drawBorders();
if (layerIsOn("toggleBorders")) drawBorders();
provs.select("#provincesBody").remove();
turnButtonOff("toggleProvinces");

View file

@ -218,7 +218,7 @@ function editRegiment(selector) {
newReg.name = Military.getName(newReg, military);
military.push(newReg);
Military.generateNote(newReg, pack.states[state]); // add legend
Military.drawRegiment(newReg, state); // draw new reg below
drawRegiment(newReg, state); // draw new reg below
if (regimentsOverviewRefresh.offsetParent) regimentsOverviewRefresh.click();
}
@ -246,7 +246,7 @@ function editRegiment(selector) {
reg.name = Military.getName(reg, military);
military.push(reg);
Military.generateNote(reg, pack.states[state]); // add legend
Military.drawRegiment(reg, state);
drawRegiment(reg, state);
if (regimentsOverviewRefresh.offsetParent) regimentsOverviewRefresh.click();
toggleAdd();
}
@ -296,7 +296,7 @@ function editRegiment(selector) {
(defender.px = defender.x), (defender.py = defender.y);
// move attacker to defender
Military.moveRegiment(attacker, defender.x, defender.y - 8);
moveRegiment(attacker, defender.x, defender.y - 8);
// draw battle icon
const attack = d3

View file

@ -179,7 +179,7 @@ function overviewRegiments(state) {
reg.name = Military.getName(reg, military);
military.push(reg);
Military.generateNote(reg, pack.states[state]); // add legend
Military.drawRegiment(reg, state);
drawRegiment(reg, state);
toggleAdd();
}

View file

@ -18,7 +18,7 @@ function editRouteGroups() {
// add listeners
byId("routeGroupsEditorAdd").addEventListener("click", addGroup);
byId("routeGroupsEditorBody").on("click", ev => {
const group = ev.target.parentNode.dataset.id;
const group = ev.target.closest(".states")?.dataset.id;
if (ev.target.classList.contains("editStyle")) editStyle("routes", group);
else if (ev.target.classList.contains("removeGroup")) removeGroup(group);
});
@ -72,12 +72,11 @@ function editRouteGroups() {
confirmationDialog({
title: "Remove route group",
message:
"Are you sure you want to remove the entire route group? All routes in this group will be removed. This action can't be reverted.",
"Are you sure you want to remove the entire route group? All routes in this group will be removed.<br>This action can't be reverted",
confirm: "Remove",
onConfirm: () => {
const routes = pack.routes.filter(r => r.group === group);
routes.forEach(r => Routes.remove(r));
if (DEFAULT_GROUPS.includes(group)) routes.select(`#${group}`).remove();
pack.routes.filter(r => r.group === group).forEach(Routes.remove);
if (!DEFAULT_GROUPS.includes(group)) routes.select(`#${group}`).remove();
addLines();
}
});

View file

@ -174,9 +174,10 @@ function editRoute(id) {
function handleControlPointClick() {
const controlPoint = d3.select(this);
const point = controlPoint.datum();
const route = getRoute();
if (route.points.length < 3) return; // can't remove or split point if only 2 points in route
const index = route.points.indexOf(point);
const isSplitMode = byId("routeSplit").classList.contains("pressed");

View file

@ -32,6 +32,7 @@ function overviewRoutes() {
let lines = "";
for (const route of pack.routes) {
if (!route.points || route.points.length < 2) continue;
route.name = route.name || Routes.generateName(route);
route.length = route.length || Routes.getLength(route.i);
const length = rn(route.length * distanceScale) + " " + distanceUnitInput.value;
@ -92,8 +93,8 @@ function overviewRoutes() {
}
function zoomToRoute() {
const r = +this.parentNode.dataset.id;
const route = routes.select("#route" + r).node();
const routeId = +this.parentNode.dataset.id;
const route = routes.select("#route" + routeId).node();
highlightElement(route, 3);
}
@ -111,15 +112,16 @@ function overviewRoutes() {
}
function openRouteEditor() {
const id = "route" + this.parentNode.dataset.id;
editRoute(id);
const routeId = "route" + this.parentNode.dataset.id;
editRoute(routeId);
}
function toggleLockStatus() {
const routeId = +this.parentNode.dataset.id;
const route = pack.routes[routeId];
route.lock = !route.lock;
const route = pack.routes.find(route => route.i === routeId);
if (!route) return;
route.lock = !route.lock;
if (this.classList.contains("icon-lock")) {
this.classList.remove("icon-lock");
this.classList.add("icon-lock-open");

View file

@ -70,6 +70,10 @@ function getColorScheme(scheme = "bright") {
return heightmapColorSchemes[scheme];
}
function getColor(value, scheme = getColorScheme("bright")) {
return scheme(1 - (value < 20 ? value - 5 : value) / 100);
}
// Toggle style sections on element select
styleElementSelect.on("change", selectStyleElement);
@ -114,6 +118,7 @@ function selectStyleElement() {
"armies",
"routes",
"lakes",
"biomes",
"borders",
"cults",
"relig",
@ -952,7 +957,7 @@ styleArmiesSize.on("input", e => {
armies.selectAll("g").remove(); // clear armies layer
pack.states.forEach(s => {
if (!s.i || s.removed || !s.military.length) return;
Military.drawRegiments(s.military, s.i);
drawRegiments(s.military, s.i);
});
});

View file

@ -28,6 +28,7 @@ window.UISubmap = (function () {
$("#submapOptionsDialog").dialog({
title: "Create a submap",
resizable: false,
width: "32em",
position: {my: "center", at: "center", of: "svg"},
buttons: {
Submap: function () {
@ -142,6 +143,7 @@ window.UISubmap = (function () {
fullMap: true,
noLabels: true,
noScaleBar: true,
noVignette: true,
noIce: true
});
@ -282,7 +284,7 @@ window.UISubmap = (function () {
oldstate = null; // destroy old state to free memory
restoreLayers();
drawLayers();
if (ThreeD.options.isOn) ThreeD.redraw();
if ($("#worldConfigurator").is(":visible")) editWorld();
}

View file

@ -75,8 +75,10 @@ toolsContent.addEventListener("click", function (event) {
});
function processFeatureRegeneration(event, button) {
if (button === "regenerateStateLabels") drawStateLabels();
else if (button === "regenerateReliefIcons") {
if (button === "regenerateStateLabels") {
$("#labels").fadeIn();
drawStateLabels();
} else if (button === "regenerateReliefIcons") {
ReliefIcons.draw();
if (!layerIsOn("toggleRelief")) toggleRelief();
} else if (button === "regenerateRoutes") {
@ -126,14 +128,14 @@ function regenerateRoutes() {
function regenerateRivers() {
Rivers.generate();
Lakes.defineGroup();
Rivers.specify();
if (!layerIsOn("toggleRivers")) toggleRivers();
else drawRivers();
Features.specify();
if (layerIsOn("toggleRivers")) drawRivers();
}
function recalculatePopulation() {
rankCells();
pack.burgs.forEach(b => {
if (!b.i || b.removed || b.lock) return;
const i = b.cell;
@ -143,6 +145,8 @@ function recalculatePopulation() {
if (b.port) b.population = b.population * 1.3; // increase port population
b.population = rn(b.population * gauss(2, 3, 0.6, 20, 3), 3);
});
layerIsOn("togglePopulation") ? drawPopulation() : togglePopulation();
}
function regenerateStates() {
@ -152,12 +156,14 @@ function regenerateStates() {
pack.states = newStates;
BurgsAndStates.expandStates();
BurgsAndStates.normalizeStates();
BurgsAndStates.getPoles();
BurgsAndStates.collectStatistics();
BurgsAndStates.assignColors();
BurgsAndStates.generateCampaigns();
BurgsAndStates.generateDiplomacy();
BurgsAndStates.defineStateForms();
BurgsAndStates.generateProvinces(true);
Provinces.generate(true);
Provinces.getPoles();
layerIsOn("toggleStates") ? drawStates() : toggleStates();
layerIsOn("toggleBorders") ? drawBorders() : toggleBorders();
@ -332,9 +338,11 @@ function recreateStates() {
function regenerateProvinces() {
unfog();
BurgsAndStates.generateProvinces(true, true);
drawBorders();
if (layerIsOn("toggleProvinces")) drawProvinces();
Provinces.generate(true, true);
Provinces.getPoles();
if (layerIsOn("toggleBorders")) drawBorders();
layerIsOn("toggleProvinces") ? drawProvinces() : toggleProvinces();
// remove emblems
document.querySelectorAll("[id^=provinceCOA]").forEach(el => el.remove());
@ -437,9 +445,11 @@ function regenerateBurgs() {
BurgsAndStates.specifyBurgs();
BurgsAndStates.defineBurgFeatures();
BurgsAndStates.drawBurgs();
regenerateRoutes();
drawBurgIcons();
drawBurgLabels();
// remove emblems
document.querySelectorAll("[id^=burgCOA]").forEach(el => el.remove());
emblems.selectAll("use").remove();
@ -498,13 +508,13 @@ function regenerateEmblems() {
province.coa.shield = COA.getShield(culture, province.state);
});
if (layerIsOn("toggleEmblems")) drawEmblems(); // redrawEmblems
layerIsOn("toggleEmblems") ? drawEmblems() : toggleEmblems();
}
function regenerateReligions() {
Religions.generate();
if (!layerIsOn("toggleReligions")) toggleReligions();
else drawReligions();
if (layerIsOn("toggleReligions")) drawReligions();
else toggleReligions();
refreshAllEditors();
}
@ -520,7 +530,9 @@ function regenerateCultures() {
function regenerateMilitary() {
Military.generate();
if (!layerIsOn("toggleMilitary")) toggleMilitary();
if (layerIsOn("toggleMilitary")) drawMilitary();
else toggleMilitary();
if (byId("militaryOverviewRefresh").offsetParent) militaryOverviewRefresh.click();
}

View file

@ -66,11 +66,11 @@ function editUnits() {
function changeHeightExponent() {
calculateTemperatures();
if (layerIsOn("toggleTemp")) drawTemp();
if (layerIsOn("toggleTemperature")) drawTemperature();
}
function changeTemperatureScale() {
if (layerIsOn("toggleTemp")) drawTemp();
if (layerIsOn("toggleTemperature")) drawTemperature();
}
function changePopulationRate() {

View file

@ -86,13 +86,13 @@ function editWorld() {
generatePrecipitation();
const heights = new Uint8Array(pack.cells.h);
Rivers.generate();
Lakes.defineGroup();
Rivers.specify();
pack.cells.h = new Float32Array(heights);
Biomes.define();
Features.specify();
if (layerIsOn("toggleTemp")) drawTemp();
if (layerIsOn("togglePrec")) drawPrec();
if (layerIsOn("toggleTemperature")) drawTemperature();
if (layerIsOn("togglePrecipitation")) drawPrecipitation();
if (layerIsOn("toggleBiomes")) drawBiomes();
if (layerIsOn("toggleCoordinates")) drawCoordinates();
if (layerIsOn("toggleRivers")) drawRivers();

View file

@ -472,6 +472,7 @@ function editZones() {
burgs.forEach(b => (b.population = population));
}
if (layerIsOn("togglePopulation")) drawPopulation();
zonesEditorAddLines();
}
}