mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-22 12:01:23 +01:00
refactor(#902): start with states regenertion
This commit is contained in:
parent
80b8bc89a1
commit
e984c708d1
4 changed files with 189 additions and 216 deletions
11
index.html
11
index.html
|
|
@ -2017,11 +2017,14 @@
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
id="regenerateStates"
|
id="regenerateStates"
|
||||||
data-tip="Click to select new capitals and regenerate unlocked states. Emblems and military forces will be regenerated as well, burgs will remain as they are"
|
data-tip="Click to select new capitals and regenerate non-locked states. Emblems and military forces will be regenerated as well, burgs will remain as they are"
|
||||||
>
|
>
|
||||||
States
|
States
|
||||||
</button>
|
</button>
|
||||||
<button id="regenerateProvinces" data-tip="Click to regenerate unlocked provinces. States will remain as they are">
|
<button
|
||||||
|
id="regenerateProvinces"
|
||||||
|
data-tip="Click to regenerate non-locked provinces. States will remain as they are"
|
||||||
|
>
|
||||||
Provinces
|
Provinces
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
|
|
@ -2031,8 +2034,8 @@
|
||||||
Burgs
|
Burgs
|
||||||
</button>
|
</button>
|
||||||
<button id="regenerateEmblems" data-tip="Click to regenerate all emblems">Emblems</button>
|
<button id="regenerateEmblems" data-tip="Click to regenerate all emblems">Emblems</button>
|
||||||
<button id="regenerateReligions" data-tip="Click to regenerate unlocked religions">Religions</button>
|
<button id="regenerateReligions" data-tip="Click to regenerate non-locked religions">Religions</button>
|
||||||
<button id="regenerateCultures" data-tip="Click to regenerate unlocked cultures">Cultures</button>
|
<button id="regenerateCultures" data-tip="Click to regenerate non-locked cultures">Cultures</button>
|
||||||
<button
|
<button
|
||||||
id="regenerateMilitary"
|
id="regenerateMilitary"
|
||||||
data-tip="Click to recalculate military forces based on current military options"
|
data-tip="Click to recalculate military forces based on current military options"
|
||||||
|
|
|
||||||
|
|
@ -163,144 +163,6 @@ window.BurgsAndStates = (function () {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const regenerateStates = function() {
|
|
||||||
const localSeed = generateSeed();
|
|
||||||
Math.random = aleaPRNG(localSeed);
|
|
||||||
|
|
||||||
const statesCount = +regionsOutput.value;
|
|
||||||
const burgs = pack.burgs.filter(b => b.i && !b.removed);
|
|
||||||
if (!burgs.length) return tip("There are no any burgs to generate states. Please create burgs first", false, "error");
|
|
||||||
if (burgs.length < statesCount)
|
|
||||||
tip(`Not enough burgs to generate ${statesCount} states. Will generate only ${burgs.length} states`, false, "warn");
|
|
||||||
|
|
||||||
// turn all old capitals into towns, except for the capitals of locked states
|
|
||||||
burgs
|
|
||||||
.filter(b => b.capital && pack.states.find(s => s.lock && s.capital === b.i) === undefined)
|
|
||||||
.forEach(b => {
|
|
||||||
moveBurgToGroup(b.i, "towns");
|
|
||||||
b.capital = 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
// remove emblems
|
|
||||||
document.querySelectorAll("[id^=stateCOA]").forEach(el => el.remove());
|
|
||||||
document.querySelectorAll("[id^=provinceCOA]").forEach(el => el.remove());
|
|
||||||
emblems.selectAll("use").remove();
|
|
||||||
|
|
||||||
unfog();
|
|
||||||
|
|
||||||
if (!statesCount) {
|
|
||||||
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
|
|
||||||
|
|
||||||
if (document.getElementById("burgsOverviewRefresh").offsetParent) burgsOverviewRefresh.click();
|
|
||||||
if (document.getElementById("statesEditorRefresh").offsetParent) statesEditorRefresh.click();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// burg local ids sorted by a bit randomized population. Also ignore burgs of a locked state.
|
|
||||||
const sortedBurgs = burgs
|
|
||||||
.filter(b => !pack.states[b.state] || !pack.states[b.state].lock)
|
|
||||||
.map((b, i) => [b, b.population * Math.random()])
|
|
||||||
.sort((a, b) => b[1] - a[1])
|
|
||||||
.map(b => b[0]);
|
|
||||||
const capitalsTree = d3.quadtree();
|
|
||||||
|
|
||||||
const neutral = pack.states[0].name; // neutrals name
|
|
||||||
const count = Math.min(statesCount, burgs.length) + 1; // +1 for neutral
|
|
||||||
let spacing = (graphWidth + graphHeight) / 2 / count; // min distance between capitals
|
|
||||||
|
|
||||||
const states = [];
|
|
||||||
// Get all the states to restore
|
|
||||||
let statesToRestore = [];
|
|
||||||
if (pack.states) {
|
|
||||||
pack.states.forEach(state => {
|
|
||||||
if (!state.lock) return;
|
|
||||||
|
|
||||||
statesToRestore.push(state)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
d3.range(count).forEach(i => {
|
|
||||||
if (!i) {
|
|
||||||
states.push({i, name: neutral});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we still have states to restore from the locks, restore those first and assign them the right ids.
|
|
||||||
if (statesToRestore.length) {
|
|
||||||
const [toRestore, ...rest] = statesToRestore;
|
|
||||||
|
|
||||||
toRestore.old_i = toRestore.i;
|
|
||||||
toRestore.i = i;
|
|
||||||
states.push(toRestore);
|
|
||||||
|
|
||||||
// Also reassign the state id of all provinces of this state for locked provinces
|
|
||||||
toRestore.provinces.forEach(id => {
|
|
||||||
if (!pack.provinces[id]) return;
|
|
||||||
|
|
||||||
pack.provinces[id].state = toRestore.i;
|
|
||||||
pack.provinces[id].should_restore = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
statesToRestore = rest;
|
|
||||||
|
|
||||||
const {x, y} = burgs[toRestore.capital];
|
|
||||||
capitalsTree.add([x, y]);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let capital = null;
|
|
||||||
for (const burg of sortedBurgs) {
|
|
||||||
const {x, y} = burg;
|
|
||||||
if (capitalsTree.find(x, y, spacing) === undefined) {
|
|
||||||
burg.capital = 1;
|
|
||||||
capital = burg;
|
|
||||||
capitalsTree.add([x, y]);
|
|
||||||
moveBurgToGroup(burg.i, "cities");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
spacing = Math.max(spacing - 1, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 cultureType = pack.cultures[culture].type;
|
|
||||||
const coa = COA.generate(capital.coa, 0.3, null, cultureType);
|
|
||||||
coa.shield = capital.coa.shield;
|
|
||||||
|
|
||||||
states.push({
|
|
||||||
i,
|
|
||||||
name,
|
|
||||||
type,
|
|
||||||
capital: capital.i,
|
|
||||||
center: capital.cell,
|
|
||||||
culture,
|
|
||||||
expansionism,
|
|
||||||
coa
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
pack.states = states;
|
|
||||||
}
|
|
||||||
|
|
||||||
// define burg coordinates, coa, port status and define details
|
// define burg coordinates, coa, port status and define details
|
||||||
const specifyBurgs = function () {
|
const specifyBurgs = function () {
|
||||||
TIME && console.time("specifyBurgs");
|
TIME && console.time("specifyBurgs");
|
||||||
|
|
@ -502,12 +364,6 @@ window.BurgsAndStates = (function () {
|
||||||
TIME && console.time("expandStates");
|
TIME && console.time("expandStates");
|
||||||
const {cells, states, cultures, burgs} = pack;
|
const {cells, states, cultures, burgs} = pack;
|
||||||
|
|
||||||
const prevStates = {};
|
|
||||||
if (cells.state) {
|
|
||||||
cells.state.forEach(function (i, index) {
|
|
||||||
prevStates[index] = i;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
cells.state = new Uint16Array(cells.i.length);
|
cells.state = new Uint16Array(cells.i.length);
|
||||||
const queue = new PriorityQueue({comparator: (a, b) => a.p - b.p});
|
const queue = new PriorityQueue({comparator: (a, b) => a.p - b.p});
|
||||||
const cost = [];
|
const cost = [];
|
||||||
|
|
@ -516,17 +372,6 @@ window.BurgsAndStates = (function () {
|
||||||
states
|
states
|
||||||
.filter(s => s.i && !s.removed)
|
.filter(s => s.i && !s.removed)
|
||||||
.forEach(s => {
|
.forEach(s => {
|
||||||
if (s.lock) {
|
|
||||||
Object.entries(prevStates).forEach(function ([index, stateId]) {
|
|
||||||
if (stateId === s.old_i) {
|
|
||||||
cells.state[index] = s.i;
|
|
||||||
cost[index] = neutral;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const capitalCell = burgs[s.capital].cell;
|
const capitalCell = burgs[s.capital].cell;
|
||||||
cells.state[capitalCell] = s.i;
|
cells.state[capitalCell] = s.i;
|
||||||
const cultureCenter = cultures[s.culture].center;
|
const cultureCenter = cultures[s.culture].center;
|
||||||
|
|
@ -541,9 +386,9 @@ window.BurgsAndStates = (function () {
|
||||||
const {type, culture} = states[s];
|
const {type, culture} = states[s];
|
||||||
|
|
||||||
cells.c[e].forEach(e => {
|
cells.c[e].forEach(e => {
|
||||||
if (cells.state[e] && e === states[cells.state[e]].center) return; // do not overwrite capital cells
|
const state = states[cells.state[e]];
|
||||||
// Do not overwrite cells from a locked state.
|
if (state.lock) return; // do not overwrite cell of locked states
|
||||||
if (states[cells.state[e]].lock) return;
|
if (cells.state[e] && e === state.center) return; // do not overwrite capital cells
|
||||||
|
|
||||||
const cultureCost = culture === cells.culture[e] ? -9 : 100;
|
const cultureCost = culture === cells.culture[e] ? -9 : 100;
|
||||||
const populationCost = cells.h[e] < 20 ? 0 : cells.s[e] ? Math.max(20 - cells.s[e], 0) : 5000;
|
const populationCost = cells.h[e] < 20 ? 0 : cells.s[e] ? Math.max(20 - cells.s[e], 0) : 5000;
|
||||||
|
|
@ -608,16 +453,12 @@ window.BurgsAndStates = (function () {
|
||||||
|
|
||||||
for (const i of cells.i) {
|
for (const i of cells.i) {
|
||||||
if (cells.h[i] < 20 || cells.burg[i]) continue; // do not overwrite burgs
|
if (cells.h[i] < 20 || cells.burg[i]) continue; // do not overwrite burgs
|
||||||
|
if (pack.states[cells.state[i]]?.lock) continue; // do not overwrite cells of locks states
|
||||||
if (cells.c[i].some(c => burgs[cells.burg[c]].capital)) continue; // do not overwrite near capital
|
if (cells.c[i].some(c => burgs[cells.burg[c]].capital)) continue; // do not overwrite near capital
|
||||||
if (pack.states[cells.state[i]]?.lock) continue; // Do not overwrite cells of locks states
|
|
||||||
const neibs = cells.c[i].filter(c => cells.h[c] >= 20);
|
const neibs = cells.c[i].filter(c => cells.h[c] >= 20);
|
||||||
const adversaries = neibs.filter(
|
const adversaries = neibs.filter(c => !pack.states[cells.state[c]]?.lock && cells.state[c] !== cells.state[i]);
|
||||||
c => !pack.states[cells.state[c]]?.lock && cells.state[c] !== cells.state[i]
|
|
||||||
);
|
|
||||||
if (adversaries.length < 2) continue;
|
if (adversaries.length < 2) continue;
|
||||||
const buddies = neibs.filter(
|
const buddies = neibs.filter(c => !pack.states[cells.state[c]]?.lock && cells.state[c] === cells.state[i]);
|
||||||
c => !pack.states[cells.state[c]]?.lock && cells.state[c] === cells.state[i]
|
|
||||||
);
|
|
||||||
if (buddies.length > 2) continue;
|
if (buddies.length > 2) continue;
|
||||||
if (adversaries.length <= buddies.length) continue;
|
if (adversaries.length <= buddies.length) continue;
|
||||||
cells.state[i] = cells.state[adversaries[0]];
|
cells.state[i] = cells.state[adversaries[0]];
|
||||||
|
|
@ -763,18 +604,22 @@ window.BurgsAndStates = (function () {
|
||||||
|
|
||||||
if (!list) {
|
if (!list) {
|
||||||
// remove all labels and textpaths
|
// remove all labels and textpaths
|
||||||
g.selectAll("text").filter((_, i) => {
|
g.selectAll("text")
|
||||||
|
.filter((_, i) => {
|
||||||
const id = g.select(`:nth-child(${i + 1})`).node()?.id;
|
const id = g.select(`:nth-child(${i + 1})`).node()?.id;
|
||||||
if (!id) return true;
|
if (!id) return true;
|
||||||
|
|
||||||
return !pack.states.some(s => s.lock && `${s.old_i}` === id.substring(10));
|
return !pack.states.some(s => s.lock && `${s.old_i}` === id.substring(10));
|
||||||
}).remove();
|
})
|
||||||
t.selectAll("path[id*='stateLabel']").filter((_, i) => {
|
.remove();
|
||||||
|
t.selectAll("path[id*='stateLabel']")
|
||||||
|
.filter((_, i) => {
|
||||||
const id = t.select(`:nth-child(${i + 1})`).node()?.id;
|
const id = t.select(`:nth-child(${i + 1})`).node()?.id;
|
||||||
if (!id) return true;
|
if (!id) return true;
|
||||||
|
|
||||||
return !pack.states.some(s => s.lock && `${s.old_i}` === id.substring(19));
|
return !pack.states.some(s => s.lock && `${s.old_i}` === id.substring(19));
|
||||||
}).remove();
|
})
|
||||||
|
.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
pack.states.forEach(s => {
|
pack.states.forEach(s => {
|
||||||
|
|
@ -787,11 +632,8 @@ window.BurgsAndStates = (function () {
|
||||||
const labelNode = g.select(`#stateLabel${s.old_i}`);
|
const labelNode = g.select(`#stateLabel${s.old_i}`);
|
||||||
const textNode = t.select(`#textPath_stateLabel${s.old_i}`);
|
const textNode = t.select(`#textPath_stateLabel${s.old_i}`);
|
||||||
|
|
||||||
labelNode
|
labelNode.attr("id", `stateLabel${s.i}`).select("textPath").attr("xlink:href", `#textPath_stateLabel${s.i}`);
|
||||||
.attr('id', `stateLabel${s.i}`)
|
textNode.attr("id", `textPath_stateLabel${s.i}`);
|
||||||
.select("textPath")
|
|
||||||
.attr("xlink:href", `#textPath_stateLabel${s.i}`);
|
|
||||||
textNode.attr('id', `textPath_stateLabel${s.i}`);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const example = g.append("text").attr("x", 0).attr("x", 0).text("Average");
|
const example = g.append("text").attr("x", 0).attr("x", 0).text("Average");
|
||||||
|
|
@ -1332,25 +1174,20 @@ window.BurgsAndStates = (function () {
|
||||||
return adjName ? `${getAdjective(s.name)} ${s.formName}` : `${s.formName} of ${s.name}`;
|
return adjName ? `${getAdjective(s.name)} ${s.formName}` : `${s.formName} of ${s.name}`;
|
||||||
};
|
};
|
||||||
|
|
||||||
const generateProvinces = function (
|
const generateProvinces = function (regenerate = false, ignoreLockedStates = false) {
|
||||||
regenerate = false,
|
|
||||||
ignoreLockedStates = false
|
|
||||||
) {
|
|
||||||
TIME && console.time("generateProvinces");
|
TIME && console.time("generateProvinces");
|
||||||
const localSeed = regenerate ? generateSeed() : seed;
|
const localSeed = regenerate ? generateSeed() : seed;
|
||||||
Math.random = aleaPRNG(localSeed);
|
Math.random = aleaPRNG(localSeed);
|
||||||
|
|
||||||
const {cells, states, burgs} = pack;
|
const {cells, states, burgs} = pack;
|
||||||
const provincesToRestore = pack.provinces ?
|
const provincesToRestore = pack.provinces ? pack.provinces.filter(p => p.lock || p.should_restore) : [];
|
||||||
pack.provinces.filter(p => p.lock || p.should_restore)
|
|
||||||
: [];
|
|
||||||
const provinces = (pack.provinces = [0].concat(...provincesToRestore));
|
const provinces = (pack.provinces = [0].concat(...provincesToRestore));
|
||||||
|
|
||||||
const prevProvinces = {};
|
const prevProvinces = {};
|
||||||
if (cells.province) {
|
if (cells.province) {
|
||||||
cells.province.forEach((i, index) => {
|
cells.province.forEach((i, index) => {
|
||||||
prevProvinces[index] = i;
|
prevProvinces[index] = i;
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
cells.province = new Uint16Array(cells.i.length); // cell state
|
cells.province = new Uint16Array(cells.i.length); // cell state
|
||||||
const percentage = +provincesInput.value;
|
const percentage = +provincesInput.value;
|
||||||
|
|
@ -1380,7 +1217,8 @@ window.BurgsAndStates = (function () {
|
||||||
if (!s.i || s.removed) return;
|
if (!s.i || s.removed) return;
|
||||||
const stateBurgs = burgs
|
const stateBurgs = burgs
|
||||||
// Filter for burgs of this state that haven't been removed and that are not in a locked province.
|
// Filter for burgs of this state that haven't been removed and that are not in a locked province.
|
||||||
.filter(b =>
|
.filter(
|
||||||
|
b =>
|
||||||
b.state === s.i && !b.removed && provincesToRestore.find(p => prevProvinces[b.cell] === p.i) === undefined
|
b.state === s.i && !b.removed && provincesToRestore.find(p => prevProvinces[b.cell] === p.i) === undefined
|
||||||
)
|
)
|
||||||
.sort((a, b) => b.population * gauss(1, 0.2, 0.5, 1.5, 3) - a.population)
|
.sort((a, b) => b.population * gauss(1, 0.2, 0.5, 1.5, 3) - a.population)
|
||||||
|
|
@ -1450,16 +1288,15 @@ window.BurgsAndStates = (function () {
|
||||||
// Do not overwrite cells from a locked state or province.
|
// Do not overwrite cells from a locked state or province.
|
||||||
if (
|
if (
|
||||||
(provinces[cells.province[e]] && provinces[cells.province[e]].lock) ||
|
(provinces[cells.province[e]] && provinces[cells.province[e]].lock) ||
|
||||||
(
|
|
||||||
// For finding if the state is locked, first make sure we care about that
|
// For finding if the state is locked, first make sure we care about that
|
||||||
// then find the province, the state for the province, and if both are defined,
|
// then find the province, the state for the province, and if both are defined,
|
||||||
// check the lock.
|
// check the lock.
|
||||||
!ignoreLockedStates &&
|
(!ignoreLockedStates &&
|
||||||
provinces[cells.province[e]] &&
|
provinces[cells.province[e]] &&
|
||||||
states[provinces[cells.province[e]].state] &&
|
states[provinces[cells.province[e]].state] &&
|
||||||
states[provinces[cells.province[e]].state].lock
|
states[provinces[cells.province[e]].state].lock)
|
||||||
)
|
)
|
||||||
) return;
|
return;
|
||||||
|
|
||||||
const land = cells.h[e] >= 20;
|
const land = cells.h[e] >= 20;
|
||||||
if (!land && !cells.t[e]) return; // cannot pass deep ocean
|
if (!land && !cells.t[e]) return; // cannot pass deep ocean
|
||||||
|
|
@ -1480,17 +1317,16 @@ window.BurgsAndStates = (function () {
|
||||||
for (const i of cells.i) {
|
for (const i of cells.i) {
|
||||||
if (cells.burg[i]) continue; // do not overwrite burgs
|
if (cells.burg[i]) continue; // do not overwrite burgs
|
||||||
// Do not process any locked provinces or states, if we care about the latter
|
// Do not process any locked provinces or states, if we care about the latter
|
||||||
if (
|
if (pack.provinces[cells.province[i]].lock || (!ignoreLockedStates && pack.states[cells.state[i]].lock)) continue;
|
||||||
pack.provinces[cells.province[i]].lock ||
|
|
||||||
(!ignoreLockedStates && pack.states[cells.state[i]].lock)
|
|
||||||
) continue;
|
|
||||||
// Find neighbors, but ignore any cells from locked states or provinces
|
// Find neighbors, but ignore any cells from locked states or provinces
|
||||||
const neibs = cells.c[i].filter(
|
const neibs = cells.c[i]
|
||||||
|
.filter(
|
||||||
c =>
|
c =>
|
||||||
(ignoreLockedStates || !pack.states[cells.state[c]].lock) &&
|
(ignoreLockedStates || !pack.states[cells.state[c]].lock) &&
|
||||||
!pack.provinces[cells.province[c]].lock &&
|
!pack.provinces[cells.province[c]].lock &&
|
||||||
cells.state[c] === cells.state[i]
|
cells.state[c] === cells.state[i]
|
||||||
).map(c => cells.province[c]);
|
)
|
||||||
|
.map(c => cells.province[c]);
|
||||||
const adversaries = neibs.filter(c => c !== cells.province[i]);
|
const adversaries = neibs.filter(c => c !== cells.province[i]);
|
||||||
if (adversaries.length < 2) continue;
|
if (adversaries.length < 2) continue;
|
||||||
const buddies = neibs.filter(c => c === cells.province[i]).length;
|
const buddies = neibs.filter(c => c === cells.province[i]).length;
|
||||||
|
|
@ -1613,7 +1449,6 @@ window.BurgsAndStates = (function () {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
generate,
|
generate,
|
||||||
regenerateStates,
|
|
||||||
expandStates,
|
expandStates,
|
||||||
normalizeStates,
|
normalizeStates,
|
||||||
assignColors,
|
assignColors,
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,8 @@ function addListeners() {
|
||||||
else if (classList.contains("statePopulation")) changePopulation(stateId);
|
else if (classList.contains("statePopulation")) changePopulation(stateId);
|
||||||
else if (classList.contains("icon-pin")) toggleFog(stateId, classList);
|
else if (classList.contains("icon-pin")) toggleFog(stateId, classList);
|
||||||
else if (classList.contains("icon-trash-empty")) stateRemovePrompt(stateId);
|
else if (classList.contains("icon-trash-empty")) stateRemovePrompt(stateId);
|
||||||
else if (classList.contains("icon-lock") || classList.contains("icon-lock-open")) updateLockStatus(stateId, classList);
|
else if (classList.contains("icon-lock") || classList.contains("icon-lock-open"))
|
||||||
|
updateLockStatus(stateId, classList);
|
||||||
});
|
});
|
||||||
|
|
||||||
$body.on("input", function (ev) {
|
$body.on("input", function (ev) {
|
||||||
|
|
@ -289,7 +290,9 @@ function statesEditorAddLines() {
|
||||||
<span data-tip="Cells count" class="icon-check-empty ${hidden} show hide"></span>
|
<span data-tip="Cells count" class="icon-check-empty ${hidden} show hide"></span>
|
||||||
<div data-tip="Cells count" class="stateCells ${hidden} show hide">${s.cells}</div>
|
<div data-tip="Cells count" class="stateCells ${hidden} show hide">${s.cells}</div>
|
||||||
<span data-tip="Toggle state focus" class="icon-pin ${focused ? "" : " inactive"} hide"></span>
|
<span data-tip="Toggle state focus" class="icon-pin ${focused ? "" : " inactive"} hide"></span>
|
||||||
<span data-tip="Lock the state" class="icon-lock${s.lock ? '' : '-open'} hide"></span>
|
<span data-tip="Lock the state to protect it from re-generation" class="icon-lock${
|
||||||
|
s.lock ? "" : "-open"
|
||||||
|
} hide"></span>
|
||||||
<span data-tip="Remove the state" class="icon-trash-empty hide"></span>
|
<span data-tip="Remove the state" class="icon-trash-empty hide"></span>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// module to control the Tools options (click to edit, to re-geenerate, tp add)
|
// module to control the Tools options (click to edit, to re-geenerate, tp add)
|
||||||
|
|
||||||
toolsContent.addEventListener("click", function (event) {
|
toolsContent.addEventListener("click", function (event) {
|
||||||
|
|
@ -138,7 +139,7 @@ function recalculatePopulation() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function regenerateStates() {
|
function regenerateStates() {
|
||||||
BurgsAndStates.regenerateStates();
|
recreateStates();
|
||||||
BurgsAndStates.expandStates();
|
BurgsAndStates.expandStates();
|
||||||
BurgsAndStates.normalizeStates();
|
BurgsAndStates.normalizeStates();
|
||||||
BurgsAndStates.collectStatistics();
|
BurgsAndStates.collectStatistics();
|
||||||
|
|
@ -160,6 +161,137 @@ function regenerateStates() {
|
||||||
if (document.getElementById("militaryOverviewRefresh")?.offsetParent) militaryOverviewRefresh.click();
|
if (document.getElementById("militaryOverviewRefresh")?.offsetParent) militaryOverviewRefresh.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function recreateStates() {
|
||||||
|
const localSeed = generateSeed();
|
||||||
|
Math.random = aleaPRNG(localSeed);
|
||||||
|
|
||||||
|
const statesCount = +regionsOutput.value;
|
||||||
|
const validBurgs = pack.burgs.filter(b => b.i && !b.removed);
|
||||||
|
if (!validBurgs.length)
|
||||||
|
return tip("There are no any burgs to generate states. Please create burgs first", false, "error");
|
||||||
|
if (validBurgs.length < statesCount)
|
||||||
|
tip(
|
||||||
|
`Not enough burgs to generate ${statesCount} states. Will generate only ${validBurgs.length} states`,
|
||||||
|
false,
|
||||||
|
"warn"
|
||||||
|
);
|
||||||
|
|
||||||
|
const lockedStates = pack.states.filter(s => s.i && !s.removed && s.lock);
|
||||||
|
const lockedStatesIds = lockedStates.map(s => s.i);
|
||||||
|
const lockedStatesCapitals = lockedStates.map(s => s.capital);
|
||||||
|
|
||||||
|
// turn all old capitals into towns, except for the capitals of locked states
|
||||||
|
for (const burg of validBurgs) {
|
||||||
|
if (!burg.capital) continue;
|
||||||
|
if (lockedStatesCapitals.includes(burg.i)) continue;
|
||||||
|
|
||||||
|
moveBurgToGroup(burg.i, "towns");
|
||||||
|
burg.capital = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove emblems
|
||||||
|
document.querySelectorAll("[id^=stateCOA]").forEach(el => el.remove());
|
||||||
|
document.querySelectorAll("[id^=provinceCOA]").forEach(el => el.remove());
|
||||||
|
emblems.selectAll("use").remove();
|
||||||
|
|
||||||
|
unfog();
|
||||||
|
|
||||||
|
if (!statesCount) {
|
||||||
|
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
|
||||||
|
|
||||||
|
if (document.getElementById("burgsOverviewRefresh").offsetParent) burgsOverviewRefresh.click();
|
||||||
|
if (document.getElementById("statesEditorRefresh").offsetParent) statesEditorRefresh.click();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// burg local ids sorted by a bit randomized population. Also ignore burgs of a locked state
|
||||||
|
const sortedBurgs = validBurgs
|
||||||
|
.filter(b => !lockedStatesIds.includes(b.state))
|
||||||
|
.map(b => [b, b.population * Math.random()])
|
||||||
|
.sort((a, b) => b[1] - a[1])
|
||||||
|
.map(b => b[0]);
|
||||||
|
|
||||||
|
const count = Math.min(statesCount, validBurgs.length) + 1; // +1 for neutral
|
||||||
|
let spacing = (graphWidth + graphHeight) / 2 / count; // min distance between capitals
|
||||||
|
|
||||||
|
const capitalsTree = d3.quadtree();
|
||||||
|
const isTooClose = (x, y, spacing) => Boolean(capitalsTree.find(x, y, spacing));
|
||||||
|
|
||||||
|
const newStates = [{i: 0, name: pack.states[0].name}];
|
||||||
|
|
||||||
|
// restore locked states
|
||||||
|
lockedStates.forEach(s => {
|
||||||
|
const newId = newStates.length;
|
||||||
|
|
||||||
|
s.provinces.forEach(id => {
|
||||||
|
if (!pack.provinces[id] || !pack.provinces[id].removed) return;
|
||||||
|
pack.provinces[id].state = newId;
|
||||||
|
// pack.provinces[id].should_restore = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const i of pack.cells.i) {
|
||||||
|
const stateId = pack.cells.state[i];
|
||||||
|
if (stateId === s.i) pack.cells.state[i] = newId;
|
||||||
|
}
|
||||||
|
|
||||||
|
const {x, y} = validBurgs[s.capital];
|
||||||
|
capitalsTree.add([x, y]);
|
||||||
|
|
||||||
|
s.i = newId;
|
||||||
|
newStates.push(s);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (let i = newStates.length; i < count; i++) {
|
||||||
|
let capital = null;
|
||||||
|
|
||||||
|
for (const burg of sortedBurgs) {
|
||||||
|
const {x, y} = burg;
|
||||||
|
if (!isTooClose(x, y, spacing)) {
|
||||||
|
burg.capital = 1;
|
||||||
|
capital = burg;
|
||||||
|
capitalsTree.add([x, y]);
|
||||||
|
moveBurgToGroup(burg.i, "cities");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
spacing = Math.max(spacing - 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// all burgs are too close, should not happen in normal conditions
|
||||||
|
if (!capital) break;
|
||||||
|
|
||||||
|
// create new state
|
||||||
|
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 cultureType = pack.cultures[culture].type;
|
||||||
|
const coa = COA.generate(capital.coa, 0.3, null, cultureType);
|
||||||
|
coa.shield = capital.coa.shield;
|
||||||
|
|
||||||
|
newStates.push({i, name, type, capital: capital.i, center: capital.cell, culture, expansionism, coa});
|
||||||
|
}
|
||||||
|
|
||||||
|
pack.states = newStates;
|
||||||
|
}
|
||||||
|
|
||||||
function regenerateProvinces() {
|
function regenerateProvinces() {
|
||||||
unfog();
|
unfog();
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue