v1.5.05 - emblems regeneration

This commit is contained in:
Azgaar 2021-02-02 23:47:45 +03:00
parent 24d2efe8b6
commit 925e7accbd
8 changed files with 222 additions and 153 deletions

View file

@ -91,7 +91,7 @@
const nomadic = [1, 2, 3, 4].includes(cells.biome[b.cell]);
const type = nomadic ? "Nomadic" : cultures[b.culture].type === "Nomadic" ? "Generic" : cultures[b.culture].type;
const coa = COA.generate(null);
coa.shield = getShield(b.culture, null);
coa.shield = COA.getShield(b.culture, null);
states.push({i, color: colors[i-1], name, expansionism, capital: i, type, center: b.cell, culture: b.culture, coa});
cells.burg[b.cell] = i;
});
@ -139,14 +139,6 @@
}
}
function getShield(culture, state) {
const emblemShape = document.getElementById("emblemShape").value;
if (emblemShape === "state" && state && pack.states[state].coa) return pack.states[state].coa.shield;
if (pack.cultures[culture].shield) return pack.cultures[culture].shield;
console.error("Emblem shape is not defined on culture level", pack.cultures[culture]);
return "heater";
}
// define burg coordinates, coa, port status and define details
const specifyBurgs = function() {
TIME && console.time("specifyBurgs");
@ -194,7 +186,7 @@
else if (b.port) kinship -= .1;
if (b.culture !== state.culture) kinship -= .25;
b.coa = COA.generate(stateCOA, kinship);
b.coa.shield = getShield(b.culture, b.state);
b.coa.shield = COA.getShield(b.culture, b.state);
}
// de-assign port status if it's the only one on feature
@ -592,6 +584,7 @@
TIME && console.time("collectStatistics");
const cells = pack.cells, states = pack.states;
states.forEach(s => {
if (s.removed) return;
s.cells = s.area = s.burgs = s.rural = s.urban = 0;
s.neighbors = new Set();
});
@ -614,7 +607,10 @@
}
// convert neighbors Set object into array
states.forEach(s => s.neighbors = Array.from(s.neighbors));
states.forEach(s => {
if (!s.neighbors) return;
s.neighbors = Array.from(s.neighbors);
});
TIME && console.timeEnd("collectStatistics");
}
@ -957,7 +953,7 @@
const color = getMixedColor(s.color);
const kinship = nameByBurg ? .8 : .4;
const coa = COA.generate(stateBurgs[i].coa, kinship);
coa.shield = getShield(c, s.i);
coa.shield = COA.getShield(c, s.i);
provinces.push({i:province, state:s.i, center, burg, name, formName, fullName, color, coa});
}
});
@ -1055,7 +1051,7 @@
const dominion = colony ? P(.95) : singleIsle || isleGroup ? P(.7) : P(.3);
const kinship = dominion ? 0 : .4;
const coa = COA.generate(s.coa, kinship, dominion);
coa.shield = getShield(c, s.i);
coa.shield = COA.getShield(c, s.i);
provinces.push({i:province, state:s.i, center, burg, name, formName, fullName, color, coa});
s.provinces.push(province);

View file

@ -196,18 +196,14 @@
// TODO
// seafaring
// stringify coa on save and load
// regenerateAll
// generate on new item creation
// old versions auto migration: coa generation for cultures and states etc.
// emblems layer for old maps
// define emblems layer style for all styles
// add coa on click events for loaded map
// generatate state/prov/burg - remove all rendered coas
// remove state/prov/burg - remove rendered coa [burg - done, provice - done, state]
// style settings for emblems layer
// fix download svg/png
// fix map download svg/png
// test in FF
// generate all?
// layout preset
// burg editor - add emblem
// other editors
@ -340,11 +336,13 @@
}
// dominions have canton with parent coa
if (P(dominion)) {
const t = getType(parent.t1) === getType(coa.t1) ? getTincture("division", usedTinctures, coa.t1) : parent.t1;
if (P(dominion) && parent.charges) {
const invert = isSameType(parent.t1, coa.t1);
const t = invert ? getTincture("division", usedTinctures, coa.t1) : parent.t1;
const canton = {ordinary: "canton", t};
if (coa.charges) {
coa.charges.forEach((charge, i) => {
if (charge.size === 1.5) charge.size = 1.4;
if (charge.p.includes("a")) charge.p = charge.p.replaceAll("a", "");
if (charge.p.includes("j")) charge.p = charge.p.replaceAll("j", "");
if (charge.p.includes("y")) charge.p = charge.p.replaceAll("y", "");
@ -352,17 +350,20 @@
});
}
if (parent.charges) {
let charge = parent.charges[0].charge;
if (charge === "inescutcheon" && parent.charges[1]) charge = parent.charges[1].charge;
let charge = parent.charges[0].charge;
if (charge === "inescutcheon" && parent.charges[1]) charge = parent.charges[1].charge;
let t2 = invert ? parent.t1 : parent.charges[0].t;
if (isSameType(t, t2)) t2 = getTincture("charge", usedTinctures, t);
if (!coa.charges) coa.charges = [];
coa.charges.push({charge, t: t2, p: "y", size: 0.5});
let t2 = parent.charges[0].t;
if (getType(t) === getType(t2)) t2 = getTincture("charge", usedTinctures, t);
if (!coa.charges) coa.charges = [];
coa.charges.push({charge, t: t2, p: "y", size: 0.5});
} else canton.above = 1;
coa.ordinaries ? coa.ordinaries.push(canton) : coa.ordinaries = [canton];
console.log(encodeURI(`https://azgaar.github.io/Armoria/?coa=${JSON.stringify(coa)}`));
console.log(encodeURI(`https://azgaar.github.io/Armoria/?coa=${JSON.stringify(parent)}`));
console.log("-------");
}
function selectCharge(set) {
@ -398,6 +399,18 @@
if (Object.keys(tinctures.stains).includes(tincture)) return "stains";
}
function isSameType(t1, t2) {
return type(t1) === type(t2);
function type(tincture) {
if (Object.keys(tinctures.metals).includes(tincture)) return "metals";
if (Object.keys(tinctures.colours).includes(tincture)) return "colours";
if (Object.keys(tinctures.stains).includes(tincture)) return "stains";
else return "pattern";
}
}
function definePattern(pattern, element, size = "") {
let t1 = null, t2 = null;
if (P(.15)) size = "-small";
@ -465,19 +478,17 @@
return coa;
}
const generateAll = function() {
const states = pack.states, burgs = pack.burgs, provinces = pack.provinces;
states.forEach(state => {
if (!state.i || state.removed) return;
const coa = generate();
s.coa = coa;
});
const getShield = function(culture, state) {
const emblemShape = document.getElementById("emblemShape").value;
if (emblemShape === "state" && state && pack.states[state].coa) return pack.states[state].coa.shield;
if (pack.cultures[culture].shield) return pack.cultures[culture].shield;
console.error("Emblem shape is not defined on culture level", pack.cultures[culture]);
return "heater";
}
const toString = coa => JSON.stringify(coa).replaceAll("#", "%23");
const copy = coa => JSON.parse(JSON.stringify(coa));
return {generate, generateAll, toString, copy};
return {generate, toString, copy, getShield};
})));

View file

@ -3,7 +3,7 @@ function editEmblem(type, id, el) {
if (customization) return;
if (!id && d3.event) defineEmblemData(d3.event);
emblems.selectAll(":scope > use").call(d3.drag().on("drag", dragEmblem)).classed("draggable", true);
emblems.selectAll("use").call(d3.drag().on("drag", dragEmblem)).classed("draggable", true);
const emblemStates = document.getElementById("emblemStates");
const emblemProvinces = document.getElementById("emblemProvinces");
@ -337,6 +337,6 @@ function editEmblem(type, id, el) {
}
function closeEmblemEditor() {
emblems.selectAll(":scope > use").call(d3.drag().on("drag", null)).attr("class", null);
emblems.selectAll("use").call(d3.drag().on("drag", null)).attr("class", null);
}
}

View file

