mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-22 12:01:23 +01:00
Merge branch 'master' into mosuzi-dev
This commit is contained in:
commit
fe5f2133c0
160 changed files with 29893 additions and 427 deletions
|
|
@ -223,18 +223,19 @@ function addBurgsGroup(group) {
|
|||
}
|
||||
|
||||
function removeBurg(id) {
|
||||
const label = document.querySelector("#burgLabels [data-id='" + id + "']");
|
||||
const icon = document.querySelector("#burgIcons [data-id='" + id + "']");
|
||||
const anchor = document.querySelector("#anchors [data-id='" + id + "']");
|
||||
if (label) label.remove();
|
||||
if (icon) icon.remove();
|
||||
if (anchor) anchor.remove();
|
||||
document.querySelector("#burgLabels [data-id='" + id + "']")?.remove();
|
||||
document.querySelector("#burgIcons [data-id='" + id + "']")?.remove();
|
||||
document.querySelector("#anchors [data-id='" + id + "']")?.remove();
|
||||
|
||||
const cells = pack.cells;
|
||||
const burg = pack.burgs[id];
|
||||
|
||||
const cells = pack.cells,
|
||||
burg = pack.burgs[id];
|
||||
burg.removed = true;
|
||||
cells.burg[burg.cell] = 0;
|
||||
|
||||
const noteId = notes.findIndex(note => note.id === `burg${id}`);
|
||||
if (noteId !== -1) notes.splice(noteId, 1);
|
||||
|
||||
if (burg.coa) {
|
||||
const coaId = "burgCOA" + id;
|
||||
if (byId(coaId)) byId(coaId).remove();
|
||||
|
|
@ -1224,7 +1225,7 @@ function refreshAllEditors() {
|
|||
// dynamically loaded editors
|
||||
async function editStates() {
|
||||
if (customization) return;
|
||||
const Editor = await import("../dynamic/editors/states-editor.js?v=1.96.06");
|
||||
const Editor = await import("../dynamic/editors/states-editor.js?v=1.97.06");
|
||||
Editor.open();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -109,8 +109,8 @@ function showElevationProfile(data, routeLen, isRiver) {
|
|||
draw();
|
||||
|
||||
function downloadCSV() {
|
||||
let data =
|
||||
"Point,X,Y,Cell,Height,Height value,Population,Burg,Burg population,Biome,Biome color,Culture,Culture color,Religion,Religion color,Province,Province color,State,State color\n"; // headers
|
||||
let csv =
|
||||
"Id,x,y,lat,lon,Cell,Height,Height value,Population,Burg,Burg population,Biome,Biome color,Culture,Culture color,Religion,Religion color,Province,Province color,State,State color\n"; // headers
|
||||
|
||||
for (let k = 0; k < chartData.points.length; k++) {
|
||||
let cell = chartData.cell[k];
|
||||
|
|
@ -123,35 +123,39 @@ function showElevationProfile(data, routeLen, isRiver) {
|
|||
let pop = pack.cells.pop[cell];
|
||||
let h = pack.cells.h[cell];
|
||||
|
||||
data += k + 1 + ",";
|
||||
data += chartData.points[k][0] + ",";
|
||||
data += chartData.points[k][1] + ",";
|
||||
data += cell + ",";
|
||||
data += getHeight(h) + ",";
|
||||
data += h + ",";
|
||||
data += rn(pop * populationRate) + ",";
|
||||
csv += k + 1 + ",";
|
||||
const [x, y] = pack.cells.p[data[k]];
|
||||
csv += x + ",";
|
||||
csv += y + ",";
|
||||
const lat = getLatitude(y, 2);
|
||||
const lon = getLongitude(x, 2);
|
||||
csv += lat + ",";
|
||||
csv += lon + ",";
|
||||
csv += cell + ",";
|
||||
csv += getHeight(h) + ",";
|
||||
csv += h + ",";
|
||||
csv += rn(pop * populationRate) + ",";
|
||||
if (burg) {
|
||||
data += pack.burgs[burg].name + ",";
|
||||
data += pack.burgs[burg].population * populationRate * urbanization + ",";
|
||||
csv += pack.burgs[burg].name + ",";
|
||||
csv += pack.burgs[burg].population * populationRate * urbanization + ",";
|
||||
} else {
|
||||
data += ",0,";
|
||||
csv += ",0,";
|
||||
}
|
||||
data += biomesData.name[biome] + ",";
|
||||
data += biomesData.color[biome] + ",";
|
||||
data += pack.cultures[culture].name + ",";
|
||||
data += pack.cultures[culture].color + ",";
|
||||
data += pack.religions[religion].name + ",";
|
||||
data += pack.religions[religion].color + ",";
|
||||
data += pack.provinces[province].name + ",";
|
||||
data += pack.provinces[province].color + ",";
|
||||
data += pack.states[state].name + ",";
|
||||
data += pack.states[state].color + ",";
|
||||
|
||||
data = data + "\n";
|
||||
csv += biomesData.name[biome] + ",";
|
||||
csv += biomesData.color[biome] + ",";
|
||||
csv += pack.cultures[culture].name + ",";
|
||||
csv += pack.cultures[culture].color + ",";
|
||||
csv += pack.religions[religion].name + ",";
|
||||
csv += pack.religions[religion].color + ",";
|
||||
csv += pack.provinces[province].name + ",";
|
||||
csv += pack.provinces[province].color + ",";
|
||||
csv += pack.states[state].name + ",";
|
||||
csv += pack.states[state].color + ",";
|
||||
csv += "\n";
|
||||
}
|
||||
|
||||
const name = getFileName("elevation profile") + ".csv";
|
||||
downloadFile(data, name);
|
||||
downloadFile(csv, name);
|
||||
}
|
||||
|
||||
function draw() {
|
||||
|
|
@ -335,10 +339,7 @@ function showElevationProfile(data, routeLen, isRiver) {
|
|||
.attr("transform", "translate(" + xOffset + "," + parseInt(chartHeight + +yOffset + 20) + ")")
|
||||
.call(xAxis)
|
||||
.selectAll("text")
|
||||
.style("text-anchor", "center")
|
||||
.attr("transform", function (d) {
|
||||
return "rotate(0)"; // used to rotate labels, - anti-clockwise, + clockwise
|
||||
});
|
||||
.style("text-anchor", "center");
|
||||
|
||||
chart
|
||||
.append("g")
|
||||
|
|
|
|||
|
|
@ -1525,10 +1525,6 @@ function toggleCompass(event) {
|
|||
if (!layerIsOn("toggleCompass")) {
|
||||
turnButtonOn("toggleCompass");
|
||||
$("#compass").fadeIn();
|
||||
if (!compass.selectAll("*").size()) {
|
||||
compass.append("use").attr("xlink:href", "#rose");
|
||||
shiftCompass();
|
||||
}
|
||||
if (event && isCtrlClick(event)) editStyle("compass");
|
||||
} else {
|
||||
if (event && isCtrlClick(event)) {
|
||||
|
|
|
|||
|
|
@ -55,7 +55,9 @@ function overviewMilitary() {
|
|||
for (const u of options.military) {
|
||||
const label = capitalize(u.name.replace(/_/g, " "));
|
||||
insert(
|
||||
`<div data-tip="State ${u.name} units number. Click to sort" class="sortable removable" data-sortby="${u.name}">${label} </div>`
|
||||
`<div data-tip="State ${
|
||||
u.name
|
||||
} units number. Click to sort" class="sortable removable" data-sortby="${u.name.toLowerCase()}">${label} </div>`
|
||||
);
|
||||
}
|
||||
header.querySelectorAll(".removable").forEach(function (e) {
|
||||
|
|
@ -77,7 +79,7 @@ function overviewMilitary() {
|
|||
const total = options.military.reduce((s, u) => s + getForces(u) * u.crew, 0);
|
||||
const rate = (total / population) * 100;
|
||||
|
||||
const sortData = options.military.map(u => `data-${u.name}="${getForces(u)}"`).join(" ");
|
||||
const sortData = options.military.map(u => `data-${u.name.toLowerCase()}="${getForces(u)}"`).join(" ");
|
||||
const lineData = options.military
|
||||
.map(u => `<div data-type="${u.name}" data-tip="State ${u.name} units number">${getForces(u)}</div>`)
|
||||
.join(" ");
|
||||
|
|
@ -469,7 +471,7 @@ function overviewMilitary() {
|
|||
body.querySelectorAll(":scope > div").forEach(function (el) {
|
||||
data += el.dataset.id + ",";
|
||||
data += el.dataset.state + ",";
|
||||
data += units.map(u => el.dataset[u]).join(",") + ",";
|
||||
data += units.map(u => el.dataset[u.toLowerCase()]).join(",") + ",";
|
||||
data += el.dataset.total + ",";
|
||||
data += el.dataset.population + ",";
|
||||
data += rn(el.dataset.rate, 2) + "%,";
|
||||
|
|
|
|||
|
|
@ -79,11 +79,13 @@ function editNotes(id, name) {
|
|||
}
|
||||
|
||||
if (window.tinymce) {
|
||||
window.tinymce._setBaseUrl("https://azgaar.github.io/Fantasy-Map-Generator/libs/tinymce");
|
||||
tinymce.init({
|
||||
license_key: "gpl",
|
||||
selector: "#notesLegend",
|
||||
height: "90%",
|
||||
menubar: false,
|
||||
plugins: `autolink lists link charmap print code fullscreen image link media table paste hr wordcount`,
|
||||
plugins: `autolink lists link charmap code fullscreen image link media table wordcount`,
|
||||
toolbar: `code | undo redo | removeformat | bold italic strikethrough | forecolor backcolor | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image media table | fontselect fontsizeselect | blockquote hr charmap | print fullscreen`,
|
||||
media_alt_source: false,
|
||||
media_poster: false,
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ document
|
|||
|
||||
// show popup with a list of Patreon supportes (updated manually)
|
||||
async function showSupporters() {
|
||||
const {supporters} = await import("../dynamic/supporters.js?v=1.93.08");
|
||||
const {supporters} = await import("../dynamic/supporters.js?v=1.97.14");
|
||||
const list = supporters.split("\n").sort();
|
||||
const columns = window.innerWidth < 800 ? 2 : 5;
|
||||
|
||||
|
|
@ -119,9 +119,9 @@ function updateOutputToFollowInput(ev) {
|
|||
|
||||
// Option listeners
|
||||
const optionsContent = byId("optionsContent");
|
||||
optionsContent.addEventListener("input", function (event) {
|
||||
const id = event.target.id;
|
||||
const value = event.target.value;
|
||||
|
||||
optionsContent.addEventListener("input", event => {
|
||||
const {id, value} = event.target;
|
||||
if (id === "mapWidthInput" || id === "mapHeightInput") mapSizeInputChange();
|
||||
else if (id === "pointsInput") changeCellsDensity(+value);
|
||||
else if (id === "culturesSet") changeCultureSet();
|
||||
|
|
@ -133,10 +133,8 @@ optionsContent.addEventListener("input", function (event) {
|
|||
else if (id === "transparencyInput") changeDialogsTheme(themeColorInput.value, value);
|
||||
});
|
||||
|
||||
optionsContent.addEventListener("change", function (event) {
|
||||
const id = event.target.id;
|
||||
const value = event.target.value;
|
||||
|
||||
optionsContent.addEventListener("change", event => {
|
||||
const {id, value} = event.target;
|
||||
if (id === "zoomExtentMin" || id === "zoomExtentMax") changeZoomExtent(value);
|
||||
else if (id === "optionsSeed") generateMapWithSeed("seed change");
|
||||
else if (id === "uiSizeInput" || id === "uiSizeOutput") changeUiSize(value);
|
||||
|
|
@ -146,8 +144,8 @@ optionsContent.addEventListener("change", function (event) {
|
|||
else if (id === "stateLabelsModeInput") options.stateLabelsMode = value;
|
||||
});
|
||||
|
||||
optionsContent.addEventListener("click", function (event) {
|
||||
const id = event.target.id;
|
||||
optionsContent.addEventListener("click", event => {
|
||||
const {id} = event.target;
|
||||
if (id === "restoreDefaultCanvasSize") restoreDefaultCanvasSize();
|
||||
else if (id === "optionsMapHistory") showSeedHistoryDialog();
|
||||
else if (id === "optionsCopySeed") copyMapURL();
|
||||
|
|
@ -327,6 +325,7 @@ const cellsDensityMap = {
|
|||
};
|
||||
|
||||
function changeCellsDensity(value) {
|
||||
pointsInput.value = value;
|
||||
const cells = cellsDensityMap[value] || 1000;
|
||||
pointsInput.dataset.cells = cells;
|
||||
pointsOutputFormatted.value = getCellsDensityValue(cells);
|
||||
|
|
@ -536,6 +535,7 @@ function applyStoredOptions() {
|
|||
const key = localStorage.key(i);
|
||||
|
||||
if (key === "speakerVoice") continue;
|
||||
|
||||
const input = byId(key + "Input") || byId(key);
|
||||
const output = byId(key + "Output");
|
||||
|
||||
|
|
@ -544,6 +544,8 @@ function applyStoredOptions() {
|
|||
if (output) output.value = value;
|
||||
lock(key);
|
||||
|
||||
if (key === "points") changeCellsDensity(+value);
|
||||
|
||||
// add saved style presets to options
|
||||
if (key.slice(0, 5) === "style") applyOption(stylePreset, key, key.slice(5));
|
||||
}
|
||||
|
|
@ -581,6 +583,7 @@ function randomizeOptions() {
|
|||
const randomize = new URL(window.location.href).searchParams.get("options") === "default"; // ignore stored options
|
||||
|
||||
// 'Options' settings
|
||||
if (randomize || !locked("points")) changeCellsDensity(4); // reset to default, no need to randomize
|
||||
if (randomize || !locked("template")) randomizeHeightmapTemplate();
|
||||
if (randomize || !locked("regions")) regionsInput.value = regionsOutput.value = gauss(18, 5, 2, 30);
|
||||
if (randomize || !locked("provinces")) provincesInput.value = provincesOutput.value = gauss(20, 10, 20, 100);
|
||||
|
|
@ -774,7 +777,7 @@ function showExportPane() {
|
|||
}
|
||||
|
||||
async function exportToJson(type) {
|
||||
const {exportToJson} = await import("../dynamic/export-json.js?v=1.96.00");
|
||||
const {exportToJson} = await import("../dynamic/export-json.js?v=1.97.08");
|
||||
exportToJson(type);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,9 +8,10 @@ function editRegiment(selector) {
|
|||
armies.selectAll(":scope > g > g").call(d3.drag().on("drag", dragRegiment));
|
||||
elSelected = selector ? document.querySelector(selector) : d3.event.target.parentElement; // select g element
|
||||
if (!pack.states[elSelected.dataset.state]) return;
|
||||
if (!regiment()) return;
|
||||
updateRegimentData(regiment());
|
||||
if (!getRegiment()) return;
|
||||
updateRegimentData(getRegiment());
|
||||
drawBase();
|
||||
drawRotationControl();
|
||||
|
||||
$("#regimentEditor").dialog({
|
||||
title: "Edit Regiment",
|
||||
|
|
@ -37,8 +38,8 @@ function editRegiment(selector) {
|
|||
document.getElementById("regimentRemove").addEventListener("click", removeRegiment);
|
||||
|
||||
// get regiment data element
|
||||
function regiment() {
|
||||
return pack.states[elSelected.dataset.state].military.find(r => r.i == elSelected.dataset.id);
|
||||
function getRegiment() {
|
||||
return pack.states[elSelected.dataset.state]?.military.find(r => r.i == elSelected.dataset.id);
|
||||
}
|
||||
|
||||
function updateRegimentData(regiment) {
|
||||
|
|
@ -60,7 +61,7 @@ function editRegiment(selector) {
|
|||
}
|
||||
|
||||
function drawBase() {
|
||||
const reg = regiment();
|
||||
const reg = getRegiment();
|
||||
const clr = pack.states[elSelected.dataset.state].color;
|
||||
const base = viewbox
|
||||
.insert("g", "g#armies")
|
||||
|
|
@ -69,12 +70,8 @@ function editRegiment(selector) {
|
|||
.attr("stroke", "#000")
|
||||
.attr("cursor", "move");
|
||||
base
|
||||
.on("mouseenter", () => {
|
||||
tip("Regiment base. Drag to re-base the regiment", true);
|
||||
})
|
||||
.on("mouseleave", () => {
|
||||
tip("", true);
|
||||
});
|
||||
.on("mouseenter", () => tip("Regiment base. Drag to re-base the regiment", true))
|
||||
.on("mouseleave", () => tip("", true));
|
||||
|
||||
base
|
||||
.append("line")
|
||||
|
|
@ -92,8 +89,42 @@ function editRegiment(selector) {
|
|||
.call(d3.drag().on("drag", dragBase));
|
||||
}
|
||||
|
||||
function drawRotationControl() {
|
||||
const reg = getRegiment();
|
||||
const {x, width, y, height} = elSelected.getBBox();
|
||||
|
||||
debug
|
||||
.append("circle")
|
||||
.attr("id", "rotationControl")
|
||||
.attr("cx", x + width)
|
||||
.attr("cy", y + height / 2)
|
||||
.attr("r", 1)
|
||||
.attr("opacity", 1)
|
||||
.attr("fill", "yellow")
|
||||
.attr("stroke-width", 0.3)
|
||||
.attr("stroke", "black")
|
||||
.attr("cursor", "alias")
|
||||
.attr("transform", `rotate(${reg.angle || 0})`)
|
||||
.attr("transform-origin", `${reg.x}px ${reg.y}px`)
|
||||
.on("mouseenter", () => tip("Drag to rotate the regiment", true))
|
||||
.on("mouseleave", () => tip("", true))
|
||||
.call(d3.drag().on("start", rotateRegiment));
|
||||
}
|
||||
|
||||
function rotateRegiment() {
|
||||
const reg = getRegiment();
|
||||
|
||||
d3.event.on("drag", function () {
|
||||
const {x, y} = d3.event;
|
||||
const angle = rn(Math.atan2(y - reg.y, x - reg.x) * (180 / Math.PI), 2);
|
||||
elSelected.setAttribute("transform", `rotate(${angle})`);
|
||||
this.setAttribute("transform", `rotate(${angle})`);
|
||||
reg.angle = rn(angle, 2);
|
||||
});
|
||||
}
|
||||
|
||||
function changeType() {
|
||||
const reg = regiment();
|
||||
const reg = getRegiment();
|
||||
reg.n = +!reg.n;
|
||||
document.getElementById("regimentType").className = reg.n ? "icon-anchor" : "icon-users";
|
||||
|
||||
|
|
@ -110,11 +141,11 @@ function editRegiment(selector) {
|
|||
}
|
||||
|
||||
function changeName() {
|
||||
elSelected.dataset.name = regiment().name = this.value;
|
||||
elSelected.dataset.name = getRegiment().name = this.value;
|
||||
}
|
||||
|
||||
function restoreName() {
|
||||
const reg = regiment(),
|
||||
const reg = getRegiment(),
|
||||
regs = pack.states[elSelected.dataset.state].military;
|
||||
const name = Military.getName(reg, regs);
|
||||
elSelected.dataset.name = reg.name = document.getElementById("regimentName").value = name;
|
||||
|
|
@ -129,12 +160,12 @@ function editRegiment(selector) {
|
|||
|
||||
function changeEmblem() {
|
||||
const emblem = document.getElementById("regimentEmblem").value;
|
||||
regiment().icon = elSelected.querySelector(".regimentIcon").innerHTML = emblem;
|
||||
getRegiment().icon = elSelected.querySelector(".regimentIcon").innerHTML = emblem;
|
||||
}
|
||||
|
||||
function changeUnit() {
|
||||
const u = this.dataset.u;
|
||||
const reg = regiment();
|
||||
const reg = getRegiment();
|
||||
reg.u[u] = +this.value || 0;
|
||||
reg.a = d3.sum(Object.values(reg.u));
|
||||
elSelected.querySelector("text").innerHTML = Military.getTotal(reg);
|
||||
|
|
@ -143,7 +174,7 @@ function editRegiment(selector) {
|
|||
}
|
||||
|
||||
function splitRegiment() {
|
||||
const reg = regiment(),
|
||||
const reg = getRegiment(),
|
||||
u1 = reg.u;
|
||||
const state = +elSelected.dataset.state,
|
||||
military = pack.states[state].military;
|
||||
|
|
@ -206,8 +237,7 @@ function editRegiment(selector) {
|
|||
function addRegimentOnClick() {
|
||||
const point = d3.mouse(this);
|
||||
const cell = findCell(point[0], point[1]);
|
||||
const x = pack.cells.p[cell][0],
|
||||
y = pack.cells.p[cell][1];
|
||||
const [x, y] = pack.cells.p[cell];
|
||||
const state = +elSelected.dataset.state,
|
||||
military = pack.states[state].military;
|
||||
const i = military.length ? last(military).i + 1 : 0;
|
||||
|
|
@ -254,7 +284,7 @@ function editRegiment(selector) {
|
|||
return;
|
||||
}
|
||||
|
||||
const attacker = regiment();
|
||||
const attacker = getRegiment();
|
||||
const defender = pack.states[regSelected.dataset.state].military.find(r => r.i == regSelected.dataset.id);
|
||||
if (!attacker.a || !defender.a) {
|
||||
tip("Regiment has no troops to battle", false, "error");
|
||||
|
|
@ -322,7 +352,7 @@ function editRegiment(selector) {
|
|||
return;
|
||||
}
|
||||
|
||||
const reg = regiment(); // reg to be attached
|
||||
const reg = getRegiment(); // reg to be attached
|
||||
const sel = pack.states[newState].military.find(r => r.i == regSelected.dataset.id); // reg to attach to
|
||||
|
||||
for (const unit of options.military) {
|
||||
|
|
@ -349,11 +379,11 @@ function editRegiment(selector) {
|
|||
if (index != -1) notes.splice(index, 1);
|
||||
|
||||
const s = pack.states[elSelected.dataset.state];
|
||||
Military.generateNote(regiment(), s);
|
||||
Military.generateNote(getRegiment(), s);
|
||||
}
|
||||
|
||||
function editLegend() {
|
||||
editNotes(elSelected.id, regiment().name);
|
||||
editNotes(elSelected.id, getRegiment().name);
|
||||
}
|
||||
|
||||
function removeRegiment() {
|
||||
|
|
@ -365,7 +395,7 @@ function editRegiment(selector) {
|
|||
Remove: function () {
|
||||
$(this).dialog("close");
|
||||
const military = pack.states[elSelected.dataset.state].military;
|
||||
const regIndex = military.indexOf(regiment());
|
||||
const regIndex = military.indexOf(getRegiment());
|
||||
if (regIndex === -1) return;
|
||||
military.splice(regIndex, 1);
|
||||
|
||||
|
|
@ -392,8 +422,6 @@ function editRegiment(selector) {
|
|||
const size = +armies.attr("box-size");
|
||||
const w = reg.n ? size * 4 : size * 6;
|
||||
const h = size * 2;
|
||||
const x1 = x => rn(x - w / 2, 2);
|
||||
const y1 = y => rn(y - size, 2);
|
||||
|
||||
const baseRect = this.querySelector("rect");
|
||||
const text = this.querySelector("text");
|
||||
|
|
@ -402,26 +430,37 @@ function editRegiment(selector) {
|
|||
|
||||
const self = elSelected === this;
|
||||
const baseLine = viewbox.select("g#regimentBase > line");
|
||||
const rotationControl = debug.select("#rotationControl");
|
||||
|
||||
d3.event.on("drag", function () {
|
||||
const x = (reg.x = d3.event.x),
|
||||
y = (reg.y = d3.event.y);
|
||||
const {x, y} = d3.event;
|
||||
reg.x = x;
|
||||
reg.y = y;
|
||||
const x1 = rn(x - w / 2, 2);
|
||||
const y1 = rn(y - size, 2);
|
||||
|
||||
baseRect.setAttribute("x", x1(x));
|
||||
baseRect.setAttribute("y", y1(y));
|
||||
this.setAttribute("transform-origin", `${x}px ${y}px`);
|
||||
baseRect.setAttribute("x", x1);
|
||||
baseRect.setAttribute("y", y1);
|
||||
text.setAttribute("x", x);
|
||||
text.setAttribute("y", y);
|
||||
iconRect.setAttribute("x", x1(x) - h);
|
||||
iconRect.setAttribute("y", y1(y));
|
||||
icon.setAttribute("x", x1(x) - size);
|
||||
iconRect.setAttribute("x", x1 - h);
|
||||
iconRect.setAttribute("y", y1);
|
||||
icon.setAttribute("x", x1 - size);
|
||||
icon.setAttribute("y", y);
|
||||
if (self) baseLine.attr("x2", x).attr("y2", y);
|
||||
if (self) {
|
||||
baseLine.attr("x2", x).attr("y2", y);
|
||||
rotationControl
|
||||
.attr("cx", x1 + w)
|
||||
.attr("cy", y)
|
||||
.attr("transform-origin", `${x}px ${y}px`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function dragBase() {
|
||||
const baseLine = viewbox.select("g#regimentBase > line");
|
||||
const reg = regiment();
|
||||
const reg = getRegiment();
|
||||
|
||||
d3.event.on("drag", function () {
|
||||
this.setAttribute("cx", d3.event.x);
|
||||
|
|
@ -436,9 +475,10 @@ function editRegiment(selector) {
|
|||
}
|
||||
|
||||
function closeEditor() {
|
||||
debug.selectAll("*").remove();
|
||||
viewbox.selectAll("g#regimentBase").remove();
|
||||
armies.selectAll(":scope > g").classed("draggable", false);
|
||||
armies.selectAll("g>g").call(d3.drag().on("drag", null));
|
||||
viewbox.selectAll("g#regimentBase").remove();
|
||||
document.getElementById("regimentAdd").classList.remove("pressed");
|
||||
document.getElementById("regimentAttack").classList.remove("pressed");
|
||||
document.getElementById("regimentAttach").classList.remove("pressed");
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ async function getStylePreset(desiredPreset) {
|
|||
|
||||
async function fetchSystemPreset(preset) {
|
||||
try {
|
||||
const res = await fetch(`./styles/${preset}.json`);
|
||||
const res = await fetch(`./styles/${preset}.json?v=${version}`);
|
||||
return await res.json();
|
||||
} catch (err) {
|
||||
throw new Error("Cannot fetch style preset", preset);
|
||||
|
|
@ -198,7 +198,7 @@ function addStylePreset() {
|
|||
"mask"
|
||||
],
|
||||
"#compass": ["opacity", "transform", "filter", "mask", "shape-rendering"],
|
||||
"#rose": ["transform"],
|
||||
"#compass > use": ["transform"],
|
||||
"#relig": ["opacity", "stroke", "stroke-width", "filter"],
|
||||
"#cults": ["opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter"],
|
||||
"#landmass": ["opacity", "fill", "filter"],
|
||||
|
|
|
|||
|
|
@ -335,29 +335,44 @@ function regenerateProvinces() {
|
|||
}
|
||||
|
||||
function regenerateBurgs() {
|
||||
const {cells, states} = pack;
|
||||
const lockedburgs = pack.burgs.filter(b => b.i && !b.removed && b.lock);
|
||||
const {cells, features, burgs, states, provinces} = pack;
|
||||
|
||||
rankCells();
|
||||
|
||||
cells.burg = new Uint16Array(cells.i.length);
|
||||
const burgs = (pack.burgs = [0]); // clear burgs array
|
||||
states.filter(s => s.i).forEach(s => (s.capital = 0)); // clear state capitals
|
||||
pack.provinces.filter(p => p.i).forEach(p => (p.burg = 0)); // clear province capitals
|
||||
// remove notes for unlocked burgs
|
||||
notes = notes.filter(note => {
|
||||
if (note.id.startsWith("burg")) {
|
||||
const burgId = +note.id.slice(4);
|
||||
return burgs[burgId]?.lock;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
const newBurgs = [0]; // new burgs array
|
||||
const burgsTree = d3.quadtree();
|
||||
|
||||
// add locked burgs
|
||||
cells.burg = new Uint16Array(cells.i.length); // clear cells burg data
|
||||
states.filter(s => s.i).forEach(s => (s.capital = 0)); // clear state capitals
|
||||
provinces.filter(p => p.i).forEach(p => (p.burg = 0)); // clear province capitals
|
||||
|
||||
// readd locked burgs
|
||||
const lockedburgs = burgs.filter(burg => burg.i && !burg.removed && burg.lock);
|
||||
for (let j = 0; j < lockedburgs.length; j++) {
|
||||
const id = burgs.length;
|
||||
const lockedBurg = lockedburgs[j];
|
||||
lockedBurg.i = id;
|
||||
burgs.push(lockedBurg);
|
||||
const newId = newBurgs.length;
|
||||
|
||||
const noteIndex = notes.findIndex(note => note.id === `burg${lockedBurg.i}`);
|
||||
if (noteIndex !== -1) notes[noteIndex].id = `burg${newId}`;
|
||||
|
||||
lockedBurg.i = newId;
|
||||
newBurgs.push(lockedBurg);
|
||||
|
||||
burgsTree.add([lockedBurg.x, lockedBurg.y]);
|
||||
cells.burg[lockedBurg.cell] = id;
|
||||
cells.burg[lockedBurg.cell] = newId;
|
||||
|
||||
if (lockedBurg.capital) {
|
||||
const stateId = lockedBurg.state;
|
||||
states[stateId].capital = id;
|
||||
states[stateId].capital = newId;
|
||||
states[stateId].center = lockedBurg.cell;
|
||||
}
|
||||
}
|
||||
|
|
@ -370,8 +385,8 @@ function regenerateBurgs() {
|
|||
existingStatesCount;
|
||||
const spacing = (graphWidth + graphHeight) / 150 / (burgsCount ** 0.7 / 66); // base min distance between towns
|
||||
|
||||
for (let i = 0; i < sorted.length && burgs.length < burgsCount; i++) {
|
||||
const id = burgs.length;
|
||||
for (let i = 0; i < sorted.length && newBurgs.length < burgsCount; i++) {
|
||||
const id = newBurgs.length;
|
||||
const cell = sorted[i];
|
||||
const [x, y] = cells.p[cell];
|
||||
|
||||
|
|
@ -387,24 +402,27 @@ function regenerateBurgs() {
|
|||
|
||||
const culture = cells.culture[cell];
|
||||
const name = Names.getCulture(culture);
|
||||
burgs.push({cell, x, y, state: stateId, i: id, culture, name, capital, feature: cells.f[cell]});
|
||||
newBurgs.push({cell, x, y, state: stateId, i: id, culture, name, capital, feature: cells.f[cell]});
|
||||
burgsTree.add([x, y]);
|
||||
cells.burg[cell] = id;
|
||||
}
|
||||
|
||||
pack.burgs = newBurgs; // assign new burgs array
|
||||
|
||||
// add a capital at former place for states without added capitals
|
||||
states
|
||||
.filter(s => s.i && !s.removed && !s.capital)
|
||||
.forEach(s => {
|
||||
const burg = addBurg([cells.p[s.center][0], cells.p[s.center][1]]); // add new burg
|
||||
s.capital = burg;
|
||||
s.center = pack.burgs[burg].cell;
|
||||
pack.burgs[burg].capital = 1;
|
||||
pack.burgs[burg].state = s.i;
|
||||
moveBurgToGroup(burg, "cities");
|
||||
const [x, y] = cells.p[s.center];
|
||||
const burgId = addBurg([x, y]);
|
||||
s.capital = burgId;
|
||||
s.center = pack.burgs[burgId].cell;
|
||||
pack.burgs[burgId].capital = 1;
|
||||
pack.burgs[burgId].state = s.i;
|
||||
moveBurgToGroup(burgId, "cities");
|
||||
});
|
||||
|
||||
pack.features.forEach(f => {
|
||||
features.forEach(f => {
|
||||
if (f.port) f.port = 0; // reset features ports counter
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -5,18 +5,17 @@ function editWorld() {
|
|||
title: "Configure World",
|
||||
resizable: false,
|
||||
width: "minmax(40em, 85vw)",
|
||||
buttons: {
|
||||
"Whole World": () => applyWorldPreset(100, 50),
|
||||
Northern: () => applyWorldPreset(33, 25),
|
||||
Tropical: () => applyWorldPreset(33, 50),
|
||||
Southern: () => applyWorldPreset(33, 75)
|
||||
},
|
||||
buttons: {"Update world": updateWorld},
|
||||
open: function () {
|
||||
const buttons = $(this).dialog("widget").find(".ui-dialog-buttonset > button");
|
||||
buttons[0].addEventListener("mousemove", () => tip("Click to set map size to cover the whole World"));
|
||||
buttons[1].addEventListener("mousemove", () => tip("Click to set map size to cover the Northern latitudes"));
|
||||
buttons[2].addEventListener("mousemove", () => tip("Click to set map size to cover the Tropical latitudes"));
|
||||
buttons[3].addEventListener("mousemove", () => tip("Click to set map size to cover the Southern latitudes"));
|
||||
const checkbox = /* html */ `<div class="dontAsk" data-tip="Automatically update world on input changes and button clicks">
|
||||
<input id="wcAutoChange" class="checkbox" type="checkbox" checked />
|
||||
<label for="wcAutoChange" class="checkbox-label"><i>auto-apply changes</i></label>
|
||||
</div>`;
|
||||
const pane = this.parentElement.querySelector(".ui-dialog-buttonpane");
|
||||
pane.insertAdjacentHTML("afterbegin", checkbox);
|
||||
|
||||
const button = this.parentElement.querySelector(".ui-dialog-buttonset > button");
|
||||
button.on("mousemove", () => tip("Apply curreny settings to the map"));
|
||||
},
|
||||
close: function () {
|
||||
$(this).dialog("destroy");
|
||||
|
|
@ -34,12 +33,16 @@ function editWorld() {
|
|||
if (modules.editWorld) return;
|
||||
modules.editWorld = true;
|
||||
|
||||
byId("worldControls").addEventListener("input", e => updateWorld(e.target));
|
||||
globe.select("#globeWindArrows").on("click", changeWind);
|
||||
globe.select("#globeWindArrows").on("click", handleWindChange);
|
||||
globe.select("#globeGraticule").attr("d", round(path(d3.geoGraticule()()))); // globe graticule
|
||||
updateWindDirections();
|
||||
|
||||
byId("restoreWinds").addEventListener("click", restoreDefaultWinds);
|
||||
byId("worldControls").on("input", handleControlsChange);
|
||||
byId("restoreWinds").on("click", restoreDefaultWinds);
|
||||
byId("wcWholeWorld").on("click", () => applyWorldPreset(100, 50));
|
||||
byId("wcNorthern").on("click", () => applyWorldPreset(33, 25));
|
||||
byId("wcTropical").on("click", () => applyWorldPreset(33, 50));
|
||||
byId("wcSouthern").on("click", () => applyWorldPreset(33, 75));
|
||||
|
||||
function updateInputValues() {
|
||||
byId("temperatureEquatorInput").value = options.temperatureEquator;
|
||||
|
|
@ -55,27 +58,27 @@ function editWorld() {
|
|||
byId("temperatureSouthPoleF").innerText = convertTemperature(options.temperatureSouthPole, "°F");
|
||||
}
|
||||
|
||||
function updateWorld(el) {
|
||||
if (el?.dataset.stored) {
|
||||
const stored = el.dataset.stored;
|
||||
byId(stored + "Input").value = el.value;
|
||||
byId(stored + "Output").value = el.value;
|
||||
lock(el.dataset.stored);
|
||||
function handleControlsChange({target}) {
|
||||
const stored = target.dataset.stored;
|
||||
byId(stored + "Input").value = target.value;
|
||||
byId(stored + "Output").value = target.value;
|
||||
lock(stored);
|
||||
|
||||
if (stored === "temperatureEquator") {
|
||||
options.temperatureEquator = Number(el.value);
|
||||
byId("temperatureEquatorF").innerText = convertTemperature(options.temperatureEquator, "°F");
|
||||
}
|
||||
if (stored === "temperatureNorthPole") {
|
||||
options.temperatureNorthPole = Number(el.value);
|
||||
byId("temperatureNorthPoleF").innerText = convertTemperature(options.temperatureNorthPole, "°F");
|
||||
}
|
||||
if (stored === "temperatureSouthPole") {
|
||||
options.temperatureSouthPole = Number(el.value);
|
||||
byId("temperatureSouthPoleF").innerText = convertTemperature(options.temperatureSouthPole, "°F");
|
||||
}
|
||||
if (stored === "temperatureEquator") {
|
||||
options.temperatureEquator = Number(target.value);
|
||||
byId("temperatureEquatorF").innerText = convertTemperature(options.temperatureEquator, "°F");
|
||||
} else if (stored === "temperatureNorthPole") {
|
||||
options.temperatureNorthPole = Number(target.value);
|
||||
byId("temperatureNorthPoleF").innerText = convertTemperature(options.temperatureNorthPole, "°F");
|
||||
} else if (stored === "temperatureSouthPole") {
|
||||
options.temperatureSouthPole = Number(target.value);
|
||||
byId("temperatureSouthPoleF").innerText = convertTemperature(options.temperatureSouthPole, "°F");
|
||||
}
|
||||
|
||||
if (byId("wcAutoChange").checked) updateWorld();
|
||||
}
|
||||
|
||||
function updateWorld() {
|
||||
updateGlobeTemperature();
|
||||
updateGlobePosition();
|
||||
calculateTemperatures();
|
||||
|
|
@ -113,9 +116,11 @@ function editWorld() {
|
|||
|
||||
function toKilometer(v) {
|
||||
if (unit === "km") return v;
|
||||
else if (unit === "mi") return v * 1.60934;
|
||||
else if (unit === "lg") return v * 5.556;
|
||||
else if (unit === "vr") return v * 1.0668;
|
||||
if (unit === "mi") return v * 1.60934;
|
||||
if (unit === "lg") return v * 4.828;
|
||||
if (unit === "vr") return v * 1.0668;
|
||||
if (unit === "nmi") return v * 1.852;
|
||||
if (unit === "nlg") return v * 5.556;
|
||||
return 0; // 0 if distanceUnitInput is a custom unit
|
||||
}
|
||||
|
||||
|
|
@ -128,6 +133,7 @@ function editWorld() {
|
|||
[mc.lonW, mc.latN],
|
||||
[mc.lonE, mc.latS]
|
||||
]);
|
||||
|
||||
globe.select("#globeArea").attr("d", round(path(area.outline()))); // map area
|
||||
}
|
||||
|
||||
|
|
@ -161,21 +167,22 @@ function editWorld() {
|
|||
});
|
||||
}
|
||||
|
||||
function changeWind() {
|
||||
function handleWindChange() {
|
||||
const arrow = d3.event.target.nextElementSibling;
|
||||
const tier = +arrow.dataset.tier;
|
||||
options.winds[tier] = (options.winds[tier] + 45) % 360;
|
||||
const tr = parseTransform(arrow.getAttribute("transform"));
|
||||
arrow.setAttribute("transform", `rotate(${options.winds[tier]} ${tr[1]} ${tr[2]})`);
|
||||
localStorage.setItem("winds", options.winds);
|
||||
|
||||
const mapTiers = d3.range(mapCoordinates.latN, mapCoordinates.latS, -30).map(c => ((90 - c) / 30) | 0);
|
||||
if (mapTiers.includes(tier)) updateWorld();
|
||||
if (byId("wcAutoChange").checked && mapTiers.includes(tier)) updateWorld();
|
||||
}
|
||||
|
||||
function restoreDefaultWinds() {
|
||||
const defaultWinds = [225, 45, 225, 315, 135, 315];
|
||||
const mapTiers = d3.range(mapCoordinates.latN, mapCoordinates.latS, -30).map(c => ((90 - c) / 30) | 0);
|
||||
const update = mapTiers.some(t => options.winds[t] != defaultWinds[t]);
|
||||
const update = byId("wcAutoChange").checked && mapTiers.some(t => options.winds[t] != defaultWinds[t]);
|
||||
options.winds = defaultWinds;
|
||||
updateWindDirections();
|
||||
if (update) updateWorld();
|
||||
|
|
@ -186,6 +193,6 @@ function editWorld() {
|
|||
byId("latitudeInput").value = byId("latitudeOutput").value = lat;
|
||||
lock("mapSize");
|
||||
lock("latitude");
|
||||
updateWorld();
|
||||
if (byId("wcAutoChange").checked) updateWorld();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
function editZones() {
|
||||
closeDialogs();
|
||||
if (!layerIsOn("toggleZones")) toggleZones();
|
||||
const body = document.getElementById("zonesBodySection");
|
||||
const body = byId("zonesBodySection");
|
||||
|
||||
updateFilters();
|
||||
zonesEditorAddLines();
|
||||
|
|
@ -20,20 +20,20 @@ function editZones() {
|
|||
});
|
||||
|
||||
// add listeners
|
||||
document.getElementById("zonesFilterType").addEventListener("click", updateFilters);
|
||||
document.getElementById("zonesFilterType").addEventListener("change", filterZonesByType);
|
||||
document.getElementById("zonesEditorRefresh").addEventListener("click", zonesEditorAddLines);
|
||||
document.getElementById("zonesEditStyle").addEventListener("click", () => editStyle("zones"));
|
||||
document.getElementById("zonesLegend").addEventListener("click", toggleLegend);
|
||||
document.getElementById("zonesPercentage").addEventListener("click", togglePercentageMode);
|
||||
document.getElementById("zonesManually").addEventListener("click", enterZonesManualAssignent);
|
||||
document.getElementById("zonesManuallyApply").addEventListener("click", applyZonesManualAssignent);
|
||||
document.getElementById("zonesManuallyCancel").addEventListener("click", cancelZonesManualAssignent);
|
||||
document.getElementById("zonesAdd").addEventListener("click", addZonesLayer);
|
||||
document.getElementById("zonesExport").addEventListener("click", downloadZonesData);
|
||||
document.getElementById("zonesRemove").addEventListener("click", toggleEraseMode);
|
||||
byId("zonesFilterType").on("click", updateFilters);
|
||||
byId("zonesFilterType").on("change", filterZonesByType);
|
||||
byId("zonesEditorRefresh").on("click", zonesEditorAddLines);
|
||||
byId("zonesEditStyle").on("click", () => editStyle("zones"));
|
||||
byId("zonesLegend").on("click", toggleLegend);
|
||||
byId("zonesPercentage").on("click", togglePercentageMode);
|
||||
byId("zonesManually").on("click", enterZonesManualAssignent);
|
||||
byId("zonesManuallyApply").on("click", applyZonesManualAssignent);
|
||||
byId("zonesManuallyCancel").on("click", cancelZonesManualAssignent);
|
||||
byId("zonesAdd").on("click", addZonesLayer);
|
||||
byId("zonesExport").on("click", downloadZonesData);
|
||||
byId("zonesRemove").on("click", toggleEraseMode);
|
||||
|
||||
body.addEventListener("click", function (ev) {
|
||||
body.on("click", function (ev) {
|
||||
const el = ev.target,
|
||||
cl = el.classList,
|
||||
zone = el.parentNode.dataset.id;
|
||||
|
|
@ -45,7 +45,7 @@ function editZones() {
|
|||
if (customization) selectZone(el);
|
||||
});
|
||||
|
||||
body.addEventListener("input", function (ev) {
|
||||
body.on("input", function (ev) {
|
||||
const el = ev.target;
|
||||
const zone = zones.select("#" + el.parentNode.dataset.id);
|
||||
|
||||
|
|
@ -58,10 +58,11 @@ function editZones() {
|
|||
const zones = Array.from(document.querySelectorAll("#zones > g"));
|
||||
const types = unique(zones.map(zone => zone.dataset.type));
|
||||
|
||||
const filterSelect = document.getElementById("zonesFilterType");
|
||||
const filterSelect = byId("zonesFilterType");
|
||||
const typeToFilterBy = types.includes(zonesFilterType.value) ? zonesFilterType.value : "all";
|
||||
|
||||
filterSelect.innerHTML = "<option value='all'>all</option>" + types.map(type => `<option value="${type}">${type}</option>`).join("");
|
||||
filterSelect.innerHTML =
|
||||
"<option value='all'>all</option>" + types.map(type => `<option value="${type}">${type}</option>`).join("");
|
||||
filterSelect.value = typeToFilterBy;
|
||||
}
|
||||
|
||||
|
|
@ -69,7 +70,7 @@ function editZones() {
|
|||
function zonesEditorAddLines() {
|
||||
const unit = " " + getAreaUnit();
|
||||
|
||||
const typeToFilterBy = document.getElementById("zonesFilterType").value;
|
||||
const typeToFilterBy = byId("zonesFilterType").value;
|
||||
const zones = Array.from(document.querySelectorAll("#zones > g"));
|
||||
const filteredZones = typeToFilterBy === "all" ? zones : zones.filter(zone => zone.dataset.type === typeToFilterBy);
|
||||
|
||||
|
|
@ -80,9 +81,12 @@ function editZones() {
|
|||
const fill = zoneEl.getAttribute("fill");
|
||||
const area = getArea(d3.sum(c.map(i => pack.cells.area[i])));
|
||||
const rural = d3.sum(c.map(i => pack.cells.pop[i])) * populationRate;
|
||||
const urban = d3.sum(c.map(i => pack.cells.burg[i]).map(b => pack.burgs[b].population)) * populationRate * urbanization;
|
||||
const urban =
|
||||
d3.sum(c.map(i => pack.cells.burg[i]).map(b => pack.burgs[b].population)) * populationRate * urbanization;
|
||||
const population = rural + urban;
|
||||
const populationTip = `Total population: ${si(population)}; Rural population: ${si(rural)}; Urban population: ${si(urban)}. Click to change`;
|
||||
const populationTip = `Total population: ${si(population)}; Rural population: ${si(
|
||||
rural
|
||||
)}; Urban population: ${si(urban)}. Click to change`;
|
||||
const inactive = zoneEl.style.display === "none";
|
||||
const focused = defs.select("#fog #focus" + zoneEl.id).size();
|
||||
|
||||
|
|
@ -98,8 +102,12 @@ function editZones() {
|
|||
<span data-tip="${populationTip}" class="icon-male hide"></span>
|
||||
<div data-tip="${populationTip}" class="culturePopulation hide">${si(population)}</div>
|
||||
<span data-tip="Drag to raise or lower the zone" class="icon-resize-vertical hide"></span>
|
||||
<span data-tip="Toggle zone focus" class="icon-pin ${focused ? "" : " inactive"} hide ${c.length ? "" : " placeholder"}"></span>
|
||||
<span data-tip="Toggle zone visibility" class="icon-eye ${inactive ? " inactive" : ""} hide ${c.length ? "" : " placeholder"}"></span>
|
||||
<span data-tip="Toggle zone focus" class="icon-pin ${focused ? "" : " inactive"} hide ${
|
||||
c.length ? "" : " placeholder"
|
||||
}"></span>
|
||||
<span data-tip="Toggle zone visibility" class="icon-eye ${inactive ? " inactive" : ""} hide ${
|
||||
c.length ? "" : " placeholder"
|
||||
}"></span>
|
||||
<span data-tip="Remove zone" class="icon-trash-empty hide"></span>
|
||||
</div>`;
|
||||
});
|
||||
|
|
@ -109,7 +117,9 @@ function editZones() {
|
|||
// update footer
|
||||
const totalArea = getArea(graphWidth * graphHeight);
|
||||
zonesFooterArea.dataset.area = totalArea;
|
||||
const totalPop = (d3.sum(pack.cells.pop) + d3.sum(pack.burgs.filter(b => !b.removed).map(b => b.population)) * urbanization) * populationRate;
|
||||
const totalPop =
|
||||
(d3.sum(pack.cells.pop) + d3.sum(pack.burgs.filter(b => !b.removed).map(b => b.population)) * urbanization) *
|
||||
populationRate;
|
||||
zonesFooterPopulation.dataset.population = totalPop;
|
||||
zonesFooterNumber.innerHTML = /* html */ `${filteredZones.length} of ${zones.length}`;
|
||||
zonesFooterCells.innerHTML = pack.cells.i.length;
|
||||
|
|
@ -117,8 +127,8 @@ function editZones() {
|
|||
zonesFooterPopulation.innerHTML = si(totalPop);
|
||||
|
||||
// add listeners
|
||||
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseenter", ev => zoneHighlightOn(ev)));
|
||||
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseleave", ev => zoneHighlightOff(ev)));
|
||||
body.querySelectorAll("div.states").forEach(el => el.on("mouseenter", ev => zoneHighlightOn(ev)));
|
||||
body.querySelectorAll("div.states").forEach(el => el.on("mouseleave", ev => zoneHighlightOff(ev)));
|
||||
|
||||
if (body.dataset.type === "percentage") {
|
||||
body.dataset.type = "absolute";
|
||||
|
|
@ -150,7 +160,13 @@ function editZones() {
|
|||
zonesEditorAddLines();
|
||||
}
|
||||
|
||||
$(body).sortable({items: "div.states", handle: ".icon-resize-vertical", containment: "parent", axis: "y", update: movezone});
|
||||
$(body).sortable({
|
||||
items: "div.states",
|
||||
handle: ".icon-resize-vertical",
|
||||
containment: "parent",
|
||||
axis: "y",
|
||||
update: movezone
|
||||
});
|
||||
function movezone(ev, ui) {
|
||||
const zone = $("#" + ui.item.attr("data-id"));
|
||||
const prev = $("#" + ui.item.prev().attr("data-id"));
|
||||
|
|
@ -166,7 +182,7 @@ function editZones() {
|
|||
if (!layerIsOn("toggleZones")) toggleZones();
|
||||
customization = 10;
|
||||
document.querySelectorAll("#zonesBottom > *").forEach(el => (el.style.display = "none"));
|
||||
document.getElementById("zonesManuallyButtons").style.display = "inline-block";
|
||||
byId("zonesManuallyButtons").style.display = "inline-block";
|
||||
|
||||
zonesEditor.querySelectorAll(".hide").forEach(el => el.classList.add("hidden"));
|
||||
zonesFooter.style.display = "none";
|
||||
|
|
@ -174,7 +190,11 @@ function editZones() {
|
|||
$("#zonesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}});
|
||||
|
||||
tip("Click to select a zone, drag to paint a zone", true);
|
||||
viewbox.style("cursor", "crosshair").on("click", selectZoneOnMapClick).call(d3.drag().on("start", dragZoneBrush)).on("touchmove mousemove", moveZoneBrush);
|
||||
viewbox
|
||||
.style("cursor", "crosshair")
|
||||
.on("click", selectZoneOnMapClick)
|
||||
.call(d3.drag().on("start", dragZoneBrush))
|
||||
.on("touchmove mousemove", moveZoneBrush);
|
||||
|
||||
body.querySelector("div").classList.add("selected");
|
||||
zones.selectAll("g").each(function () {
|
||||
|
|
@ -195,24 +215,27 @@ function editZones() {
|
|||
}
|
||||
|
||||
function dragZoneBrush() {
|
||||
const r = +zonesBrush.value;
|
||||
const radius = +byId("zonesBrush").value;
|
||||
const eraseMode = byId("zonesRemove").classList.contains("pressed");
|
||||
const landOnly = byId("zonesBrushLandOnly").checked;
|
||||
|
||||
const selected = body.querySelector("div.selected");
|
||||
const zone = zones.select("#" + selected.dataset.id);
|
||||
const base = zone.attr("id") + "_"; // id generic part
|
||||
|
||||
d3.event.on("drag", () => {
|
||||
if (!d3.event.dx && !d3.event.dy) return;
|
||||
const p = d3.mouse(this);
|
||||
moveCircle(p[0], p[1], r);
|
||||
const [x, y] = d3.mouse(this);
|
||||
moveCircle(x, y, radius);
|
||||
|
||||
const selection = r > 5 ? findAll(p[0], p[1], r) : [findCell(p[0], p[1], r)];
|
||||
let selection = radius > 5 ? findAll(x, y, radius) : [findCell(x, y, radius)];
|
||||
if (landOnly) selection = selection.filter(i => pack.cells.h[i] >= 20);
|
||||
if (!selection) return;
|
||||
|
||||
const selected = body.querySelector("div.selected");
|
||||
const zone = zones.select("#" + selected.dataset.id);
|
||||
const base = zone.attr("id") + "_"; // id generic part
|
||||
const dataCells = zone.attr("data-cells");
|
||||
let cells = dataCells ? dataCells.split(",").map(i => +i) : [];
|
||||
|
||||
const erase = document.getElementById("zonesRemove").classList.contains("pressed");
|
||||
if (erase) {
|
||||
if (eraseMode) {
|
||||
// remove
|
||||
selection.forEach(i => {
|
||||
const index = cells.indexOf(i);
|
||||
|
|
@ -280,12 +303,13 @@ function editZones() {
|
|||
customization = 0;
|
||||
removeCircle();
|
||||
document.querySelectorAll("#zonesBottom > *").forEach(el => (el.style.display = "inline-block"));
|
||||
document.getElementById("zonesManuallyButtons").style.display = "none";
|
||||
byId("zonesManuallyButtons").style.display = "none";
|
||||
|
||||
zonesEditor.querySelectorAll(".hide:not(.show)").forEach(el => el.classList.remove("hidden"));
|
||||
zonesFooter.style.display = "block";
|
||||
body.querySelectorAll("div > input, select, svg").forEach(e => (e.style.pointerEvents = "all"));
|
||||
if (!close) $("#zonesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}});
|
||||
if (!close)
|
||||
$("#zonesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}});
|
||||
|
||||
restoreDefaultEvents();
|
||||
clearMainTip();
|
||||
|
|
@ -300,7 +324,7 @@ function editZones() {
|
|||
const fill = el.getAttribute("fill");
|
||||
const callback = newFill => {
|
||||
el.fill = newFill;
|
||||
document.getElementById(el.parentNode.dataset.id).setAttribute("fill", newFill);
|
||||
byId(el.parentNode.dataset.id).setAttribute("fill", newFill);
|
||||
};
|
||||
|
||||
openPicker(fill, callback);
|
||||
|
|
@ -356,7 +380,8 @@ function editZones() {
|
|||
body.querySelectorAll(":scope > div").forEach(function (el) {
|
||||
el.querySelector(".stateCells").innerHTML = rn((+el.dataset.cells / totalCells) * 100, 2) + "%";
|
||||
el.querySelector(".biomeArea").innerHTML = rn((+el.dataset.area / totalArea) * 100, 2) + "%";
|
||||
el.querySelector(".culturePopulation").innerHTML = rn((+el.dataset.population / totalPopulation) * 100, 2) + "%";
|
||||
el.querySelector(".culturePopulation").innerHTML =
|
||||
rn((+el.dataset.population / totalPopulation) * 100, 2) + "%";
|
||||
});
|
||||
} else {
|
||||
body.dataset.type = "absolute";
|
||||
|
|
@ -369,7 +394,13 @@ function editZones() {
|
|||
const description = "Unknown zone";
|
||||
const type = "Unknown";
|
||||
const fill = "url(#hatch" + (id.slice(4) % 42) + ")";
|
||||
zones.append("g").attr("id", id).attr("data-description", description).attr("data-type", type).attr("data-cells", "").attr("fill", fill);
|
||||
zones
|
||||
.append("g")
|
||||
.attr("id", id)
|
||||
.attr("data-description", description)
|
||||
.attr("data-type", type)
|
||||
.attr("data-cells", "")
|
||||
.attr("fill", fill);
|
||||
|
||||
zonesEditorAddLines();
|
||||
}
|
||||
|
|
@ -411,13 +442,19 @@ function editZones() {
|
|||
const burgs = pack.burgs.filter(b => !b.removed && cells.includes(b.cell));
|
||||
|
||||
const rural = rn(d3.sum(cells.map(i => pack.cells.pop[i])) * populationRate);
|
||||
const urban = rn(d3.sum(cells.map(i => pack.cells.burg[i]).map(b => pack.burgs[b].population)) * populationRate * urbanization);
|
||||
const urban = rn(
|
||||
d3.sum(cells.map(i => pack.cells.burg[i]).map(b => pack.burgs[b].population)) * populationRate * urbanization
|
||||
);
|
||||
const total = rural + urban;
|
||||
const l = n => Number(n).toLocaleString();
|
||||
|
||||
alertMessage.innerHTML = /* html */ `Rural: <input type="number" min="0" step="1" id="ruralPop" value=${rural} style="width:6em" /> Urban:
|
||||
<input type="number" min="0" step="1" id="urbanPop" value=${urban} style="width:6em" ${burgs.length ? "" : "disabled"} />
|
||||
<p>Total population: ${l(total)} ⇒ <span id="totalPop">${l(total)}</span> (<span id="totalPopPerc">100</span>%)</p>`;
|
||||
<input type="number" min="0" step="1" id="urbanPop" value=${urban} style="width:6em" ${
|
||||
burgs.length ? "" : "disabled"
|
||||
} />
|
||||
<p>Total population: ${l(total)} ⇒ <span id="totalPop">${l(
|
||||
total
|
||||
)}</span> (<span id="totalPopPerc">100</span>%)</p>`;
|
||||
|
||||
const update = function () {
|
||||
const totalNew = ruralPop.valueAsNumber + urbanPop.valueAsNumber;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue