@@ -2786,8 +2790,8 @@
diff --git a/main.js b/main.js
index d3713113..b718f045 100644
--- a/main.js
+++ b/main.js
@@ -284,7 +284,7 @@ function applyDefaultStyle() {
coastline.attr("opacity", .5).attr("stroke", "#1f3846").attr("stroke-width", .7).attr("filter", "url(#dropShadow)");
styleCoastlineAuto.checked = true;
- relig.attr("opacity", .8).attr("stroke", "#777777").attr("stroke-width", 0).attr("filter", null).attr("fill-rule", "evenodd");
+ relig.attr("opacity", .6).attr("stroke", "#777777").attr("stroke-width", .2).attr("filter", null).attr("fill-rule", "evenodd");
cults.attr("opacity", .6).attr("stroke", "#777777").attr("stroke-width", .5).attr("filter", null).attr("fill-rule", "evenodd");
icons.selectAll("g").attr("opacity", null).attr("fill", "#ffffff").attr("stroke", "#3e3e4b").attr("filter", null).attr("mask", null);
landmass.attr("opacity", 1).attr("fill", "#eef6fb").attr("filter", null);
@@ -391,7 +391,7 @@ function focusOn() {
}
const b = +params.get("burg");
- if (b) {
+ if (b && pack.burgs[b]) {
x = pack.burgs[b].x;
y = pack.burgs[b].y;
}
@@ -1186,7 +1186,7 @@ function addMarkers() {
.attr("xlink:href", "#marker_volcano").attr("data-id", "#marker_volcano")
.attr("data-x", x).attr("data-y", y).attr("x", x - 15).attr("y", y - 30)
.attr("data-size", 1).attr("width", 30).attr("height", 30);
- const height = getFriendlyHeight(cells.h[cell]);
+ const height = getFriendlyHeight([x, y]);
const proper = Names.getCulture(cells.culture[cell]);
const name = Math.random() < .3 ? "Mount " + proper : Math.random() > .3 ? proper + " Volcano" : proper;
notes.push({id, name, legend:`Active volcano. Height: ${height}`});
diff --git a/modules/burgs-and-states.js b/modules/burgs-and-states.js
index 365e0c7d..f0478656 100644
--- a/modules/burgs-and-states.js
+++ b/modules/burgs-and-states.js
@@ -588,9 +588,9 @@
const valid = states.filter(s => s.i && !states.removed);
if (valid.length < 2) return;
- const neibs = {"Ally":1, "Sympathy":2, "Neutral":1, "Suspicion":10, "Rival":9}; // relations to neighbors
- const neibsOfNeibs = {"Ally":10, "Sympathy":8, "Neutral":5, "Suspicion":1}; // relations to neighbors of neighbors
- const far = {"Sympathy":1, "Neutral":12, "Suspicion":2, "Unknown":6}; // relations to other
+ const neibs = {"Ally":1, "Friendly":2, "Neutral":1, "Suspicion":10, "Rival":9}; // relations to neighbors
+ const neibsOfNeibs = {"Ally":10, "Friendly":8, "Neutral":5, "Suspicion":1}; // relations to neighbors of neighbors
+ const far = {"Friendly":1, "Neutral":12, "Suspicion":2, "Unknown":6}; // relations to other
const navals = {"Neutral":1, "Suspicion":2, "Rival":1, "Unknown":1}; // relations of naval powers
valid.forEach(s => s.diplomacy = new Array(states.length).fill("x")); // clear all relationships
diff --git a/modules/heightmap-generator.js b/modules/heightmap-generator.js
index 40c9e902..bdd04b35 100644
--- a/modules/heightmap-generator.js
+++ b/modules/heightmap-generator.js
@@ -14,17 +14,18 @@
const input = document.getElementById("templateInput");
if (!locked("template")) {
- const rnd = Math.random();
- if (rnd < .05) input.value = "Volcano"; else // 5%
- if (rnd < .25) input.value = "High Island"; else // 20%
- if (rnd < .35) input.value = "Low Island"; else // 10%
- if (rnd < .55) input.value = "Continents"; else // 20%
- if (rnd < .85) input.value = "Archipelago"; else // 30%
- if (rnd < .90) input.value = "Mediterranean"; else // 5%
- if (rnd < .95) input.value = "Peninsula"; else // 5%
- if (rnd < .97) input.value = "Pangea"; else // 2%
- if (rnd < .99) input.value = "Isthmus"; else // 2%
- input.value = "Atoll"; // 1%
+ const templates = {
+ "Volcano": 5,
+ "High Island": 22,
+ "Low Island": 10,
+ "Continents": 20,
+ "Archipelago": 32,
+ "Mediterranean":3,
+ "Peninsula": 3,
+ "Pangea": 2,
+ "Isthmus": 2,
+ "Atoll": 1};
+ input.value = rw(templates);
}
switch (input.value) {
diff --git a/modules/religions-generator.js b/modules/religions-generator.js
index 24c0327d..8b7d4def 100644
--- a/modules/religions-generator.js
+++ b/modules/religions-generator.js
@@ -35,7 +35,7 @@
Heresy:{"Heresy":1}
};
- const methods = {"Random + type":3, "Random + ism":1, "Supreme + ism":5, "Faith of + Supreme":3, "Place + ism":1, "Culture + ism":1, "Place + ian + type":6, "Culture + type":4};
+ const methods = {"Random + type":3, "Random + ism":1, "Supreme + ism":5, "Faith of + Supreme":3, "Place + ism":1, "Culture + ism":2, "Place + ian + type":6, "Culture + type":4};
const types = {
"Shamanism":{"Beliefs":3, "Shamanism":2, "Spirits":1},
@@ -65,7 +65,7 @@
const form = rw(forms.Folk);
const name = c.name + " " + rw(types[form]);
const deity = form === "Animism" ? null : getDeityName(c.i);
- const color = `url(#hatch${rand(8,13)})`;
+ const color = getRandomColor(); // `url(#hatch${rand(8,13)})`;
religions.push({i: c.i, name, color, culture: c.i, type:"Folk", form, deity});
});
@@ -85,18 +85,25 @@
const culture = cells.culture[center];
const deity = form === "Non-theism" ? null : getDeityName(culture);
- const [name, expansion] = getReligionName(form, deity, center);
+ let [name, expansion] = getReligionName(form, deity, center);
+ if (expansion === "state" && !state) expansion = "global";
+ if (expansion === "culture" && !culture) expansion = "global";
+
+ if (expansion === "state" && Math.random() > .5) center = states[state].center;
+ if (expansion === "culture" && Math.random() > .5) center = cultures[culture].center;
- if (expansion === "state" && state && Math.random() > .5) center = states[state].center;
- if (expansion === "culture" && culture && Math.random() > .5) center = cultures[culture].center;
if (!cells.burg[center] && cells.c[center].some(c => cells.burg[c])) center = cells.c[center].find(c => cells.burg[c]);
const x = cells.p[center][0], y = cells.p[center][1];
const s = spacing * gauss(1, .3, .2, 2, 2); // randomize to make the placement not uniform
if (religionsTree.find(x, y, s) !== undefined) continue; // to close to existing religion
+ // add "Old" to name of the folk religion on this culture
+ const folk = religions.find(r => expansion === "culture" && r.culture === culture && r.type === "Folk");
+ if (folk && folk.name.slice(0,3) !== "Old") folk.name = "Old " + folk.name;
+
const expansionism = rand(3, 8);
- const color = `url(#hatch${rand(0,5)})`;
+ const color = getRandomColor(); // `url(#hatch${rand(0,5)})`;
religions.push({i: religions.length, name, color, culture, type:"Organized", form, deity, expansion, expansionism, center});
religionsTree.add([x, y]);
//debug.append("circle").attr("cx", x).attr("cy", y).attr("r", 2).attr("fill", "blue");
@@ -116,7 +123,8 @@
const deity = getDeityName(culture);
const name = getCultName(form, center);
const expansionism = gauss(1.1, .5, 0, 5);
- religions.push({i: religions.length, name, color: "url(#hatch7)", culture, type:"Cult", form, deity, expansion:"global", expansionism, center});
+ const color = getRandomColor(); // "url(#hatch7)";
+ religions.push({i: religions.length, name, color, culture, type:"Cult", form, deity, expansion:"global", expansionism, center});
religionsTree.add([x, y]);
//debug.append("circle").attr("cx", x).attr("cy", y).attr("r", 2).attr("fill", "red");
}
@@ -137,7 +145,8 @@
const culture = cells.culture[center];
const name = getCultName("Heresy", center);
const expansionism = gauss(1.2, .5, 0, 5);
- religions.push({i: religions.length, name, color:"url(#hatch6)", culture, type:"Heresy", form:"Heresy", deity: r.deity, expansion:"global", expansionism, center});
+ const color = getRandomColor(); // "url(#hatch6)";
+ religions.push({i: religions.length, name, color, culture, type:"Heresy", form:"Heresy", deity: r.deity, expansion:"global", expansionism, center});
religionsTree.add([x, y]);
//debug.append("circle").attr("cx", x).attr("cy", y).attr("r", 2).attr("fill", "green");
}
@@ -175,34 +184,37 @@
religions.filter(r => r.type === "Organized" || r.type === "Cult").forEach(r => {
cells.religion[r.center] = r.i;
- queue.queue({e:r.center, p:0, r:r.i});
+ queue.queue({e:r.center, p:0, r:r.i, s: cells.state[r.center], c:r.culture});
cost[r.center] = 1;
});
const neutral = cells.i.length / 5000 * 200 * gauss(1, .3, .2, 2, 2) * neutralInput.value; // limit cost for organized religions growth
+ const popCost = d3.max(cells.pop) / 3; // enougth population to spered religion without penalty
while (queue.length) {
- const next = queue.dequeue(), n = next.e, p = next.p, r = next.r;
+ const next = queue.dequeue(), n = next.e, p = next.p, r = next.r, c = next.c, s = next.s;
const expansion = religions[r].expansion;
cells.c[n].forEach(function(e) {
- const cultureCost = expansion === "culture" ? religions[r].culture == cells.culture[e] ? 0 : 20000 : 10;
- const stateCost = expansion === "state" ? cells.state[religions[r].center] == cells.state[e] ? 0 : 20000 : 10;
- const biomeCost = cells.road[e] ? 0 : biomesData.cost[cells.biome[e]];
+ if (expansion === "culture" && c !== cells.culture[e]) return;
+ if (expansion === "state" && s !== cells.state[e]) return;
+
+ const cultureCost = c !== cells.culture[e] ? 10 : 0;
+ const stateCost = s !== cells.state[e] ? 10 : 0;
+ const biomeCost = cells.road[e] ? 1 : biomesData.cost[cells.biome[e]];
+ const populationCost = Math.max(rn(popCost - cells.pop[e]), 0);
const heightCost = Math.max(cells.h[e], 20) - 20;
const waterCost = cells.h[e] < 20 ? cells.road[e] ? 50 : 1000 : 0;
- const totalCost = p + (cultureCost + stateCost + biomeCost + heightCost + waterCost) / religions[r].expansionism;
-
+ const totalCost = p + (cultureCost + stateCost + biomeCost + populationCost + heightCost + waterCost) / religions[r].expansionism;
if (totalCost > neutral) return;
if (!cost[e] || totalCost < cost[e]) {
if (cells.h[e] >= 20 && cells.culture[e]) cells.religion[e] = r; // assign religion to cell
cost[e] = totalCost;
- queue.queue({e, p:totalCost, r});
+ queue.queue({e, p:totalCost, r, c, s});
}
});
}
- //debug.selectAll(".text").data(cost).enter().append("text").attr("x", (d, e) => cells.p[e][0]-1).attr("y", (d, e) => cells.p[e][1]-1).text(d => d ? rn(d) : "").attr("font-size", 2);
console.timeEnd("expandReligions");
}
@@ -288,7 +300,7 @@
if (m === "Random + ism") return [trimVowels(random()) + "ism", "global"];
if (m === "Supreme + ism" && deity) return [trimVowels(supreme()) + "ism", "global"];
if (m === "Faith of + Supreme" && deity) return ["Faith of " + supreme(), "global"];
- if (m === "Place + ism") return [place() + "ism", "global"];
+ if (m === "Place + ism") return [place() + "ism", "state"];
if (m === "Culture + ism") return [trimVowels(culture()) + "ism", "culture"];
if (m === "Place + ian + type") return [place("adj") + " " + type(), "state"];
if (m === "Culture + type") return [culture() + " " + type(), "culture"];
diff --git a/modules/save-and-load.js b/modules/save-and-load.js
index 7af3977e..37b37589 100644
--- a/modules/save-and-load.js
+++ b/modules/save-and-load.js
@@ -540,7 +540,7 @@ function parseLoadedData(data) {
if (version < 1) {
// 1.0 adds a new religions layer
- relig = viewbox.insert("g", "#terrain").attr("id", "cults");
+ relig = viewbox.insert("g", "#terrain").attr("id", "relig");
Religions.generate();
// 1.0 adds a legend box
@@ -601,6 +601,16 @@ function parseLoadedData(data) {
biomesData.color.push("#0b9131");
biomesData.habitability.push(12);
}
+
+ if (version == 1) {
+ // v 1.0 initial code had a bug with religion layer id
+ if (!relig.size()) relig = viewbox.insert("g", "#terrain").attr("id", "relig");
+
+ // v 1.0 initially has Sympathy status then relaced with Friendly
+ for (const s of pack.states) {
+ s.diplomacy = s.diplomacy.map(r => r === "Sympathy" ? "Friendly" : r);
+ }
+ }
}()
changeMapSize();
diff --git a/modules/ui/biomes-editor.js b/modules/ui/biomes-editor.js
index ac93b78e..45c77a77 100644
--- a/modules/ui/biomes-editor.js
+++ b/modules/ui/biomes-editor.js
@@ -114,7 +114,7 @@ function editBiomes() {
if (body.dataset.type === "percentage") {body.dataset.type = "absolute"; togglePercentageMode();}
applySorting(biomesHeader);
- $("#biomesEditor").dialog();
+ $("#biomesEditor").dialog({width: fitContent()});
}
function biomeHighlightOn(event) {
diff --git a/modules/ui/cultures-editor.js b/modules/ui/cultures-editor.js
index 8a0dfb74..4681b894 100644
--- a/modules/ui/cultures-editor.js
+++ b/modules/ui/cultures-editor.js
@@ -129,7 +129,7 @@ function editCultures() {
if (body.dataset.type === "percentage") {body.dataset.type = "absolute"; togglePercentageMode();}
applySorting(culturesHeader);
- $("#culturesEditor").dialog();
+ $("#culturesEditor").dialog({width: fitContent()});
}
function getTypeOptions(type) {
diff --git a/modules/ui/diplomacy-editor.js b/modules/ui/diplomacy-editor.js
index 2b42b9a8..947ca602 100644
--- a/modules/ui/diplomacy-editor.js
+++ b/modules/ui/diplomacy-editor.js
@@ -15,11 +15,11 @@ function editDiplomacy() {
if (layerIsOn("toggleReligions")) toggleReligions();
const body = document.getElementById("diplomacyBodySection");
- const statuses = ["Ally", "Sympathy", "Neutral", "Suspicion", "Enemy", "Unknown", "Rival", "Vassal", "Suzerain"];
+ const statuses = ["Ally", "Friendly", "Neutral", "Suspicion", "Enemy", "Unknown", "Rival", "Vassal", "Suzerain"];
const colors = ["#00b300", "#d4f8aa", "#edeee8", "#f3c7c4", "#e64b40", "#a9a9a9", "#ad5a1f", "#87CEFA", "#00008B"];
refreshDiplomacyEditor();
- tip("Click on a state to see its diplomatical relations", false, "warning");
+ tip("Click on a state to see its diplomatic relations", false, "warning");
viewbox.style("cursor", "crosshair").on("click", selectStateOnMapClick);
if (modules.editDiplomacy) return;
@@ -213,11 +213,7 @@ function editDiplomacy() {
message += `
| ${s.name} | ` + s.diplomacy.filter((v, i) => valid.includes(i)).map(r => `${r} | `).join("") + "
|---|
";
});
message += ``;
- console.log(alertMessage.innerHTML)
- console.log(message)
alertMessage.innerHTML = message;
- console.log(alertMessage.innerHTML)
-
$("#alert").dialog({title: "Relations matrix", width: fitContent(), position: {my: "center", at: "center", of: "svg"}, buttons: {}});
}
diff --git a/modules/ui/editors.js b/modules/ui/editors.js
index bf5037fb..4f544f4b 100644
--- a/modules/ui/editors.js
+++ b/modules/ui/editors.js
@@ -252,9 +252,13 @@ function clearLegend() {
// draw color (fill) picker
function createPicker() {
+ const pos = () => tip("Drag to change the picker position");
+ const cl = () => tip("Click to close the picker");
+ const closePicker = () => contaiter.style("display", "none");
+
const contaiter = d3.select("body").append("svg").attr("id", "pickerContainer").attr("width", "100%").attr("height", "100%");
- const curtain = contaiter.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%").attr("opacity", .2);
- curtain.on("click", () => contaiter.style("display", "none")).on("mousemove", () => tip("Click to close the picker"));
+ contaiter.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%").attr("opacity", .2)
+ .on("mousemove", cl).on("click", closePicker);
const picker = contaiter.append("g").attr("id", "picker").call(d3.drag().on("start", dragPicker));
const controls = picker.append("g").attr("id", "pickerControls");
@@ -279,6 +283,24 @@ function createPicker() {
controls.selectAll("line").on("click", clickPickerControl);
controls.selectAll("circle").call(d3.drag().on("start", dragPickerControl));
+ const spaces = picker.append("foreignObject").attr("id", "pickerSpaces")
+ .attr("x", 4).attr("y", 20).attr("width", 303).attr("height", 20)
+ .on("mousemove", () => tip("Color value in different color spaces. Edit to change"));
+ const html = `
+
+
+
`;
+ spaces.node().insertAdjacentHTML('beforeend', html);
+ spaces.selectAll("input").on("change", changePickerSpace);
+
const colors = picker.append("g").attr("id", "pickerColors").attr("stroke", "#333333");
const hatches = picker.append("g").attr("id", "pickerHatches").attr("stroke", "#333333");
const hatching = d3.selectAll("g#hatching > pattern");
@@ -287,12 +309,12 @@ function createPicker() {
const clr = d3.range(number).map(i => d3.hsl(i/number*360, .7, .7).hex());
clr.forEach(function(d, i) {
colors.append("rect").attr("id", "picker_" + d).attr("fill", d).attr("class", i?"":"selected")
- .attr("x", i*22+4).attr("y", 20).attr("width", 16).attr("height", 16);
+ .attr("x", i*22+4).attr("y", 40).attr("width", 16).attr("height", 16);
});
hatching.each(function(d, i) {
hatches.append("rect").attr("id", "picker_" + this.id).attr("fill", "url(#" + this.id + ")")
- .attr("x", i*22+4).attr("y", 41).attr("width", 16).attr("height", 16);
+ .attr("x", i*22+4).attr("y", 61).attr("width", 16).attr("height", 16);
});
colors.selectAll("rect").on("click", pickerFillClicked).on("mousemove", () => tip("Click to fill with the color"));
@@ -302,8 +324,10 @@ function createPicker() {
const bbox = picker.node().getBBox();
const width = bbox.width + 8;
const height = bbox.height + 9;
- const pos = () => tip("Drag to change the picker position");
- picker.insert("rect", ":first-child").attr("x", 0).attr("y", 0).attr("width", width).attr("height", height).attr("fill", "#ffffff").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", -11).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("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})`);
@@ -314,6 +338,25 @@ function updateSelectedRect(fill) {
document.getElementById("picker").querySelector("rect[fill='"+fill+"']").classList.add("selected");
}
+function updateSpaces() {
+ // hsl
+ const h = getPickerControl(pickerH, 360);
+ const s = getPickerControl(pickerS, 1);
+ const l = getPickerControl(pickerL, 1);
+ pickerHSL_H.value = rn(h);
+ pickerHSL_S.value = rn(s * 100); // multiplied by 100
+ pickerHSL_L.value = rn(l * 100); // multiplied by 100
+
+ // rgb
+ const rgb = d3.color(d3.hsl(h, s, l));
+ pickerRGB_R.value = rgb.r;
+ pickerRGB_G.value = rgb.g;
+ pickerRGB_B.value = rgb.b;
+
+ // hex
+ pickerHEX.value = rgb.hex();
+}
+
function updatePickerColors() {
const colors = d3.select("#picker > #pickerColors").selectAll("rect");
const number = colors.size();
@@ -339,6 +382,7 @@ function openPicker(fill, callback) {
if (!isNaN(hsl.h)) setPickerControl(pickerH, hsl.h, 360);
if (!isNaN(hsl.s)) setPickerControl(pickerS, hsl.s, 1);
if (!isNaN(hsl.l)) setPickerControl(pickerL, hsl.l, 1);
+ updateSpaces();
updatePickerColors();
}
@@ -380,13 +424,20 @@ function dragPicker() {
}
function pickerFillClicked() {
- updateSelectedRect(this.getAttribute("fill"));
+ const fill = this.getAttribute("fill");
+ updateSelectedRect(fill);
openPicker.updateFill();
+
+ const hsl = d3.hsl(fill);
+ if (isNaN(hsl.h)) return; // not a color
+ setPickerControl(pickerH, hsl.h, 360);
+ updateSpaces();
}
function clickPickerControl() {
const min = this.getScreenCTM().e;
this.nextSibling.setAttribute("cx", d3.event.x - min);
+ updateSpaces();
updatePickerColors();
openPicker.updateFill();
}
@@ -398,11 +449,33 @@ function dragPickerControl() {
d3.event.on("drag", function() {
const x = Math.max(Math.min(d3.event.x, max), min);
this.setAttribute("cx", x);
+ updateSpaces();
updatePickerColors();
openPicker.updateFill();
});
}
+function changePickerSpace() {
+ const valid = this.checkValidity();
+ if (!valid) {tip("You must provide a correct value", false, "error"); return;}
+
+ const space = this.dataset.space;
+ const i = Array.from(this.parentNode.querySelectorAll("input")).map(input => input.value); // inputs
+ const fill = space === "hex" ? d3.rgb(this.value)
+ : space === "rgb" ? d3.rgb(i[0], i[1], i[2])
+ : d3.hsl(i[0], i[1]/100, i[2]/100);
+
+ const hsl = d3.hsl(fill);
+ if (isNaN(hsl.l)) {tip("You must provide a correct value", false, "error"); return;}
+ if (!isNaN(hsl.h)) setPickerControl(pickerH, hsl.h, 360);
+ if (!isNaN(hsl.s)) setPickerControl(pickerS, hsl.s, 1);
+ if (!isNaN(hsl.l)) setPickerControl(pickerL, hsl.l, 1);
+
+ updateSpaces();
+ updatePickerColors();
+ openPicker.updateFill();
+}
+
// remove all fogging
function unfog() {
defs.select("#fog").selectAll("path").remove();
diff --git a/modules/ui/general.js b/modules/ui/general.js
index a3971675..77ce6fe4 100644
--- a/modules/ui/general.js
+++ b/modules/ui/general.js
@@ -48,7 +48,7 @@ function moved() {
if (i === undefined) return;
showNotes(d3.event, i);
const g = findGridCell(point[0], point[1]); // grid cell id
- if (tooltip.dataset.main) showMainTip(); else showMapTooltip(d3.event, i, g);
+ if (tooltip.dataset.main) showMainTip(); else showMapTooltip(point, d3.event, i, g);
if (toolsContent.style.display === "block" && cellInfo.style.display === "block") updateCellInfo(point, i, g);
}
@@ -71,7 +71,7 @@ function showNotes(e, i) {
}
// show viewbox tooltip if main tooltip is blank
-function showMapTooltip(e, i, g) {
+function showMapTooltip(point, e, i, g) {
tip(""); // clear tip
const tag = e.target.tagName;
const path = e.composedPath ? e.composedPath() : getComposedPath(e.target); // apply polyfill
@@ -116,7 +116,7 @@ function showMapTooltip(e, i, g) {
tip(prov + state);
} else
if (layerIsOn("toggleCultures") && pack.cells.culture[i]) tip("Culture: " + pack.cultures[pack.cells.culture[i]].name); else
- if (layerIsOn("toggleHeight")) tip("Height: " + getFriendlyHeight(pack.cells.h[i]));
+ if (layerIsOn("toggleHeight")) tip("Height: " + getFriendlyHeight(point));
}
// get cell info on mouse move
@@ -127,7 +127,8 @@ function updateCellInfo(point, i, g) {
infoCell.innerHTML = i;
const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value;
infoArea.innerHTML = cells.area[i] ? si(cells.area[i] * distanceScaleInput.value ** 2) + unit : "n/a";
- infoHeight.innerHTML = getFriendlyHeight(cells.h[i]) + " (" + cells.h[i] + ")";
+ const h = pack.cells.h[i] < 20 ? grid.cells.h[pack.cells.g[i]] : pack.cells.h[i];
+ infoHeight.innerHTML = getFriendlyHeight(point) + " (" + h + ")";
infoTemp.innerHTML = convertTemperature(grid.cells.temp[g]);
infoPrec.innerHTML = cells.h[i] >= 20 ? getFriendlyPrecipitation(i) : "n/a";
infoState.innerHTML = cells.h[i] >= 20 ? cells.state[i] ? `${pack.states[cells.state[i]].fullName} (${cells.state[i]})` : "neutral lands (0)" : "no";
@@ -142,12 +143,16 @@ function updateCellInfo(point, i, g) {
}
// get user-friendly (real-world) height value from map data
-function getFriendlyHeight(h) {
+function getFriendlyHeight(p) {
const unit = heightUnit.value;
let unitRatio = 3.281; // default calculations are in feet
if (unit === "m") unitRatio = 1; // if meter
else if (unit === "f") unitRatio = 0.5468; // if fathom
+ const packH = pack.cells.h[findCell(p[0], p[1])];
+ const gridH = grid.cells.h[findGridCell(p[0], p[1])];
+ const h = packH < 20 ? gridH : packH;
+
let height = -990;
if (h >= 20) height = Math.pow(h - 18, +heightExponentInput.value);
else if (h < 20 && h > 0) height = (h - 20) / h * 50;
diff --git a/modules/ui/heightmap-editor.js b/modules/ui/heightmap-editor.js
index c8f3035a..1a914e8c 100644
--- a/modules/ui/heightmap-editor.js
+++ b/modules/ui/heightmap-editor.js
@@ -82,9 +82,8 @@ function editHeightmap() {
heightmapInfoX.innerHTML = rn(p[0]);
heightmapInfoY.innerHTML = rn(p[1]);
heightmapInfoCell.innerHTML = cell;
- heightmapInfoHeight.innerHTML = grid.cells.h[cell];
-
- tip("Height: " + getFriendlyHeight(grid.cells.h[cell]));
+ heightmapInfoHeight.innerHTML = `${grid.cells.h[cell]} (${getHeight(grid.cells.h[cell])})`;
+ if (tooltip.dataset.main) showMainTip();
// move radius circle if drag mode is active
const pressed = document.querySelector("#brushesButtons > button.pressed");
@@ -92,6 +91,20 @@ function editHeightmap() {
moveCircle(p[0], p[1], brushRadius.valueAsNumber, "#333");
}
+// get user-friendly (real-world) height value from map data
+function getHeight(h) {
+ const unit = heightUnit.value;
+ let unitRatio = 3.281; // default calculations are in feet
+ if (unit === "m") unitRatio = 1; // if meter
+ else if (unit === "f") unitRatio = 0.5468; // if fathom
+
+ let height = -990;
+ if (h >= 20) height = Math.pow(h - 18, +heightExponentInput.value);
+ else if (h < 20 && h > 0) height = (h - 20) / h * 50;
+
+ return rn(height * unitRatio) + " " + unit;
+}
+
// Exit customization mode
function finalizeHeightmap() {
if (terrs.selectAll("*").size() < 200) {
@@ -218,6 +231,14 @@ function editHeightmap() {
if (grid.cells.h[i] < 20) grid.cells.h[i] = 20;
}
+ // save culture centers x and y to restore center cell id after re-graph
+ for (const c of pack.cultures) {
+ if (!c.i || c.removed) continue;
+ const p = pack.cells.p[c.center];
+ c.x = p[0];
+ c.y = p[1];
+ }
+
markFeatures();
OceanLayers();
calculateTemperatures();
@@ -299,6 +320,11 @@ function editHeightmap() {
else {p.center = provCells[0]; p.burg = pack.cells.burg[p.center];}
}
+ for (const c of pack.cultures) {
+ if (!c.i || c.removed) continue;
+ c.center = findCell(c.x, c.y);
+ }
+
BurgsAndStates.drawStateLabels();
drawStates();
drawBorders();
@@ -883,7 +909,12 @@ function editHeightmap() {
setOverlayOpacity(0);
document.getElementById("convertImageLoad").classList.add("glow"); // add glow effect
- tip('Image Converter is opened. Upload the image and assign the colors to desired heights', true); // main tip
+ tip('Image Converter is opened. Upload the image and assign the colors to desired heights', true, "warn"); // main tip
+
+ // remove all heights
+ grid.cells.h = new Uint8Array(grid.cells.i.length);
+ terrs.selectAll("*").remove();
+ updateHistory();
if (modules.openImageConverter) return;
modules.openImageConverter = true;
@@ -944,8 +975,8 @@ function editHeightmap() {
colorsUnassigned.style.display = "block";
colorsAssigned.style.display = "none";
- const gridColors = grid.points.map(function(p) {
- const x = Math.floor(p[0]), y = Math.floor(p[1]);
+ const gridColors = grid.points.map(p => {
+ const x = Math.floor(p[0]-.01), y = Math.floor(p[1]-.01);
const i = (x + y * graphWidth) * 4;
const r = data[i], g = data[i+1], b = data[i+2];
return [r, g, b];
@@ -961,7 +992,7 @@ function editHeightmap() {
return clr;
}).on("click", mapClicked);
- const unassigned = [...usedColors].sort((a, b) => d3.lab(a).b - d3.lab(b).b);
+ const unassigned = [...usedColors].sort((a, b) => d3.lab(a).l - d3.lab(b).l);
const unassignedContainer = d3.select("#colorsUnassigned");
unassignedContainer.selectAll("div").data(unassigned).enter().append("div")
.attr("data-color", i => i).style("background-color", i => i)
@@ -1002,7 +1033,7 @@ function editHeightmap() {
const height = +this.getAttribute("data-color");
const rgb = color(1 - height/100);
- const selectedColor = imageConverter.querySelector("div.selectedColor");
+ const selectedColor = imageConverter.querySelector("div.selectedColor");
selectedColor.style.backgroundColor = rgb;
selectedColor.setAttribute("data-color", rgb);
selectedColor.setAttribute("data-height", height);
@@ -1027,19 +1058,19 @@ function editHeightmap() {
unassigned.forEach(function(el) {
const colorFrom = el.getAttribute("data-color");
const lab = d3.lab(colorFrom);
- const normalized = type === "hue" ? rn(normalize(lab.b + lab.a / 2, -50, 200), 2) : rn(normalize(lab.l, 0, 100), 2);
+ const normalized = type === "hue" ? rn(normalize(lab.b + lab.a / 2, -50, 200), 2) : rn(normalize(lab.l, -15, 100), 2);
const colorTo = color(1 - normalized);
const heightTo = normalized * 100;
-
+
terrs.selectAll("polygon[fill='" + colorFrom + "']").attr("fill", colorTo).attr("data-height", heightTo);
el.style.backgroundColor = colorTo;
el.setAttribute("data-color", colorTo);
el.setAttribute("data-height", heightTo);
- colorsAssigned.appendChild(el);
+ colorsAssigned.appendChild(el);
});
colorsAssigned.style.display = "block";
- colorsUnassigned.style.display = "none";
+ colorsUnassigned.style.display = "none";
}
function changeConvertColorsNumber(change) {
diff --git a/modules/ui/markers-editor.js b/modules/ui/markers-editor.js
index 853c280d..b1a28dd1 100644
--- a/modules/ui/markers-editor.js
+++ b/modules/ui/markers-editor.js
@@ -183,33 +183,49 @@ function editMarker() {
["1F4B0", "💰", "Money bag"],
["1F6A8", "🚨", "Revolving Light"],
["1F309", "🌉", "Bridge at Night"],
- ["1F4A8", "💨", "Dashing away"],
- ["1F334", "🌴", "Palm"],
- ["1F335", "🌵", "Cactus"],
- ["1F33E", "🌾", "Sheaf"],
["1F5FB", "🗻", "Mountain"],
["1F30B", "🌋", "Volcano"],
+ ["270A", "✊", "Raised Fist"],
+ ["1F44A", "👊", "Oncoming Fist"],
+ ["1F4AA", "💪", "Flexed Biceps"],
+ ["1F47C", "👼", "Baby Angel"],
["1F40E", "🐎", "Horse"],
["1F434", "🐴", "Horse Face"],
["1F42E", "🐮", "Cow"],
["1F43A", "🐺", "Wolf Face"],
["1F435", "🐵", "Monkey face"],
["1F437", "🐷", "Pig face"],
- ["1F414", "🐔", "Chiken"],
- ["1F411", "🐑", "Eve"],
+ ["1F414", "🐔", "Chicken"],
+ ["1F411", "🐑", "Ewe"],
["1F42B", "🐫", "Camel"],
["1F418", "🐘", "Elephant"],
["1F422", "🐢", "Turtle"],
["1F40C", "🐌", "Snail"],
["1F40D", "🐍", "Snake"],
+ ["1F41D", "🐝", "Honeybee"],
+ ["1F41C", "🐜", "Ant"],
+ ["1F41B", "🐛", "Bug"],
+ ["1F426", "🐦", "Bird"],
+ ["1F438", "🐸", "Frog Face"],
["1F433", "🐳", "Whale"],
["1F42C", "🐬", "Dolphin"],
["1F420", "🐟", "Fish"],
+ ["1F480", "💀", "Skull"],
["1F432", "🐲", "Dragon Head"],
["1F479", "👹", "Ogre"],
+ ["1F47A", "👺", "Goblin"],
["1F47B", "👻", "Ghost"],
["1F47E", "👾", "Alien"],
- ["1F480", "💀", "Skull"],
+ ["1F383", "🎃", "Jack-O-Lantern"],
+ ["1F384", "🎄", "Christmas Tree"],
+ ["1F334", "🌴", "Palm"],
+ ["1F335", "🌵", "Cactus"],
+ ["2618", "☘️", "Shamrock"],
+ ["1F340", "🍀", "Four Leaf Clover"],
+ ["1F341", "🍁", "Maple Leaf"],
+ ["1F33F", "🌿", "Herb"],
+ ["1F33E", "🌾", "Sheaf"],
+ ["1F344", "🍄", "Mushroom"],
["1F374", "🍴", "Fork and knife"],
["1F372", "🍲", "Food"],
["1F35E", "🍞", "Bread"],
@@ -223,24 +239,37 @@ function editMarker() {
["1F377", "🍷", "Wine glass"],
["1F3BB", "🎻", "Violin"],
["1F3B8", "🎸", "Guitar"],
+ ["1F52A", "🔪", "Knife"],
+ ["1F52B", "🔫", "Pistol"],
+ ["1F4A3", "💣", "Bomb"],
+ ["1F4A5", "💥", "Collision"],
+ ["1F4A8", "💨", "Dashing away"],
+ ["1F301", "🌁", "Foggy"],
+ ["2744", "❄️", "Snowflake"],
["26A1", "⚡", "Electricity"],
["1F320", "🌠", "Shooting star"],
["1F319", "🌙", "Crescent moon"],
["1F525", "🔥", "Fire"],
["1F4A7", "💧", "Droplet"],
["1F30A", "🌊", "Wave"],
+ ["23F0", "⏰", "Alarm Clock"],
["231B", "⌛", "Hourglass"],
["1F3C6", "🏆", "Goblet"],
["26F2", "⛲", "Fountain"],
["26F5", "⛵", "Sailboat"],
- ["26FA", "⛺", "Tend"],
+ ["26FA", "⛺", "Campfire"],
+ ["2764", "❤", "Red Heart"],
+ ["1F498", "💘", "Heart With Arrow"],
["1F489", "💉", "Syringe"],
+ ["1F4D5", "📕", "Closed Book"],
["1F4D6", "📚", "Books"],
+ ["1F381", "🎁", "Wrapped Gift"],
["1F3AF", "🎯", "Archery"],
["1F52E", "🔮", "Magic ball"],
["1F3AD", "🎭", "Performing arts"],
["1F3A8", "🎨", "Artist palette"],
["1F457", "👗", "Dress"],
+ ["1F392", "🎒", "Backpack"],
["1F451", "👑", "Crown"],
["1F48D", "💍", "Ring"],
["1F48E", "💎", "Gem"],
@@ -259,7 +288,6 @@ function editMarker() {
["21F6", "⇶", "Three arrows"],
["2699", "⚙", "Gear"],
["269B", "⚛", "Atom"],
- ["0024", "$", "Dollar"],
["2680", "⚀", "Die1"],
["2681", "⚁", "Die2"],
["2682", "⚂", "Die3"],
diff --git a/modules/ui/options.js b/modules/ui/options.js
index 6f6830df..d8296a31 100644
--- a/modules/ui/options.js
+++ b/modules/ui/options.js
@@ -873,13 +873,13 @@ function randomizeOptions() {
if (!locked("regions")) regionsInput.value = regionsOutput.value = gauss(15, 3, 2, 30);
if (!locked("provinces")) provincesInput.value = provincesOutput.value = gauss(40, 20, 20, 100);
if (!locked("manors")) {manorsInput.value = 1000; manorsOutput.value = "auto";}
- if (!locked("religions")) religionsInput.value = religionsOutput.value = gauss(6, 2, 3, 20);
+ if (!locked("religions")) religionsInput.value = religionsOutput.value = gauss(5, 2, 2, 10);
if (!locked("power")) powerInput.value = powerOutput.value = gauss(3, 2, 0, 10);
if (!locked("neutral")) neutralInput.value = neutralOutput.value = rn(1 + Math.random(), 1);
if (!locked("cultures")) culturesInput.value = culturesOutput.value = gauss(12, 3, 5, 30);
// 'Configure World' settings
- if (!locked("prec")) precInput.value = precOutput.value = gauss(100, 40, 0, 500);
+ if (!locked("prec")) precInput.value = precOutput.value = gauss(100, 20, 5, 500);
const tMax = +temperatureEquatorOutput.max, tMin = +temperatureEquatorOutput.min; // temperature extremes
if (!locked("temperatureEquator")) temperatureEquatorOutput.value = temperatureEquatorInput.value = rand(tMax-6, tMax);
if (!locked("temperaturePole")) temperaturePoleOutput.value = temperaturePoleInput.value = rand(tMin, tMin+10);
diff --git a/modules/ui/religions-editor.js b/modules/ui/religions-editor.js
index 709ab165..79b7c9d2 100644
--- a/modules/ui/religions-editor.js
+++ b/modules/ui/religions-editor.js
@@ -25,6 +25,7 @@ function editReligions() {
document.getElementById("religionsEditorRefresh").addEventListener("click", refreshReligionsEditor);
document.getElementById("religionsLegend").addEventListener("click", toggleLegend);
document.getElementById("religionsPercentage").addEventListener("click", togglePercentageMode);
+ document.getElementById("religionsExtinct").addEventListener("click", toggleExtinct);
document.getElementById("religionsManually").addEventListener("click", enterReligionsManualAssignent);
document.getElementById("religionsManuallyApply").addEventListener("click", applyReligionsManualAssignent);
document.getElementById("religionsManuallyCancel").addEventListener("click", () => exitReligionsManualAssignment());
@@ -56,11 +57,13 @@ function editReligions() {
for (const r of pack.religions) {
if (r.removed) continue;
+
const area = r.area * (distanceScaleInput.value ** 2);
const rural = r.rural * populationRate.value;
const urban = r.urban * populationRate.value * urbanization.value;
const population = rn(rural + urban);
- const populationTip = `Total population: ${si(population)}; Rural population: ${si(rural)}; Urban population: ${si(urban)}`;
+ if (r.i && !population && body.dataset.extinct !== "show") continue; // hide extinct religions
+ const populationTip = `Believers: ${si(population)}; Rural areas: ${si(rural)}; Urban areas: ${si(urban)}`;
totalArea += area;
totalPopulation += population;
@@ -98,7 +101,11 @@ function editReligions() {
body.innerHTML = lines;
// update footer
- religionsFooterNumber.innerHTML = pack.religions.filter(r => r.i && !r.removed).length;
+ const valid = pack.religions.filter(r => r.i && !r.removed);
+ religionsOrganized.innerHTML = valid.filter(r => r.type === "Organized").length;
+ religionsHeresies.innerHTML = valid.filter(r => r.type === "Heresy").length;
+ religionsCults.innerHTML = valid.filter(r => r.type === "Cult").length;
+ religionsFolk.innerHTML = valid.filter(r => r.type === "Folk").length;
religionsFooterArea.innerHTML = si(totalArea) + unit;
religionsFooterPopulation.innerHTML = si(totalPopulation);
religionsFooterArea.dataset.area = totalArea;
@@ -118,7 +125,7 @@ function editReligions() {
if (body.dataset.type === "percentage") {body.dataset.type = "absolute"; togglePercentageMode();}
applySorting(religionsHeader);
- $("#religionsEditor").dialog();
+ $("#religionsEditor").dialog({width: fitContent()});
}
function getTypeOptions(type) {
@@ -204,7 +211,7 @@ function editReligions() {
}
function drawReligionCenters() {
- const tooltip = "Drag to move the religion center";
+ const tooltip = "Drag to move the religion center (it won't affect religions distribution)";
debug.select("#religionCenters").remove();
const religionCenters = debug.append("g").attr("id", "religionCenters")
.attr("stroke-width", 2).attr("stroke", "#444444").style("cursor", "move");
@@ -252,6 +259,11 @@ function editReligions() {
}
}
+ function toggleExtinct() {
+ body.dataset.extinct = body.dataset.extinct !== "show" ? "show" : "hide";
+ religionsEditorAddLines();
+ }
+
function enterReligionsManualAssignent() {
if (!layerIsOn("toggleReligions")) toggleReligions();
customization = 7;
@@ -398,8 +410,8 @@ function editReligions() {
function downloadReligionsData() {
const unit = areaUnit.value === "square" ? distanceUnitInput.value + "2" : areaUnit.value;
- let data = "Id,Religion,Color,Type,Form,Deity,Area "+unit+",Population\n"; // headers
-
+ let data = "Id,Religion,Color,Type,Form,Deity,Area "+unit+",Believers\n"; // headers
+
body.querySelectorAll(":scope > div").forEach(function(el) {
data += el.dataset.id + ",";
data += el.dataset.name + ",";
diff --git a/modules/ui/states-editor.js b/modules/ui/states-editor.js
index 624716d9..288b6e76 100644
--- a/modules/ui/states-editor.js
+++ b/modules/ui/states-editor.js
@@ -151,7 +151,7 @@ function editStates() {
if (body.dataset.type === "percentage") {body.dataset.type = "absolute"; togglePercentageMode();}
applySorting(statesHeader);
- $("#statesEditor").dialog();
+ $("#statesEditor").dialog({width: fitContent()});
}
function getCultureOptions(culture) {
diff --git a/modules/ui/zones-editor.js b/modules/ui/zones-editor.js
index 97a396f5..0a2ea3ac 100644
--- a/modules/ui/zones-editor.js
+++ b/modules/ui/zones-editor.js
@@ -86,7 +86,7 @@ function editZones() {
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseleave", ev => zoneHighlightOff(ev)));
if (body.dataset.type === "percentage") {body.dataset.type = "absolute"; togglePercentageMode();}
- $("#zonesEditor").dialog();
+ $("#zonesEditor").dialog({width: fitContent()});
}
function zoneHighlightOn(event) {
diff --git a/modules/utils.js b/modules/utils.js
index 55f8efe7..ea6ed102 100644
--- a/modules/utils.js
+++ b/modules/utils.js
@@ -24,13 +24,14 @@ function getBoundaryPoints(width, height, spacing) {
// get points on a regular square grid and jitter them a bit
function getJitteredGrid(width, height, spacing) {
const radius = spacing / 2; // square radius
- const jittering = radius * 0.9; // max deviation
- const jitter = function() {return Math.random() * 2 * jittering - jittering;};
+ const jittering = radius * .9; // max deviation
+ const jitter = () => Math.random() * 2 * jittering - jittering;
+
let points = [];
for (let y = radius; y < height; y += spacing) {
for (let x = radius; x < width; x += spacing) {
- let xj = rn(x + jitter(), 2);
- let yj = rn(y + jitter(), 2);
+ const xj = Math.min(rn(x + jitter(), 2), width);
+ const yj = Math.min(rn(y + jitter(), 2), height);
points.push([xj, yj]);
}
}