@ -1297,32 +1297,23 @@ function drawEmblems() {
.force('collision', d3.forceCollide().radius(d => d.size/2))
.stop();
// debug.attr("fill", "#fff").attr("stroke", "#000")
// .selectAll("circle").data(nodes).join("circle")
// .attr("cx", d => d.x)
// .attr("cy", d => d.y)
// .attr("r", 2);
d3.timeout(function() {
const n = Math.ceil(Math.log(simulation.alphaMin()) / Math.log(1 - simulation.alphaDecay()));
for (let i = 0; i < n; ++i) {
simulation.tick();
}
emblems.select("#burgEmblems").attr("font-size", sizeBurgs)
.selectAll("use").data(nodes.filter(node => node.type === "burg")).join("use")
.attr("x", d => d.x - d.size / 2).attr("y", d => d.y - d.size / 2)
.attr("width", "1em").attr("height", "1em").attr("data-i", d => d.i);
const burgNodes = nodes.filter(node => node.type === "burg");
const burgString = burgNodes.map(d => `<use data-i="${d.i}" x="${d.x - d.size / 2}" y="${d.y - d.size / 2}" width="1em" height="1em"/>`).join("");
emblems.select("#burgEmblems").attr("font-size", sizeBurgs).html(burgString);
emblems.select("#provinceEmblems").attr("font-size", sizeProvinces)
.selectAll("use").data(nodes.filter(node => node.type === "province")).join("use")
.attr("x", d => d.x - d.size / 2).attr("y", d => d.y - d.size / 2)
.attr("width", "1em").attr("height", "1em").attr("data-i", d => d.i);
const provinceNodes = nodes.filter(node => node.type === "province");
const provinceString = provinceNodes.map(d => `<use data-i="${d.i}" x="${d.x - d.size / 2}" y="${d.y - d.size / 2}" width="1em" height="1em"/>`).join("");
emblems.select("#provinceEmblems").attr("font-size", sizeProvinces).html(provinceString);
emblems.select("#stateEmblems").attr("font-size", sizeStates)
.selectAll("use").data(nodes.filter(node => node.type === "state")).join("use")
.attr("x", d => d.x - d.size / 2).attr("y", d => d.y - d.size / 2)
.attr("width", "1em").attr("height", "1em").attr("data-i", d => d.i);
const stateNodes = nodes.filter(node => node.type === "state");
const stateString = stateNodes.map(d => `<use data-i="${d.i}" x="${d.x - d.size / 2}" y="${d.y - d.size / 2}" width="1em" height="1em"/>`).join("");
emblems.select("#stateEmblems").attr("font-size", sizeStates).html(stateString);
invokeActiveZooming();
});

View file

@ -352,18 +352,16 @@ function editProvinces() {
pack.cells.province.forEach((province, i) => {
if(province === p) pack.cells.province[i] = 0;
});
const province = pack.provinces[p];
const s = province.state, state = pack.states[s];
if (state.provinces.includes(p)) state.provinces.splice(state.provinces.indexOf(p), 1);
province.removed = true;
unfog("focusProvince"+p);
if (province.coa) {
const coaId = "provinceCOA" + p;
if (document.getElementById(coaId)) document.getElementById(coaId).remove();
emblems.select(`#provinceEmblems > use[data-i='${p}']`).remove();
delete province.coa; // remove to save data
}
const coaId = "provinceCOA" + p;
if (document.getElementById(coaId)) document.getElementById(coaId).remove();
emblems.select(`#provinceEmblems > use[data-i='${p}']`).remove();
pack.provinces[p] = {i: p, removed: true};
const g = provs.select("#provincesBody");
g.select("#province"+p).remove();

View file

@ -422,17 +422,28 @@ function editStates() {
statesBody.select("#state"+state).remove();
statesBody.select("#state-gap"+state).remove();
statesHalo.select("#state-border"+state).remove();
labels.select("#stateLabel"+state).remove();
defs.select("#textPath_stateLabel"+state).remove();
unfog("focusState"+state);
const label = document.querySelector("#stateLabel"+state);
if (label) label.remove();
pack.burgs.forEach(b => {if(b.state === state) b.state = 0;});
pack.cells.state.forEach((s, i) => {if(s === state) pack.cells.state[i] = 0;});
pack.states[state].removed = true;
// remove emblem
const coaId = "stateCOA" + state;
document.getElementById(coaId).remove();
emblems.select(`#stateEmblems > use[data-i='${state}']`).remove();
// remove provinces
pack.states[state].provinces.forEach(p => {
pack.provinces[p].removed = true;
pack.provinces[p] = {i: p, removed: true};
pack.cells.province.forEach((pr, i) => {if(pr === p) pack.cells.province[i] = 0;});
const coaId = "provinceCOA" + p;
if (document.getElementById(coaId)) document.getElementById(coaId).remove();
emblems.select(`#provinceEmblems > use[data-i='${p}']`).remove();
const g = provs.select("#provincesBody");
g.select("#province"+p).remove();
g.select("#province-gap"+p).remove();
});
// remove military
@ -448,8 +459,7 @@ function editStates() {
pack.burgs[capital].state = 0;
moveBurgToGroup(capital, "towns");
// clean state object
pack.states[state].military = [];
pack.states[state] = {i: state, removed: true};
debug.selectAll(".highlight").remove();
if (!layerIsOn("toggleStates")) toggleStates(); else drawStates();

View file

@ -60,9 +60,10 @@ function processFeatureRegeneration(event, button) {
if (button === "regenerateRoutes") {Routes.regenerate(); if (!layerIsOn("toggleRoutes")) toggleRoutes();} else
if (button === "regenerateRivers") regenerateRivers(); else
if (button === "regeneratePopulation") recalculatePopulation(); else
if (button === "regenerateBurgs") regenerateBurgs(); else
if (button === "regenerateStates") regenerateStates(); else
if (button === "regenerateProvinces") regenerateProvinces(); else
if (button === "regenerateBurgs") regenerateBurgs(); else
if (button === "regenerateEmblems") regenerateEmblems(); else
if (button === "regenerateReligions") regenerateReligions(); else
if (button === "regenerateCultures") regenerateCultures(); else
if (button === "regenerateMilitary") regenerateMilitary(); else
@ -95,6 +96,108 @@ function recalculatePopulation() {
});
}
function regenerateStates() {
Math.seedrandom(Math.floor(Math.random() * 1e9)); // new random seed
const burgs = pack.burgs.filter(b => b.i && !b.removed);
if (!burgs.length) {
tip("No burgs to generate states. Please create burgs first", false, "error");
return;
}
if (burgs.length < +regionsInput.value) {
tip(`Not enough burgs to generate ${regionsInput.value} states. Will generate only ${burgs.length} states`, false, "warn");
}
// burg local ids sorted by a bit randomized population:
const sorted = burgs.map((b, i) => [i, b.population * Math.random()]).sort((a, b) => b[1] - a[1]).map(b => b[0]);
const capitalsTree = d3.quadtree();
// turn all old capitals into towns
burgs.filter(b => b.capital).forEach(b => {
moveBurgToGroup(b.i, "towns");
b.capital = 0;
});
unfog();
// if desired states number is 0
if (regionsInput.value == 0) {
tip(`Cannot generate zero states. Please check the <i>States Number</i> option`, false, "warn");
pack.states = pack.states.slice(0,1); // remove all except of neutrals
pack.states[0].diplomacy = []; // clear diplomacy
pack.provinces = [0]; // remove all provinces
pack.cells.state = new Uint16Array(pack.cells.i.length); // reset cells data
borders.selectAll("path").remove(); // remove borders
regions.selectAll("path").remove(); // remove states fill
labels.select("#states").selectAll("text"); // remove state labels
defs.select("#textPaths").selectAll("path[id*='stateLabel']").remove(); // remove state labels paths
// remove emblems
document.querySelectorAll("[id^=stateCOA]").forEach(el => el.remove());
document.querySelectorAll("[id^=provinceCOA]").forEach(el => el.remove());
emblems.selectAll("use").remove();
if (document.getElementById("burgsOverviewRefresh").offsetParent) burgsOverviewRefresh.click();
if (document.getElementById("statesEditorRefresh").offsetParent) statesEditorRefresh.click();
return;
}
const neutral = pack.states[0].name;
const count = Math.min(+regionsInput.value, burgs.length);
let spacing = (graphWidth + graphHeight) / 2 / count; // min distance between capitals
pack.states = d3.range(count).map(i => {
if (!i) return {i, name: neutral};
let capital = null, x = 0, y = 0;
for (const i of sorted) {
capital = burgs[i];
x = capital.x, y = capital.y;
if (capitalsTree.find(x, y, spacing) === undefined) break;
spacing = Math.max(spacing - 1, 1);
}
capitalsTree.add([x, y]);
capital.capital = 1;
moveBurgToGroup(capital.i, "cities");
const culture = capital.culture;
const basename = capital.name.length < 9 && capital.cell%5 === 0 ? capital.name : Names.getCulture(culture, 3, 6, "", 0);
const name = Names.getState(basename, culture);
const nomadic = [1, 2, 3, 4].includes(pack.cells.biome[capital.cell]);
const type = nomadic ? "Nomadic" : pack.cultures[culture].type === "Nomadic" ? "Generic" : pack.cultures[culture].type;
const expansionism = rn(Math.random() * powerInput.value + 1, 1);
const coa = COA.generate(capital.coa, .3);
coa.shield = capital.coa.shield;
return {i, name, type, capital:capital.i, center:capital.cell, culture, expansionism, coa};
});
BurgsAndStates.expandStates();
BurgsAndStates.normalizeStates();
BurgsAndStates.collectStatistics();
BurgsAndStates.assignColors();
BurgsAndStates.generateCampaigns();
BurgsAndStates.generateDiplomacy();
BurgsAndStates.defineStateForms();
BurgsAndStates.generateProvinces(true);
if (!layerIsOn("toggleStates")) toggleStates(); else drawStates();
if (!layerIsOn("toggleBorders")) toggleBorders(); else drawBorders();
BurgsAndStates.drawStateLabels();
Military.generate();
if (layerIsOn("toggleEmblems")) drawEmblems(); // redrawEmblems
if (document.getElementById("burgsOverviewRefresh").offsetParent) burgsOverviewRefresh.click();
if (document.getElementById("statesEditorRefresh").offsetParent) statesEditorRefresh.click();
if (document.getElementById("militaryOverviewRefresh").offsetParent) militaryOverviewRefresh.click();
}
function regenerateProvinces() {
unfog();
BurgsAndStates.generateProvinces(true);
drawBorders();
if (layerIsOn("toggleProvinces")) drawProvinces();
}
function regenerateBurgs() {
const cells = pack.cells, states = pack.states;
rankCells();
@ -148,96 +251,55 @@ function regenerateBurgs() {
if (document.getElementById("statesEditorRefresh").offsetParent) statesEditorRefresh.click();
}
function regenerateStates() {
Math.seedrandom(Math.floor(Math.random() * 1e9)); // new random seed
const burgs = pack.burgs.filter(b => b.i && !b.removed);
if (!burgs.length) {
tip("No burgs to generate states. Please create burgs first", false, "error");
return;
}
if (burgs.length < +regionsInput.value) {
tip(`Not enough burgs to generate ${regionsInput.value} states. Will generate only ${burgs.length} states`, false, "warn");
}
function regenerateEmblems() {
// remove old emblems
document.querySelectorAll("[id^=stateCOA]").forEach(el => el.remove());
document.querySelectorAll("[id^=provinceCOA]").forEach(el => el.remove());
document.querySelectorAll("[id^=burgCOA]").forEach(el => el.remove());
emblems.selectAll("use").remove();
// burg local ids sorted by a bit randomized population:
const sorted = burgs.map((b, i) => [i, b.population * Math.random()]).sort((a, b) => b[1] - a[1]).map(b => b[0]);
const capitalsTree = d3.quadtree();
// turn all old capitals into towns
burgs.filter(b => b.capital).forEach(b => {
moveBurgToGroup(b.i, "towns");
b.capital = 0;
// generate new emblems
pack.states.forEach(state => {
if (!state.i || state.removed) return;
state.coa = COA.generate(null);
state.coa.shield = COA.getShield(state.culture, null);
});
unfog();
pack.burgs.forEach(burg => {
if (!burg.i || burg.removed) return;
const state = pack.states[burg.state];
// if desired states number is 0
if (regionsInput.value == 0) {
tip(`Cannot generate zero states. Please check the <i>States Number</i> option`, false, "warn");
pack.states = pack.states.slice(0,1); // remove all except of neutrals
pack.states[0].diplomacy = []; // clear diplomacy
pack.provinces = [0]; // remove all provinces
pack.cells.state = new Uint16Array(pack.cells.i.length); // reset cells data
borders.selectAll("path").remove(); // remove borders
regions.selectAll("path").remove(); // remove states fill
labels.select("#states").selectAll("text"); // remove state labels
defs.select("#textPaths").selectAll("path[id*='stateLabel']").remove(); // remove state labels paths
let kinship = .25;
if (burg.capital) kinship += .1;
else if (burg.port) kinship -= .1;
if (burg.culture !== state.culture) kinship -= .25;
burg.coa = COA.generate(state.coa, kinship);
burg.coa.shield = COA.getShield(burg.culture, burg.state);
});
if (document.getElementById("burgsOverviewRefresh").offsetParent) burgsOverviewRefresh.click();
if (document.getElementById("statesEditorRefresh").offsetParent) statesEditorRefresh.click();
return;
}
pack.provinces.forEach(province => {
if (!province.i || province.removed) return;
const parent = province.burg ? pack.burgs[province.burg] : pack.states[province.state];
const neutral = pack.states[0].name;
const count = Math.min(+regionsInput.value, burgs.length);
let spacing = (graphWidth + graphHeight) / 2 / count; // min distance between capitals
pack.states = d3.range(count).map(i => {
if (!i) return {i, name: neutral};
let capital = null, x = 0, y = 0;
for (const i of sorted) {
capital = burgs[i];
x = capital.x, y = capital.y;
if (capitalsTree.find(x, y, spacing) === undefined) break;
spacing = Math.max(spacing - 1, 1);
let dominion = false;
if (province.burg) {
dominion = P(.2);
if (province.formName === "Colony") dominion = P(.95); else
if (province.formName === "Island") dominion = P(.6); else
if (province.formName === "Islands") dominion = P(.5); else
if (province.formName === "Territory") dominion = P(.4); else
if (province.formName === "Land") dominion = P(.3);
}
capitalsTree.add([x, y]);
capital.capital = 1;
moveBurgToGroup(capital.i, "cities");
const nameByBurg = province.burg && province.name.slice(0, 3) === parent.name.slice(0, 3);
const kinship = dominion ? 0 : nameByBurg ? .8 : .4;
const culture = pack.cells.culture[province.center];
const culture = capital.culture;
const basename = capital.name.length < 9 && capital.cell%5 === 0 ? capital.name : Names.getCulture(culture, 3, 6, "", 0);
const name = Names.getState(basename, culture);
const nomadic = [1, 2, 3, 4].includes(pack.cells.biome[capital.cell]);
const type = nomadic ? "Nomadic" : pack.cultures[culture].type === "Nomadic" ? "Generic" : pack.cultures[culture].type;
const expansionism = rn(Math.random() * powerInput.value + 1, 1);
return {i, name, type, capital:capital.i, center:capital.cell, culture, expansionism};
province.coa = COA.generate(parent.coa, kinship, dominion);
province.coa.shield = COA.getShield(culture, province.state);
});
BurgsAndStates.expandStates();
BurgsAndStates.normalizeStates();
BurgsAndStates.collectStatistics();
BurgsAndStates.assignColors();
BurgsAndStates.generateCampaigns();
BurgsAndStates.generateDiplomacy();
BurgsAndStates.defineStateForms();
BurgsAndStates.generateProvinces(true);
if (!layerIsOn("toggleStates")) toggleStates(); else drawStates();
if (!layerIsOn("toggleBorders")) toggleBorders(); else drawBorders();
BurgsAndStates.drawStateLabels();
Military.generate();
if (document.getElementById("burgsOverviewRefresh").offsetParent) burgsOverviewRefresh.click();
if (document.getElementById("statesEditorRefresh").offsetParent) statesEditorRefresh.click();
if (document.getElementById("militaryOverviewRefresh").offsetParent) militaryOverviewRefresh.click();
}
function regenerateProvinces() {
unfog();
BurgsAndStates.generateProvinces(true);
drawBorders();
if (layerIsOn("toggleProvinces")) drawProvinces();
if (layerIsOn("toggleEmblems")) drawEmblems(); // redrawEmblems
}
function regenerateReligions() {