diff --git a/main.js b/main.js
index 4f642d14..6828cab3 100644
--- a/main.js
+++ b/main.js
@@ -338,11 +338,39 @@ function findBurgForMFCG(params) {
// apply default biomes data
function applyDefaultBiomesSystem() {
- const name = ["Marine", "Hot desert", "Cold desert", "Savanna", "Grassland", "Tropical seasonal forest", "Temperate deciduous forest", "Tropical rainforest", "Temperate rainforest", "Taiga", "Tundra", "Glacier", "Wetland"];
+ const name = [
+ "Marine",
+ "Hot desert",
+ "Cold desert",
+ "Savanna",
+ "Grassland",
+ "Tropical seasonal forest",
+ "Temperate deciduous forest",
+ "Tropical rainforest",
+ "Temperate rainforest",
+ "Taiga",
+ "Tundra",
+ "Glacier",
+ "Wetland"
+ ];
const color = ["#466eab", "#fbe79f", "#b5b887", "#d2d082", "#c8d68f", "#b6d95d", "#29bc56", "#7dcb35", "#409c43", "#4b6b32", "#96784b", "#d5e7eb", "#0b9131"];
const habitability = [0, 4, 10, 22, 30, 50, 100, 80, 90, 12, 4, 0, 12];
const iconsDensity = [0, 3, 2, 120, 120, 120, 120, 150, 150, 100, 5, 0, 150];
- const icons = [{}, {dune: 3, cactus: 6, deadTree: 1}, {dune: 9, deadTree: 1}, {acacia: 1, grass: 9}, {grass: 1}, {acacia: 8, palm: 1}, {deciduous: 1}, {acacia: 5, palm: 3, deciduous: 1, swamp: 1}, {deciduous: 6, swamp: 1}, {conifer: 1}, {grass: 1}, {}, {swamp: 1}];
+ const icons = [
+ {},
+ {dune: 3, cactus: 6, deadTree: 1},
+ {dune: 9, deadTree: 1},
+ {acacia: 1, grass: 9},
+ {grass: 1},
+ {acacia: 8, palm: 1},
+ {deciduous: 1},
+ {acacia: 5, palm: 3, deciduous: 1, swamp: 1},
+ {deciduous: 6, swamp: 1},
+ {conifer: 1},
+ {grass: 1},
+ {},
+ {swamp: 1}
+ ];
const cost = [10, 200, 150, 60, 50, 70, 70, 80, 90, 200, 1000, 5000, 150]; // biome movement cost
const biomesMartix = [
// hot ↔ cold [>19°C; <-4°C]; dry ↕ wet
@@ -494,9 +522,7 @@ function invokeActiveZooming() {
+markers.attr("rescale") &&
pack.markers?.forEach(marker => {
const {i, x, y, size = 30, hidden} = marker;
- if (hidden) return;
-
- const el = document.getElementById(`marker${i}`);
+ const el = !hidden && document.getElementById(`marker${i}`);
if (!el) return;
const zoomedSize = Math.max(rn(size / 5 + 24 / scale, 2), 1);
@@ -1483,7 +1509,18 @@ function addZones(number = 1) {
});
}
- const invasion = rw({Invasion: 4, Occupation: 3, Raid: 2, Conquest: 2, Subjugation: 1, Foray: 1, Skirmishes: 1, Incursion: 2, Pillaging: 1, Intervention: 1});
+ const invasion = rw({
+ Invasion: 4,
+ Occupation: 3,
+ Raid: 2,
+ Conquest: 2,
+ Subjugation: 1,
+ Foray: 1,
+ Skirmishes: 1,
+ Incursion: 2,
+ Pillaging: 1,
+ Intervention: 1
+ });
const name = getAdjective(invader.name) + " " + invasion;
data.push({name, type: "Invasion", cells: cellsArray, fill: "url(#hatch1)"});
}
diff --git a/modules/markers-generator.js b/modules/markers-generator.js
index 2ee99eef..40f32e63 100644
--- a/modules/markers-generator.js
+++ b/modules/markers-generator.js
@@ -10,6 +10,7 @@ window.Markers = (function () {
TIME && console.time("addMarkers");
const culturesSet = document.getElementById("culturesSet").value;
+ // TODO: don't put multiple markers to the same cell
addVolcanoes();
addHotSprings();
@@ -120,7 +121,9 @@ window.Markers = (function () {
const {cells, burgs} = pack;
const meanFlux = d3.mean(cells.fl.filter(fl => fl));
- let bridges = Array.from(cells.i.filter(i => cells.burg[i] && cells.t[i] !== 1 && burgs[cells.burg[i]].population > 20 && cells.r[i] && cells.fl[i] > meanFlux));
+ let bridges = Array.from(
+ cells.i.filter(i => cells.burg[i] && cells.t[i] !== 1 && burgs[cells.burg[i]].population > 20 && cells.r[i] && cells.fl[i] > meanFlux)
+ );
let quantity = getQuantity(bridges, 1, 5);
if (!quantity) return;
@@ -143,13 +146,233 @@ window.Markers = (function () {
let quantity = getQuantity(taverns, 1, 100);
if (!quantity) return;
- const colors = ["Dark", "Light", "Bright", "Golden", "White", "Black", "Red", "Pink", "Purple", "Blue", "Green", "Yellow", "Amber", "Orange", "Brown", "Grey"];
- const animals = ["Antelope", "Ape", "Badger", "Bear", "Beaver", "Bison", "Boar", "Buffalo", "Cat", "Crane", "Crocodile", "Crow", "Deer", "Dog", "Eagle", "Elk", "Fox", "Goat", "Goose", "Hare", "Hawk", "Heron", "Horse", "Hyena", "Ibis", "Jackal", "Jaguar", "Lark", "Leopard", "Lion", "Mantis", "Marten", "Moose", "Mule", "Narwhal", "Owl", "Panther", "Rat", "Raven", "Rook", "Scorpion", "Shark", "Sheep", "Snake", "Spider", "Swan", "Tiger", "Turtle", "Wolf", "Wolverine", "Camel", "Falcon", "Hound", "Ox"];
- const adjectives = ["New", "Good", "High", "Old", "Great", "Big", "Major", "Happy", "Main", "Huge", "Far", "Beautiful", "Fair", "Prime", "Ancient", "Golden", "Proud", "Lucky", "Fat", "Honest", "Giant", "Distant", "Friendly", "Loud", "Hungry", "Magical", "Superior", "Peaceful", "Frozen", "Divine", "Favorable", "Brave", "Sunny", "Flying"];
- const methods = ["Boiled", "Grilled", "Roasted", "Spit-roasted", "Stewed", "Stuffed", "Jugged", "Mashed", "Baked", "Braised", "Poached", "Marinated", "Pickled", "Smoked", "Dried", "Dry-aged", "Corned", "Fried", "Pan-fried", "Deep-fried", "Dressed", "Steamed", "Cured", "Syrupped", "Flame-Broiled"];
- const courses = ["beef", "pork", "bacon", "chicken", "lamb", "chevon", "hare", "rabbit", "hart", "deer", "antlers", "bear", "buffalo", "badger", "beaver", "turkey", "pheasant", "duck", "goose", "teal", "quail", "pigeon", "seal", "carp", "bass", "pike", "catfish", "sturgeon", "escallop", "pie", "cake", "pottage", "pudding", "onions", "carrot", "potato", "beet", "garlic", "cabbage", "eggplant", "eggs", "broccoli", "zucchini", "pepper", "olives", "pumpkin", "spinach", "peas", "chickpea", "beans", "rice", "pasta", "bread", "apples", "peaches", "pears", "melon", "oranges", "mango", "tomatoes", "cheese", "corn", "rat tails", "pig ears"];
+ const colors = [
+ "Dark",
+ "Light",
+ "Bright",
+ "Golden",
+ "White",
+ "Black",
+ "Red",
+ "Pink",
+ "Purple",
+ "Blue",
+ "Green",
+ "Yellow",
+ "Amber",
+ "Orange",
+ "Brown",
+ "Grey"
+ ];
+ const animals = [
+ "Antelope",
+ "Ape",
+ "Badger",
+ "Bear",
+ "Beaver",
+ "Bison",
+ "Boar",
+ "Buffalo",
+ "Cat",
+ "Crane",
+ "Crocodile",
+ "Crow",
+ "Deer",
+ "Dog",
+ "Eagle",
+ "Elk",
+ "Fox",
+ "Goat",
+ "Goose",
+ "Hare",
+ "Hawk",
+ "Heron",
+ "Horse",
+ "Hyena",
+ "Ibis",
+ "Jackal",
+ "Jaguar",
+ "Lark",
+ "Leopard",
+ "Lion",
+ "Mantis",
+ "Marten",
+ "Moose",
+ "Mule",
+ "Narwhal",
+ "Owl",
+ "Panther",
+ "Rat",
+ "Raven",
+ "Rook",
+ "Scorpion",
+ "Shark",
+ "Sheep",
+ "Snake",
+ "Spider",
+ "Swan",
+ "Tiger",
+ "Turtle",
+ "Wolf",
+ "Wolverine",
+ "Camel",
+ "Falcon",
+ "Hound",
+ "Ox"
+ ];
+ const adjectives = [
+ "New",
+ "Good",
+ "High",
+ "Old",
+ "Great",
+ "Big",
+ "Major",
+ "Happy",
+ "Main",
+ "Huge",
+ "Far",
+ "Beautiful",
+ "Fair",
+ "Prime",
+ "Ancient",
+ "Golden",
+ "Proud",
+ "Lucky",
+ "Fat",
+ "Honest",
+ "Giant",
+ "Distant",
+ "Friendly",
+ "Loud",
+ "Hungry",
+ "Magical",
+ "Superior",
+ "Peaceful",
+ "Frozen",
+ "Divine",
+ "Favorable",
+ "Brave",
+ "Sunny",
+ "Flying"
+ ];
+ const methods = [
+ "Boiled",
+ "Grilled",
+ "Roasted",
+ "Spit-roasted",
+ "Stewed",
+ "Stuffed",
+ "Jugged",
+ "Mashed",
+ "Baked",
+ "Braised",
+ "Poached",
+ "Marinated",
+ "Pickled",
+ "Smoked",
+ "Dried",
+ "Dry-aged",
+ "Corned",
+ "Fried",
+ "Pan-fried",
+ "Deep-fried",
+ "Dressed",
+ "Steamed",
+ "Cured",
+ "Syrupped",
+ "Flame-Broiled"
+ ];
+ const courses = [
+ "beef",
+ "pork",
+ "bacon",
+ "chicken",
+ "lamb",
+ "chevon",
+ "hare",
+ "rabbit",
+ "hart",
+ "deer",
+ "antlers",
+ "bear",
+ "buffalo",
+ "badger",
+ "beaver",
+ "turkey",
+ "pheasant",
+ "duck",
+ "goose",
+ "teal",
+ "quail",
+ "pigeon",
+ "seal",
+ "carp",
+ "bass",
+ "pike",
+ "catfish",
+ "sturgeon",
+ "escallop",
+ "pie",
+ "cake",
+ "pottage",
+ "pudding",
+ "onions",
+ "carrot",
+ "potato",
+ "beet",
+ "garlic",
+ "cabbage",
+ "eggplant",
+ "eggs",
+ "broccoli",
+ "zucchini",
+ "pepper",
+ "olives",
+ "pumpkin",
+ "spinach",
+ "peas",
+ "chickpea",
+ "beans",
+ "rice",
+ "pasta",
+ "bread",
+ "apples",
+ "peaches",
+ "pears",
+ "melon",
+ "oranges",
+ "mango",
+ "tomatoes",
+ "cheese",
+ "corn",
+ "rat tails",
+ "pig ears"
+ ];
const types = ["hot", "cold", "fire", "ice", "smoky", "misty", "shiny", "sweet", "bitter", "salty", "sour", "sparkling", "smelly"];
- const drinks = ["wine", "brandy", "jinn", "whisky", "rom", "beer", "cider", "mead", "liquor", "spirit", "vodka", "tequila", "absinthe", "nectar", "milk", "kvass", "kumis", "tea", "water", "juice", "sap"];
+ const drinks = [
+ "wine",
+ "brandy",
+ "jinn",
+ "whisky",
+ "rom",
+ "beer",
+ "cider",
+ "mead",
+ "liquor",
+ "spirit",
+ "vodka",
+ "tequila",
+ "absinthe",
+ "nectar",
+ "milk",
+ "kvass",
+ "kumis",
+ "tea",
+ "water",
+ "juice",
+ "sap"
+ ];
while (quantity) {
const [cell] = extractAnyElement(taverns);
@@ -282,7 +505,14 @@ window.Markers = (function () {
const subjects = ["Locals", "Old folks", "Old books", "Tipplers"];
const species = ["Ogre", "Troll", "Cyclops", "Giant", "Monster", "Beast", "Dragon", "Undead", "Ghoul", "Vampire"];
- const modusOperandi = ["steals their cattle", "doesn't mind eating children", "doesn't mind of human flesh", "keeps the region at bay", "eats their kids", "abducts young women"];
+ const modusOperandi = [
+ "steals their cattle",
+ "doesn't mind eating children",
+ "doesn't mind of human flesh",
+ "keeps the region at bay",
+ "eats their kids",
+ "abducts young women"
+ ];
while (quantity) {
const [cell] = extractAnyElement(hills);
@@ -376,7 +606,38 @@ window.Markers = (function () {
let quantity = getQuantity(roads, 50, 100);
if (!quantity) return;
- const animals = ["Apes", "Badgers", "Bears", "Beavers", "Bisons", "Boars", "Cats", "Crows", "Dogs", "Foxes", "Hares", "Hawks", "Hyenas", "Jackals", "Jaguars", "Leopards", "Lions", "Owls", "Panthers", "Rats", "Ravens", "Rooks", "Scorpions", "Sharks", "Snakes", "Spiders", "Tigers", "Wolfs", "Wolverines", "Falcons"];
+ const animals = [
+ "Apes",
+ "Badgers",
+ "Bears",
+ "Beavers",
+ "Bisons",
+ "Boars",
+ "Cats",
+ "Crows",
+ "Dogs",
+ "Foxes",
+ "Hares",
+ "Hawks",
+ "Hyenas",
+ "Jackals",
+ "Jaguars",
+ "Leopards",
+ "Lions",
+ "Owls",
+ "Panthers",
+ "Rats",
+ "Ravens",
+ "Rooks",
+ "Scorpions",
+ "Sharks",
+ "Snakes",
+ "Spiders",
+ "Tigers",
+ "Wolfs",
+ "Wolverines",
+ "Falcons"
+ ];
const types = {brigands: 4, bandits: 3, robbers: 1, highwaymen: 1};
while (quantity) {
@@ -385,7 +646,18 @@ window.Markers = (function () {
const culture = cells.culture[cell];
const biome = cells.biome[cell];
const height = cells.p[cell];
- const locality = height >= 70 ? "highlander" : [1, 2].includes(biome) ? "desert" : [3, 4].includes(biome) ? "mounted" : [5, 6, 7, 8, 9].includes(biome) ? "forest" : biome === 12 ? "swamp" : "angry";
+ const locality =
+ height >= 70
+ ? "highlander"
+ : [1, 2].includes(biome)
+ ? "desert"
+ : [3, 4].includes(biome)
+ ? "mounted"
+ : [5, 6, 7, 8, 9].includes(biome)
+ ? "forest"
+ : biome === 12
+ ? "swamp"
+ : "angry";
const name = `${Names.getCulture(culture)} ${ra(animals)}`;
const legend = `A gang of ${locality} ${rw(types)}`;
notes.push({id, name, legend});
diff --git a/modules/ui/editors.js b/modules/ui/editors.js
index 55a88e92..774ecc3d 100644
--- a/modules/ui/editors.js
+++ b/modules/ui/editors.js
@@ -29,7 +29,7 @@ function clicked() {
else if (grand.id === "burgIcons") editBurg();
else if (parent.id === "ice") editIce();
else if (parent.id === "terrain") editReliefIcon();
- else if (grand.id === "markers") editMarker();
+ else if (grand.id === "markers" || great.id === "markers") editMarker();
else if (grand.id === "coastline") editCoastline();
else if (great.id === "armies") editRegiment();
else if (pack.cells.t[i] === 1) {
@@ -332,7 +332,15 @@ function drawLegend(name, data) {
const width = bbox.width + colOffset * 2;
const height = bbox.height + colOffset / 2 + vOffset;
- legend.insert("rect", ":first-child").attr("id", "legendBox").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height).attr("fill", backClr).attr("fill-opacity", opacity);
+ legend
+ .insert("rect", ":first-child")
+ .attr("id", "legendBox")
+ .attr("x", 0)
+ .attr("y", 0)
+ .attr("width", width)
+ .attr("height", height)
+ .attr("fill", backClr)
+ .attr("fill-opacity", opacity);
fitLegendBox();
}
@@ -385,7 +393,15 @@ function createPicker() {
const closePicker = () => contaiter.style("display", "none");
const contaiter = d3.select("body").append("svg").attr("id", "pickerContainer").attr("width", "100%").attr("height", "100%");
- contaiter.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%").attr("opacity", 0.2).on("mousemove", cl).on("click", closePicker);
+ contaiter
+ .append("rect")
+ .attr("x", 0)
+ .attr("y", 0)
+ .attr("width", "100%")
+ .attr("height", "100%")
+ .attr("opacity", 0.2)
+ .on("mousemove", cl)
+ .on("click", closePicker);
const picker = contaiter
.append("g")
.attr("id", "picker")
@@ -484,9 +500,25 @@ function createPicker() {
const width = bbox.width + 8;
const height = bbox.height + 9;
- picker.insert("rect", ":first-child").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height).attr("fill", "#ffffff").attr("stroke", "#5d4651").on("mousemove", pos);
+ picker
+ .insert("rect", ":first-child")
+ .attr("x", 0)
+ .attr("y", 0)
+ .attr("width", width)
+ .attr("height", height)
+ .attr("fill", "#ffffff")
+ .attr("stroke", "#5d4651")
+ .on("mousemove", pos);
picker.insert("text", ":first-child").attr("x", 291).attr("y", -10).attr("id", "pickerCloseText").text("✕");
- picker.insert("rect", ":first-child").attr("x", 288).attr("y", -21).attr("id", "pickerCloseRect").attr("width", 14).attr("height", 14).on("mousemove", cl).on("click", closePicker);
+ picker
+ .insert("rect", ":first-child")
+ .attr("x", 288)
+ .attr("y", -21)
+ .attr("id", "pickerCloseRect")
+ .attr("width", 14)
+ .attr("height", 14)
+ .on("mousemove", cl)
+ .on("click", closePicker);
picker.insert("text", ":first-child").attr("x", 12).attr("y", -10).attr("id", "pickerLabel").text("Color Picker").on("mousemove", pos);
picker.insert("rect", ":first-child").attr("x", 0).attr("y", -30).attr("width", width).attr("height", 30).attr("id", "pickerHeader").on("mousemove", pos);
picker.attr("transform", `translate(${(svgWidth - width) / 2},${(svgHeight - height) / 2})`);
@@ -705,7 +737,14 @@ function highlightElement(element) {
const highlight = debug.append("rect").attr("x", box.x).attr("y", box.y).attr("width", box.width).attr("height", box.height).attr("transform", transform);
- highlight.classed("highlighted", 1).transition(enter).style("outline-offset", "0px").transition(exit).style("outline-color", "transparent").delay(1000).remove();
+ highlight
+ .classed("highlighted", 1)
+ .transition(enter)
+ .style("outline-offset", "0px")
+ .transition(exit)
+ .style("outline-color", "transparent")
+ .delay(1000)
+ .remove();
const tr = parseTransform(transform);
let x = box.x + box.width / 2;
@@ -922,6 +961,7 @@ function selectIcon(initial, callback) {
}
}
+ input.oninput = e => callback(input.value);
table.onclick = e => {
if (e.target.tagName === "TD") {
input.value = e.target.innerHTML;
@@ -948,6 +988,31 @@ function selectIcon(initial, callback) {
});
}
+function confirmationDialog(options) {
+ const {
+ title = "Confirm action",
+ message = "Are you sure you want to continue?
The action cannot be reverted",
+ cancel = "Cancel",
+ confirm = "Continue",
+ onCancel,
+ onConfirm
+ } = options;
+
+ const buttons = {
+ [confirm]: function () {
+ if (onConfirm) onConfirm();
+ $(this).dialog("close");
+ },
+ [cancel]: function () {
+ if (onCancel) onCancel();
+ $(this).dialog("close");
+ }
+ };
+
+ document.getElementById("alertMessage").innerHTML = message;
+ $("#alert").dialog({resizable: false, title, buttons});
+}
+
// add and register event listeners to clean up on editor closure
function listen(element, event, handler) {
element.addEventListener(event, handler);
diff --git a/modules/ui/layers.js b/modules/ui/layers.js
index 76cf9d79..ffa021fa 100644
--- a/modules/ui/layers.js
+++ b/modules/ui/layers.js
@@ -196,7 +196,8 @@ function drawHeightmap() {
for (const i of d3.range(20, 101)) {
if (paths[i].length < 10) continue;
const color = getColor(i, scheme);
- if (terracing) terrs.append("path").attr("d", paths[i]).attr("transform", "translate(.7,1.4)").attr("fill", d3.color(color).darker(terracing)).attr("data-height", i);
+ if (terracing)
+ terrs.append("path").attr("d", paths[i]).attr("transform", "translate(.7,1.4)").attr("fill", d3.color(color).darker(terracing)).attr("data-height", i);
terrs.append("path").attr("d", paths[i]).attr("fill", color).attr("data-height", i);
}
@@ -798,7 +799,10 @@ function drawReligions() {
if (!vArray[r]) vArray[r] = [];
vArray[r].push(points);
body[r] += "M" + points.join("L");
- gap[r] += "M" + vertices.p[chain[0][0]] + chain.reduce((r2, v, i, d) => (!i ? r2 : !v[2] ? r2 + "L" + vertices.p[v[0]] : d[i + 1] && !d[i + 1][2] ? r2 + "M" + vertices.p[v[0]] : r2), "");
+ gap[r] +=
+ "M" +
+ vertices.p[chain[0][0]] +
+ chain.reduce((r2, v, i, d) => (!i ? r2 : !v[2] ? r2 + "L" + vertices.p[v[0]] : d[i + 1] && !d[i + 1][2] ? r2 + "M" + vertices.p[v[0]] : r2), "");
}
const bodyData = body.map((p, i) => [p.length > 10 ? p : null, i, religions[i].color]).filter(d => d[0]);
@@ -966,7 +970,12 @@ function drawStates() {
const gapString = gapData.map(d => `
`).join("");
const clipString = bodyData.map(d => `
`).join("");
const haloString = haloData
- .map(d => `
`)
+ .map(
+ d =>
+ `
`
+ )
.join("");
statesBody.html(bodyString + gapString);
@@ -1219,7 +1228,10 @@ function getProvincesVertices() {
if (!vArray[p]) vArray[p] = [];
vArray[p].push(points);
body[p] += "M" + points.join("L");
- gap[p] += "M" + vertices.p[chain[0][0]] + chain.reduce((r, v, i, d) => (!i ? r : !v[2] ? r + "L" + vertices.p[v[0]] : d[i + 1] && !d[i + 1][2] ? r + "M" + vertices.p[v[0]] : r), "");
+ gap[p] +=
+ "M" +
+ vertices.p[chain[0][0]] +
+ chain.reduce((r, v, i, d) => (!i ? r : !v[2] ? r + "L" + vertices.p[v[0]] : d[i + 1] && !d[i + 1][2] ? r + "M" + vertices.p[v[0]] : r), "");
}
// find province visual center
@@ -1300,7 +1312,12 @@ function drawGrid() {
const maxWidth = Math.max(+mapWidthInput.value, graphWidth);
const maxHeight = Math.max(+mapHeightInput.value, graphHeight);
- d3.select(pattern).attr("stroke", stroke).attr("stroke-width", width).attr("stroke-dasharray", dasharray).attr("stroke-linecap", linecap).attr("patternTransform", tr);
+ d3.select(pattern)
+ .attr("stroke", stroke)
+ .attr("stroke-width", width)
+ .attr("stroke-dasharray", dasharray)
+ .attr("stroke-linecap", linecap)
+ .attr("patternTransform", tr);
gridOverlay
.append("rect")
.attr("width", maxWidth)
@@ -1510,23 +1527,23 @@ function toggleMarkers(event) {
drawMarkers();
if (event && isCtrlClick(event)) editStyle("markers");
} else {
- if (event && isCtrlClick(event)) {
- editStyle("markers");
- return;
- }
+ if (event && isCtrlClick(event)) return editStyle("markers");
markers.selectAll("*").remove();
turnButtonOff("toggleMarkers");
}
}
function drawMarkers() {
- const html = pack.markers.map(drawMarker);
+ const rescale = +markers.attr("rescale");
+ const html = pack.markers.map(marker => drawMarker(marker, rescale));
markers.html(html.join(""));
}
const getPin = (shape = "bubble", fill = "#fff", stroke = "#000") => {
- if (shape === "bubble") return `
`;
- if (shape === "pin") return `
`;
+ if (shape === "bubble")
+ return `
`;
+ if (shape === "pin")
+ return `
`;
if (shape === "square") return `
`;
if (shape === "squarish") return `
`;
if (shape === "diamond") return `
`;
@@ -1540,14 +1557,14 @@ const getPin = (shape = "bubble", fill = "#fff", stroke = "#000") => {
if (shape === "no") return "";
};
-function drawMarker(marker) {
+function drawMarker(marker, rescale = 1) {
const {i, icon, x, y, dx = 50, dy = 50, px = 12, size = 30} = marker;
const id = `marker${i}`;
- const zoomSize = Math.max(rn(size / 5 + 24 / scale, 2), 1);
+ const zoomSize = rescale ? Math.max(rn(size / 5 + 24 / scale, 2), 1) : 1;
const viewX = rn(x - zoomSize / 2, 1);
const viewY = rn(y - zoomSize, 1);
- return `
`;
+ return `
`;
}
function toggleLabels(event) {
@@ -1717,15 +1734,21 @@ function drawEmblems() {
}
const burgNodes = nodes.filter(node => node.type === "burg");
- const burgString = burgNodes.map(d => `
`).join("");
+ const burgString = burgNodes
+ .map(d => `
`)
+ .join("");
emblems.select("#burgEmblems").attr("font-size", sizeBurgs).html(burgString);
const provinceNodes = nodes.filter(node => node.type === "province");
- const provinceString = provinceNodes.map(d => `
`).join("");
+ const provinceString = provinceNodes
+ .map(d => `
`)
+ .join("");
emblems.select("#provinceEmblems").attr("font-size", sizeProvinces).html(provinceString);
const stateNodes = nodes.filter(node => node.type === "state");
- const stateString = stateNodes.map(d => `
`).join("");
+ const stateString = stateNodes
+ .map(d => `
`)
+ .join("");
emblems.select("#stateEmblems").attr("font-size", sizeStates).html(stateString);
invokeActiveZooming();
diff --git a/modules/ui/markers-editor.js b/modules/ui/markers-editor.js
index 07bdef31..2e6f3f93 100644
--- a/modules/ui/markers-editor.js
+++ b/modules/ui/markers-editor.js
@@ -3,23 +3,28 @@ function editMarker() {
if (customization) return;
closeDialogs(".stable");
- const element = d3.event.target.parentElement;
- elSelected = d3.select(element).call(d3.drag().on("start", dragMarker)).classed("draggable", true);
- const marker = pack.markers.find(({i}) => Number(elSelected.attr("id").slice(6)) === i);
+ const element = d3.event.target.closest("svg");
+ const marker = pack.markers.find(({i}) => Number(element.id.slice(6)) === i);
if (!marker) return;
+ elSelected = d3.select(element).raise().call(d3.drag().on("start", dragMarker)).classed("draggable", true);
+
// dom elements
- const markerSelectGroup = document.getElementById("markerSelectGroup");
+ const markerType = document.getElementById("markerType");
+ const markerIcon = document.getElementById("markerIcon");
+ const markerIconSelect = document.getElementById("markerIconSelect");
const markerIconSize = document.getElementById("markerIconSize");
const markerIconShiftX = document.getElementById("markerIconShiftX");
const markerIconShiftY = document.getElementById("markerIconShiftY");
-
const markerSize = document.getElementById("markerSize");
- const markerBaseStroke = document.getElementById("markerBaseStroke");
- const markerBaseFill = document.getElementById("markerBaseFill");
+ const markerPin = document.getElementById("markerPin");
+ const markerFill = document.getElementById("markerFill");
+ const markerStroke = document.getElementById("markerStroke");
- const markerToggleBubble = document.getElementById("markerToggleBubble");
- const markerIconSelect = document.getElementById("markerIconSelect");
+ const markerNotes = document.getElementById("markerNotes");
+ const addMarker = document.getElementById("addMarker");
+ const markerAdd = document.getElementById("markerAdd");
+ const markerRemove = document.getElementById("markerRemove");
updateInputs();
@@ -31,22 +36,27 @@ function editMarker() {
});
const listeners = [
- listen(markerSelectGroup, "change", changeGroup),
- listen(document.getElementById("markerIcon"), "click", toggleIconSection),
+ listen(markerType, "change", changeMarkerType),
+ listen(markerIcon, "input", changeMarkerIcon),
+ listen(markerIconSelect, "click", selectMarkerIcon),
listen(markerIconSize, "input", changeIconSize),
listen(markerIconShiftX, "input", changeIconShiftX),
listen(markerIconShiftY, "input", changeIconShiftY),
- listen(document.getElementById("markerIconSelect"), "click", selectMarkerIcon),
- listen(document.getElementById("markerStyle"), "click", toggleStyleSection),
listen(markerSize, "input", changeMarkerSize),
- listen(markerBaseStroke, "input", changePinStroke),
- listen(markerBaseFill, "input", changePinFill),
- listen(markerToggleBubble, "click", togglePinVisibility),
- listen(document.getElementById("markerLegendButton"), "click", editMarkerLegend),
- listen(document.getElementById("markerAdd"), "click", toggleAddMarker),
- listen(document.getElementById("markerRemove"), "click", removeMarker)
+ listen(markerPin, "change", changeMarkerPin),
+ listen(markerFill, "input", changePinFill),
+ listen(markerStroke, "input", changePinStroke),
+ listen(markerNotes, "click", editMarkerLegend),
+ listen(markerAdd, "click", toggleAddMarker),
+ listen(markerRemove, "click", confirmMarkerDeletion)
];
+ function getSameTypeMarkers() {
+ const currentType = marker.type;
+ if (!currentType) return [marker];
+ return pack.markers.filter(({type}) => type === currentType);
+ }
+
function dragMarker() {
const dx = +this.getAttribute("x") - d3.event.x;
const dy = +this.getAttribute("y") - d3.event.y;
@@ -70,173 +80,147 @@ function editMarker() {
}
function updateInputs() {
- const {icon, type = "", size = 30, dx = 50, dy = 50, px = 12, stroke = "#000", fill = "#fff", pin = "bubble"} = marker;
+ const {icon, type = "", size = 30, dx = 50, dy = 50, px = 12, stroke = "#000000", fill = "#ffffff", pin = "bubble"} = marker;
- markerSelectGroup.value = type;
+ markerType.value = type;
+ markerIcon.value = icon;
markerIconSize.value = px;
markerIconShiftX.value = dx;
markerIconShiftY.value = dy;
-
markerSize.value = size;
- markerBaseStroke.value = stroke;
- markerBaseFill.value = fill;
-
- markerToggleBubble.className = pin;
- markerIconSelect.innerHTML = icon;
+ markerPin.value = pin;
+ markerFill.value = fill;
+ markerStroke.value = stroke;
}
- function toggleGroupSection() {
- if (markerGroupSection.style.display === "inline-block") {
- markerEditor.querySelectorAll("button:not(#markerGroup)").forEach(b => (b.style.display = "inline-block"));
- markerGroupSection.style.display = "none";
- } else {
- markerEditor.querySelectorAll("button:not(#markerGroup)").forEach(b => (b.style.display = "none"));
- markerGroupSection.style.display = "inline-block";
- }
+ function changeMarkerType() {
+ marker.type = this.value;
}
- function changeGroup() {
- elSelected.attr("xlink:href", "#" + this.value);
- elSelected.attr("data-id", "#" + this.value);
- }
-
- function toggleIconSection() {
- console.log(marker);
- if (markerIconSection.style.display === "inline-block") {
- markerEditor.querySelectorAll("button:not(#markerIcon)").forEach(b => (b.style.display = "inline-block"));
- markerIconSection.style.display = "none";
- markerIconSelect.style.display = "none";
- } else {
- markerEditor.querySelectorAll("button:not(#markerIcon)").forEach(b => (b.style.display = "none"));
- markerIconSection.style.display = "inline-block";
- markerIconSelect.style.display = "inline-block";
- }
+ function changeMarkerIcon() {
+ const icon = this.value;
+ getSameTypeMarkers().forEach(marker => {
+ marker.icon = icon;
+ redrawIcon(marker);
+ });
}
function selectMarkerIcon() {
- selectIcon(this.innerHTML, v => {
- this.innerHTML = v;
- const id = elSelected.attr("data-id");
- d3.select("#defs-markers").select(id).select("text").text(v);
+ selectIcon(marker.icon, icon => {
+ markerIcon.value = icon;
+ getSameTypeMarkers().forEach(marker => {
+ marker.icon = icon;
+ redrawIcon(marker);
+ });
});
}
function changeIconSize() {
- const id = elSelected.attr("data-id");
- d3.select("#defs-markers")
- .select(id)
- .select("text")
- .attr("font-size", this.value + "px");
+ const px = +this.value;
+ getSameTypeMarkers().forEach(marker => {
+ marker.px = px;
+ redrawIcon(marker);
+ });
}
function changeIconShiftX() {
- const id = elSelected.attr("data-id");
- d3.select("#defs-markers")
- .select(id)
- .select("text")
- .attr("x", this.value + "%");
+ const dx = +this.value;
+ getSameTypeMarkers().forEach(marker => {
+ marker.dx = dx;
+ redrawIcon(marker);
+ });
}
function changeIconShiftY() {
- const id = elSelected.attr("data-id");
- d3.select("#defs-markers")
- .select(id)
- .select("text")
- .attr("y", this.value + "%");
- }
-
- function toggleStyleSection() {
- if (markerStyleSection.style.display === "inline-block") {
- markerEditor.querySelectorAll("button:not(#markerStyle)").forEach(b => (b.style.display = "inline-block"));
- markerStyleSection.style.display = "none";
- } else {
- markerEditor.querySelectorAll("button:not(#markerStyle)").forEach(b => (b.style.display = "none"));
- markerStyleSection.style.display = "inline-block";
- }
+ const dy = +this.value;
+ getSameTypeMarkers().forEach(marker => {
+ marker.dy = dy;
+ redrawIcon(marker);
+ });
}
function changeMarkerSize() {
- const id = elSelected.attr("data-id");
- document.querySelectorAll("use[data-id='" + id + "']").forEach(e => {
- const x = +e.dataset.x,
- y = +e.dataset.y;
- const desired = (e.dataset.size = +markerSize.value);
- const size = Math.max(desired * 5 + 25 / scale, 1);
+ const size = +this.value;
+ const rescale = +markers.attr("rescale");
- e.setAttribute("x", x - size / 2);
- e.setAttribute("y", y - size / 2);
- e.setAttribute("width", size);
- e.setAttribute("height", size);
+ getSameTypeMarkers().forEach(marker => {
+ marker.size = size;
+ const {i, x, y, hidden} = marker;
+ const el = !hidden && document.getElementById(`marker${i}`);
+ if (!el) return;
+
+ const zoomedSize = rescale ? Math.max(rn(size / 5 + 24 / scale, 2), 1) : size;
+ el.setAttribute("width", zoomedSize);
+ el.setAttribute("height", zoomedSize);
+ el.setAttribute("x", rn(x - zoomedSize / 2, 1));
+ el.setAttribute("y", rn(y - zoomedSize, 1));
});
- invokeActiveZooming();
}
- function changePinStroke() {
- const id = elSelected.attr("data-id");
- d3.select(id).select("path").attr("fill", this.value);
- d3.select(id).select("circle").attr("stroke", this.value);
+ function changeMarkerPin() {
+ const pin = this.value;
+ getSameTypeMarkers().forEach(marker => {
+ marker.pin = pin;
+ redrawPin(marker);
+ });
}
function changePinFill() {
- const id = elSelected.attr("data-id");
- d3.select(id).select("circle").attr("fill", this.value);
+ const fill = this.value;
+ getSameTypeMarkers().forEach(marker => {
+ marker.fill = fill;
+ redrawPin(marker);
+ });
}
- function changeIconStrokeWidth() {
- const id = elSelected.attr("data-id");
- d3.select("#defs-markers").select(id).select("text").attr("stroke-width", this.value);
+ function changePinStroke() {
+ const stroke = this.value;
+ getSameTypeMarkers().forEach(marker => {
+ marker.stroke = stroke;
+ redrawPin(marker);
+ });
}
- function changeIconStroke() {
- const id = elSelected.attr("data-id");
- d3.select("#defs-markers").select(id).select("text").attr("stroke", this.value);
+ function redrawIcon({i, hidden, icon, dx = 50, dy = 50, px = 12}) {
+ const iconElement = !hidden && document.querySelector(`#marker${i} > text`);
+ if (iconElement) {
+ iconElement.innerHTML = icon;
+ iconElement.setAttribute("x", dx + "%");
+ iconElement.setAttribute("y", dy + "%");
+ iconElement.setAttribute("font-size", px + "px");
+ }
}
- function changeIconFill() {
- const id = elSelected.attr("data-id");
- d3.select("#defs-markers").select(id).select("text").attr("fill", this.value);
- }
-
- function togglePinVisibility() {
- const id = elSelected.attr("data-id");
- let show = 1;
- if (this.className === "icon-info-circled") {
- this.className = "icon-info";
- show = 0;
- } else this.className = "icon-info-circled";
- d3.select(id).select("circle").attr("opacity", show);
- d3.select(id).select("path").attr("opacity", show);
+ function redrawPin({i, hidden, pin = "bubble", fill = "#fff", stroke = "#000"}) {
+ const pinGroup = !hidden && document.querySelector(`#marker${i} > g`);
+ if (pinGroup) pinGroup.innerHTML = getPin(pin, fill, stroke);
}
function editMarkerLegend() {
- const id = elSelected.attr("id");
+ const id = element.id;
editNotes(id, id);
}
function toggleAddMarker() {
- document.getElementById("addMarker").click();
+ addMarker.click();
}
- function removeMarker() {
- alertMessage.innerHTML = "Are you sure you want to remove the marker?";
- $("#alert").dialog({
- resizable: false,
+ function confirmMarkerDeletion() {
+ confirmationDialog({
title: "Remove marker",
- buttons: {
- Remove: function () {
- $(this).dialog("close");
- const index = notes.findIndex(n => n.id === elSelected.attr("id"));
- if (index != -1) notes.splice(index, 1);
- elSelected.remove();
- $("#markerEditor").dialog("close");
- },
- Cancel: function () {
- $(this).dialog("close");
- }
- }
+ message: "Are you sure you want to remove this marker? The action cannot be reverted",
+ confirm: "Remove",
+ onConfirm: deleteMarker
});
}
+ function deleteMarker() {
+ notes = notes.filter(note => note.id !== element.id);
+ pack.markers = pack.markers.filter(m => m.i !== marker.i);
+ element.remove();
+ $("#markerEditor").dialog("close");
+ }
+
function closeMarkerEditor() {
listeners.forEach(removeListener => removeListener());
diff --git a/modules/ui/tools.js b/modules/ui/tools.js
index d86f961a..cdd17ad5 100644
--- a/modules/ui/tools.js
+++ b/modules/ui/tools.js
@@ -49,7 +49,9 @@ toolsContent.addEventListener("click", function (event) {
},
open: function () {
const pane = $(this).dialog("widget").find(".ui-dialog-buttonpane");
- $('
').prependTo(pane);
+ $(
+ ''
+ ).prependTo(pane);
},
close: function () {
const box = $(this).dialog("widget").find(".checkbox")[0];
@@ -262,7 +264,8 @@ function regenerateBurgs() {
const score = new Int16Array(cells.s.map(s => s * Math.random())); // cell score for capitals placement
const sorted = cells.i.filter(i => score[i] > 0 && cells.culture[i]).sort((a, b) => score[b] - score[a]); // filtered and sorted array of indexes
- const burgsCount = manorsInput.value == 1000 ? rn(sorted.length / 5 / (grid.points.length / 10000) ** 0.8) + states.length : +manorsInput.value + states.length;
+ const burgsCount =
+ manorsInput.value == 1000 ? rn(sorted.length / 5 / (grid.points.length / 10000) ** 0.8) + states.length : +manorsInput.value + states.length;
const spacing = (graphWidth + graphHeight) / 150 / (burgsCount ** 0.7 / 66); // base min distance between towns
//clear locked list since ids will change
@@ -414,6 +417,7 @@ function regenerateIce() {
}
function regenerateMarkers(event) {
+ // TODO: rework for new markers system
if (isCtrlClick(event)) prompt("Please provide markers number multiplier", {default: 1, step: 0.01, min: 0, max: 100}, v => addNumberOfMarkers(v));
else addNumberOfMarkers(gauss(1, 0.5, 0.3, 5, 2));
@@ -475,7 +479,18 @@ function addLabelOnClick() {
const id = getNextId("label");
let group = labels.select("#addedLabels");
- if (!group.size()) group = labels.append("g").attr("id", "addedLabels").attr("fill", "#3e3e4b").attr("opacity", 1).attr("stroke", "#3a3a3a").attr("stroke-width", 0).attr("font-family", "Almendra SC").attr("font-size", 18).attr("data-size", 18).attr("filter", null);
+ if (!group.size())
+ group = labels
+ .append("g")
+ .attr("id", "addedLabels")
+ .attr("fill", "#3e3e4b")
+ .attr("opacity", 1)
+ .attr("stroke", "#3a3a3a")
+ .attr("stroke-width", 0)
+ .attr("font-family", "Almendra SC")
+ .attr("font-size", 18)
+ .attr("data-size", 18)
+ .attr("filter", null);
const example = group.append("text").attr("x", 0).attr("x", 0).text(name);
const width = example.node().getBBox().width;