mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-21 19:41:23 +01:00
997 lines
37 KiB
JavaScript
997 lines
37 KiB
JavaScript
function addRoutePoint(point) {
|
|
const controlPoints = debug.select(".controlPoints").size()
|
|
? debug.select(".controlPoints")
|
|
: debug.append("g").attr("class", "controlPoints");
|
|
controlPoints.append("circle")
|
|
.attr("cx", point.x).attr("cy", point.y).attr("r", 0.35)
|
|
.call(d3.drag().on("drag", routePointDrag))
|
|
.on("click", function(d) {
|
|
if ($("#routeSplit").hasClass('pressed')) {
|
|
routeSplitInPoint(this);
|
|
} else {
|
|
$(this).remove();
|
|
routeRedraw();
|
|
}
|
|
});
|
|
}
|
|
|
|
function routePointDrag() {
|
|
d3.select(this).attr("cx", d3.event.x).attr("cy", d3.event.y);
|
|
routeRedraw();
|
|
}
|
|
|
|
function routeRedraw() {
|
|
let points = [];
|
|
debug.select(".controlPoints").selectAll("circle").each(function() {
|
|
const el = d3.select(this);
|
|
points.push({scX: +el.attr("cx"), scY: +el.attr("cy")});
|
|
});
|
|
lineGen.curve(d3.curveCatmullRom.alpha(0.1));
|
|
elSelected.attr("d", lineGen(points));
|
|
// get route distance
|
|
const l = elSelected.node().getTotalLength();
|
|
routeLength.innerHTML = rn(l * distanceScale.value) + " " + distanceUnit.value;
|
|
}
|
|
|
|
function addNewRoute() {
|
|
let routeType = elSelected && elSelected.node() ? elSelected.node().parentNode.id : "searoutes";
|
|
const group = routes.select("#"+routeType);
|
|
const id = routeType + "" + group.selectAll("*").size();
|
|
elSelected = group.append("path").attr("data-route", "new").attr("id", id).on("click", editRoute);
|
|
routeUpdateGroups();
|
|
$("#routeEditor").dialog({
|
|
title: "Edit Route", minHeight: 30, width: "auto", resizable: false,
|
|
close: function() {
|
|
if ($("#addRoute").hasClass('pressed')) completeNewRoute();
|
|
if ($("#routeSplit").hasClass('pressed')) $("#routeSplit").removeClass('pressed');
|
|
unselect();
|
|
}
|
|
});
|
|
}
|
|
|
|
function newRouteAddPoint() {
|
|
const point = d3.mouse(this);
|
|
const x = rn(point[0],2), y = rn(point[1],2);
|
|
addRoutePoint({x, y});
|
|
routeRedraw();
|
|
}
|
|
|
|
function completeNewRoute() {
|
|
$("#routeNew, #addRoute").removeClass('pressed');
|
|
restoreDefaultEvents();
|
|
if (!elSelected.size()) return;
|
|
if (elSelected.attr("data-route") === "new") {
|
|
routeRedraw();
|
|
elSelected.attr("data-route", "");
|
|
const node = elSelected.node();
|
|
const l = node.getTotalLength();
|
|
let pathCells = [];
|
|
for (let i = 0; i <= l; i ++) {
|
|
const p = node.getPointAtLength(i);
|
|
const cell = diagram.find(p.x, p.y);
|
|
if (!cell) {return;}
|
|
pathCells.push(cell.index);
|
|
}
|
|
const uniqueCells = [...new Set(pathCells)];
|
|
uniqueCells.map(function(c) {
|
|
if (cells[c].path !== undefined) {cells[c].path += 1;}
|
|
else {cells[c].path = 1;}
|
|
});
|
|
}
|
|
tip("", true);
|
|
}
|
|
|
|
function routeUpdateGroups() {
|
|
routeGroup.innerHTML = "";
|
|
routes.selectAll("g").each(function() {
|
|
const opt = document.createElement("option");
|
|
opt.value = opt.innerHTML = this.id;
|
|
routeGroup.add(opt);
|
|
});
|
|
}
|
|
|
|
function routeSplitInPoint(clicked) {
|
|
const group = d3.select(elSelected.node().parentNode);
|
|
$("#routeSplit").removeClass('pressed');
|
|
const points1 = [],points2 = [];
|
|
let points = points1;
|
|
debug.select(".controlPoints").selectAll("circle").each(function() {
|
|
const el = d3.select(this);
|
|
points.push({scX: +el.attr("cx"), scY: +el.attr("cy")});
|
|
if (this === clicked) {
|
|
points = points2;
|
|
points.push({scX: +el.attr("cx"), scY: +el.attr("cy")});
|
|
}
|
|
el.remove();
|
|
});
|
|
lineGen.curve(d3.curveCatmullRom.alpha(0.1));
|
|
elSelected.attr("d", lineGen(points1));
|
|
const id = routeGroup.value + "" + group.selectAll("*").size();
|
|
group.append("path").attr("id", id).attr("d", lineGen(points2)).on("click", editRoute);
|
|
routeDrawPoints();
|
|
}
|
|
|
|
$("#routeGroup").change(function() {
|
|
$(elSelected.node()).detach().appendTo($("#"+this.value));
|
|
});
|
|
|
|
// open legendsEditor
|
|
document.getElementById("routeLegend").addEventListener("click", function() {
|
|
let id = elSelected.attr("id");
|
|
editLegends(id, id);
|
|
});
|
|
|
|
$("#routeNew").click(function() {
|
|
if ($(this).hasClass('pressed')) {
|
|
completeNewRoute();
|
|
} else {
|
|
// enter creation mode
|
|
$(".pressed").removeClass('pressed');
|
|
$("#routeNew, #addRoute").addClass('pressed');
|
|
debug.select(".controlPoints").selectAll("*").remove();
|
|
addNewRoute();
|
|
viewbox.style("cursor", "crosshair").on("click", newRouteAddPoint);
|
|
tip("Click on map to add route point", true);
|
|
}
|
|
});
|
|
|
|
$("#routeRemove").click(function() {
|
|
alertMessage.innerHTML = `Are you sure you want to remove the route?`;
|
|
$("#alert").dialog({resizable: false, title: "Remove route",
|
|
buttons: {
|
|
Remove: function() {
|
|
$(this).dialog("close");
|
|
elSelected.remove();
|
|
$("#routeEditor").dialog("close");
|
|
},
|
|
Cancel: function() {$(this).dialog("close");}
|
|
}
|
|
})
|
|
});
|
|
}
|
|
|
|
function editIcon() {
|
|
if (customization) return;
|
|
if (elSelected) if (this.isSameNode(elSelected.node())) return;
|
|
|
|
unselect();
|
|
closeDialogs("#iconEditor, .stable");
|
|
elSelected = d3.select(this).call(d3.drag().on("start", elementDrag)).classed("draggable", true);
|
|
|
|
// update group parameters
|
|
const group = d3.select(this.parentNode);
|
|
iconUpdateGroups();
|
|
iconGroup.value = group.attr("id");
|
|
iconFillColor.value = group.attr("fill");
|
|
iconStrokeColor.value = group.attr("stroke");
|
|
iconSize.value = group.attr("size");
|
|
iconStrokeWidth.value = group.attr("stroke-width");
|
|
|
|
$("#iconEditor").dialog({
|
|
title: "Edit icon: " + group.attr("id"),
|
|
minHeight: 30, width: "auto", resizable: false,
|
|
position: {my: "center top+20", at: "top", of: d3.event},
|
|
close: unselect
|
|
});
|
|
|
|
if (modules.editIcon) {return;}
|
|
modules.editIcon = true;
|
|
|
|
$("#iconGroups").click(function() {
|
|
$("#iconEditor > button").not(this).toggle();
|
|
$("#iconGroupsSelection").toggle();
|
|
});
|
|
|
|
function iconUpdateGroups() {
|
|
iconGroup.innerHTML = "";
|
|
const anchor = group.attr("id").includes("anchor");
|
|
icons.selectAll("g").each(function(d) {
|
|
const id = d3.select(this).attr("id");
|
|
if (id === "burgs") return;
|
|
if (!anchor && id.includes("anchor")) return;
|
|
if (anchor && !id.includes("anchor")) return;
|
|
const opt = document.createElement("option");
|
|
opt.value = opt.innerHTML = id;
|
|
iconGroup.add(opt);
|
|
});
|
|
}
|
|
|
|
$("#iconGroup").change(function() {
|
|
const newGroup = this.value;
|
|
const to = $("#icons > #"+newGroup);
|
|
$(elSelected.node()).detach().appendTo(to);
|
|
});
|
|
|
|
$("#iconCopy").click(function() {
|
|
const group = d3.select(elSelected.node().parentNode);
|
|
const copy = elSelected.node().cloneNode();
|
|
copy.removeAttribute("data-id"); // remove assignment to burg if any
|
|
const tr = parseTransform(copy.getAttribute("transform"));
|
|
const shift = 10 / Math.sqrt(scale);
|
|
let transform = "translate(" + rn(tr[0] - shift, 1) + "," + rn(tr[1] - shift, 1) + ")";
|
|
for (let i=2; group.selectAll("[transform='" + transform + "']").size() > 0; i++) {
|
|
transform = "translate(" + rn(tr[0] - shift * i, 1) + "," + rn(tr[1] - shift * i, 1) + ")";
|
|
}
|
|
copy.setAttribute("transform", transform);
|
|
group.node().insertBefore(copy, null);
|
|
copy.addEventListener("click", editIcon);
|
|
});
|
|
|
|
$("#iconRemoveGroup").click(function() {
|
|
const group = d3.select(elSelected.node().parentNode);
|
|
const count = group.selectAll("*").size();
|
|
if (count < 2) {
|
|
group.remove();
|
|
$("#labelEditor").dialog("close");
|
|
return;
|
|
}
|
|
const message = "Are you sure you want to remove all '" + iconGroup.value + "' icons (" + count + ")?";
|
|
alertMessage.innerHTML = message;
|
|
$("#alert").dialog({resizable: false, title: "Remove icon group",
|
|
buttons: {
|
|
Remove: function() {
|
|
$(this).dialog("close");
|
|
group.remove();
|
|
$("#iconEditor").dialog("close");
|
|
},
|
|
Cancel: function() {$(this).dialog("close");}
|
|
}
|
|
});
|
|
});
|
|
|
|
$("#iconColors").click(function() {
|
|
$("#iconEditor > button").not(this).toggle();
|
|
$("#iconColorsSection").toggle();
|
|
});
|
|
|
|
$("#iconFillColor").change(function() {
|
|
const group = d3.select(elSelected.node().parentNode);
|
|
group.attr("fill", this.value);
|
|
});
|
|
|
|
$("#iconStrokeColor").change(function() {
|
|
const group = d3.select(elSelected.node().parentNode);
|
|
group.attr("stroke", this.value);
|
|
});
|
|
|
|
$("#iconSetSize").click(function() {
|
|
$("#iconEditor > button").not(this).toggle();
|
|
$("#iconSizeSection").toggle();
|
|
});
|
|
|
|
$("#iconSize").change(function() {
|
|
const group = d3.select(elSelected.node().parentNode);
|
|
const size = +this.value;
|
|
group.attr("size", size);
|
|
group.selectAll("*").each(function() {d3.select(this).attr("width", size).attr("height", size)});
|
|
});
|
|
|
|
$("#iconStrokeWidth").change(function() {
|
|
const group = d3.select(elSelected.node().parentNode);
|
|
group.attr("stroke-width", this.value);
|
|
});
|
|
|
|
$("#iconRemove").click(function() {
|
|
alertMessage.innerHTML = `Are you sure you want to remove the icon?`;
|
|
$("#alert").dialog({resizable: false, title: "Remove icon",
|
|
buttons: {
|
|
Remove: function() {
|
|
$(this).dialog("close");
|
|
elSelected.remove();
|
|
$("#iconEditor").dialog("close");
|
|
},
|
|
Cancel: function() {$(this).dialog("close");}
|
|
}
|
|
})
|
|
});
|
|
}
|
|
|
|
function editReliefIcon() {
|
|
if (customization) return;
|
|
if (elSelected) if (this.isSameNode(elSelected.node())) return;
|
|
|
|
unselect();
|
|
closeDialogs("#reliefEditor, .stable");
|
|
elSelected = d3.select(this).raise().call(d3.drag().on("start", elementDrag)).classed("draggable", true);
|
|
const group = elSelected.node().parentNode.id;
|
|
reliefGroup.value = group;
|
|
|
|
let bulkRemoveSection = document.getElementById("reliefBulkRemoveSection");
|
|
if (bulkRemoveSection.style.display != "none") reliefBulkRemove.click();
|
|
|
|
$("#reliefEditor").dialog({
|
|
title: "Edit relief icon",
|
|
minHeight: 30, width: "auto", resizable: false,
|
|
position: {my: "center top+40", at: "top", of: d3.event},
|
|
close: unselect
|
|
});
|
|
|
|
if (modules.editReliefIcon) {return;}
|
|
modules.editReliefIcon = true;
|
|
|
|
$("#reliefGroups").click(function() {
|
|
$("#reliefEditor > button").not(this).toggle();
|
|
$("#reliefGroupsSelection").toggle();
|
|
});
|
|
|
|
$("#reliefGroup").change(function() {
|
|
const type = this.value;
|
|
const bbox = elSelected.node().getBBox();
|
|
const cx = bbox.x;
|
|
const cy = bbox.y + bbox.height / 2;
|
|
const cell = diagram.find(cx, cy).index;
|
|
const height = cell !== undefined ? cells[cell].height : 50;
|
|
elSelected.remove();
|
|
elSelected = addReliefIcon(height / 100, type, cx, cy, cell);
|
|
elSelected.call(d3.drag().on("start", elementDrag));
|
|
});
|
|
|
|
$("#reliefCopy").click(function() {
|
|
const group = d3.select(elSelected.node().parentNode);
|
|
const copy = elSelected.node().cloneNode(true);
|
|
const tr = parseTransform(copy.getAttribute("transform"));
|
|
const shift = 10 / Math.sqrt(scale);
|
|
let transform = "translate(" + rn(tr[0] - shift, 1) + "," + rn(tr[1] - shift, 1) + ")";
|
|
for (let i=2; group.selectAll("[transform='" + transform + "']").size() > 0; i++) {
|
|
transform = "translate(" + rn(tr[0] - shift * i, 1) + "," + rn(tr[1] - shift * i, 1) + ")";
|
|
}
|
|
copy.setAttribute("transform", transform);
|
|
group.node().insertBefore(copy, null);
|
|
copy.addEventListener("click", editReliefIcon);
|
|
});
|
|
|
|
$("#reliefAddfromEditor").click(function() {
|
|
clickToAdd(); // to load on click event function
|
|
$("#addRelief").click();
|
|
});
|
|
|
|
$("#reliefRemoveGroup").click(function() {
|
|
const group = d3.select(elSelected.node().parentNode);
|
|
const count = group.selectAll("*").size();
|
|
if (count < 2) {
|
|
group.selectAll("*").remove();
|
|
$("#labelEditor").dialog("close");
|
|
return;
|
|
}
|
|
const message = "Are you sure you want to remove all '" + reliefGroup.value + "' icons (" + count + ")?";
|
|
alertMessage.innerHTML = message;
|
|
$("#alert").dialog({resizable: false, title: "Remove all icons within group",
|
|
buttons: {
|
|
Remove: function() {
|
|
$(this).dialog("close");
|
|
group.selectAll("*").remove();
|
|
$("#reliefEditor").dialog("close");
|
|
},
|
|
Cancel: function() {$(this).dialog("close");}
|
|
}
|
|
});
|
|
});
|
|
|
|
$("#reliefBulkRemove").click(function() {
|
|
$("#reliefEditor > button").not(this).toggle();
|
|
let section = document.getElementById("reliefBulkRemoveSection");
|
|
if (section.style.display === "none") {
|
|
section.style.display = "inline-block";
|
|
tip("Drag to remove relief icons in radius", true);
|
|
viewbox.style("cursor", "crosshair").call(d3.drag().on("drag", dragToRemoveReliefIcons));
|
|
customization = 5;
|
|
} else {
|
|
section.style.display = "none";
|
|
restoreDefaultEvents();
|
|
customization = 0;
|
|
}
|
|
});
|
|
|
|
function dragToRemoveReliefIcons() {
|
|
let point = d3.mouse(this);
|
|
let cell = diagram.find(point[0], point[1]).index;
|
|
let radius = +reliefBulkRemoveRadius.value;
|
|
let r = rn(6 / graphSize * radius, 1);
|
|
moveCircle(point[0], point[1], r);
|
|
let selection = defineBrushSelection(cell, radius);
|
|
if (selection) removeReliefIcons(selection);
|
|
}
|
|
|
|
function removeReliefIcons(selection) {
|
|
if (selection.length === 0) return;
|
|
selection.map(function(index) {
|
|
const selected = terrain.selectAll("g").selectAll("g[data-cell='"+index+"']");
|
|
selected.remove();
|
|
});
|
|
}
|
|
|
|
$("#reliefRemove").click(function() {
|
|
alertMessage.innerHTML = `Are you sure you want to remove the icon?`;
|
|
$("#alert").dialog({resizable: false, title: "Remove relief icon",
|
|
buttons: {
|
|
Remove: function() {
|
|
$(this).dialog("close");
|
|
elSelected.remove();
|
|
$("#reliefEditor").dialog("close");
|
|
},
|
|
Cancel: function() {$(this).dialog("close");}
|
|
}
|
|
})
|
|
});
|
|
}
|
|
|
|
function editBurg() {
|
|
if (customization) return;
|
|
unselect();
|
|
closeDialogs("#burgEditor, .stable");
|
|
elSelected = d3.select(this);
|
|
const id = +elSelected.attr("data-id");
|
|
if (id === undefined) return;
|
|
d3.selectAll("[data-id='" + id + "']").call(d3.drag().on("start", elementDrag)).classed("draggable", true);
|
|
|
|
// update Burg details
|
|
const type = elSelected.node().parentNode.id;
|
|
const labelGroup = burgLabels.select("#"+type);
|
|
const iconGroup = burgIcons.select("#"+type);
|
|
burgNameInput.value = manors[id].name;
|
|
updateBurgsGroupOptions();
|
|
burgSelectGroup.value = labelGroup.attr("id");
|
|
burgSelectDefaultFont.value = fonts.indexOf(labelGroup.attr("data-font"));
|
|
burgSetLabelSize.value = labelGroup.attr("data-size");
|
|
burgLabelColorInput.value = toHEX(labelGroup.attr("fill"));
|
|
burgLabelOpacity.value = labelGroup.attr("opacity") === undefined ? 1 : +labelGroup.attr("opacity");
|
|
const tr = parseTransform(elSelected.attr("transform"));
|
|
burgLabelAngle.value = tr[2];
|
|
burgLabelAngleOutput.innerHTML = Math.abs(+tr[2]) + "°";
|
|
burgIconSize.value = iconGroup.attr("size");
|
|
burgIconFillOpacity.value = iconGroup.attr("fill-opacity") === undefined ? 1 : +iconGroup.attr("fill-opacity");
|
|
burgIconFillColor.value = iconGroup.attr("fill");
|
|
burgIconStrokeWidth.value = iconGroup.attr("stroke-width");
|
|
burgIconStrokeOpacity.value = iconGroup.attr("stroke-opacity") === undefined ? 1 : +iconGroup.attr("stroke-opacity");
|
|
burgIconStrokeColor.value = iconGroup.attr("stroke");
|
|
const cell = cells[manors[id].cell];
|
|
if (cell.region !== "neutral" && cell.region !== undefined) {
|
|
burgToggleCapital.disabled = false;
|
|
const capital = states[manors[id].region] ? id === states[manors[id].region].capital ? 1 : 0 : 0;
|
|
d3.select("#burgToggleCapital").classed("pressed", capital);
|
|
} else {
|
|
burgToggleCapital.disabled = true;
|
|
d3.select("#burgToggleCapital").classed("pressed", false);
|
|
}
|
|
d3.select("#burgTogglePort").classed("pressed", cell.port !== undefined);
|
|
burgPopulation.value = manors[id].population;
|
|
burgPopulationFriendly.value = rn(manors[id].population * urbanization.value * populationRate.value * 1000);
|
|
|
|
$("#burgEditor").dialog({
|
|
title: "Edit Burg: " + manors[id].name,
|
|
minHeight: 30, width: "auto", resizable: false,
|
|
position: {my: "center top+40", at: "top", of: d3.event},
|
|
close: function() {
|
|
d3.selectAll("[data-id='" + id + "']").call(d3.drag().on("drag", null)).classed("draggable", false);
|
|
elSelected = null;
|
|
}
|
|
});
|
|
|
|
if (modules.editBurg) return;
|
|
modules.editBurg = true;
|
|
|
|
loadDefaultFonts();
|
|
|
|
function updateBurgsGroupOptions() {
|
|
burgSelectGroup.innerHTML = "";
|
|
burgIcons.selectAll("g").each(function(d) {
|
|
const opt = document.createElement("option");
|
|
opt.value = opt.innerHTML = d3.select(this).attr("id");
|
|
burgSelectGroup.add(opt);
|
|
});
|
|
}
|
|
|
|
$("#burgEditor > button").not("#burgAddfromEditor").not("#burgRelocate").not("#burgRemove").click(function() {
|
|
if ($(this).next().is(":visible")) {
|
|
$("#burgEditor > button").show();
|
|
$(this).next("div").hide();
|
|
} else {
|
|
$("#burgEditor > *").not(this).hide();
|
|
$(this).next("div").show();
|
|
}
|
|
});
|
|
|
|
$("#burgEditor > div > button").click(function() {
|
|
if ($(this).next().is(":visible")) {
|
|
$("#burgEditor > div > button").show();
|
|
$(this).parent().prev().show();
|
|
$(this).next("div").hide();
|
|
} else {
|
|
$("#burgEditor > div > button").not(this).hide();
|
|
$(this).parent().prev().hide();
|
|
$(this).next("div").show();
|
|
}
|
|
});
|
|
|
|
$("#burgSelectGroup").change(function() {
|
|
const id = +elSelected.attr("data-id");
|
|
const g = this.value;
|
|
moveBurgToGroup(id, g);
|
|
});
|
|
|
|
$("#burgInputGroup").change(function() {
|
|
let newGroup = this.value.toLowerCase().replace(/ /g, "_").replace(/[^\w\s]/gi, "");
|
|
if (Number.isFinite(+newGroup.charAt(0))) newGroup = "g" + newGroup;
|
|
if (burgLabels.select("#"+newGroup).size()) {
|
|
tip('The group "'+ newGroup + '" is already exists');
|
|
return;
|
|
}
|
|
burgInputGroup.value = "";
|
|
// clone old group assigning new id
|
|
const id = elSelected.node().parentNode.id;
|
|
const l = burgLabels.select("#"+id).node().cloneNode(false);
|
|
l.id = newGroup;
|
|
const i = burgIcons.select("#"+id).node().cloneNode(false);
|
|
i.id = newGroup;
|
|
burgLabels.node().insertBefore(l, null);
|
|
burgIcons.node().insertBefore(i, null);
|
|
// select new group
|
|
const opt = document.createElement("option");
|
|
opt.value = opt.innerHTML = newGroup;
|
|
burgSelectGroup.add(opt);
|
|
$("#burgSelectGroup").val(newGroup).change();
|
|
$("#burgSelectGroup, #burgInputGroup").toggle();
|
|
updateLabelGroups();
|
|
});
|
|
|
|
$("#burgAddGroup").click(function() {
|
|
if ($("#burgInputGroup").css("display") === "none") {
|
|
$("#burgInputGroup").css("display", "inline-block");
|
|
$("#burgSelectGroup").css("display", "none");
|
|
burgInputGroup.focus();
|
|
} else {
|
|
$("#burgSelectGroup").css("display", "inline-block");
|
|
$("#burgInputGroup").css("display", "none");
|
|
}
|
|
});
|
|
|
|
$("#burgRemoveGroup").click(function() {
|
|
const group = d3.select(elSelected.node().parentNode);
|
|
const type = group.attr("id");
|
|
const id = +elSelected.attr("data-id");
|
|
const count = group.selectAll("*").size();
|
|
const message = "Are you sure you want to remove all Burgs (" + count + ") of that group?";
|
|
alertMessage.innerHTML = message;
|
|
$("#alert").dialog({resizable: false, title: "Remove Burgs",
|
|
buttons: {
|
|
Remove: function() {
|
|
$(this).dialog("close");
|
|
group.selectAll("*").each(function(d) {
|
|
const id = +d3.select(this).attr("data-id");
|
|
if (id === undefined) return;
|
|
const cell = manors[id].cell;
|
|
const state = manors[id].region;
|
|
if (states[state]) {
|
|
if (states[state].capital === id) states[state].capital = "select";
|
|
states[state].burgs --;
|
|
}
|
|
manors[id].region = "removed";
|
|
cells[cell].manor = undefined;
|
|
});
|
|
burgLabels.select("#"+type).selectAll("*").remove();
|
|
burgIcons.select("#"+type).selectAll("*").remove();
|
|
$("#icons g[id*='anchors'] [data-id=" + id + "]").parent().children().remove();
|
|
closeDialogs(".stable");
|
|
updateCountryEditors();
|
|
$("#burgEditor").dialog("close");
|
|
},
|
|
Cancel: function() {$(this).dialog("close");}
|
|
}
|
|
});
|
|
|
|
});
|
|
|
|
$("#burgNameInput").on("input", function() {
|
|
if (this.value === "") {
|
|
tip("Name should not be blank, set opacity to 0 to hide label or remove button to delete");
|
|
return;
|
|
}
|
|
const id = +elSelected.attr("data-id");
|
|
burgLabels.selectAll("[data-id='" + id + "']").text(this.value);
|
|
manors[id].name = this.value;
|
|
$("div[aria-describedby='burgEditor'] .ui-dialog-title").text("Edit Burg: " + this.value);
|
|
});
|
|
|
|
$("#burgNameReCulture, #burgNameReRandom").click(function() {
|
|
const id = +elSelected.attr("data-id");
|
|
const culture = this.id === "burgNameReCulture" ? manors[id].culture : Math.floor(Math.random() * cultures.length);
|
|
const name = generateName(culture);
|
|
burgLabels.selectAll("[data-id='" + id + "']").text(name);
|
|
manors[id].name = name;
|
|
burgNameInput.value = name;
|
|
$("div[aria-describedby='burgEditor'] .ui-dialog-title").text("Edit Burg: " + name);
|
|
});
|
|
|
|
$("#burgToggleExternalFont").click(function() {
|
|
if ($("#burgInputExternalFont").css("display") === "none") {
|
|
$("#burgInputExternalFont").css("display", "inline-block");
|
|
$("#burgSelectDefaultFont").css("display", "none");
|
|
burgInputExternalFont.focus();
|
|
} else {
|
|
$("#burgSelectDefaultFont").css("display", "inline-block");
|
|
$("#burgInputExternalFont").css("display", "none");
|
|
}
|
|
});
|
|
|
|
$("#burgSelectDefaultFont").change(function() {
|
|
const type = elSelected.node().parentNode.id;
|
|
const group = burgLabels.select("#"+type);
|
|
if (burgSelectDefaultFont.value === "") return;
|
|
const font = fonts[burgSelectDefaultFont.value].split(':')[0].replace(/\+/g, " ");
|
|
group.attr("font-family", font).attr("data-font", fonts[burgSelectDefaultFont.value]);
|
|
});
|
|
|
|
$("#burgInputExternalFont").change(function() {
|
|
fetchFonts(this.value).then(fetched => {
|
|
if (!fetched) return;
|
|
burgToggleExternalFont.click();
|
|
burgInputExternalFont.value = "";
|
|
if (fetched === 1) $("#burgSelectDefaultFont").val(fonts.length - 1).change();
|
|
});
|
|
});
|
|
|
|
$("#burgSetLabelSize").on("input", function() {
|
|
const type = elSelected.node().parentNode.id;
|
|
const group = burgLabels.select("#"+type);
|
|
group.attr("data-size", +this.value);
|
|
invokeActiveZooming();
|
|
});
|
|
|
|
$("#burgLabelColorInput").on("input", function() {
|
|
const type = elSelected.node().parentNode.id;
|
|
const group = burgLabels.select("#"+type);
|
|
group.attr("fill", this.value);
|
|
});
|
|
|
|
$("#burgLabelOpacity").on("input", function() {
|
|
const type = elSelected.node().parentNode.id;
|
|
const group = burgLabels.select("#"+type);
|
|
group.attr("opacity", +this.value);
|
|
});
|
|
|
|
$("#burgLabelAngle").on("input", function() {
|
|
const id = +elSelected.attr("data-id");
|
|
const el = burgLabels.select("[data-id='"+ id +"']");
|
|
const tr = parseTransform(el.attr("transform"));
|
|
const c = el.node().getBBox();
|
|
burgLabelAngleOutput.innerHTML = Math.abs(+this.value) + "°";
|
|
const angle = +this.value;
|
|
const transform = `translate(${tr[0]},${tr[1]}) rotate(${angle} ${(c.x+c.width/2)} ${(c.y+c.height/2)})`;
|
|
el.attr("transform", transform);
|
|
});
|
|
|
|
$("#burgIconSize").on("input", function() {
|
|
const type = elSelected.node().parentNode.id;
|
|
const group = burgIcons.select("#"+type);
|
|
const size = +this.value;
|
|
group.attr("size", size);
|
|
group.selectAll("*").each(function() {d3.select(this).attr("r", size)});
|
|
});
|
|
|
|
$("#burgIconFillOpacity").on("input", function() {
|
|
const type = elSelected.node().parentNode.id;
|
|
const group = burgIcons.select("#"+type);
|
|
group.attr("fill-opacity", +this.value);
|
|
});
|
|
|
|
$("#burgIconFillColor").on("input", function() {
|
|
const type = elSelected.node().parentNode.id;
|
|
const group = burgIcons.select("#"+type);
|
|
group.attr("fill", this.value);
|
|
});
|
|
|
|
$("#burgIconStrokeWidth").on("input", function() {
|
|
const type = elSelected.node().parentNode.id;
|
|
const group = burgIcons.select("#"+type);
|
|
group.attr("stroke-width", +this.value);
|
|
});
|
|
|
|
$("#burgIconStrokeOpacity").on("input", function() {
|
|
const type = elSelected.node().parentNode.id;
|
|
const group = burgIcons.select("#"+type);
|
|
group.attr("stroke-opacity", +this.value);
|
|
});
|
|
|
|
$("#burgIconStrokeColor").on("input", function() {
|
|
const type = elSelected.node().parentNode.id;
|
|
const group = burgIcons.select("#"+type);
|
|
group.attr("stroke", this.value);
|
|
});
|
|
|
|
$("#burgToggleCapital").click(function() {
|
|
const id = +elSelected.attr("data-id");
|
|
const state = manors[id].region;
|
|
if (states[state] === undefined) return;
|
|
const capital = states[manors[id].region] ? id === states[manors[id].region].capital ? 0 : 1 : 1;
|
|
if (capital && states[state].capital !== "select") {
|
|
// move oldCapital to a town group
|
|
const oldCapital = states[state].capital;
|
|
moveBurgToGroup(oldCapital, "towns");
|
|
}
|
|
states[state].capital = capital ? id : "select";
|
|
d3.select("#burgToggleCapital").classed("pressed", capital);
|
|
const g = capital ? "capitals" : "towns";
|
|
moveBurgToGroup(id, g);
|
|
});
|
|
|
|
$("#burgTogglePort").click(function() {
|
|
const id = +elSelected.attr("data-id");
|
|
const cell = cells[manors[id].cell];
|
|
const markAsPort = cell.port === undefined ? true : undefined;
|
|
cell.port = markAsPort;
|
|
d3.select("#burgTogglePort").classed("pressed", markAsPort);
|
|
if (markAsPort) {
|
|
const type = elSelected.node().parentNode.id;
|
|
const ag = type === "capitals" ? "#capital-anchors" : "#town-anchors";
|
|
const group = icons.select(ag);
|
|
const size = +group.attr("size");
|
|
const x = rn(manors[id].x - size * 0.47, 2);
|
|
const y = rn(manors[id].y - size * 0.47, 2);
|
|
group.append("use").attr("xlink:href", "#icon-anchor").attr("data-id", id)
|
|
.attr("x", x).attr("y", y).attr("width", size).attr("height", size)
|
|
.on("click", editIcon);
|
|
} else {
|
|
$("#icons g[id*='anchors'] [data-id=" + id + "]").remove();
|
|
}
|
|
});
|
|
|
|
$("#burgPopulation").on("input", function() {
|
|
const id = +elSelected.attr("data-id");
|
|
burgPopulationFriendly.value = rn(this.value * urbanization.value * populationRate.value * 1000);
|
|
manors[id].population = +this.value;
|
|
});
|
|
|
|
$("#burgRelocate").click(function() {
|
|
if ($(this).hasClass('pressed')) {
|
|
$(".pressed").removeClass('pressed');
|
|
restoreDefaultEvents();
|
|
tip("", true);
|
|
} else {
|
|
$(".pressed").removeClass('pressed');
|
|
const id = elSelected.attr("data-id");
|
|
$(this).addClass('pressed').attr("data-id", id);
|
|
viewbox.style("cursor", "crosshair").on("click", relocateBurgOnClick);
|
|
tip("Click on map to relocate burg. Hold Shift for continuous move", true);
|
|
}
|
|
});
|
|
|
|
// open legendsEditor
|
|
document.getElementById("burglLegend").addEventListener("click", function() {
|
|
let burg = +elSelected.attr("data-id");
|
|
let id = "burg" + burg;
|
|
let name = manors[burg].name;
|
|
editLegends(id, name);
|
|
});
|
|
|
|
// move burg to a different cell
|
|
function relocateBurgOnClick() {
|
|
const point = d3.mouse(this);
|
|
const index = getIndex(point);
|
|
const i = +$("#burgRelocate").attr("data-id");
|
|
if (isNaN(i) || !manors[i]) return;
|
|
|
|
if (cells[index].height < 20) {
|
|
tip("Cannot place burg in the water! Select a land cell", null, "error");
|
|
return;
|
|
}
|
|
|
|
if (cells[index].manor !== undefined && cells[index].manor !== i) {
|
|
tip("There is already a burg in this cell. Please select a free cell", null, "error");
|
|
$('#grid').fadeIn();
|
|
d3.select("#toggleGrid").classed("buttonoff", false);
|
|
return;
|
|
}
|
|
|
|
let region = cells[index].region;
|
|
const oldRegion = manors[i].region;
|
|
// relocating capital to other country you "conquer" target cell
|
|
if (states[oldRegion] && states[oldRegion].capital === i) {
|
|
if (region !== oldRegion) {
|
|
tip("Capital cannot be moved to another country!", null, "error");
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (d3.event.shiftKey === false) {
|
|
$("#burgRelocate").removeClass("pressed");
|
|
restoreDefaultEvents();
|
|
tip("", true);
|
|
if (region !== oldRegion) {
|
|
recalculateStateData(oldRegion);
|
|
recalculateStateData(region);
|
|
updateCountryEditors();
|
|
}
|
|
}
|
|
|
|
const x = rn(point[0],2), y = rn(point[1],2);
|
|
burgIcons.select("circle[data-id='"+i+"']").attr("transform", null).attr("cx", x).attr("cy", y);
|
|
burgLabels.select("text[data-id='"+i+"']").attr("transform", null).attr("x", x).attr("y", y);
|
|
const anchor = icons.select("use[data-id='"+i+"']");
|
|
if (anchor.size()) {
|
|
const size = anchor.attr("width");
|
|
const xa = rn(x - size * 0.47, 2);
|
|
const ya = rn(y - size * 0.47, 2);
|
|
anchor.attr("transform", null).attr("x", xa).attr("y", ya);
|
|
}
|
|
cells[index].manor = i;
|
|
cells[manors[i].cell].manor = undefined;
|
|
manors[i].x = x, manors[i].y = y, manors[i].region = region, manors[i].cell = index;
|
|
}
|
|
|
|
// open in MFCG
|
|
$("#burgSeeInMFCG").click(function() {
|
|
const id = +elSelected.attr("data-id");
|
|
const name = manors[id].name;
|
|
const cell = manors[id].cell;
|
|
const pop = rn(manors[id].population);
|
|
const size = pop > 65 ? 65 : pop < 6 ? 6 : pop;
|
|
const s = seed + "" + id;
|
|
const hub = cells[cell].crossroad > 2 ? 1 : 0;
|
|
const river = cells[cell].river ? 1 : 0;
|
|
const coast = cells[cell].port !== undefined ? 1 : 0;
|
|
const sec = pop > 40 ? 1 : Math.random() < pop / 100 ? 1 : 0;
|
|
const thr = sec && Math.random() < 0.8 ? 1 : 0;
|
|
const url = "http://fantasycities.watabou.ru/";
|
|
let params = `?name=${name}&size=${size}&seed=${s}&hub=${hub}&random=0&continuous=0`;
|
|
params += `&river=${river}&coast=${coast}&citadel=${id&1}&plaza=${sec}&temple=${thr}&walls=${sec}&shantytown=${sec}`;
|
|
const win = window.open(url+params, '_blank');
|
|
win.focus();
|
|
});
|
|
|
|
$("#burgAddfromEditor").click(function() {
|
|
clickToAdd(); // to load on click event function
|
|
$("#addBurg").click();
|
|
});
|
|
|
|
$("#burgRemove").click(function() {
|
|
alertMessage.innerHTML = `Are you sure you want to remove the Burg?`;
|
|
$("#alert").dialog({resizable: false, title: "Remove Burg",
|
|
buttons: {
|
|
Remove: function() {
|
|
$(this).dialog("close");
|
|
const id = +elSelected.attr("data-id");
|
|
d3.selectAll("[data-id='" + id + "']").remove();
|
|
const cell = manors[id].cell;
|
|
const state = manors[id].region;
|
|
if (states[state]) {
|
|
if (states[state].capital === id) states[state].capital = "select";
|
|
states[state].burgs --;
|
|
}
|
|
manors[id].region = "removed";
|
|
cells[cell].manor = undefined;
|
|
closeDialogs(".stable");
|
|
updateCountryEditors();
|
|
},
|
|
Cancel: function() {$(this).dialog("close");}
|
|
}
|
|
})
|
|
});
|
|
}
|
|
|
|
function editMarker() {
|
|
if (customization) return;
|
|
|
|
unselect();
|
|
closeDialogs("#markerEditor, .stable");
|
|
elSelected = d3.select(this).call(d3.drag().on("start", elementDrag)).classed("draggable", true);
|
|
|
|
$("#markerEditor").dialog({
|
|
title: "Edit Marker",
|
|
minHeight: 30, width: "auto", maxWidth: 275, resizable: false,
|
|
position: {my: "center top+30", at: "bottom", of: d3.event},
|
|
close: unselect
|
|
});
|
|
|
|
// update inputs
|
|
let id = elSelected.attr("href");
|
|
let symbol = d3.select("#defs-markers").select(id);
|
|
let icon = symbol.select("text");
|
|
markerSelectGroup.value = id.slice(1);
|
|
markerIconSize.value = parseFloat(icon.attr("font-size"));
|
|
markerIconShiftX.value = parseFloat(icon.attr("x"));
|
|
markerIconShiftY.value = parseFloat(icon.attr("y"));
|
|
markerIconFill.value = icon.attr("fill");
|
|
markerIconStrokeWidth.value = icon.attr("stroke-width");
|
|
markerIconStroke.value = icon.attr("stroke");
|
|
markerSize.value = elSelected.attr("data-size");
|
|
markerBase.value = symbol.select("path").attr("fill");
|
|
markerFill.value = symbol.select("circle").attr("fill");
|
|
let opacity = symbol.select("circle").attr("opacity");
|
|
markerToggleBubble.className = opacity === "0" ? "icon-info" : "icon-info-circled";
|
|
|
|
let table = document.getElementById("markerIconTable");
|
|
let selected = table.getElementsByClassName("selected");
|
|
if (selected.length) selected[0].removeAttribute("class");
|
|
selected = document.querySelectorAll("#markerIcon" + icon.text().codePointAt());
|
|
if (selected.length) selected[0].className = "selected";
|
|
markerIconCustom.value = selected.length ? "" : icon.text();
|
|
|
|
if (modules.editMarker) return;
|
|
modules.editMarker = true;
|
|
|
|
$("#markerGroup").click(function() {
|
|
$("#markerEditor > button").not(this).toggle();
|
|
$("#markerGroupSection").toggle();
|
|
updateMarkerGroupOptions();
|
|
});
|
|
|
|
function updateMarkerGroupOptions() {
|
|
markerSelectGroup.innerHTML = "";
|
|
d3.select("#defs-markers").selectAll("symbol").each(function() {
|
|
let opt = document.createElement("option");
|
|
opt.value = opt.innerHTML = this.id;
|
|
markerSelectGroup.add(opt);
|
|
});
|
|
let id = elSelected.attr("href").slice(1);
|
|
markerSelectGroup.value = id;
|
|
}
|
|
|
|
// on add marker type click
|
|
document.getElementById("markerAddGroup").addEventListener("click", function() {
|
|
if ($("#markerInputGroup").css("display") === "none") {
|
|
$("#markerInputGroup").css("display", "inline-block");
|
|
$("#markerSelectGroup").css("display", "none");
|
|
markerInputGroup.focus();
|
|
} else {
|
|
$("#markerSelectGroup").css("display", "inline-block");
|
|
$("#markerInputGroup").css("display", "none");
|
|
}
|
|
});
|
|
|
|
// on marker type change
|
|
document.getElementById("markerSelectGroup").addEventListener("change", function() {
|
|
elSelected.attr("href", "#"+this.value);
|
|
elSelected.attr("data-id", "#"+this.value);
|
|
});
|
|
|
|
// on new type input
|
|
document.getElementById("markerInputGroup").addEventListener("change", function() {
|
|
let newGroup = this.value.toLowerCase().replace(/ /g, "_").replace(/[^\w\s]/gi, "");
|
|
if (Number.isFinite(+newGroup.charAt(0))) newGroup = "m" + newGroup;
|
|
if (d3.select("#defs-markers").select("#"+newGroup).size()) {
|
|
tip('The type "'+ newGroup + '" is already exists');
|
|
return;
|
|
}
|
|
markerInputGroup.value = "";
|
|
// clone old group assigning new id
|
|
let id = elSelected.attr("href");
|
|
let l = d3.select("#defs-markers").select(id).node().cloneNode(true);
|
|
l.id = newGroup;
|
|
elSelected.attr("href", "#"+newGroup);
|
|
elSelected.attr("data-id", "#"+newGroup);
|
|
document.getElementById("defs-markers").insertBefore(l, null);
|
|
|
|
// select new group
|
|
let opt = document.createElement("option");
|
|
opt.value = opt.innerHTML = newGroup;
|
|
markerSelectGroup.add(opt);
|
|
$("#markerSelectGroup").val(newGroup).change();
|
|
$("#markerSelectGroup, #markerInputGroup").toggle();
|
|
updateMarkerGroupOptions();
|
|
});
|
|
|
|
$("#markerIconButton").click(function() {
|
|
$("#markerEditor > button").not(this).toggle();
|
|
$("#markerIconButtons").toggle();
|
|
if (!$("#markerIconTable").text()) drawIconsList(icons);
|
|
});
|
|
|
|
$("#markerRemoveGroup").click(function() {
|
|
let id = elSelected.attr("href");
|
|
let used = document.querySelectorAll("use[data-id='"+id+"']");
|
|
let count = used.length === 1 ? "1 element" : used.length + " elements";
|
|
const message = "Are you sure you want to remove the marker (" + count + ")?";
|
|
alertMessage.innerHTML = message;
|
|
$("#alert").dialog({resizable: false, title: "Remove marker",
|
|
buttons: {
|
|
Remove: function() {
|
|
$(this).dialog("close");
|
|
if (id !== "#marker0") d3.select("#defs-markers").select(id).remove();
|
|
used.forEach(function(e) {e.remove();});
|
|
updateMarkerGroupOptions();
|
|
$("#markerEditor").dialog("close");
|
|
},
|
|
Cancel: function() {$(this).dialog("close");}
|
|
}
|
|
});
|
|
});
|