mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 17:51:24 +01:00
refactor: start files migration nightmare
This commit is contained in:
parent
c4736cc640
commit
bc65e0e207
64 changed files with 1990 additions and 816 deletions
|
|
@ -32,17 +32,27 @@ class Battle {
|
|||
close: () => Battle.prototype.context.cancelResults()
|
||||
});
|
||||
|
||||
if (modules.Battle) return;
|
||||
modules.Battle = true;
|
||||
if (fmg.modules.Battle) return;
|
||||
fmg.modules.Battle = true;
|
||||
|
||||
// add listeners
|
||||
document.getElementById("battleType").addEventListener("click", ev => this.toggleChange(ev));
|
||||
document.getElementById("battleType").nextElementSibling.addEventListener("click", ev => Battle.prototype.context.changeType(ev));
|
||||
document.getElementById("battleNameShow").addEventListener("click", () => Battle.prototype.context.showNameSection());
|
||||
document.getElementById("battleNamePlace").addEventListener("change", ev => (Battle.prototype.context.place = ev.target.value));
|
||||
document
|
||||
.getElementById("battleType")
|
||||
.nextElementSibling.addEventListener("click", ev => Battle.prototype.context.changeType(ev));
|
||||
document
|
||||
.getElementById("battleNameShow")
|
||||
.addEventListener("click", () => Battle.prototype.context.showNameSection());
|
||||
document
|
||||
.getElementById("battleNamePlace")
|
||||
.addEventListener("change", ev => (Battle.prototype.context.place = ev.target.value));
|
||||
document.getElementById("battleNameFull").addEventListener("change", ev => Battle.prototype.context.changeName(ev));
|
||||
document.getElementById("battleNameCulture").addEventListener("click", () => Battle.prototype.context.generateName("culture"));
|
||||
document.getElementById("battleNameRandom").addEventListener("click", () => Battle.prototype.context.generateName("random"));
|
||||
document
|
||||
.getElementById("battleNameCulture")
|
||||
.addEventListener("click", () => Battle.prototype.context.generateName("culture"));
|
||||
document
|
||||
.getElementById("battleNameRandom")
|
||||
.addEventListener("click", () => Battle.prototype.context.generateName("random"));
|
||||
document.getElementById("battleNameHide").addEventListener("click", this.hideNameSection);
|
||||
document.getElementById("battleAddRegiment").addEventListener("click", this.addSide);
|
||||
document.getElementById("battleRoll").addEventListener("click", () => Battle.prototype.context.randomize());
|
||||
|
|
@ -52,11 +62,19 @@ class Battle {
|
|||
document.getElementById("battleWiki").addEventListener("click", () => wiki("Battle-Simulator"));
|
||||
|
||||
document.getElementById("battlePhase_attackers").addEventListener("click", ev => this.toggleChange(ev));
|
||||
document.getElementById("battlePhase_attackers").nextElementSibling.addEventListener("click", ev => Battle.prototype.context.changePhase(ev, "attackers"));
|
||||
document
|
||||
.getElementById("battlePhase_attackers")
|
||||
.nextElementSibling.addEventListener("click", ev => Battle.prototype.context.changePhase(ev, "attackers"));
|
||||
document.getElementById("battlePhase_defenders").addEventListener("click", ev => this.toggleChange(ev));
|
||||
document.getElementById("battlePhase_defenders").nextElementSibling.addEventListener("click", ev => Battle.prototype.context.changePhase(ev, "defenders"));
|
||||
document.getElementById("battleDie_attackers").addEventListener("click", () => Battle.prototype.context.rollDie("attackers"));
|
||||
document.getElementById("battleDie_defenders").addEventListener("click", () => Battle.prototype.context.rollDie("defenders"));
|
||||
document
|
||||
.getElementById("battlePhase_defenders")
|
||||
.nextElementSibling.addEventListener("click", ev => Battle.prototype.context.changePhase(ev, "defenders"));
|
||||
document
|
||||
.getElementById("battleDie_attackers")
|
||||
.addEventListener("click", () => Battle.prototype.context.rollDie("attackers"));
|
||||
document
|
||||
.getElementById("battleDie_defenders")
|
||||
.addEventListener("click", () => Battle.prototype.context.rollDie("defenders"));
|
||||
}
|
||||
|
||||
defineType() {
|
||||
|
|
@ -82,8 +100,12 @@ class Battle {
|
|||
document.getElementById("battleType").className = "icon-button-" + this.type;
|
||||
|
||||
const sideSpecific = document.getElementById("battlePhases_" + this.type + "_attackers");
|
||||
const attackers = sideSpecific ? sideSpecific.content : document.getElementById("battlePhases_" + this.type).content;
|
||||
const defenders = sideSpecific ? document.getElementById("battlePhases_" + this.type + "_defenders").content : attackers;
|
||||
const attackers = sideSpecific
|
||||
? sideSpecific.content
|
||||
: document.getElementById("battlePhases_" + this.type).content;
|
||||
const defenders = sideSpecific
|
||||
? document.getElementById("battlePhases_" + this.type + "_defenders").content
|
||||
: attackers;
|
||||
|
||||
document.getElementById("battlePhase_attackers").nextElementSibling.innerHTML = "";
|
||||
document.getElementById("battlePhase_defenders").nextElementSibling.innerHTML = "";
|
||||
|
|
@ -146,19 +168,30 @@ class Battle {
|
|||
<text x="0" y="1.04em" style="">${regiment.icon}</text></svg>`;
|
||||
const body = `<tbody id="battle${state.i}-${regiment.i}">`;
|
||||
|
||||
let initial = `<tr class="battleInitial"><td>${icon}</td><td class="regiment" data-tip="${regiment.name}">${regiment.name.slice(0, 24)}</td>`;
|
||||
let casualties = `<tr class="battleCasualties"><td></td><td data-tip="${state.fullName}">${state.fullName.slice(0, 26)}</td>`;
|
||||
let initial = `<tr class="battleInitial"><td>${icon}</td><td class="regiment" data-tip="${
|
||||
regiment.name
|
||||
}">${regiment.name.slice(0, 24)}</td>`;
|
||||
let casualties = `<tr class="battleCasualties"><td></td><td data-tip="${state.fullName}">${state.fullName.slice(
|
||||
0,
|
||||
26
|
||||
)}</td>`;
|
||||
let survivors = `<tr class="battleSurvivors"><td></td><td data-tip="Supply line length, affects morale">Distance to base: ${distance} ${distanceUnitInput.value}</td>`;
|
||||
|
||||
for (const u of options.military) {
|
||||
initial += `<td data-tip="Initial forces" style="width: 2.5em; text-align: center">${regiment.u[u.name] || 0}</td>`;
|
||||
initial += `<td data-tip="Initial forces" style="width: 2.5em; text-align: center">${
|
||||
regiment.u[u.name] || 0
|
||||
}</td>`;
|
||||
casualties += `<td data-tip="Casualties" style="width: 2.5em; text-align: center; color: red">0</td>`;
|
||||
survivors += `<td data-tip="Survivors" style="width: 2.5em; text-align: center; color: green">${regiment.u[u.name] || 0}</td>`;
|
||||
survivors += `<td data-tip="Survivors" style="width: 2.5em; text-align: center; color: green">${
|
||||
regiment.u[u.name] || 0
|
||||
}</td>`;
|
||||
}
|
||||
|
||||
initial += `<td data-tip="Initial forces" style="width: 2.5em; text-align: center">${regiment.a || 0}</td></tr>`;
|
||||
casualties += `<td data-tip="Casualties" style="width: 2.5em; text-align: center; color: red">0</td></tr>`;
|
||||
survivors += `<td data-tip="Survivors" style="width: 2.5em; text-align: center; color: green">${regiment.a || 0}</td></tr>`;
|
||||
survivors += `<td data-tip="Survivors" style="width: 2.5em; text-align: center; color: green">${
|
||||
regiment.a || 0
|
||||
}</td></tr>`;
|
||||
|
||||
const div = side === "attackers" ? battleAttackers : battleDefenders;
|
||||
div.innerHTML += body + initial + casualties + survivors + "</tbody>";
|
||||
|
|
@ -173,17 +206,23 @@ class Battle {
|
|||
.filter(s => s.military && !s.removed)
|
||||
.map(s => s.military)
|
||||
.flat();
|
||||
const distance = reg => rn(Math.hypot(context.y - reg.y, context.x - reg.x) * distanceScaleInput.value) + " " + distanceUnitInput.value;
|
||||
const isAdded = reg => context.defenders.regiments.some(r => r === reg) || context.attackers.regiments.some(r => r === reg);
|
||||
const distance = reg =>
|
||||
rn(Math.hypot(context.y - reg.y, context.x - reg.x) * distanceScaleInput.value) + " " + distanceUnitInput.value;
|
||||
const isAdded = reg =>
|
||||
context.defenders.regiments.some(r => r === reg) || context.attackers.regiments.some(r => r === reg);
|
||||
|
||||
body.innerHTML = regiments
|
||||
.map(r => {
|
||||
const s = pack.states[r.state],
|
||||
added = isAdded(r),
|
||||
dist = added ? "0 " + distanceUnitInput.value : distance(r);
|
||||
return `<div ${added ? "class='inactive'" : ""} data-s=${s.i} data-i=${r.i} data-state=${s.name} data-regiment=${r.name}
|
||||
return `<div ${added ? "class='inactive'" : ""} data-s=${s.i} data-i=${r.i} data-state=${
|
||||
s.name
|
||||
} data-regiment=${r.name}
|
||||
data-total=${r.a} data-distance=${dist} data-tip="Click to select regiment">
|
||||
<svg width=".9em" height=".9em" style="margin-bottom:-1px; stroke: #333"><rect x="0" y="0" width="100%" height="100%" fill="${s.color}" ></svg>
|
||||
<svg width=".9em" height=".9em" style="margin-bottom:-1px; stroke: #333"><rect x="0" y="0" width="100%" height="100%" fill="${
|
||||
s.color
|
||||
}" ></svg>
|
||||
<div style="width:6em">${s.name.slice(0, 11)}</div>
|
||||
<div style="width:1.2em">${r.icon}</div>
|
||||
<div style="width:13em">${r.name.slice(0, 24)}</div>
|
||||
|
|
@ -267,7 +306,10 @@ class Battle {
|
|||
}
|
||||
|
||||
generateName(type) {
|
||||
const place = type === "culture" ? Names.getCulture(pack.cells.culture[this.cell], null, null, "") : Names.getBase(rand(nameBases.length - 1));
|
||||
const place =
|
||||
type === "culture"
|
||||
? Names.getCulture(pack.cells.culture[this.cell], null, null, "")
|
||||
: Names.getBase(rand(nameBases.length - 1));
|
||||
document.getElementById("battleNamePlace").value = this.place = place;
|
||||
document.getElementById("battleNameFull").value = this.name = this.defineName();
|
||||
$("#battleScreen").dialog({title: this.name});
|
||||
|
|
@ -286,35 +328,161 @@ class Battle {
|
|||
calculateStrength(side) {
|
||||
const scheme = {
|
||||
// field battle phases
|
||||
skirmish: {melee: 0.2, ranged: 2.4, mounted: 0.1, machinery: 3, naval: 1, armored: 0.2, aviation: 1.8, magical: 1.8}, // ranged excel
|
||||
skirmish: {
|
||||
melee: 0.2,
|
||||
ranged: 2.4,
|
||||
mounted: 0.1,
|
||||
machinery: 3,
|
||||
naval: 1,
|
||||
armored: 0.2,
|
||||
aviation: 1.8,
|
||||
magical: 1.8
|
||||
}, // ranged excel
|
||||
melee: {melee: 2, ranged: 1.2, mounted: 1.5, machinery: 0.5, naval: 0.2, armored: 2, aviation: 0.8, magical: 0.8}, // melee excel
|
||||
pursue: {melee: 1, ranged: 1, mounted: 4, machinery: 0.05, naval: 1, armored: 1, aviation: 1.5, magical: 0.6}, // mounted excel
|
||||
retreat: {melee: 0.1, ranged: 0.01, mounted: 0.5, machinery: 0.01, naval: 0.2, armored: 0.1, aviation: 0.8, magical: 0.05}, // reduced
|
||||
retreat: {
|
||||
melee: 0.1,
|
||||
ranged: 0.01,
|
||||
mounted: 0.5,
|
||||
machinery: 0.01,
|
||||
naval: 0.2,
|
||||
armored: 0.1,
|
||||
aviation: 0.8,
|
||||
magical: 0.05
|
||||
}, // reduced
|
||||
|
||||
// naval battle phases
|
||||
shelling: {melee: 0, ranged: 0.2, mounted: 0, machinery: 2, naval: 2, armored: 0, aviation: 0.1, magical: 0.5}, // naval and machinery excel
|
||||
boarding: {melee: 1, ranged: 0.5, mounted: 0.5, machinery: 0, naval: 0.5, armored: 0.4, aviation: 0, magical: 0.2}, // melee excel
|
||||
boarding: {
|
||||
melee: 1,
|
||||
ranged: 0.5,
|
||||
mounted: 0.5,
|
||||
machinery: 0,
|
||||
naval: 0.5,
|
||||
armored: 0.4,
|
||||
aviation: 0,
|
||||
magical: 0.2
|
||||
}, // melee excel
|
||||
chase: {melee: 0, ranged: 0.15, mounted: 0, machinery: 1, naval: 1, armored: 0, aviation: 0.15, magical: 0.5}, // reduced
|
||||
withdrawal: {melee: 0, ranged: 0.02, mounted: 0, machinery: 0.5, naval: 0.1, armored: 0, aviation: 0.1, magical: 0.3}, // reduced
|
||||
withdrawal: {
|
||||
melee: 0,
|
||||
ranged: 0.02,
|
||||
mounted: 0,
|
||||
machinery: 0.5,
|
||||
naval: 0.1,
|
||||
armored: 0,
|
||||
aviation: 0.1,
|
||||
magical: 0.3
|
||||
}, // reduced
|
||||
|
||||
// siege phases
|
||||
blockade: {melee: 0.25, ranged: 0.25, mounted: 0.2, machinery: 0.5, naval: 0.2, armored: 0.1, aviation: 0.25, magical: 0.25}, // no active actions
|
||||
sheltering: {melee: 0.3, ranged: 0.5, mounted: 0.2, machinery: 0.5, naval: 0.2, armored: 0.1, aviation: 0.25, magical: 0.25}, // no active actions
|
||||
blockade: {
|
||||
melee: 0.25,
|
||||
ranged: 0.25,
|
||||
mounted: 0.2,
|
||||
machinery: 0.5,
|
||||
naval: 0.2,
|
||||
armored: 0.1,
|
||||
aviation: 0.25,
|
||||
magical: 0.25
|
||||
}, // no active actions
|
||||
sheltering: {
|
||||
melee: 0.3,
|
||||
ranged: 0.5,
|
||||
mounted: 0.2,
|
||||
machinery: 0.5,
|
||||
naval: 0.2,
|
||||
armored: 0.1,
|
||||
aviation: 0.25,
|
||||
magical: 0.25
|
||||
}, // no active actions
|
||||
sortie: {melee: 2, ranged: 0.5, mounted: 1.2, machinery: 0.2, naval: 0.1, armored: 0.5, aviation: 1, magical: 1}, // melee excel
|
||||
bombardment: {melee: 0.2, ranged: 0.5, mounted: 0.2, machinery: 3, naval: 1, armored: 0.5, aviation: 1, magical: 1}, // machinery excel
|
||||
storming: {melee: 1, ranged: 0.6, mounted: 0.5, machinery: 1, naval: 0.1, armored: 0.1, aviation: 0.5, magical: 0.5}, // melee excel
|
||||
bombardment: {
|
||||
melee: 0.2,
|
||||
ranged: 0.5,
|
||||
mounted: 0.2,
|
||||
machinery: 3,
|
||||
naval: 1,
|
||||
armored: 0.5,
|
||||
aviation: 1,
|
||||
magical: 1
|
||||
}, // machinery excel
|
||||
storming: {
|
||||
melee: 1,
|
||||
ranged: 0.6,
|
||||
mounted: 0.5,
|
||||
machinery: 1,
|
||||
naval: 0.1,
|
||||
armored: 0.1,
|
||||
aviation: 0.5,
|
||||
magical: 0.5
|
||||
}, // melee excel
|
||||
defense: {melee: 2, ranged: 3, mounted: 1, machinery: 1, naval: 0.1, armored: 1, aviation: 0.5, magical: 1}, // ranged excel
|
||||
looting: {melee: 1.6, ranged: 1.6, mounted: 0.5, machinery: 0.2, naval: 0.02, armored: 0.2, aviation: 0.1, magical: 0.3}, // melee excel
|
||||
surrendering: {melee: 0.1, ranged: 0.1, mounted: 0.05, machinery: 0.01, naval: 0.01, armored: 0.02, aviation: 0.01, magical: 0.03}, // reduced
|
||||
looting: {
|
||||
melee: 1.6,
|
||||
ranged: 1.6,
|
||||
mounted: 0.5,
|
||||
machinery: 0.2,
|
||||
naval: 0.02,
|
||||
armored: 0.2,
|
||||
aviation: 0.1,
|
||||
magical: 0.3
|
||||
}, // melee excel
|
||||
surrendering: {
|
||||
melee: 0.1,
|
||||
ranged: 0.1,
|
||||
mounted: 0.05,
|
||||
machinery: 0.01,
|
||||
naval: 0.01,
|
||||
armored: 0.02,
|
||||
aviation: 0.01,
|
||||
magical: 0.03
|
||||
}, // reduced
|
||||
|
||||
// ambush phases
|
||||
surprise: {melee: 2, ranged: 2.4, mounted: 1, machinery: 1, naval: 1, armored: 1, aviation: 0.8, magical: 1.2}, // increased
|
||||
shock: {melee: 0.5, ranged: 0.5, mounted: 0.5, machinery: 0.4, naval: 0.3, armored: 0.1, aviation: 0.4, magical: 0.5}, // reduced
|
||||
shock: {
|
||||
melee: 0.5,
|
||||
ranged: 0.5,
|
||||
mounted: 0.5,
|
||||
machinery: 0.4,
|
||||
naval: 0.3,
|
||||
armored: 0.1,
|
||||
aviation: 0.4,
|
||||
magical: 0.5
|
||||
}, // reduced
|
||||
|
||||
// langing phases
|
||||
landing: {melee: 0.8, ranged: 0.6, mounted: 0.6, machinery: 0.5, naval: 0.5, armored: 0.5, aviation: 0.5, magical: 0.6}, // reduced
|
||||
flee: {melee: 0.1, ranged: 0.01, mounted: 0.5, machinery: 0.01, naval: 0.5, armored: 0.1, aviation: 0.2, magical: 0.05}, // reduced
|
||||
waiting: {melee: 0.05, ranged: 0.5, mounted: 0.05, machinery: 0.5, naval: 2, armored: 0.05, aviation: 0.5, magical: 0.5}, // reduced
|
||||
landing: {
|
||||
melee: 0.8,
|
||||
ranged: 0.6,
|
||||
mounted: 0.6,
|
||||
machinery: 0.5,
|
||||
naval: 0.5,
|
||||
armored: 0.5,
|
||||
aviation: 0.5,
|
||||
magical: 0.6
|
||||
}, // reduced
|
||||
flee: {
|
||||
melee: 0.1,
|
||||
ranged: 0.01,
|
||||
mounted: 0.5,
|
||||
machinery: 0.01,
|
||||
naval: 0.5,
|
||||
armored: 0.1,
|
||||
aviation: 0.2,
|
||||
magical: 0.05
|
||||
}, // reduced
|
||||
waiting: {
|
||||
melee: 0.05,
|
||||
ranged: 0.5,
|
||||
mounted: 0.05,
|
||||
machinery: 0.5,
|
||||
naval: 2,
|
||||
armored: 0.05,
|
||||
aviation: 0.5,
|
||||
magical: 0.5
|
||||
}, // reduced
|
||||
|
||||
// air battle phases
|
||||
maneuvering: {melee: 0, ranged: 0.1, mounted: 0, machinery: 0.2, naval: 0, armored: 0, aviation: 1, magical: 0.2}, // aviation
|
||||
|
|
@ -324,7 +492,8 @@ class Battle {
|
|||
const forces = this.getJoinedForces(this[side].regiments);
|
||||
const phase = this[side].phase;
|
||||
const adjuster = Math.max(populationRate / 10, 10); // population adjuster, by default 100
|
||||
this[side].power = d3.sum(options.military.map(u => (forces[u.name] || 0) * u.power * scheme[phase][u.type])) / adjuster;
|
||||
this[side].power =
|
||||
d3.sum(options.military.map(u => (forces[u.name] || 0) * u.power * scheme[phase][u.type])) / adjuster;
|
||||
const UIvalue = this[side].power ? Math.max(this[side].power | 0, 1) : 0;
|
||||
document.getElementById("battlePower_" + side).innerHTML = UIvalue;
|
||||
}
|
||||
|
|
@ -723,11 +892,13 @@ class Battle {
|
|||
|
||||
const status = battleStatus[+P(0.7)];
|
||||
const result = `The ${this.getTypeName(this.type)} ended in ${status}`;
|
||||
const legend = `${this.name} took place in ${options.year} ${options.eraShort}. It was fought between ${getSide(this.attackers.regiments, 1)} and ${getSide(
|
||||
this.defenders.regiments,
|
||||
0
|
||||
)}. ${result}.
|
||||
\r\nAttackers losses: ${getLosses(this.attackers.casualties)}%, defenders losses: ${getLosses(this.defenders.casualties)}%`;
|
||||
const legend = `${this.name} took place in ${options.year} ${options.eraShort}. It was fought between ${getSide(
|
||||
this.attackers.regiments,
|
||||
1
|
||||
)} and ${getSide(this.defenders.regiments, 0)}. ${result}.
|
||||
\r\nAttackers losses: ${getLosses(this.attackers.casualties)}%, defenders losses: ${getLosses(
|
||||
this.defenders.casualties
|
||||
)}%`;
|
||||
notes.push({id: `marker${i}`, name: this.name, legend});
|
||||
|
||||
tip(`${this.name} is over. ${result}`, true, "success", 4000);
|
||||
|
|
|
|||
|
|
@ -12,8 +12,8 @@ function editBiomes() {
|
|||
const animate = d3.transition().duration(2000).ease(d3.easeSinIn);
|
||||
refreshBiomesEditor();
|
||||
|
||||
if (modules.editBiomes) return;
|
||||
modules.editBiomes = true;
|
||||
if (fmg.modules.editBiomes) return;
|
||||
fmg.modules.editBiomes = true;
|
||||
|
||||
$("#biomesEditor").dialog({
|
||||
title: "Biomes Editor",
|
||||
|
|
@ -88,7 +88,9 @@ function editBiomes() {
|
|||
const rural = b.rural[i] * populationRate;
|
||||
const urban = b.urban[i] * populationRate * urbanization;
|
||||
const population = rn(rural + urban);
|
||||
const populationTip = `Total population: ${si(population)}; Rural population: ${si(rural)}; Urban population: ${si(urban)}`;
|
||||
const populationTip = `Total population: ${si(population)}; Rural population: ${si(
|
||||
rural
|
||||
)}; Urban population: ${si(urban)}`;
|
||||
totalArea += area;
|
||||
totalPopulation += population;
|
||||
|
||||
|
|
@ -104,7 +106,9 @@ function editBiomes() {
|
|||
data-color=${b.color[i]}
|
||||
>
|
||||
<fill-box fill="${b.color[i]}"></fill-box>
|
||||
<input data-tip="Biome name. Click and type to change" class="biomeName" value="${b.name[i]}" autocorrect="off" spellcheck="false" />
|
||||
<input data-tip="Biome name. Click and type to change" class="biomeName" value="${
|
||||
b.name[i]
|
||||
}" autocorrect="off" spellcheck="false" />
|
||||
<span data-tip="Biome habitability percent" class="hide">%</span>
|
||||
<input
|
||||
data-tip="Biome habitability percent. Click and set new value to change"
|
||||
|
|
@ -121,7 +125,11 @@ function editBiomes() {
|
|||
<span data-tip="${populationTip}" class="icon-male hide"></span>
|
||||
<div data-tip="${populationTip}" class="biomePopulation hide">${si(population)}</div>
|
||||
<span data-tip="Open Wikipedia article about the biome" class="icon-info-circled pointer hide"></span>
|
||||
${i > 12 && !b.cells[i] ? '<span data-tip="Remove the custom biome" class="icon-trash-empty hide"></span>' : ""}
|
||||
${
|
||||
i > 12 && !b.cells[i]
|
||||
? '<span data-tip="Remove the custom biome" class="icon-trash-empty hide"></span>'
|
||||
: ""
|
||||
}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
|
@ -403,7 +411,14 @@ function editBiomes() {
|
|||
|
||||
// change of append new element
|
||||
if (exists.size()) exists.attr("data-biome", biomeNew).attr("fill", color).attr("stroke", color);
|
||||
else temp.append("polygon").attr("data-cell", i).attr("data-biome", biomeNew).attr("points", getPackPolygon(i)).attr("fill", color).attr("stroke", color);
|
||||
else
|
||||
temp
|
||||
.append("polygon")
|
||||
.attr("data-cell", i)
|
||||
.attr("data-biome", biomeNew)
|
||||
.attr("points", getPackPolygon(i))
|
||||
.attr("fill", color)
|
||||
.attr("stroke", color);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ function editBurg(id) {
|
|||
position: {my: "left top", at: "left+10 top+10", of: "svg", collision: "fit"}
|
||||
});
|
||||
|
||||
if (modules.editBurg) return;
|
||||
modules.editBurg = true;
|
||||
if (fmg.modules.editBurg) return;
|
||||
fmg.modules.editBurg = true;
|
||||
|
||||
// add listeners
|
||||
document.getElementById("burgGroupShow").addEventListener("click", showGroupSection);
|
||||
|
|
@ -284,7 +284,9 @@ function editBurg(id) {
|
|||
alertMessage.innerHTML = /* html */ `Are you sure you want to remove ${
|
||||
basic || capital ? "all unlocked elements in the burg group" : "the entire burg group"
|
||||
}?
|
||||
<br />Please note that capital or locked burgs will not be deleted. <br /><br />Burgs to be removed: ${burgsToRemove.length}`;
|
||||
<br />Please note that capital or locked burgs will not be deleted. <br /><br />Burgs to be removed: ${
|
||||
burgsToRemove.length
|
||||
}`;
|
||||
$("#alert").dialog({
|
||||
resizable: false,
|
||||
title: "Remove burg group",
|
||||
|
|
@ -433,7 +435,8 @@ function editBurg(id) {
|
|||
function addCustomMfcgLink() {
|
||||
const id = +elSelected.attr("data-id");
|
||||
const burg = pack.burgs[id];
|
||||
const message = "Enter custom link to the burg map. It can be a link to Medieval Fantasy City Generator or other tool. Keep empty to use MFCG seed";
|
||||
const message =
|
||||
"Enter custom link to the burg map. It can be a link to Medieval Fantasy City Generator or other tool. Keep empty to use MFCG seed";
|
||||
prompt(message, {default: burg.link || "", required: false}, link => {
|
||||
if (link) burg.link = link;
|
||||
else delete burg.link;
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ function overviewBurgs() {
|
|||
burgsOverviewAddLines();
|
||||
$("#burgsOverview").dialog();
|
||||
|
||||
if (modules.overviewBurgs) return;
|
||||
modules.overviewBurgs = true;
|
||||
if (fmg.modules.overviewBurgs) return;
|
||||
fmg.modules.overviewBurgs = true;
|
||||
|
||||
$("#burgsOverview").dialog({
|
||||
title: "Burgs Overview",
|
||||
|
|
@ -93,7 +93,9 @@ function overviewBurgs() {
|
|||
data-type="${type}"
|
||||
>
|
||||
<span data-tip="Click to zoom into view" class="icon-dot-circled pointer"></span>
|
||||
<input data-tip="Burg name. Click and type to change" class="burgName" value="${b.name}" autocorrect="off" spellcheck="false" />
|
||||
<input data-tip="Burg name. Click and type to change" class="burgName" value="${
|
||||
b.name
|
||||
}" autocorrect="off" spellcheck="false" />
|
||||
<input data-tip="Burg province" class="burgState" value="${province}" disabled />
|
||||
<input data-tip="Burg state" class="burgState" value="${state}" disabled />
|
||||
<select data-tip="Dominant culture. Click to change burg culture (to change cell culture use Cultures Editor)" class="stateCulture">
|
||||
|
|
@ -106,10 +108,14 @@ function overviewBurgs() {
|
|||
data-tip="${b.capital ? " This burg is a state capital" : "Click to assign a capital status"}"
|
||||
class="icon-star-empty${b.capital ? "" : " inactive pointer"}"
|
||||
></span>
|
||||
<span data-tip="Click to toggle port status" class="icon-anchor pointer${b.port ? "" : " inactive"}" style="font-size:.9em"></span>
|
||||
<span data-tip="Click to toggle port status" class="icon-anchor pointer${
|
||||
b.port ? "" : " inactive"
|
||||
}" style="font-size:.9em"></span>
|
||||
</div>
|
||||
<span data-tip="Edit burg" class="icon-pencil"></span>
|
||||
<span class="locks pointer ${b.lock ? "icon-lock" : "icon-lock-open inactive"}" onmouseover="showElementLockTip(event)"></span>
|
||||
<span class="locks pointer ${
|
||||
b.lock ? "icon-lock" : "icon-lock-open inactive"
|
||||
}" onmouseover="showElementLockTip(event)"></span>
|
||||
<span data-tip="Remove burg" class="icon-trash-empty"></span>
|
||||
</div>`;
|
||||
}
|
||||
|
|
@ -125,8 +131,12 @@ function overviewBurgs() {
|
|||
body.querySelectorAll("div > input.burgName").forEach(el => el.addEventListener("input", changeBurgName));
|
||||
body.querySelectorAll("div > span.icon-dot-circled").forEach(el => el.addEventListener("click", zoomIntoBurg));
|
||||
body.querySelectorAll("div > select.stateCulture").forEach(el => el.addEventListener("change", changeBurgCulture));
|
||||
body.querySelectorAll("div > input.burgPopulation").forEach(el => el.addEventListener("change", changeBurgPopulation));
|
||||
body.querySelectorAll("div > span.icon-star-empty").forEach(el => el.addEventListener("click", toggleCapitalStatus));
|
||||
body
|
||||
.querySelectorAll("div > input.burgPopulation")
|
||||
.forEach(el => el.addEventListener("change", changeBurgPopulation));
|
||||
body
|
||||
.querySelectorAll("div > span.icon-star-empty")
|
||||
.forEach(el => el.addEventListener("click", toggleCapitalStatus));
|
||||
body.querySelectorAll("div > span.icon-anchor").forEach(el => el.addEventListener("click", togglePortStatus));
|
||||
body.querySelectorAll("div > span.locks").forEach(el => el.addEventListener("click", toggleBurgLockStatus));
|
||||
body.querySelectorAll("div > span.icon-pencil").forEach(el => el.addEventListener("click", openBurgEditor));
|
||||
|
|
@ -137,7 +147,9 @@ function overviewBurgs() {
|
|||
|
||||
function getCultureOptions(culture) {
|
||||
let options = "";
|
||||
pack.cultures.filter(c => !c.removed).forEach(c => (options += `<option ${c.i === culture ? "selected" : ""} value="${c.i}">${c.name}</option>`));
|
||||
pack.cultures
|
||||
.filter(c => !c.removed)
|
||||
.forEach(c => (options += `<option ${c.i === culture ? "selected" : ""} value="${c.i}">${c.name}</option>`));
|
||||
return options;
|
||||
}
|
||||
|
||||
|
|
@ -228,7 +240,8 @@ function overviewBurgs() {
|
|||
|
||||
function triggerBurgRemove() {
|
||||
const burg = +this.parentNode.dataset.id;
|
||||
if (pack.burgs[burg].capital) return tip("You cannot remove the capital. Please change the capital first", false, "error");
|
||||
if (pack.burgs[burg].capital)
|
||||
return tip("You cannot remove the capital. Please change the capital first", false, "error");
|
||||
|
||||
confirmationDialog({
|
||||
title: "Remove burg",
|
||||
|
|
@ -266,8 +279,10 @@ function overviewBurgs() {
|
|||
function addBurgOnClick() {
|
||||
const point = d3.mouse(this);
|
||||
const cell = findCell(point[0], point[1]);
|
||||
if (pack.cells.h[cell] < 20) return tip("You cannot place state into the water. Please click on a land cell", false, "error");
|
||||
if (pack.cells.burg[cell]) return tip("There is already a burg in this cell. Please select a free cell", false, "error");
|
||||
if (pack.cells.h[cell] < 20)
|
||||
return tip("You cannot place state into the water. Please click on a land cell", false, "error");
|
||||
if (pack.cells.burg[cell])
|
||||
return tip("There is already a burg in this cell. Please select a free cell", false, "error");
|
||||
|
||||
addBurg(point); // add new burg
|
||||
|
||||
|
|
@ -301,7 +316,19 @@ function overviewBurgs() {
|
|||
const capital = b.capital;
|
||||
const province = pack.cells.province[b.cell];
|
||||
const parent = province ? province + states.length - 1 : b.state;
|
||||
return {id, i: b.i, state: b.state, culture: b.culture, province, parent, name: b.name, population, capital, x: b.x, y: b.y};
|
||||
return {
|
||||
id,
|
||||
i: b.i,
|
||||
state: b.state,
|
||||
culture: b.culture,
|
||||
province,
|
||||
parent,
|
||||
name: b.name,
|
||||
population,
|
||||
capital,
|
||||
x: b.x,
|
||||
y: b.y
|
||||
};
|
||||
});
|
||||
const data = states.concat(burgs);
|
||||
if (data.length < 2) return tip("No burgs to show", false, "error");
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ function editCoastline(node = d3.event.target) {
|
|||
drawCoastlineVertices();
|
||||
viewbox.on("touchmove mousemove", null);
|
||||
|
||||
if (modules.editCoastline) return;
|
||||
modules.editCoastline = true;
|
||||
if (fmg.modules.editCoastline) return;
|
||||
fmg.modules.editCoastline = true;
|
||||
|
||||
// add listeners
|
||||
document.getElementById("coastlineGroupsShow").addEventListener("click", showGroupSection);
|
||||
|
|
@ -55,7 +55,9 @@ function editCoastline(node = d3.event.target) {
|
|||
.attr("r", 0.4)
|
||||
.attr("data-v", d => d)
|
||||
.call(d3.drag().on("drag", dragVertex))
|
||||
.on("mousemove", () => tip("Drag to move the vertex, please use for fine-tuning only. Edit heightmap to change actual cell heights"));
|
||||
.on("mousemove", () =>
|
||||
tip("Drag to move the vertex, please use for fine-tuning only. Edit heightmap to change actual cell heights")
|
||||
);
|
||||
|
||||
const area = pack.features[f].area;
|
||||
coastlineArea.innerHTML = si(getArea(area)) + " " + getAreaUnit();
|
||||
|
|
|
|||
|
|
@ -57,8 +57,8 @@ function editDiplomacy() {
|
|||
refreshDiplomacyEditor();
|
||||
viewbox.style("cursor", "crosshair").on("click", selectStateOnMapClick);
|
||||
|
||||
if (modules.editDiplomacy) return;
|
||||
modules.editDiplomacy = true;
|
||||
if (fmg.modules.editDiplomacy) return;
|
||||
fmg.modules.editDiplomacy = true;
|
||||
|
||||
$("#diplomacyEditor").dialog({
|
||||
title: "Diplomacy Editor",
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
// module stub to store common functions for ui editors
|
||||
"use strict";
|
||||
|
||||
modules.editors = true;
|
||||
|
||||
// restore default viewbox events
|
||||
function restoreDefaultEvents() {
|
||||
svg.call(zoom);
|
||||
|
|
|
|||
|
|
@ -194,7 +194,14 @@ function showElevationProfile(data, routeLen, isRiver) {
|
|||
.attr("fill", "darkgray");
|
||||
|
||||
let colors = getColorScheme(terrs.attr("scheme"));
|
||||
const landdef = chart.select("defs").append("linearGradient").attr("id", "landdef").attr("x1", "0%").attr("y1", "0%").attr("x2", "0%").attr("y2", "100%");
|
||||
const landdef = chart
|
||||
.select("defs")
|
||||
.append("linearGradient")
|
||||
.attr("id", "landdef")
|
||||
.attr("x1", "0%")
|
||||
.attr("y1", "0%")
|
||||
.attr("x2", "0%")
|
||||
.attr("y2", "100%");
|
||||
|
||||
if (chartData.mah == chartData.mih) {
|
||||
landdef
|
||||
|
|
@ -247,7 +254,14 @@ function showElevationProfile(data, routeLen, isRiver) {
|
|||
path += " L" + parseInt(xscale(extra.length) + +xOffset) + "," + parseInt(yscale(0) + +yOffset);
|
||||
path += " L" + parseInt(xscale(0) + +xOffset) + "," + parseInt(yscale(0) + +yOffset);
|
||||
path += "Z";
|
||||
chart.append("g").attr("id", "epland").append("path").attr("d", path).attr("stroke", "purple").attr("stroke-width", "0").attr("fill", "url(#landdef)");
|
||||
chart
|
||||
.append("g")
|
||||
.attr("id", "epland")
|
||||
.append("path")
|
||||
.attr("d", path)
|
||||
.attr("stroke", "purple")
|
||||
.attr("stroke-width", "0")
|
||||
.attr("fill", "url(#landdef)");
|
||||
|
||||
// biome / heights
|
||||
let g = chart.append("g").attr("id", "epbiomes");
|
||||
|
|
@ -289,7 +303,14 @@ function showElevationProfile(data, routeLen, isRiver) {
|
|||
chartData.cell[k] +
|
||||
")";
|
||||
|
||||
g.append("rect").attr("stroke", c).attr("fill", c).attr("x", x).attr("y", y).attr("width", xscale(1)).attr("height", 15).attr("data-tip", dataTip);
|
||||
g.append("rect")
|
||||
.attr("stroke", c)
|
||||
.attr("fill", c)
|
||||
.attr("x", x)
|
||||
.attr("y", y)
|
||||
.attr("width", xscale(1))
|
||||
.attr("height", 15)
|
||||
.attr("data-tip", dataTip);
|
||||
}
|
||||
|
||||
const xAxis = d3
|
||||
|
|
@ -371,7 +392,17 @@ function showElevationProfile(data, routeLen, isRiver) {
|
|||
// arrow from burg name to graph line
|
||||
g.append("path")
|
||||
.attr("id", "eparrow" + b)
|
||||
.attr("d", "M" + x1.toString() + "," + (y1 + 3).toString() + "L" + x1.toString() + "," + parseInt(chartData.points[k][1] - 3).toString())
|
||||
.attr(
|
||||
"d",
|
||||
"M" +
|
||||
x1.toString() +
|
||||
"," +
|
||||
(y1 + 3).toString() +
|
||||
"L" +
|
||||
x1.toString() +
|
||||
"," +
|
||||
parseInt(chartData.points[k][1] - 3).toString()
|
||||
)
|
||||
.attr("stroke", "darkgray")
|
||||
.attr("fill", "lightgray")
|
||||
.attr("stroke-width", "1")
|
||||
|
|
@ -385,6 +416,6 @@ function showElevationProfile(data, routeLen, isRiver) {
|
|||
document.getElementById("epCurve").removeEventListener("change", draw);
|
||||
document.getElementById("epSave").removeEventListener("click", downloadCSV);
|
||||
document.getElementById("elevationGraph").innerHTML = "";
|
||||
modules.elevation = false;
|
||||
fmg.modules.elevation = false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
"use strict";
|
||||
// Module to store general UI functions
|
||||
import {findCell} from "/src/utils/graphUtils";
|
||||
import {MOBILE} from "/src/constants";
|
||||
|
||||
// fit full-screen map if window is resized
|
||||
window.addEventListener("resize", function (e) {
|
||||
|
|
@ -431,7 +431,7 @@ document.querySelectorAll("[data-locked]").forEach(function (e) {
|
|||
});
|
||||
|
||||
// lock option
|
||||
function lock(id) {
|
||||
export function lock(id) {
|
||||
const input = document.querySelector('[data-stored="' + id + '"]');
|
||||
if (input) store(id, input.value);
|
||||
const el = document.getElementById("lock_" + id);
|
||||
|
|
@ -450,13 +450,13 @@ function unlock(id) {
|
|||
}
|
||||
|
||||
// check if option is locked
|
||||
function locked(id) {
|
||||
export function locked(id) {
|
||||
const lockEl = document.getElementById("lock_" + id);
|
||||
return lockEl.dataset.locked == 1;
|
||||
}
|
||||
|
||||
// return key value stored in localStorage or null
|
||||
function stored(key) {
|
||||
export function stored(key) {
|
||||
return localStorage.getItem(key) || null;
|
||||
}
|
||||
|
||||
|
|
@ -482,7 +482,7 @@ function speak(text) {
|
|||
}
|
||||
|
||||
// apply drop-down menu option. If the value is not in options, add it
|
||||
function applyOption($select, value, name = value) {
|
||||
export function applyOption($select, value, name = value) {
|
||||
const isExisting = Array.from($select.options).some(o => o.value === value);
|
||||
if (!isExisting) $select.options.add(new Option(name, value));
|
||||
$select.value = value;
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ function editHeightmap(options) {
|
|||
if (!mode) showModeDialog();
|
||||
else enterHeightmapEditMode(mode);
|
||||
|
||||
if (modules.editHeightmap) return;
|
||||
modules.editHeightmap = true;
|
||||
if (fmg.modules.editHeightmap) return;
|
||||
fmg.modules.editHeightmap = true;
|
||||
|
||||
// add listeners
|
||||
byId("paintBrushes").on("click", openBrushesPanel);
|
||||
|
|
@ -29,7 +29,10 @@ function editHeightmap(options) {
|
|||
<p>You can <i>keep</i> the data, but you won't be able to change the coastline.</p>
|
||||
<p>Try <i>risk</i> mode to change the coastline and keep the data. The data will be restored as much as possible, but it can cause unpredictable errors.</p>
|
||||
<p>Please <span class="pseudoLink" onclick="dowloadMap();">save the map</span> before editing the heightmap!</p>
|
||||
<p style="margin-bottom: 0">Check out ${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Heightmap-customization", "wiki")} for guidance.</p>`;
|
||||
<p style="margin-bottom: 0">Check out ${link(
|
||||
"https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Heightmap-customization",
|
||||
"wiki"
|
||||
)} for guidance.</p>`;
|
||||
|
||||
$("#alert").dialog({
|
||||
resizable: false,
|
||||
|
|
@ -148,7 +151,11 @@ function editHeightmap(options) {
|
|||
// Exit customization mode
|
||||
function finalizeHeightmap() {
|
||||
if (viewbox.select("#heights").selectAll("*").size() < 200)
|
||||
return tip("Insufficient land area! There should be at least 200 land cells to finalize the heightmap", null, "error");
|
||||
return tip(
|
||||
"Insufficient land area! There should be at least 200 land cells to finalize the heightmap",
|
||||
null,
|
||||
"error"
|
||||
);
|
||||
if (byId("imageConverter").offsetParent) return tip("Please exit the Image Conversion mode first", null, "error");
|
||||
|
||||
delete window.edits; // remove global variable
|
||||
|
|
@ -210,7 +217,8 @@ function editHeightmap(options) {
|
|||
if (!erosionAllowed) {
|
||||
for (const i of pack.cells.i) {
|
||||
const g = pack.cells.g[i];
|
||||
if (pack.cells.h[i] !== grid.cells.h[g] && pack.cells.h[i] >= 20 === grid.cells.h[g] >= 20) pack.cells.h[i] = grid.cells.h[g];
|
||||
if (pack.cells.h[i] !== grid.cells.h[g] && pack.cells.h[i] >= 20 === grid.cells.h[g] >= 20)
|
||||
pack.cells.h[i] = grid.cells.h[g];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -349,7 +357,8 @@ function editHeightmap(options) {
|
|||
const isLand = pack.cells.h[i] >= 20;
|
||||
|
||||
// check biome
|
||||
pack.cells.biome[i] = isLand && biome[g] ? biome[g] : getBiomeId(grid.cells.prec[g], grid.cells.temp[g], pack.cells.h[i]);
|
||||
pack.cells.biome[i] =
|
||||
isLand && biome[g] ? biome[g] : getBiomeId(grid.cells.prec[g], grid.cells.temp[g], pack.cells.h[i]);
|
||||
|
||||
// rivers data
|
||||
if (!erosionAllowed) {
|
||||
|
|
@ -373,7 +382,9 @@ function editHeightmap(options) {
|
|||
const findBurgCell = function (x, y) {
|
||||
let i = findCell(x, y);
|
||||
if (pack.cells.h[i] >= 20) return i;
|
||||
const dist = pack.cells.c[i].map(c => (pack.cells.h[c] < 20 ? Infinity : (pack.cells.p[c][0] - x) ** 2 + (pack.cells.p[c][1] - y) ** 2));
|
||||
const dist = pack.cells.c[i].map(c =>
|
||||
pack.cells.h[c] < 20 ? Infinity : (pack.cells.p[c][0] - x) ** 2 + (pack.cells.p[c][1] - y) ** 2
|
||||
);
|
||||
return pack.cells.c[i][d3.scan(dist)];
|
||||
};
|
||||
|
||||
|
|
@ -551,8 +562,8 @@ function editHeightmap(options) {
|
|||
})
|
||||
.on("dialogclose", exitBrushMode);
|
||||
|
||||
if (modules.openBrushesPanel) return;
|
||||
modules.openBrushesPanel = true;
|
||||
if (fmg.modules.openBrushesPanel) return;
|
||||
fmg.modules.openBrushesPanel = true;
|
||||
|
||||
// add listeners
|
||||
byId("brushesButtons").on("click", e => toggleBrushMode(e));
|
||||
|
|
@ -630,15 +641,25 @@ function editHeightmap(options) {
|
|||
|
||||
const brush = document.querySelector("#brushesButtons > button.pressed").id;
|
||||
if (brush === "brushRaise") s.forEach(i => (h[i] = h[i] < 20 ? 20 : lim(h[i] + power)));
|
||||
else if (brush === "brushElevate") s.forEach((i, d) => (h[i] = lim(h[i] + interpolate(d / Math.max(s.length - 1, 1)))));
|
||||
else if (brush === "brushElevate")
|
||||
s.forEach((i, d) => (h[i] = lim(h[i] + interpolate(d / Math.max(s.length - 1, 1)))));
|
||||
else if (brush === "brushLower") s.forEach(i => (h[i] = lim(h[i] - power)));
|
||||
else if (brush === "brushDepress") s.forEach((i, d) => (h[i] = lim(h[i] - interpolate(d / Math.max(s.length - 1, 1)))));
|
||||
else if (brush === "brushDepress")
|
||||
s.forEach((i, d) => (h[i] = lim(h[i] - interpolate(d / Math.max(s.length - 1, 1)))));
|
||||
else if (brush === "brushAlign") s.forEach(i => (h[i] = lim(h[start])));
|
||||
else if (brush === "brushSmooth")
|
||||
s.forEach(
|
||||
i => (h[i] = rn((d3.mean(grid.cells.c[i].filter(i => (land ? h[i] >= 20 : 1)).map(c => h[c])) + h[i] * (10 - power) + 0.6) / (11 - power), 1))
|
||||
i =>
|
||||
(h[i] = rn(
|
||||
(d3.mean(grid.cells.c[i].filter(i => (land ? h[i] >= 20 : 1)).map(c => h[c])) +
|
||||
h[i] * (10 - power) +
|
||||
0.6) /
|
||||
(11 - power),
|
||||
1
|
||||
))
|
||||
);
|
||||
else if (brush === "brushDisrupt") s.forEach(i => (h[i] = h[i] < 15 ? h[i] : lim(h[i] + power / 1.6 - Math.random() * power)));
|
||||
else if (brush === "brushDisrupt")
|
||||
s.forEach(i => (h[i] = h[i] < 15 ? h[i] : lim(h[i] + power / 1.6 - Math.random() * power)));
|
||||
|
||||
mockHeightmapSelection(s);
|
||||
// updateHistory(); uncomment to update history every step
|
||||
|
|
@ -662,7 +683,8 @@ function editHeightmap(options) {
|
|||
const operator = conditionSign.value;
|
||||
const operand = rescaleModifier.valueAsNumber;
|
||||
if (Number.isNaN(operand)) return tip("Operand should be a number", false, "error");
|
||||
if ((operator === "add" || operator === "subtract") && !Number.isInteger(operand)) return tip("Operand should be an integer", false, "error");
|
||||
if ((operator === "add" || operator === "subtract") && !Number.isInteger(operand))
|
||||
return tip("Operand should be an integer", false, "error");
|
||||
|
||||
HeightmapGenerator.setGraph(grid);
|
||||
|
||||
|
|
@ -691,7 +713,8 @@ function editHeightmap(options) {
|
|||
function startFromScratch() {
|
||||
if (changeOnlyLand.checked) return tip("Not allowed when 'Change only land cells' mode is set", false, "error");
|
||||
const someHeights = grid.cells.h.some(h => h);
|
||||
if (!someHeights) return tip("Heightmap is already cleared, please do not click twice if not required", false, "error");
|
||||
if (!someHeights)
|
||||
return tip("Heightmap is already cleared, please do not click twice if not required", false, "error");
|
||||
|
||||
grid.cells.h = new Uint8Array(grid.cells.i.length);
|
||||
viewbox.select("#heights").selectAll("*").remove();
|
||||
|
|
@ -711,10 +734,15 @@ function editHeightmap(options) {
|
|||
position: {my: "right top", at: "right-10 top+10", of: "svg"}
|
||||
});
|
||||
|
||||
if (modules.openTemplateEditor) return;
|
||||
modules.openTemplateEditor = true;
|
||||
if (fmg.modules.openTemplateEditor) return;
|
||||
fmg.modules.openTemplateEditor = true;
|
||||
|
||||
$("#templateBody").sortable({items: "> div", handle: ".icon-resize-vertical", containment: "#templateBody", axis: "y"});
|
||||
$("#templateBody").sortable({
|
||||
items: "> div",
|
||||
handle: ".icon-resize-vertical",
|
||||
containment: "#templateBody",
|
||||
axis: "y"
|
||||
});
|
||||
|
||||
// add listeners
|
||||
$body.on("click", function (ev) {
|
||||
|
|
@ -788,22 +816,31 @@ function editHeightmap(options) {
|
|||
const common = /* html */ `<div data-type="${type}">${Hide}<div style="width:4em">${type}</div>${Trash}${Reorder}`;
|
||||
|
||||
const TempY = /* html */ `<span>y:
|
||||
<input class="templateY" data-tip="Placement range percentage along Y axis (minY-maxY)" value=${arg5 || "20-80"} />
|
||||
<input class="templateY" data-tip="Placement range percentage along Y axis (minY-maxY)" value=${
|
||||
arg5 || "20-80"
|
||||
} />
|
||||
</span>`;
|
||||
|
||||
const TempX = /* html */ `<span>x:
|
||||
<input class="templateX" data-tip="Placement range percentage along X axis (minX-maxX)" value=${arg4 || "15-85"} />
|
||||
<input class="templateX" data-tip="Placement range percentage along X axis (minX-maxX)" value=${
|
||||
arg4 || "15-85"
|
||||
} />
|
||||
</span>`;
|
||||
|
||||
const Height = /* html */ `<span>h:
|
||||
<input class="templateHeight" data-tip="Blob maximum height, use hyphen to get a random number in range" value=${arg3 || "40-50"} />
|
||||
<input class="templateHeight" data-tip="Blob maximum height, use hyphen to get a random number in range" value=${
|
||||
arg3 || "40-50"
|
||||
} />
|
||||
</span>`;
|
||||
|
||||
const Count = /* html */ `<span>n:
|
||||
<input class="templateCount" data-tip="Blobs to add, use hyphen to get a random number in range" value=${count || "1-2"} />
|
||||
<input class="templateCount" data-tip="Blobs to add, use hyphen to get a random number in range" value=${
|
||||
count || "1-2"
|
||||
} />
|
||||
</span>`;
|
||||
|
||||
if (type === "Hill" || type === "Pit" || type === "Range" || type === "Trough") return /* html */ `${common}${TempY}${TempX}${Height}${Count}</div>`;
|
||||
if (type === "Hill" || type === "Pit" || type === "Range" || type === "Trough")
|
||||
return /* html */ `${common}${TempY}${TempX}${Height}${Count}</div>`;
|
||||
|
||||
if (type === "Strait")
|
||||
return /* html */ `${common}
|
||||
|
|
@ -814,7 +851,9 @@ function editHeightmap(options) {
|
|||
</select>
|
||||
</span>
|
||||
<span>w:
|
||||
<input class="templateCount" data-tip="Strait width, use hyphen to get a random number in range" value=${count || "2-7"} />
|
||||
<input class="templateCount" data-tip="Strait width, use hyphen to get a random number in range" value=${
|
||||
count || "2-7"
|
||||
} />
|
||||
</span>
|
||||
</div>`;
|
||||
|
||||
|
|
@ -1042,8 +1081,8 @@ function editHeightmap(options) {
|
|||
viewbox.select("#heights").selectAll("*").remove();
|
||||
updateHistory();
|
||||
|
||||
if (modules.openImageConverter) return;
|
||||
modules.openImageConverter = true;
|
||||
if (fmg.modules.openImageConverter) return;
|
||||
fmg.modules.openImageConverter = true;
|
||||
|
||||
// add color pallete
|
||||
void (function createColorPallete() {
|
||||
|
|
@ -1245,7 +1284,8 @@ function editHeightmap(options) {
|
|||
const assinged = []; // store assigned heights
|
||||
unassigned.forEach(el => {
|
||||
const clr = el.dataset.color;
|
||||
const height = type === "hue" ? getHeightByHue(clr) : type === "lum" ? getHeightByLum(clr) : getHeightByScheme(clr);
|
||||
const height =
|
||||
type === "hue" ? getHeightByHue(clr) : type === "lum" ? getHeightByLum(clr) : getHeightByScheme(clr);
|
||||
const colorTo = color(1 - (height < 20 ? (height - 5) / 100 : height / 100));
|
||||
viewbox
|
||||
.select("#heights")
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ function handleKeydown(event) {
|
|||
}
|
||||
|
||||
function handleKeyup(event) {
|
||||
if (!modules.editors) return; // if editors are not loaded, do nothing
|
||||
if (!allowHotkeys()) return; // in some cases (e.g. in a textarea) hotkeys are not allowed
|
||||
|
||||
event.stopPropagation();
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ function editIce() {
|
|||
close: closeEditor
|
||||
});
|
||||
|
||||
if (modules.editIce) return;
|
||||
modules.editIce = true;
|
||||
if (fmg.modules.editIce) return;
|
||||
fmg.modules.editIce = true;
|
||||
|
||||
// add listeners
|
||||
document.getElementById("iceEditStyle").addEventListener("click", () => editStyle("ice"));
|
||||
|
|
|
|||
|
|
@ -22,8 +22,8 @@ function editLabel() {
|
|||
selectLabelGroup(text);
|
||||
updateValues(textPath);
|
||||
|
||||
if (modules.editLabel) return;
|
||||
modules.editLabel = true;
|
||||
if (fmg.modules.editLabel) return;
|
||||
fmg.modules.editLabel = true;
|
||||
|
||||
// add listeners
|
||||
document.getElementById("labelGroupShow").addEventListener("click", showGroupSection);
|
||||
|
|
@ -78,7 +78,9 @@ function editLabel() {
|
|||
}
|
||||
|
||||
function updateValues(textPath) {
|
||||
document.getElementById("labelText").value = [...textPath.querySelectorAll("tspan")].map(tspan => tspan.textContent).join("|");
|
||||
document.getElementById("labelText").value = [...textPath.querySelectorAll("tspan")]
|
||||
.map(tspan => tspan.textContent)
|
||||
.join("|");
|
||||
document.getElementById("labelStartOffset").value = parseFloat(textPath.getAttribute("startOffset"));
|
||||
document.getElementById("labelRelativeSize").value = parseFloat(textPath.getAttribute("font-size"));
|
||||
}
|
||||
|
|
@ -298,7 +300,13 @@ function editLabel() {
|
|||
function changeText() {
|
||||
const input = document.getElementById("labelText").value;
|
||||
const el = elSelected.select("textPath").node();
|
||||
const example = d3.select(elSelected.node().parentNode).append("text").attr("x", 0).attr("x", 0).attr("font-size", el.getAttribute("font-size")).node();
|
||||
const example = d3
|
||||
.select(elSelected.node().parentNode)
|
||||
.append("text")
|
||||
.attr("x", 0)
|
||||
.attr("x", 0)
|
||||
.attr("font-size", el.getAttribute("font-size"))
|
||||
.node();
|
||||
|
||||
const lines = input.split("|");
|
||||
const top = (lines.length - 1) / -2; // y offset
|
||||
|
|
@ -313,7 +321,8 @@ function editLabel() {
|
|||
el.innerHTML = inner;
|
||||
example.remove();
|
||||
|
||||
if (elSelected.attr("id").slice(0, 10) === "stateLabel") tip("Use States Editor to change an actual state name, not just a label", false, "warning");
|
||||
if (elSelected.attr("id").slice(0, 10) === "stateLabel")
|
||||
tip("Use States Editor to change an actual state name, not just a label", false, "warning");
|
||||
}
|
||||
|
||||
function generateRandomName() {
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ function editLake() {
|
|||
drawLakeVertices();
|
||||
viewbox.on("touchmove mousemove", null);
|
||||
|
||||
if (modules.editLake) return;
|
||||
modules.editLake = true;
|
||||
if (fmg.modules.editLake) return;
|
||||
fmg.modules.editLake = true;
|
||||
|
||||
// add listeners
|
||||
document.getElementById("lakeName").addEventListener("input", changeName);
|
||||
|
|
@ -48,7 +48,8 @@ function editLake() {
|
|||
document.getElementById("lakeArea").value = si(getArea(l.area)) + " " + getAreaUnit();
|
||||
|
||||
const length = d3.polygonLength(l.vertices.map(v => pack.vertices.p[v]));
|
||||
document.getElementById("lakeShoreLength").value = si(length * distanceScaleInput.value) + " " + distanceUnitInput.value;
|
||||
document.getElementById("lakeShoreLength").value =
|
||||
si(length * distanceScaleInput.value) + " " + distanceUnitInput.value;
|
||||
|
||||
const lakeCells = Array.from(cells.i.filter(i => cells.f[i] === l.i));
|
||||
const heights = lakeCells.map(i => cells.h[i]);
|
||||
|
|
@ -91,7 +92,9 @@ function editLake() {
|
|||
.attr("r", 0.4)
|
||||
.attr("data-v", d => d)
|
||||
.call(d3.drag().on("drag", dragVertex))
|
||||
.on("mousemove", () => tip("Drag to move the vertex, please use for fine-tuning only. Edit heightmap to change actual cell heights"));
|
||||
.on("mousemove", () =>
|
||||
tip("Drag to move the vertex, please use for fine-tuning only. Edit heightmap to change actual cell heights")
|
||||
);
|
||||
}
|
||||
|
||||
function dragVertex() {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// UI module stub to control map layers
|
||||
"use strict";
|
||||
import {TIME} from "/src/config/logging";
|
||||
import {invokeActiveZooming} from "../activeZooming";
|
||||
|
||||
let presets = {}; // global object
|
||||
restoreCustomPresets(); // run on-load
|
||||
|
|
@ -946,7 +946,7 @@ function toggleStates(event) {
|
|||
}
|
||||
}
|
||||
|
||||
function drawStates() {
|
||||
export function drawStates() {
|
||||
TIME && console.time("drawStates");
|
||||
regions.selectAll("path").remove();
|
||||
|
||||
|
|
@ -1110,7 +1110,7 @@ function toggleBorders(event) {
|
|||
}
|
||||
|
||||
// draw state and province borders
|
||||
function drawBorders() {
|
||||
export function drawBorders() {
|
||||
TIME && console.time("drawBorders");
|
||||
borders.selectAll("path").remove();
|
||||
|
||||
|
|
@ -1554,7 +1554,7 @@ function toggleRivers(event) {
|
|||
}
|
||||
}
|
||||
|
||||
function drawRivers() {
|
||||
export function drawRivers() {
|
||||
TIME && console.time("drawRivers");
|
||||
rivers.selectAll("*").remove();
|
||||
|
||||
|
|
@ -1870,7 +1870,7 @@ function drawEmblems() {
|
|||
TIME && console.timeEnd("drawEmblems");
|
||||
}
|
||||
|
||||
function layerIsOn(el) {
|
||||
export function layerIsOn(el) {
|
||||
const buttonoff = document.getElementById(el).classList.contains("buttonoff");
|
||||
return !buttonoff;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,8 +10,8 @@ function overviewMilitary() {
|
|||
addLines();
|
||||
$("#militaryOverview").dialog();
|
||||
|
||||
if (modules.overviewMilitary) return;
|
||||
modules.overviewMilitary = true;
|
||||
if (fmg.modules.overviewMilitary) return;
|
||||
fmg.modules.overviewMilitary = true;
|
||||
updateHeaders();
|
||||
|
||||
$("#militaryOverview").dialog({
|
||||
|
|
@ -54,7 +54,9 @@ function overviewMilitary() {
|
|||
const insert = html => document.getElementById("militaryTotal").insertAdjacentHTML("beforebegin", html);
|
||||
for (const u of options.military) {
|
||||
const label = capitalize(u.name.replace(/_/g, " "));
|
||||
insert(`<div data-tip="State ${u.name} units number. Click to sort" class="sortable removable" data-sortby="${u.name}">${label} </div>`);
|
||||
insert(
|
||||
`<div data-tip="State ${u.name} units number. Click to sort" class="sortable removable" data-sortby="${u.name}">${label} </div>`
|
||||
);
|
||||
}
|
||||
header.querySelectorAll(".removable").forEach(function (e) {
|
||||
e.addEventListener("click", function () {
|
||||
|
|
@ -76,7 +78,9 @@ function overviewMilitary() {
|
|||
const rate = (total / population) * 100;
|
||||
|
||||
const sortData = options.military.map(u => `data-${u.name}="${getForces(u)}"`).join(" ");
|
||||
const lineData = options.military.map(u => `<div data-type="${u.name}" data-tip="State ${u.name} units number">${getForces(u)}</div>`).join(" ");
|
||||
const lineData = options.military
|
||||
.map(u => `<div data-type="${u.name}" data-tip="State ${u.name} units number">${getForces(u)}</div>`)
|
||||
.join(" ");
|
||||
|
||||
lines += /* html */ `<div
|
||||
class="states"
|
||||
|
|
@ -91,9 +95,14 @@ function overviewMilitary() {
|
|||
<fill-box data-tip="${s.fullName}" fill="${s.color}" disabled></fill-box>
|
||||
<input data-tip="${s.fullName}" style="width:6em" value="${s.name}" readonly />
|
||||
${lineData}
|
||||
<div data-type="total" data-tip="Total state military personnel (considering crew)" style="font-weight: bold">${si(total)}</div>
|
||||
<div data-type="total" data-tip="Total state military personnel (considering crew)" style="font-weight: bold">${si(
|
||||
total
|
||||
)}</div>
|
||||
<div data-type="population" data-tip="State population">${si(population)}</div>
|
||||
<div data-type="rate" data-tip="Military personnel rate (% of state population). Depends on war alert">${rn(rate, 2)}%</div>
|
||||
<div data-type="rate" data-tip="Military personnel rate (% of state population). Depends on war alert">${rn(
|
||||
rate,
|
||||
2
|
||||
)}%</div>
|
||||
<input
|
||||
data-tip="War Alert. Editable modifier to military forces number, depends of political situation"
|
||||
style="width:4.1em"
|
||||
|
|
@ -131,7 +140,9 @@ function overviewMilitary() {
|
|||
});
|
||||
|
||||
const getForces = u => s.military.reduce((s, r) => s + (r.u[u.name] || 0), 0);
|
||||
options.military.forEach(u => (line.dataset[u.name] = line.querySelector(`div[data-type='${u.name}']`).innerHTML = getForces(u)));
|
||||
options.military.forEach(
|
||||
u => (line.dataset[u.name] = line.querySelector(`div[data-type='${u.name}']`).innerHTML = getForces(u))
|
||||
);
|
||||
|
||||
const population = rn((s.rural + s.urban * urbanization) * populationRate);
|
||||
const total = (line.dataset.total = options.military.reduce((s, u) => s + getForces(u) * u.crew, 0));
|
||||
|
|
@ -237,7 +248,16 @@ function overviewMilitary() {
|
|||
position: {my: "center", at: "center", of: "svg"},
|
||||
buttons: {
|
||||
Apply: applyMilitaryOptions,
|
||||
Add: () => addUnitLine({icon: "🛡️", name: "custom" + militaryOptionsTable.rows.length, rural: 0.2, urban: 0.5, crew: 1, power: 1, type: "melee"}),
|
||||
Add: () =>
|
||||
addUnitLine({
|
||||
icon: "🛡️",
|
||||
name: "custom" + militaryOptionsTable.rows.length,
|
||||
rural: 0.2,
|
||||
urban: 0.5,
|
||||
crew: 1,
|
||||
power: 1,
|
||||
type: "melee"
|
||||
}),
|
||||
Restore: restoreDefaultUnits,
|
||||
Cancel: function () {
|
||||
$(this).dialog("close");
|
||||
|
|
@ -254,8 +274,8 @@ function overviewMilitary() {
|
|||
}
|
||||
});
|
||||
|
||||
if (modules.overviewMilitaryCustomize) return;
|
||||
modules.overviewMilitaryCustomize = true;
|
||||
if (fmg.modules.overviewMilitaryCustomize) return;
|
||||
fmg.modules.overviewMilitaryCustomize = true;
|
||||
|
||||
tableBody.addEventListener("click", event => {
|
||||
const el = event.target;
|
||||
|
|
@ -294,7 +314,9 @@ function overviewMilitary() {
|
|||
function addUnitLine(unit) {
|
||||
const {type, icon, name, rural, urban, power, crew, separate} = unit;
|
||||
const row = document.createElement("tr");
|
||||
const typeOptions = types.map(t => `<option ${type === t ? "selected" : ""} value="${t}">${t}</option>`).join(" ");
|
||||
const typeOptions = types
|
||||
.map(t => `<option ${type === t ? "selected" : ""} value="${t}">${t}</option>`)
|
||||
.join(" ");
|
||||
|
||||
const getLimitButton = attr =>
|
||||
`<button
|
||||
|
|
@ -305,7 +327,9 @@ function overviewMilitary() {
|
|||
${getLimitText(unit[attr])}
|
||||
</button>`;
|
||||
|
||||
row.innerHTML = /* html */ `<td><button data-type="icon" data-tip="Click to select unit icon">${icon || " "}</button></td>
|
||||
row.innerHTML = /* html */ `<td><button data-type="icon" data-tip="Click to select unit icon">${
|
||||
icon || " "
|
||||
}</button></td>
|
||||
<td><input data-tip="Type unit name. If name is changed for existing unit, old unit will be replaced" value="${name}" /></td>
|
||||
<td>${getLimitButton("biomes")}</td>
|
||||
<td>${getLimitButton("states")}</td>
|
||||
|
|
@ -344,7 +368,9 @@ function overviewMilitary() {
|
|||
const lines = filtered.map(
|
||||
({i, name, fullName, color}) =>
|
||||
`<tr data-tip="${name}"><td><span style="color:${color}">⬤</span></td>
|
||||
<td><input data-i="${i}" id="el${i}" type="checkbox" class="checkbox" ${!initial.length || initial.includes(i) ? "checked" : ""} >
|
||||
<td><input data-i="${i}" id="el${i}" type="checkbox" class="checkbox" ${
|
||||
!initial.length || initial.includes(i) ? "checked" : ""
|
||||
} >
|
||||
<label for="el${i}" class="checkbox-label">${fullName || name}</label>
|
||||
</td></tr>`
|
||||
);
|
||||
|
|
@ -395,14 +421,15 @@ function overviewMilitary() {
|
|||
$("#militaryOptions").dialog("close");
|
||||
options.military = unitLines.map((r, i) => {
|
||||
const elements = Array.from(r.querySelectorAll("input, button, select"));
|
||||
const [icon, name, biomes, states, cultures, religions, rural, urban, crew, power, type, separate] = elements.map(el => {
|
||||
const {type, value} = el.dataset || {};
|
||||
if (type === "icon") return el.innerHTML || "⠀";
|
||||
if (type) return value ? value.split(",").map(v => parseInt(v)) : null;
|
||||
if (el.type === "number") return +el.value || 0;
|
||||
if (el.type === "checkbox") return +el.checked || 0;
|
||||
return el.value;
|
||||
});
|
||||
const [icon, name, biomes, states, cultures, religions, rural, urban, crew, power, type, separate] =
|
||||
elements.map(el => {
|
||||
const {type, value} = el.dataset || {};
|
||||
if (type === "icon") return el.innerHTML || "⠀";
|
||||
if (type) return value ? value.split(",").map(v => parseInt(v)) : null;
|
||||
if (el.type === "number") return +el.value || 0;
|
||||
if (el.type === "checkbox") return +el.checked || 0;
|
||||
return el.value;
|
||||
});
|
||||
|
||||
const unit = {icon, name: names[i], rural, urban, crew, power, type, separate};
|
||||
if (biomes) unit.biomes = biomes;
|
||||
|
|
@ -419,7 +446,8 @@ function overviewMilitary() {
|
|||
}
|
||||
|
||||
function militaryRecalculate() {
|
||||
alertMessage.innerHTML = "Are you sure you want to recalculate military forces for all states?<br>Regiments for all states will be regenerated";
|
||||
alertMessage.innerHTML =
|
||||
"Are you sure you want to recalculate military forces for all states?<br>Regiments for all states will be regenerated";
|
||||
$("#alert").dialog({
|
||||
resizable: false,
|
||||
title: "Remove regiment",
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ function editNamesbase() {
|
|||
closeDialogs("#namesbaseEditor, .stable");
|
||||
$("#namesbaseEditor").dialog();
|
||||
|
||||
if (modules.editNamesbase) return;
|
||||
modules.editNamesbase = true;
|
||||
if (fmg.modules.editNamesbase) return;
|
||||
fmg.modules.editNamesbase = true;
|
||||
|
||||
// add listeners
|
||||
document.getElementById("namesbaseSelect").addEventListener("change", updateInputs);
|
||||
|
|
@ -23,15 +23,23 @@ function editNamesbase() {
|
|||
|
||||
const uploader = document.getElementById("namesbaseToLoad");
|
||||
document.getElementById("namesbaseUpload").addEventListener("click", () => {
|
||||
uploader.addEventListener("change", function (event) {
|
||||
uploadFile(event.target, d => namesbaseUpload(d, true));
|
||||
}, { once: true });
|
||||
uploader.addEventListener(
|
||||
"change",
|
||||
function (event) {
|
||||
uploadFile(event.target, d => namesbaseUpload(d, true));
|
||||
},
|
||||
{once: true}
|
||||
);
|
||||
uploader.click();
|
||||
});
|
||||
document.getElementById("namesbaseUploadExtend").addEventListener("click", () => {
|
||||
uploader.addEventListener("change", function (event) {
|
||||
uploadFile(event.target, d => namesbaseUpload(d, false));
|
||||
}, { once: true });
|
||||
uploader.addEventListener(
|
||||
"change",
|
||||
function (event) {
|
||||
uploadFile(event.target, d => namesbaseUpload(d, false));
|
||||
},
|
||||
{once: true}
|
||||
);
|
||||
uploader.click();
|
||||
});
|
||||
|
||||
|
|
@ -147,21 +155,28 @@ function editNamesbase() {
|
|||
: "none";
|
||||
|
||||
const geminate = namesArray.map(name => name.match(/[^\w\s]|(.)(?=\1)/g) || []).flat();
|
||||
const doubled = unique(geminate).filter(char => geminate.filter(doudledChar => doudledChar === char).length > 3) || ["none"];
|
||||
const doubled = unique(geminate).filter(
|
||||
char => geminate.filter(doudledChar => doudledChar === char).length > 3
|
||||
) || ["none"];
|
||||
|
||||
const duplicates = unique(namesArray.filter((e, i, a) => a.indexOf(e) !== i)).join(", ") || "none";
|
||||
const multiwordRate = d3.mean(namesArray.map(n => +n.includes(" ")));
|
||||
|
||||
const getLengthQuality = () => {
|
||||
if (length < 30) return "<span data-tip='Namesbase contains < 30 names - not enough to generate reasonable data' style='color:red'>[not enough]</span>";
|
||||
if (length < 100) return "<span data-tip='Namesbase contains < 100 names - not enough to generate good names' style='color:darkred'>[low]</span>";
|
||||
if (length <= 400) return "<span data-tip='Namesbase contains a reasonable number of samples' style='color:green'>[good]</span>";
|
||||
if (length < 30)
|
||||
return "<span data-tip='Namesbase contains < 30 names - not enough to generate reasonable data' style='color:red'>[not enough]</span>";
|
||||
if (length < 100)
|
||||
return "<span data-tip='Namesbase contains < 100 names - not enough to generate good names' style='color:darkred'>[low]</span>";
|
||||
if (length <= 400)
|
||||
return "<span data-tip='Namesbase contains a reasonable number of samples' style='color:green'>[good]</span>";
|
||||
return "<span data-tip='Namesbase contains > 400 names. That is too much, try to reduce it to ~300 names' style='color:darkred'>[overmuch]</span>";
|
||||
};
|
||||
|
||||
const getVarietyLevel = () => {
|
||||
if (variety < 15) return "<span data-tip='Namesbase average variety < 15 - generated names will be too repetitive' style='color:red'>[low]</span>";
|
||||
if (variety < 30) return "<span data-tip='Namesbase average variety < 30 - names can be too repetitive' style='color:orange'>[mean]</span>";
|
||||
if (variety < 15)
|
||||
return "<span data-tip='Namesbase average variety < 15 - generated names will be too repetitive' style='color:red'>[low]</span>";
|
||||
if (variety < 30)
|
||||
return "<span data-tip='Namesbase average variety < 30 - names can be too repetitive' style='color:orange'>[mean]</span>";
|
||||
return "<span data-tip='Namesbase variety is good' style='color:green'>[good]</span>";
|
||||
};
|
||||
|
||||
|
|
@ -175,9 +190,14 @@ function editNamesbase() {
|
|||
<div data-tip="Common name length">Median name length: ${d3.median(wordsLength)}</div>
|
||||
<hr />
|
||||
<div data-tip="Characters outside of Basic Latin have bad font support">Non-basic chars: ${nonBasicLatinChars}</div>
|
||||
<div data-tip="Characters that are frequently (more than 3 times) doubled">Doubled chars: ${doubled.join("")}</div>
|
||||
<div data-tip="Characters that are frequently (more than 3 times) doubled">Doubled chars: ${doubled.join(
|
||||
""
|
||||
)}</div>
|
||||
<div data-tip="Names used more than one time">Duplicates: ${duplicates}</div>
|
||||
<div data-tip="Percentage of names containing space character">Multi-word names: ${rn(multiwordRate * 100, 2)}%</div>
|
||||
<div data-tip="Percentage of names containing space character">Multi-word names: ${rn(
|
||||
multiwordRate * 100,
|
||||
2
|
||||
)}%</div>
|
||||
</div>`;
|
||||
|
||||
$("#alert").dialog({
|
||||
|
|
@ -194,7 +214,8 @@ function editNamesbase() {
|
|||
|
||||
function namesbaseAdd() {
|
||||
const base = nameBases.length;
|
||||
const b = "This,is,an,example,of,name,base,showing,correct,format,It,should,have,at,least,one,hundred,names,separated,with,comma";
|
||||
const b =
|
||||
"This,is,an,example,of,name,base,showing,correct,format,It,should,have,at,least,one,hundred,names,separated,with,comma";
|
||||
nameBases.push({name: "Base" + base, min: 5, max: 12, d: "", m: 0, b});
|
||||
document.getElementById("namesbaseSelect").add(new Option("Base" + base, base));
|
||||
document.getElementById("namesbaseSelect").value = base;
|
||||
|
|
@ -232,7 +253,7 @@ function editNamesbase() {
|
|||
downloadFile(data, name);
|
||||
}
|
||||
|
||||
function namesbaseUpload(dataLoaded, override=true) {
|
||||
function namesbaseUpload(dataLoaded, override = true) {
|
||||
const data = dataLoaded.split("\r\n");
|
||||
if (!data || !data[0]) {
|
||||
tip("Cannot load a namesbase. Please check the data format", false, "error");
|
||||
|
|
|
|||
|
|
@ -69,12 +69,12 @@ function editNotes(id, name) {
|
|||
if (!window.tinymce) {
|
||||
const url = "https://cdn.tiny.cloud/1/4i6a79ymt2y0cagke174jp3meoi28vyecrch12e5puyw3p9a/tinymce/5/tinymce.min.js";
|
||||
try {
|
||||
await import(url);
|
||||
await import(/* @vite-ignore */ url);
|
||||
} catch (error) {
|
||||
// error may be caused by failed request being cached, try again with random hash
|
||||
try {
|
||||
const hash = Math.random().toString(36).substring(2, 15);
|
||||
await import(`${url}#${hash}`);
|
||||
await import(/* @vite-ignore */ `${url}#${hash}`);
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
// UI module to control the options (preferences)
|
||||
"use strict";
|
||||
import {stored, lock, locked, applyOption} from "./general";
|
||||
|
||||
$("#optionsContainer").draggable({handle: ".drag-trigger", snap: "svg", snapMode: "both"});
|
||||
$("#exitCustomization").draggable({handle: "div"});
|
||||
|
|
@ -170,10 +169,7 @@ function changeMapSize() {
|
|||
|
||||
const maxWidth = Math.max(+mapWidthInput.value, graphWidth);
|
||||
const maxHeight = Math.max(+mapHeightInput.value, graphHeight);
|
||||
zoom.translateExtent([
|
||||
[0, 0],
|
||||
[maxWidth, maxHeight]
|
||||
]);
|
||||
Zoom.translateExtent([0, 0, maxWidth, maxHeight]);
|
||||
landmass.select("rect").attr("x", 0).attr("y", 0).attr("width", maxWidth).attr("height", maxHeight);
|
||||
oceanPattern.select("rect").attr("x", 0).attr("y", 0).attr("width", maxWidth).attr("height", maxHeight);
|
||||
oceanLayers.select("rect").attr("x", 0).attr("y", 0).attr("width", maxWidth).attr("height", maxHeight);
|
||||
|
|
@ -186,7 +182,7 @@ function changeMapSize() {
|
|||
}
|
||||
|
||||
// just apply canvas size that was already set
|
||||
function applyMapSize() {
|
||||
export function applyMapSize() {
|
||||
const zoomMin = +zoomExtentMin.value;
|
||||
const zoomMax = +zoomExtentMax.value;
|
||||
graphWidth = +mapWidthInput.value;
|
||||
|
|
@ -194,13 +190,10 @@ function applyMapSize() {
|
|||
svgWidth = Math.min(graphWidth, window.innerWidth);
|
||||
svgHeight = Math.min(graphHeight, window.innerHeight);
|
||||
svg.attr("width", svgWidth).attr("height", svgHeight);
|
||||
zoom
|
||||
.translateExtent([
|
||||
[0, 0],
|
||||
[graphWidth, graphHeight]
|
||||
])
|
||||
.scaleExtent([zoomMin, zoomMax])
|
||||
.scaleTo(svg, zoomMin);
|
||||
|
||||
Zoom.translateExtent([0, 0, graphWidth, graphHeight]);
|
||||
Zoom.scaleExtent([zoomMin, zoomMax]);
|
||||
Zoom.scaleTo(svg, zoomMin);
|
||||
}
|
||||
|
||||
function toggleFullscreen() {
|
||||
|
|
@ -217,17 +210,13 @@ function toggleFullscreen() {
|
|||
}
|
||||
|
||||
function toggleTranslateExtent(el) {
|
||||
const on = (el.dataset.on = +!+el.dataset.on);
|
||||
if (on)
|
||||
zoom.translateExtent([
|
||||
[-graphWidth / 2, -graphHeight / 2],
|
||||
[graphWidth * 1.5, graphHeight * 1.5]
|
||||
]);
|
||||
else
|
||||
zoom.translateExtent([
|
||||
[0, 0],
|
||||
[graphWidth, graphHeight]
|
||||
]);
|
||||
const on = !Number(el.dataset.on);
|
||||
const extent = on
|
||||
? [-graphWidth / 2, -graphHeight / 2, graphWidth * 1.5, graphHeight * 1.5]
|
||||
: [0, 0, graphWidth, graphHeight];
|
||||
Zoom.translateExtent(extent);
|
||||
|
||||
el.dataset.on = Number(on);
|
||||
}
|
||||
|
||||
// add voice options
|
||||
|
|
@ -294,7 +283,8 @@ function restoreSeed(id) {
|
|||
function restoreDefaultZoomExtent() {
|
||||
zoomExtentMin.value = 1;
|
||||
zoomExtentMax.value = 20;
|
||||
zoom.scaleExtent([1, 20]).scaleTo(svg, 1);
|
||||
Zoom.scaleExtent([1, 20]);
|
||||
Zoom.scaleTo(svg, 1);
|
||||
}
|
||||
|
||||
function copyMapURL() {
|
||||
|
|
@ -461,15 +451,16 @@ function changeDialogsTheme(themeColor, transparency) {
|
|||
}
|
||||
|
||||
function changeZoomExtent(value) {
|
||||
const min = Math.max(+zoomExtentMin.value, 0.01);
|
||||
const max = Math.min(+zoomExtentMax.value, 200);
|
||||
zoom.scaleExtent([min, max]);
|
||||
const min = Math.max(+byId("zoomExtentMin").value, 0.01);
|
||||
const max = Math.min(+byId("zoomExtentMax").value, 200);
|
||||
Zoom.scaleExtent([min, max]);
|
||||
|
||||
const scale = minmax(+value, 0.01, 200);
|
||||
zoom.scaleTo(svg, scale);
|
||||
Zoom.scaleTo(svg, scale);
|
||||
}
|
||||
|
||||
// restore options stored in localStorage
|
||||
function applyStoredOptions() {
|
||||
export function applyStoredOptions() {
|
||||
if (!stored("mapWidth") || !stored("mapHeight")) {
|
||||
mapWidthInput.value = window.innerWidth;
|
||||
mapHeightInput.value = window.innerHeight;
|
||||
|
|
@ -530,7 +521,7 @@ function applyStoredOptions() {
|
|||
}
|
||||
|
||||
// randomize options if randomization is allowed (not locked or options='default')
|
||||
function randomizeOptions() {
|
||||
export function randomizeOptions() {
|
||||
const randomize = new URL(window.location.href).searchParams.get("options") === "default"; // ignore stored options
|
||||
|
||||
// 'Options' settings
|
||||
|
|
@ -595,7 +586,7 @@ function randomizeCultureSet() {
|
|||
}
|
||||
|
||||
function setRendering(value) {
|
||||
viewbox.attr("shape-rendering", value);
|
||||
fmg.viewbox?.attr("shape-rendering", value);
|
||||
}
|
||||
|
||||
// generate current year and era name
|
||||
|
|
@ -652,7 +643,7 @@ document.getElementById("sticked").addEventListener("click", function (event) {
|
|||
else if (id === "saveButton") showSavePane();
|
||||
else if (id === "exportButton") showExportPane();
|
||||
else if (id === "loadButton") showLoadPane();
|
||||
else if (id === "zoomReset") resetZoom(1000);
|
||||
else if (id === "zoomReset") Zoom.reset(1000);
|
||||
});
|
||||
|
||||
function regeneratePrompt(options) {
|
||||
|
|
@ -975,8 +966,8 @@ function toggle3dOptions() {
|
|||
|
||||
updateValues();
|
||||
|
||||
if (modules.options3d) return;
|
||||
modules.options3d = true;
|
||||
if (fmg.modules.options3d) return;
|
||||
fmg.modules.options3d = true;
|
||||
|
||||
document.getElementById("options3dUpdate").addEventListener("click", ThreeD.update);
|
||||
document.getElementById("options3dSave").addEventListener("click", ThreeD.saveScreenshot);
|
||||
|
|
|
|||
|
|
@ -11,8 +11,8 @@ function editProvinces() {
|
|||
const body = document.getElementById("provincesBodySection");
|
||||
refreshProvincesEditor();
|
||||
|
||||
if (modules.editProvinces) return;
|
||||
modules.editProvinces = true;
|
||||
if (fmg.modules.editProvinces) return;
|
||||
fmg.modules.editProvinces = true;
|
||||
|
||||
$("#provincesEditor").dialog({
|
||||
title: "Provinces Editor",
|
||||
|
|
@ -123,7 +123,9 @@ function editProvinces() {
|
|||
const rural = p.rural * populationRate;
|
||||
const urban = p.urban * populationRate * urbanization;
|
||||
const population = rn(rural + urban);
|
||||
const populationTip = `Total population: ${si(population)}; Rural population: ${si(rural)}; Urban population: ${si(urban)}`;
|
||||
const populationTip = `Total population: ${si(population)}; Rural population: ${si(
|
||||
rural
|
||||
)}; Urban population: ${si(urban)}`;
|
||||
totalPopulation += population;
|
||||
|
||||
const stateName = pack.states[p.state].name;
|
||||
|
|
@ -144,9 +146,15 @@ function editProvinces() {
|
|||
>
|
||||
<fill-box fill="${p.color}"></fill-box>
|
||||
<input data-tip="Province name. Click to change" class="name pointer" value="${p.name}" readonly />
|
||||
<svg data-tip="Click to show and edit province emblem" class="coaIcon pointer hide" viewBox="0 0 200 200"><use href="#provinceCOA${p.i}"></use></svg>
|
||||
<input data-tip="Province form name. Click to change" class="name pointer hide" value="${p.formName}" readonly />
|
||||
<span data-tip="Province capital. Click to zoom into view" class="icon-star-empty pointer hide ${p.burg ? "" : "placeholder"}"></span>
|
||||
<svg data-tip="Click to show and edit province emblem" class="coaIcon pointer hide" viewBox="0 0 200 200"><use href="#provinceCOA${
|
||||
p.i
|
||||
}"></use></svg>
|
||||
<input data-tip="Province form name. Click to change" class="name pointer hide" value="${
|
||||
p.formName
|
||||
}" readonly />
|
||||
<span data-tip="Province capital. Click to zoom into view" class="icon-star-empty pointer hide ${
|
||||
p.burg ? "" : "placeholder"
|
||||
}"></span>
|
||||
<select
|
||||
data-tip="Province capital. Click to select from burgs within the state. No capital means the province is governed from the state capital"
|
||||
class="cultureBase hide ${p.burgs.length ? "" : "placeholder"}"
|
||||
|
|
@ -191,7 +199,9 @@ function editProvinces() {
|
|||
|
||||
function getCapitalOptions(burgs, capital) {
|
||||
let options = "";
|
||||
burgs.forEach(b => (options += `<option ${b === capital ? "selected" : ""} value="${b}">${pack.burgs[b].name}</option>`));
|
||||
burgs.forEach(
|
||||
b => (options += `<option ${b === capital ? "selected" : ""} value="${b}">${pack.burgs[b].name}</option>`)
|
||||
);
|
||||
return options;
|
||||
}
|
||||
|
||||
|
|
@ -265,7 +275,11 @@ function editProvinces() {
|
|||
const {name, burg: burgId, burgs: provinceBurgs} = province;
|
||||
|
||||
if (provinceBurgs.some(b => burgs[b].capital))
|
||||
return tip("Cannot declare independence of a province having capital burg. Please change capital first", false, "error");
|
||||
return tip(
|
||||
"Cannot declare independence of a province having capital burg. Please change capital first",
|
||||
false,
|
||||
"error"
|
||||
);
|
||||
if (!burgId) return tip("Cannot declare independence of a province without burg", false, "error");
|
||||
|
||||
const oldStateId = province.state;
|
||||
|
|
@ -311,7 +325,10 @@ function editProvinces() {
|
|||
return relations;
|
||||
});
|
||||
diplomacy.push("x");
|
||||
states[0].diplomacy.push([`Independance declaration`, `${name} declared its independance from ${states[oldStateId].name}`]);
|
||||
states[0].diplomacy.push([
|
||||
`Independance declaration`,
|
||||
`${name} declared its independance from ${states[oldStateId].name}`
|
||||
]);
|
||||
|
||||
// create new state
|
||||
states.push({
|
||||
|
|
@ -373,8 +390,12 @@ function editProvinces() {
|
|||
const l = n => Number(n).toLocaleString();
|
||||
|
||||
alertMessage.innerHTML = /* html */ ` Rural: <input type="number" min="0" step="1" id="ruralPop" value=${rural} style="width:6em" /> Urban:
|
||||
<input type="number" min="0" step="1" id="urbanPop" value=${urban} style="width:6em" ${p.burgs.length ? "" : "disabled"} />
|
||||
<p>Total population: ${l(total)} ⇒ <span id="totalPop">${l(total)}</span> (<span id="totalPopPerc">100</span>%)</p>`;
|
||||
<input type="number" min="0" step="1" id="urbanPop" value=${urban} style="width:6em" ${
|
||||
p.burgs.length ? "" : "disabled"
|
||||
} />
|
||||
<p>Total population: ${l(total)} ⇒ <span id="totalPop">${l(
|
||||
total
|
||||
)}</span> (<span id="totalPopPerc">100</span>%)</p>`;
|
||||
|
||||
const update = function () {
|
||||
const totalNew = ruralPop.valueAsNumber + urbanPop.valueAsNumber;
|
||||
|
|
@ -493,8 +514,8 @@ function editProvinces() {
|
|||
position: {my: "center", at: "center", of: "svg"}
|
||||
});
|
||||
|
||||
if (modules.editProvinceName) return;
|
||||
modules.editProvinceName = true;
|
||||
if (fmg.modules.editProvinceName) return;
|
||||
fmg.modules.editProvinceName = true;
|
||||
|
||||
// add listeners
|
||||
document.getElementById("provinceNameEditorShortCulture").addEventListener("click", regenerateShortNameCuture);
|
||||
|
|
@ -692,7 +713,13 @@ function editProvinces() {
|
|||
|
||||
function updateChart() {
|
||||
const value =
|
||||
this.value === "area" ? d => d.area : this.value === "rural" ? d => d.rural : this.value === "urban" ? d => d.urban : d => d.rural + d.urban;
|
||||
this.value === "area"
|
||||
? d => d.area
|
||||
: this.value === "rural"
|
||||
? d => d.rural
|
||||
: this.value === "urban"
|
||||
? d => d.urban
|
||||
: d => d.rural + d.urban;
|
||||
|
||||
root.sum(value);
|
||||
node.data(treeLayout(root).leaves());
|
||||
|
|
@ -774,7 +801,13 @@ function editProvinces() {
|
|||
|
||||
customization = 11;
|
||||
provs.select("g#provincesBody").append("g").attr("id", "temp");
|
||||
provs.select("g#provincesBody").append("g").attr("id", "centers").attr("fill", "none").attr("stroke", "#ff0000").attr("stroke-width", 1);
|
||||
provs
|
||||
.select("g#provincesBody")
|
||||
.append("g")
|
||||
.attr("id", "centers")
|
||||
.attr("fill", "none")
|
||||
.attr("stroke", "#ff0000")
|
||||
.attr("stroke-width", 1);
|
||||
|
||||
document.querySelectorAll("#provincesBottom > *").forEach(el => (el.style.display = "none"));
|
||||
document.getElementById("provincesManuallyButtons").style.display = "inline-block";
|
||||
|
|
@ -786,7 +819,11 @@ function editProvinces() {
|
|||
$("#provincesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}});
|
||||
|
||||
tip("Click on a province to select, drag the circle to change province", true);
|
||||
viewbox.style("cursor", "crosshair").on("click", selectProvinceOnMapClick).call(d3.drag().on("start", dragBrush)).on("touchmove mousemove", moveBrush);
|
||||
viewbox
|
||||
.style("cursor", "crosshair")
|
||||
.on("click", selectProvinceOnMapClick)
|
||||
.call(d3.drag().on("start", dragBrush))
|
||||
.on("touchmove mousemove", moveBrush);
|
||||
|
||||
body.querySelector("div").classList.add("selected");
|
||||
selectProvince(+body.querySelector("div").dataset.id);
|
||||
|
|
@ -857,7 +894,11 @@ function editProvinces() {
|
|||
if (i === pack.provinces[provinceOld].center) {
|
||||
const center = centers.select("polygon[data-center='" + i + "']");
|
||||
if (!center.size()) centers.append("polygon").attr("data-center", i).attr("points", getPackPolygon(i));
|
||||
tip("Province center cannot be assigned to a different region. Please remove the province first", false, "error");
|
||||
tip(
|
||||
"Province center cannot be assigned to a different region. Please remove the province first",
|
||||
false,
|
||||
"error"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -919,7 +960,8 @@ function editProvinces() {
|
|||
provincesHeader.querySelector("div[data-sortby='state']").style.left = "22em";
|
||||
provincesFooter.style.display = "block";
|
||||
body.querySelectorAll("div > input, select, span, svg").forEach(e => (e.style.pointerEvents = "all"));
|
||||
if (!close) $("#provincesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}});
|
||||
if (!close)
|
||||
$("#provincesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}});
|
||||
|
||||
restoreDefaultEvents();
|
||||
clearMainTip();
|
||||
|
|
@ -941,14 +983,20 @@ function editProvinces() {
|
|||
const {cells, provinces} = pack;
|
||||
const point = d3.mouse(this);
|
||||
const center = findCell(point[0], point[1]);
|
||||
if (cells.h[center] < 20) return tip("You cannot place province into the water. Please click on a land cell", false, "error");
|
||||
if (cells.h[center] < 20)
|
||||
return tip("You cannot place province into the water. Please click on a land cell", false, "error");
|
||||
|
||||
const oldProvince = cells.province[center];
|
||||
if (oldProvince && provinces[oldProvince].center === center)
|
||||
return tip("The cell is already a center of a different province. Select other cell", false, "error");
|
||||
|
||||
const state = cells.state[center];
|
||||
if (!state) return tip("You cannot create a province in neutral lands. Please assign this land to a state first", false, "error");
|
||||
if (!state)
|
||||
return tip(
|
||||
"You cannot create a province in neutral lands. Please assign this land to a state first",
|
||||
false,
|
||||
"error"
|
||||
);
|
||||
|
||||
if (d3.event.shiftKey === false) exitAddProvinceMode();
|
||||
|
||||
|
|
@ -1014,7 +1062,10 @@ function editProvinces() {
|
|||
|
||||
function downloadProvincesData() {
|
||||
const unit = areaUnit.value === "square" ? distanceUnitInput.value + "2" : areaUnit.value;
|
||||
let data = "Id,Province,Full Name,Form,State,Color,Capital,Area " + unit + ",Total Population,Rural Population,Urban Population\n"; // headers
|
||||
let data =
|
||||
"Id,Province,Full Name,Form,State,Color,Capital,Area " +
|
||||
unit +
|
||||
",Total Population,Rural Population,Urban Population\n"; // headers
|
||||
|
||||
body.querySelectorAll(":scope > div").forEach(function (el) {
|
||||
const key = parseInt(el.dataset.id);
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ function editRegiment(selector) {
|
|||
position: {my: "left top", at: "left+10 top+10", of: "#map"}
|
||||
});
|
||||
|
||||
if (modules.editRegiment) return;
|
||||
modules.editRegiment = true;
|
||||
if (fmg.modules.editRegiment) return;
|
||||
fmg.modules.editRegiment = true;
|
||||
|
||||
// add listeners
|
||||
document.getElementById("regimentNameRestore").addEventListener("click", restoreName);
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ function overviewRegiments(state) {
|
|||
addLines();
|
||||
$("#regimentsOverview").dialog();
|
||||
|
||||
if (modules.overviewRegiments) return;
|
||||
modules.overviewRegiments = true;
|
||||
if (fmg.modules.overviewRegiments) return;
|
||||
fmg.modules.overviewRegiments = true;
|
||||
updateHeaders();
|
||||
|
||||
$("#regimentsOverview").dialog({
|
||||
|
|
@ -37,7 +37,9 @@ function overviewRegiments(state) {
|
|||
const insert = html => document.getElementById("regimentsTotal").insertAdjacentHTML("beforebegin", html);
|
||||
for (const u of options.military) {
|
||||
const label = capitalize(u.name.replace(/_/g, " "));
|
||||
insert(`<div data-tip="Regiment ${u.name} units number. Click to sort" class="sortable removable" data-sortby="${u.name}">${label} </div>`);
|
||||
insert(
|
||||
`<div data-tip="Regiment ${u.name} units number. Click to sort" class="sortable removable" data-sortby="${u.name}">${label} </div>`
|
||||
);
|
||||
}
|
||||
header.querySelectorAll(".removable").forEach(function (e) {
|
||||
e.addEventListener("click", function () {
|
||||
|
|
@ -60,7 +62,9 @@ function overviewRegiments(state) {
|
|||
for (const r of s.military) {
|
||||
const sortData = options.military.map(u => `data-${u.name}=${r.u[u.name] || 0}`).join(" ");
|
||||
const lineData = options.military
|
||||
.map(u => `<div data-type="${u.name}" data-tip="${capitalize(u.name)} units number">${r.u[u.name] || 0}</div>`)
|
||||
.map(
|
||||
u => `<div data-type="${u.name}" data-tip="${capitalize(u.name)} units number">${r.u[u.name] || 0}</div>`
|
||||
)
|
||||
.join(" ");
|
||||
|
||||
lines += /* html */ `<div class="states" data-id=${r.i} data-s="${s.i}" data-state="${s.name}" data-name="${r.name}" ${sortData} data-total="${r.a}">
|
||||
|
|
@ -79,7 +83,9 @@ function overviewRegiments(state) {
|
|||
|
||||
lines += /* html */ `<div id="regimentsTotalLine" class="totalLine" data-tip="Total of all displayed regiments">
|
||||
<div style="width: 21em; margin-left: 1em">Regiments: ${regiments.length}</div>
|
||||
${options.military.map(u => `<div style="width:5em">${si(d3.sum(regiments.map(r => r.u[u.name] || 0)))}</div>`).join(" ")}
|
||||
${options.military
|
||||
.map(u => `<div style="width:5em">${si(d3.sum(regiments.map(r => r.u[u.name] || 0)))}</div>`)
|
||||
.join(" ")}
|
||||
<div style="width:5em">${si(d3.sum(regiments.map(r => r.a)))}</div>
|
||||
</div>`;
|
||||
|
||||
|
|
@ -92,7 +98,9 @@ function overviewRegiments(state) {
|
|||
|
||||
// add listeners
|
||||
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseenter", ev => regimentHighlightOn(ev)));
|
||||
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseleave", ev => regimentHighlightOff(ev)));
|
||||
body
|
||||
.querySelectorAll("div.states")
|
||||
.forEach(el => el.addEventListener("mouseleave", ev => regimentHighlightOff(ev)));
|
||||
}
|
||||
|
||||
function updateFilter(state) {
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ function editReliefIcon() {
|
|||
close: closeReliefEditor
|
||||
});
|
||||
|
||||
if (modules.editReliefIcon) return;
|
||||
modules.editReliefIcon = true;
|
||||
if (fmg.modules.editReliefIcon) return;
|
||||
fmg.modules.editReliefIcon = true;
|
||||
|
||||
// add listeners
|
||||
document.getElementById("reliefIndividual").addEventListener("click", enterIndividualMode);
|
||||
|
|
@ -260,7 +260,9 @@ function editReliefIcon() {
|
|||
const type = reliefIconsDiv.querySelector("svg.pressed")?.dataset.type;
|
||||
selection = type ? terrain.selectAll("use[href='" + type + "']") : terrain.selectAll("use");
|
||||
const size = selection.size();
|
||||
alertMessage.innerHTML = type ? `Are you sure you want to remove all ${type} icons (${size})?` : `Are you sure you want to remove all icons (${size})?`;
|
||||
alertMessage.innerHTML = type
|
||||
? `Are you sure you want to remove all ${type} icons (${size})?`
|
||||
: `Are you sure you want to remove all icons (${size})?`;
|
||||
}
|
||||
|
||||
$("#alert").dialog({
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ function createRiver() {
|
|||
close: closeRiverCreator
|
||||
});
|
||||
|
||||
if (modules.createRiver) return;
|
||||
modules.createRiver = true;
|
||||
if (fmg.modules.createRiver) return;
|
||||
fmg.modules.createRiver = true;
|
||||
|
||||
// add listeners
|
||||
document.getElementById("riverCreatorComplete").addEventListener("click", addRiver);
|
||||
|
|
@ -100,12 +100,30 @@ function createRiver() {
|
|||
const name = getName(mouth);
|
||||
const basin = getBasin(parent);
|
||||
|
||||
rivers.push({i: riverId, source, mouth, discharge, length, width, widthFactor, sourceWidth, parent, cells: riverCells, basin, name, type: "River"});
|
||||
rivers.push({
|
||||
i: riverId,
|
||||
source,
|
||||
mouth,
|
||||
discharge,
|
||||
length,
|
||||
width,
|
||||
widthFactor,
|
||||
sourceWidth,
|
||||
parent,
|
||||
cells: riverCells,
|
||||
basin,
|
||||
name,
|
||||
type: "River"
|
||||
});
|
||||
const id = "river" + riverId;
|
||||
|
||||
// render river
|
||||
lineGen.curve(d3.curveCatmullRom.alpha(0.1));
|
||||
viewbox.select("#rivers").append("path").attr("id", id).attr("d", getRiverPath(meanderedPoints, widthFactor, sourceWidth));
|
||||
viewbox
|
||||
.select("#rivers")
|
||||
.append("path")
|
||||
.attr("id", id)
|
||||
.attr("d", getRiverPath(meanderedPoints, widthFactor, sourceWidth));
|
||||
|
||||
editRiver(id);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,10 @@ function editRiver(id) {
|
|||
|
||||
elSelected = d3.select("#" + id).on("click", addControlPoint);
|
||||
|
||||
tip("Drag control points to change the river course. Click on point to remove it. Click on river to add additional control point. For major changes please create a new river instead", true);
|
||||
tip(
|
||||
"Drag control points to change the river course. Click on point to remove it. Click on river to add additional control point. For major changes please create a new river instead",
|
||||
true
|
||||
);
|
||||
debug.append("g").attr("id", "controlCells");
|
||||
debug.append("g").attr("id", "controlPoints");
|
||||
|
||||
|
|
@ -29,8 +32,8 @@ function editRiver(id) {
|
|||
close: closeRiverEditor
|
||||
});
|
||||
|
||||
if (modules.editRiver) return;
|
||||
modules.editRiver = true;
|
||||
if (fmg.modules.editRiver) return;
|
||||
fmg.modules.editRiver = true;
|
||||
|
||||
// add listeners
|
||||
document.getElementById("riverCreateSelectingCells").addEventListener("click", createRiver);
|
||||
|
|
@ -163,7 +166,7 @@ function editRiver(id) {
|
|||
elSelected.attr("d", path);
|
||||
|
||||
updateRiverLength(river);
|
||||
if (modules.elevation) showEPForRiver(elSelected.node());
|
||||
if (fmg.modules.elevation) showEPForRiver(elSelected.node());
|
||||
}
|
||||
|
||||
function addControlPoint() {
|
||||
|
|
@ -227,7 +230,7 @@ function editRiver(id) {
|
|||
}
|
||||
|
||||
function showElevationProfile() {
|
||||
modules.elevation = true;
|
||||
fmg.modules.elevation = true;
|
||||
showEPForRiver(elSelected.node());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ function overviewRivers() {
|
|||
riversOverviewAddLines();
|
||||
$("#riversOverview").dialog();
|
||||
|
||||
if (modules.overviewRivers) return;
|
||||
modules.overviewRivers = true;
|
||||
if (fmg.modules.overviewRivers) return;
|
||||
fmg.modules.overviewRivers = true;
|
||||
|
||||
$("#riversOverview").dialog({
|
||||
title: "Rivers Overview",
|
||||
|
|
@ -75,7 +75,9 @@ function overviewRivers() {
|
|||
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseleave", ev => riverHighlightOff(ev)));
|
||||
body.querySelectorAll("div > span.icon-dot-circled").forEach(el => el.addEventListener("click", zoomToRiver));
|
||||
body.querySelectorAll("div > span.icon-pencil").forEach(el => el.addEventListener("click", openRiverEditor));
|
||||
body.querySelectorAll("div > span.icon-trash-empty").forEach(el => el.addEventListener("click", triggerRiverRemove));
|
||||
body
|
||||
.querySelectorAll("div > span.icon-trash-empty")
|
||||
.forEach(el => el.addEventListener("click", triggerRiverRemove));
|
||||
|
||||
applySorting(riversHeader);
|
||||
}
|
||||
|
|
@ -110,7 +112,18 @@ function overviewRivers() {
|
|||
} else {
|
||||
rivers.attr("data-basin", "hightlighted");
|
||||
const basins = [...new Set(pack.rivers.map(r => r.basin))];
|
||||
const colors = ["#1f77b4", "#ff7f0e", "#2ca02c", "#d62728", "#9467bd", "#8c564b", "#e377c2", "#7f7f7f", "#bcbd22", "#17becf"];
|
||||
const colors = [
|
||||
"#1f77b4",
|
||||
"#ff7f0e",
|
||||
"#2ca02c",
|
||||
"#d62728",
|
||||
"#9467bd",
|
||||
"#8c564b",
|
||||
"#e377c2",
|
||||
"#7f7f7f",
|
||||
"#bcbd22",
|
||||
"#17becf"
|
||||
];
|
||||
|
||||
basins.forEach((b, i) => {
|
||||
const color = colors[i % colors.length];
|
||||
|
|
|
|||
|
|
@ -21,8 +21,8 @@ function editRoute(onClick) {
|
|||
viewbox.on("touchmove mousemove", showEditorTips);
|
||||
if (onClick) toggleRouteCreationMode();
|
||||
|
||||
if (modules.editRoute) return;
|
||||
modules.editRoute = true;
|
||||
if (fmg.modules.editRoute) return;
|
||||
fmg.modules.editRoute = true;
|
||||
|
||||
// add listeners
|
||||
document.getElementById("routeGroupsShow").addEventListener("click", showGroupSection);
|
||||
|
|
@ -97,11 +97,11 @@ function editRoute(onClick) {
|
|||
const l = elSelected.node().getTotalLength();
|
||||
routeLength.innerHTML = rn(l * distanceScaleInput.value) + " " + distanceUnitInput.value;
|
||||
|
||||
if (modules.elevation) showEPForRoute(elSelected.node());
|
||||
if (fmg.modules.elevation) showEPForRoute(elSelected.node());
|
||||
}
|
||||
|
||||
function showElevationProfile() {
|
||||
modules.elevation = true;
|
||||
fmg.modules.elevation = true;
|
||||
showEPForRoute(elSelected.node());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -76,9 +76,22 @@ function selectStyleElement() {
|
|||
|
||||
// stroke color and width
|
||||
if (
|
||||
["armies", "routes", "lakes", "borders", "cults", "relig", "cells", "coastline", "prec", "ice", "icons", "coordinates", "zones", "gridOverlay"].includes(
|
||||
sel
|
||||
)
|
||||
[
|
||||
"armies",
|
||||
"routes",
|
||||
"lakes",
|
||||
"borders",
|
||||
"cults",
|
||||
"relig",
|
||||
"cells",
|
||||
"coastline",
|
||||
"prec",
|
||||
"ice",
|
||||
"icons",
|
||||
"coordinates",
|
||||
"zones",
|
||||
"gridOverlay"
|
||||
].includes(sel)
|
||||
) {
|
||||
styleStroke.style.display = "block";
|
||||
styleStrokeInput.value = styleStrokeOutput.value = el.attr("stroke");
|
||||
|
|
@ -87,14 +100,29 @@ function selectStyleElement() {
|
|||
}
|
||||
|
||||
// stroke dash
|
||||
if (["routes", "borders", "temperature", "legend", "population", "coordinates", "zones", "gridOverlay"].includes(sel)) {
|
||||
if (
|
||||
["routes", "borders", "temperature", "legend", "population", "coordinates", "zones", "gridOverlay"].includes(sel)
|
||||
) {
|
||||
styleStrokeDash.style.display = "block";
|
||||
styleStrokeDasharrayInput.value = el.attr("stroke-dasharray") || "";
|
||||
styleStrokeLinecapInput.value = el.attr("stroke-linecap") || "inherit";
|
||||
}
|
||||
|
||||
// clipping
|
||||
if (["cells", "gridOverlay", "coordinates", "compass", "terrain", "temperature", "routes", "texture", "biomes", "zones"].includes(sel)) {
|
||||
if (
|
||||
[
|
||||
"cells",
|
||||
"gridOverlay",
|
||||
"coordinates",
|
||||
"compass",
|
||||
"terrain",
|
||||
"temperature",
|
||||
"routes",
|
||||
"texture",
|
||||
"biomes",
|
||||
"zones"
|
||||
].includes(sel)
|
||||
) {
|
||||
styleClipping.style.display = "block";
|
||||
styleClippingInput.value = el.attr("mask") || "";
|
||||
}
|
||||
|
|
@ -142,8 +170,12 @@ function selectStyleElement() {
|
|||
|
||||
if (sel === "population") {
|
||||
stylePopulation.style.display = "block";
|
||||
stylePopulationRuralStrokeInput.value = stylePopulationRuralStrokeOutput.value = population.select("#rural").attr("stroke");
|
||||
stylePopulationUrbanStrokeInput.value = stylePopulationUrbanStrokeOutput.value = population.select("#urban").attr("stroke");
|
||||
stylePopulationRuralStrokeInput.value = stylePopulationRuralStrokeOutput.value = population
|
||||
.select("#rural")
|
||||
.attr("stroke");
|
||||
stylePopulationUrbanStrokeInput.value = stylePopulationUrbanStrokeOutput.value = population
|
||||
.select("#urban")
|
||||
.attr("stroke");
|
||||
styleStrokeWidth.style.display = "block";
|
||||
styleStrokeWidthInput.value = styleStrokeWidthOutput.value = el.attr("stroke-width") || "";
|
||||
}
|
||||
|
|
@ -233,7 +265,8 @@ function selectStyleElement() {
|
|||
styleOcean.style.display = "block";
|
||||
styleOceanFill.value = styleOceanFillOutput.value = oceanLayers.select("#oceanBase").attr("fill");
|
||||
styleOceanPattern.value = document.getElementById("oceanicPattern")?.getAttribute("href");
|
||||
styleOceanPatternOpacity.value = styleOceanPatternOpacityOutput.value = document.getElementById("oceanicPattern").getAttribute("opacity") || 1;
|
||||
styleOceanPatternOpacity.value = styleOceanPatternOpacityOutput.value =
|
||||
document.getElementById("oceanicPattern").getAttribute("opacity") || 1;
|
||||
outlineLayers.value = oceanLayers.attr("layers");
|
||||
}
|
||||
|
||||
|
|
@ -551,7 +584,10 @@ styleFontAdd.addEventListener("click", function () {
|
|||
|
||||
if (!family) return tip("Please provide a font name", false, "error");
|
||||
|
||||
const existingFont = method === "fontURL" ? fonts.find(font => font.family === family && font.src === src) : fonts.find(font => font.family === family);
|
||||
const existingFont =
|
||||
method === "fontURL"
|
||||
? fonts.find(font => font.family === family && font.src === src)
|
||||
: fonts.find(font => font.family === family);
|
||||
if (existingFont) return tip("The font is already added", false, "error");
|
||||
|
||||
if (method === "fontURL") addWebFont(family, src);
|
||||
|
|
@ -710,9 +746,9 @@ styleArmiesSize.addEventListener("input", function () {
|
|||
});
|
||||
});
|
||||
|
||||
emblemsStateSizeInput.addEventListener("change", drawEmblems);
|
||||
emblemsProvinceSizeInput.addEventListener("change", drawEmblems);
|
||||
emblemsBurgSizeInput.addEventListener("change", drawEmblems);
|
||||
emblemsStateSizeInput.addEventListener("change", () => drawEmblems());
|
||||
emblemsProvinceSizeInput.addEventListener("change", () => drawEmblems());
|
||||
emblemsBurgSizeInput.addEventListener("change", () => drawEmblems());
|
||||
|
||||
// request a URL to image to be used as a texture
|
||||
function textureProvideURL() {
|
||||
|
|
|
|||
|
|
@ -1,14 +1,26 @@
|
|||
// UI module to control the style presets
|
||||
"use strict";
|
||||
// UI module to control the style presets
|
||||
|
||||
const systemPresets = ["default", "ancient", "gloom", "light", "watercolor", "clean", "atlas", "cyberpunk", "monochrome"];
|
||||
const systemPresets = [
|
||||
"default",
|
||||
"ancient",
|
||||
"gloom",
|
||||
"light",
|
||||
"watercolor",
|
||||
"clean",
|
||||
"atlas",
|
||||
"cyberpunk",
|
||||
"monochrome"
|
||||
];
|
||||
const customPresetPrefix = "fmgStyle_";
|
||||
|
||||
// add style presets to list
|
||||
{
|
||||
const systemOptions = systemPresets.map(styleName => `<option value="${styleName}">${styleName}</option>`);
|
||||
const storedStyles = Object.keys(localStorage).filter(key => key.startsWith(customPresetPrefix));
|
||||
const customOptions = storedStyles.map(styleName => `<option value="${styleName}">${styleName.replace(customPresetPrefix, "")} [custom]</option>`);
|
||||
const customOptions = storedStyles.map(
|
||||
styleName => `<option value="${styleName}">${styleName.replace(customPresetPrefix, "")} [custom]</option>`
|
||||
);
|
||||
const options = systemOptions.join("") + customOptions.join("");
|
||||
document.getElementById("stylePreset").innerHTML = options;
|
||||
}
|
||||
|
|
@ -37,7 +49,8 @@ async function getStylePreset(desiredPreset) {
|
|||
const isValid = JSON.isValid(storedStyleJSON);
|
||||
if (isValid) return [desiredPreset, JSON.parse(storedStyleJSON)];
|
||||
|
||||
ERROR && console.error(`Custom style ${desiredPreset} stored in localStorage is not valid. Applying default style`);
|
||||
ERROR &&
|
||||
console.error(`Custom style ${desiredPreset} stored in localStorage is not valid. Applying default style`);
|
||||
presetToLoad = "default";
|
||||
}
|
||||
}
|
||||
|
|
@ -126,8 +139,8 @@ function addStylePreset() {
|
|||
styleSaverJSON.value = JSON.stringify(collectStyleData(), null, 2);
|
||||
checkName();
|
||||
|
||||
if (modules.saveStyle) return;
|
||||
modules.saveStyle = true;
|
||||
if (fmg.modules.saveStyle) return;
|
||||
fmg.modules.saveStyle = true;
|
||||
|
||||
// add listeners
|
||||
document.getElementById("styleSaverName").addEventListener("input", checkName);
|
||||
|
|
@ -145,8 +158,31 @@ function addStylePreset() {
|
|||
"#stateBorders": ["opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter"],
|
||||
"#provinceBorders": ["opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter"],
|
||||
"#cells": ["opacity", "stroke", "stroke-width", "filter", "mask"],
|
||||
"#gridOverlay": ["opacity", "scale", "dx", "dy", "type", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "transform", "filter", "mask"],
|
||||
"#coordinates": ["opacity", "data-size", "font-size", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter", "mask"],
|
||||
"#gridOverlay": [
|
||||
"opacity",
|
||||
"scale",
|
||||
"dx",
|
||||
"dy",
|
||||
"type",
|
||||
"stroke",
|
||||
"stroke-width",
|
||||
"stroke-dasharray",
|
||||
"stroke-linecap",
|
||||
"transform",
|
||||
"filter",
|
||||
"mask"
|
||||
],
|
||||
"#coordinates": [
|
||||
"opacity",
|
||||
"data-size",
|
||||
"font-size",
|
||||
"stroke",
|
||||
"stroke-width",
|
||||
"stroke-dasharray",
|
||||
"stroke-linecap",
|
||||
"filter",
|
||||
"mask"
|
||||
],
|
||||
"#compass": ["opacity", "transform", "filter", "mask", "shape-rendering"],
|
||||
"#rose": ["transform"],
|
||||
"#relig": ["opacity", "stroke", "stroke-width", "filter"],
|
||||
|
|
@ -174,7 +210,17 @@ function addStylePreset() {
|
|||
"#statesBody": ["opacity", "filter"],
|
||||
"#statesHalo": ["opacity", "data-width", "stroke-width", "filter"],
|
||||
"#provs": ["opacity", "fill", "font-size", "font-family", "filter"],
|
||||
"#temperature": ["opacity", "font-size", "fill", "fill-opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter"],
|
||||
"#temperature": [
|
||||
"opacity",
|
||||
"font-size",
|
||||
"fill",
|
||||
"fill-opacity",
|
||||
"stroke",
|
||||
"stroke-width",
|
||||
"stroke-dasharray",
|
||||
"stroke-linecap",
|
||||
"filter"
|
||||
],
|
||||
"#ice": ["opacity", "fill", "stroke", "stroke-width", "filter"],
|
||||
"#emblems": ["opacity", "stroke-width", "filter"],
|
||||
"#texture": ["opacity", "filter", "mask"],
|
||||
|
|
@ -184,16 +230,65 @@ function addStylePreset() {
|
|||
"#oceanBase": ["fill"],
|
||||
"#oceanicPattern": ["href", "opacity"],
|
||||
"#terrs": ["opacity", "scheme", "terracing", "skip", "relax", "curve", "filter", "mask"],
|
||||
"#legend": ["data-size", "font-size", "font-family", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "data-x", "data-y", "data-columns"],
|
||||
"#legend": [
|
||||
"data-size",
|
||||
"font-size",
|
||||
"font-family",
|
||||
"stroke",
|
||||
"stroke-width",
|
||||
"stroke-dasharray",
|
||||
"stroke-linecap",
|
||||
"data-x",
|
||||
"data-y",
|
||||
"data-columns"
|
||||
],
|
||||
"#legendBox": ["fill", "fill-opacity"],
|
||||
"#burgLabels > #cities": ["opacity", "fill", "text-shadow", "data-size", "font-size", "font-family"],
|
||||
"#burgIcons > #cities": ["opacity", "fill", "fill-opacity", "size", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap"],
|
||||
"#burgIcons > #cities": [
|
||||
"opacity",
|
||||
"fill",
|
||||
"fill-opacity",
|
||||
"size",
|
||||
"stroke",
|
||||
"stroke-width",
|
||||
"stroke-dasharray",
|
||||
"stroke-linecap"
|
||||
],
|
||||
"#anchors > #cities": ["opacity", "fill", "size", "stroke", "stroke-width"],
|
||||
"#burgLabels > #towns": ["opacity", "fill", "text-shadow", "data-size", "font-size", "font-family"],
|
||||
"#burgIcons > #towns": ["opacity", "fill", "fill-opacity", "size", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap"],
|
||||
"#burgIcons > #towns": [
|
||||
"opacity",
|
||||
"fill",
|
||||
"fill-opacity",
|
||||
"size",
|
||||
"stroke",
|
||||
"stroke-width",
|
||||
"stroke-dasharray",
|
||||
"stroke-linecap"
|
||||
],
|
||||
"#anchors > #towns": ["opacity", "fill", "size", "stroke", "stroke-width"],
|
||||
"#labels > #states": ["opacity", "fill", "stroke", "stroke-width", "text-shadow", "data-size", "font-size", "font-family", "filter"],
|
||||
"#labels > #addedLabels": ["opacity", "fill", "stroke", "stroke-width", "text-shadow", "data-size", "font-size", "font-family", "filter"],
|
||||
"#labels > #states": [
|
||||
"opacity",
|
||||
"fill",
|
||||
"stroke",
|
||||
"stroke-width",
|
||||
"text-shadow",
|
||||
"data-size",
|
||||
"font-size",
|
||||
"font-family",
|
||||
"filter"
|
||||
],
|
||||
"#labels > #addedLabels": [
|
||||
"opacity",
|
||||
"fill",
|
||||
"stroke",
|
||||
"stroke-width",
|
||||
"text-shadow",
|
||||
"data-size",
|
||||
"font-size",
|
||||
"font-family",
|
||||
"filter"
|
||||
],
|
||||
"#fogging": ["opacity", "fill", "filter"]
|
||||
};
|
||||
|
||||
|
|
@ -238,7 +333,8 @@ function addStylePreset() {
|
|||
if (!styleJSON) return tip("Please provide a style JSON", false, "error");
|
||||
if (!JSON.isValid(styleJSON)) return tip("JSON string is not valid, please check the format", false, "error");
|
||||
if (!desiredName) return tip("Please provide a preset name", false, "error");
|
||||
if (styleSaverTip.innerHTML === "default") return tip("You cannot overwrite default preset, please change the name", false, "error");
|
||||
if (styleSaverTip.innerHTML === "default")
|
||||
return tip("You cannot overwrite default preset, please change the name", false, "error");
|
||||
|
||||
const presetName = customPresetPrefix + desiredName;
|
||||
applyOption(stylePreset, presetName, desiredName + " [custom]");
|
||||
|
|
|
|||
|
|
@ -136,7 +136,14 @@ window.UISubmap = (function () {
|
|||
}
|
||||
|
||||
async function loadPreview($container, w, h) {
|
||||
const url = await getMapURL("png", {globe: false, noWater: true, fullMap: true, noLabels: true, noScaleBar: true, noIce: true});
|
||||
const url = await getMapURL("png", {
|
||||
globe: false,
|
||||
noWater: true,
|
||||
fullMap: true,
|
||||
noLabels: true,
|
||||
noScaleBar: true,
|
||||
noIce: true
|
||||
});
|
||||
|
||||
const canvas = document.createElement("canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
|
|
@ -173,7 +180,11 @@ window.UISubmap = (function () {
|
|||
const {angle, shiftX, shiftY, ratio, mirrorH, mirrorV} = getTransformInput();
|
||||
|
||||
const [cx, cy] = [graphWidth / 2, graphHeight / 2];
|
||||
const rot = alfa => (x, y) => [(x - cx) * Math.cos(alfa) - (y - cy) * Math.sin(alfa) + cx, (y - cy) * Math.cos(alfa) + (x - cx) * Math.sin(alfa) + cy];
|
||||
const rot = alfa => (x, y) =>
|
||||
[
|
||||
(x - cx) * Math.cos(alfa) - (y - cy) * Math.sin(alfa) + cx,
|
||||
(y - cy) * Math.cos(alfa) + (x - cx) * Math.sin(alfa) + cy
|
||||
];
|
||||
const shift = (dx, dy) => (x, y) => [x + dx, y + dy];
|
||||
const scale = r => (x, y) => [(x - cx) * r + cx, (y - cy) * r + cy];
|
||||
const flipH = (x, y) => [-x + 2 * cx, y];
|
||||
|
|
@ -185,7 +196,11 @@ window.UISubmap = (function () {
|
|||
let inverse = id;
|
||||
|
||||
if (angle) [projection, inverse] = [rot(angle), rot(-angle)];
|
||||
if (ratio) [projection, inverse] = [app(scale(Math.pow(1.1, ratio)), projection), app(inverse, scale(Math.pow(1.1, -ratio)))];
|
||||
if (ratio)
|
||||
[projection, inverse] = [
|
||||
app(scale(Math.pow(1.1, ratio)), projection),
|
||||
app(inverse, scale(Math.pow(1.1, -ratio)))
|
||||
];
|
||||
if (mirrorH) [projection, inverse] = [app(flipH, projection), app(inverse, flipH)];
|
||||
if (mirrorV) [projection, inverse] = [app(flipV, projection), app(inverse, flipV)];
|
||||
if (shiftX || shiftY) {
|
||||
|
|
@ -208,6 +223,14 @@ window.UISubmap = (function () {
|
|||
});
|
||||
}, 1000);
|
||||
|
||||
// calculate x y extreme points of viewBox
|
||||
function getViewBoxExtent() {
|
||||
return [
|
||||
[Math.abs(viewX / scale), Math.abs(viewY / scale)],
|
||||
[Math.abs(viewX / scale) + graphWidth / scale, Math.abs(viewY / scale) + graphHeight / scale]
|
||||
];
|
||||
}
|
||||
|
||||
// Create submap from the current map. Submap limits defined by the current window size (canvas viewport)
|
||||
const generateSubmap = debounce(function () {
|
||||
WARN && console.warn("Resampling current map");
|
||||
|
|
@ -244,7 +267,10 @@ window.UISubmap = (function () {
|
|||
|
||||
// fix scale
|
||||
distanceScaleInput.value = distanceScaleOutput.value = rn((distanceScale = distanceScaleOutput.value / scale), 2);
|
||||
populationRateInput.value = populationRateOutput.value = rn((populationRate = populationRateOutput.value / scale), 2);
|
||||
populationRateInput.value = populationRateOutput.value = rn(
|
||||
(populationRate = populationRateOutput.value / scale),
|
||||
2
|
||||
);
|
||||
customization = 0;
|
||||
startResample(options);
|
||||
}, 1000);
|
||||
|
|
@ -253,9 +279,9 @@ window.UISubmap = (function () {
|
|||
// Do model changes with Submap.resample then do view changes if needed
|
||||
resetZoom(0);
|
||||
let oldstate = {
|
||||
grid: deepCopy(grid),
|
||||
pack: deepCopy(pack),
|
||||
notes: deepCopy(notes),
|
||||
grid: structuredClone(grid),
|
||||
pack: structuredClone(pack),
|
||||
notes: structuredClone(notes),
|
||||
seed,
|
||||
graphWidth,
|
||||
graphHeight
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@ function editUnits() {
|
|||
closeDialogs("#unitsEditor, .stable");
|
||||
$("#unitsEditor").dialog();
|
||||
|
||||
if (modules.editUnits) return;
|
||||
modules.editUnits = true;
|
||||
if (fmg.modules.editUnits) return;
|
||||
fmg.modules.editUnits = true;
|
||||
|
||||
$("#unitsEditor").dialog({
|
||||
title: "Units Editor",
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@ function editWorld() {
|
|||
updateGlobeTemperature();
|
||||
updateGlobePosition();
|
||||
|
||||
if (modules.editWorld) return;
|
||||
modules.editWorld = true;
|
||||
if (fmg.modules.editWorld) return;
|
||||
fmg.modules.editWorld = true;
|
||||
|
||||
document.getElementById("worldControls").addEventListener("input", e => updateWorld(e.target));
|
||||
globe.select("#globeWindArrows").on("click", changeWind);
|
||||
|
|
@ -78,11 +78,15 @@ function editWorld() {
|
|||
const unit = distanceUnitInput.value;
|
||||
const meridian = toKilometer(eqD * 2 * scale);
|
||||
document.getElementById("mapSize").innerHTML = `${graphWidth}x${graphHeight}`;
|
||||
document.getElementById("mapSizeFriendly").innerHTML = `${rn(graphWidth * scale)}x${rn(graphHeight * scale)} ${unit}`;
|
||||
document.getElementById("mapSizeFriendly").innerHTML = `${rn(graphWidth * scale)}x${rn(
|
||||
graphHeight * scale
|
||||
)} ${unit}`;
|
||||
document.getElementById("meridianLength").innerHTML = rn(eqD * 2);
|
||||
document.getElementById("meridianLengthFriendly").innerHTML = `${rn(eqD * 2 * scale)} ${unit}`;
|
||||
document.getElementById("meridianLengthEarth").innerHTML = meridian ? " = " + rn(meridian / 200) + "%🌏" : "";
|
||||
document.getElementById("mapCoordinates").innerHTML = `${lat(mc.latN)} ${Math.abs(rn(mc.lonW))}°W; ${lat(mc.latS)} ${rn(mc.lonE)}°E`;
|
||||
document.getElementById("mapCoordinates").innerHTML = `${lat(mc.latN)} ${Math.abs(rn(mc.lonW))}°W; ${lat(
|
||||
mc.latS
|
||||
)} ${rn(mc.lonE)}°E`;
|
||||
|
||||
function toKilometer(v) {
|
||||
if (unit === "km") return v;
|
||||
|
|
@ -110,8 +114,12 @@ function editWorld() {
|
|||
const tPole = +document.getElementById("temperaturePoleOutput").value;
|
||||
document.getElementById("temperaturePoleF").innerHTML = rn((tPole * 9) / 5 + 32);
|
||||
globe.selectAll(".tempGradient90").attr("stop-color", clr(1 - (tPole - tMin) / (tMax - tMin)));
|
||||
globe.selectAll(".tempGradient60").attr("stop-color", clr(1 - (tEq - ((tEq - tPole) * 2) / 3 - tMin) / (tMax - tMin)));
|
||||
globe.selectAll(".tempGradient30").attr("stop-color", clr(1 - (tEq - ((tEq - tPole) * 1) / 3 - tMin) / (tMax - tMin)));
|
||||
globe
|
||||
.selectAll(".tempGradient60")
|
||||
.attr("stop-color", clr(1 - (tEq - ((tEq - tPole) * 2) / 3 - tMin) / (tMax - tMin)));
|
||||
globe
|
||||
.selectAll(".tempGradient30")
|
||||
.attr("stop-color", clr(1 - (tEq - ((tEq - tPole) * 1) / 3 - tMin) / (tMax - tMin)));
|
||||
globe.select(".tempGradient0").attr("stop-color", clr(1 - (tEq - tMin) / (tMax - tMin)));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,8 +8,8 @@ function editZones() {
|
|||
updateFilters();
|
||||
zonesEditorAddLines();
|
||||
|
||||
if (modules.editZones) return;
|
||||
modules.editZones = true;
|
||||
if (fmg.modules.editZones) return;
|
||||
fmg.modules.editZones = true;
|
||||
|
||||
$("#zonesEditor").dialog({
|
||||
title: "Zones Editor",
|
||||
|
|
@ -61,7 +61,8 @@ function editZones() {
|
|||
const filterSelect = document.getElementById("zonesFilterType");
|
||||
const typeToFilterBy = types.includes(zonesFilterType.value) ? zonesFilterType.value : "all";
|
||||
|
||||
filterSelect.innerHTML = "<option value='all'>all</option>" + types.map(type => `<option value="${type}">${type}</option>`).join("");
|
||||
filterSelect.innerHTML =
|
||||
"<option value='all'>all</option>" + types.map(type => `<option value="${type}">${type}</option>`).join("");
|
||||
filterSelect.value = typeToFilterBy;
|
||||
}
|
||||
|
||||
|
|
@ -80,9 +81,12 @@ function editZones() {
|
|||
const fill = zoneEl.getAttribute("fill");
|
||||
const area = getArea(d3.sum(c.map(i => pack.cells.area[i])));
|
||||
const rural = d3.sum(c.map(i => pack.cells.pop[i])) * populationRate;
|
||||
const urban = d3.sum(c.map(i => pack.cells.burg[i]).map(b => pack.burgs[b].population)) * populationRate * urbanization;
|
||||
const urban =
|
||||
d3.sum(c.map(i => pack.cells.burg[i]).map(b => pack.burgs[b].population)) * populationRate * urbanization;
|
||||
const population = rural + urban;
|
||||
const populationTip = `Total population: ${si(population)}; Rural population: ${si(rural)}; Urban population: ${si(urban)}. Click to change`;
|
||||
const populationTip = `Total population: ${si(population)}; Rural population: ${si(
|
||||
rural
|
||||
)}; Urban population: ${si(urban)}. Click to change`;
|
||||
const inactive = zoneEl.style.display === "none";
|
||||
const focused = defs.select("#fog #focus" + zoneEl.id).size();
|
||||
|
||||
|
|
@ -98,8 +102,12 @@ function editZones() {
|
|||
<span data-tip="${populationTip}" class="icon-male hide"></span>
|
||||
<div data-tip="${populationTip}" class="culturePopulation hide">${si(population)}</div>
|
||||
<span data-tip="Drag to raise or lower the zone" class="icon-resize-vertical hide"></span>
|
||||
<span data-tip="Toggle zone focus" class="icon-pin ${focused ? "" : " inactive"} hide ${c.length ? "" : " placeholder"}"></span>
|
||||
<span data-tip="Toggle zone visibility" class="icon-eye ${inactive ? " inactive" : ""} hide ${c.length ? "" : " placeholder"}"></span>
|
||||
<span data-tip="Toggle zone focus" class="icon-pin ${focused ? "" : " inactive"} hide ${
|
||||
c.length ? "" : " placeholder"
|
||||
}"></span>
|
||||
<span data-tip="Toggle zone visibility" class="icon-eye ${inactive ? " inactive" : ""} hide ${
|
||||
c.length ? "" : " placeholder"
|
||||
}"></span>
|
||||
<span data-tip="Remove zone" class="icon-trash-empty hide"></span>
|
||||
</div>`;
|
||||
});
|
||||
|
|
@ -109,7 +117,9 @@ function editZones() {
|
|||
// update footer
|
||||
const totalArea = getArea(graphWidth * graphHeight);
|
||||
zonesFooterArea.dataset.area = totalArea;
|
||||
const totalPop = (d3.sum(pack.cells.pop) + d3.sum(pack.burgs.filter(b => !b.removed).map(b => b.population)) * urbanization) * populationRate;
|
||||
const totalPop =
|
||||
(d3.sum(pack.cells.pop) + d3.sum(pack.burgs.filter(b => !b.removed).map(b => b.population)) * urbanization) *
|
||||
populationRate;
|
||||
zonesFooterPopulation.dataset.population = totalPop;
|
||||
zonesFooterNumber.innerHTML = /* html */ `${filteredZones.length} of ${zones.length}`;
|
||||
zonesFooterCells.innerHTML = pack.cells.i.length;
|
||||
|
|
@ -150,7 +160,13 @@ function editZones() {
|
|||
zonesEditorAddLines();
|
||||
}
|
||||
|
||||
$(body).sortable({items: "div.states", handle: ".icon-resize-vertical", containment: "parent", axis: "y", update: movezone});
|
||||
$(body).sortable({
|
||||
items: "div.states",
|
||||
handle: ".icon-resize-vertical",
|
||||
containment: "parent",
|
||||
axis: "y",
|
||||
update: movezone
|
||||
});
|
||||
function movezone(ev, ui) {
|
||||
const zone = $("#" + ui.item.attr("data-id"));
|
||||
const prev = $("#" + ui.item.prev().attr("data-id"));
|
||||
|
|
@ -174,7 +190,11 @@ function editZones() {
|
|||
$("#zonesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}});
|
||||
|
||||
tip("Click to select a zone, drag to paint a zone", true);
|
||||
viewbox.style("cursor", "crosshair").on("click", selectZoneOnMapClick).call(d3.drag().on("start", dragZoneBrush)).on("touchmove mousemove", moveZoneBrush);
|
||||
viewbox
|
||||
.style("cursor", "crosshair")
|
||||
.on("click", selectZoneOnMapClick)
|
||||
.call(d3.drag().on("start", dragZoneBrush))
|
||||
.on("touchmove mousemove", moveZoneBrush);
|
||||
|
||||
body.querySelector("div").classList.add("selected");
|
||||
zones.selectAll("g").each(function () {
|
||||
|
|
@ -285,7 +305,8 @@ function editZones() {
|
|||
zonesEditor.querySelectorAll(".hide:not(.show)").forEach(el => el.classList.remove("hidden"));
|
||||
zonesFooter.style.display = "block";
|
||||
body.querySelectorAll("div > input, select, svg").forEach(e => (e.style.pointerEvents = "all"));
|
||||
if (!close) $("#zonesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}});
|
||||
if (!close)
|
||||
$("#zonesEditor").dialog({position: {my: "right top", at: "right-10 top+10", of: "svg", collision: "fit"}});
|
||||
|
||||
restoreDefaultEvents();
|
||||
clearMainTip();
|
||||
|
|
@ -356,7 +377,8 @@ function editZones() {
|
|||
body.querySelectorAll(":scope > div").forEach(function (el) {
|
||||
el.querySelector(".stateCells").innerHTML = rn((+el.dataset.cells / totalCells) * 100, 2) + "%";
|
||||
el.querySelector(".biomeArea").innerHTML = rn((+el.dataset.area / totalArea) * 100, 2) + "%";
|
||||
el.querySelector(".culturePopulation").innerHTML = rn((+el.dataset.population / totalPopulation) * 100, 2) + "%";
|
||||
el.querySelector(".culturePopulation").innerHTML =
|
||||
rn((+el.dataset.population / totalPopulation) * 100, 2) + "%";
|
||||
});
|
||||
} else {
|
||||
body.dataset.type = "absolute";
|
||||
|
|
@ -369,7 +391,13 @@ function editZones() {
|
|||
const description = "Unknown zone";
|
||||
const type = "Unknown";
|
||||
const fill = "url(#hatch" + (id.slice(4) % 42) + ")";
|
||||
zones.append("g").attr("id", id).attr("data-description", description).attr("data-type", type).attr("data-cells", "").attr("fill", fill);
|
||||
zones
|
||||
.append("g")
|
||||
.attr("id", id)
|
||||
.attr("data-description", description)
|
||||
.attr("data-type", type)
|
||||
.attr("data-cells", "")
|
||||
.attr("fill", fill);
|
||||
|
||||
zonesEditorAddLines();
|
||||
}
|
||||
|
|
@ -411,13 +439,19 @@ function editZones() {
|
|||
const burgs = pack.burgs.filter(b => !b.removed && cells.includes(b.cell));
|
||||
|
||||
const rural = rn(d3.sum(cells.map(i => pack.cells.pop[i])) * populationRate);
|
||||
const urban = rn(d3.sum(cells.map(i => pack.cells.burg[i]).map(b => pack.burgs[b].population)) * populationRate * urbanization);
|
||||
const urban = rn(
|
||||
d3.sum(cells.map(i => pack.cells.burg[i]).map(b => pack.burgs[b].population)) * populationRate * urbanization
|
||||
);
|
||||
const total = rural + urban;
|
||||
const l = n => Number(n).toLocaleString();
|
||||
|
||||
alertMessage.innerHTML = /* html */ `Rural: <input type="number" min="0" step="1" id="ruralPop" value=${rural} style="width:6em" /> Urban:
|
||||
<input type="number" min="0" step="1" id="urbanPop" value=${urban} style="width:6em" ${burgs.length ? "" : "disabled"} />
|
||||
<p>Total population: ${l(total)} ⇒ <span id="totalPop">${l(total)}</span> (<span id="totalPopPerc">100</span>%)</p>`;
|
||||
<input type="number" min="0" step="1" id="urbanPop" value=${urban} style="width:6em" ${
|
||||
burgs.length ? "" : "disabled"
|
||||
} />
|
||||
<p>Total population: ${l(total)} ⇒ <span id="totalPop">${l(
|
||||
total
|
||||
)}</span> (<span id="totalPopPerc">100</span>%)</p>`;
|
||||
|
||||
const update = function () {
|
||||
const totalNew = ruralPop.valueAsNumber + urbanPop.valueAsNumber;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue