mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-22 03:51:23 +01:00
feat: routes overview - fix distanceScale value
This commit is contained in:
parent
28bc6ccde6
commit
834d6f6cc7
17 changed files with 328 additions and 130 deletions
|
|
@ -86,7 +86,7 @@ function getMapInfo() {
|
||||||
function getSettings() {
|
function getSettings() {
|
||||||
return {
|
return {
|
||||||
distanceUnit: distanceUnitInput.value,
|
distanceUnit: distanceUnitInput.value,
|
||||||
distanceScale: distanceScaleInput.value,
|
distanceScale,
|
||||||
areaUnit: areaUnit.value,
|
areaUnit: areaUnit.value,
|
||||||
heightUnit: heightUnit.value,
|
heightUnit: heightUnit.value,
|
||||||
heightExponent: heightExponentInput.value,
|
heightExponent: heightExponentInput.value,
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ function prepareMapData() {
|
||||||
const params = [version, license, dateString, seed, graphWidth, graphHeight, mapId].join("|");
|
const params = [version, license, dateString, seed, graphWidth, graphHeight, mapId].join("|");
|
||||||
const settings = [
|
const settings = [
|
||||||
distanceUnitInput.value,
|
distanceUnitInput.value,
|
||||||
distanceScaleInput.value,
|
distanceScale,
|
||||||
areaUnit.value,
|
areaUnit.value,
|
||||||
heightUnit.value,
|
heightUnit.value,
|
||||||
heightExponentInput.value,
|
heightExponentInput.value,
|
||||||
|
|
|
||||||
|
|
@ -601,6 +601,11 @@ window.Routes = (function () {
|
||||||
return routePoints;
|
return routePoints;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getLength(routeId) {
|
||||||
|
const path = routes.select("#route" + routeId).node();
|
||||||
|
return path.getTotalLength();
|
||||||
|
}
|
||||||
|
|
||||||
function remove(route) {
|
function remove(route) {
|
||||||
const routes = pack.cells.routes;
|
const routes = pack.cells.routes;
|
||||||
|
|
||||||
|
|
@ -630,6 +635,7 @@ window.Routes = (function () {
|
||||||
generateName,
|
generateName,
|
||||||
preparePointsArray,
|
preparePointsArray,
|
||||||
getPoints,
|
getPoints,
|
||||||
|
getLength,
|
||||||
remove
|
remove
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -37,12 +37,22 @@ class Battle {
|
||||||
|
|
||||||
// add listeners
|
// add listeners
|
||||||
document.getElementById("battleType").addEventListener("click", ev => this.toggleChange(ev));
|
document.getElementById("battleType").addEventListener("click", ev => this.toggleChange(ev));
|
||||||
document.getElementById("battleType").nextElementSibling.addEventListener("click", ev => Battle.prototype.context.changeType(ev));
|
document
|
||||||
document.getElementById("battleNameShow").addEventListener("click", () => Battle.prototype.context.showNameSection());
|
.getElementById("battleType")
|
||||||
document.getElementById("battleNamePlace").addEventListener("change", ev => (Battle.prototype.context.place = ev.target.value));
|
.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("battleNameFull").addEventListener("change", ev => Battle.prototype.context.changeName(ev));
|
||||||
document.getElementById("battleNameCulture").addEventListener("click", () => Battle.prototype.context.generateName("culture"));
|
document
|
||||||
document.getElementById("battleNameRandom").addEventListener("click", () => Battle.prototype.context.generateName("random"));
|
.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("battleNameHide").addEventListener("click", this.hideNameSection);
|
||||||
document.getElementById("battleAddRegiment").addEventListener("click", this.addSide);
|
document.getElementById("battleAddRegiment").addEventListener("click", this.addSide);
|
||||||
document.getElementById("battleRoll").addEventListener("click", () => Battle.prototype.context.randomize());
|
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("battleWiki").addEventListener("click", () => wiki("Battle-Simulator"));
|
||||||
|
|
||||||
document.getElementById("battlePhase_attackers").addEventListener("click", ev => this.toggleChange(ev));
|
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").addEventListener("click", ev => this.toggleChange(ev));
|
||||||
document.getElementById("battlePhase_defenders").nextElementSibling.addEventListener("click", ev => Battle.prototype.context.changePhase(ev, "defenders"));
|
document
|
||||||
document.getElementById("battleDie_attackers").addEventListener("click", () => Battle.prototype.context.rollDie("attackers"));
|
.getElementById("battlePhase_defenders")
|
||||||
document.getElementById("battleDie_defenders").addEventListener("click", () => Battle.prototype.context.rollDie("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() {
|
defineType() {
|
||||||
|
|
@ -82,8 +100,12 @@ class Battle {
|
||||||
document.getElementById("battleType").className = "icon-button-" + this.type;
|
document.getElementById("battleType").className = "icon-button-" + this.type;
|
||||||
|
|
||||||
const sideSpecific = document.getElementById("battlePhases_" + this.type + "_attackers");
|
const sideSpecific = document.getElementById("battlePhases_" + this.type + "_attackers");
|
||||||
const attackers = sideSpecific ? sideSpecific.content : document.getElementById("battlePhases_" + this.type).content;
|
const attackers = sideSpecific
|
||||||
const defenders = sideSpecific ? document.getElementById("battlePhases_" + this.type + "_defenders").content : attackers;
|
? 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_attackers").nextElementSibling.innerHTML = "";
|
||||||
document.getElementById("battlePhase_defenders").nextElementSibling.innerHTML = "";
|
document.getElementById("battlePhase_defenders").nextElementSibling.innerHTML = "";
|
||||||
|
|
@ -139,26 +161,37 @@ class Battle {
|
||||||
regiment.survivors = Object.assign({}, regiment.u);
|
regiment.survivors = Object.assign({}, regiment.u);
|
||||||
|
|
||||||
const state = pack.states[regiment.state];
|
const state = pack.states[regiment.state];
|
||||||
const distance = (Math.hypot(this.y - regiment.by, this.x - regiment.bx) * distanceScaleInput.value) | 0; // distance between regiment and its base
|
const distance = (Math.hypot(this.y - regiment.by, this.x - regiment.bx) * distanceScale) | 0; // distance between regiment and its base
|
||||||
const color = state.color[0] === "#" ? state.color : "#999";
|
const color = state.color[0] === "#" ? state.color : "#999";
|
||||||
const icon = `<svg width="1.4em" height="1.4em" style="margin-bottom: -.6em; stroke: #333">
|
const icon = `<svg width="1.4em" height="1.4em" style="margin-bottom: -.6em; stroke: #333">
|
||||||
<rect x="0" y="0" width="100%" height="100%" fill="${color}"></rect>
|
<rect x="0" y="0" width="100%" height="100%" fill="${color}"></rect>
|
||||||
<text x="0" y="1.04em" style="">${regiment.icon}</text></svg>`;
|
<text x="0" y="1.04em" style="">${regiment.icon}</text></svg>`;
|
||||||
const body = `<tbody id="battle${state.i}-${regiment.i}">`;
|
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 initial = `<tr class="battleInitial"><td>${icon}</td><td class="regiment" data-tip="${
|
||||||
let casualties = `<tr class="battleCasualties"><td></td><td data-tip="${state.fullName}">${state.fullName.slice(0, 26)}</td>`;
|
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>`;
|
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) {
|
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>`;
|
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>`;
|
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>`;
|
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;
|
const div = side === "attackers" ? battleAttackers : battleDefenders;
|
||||||
div.innerHTML += body + initial + casualties + survivors + "</tbody>";
|
div.innerHTML += body + initial + casualties + survivors + "</tbody>";
|
||||||
|
|
@ -173,17 +206,23 @@ class Battle {
|
||||||
.filter(s => s.military && !s.removed)
|
.filter(s => s.military && !s.removed)
|
||||||
.map(s => s.military)
|
.map(s => s.military)
|
||||||
.flat();
|
.flat();
|
||||||
const distance = reg => rn(Math.hypot(context.y - reg.y, context.x - reg.x) * distanceScaleInput.value) + " " + distanceUnitInput.value;
|
const distance = reg =>
|
||||||
const isAdded = reg => context.defenders.regiments.some(r => r === reg) || context.attackers.regiments.some(r => r === reg);
|
rn(Math.hypot(context.y - reg.y, context.x - reg.x) * distanceScale) + " " + distanceUnitInput.value;
|
||||||
|
const isAdded = reg =>
|
||||||
|
context.defenders.regiments.some(r => r === reg) || context.attackers.regiments.some(r => r === reg);
|
||||||
|
|
||||||
body.innerHTML = regiments
|
body.innerHTML = regiments
|
||||||
.map(r => {
|
.map(r => {
|
||||||
const s = pack.states[r.state],
|
const s = pack.states[r.state],
|
||||||
added = isAdded(r),
|
added = isAdded(r),
|
||||||
dist = added ? "0 " + distanceUnitInput.value : distance(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">
|
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:6em">${s.name.slice(0, 11)}</div>
|
||||||
<div style="width:1.2em">${r.icon}</div>
|
<div style="width:1.2em">${r.icon}</div>
|
||||||
<div style="width:13em">${r.name.slice(0, 24)}</div>
|
<div style="width:13em">${r.name.slice(0, 24)}</div>
|
||||||
|
|
@ -267,7 +306,10 @@ class Battle {
|
||||||
}
|
}
|
||||||
|
|
||||||
generateName(type) {
|
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("battleNamePlace").value = this.place = place;
|
||||||
document.getElementById("battleNameFull").value = this.name = this.defineName();
|
document.getElementById("battleNameFull").value = this.name = this.defineName();
|
||||||
$("#battleScreen").dialog({title: this.name});
|
$("#battleScreen").dialog({title: this.name});
|
||||||
|
|
@ -286,35 +328,161 @@ class Battle {
|
||||||
calculateStrength(side) {
|
calculateStrength(side) {
|
||||||
const scheme = {
|
const scheme = {
|
||||||
// field battle phases
|
// 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
|
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
|
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
|
// 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
|
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
|
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
|
// 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
|
blockade: {
|
||||||
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
|
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
|
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
|
bombardment: {
|
||||||
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
|
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
|
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
|
looting: {
|
||||||
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
|
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
|
// ambush phases
|
||||||
surprise: {melee: 2, ranged: 2.4, mounted: 1, machinery: 1, naval: 1, armored: 1, aviation: 0.8, magical: 1.2}, // increased
|
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
|
// 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
|
landing: {
|
||||||
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
|
melee: 0.8,
|
||||||
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
|
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
|
// air battle phases
|
||||||
maneuvering: {melee: 0, ranged: 0.1, mounted: 0, machinery: 0.2, naval: 0, armored: 0, aviation: 1, magical: 0.2}, // aviation
|
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 forces = this.getJoinedForces(this[side].regiments);
|
||||||
const phase = this[side].phase;
|
const phase = this[side].phase;
|
||||||
const adjuster = Math.max(populationRate / 10, 10); // population adjuster, by default 100
|
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;
|
const UIvalue = this[side].power ? Math.max(this[side].power | 0, 1) : 0;
|
||||||
document.getElementById("battlePower_" + side).innerHTML = UIvalue;
|
document.getElementById("battlePower_" + side).innerHTML = UIvalue;
|
||||||
}
|
}
|
||||||
|
|
@ -723,11 +892,13 @@ class Battle {
|
||||||
|
|
||||||
const status = battleStatus[+P(0.7)];
|
const status = battleStatus[+P(0.7)];
|
||||||
const result = `The ${this.getTypeName(this.type)} ended in ${status}`;
|
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(
|
const legend = `${this.name} took place in ${options.year} ${options.eraShort}. It was fought between ${getSide(
|
||||||
this.defenders.regiments,
|
this.attackers.regiments,
|
||||||
0
|
1
|
||||||
)}. ${result}.
|
)} and ${getSide(this.defenders.regiments, 0)}. ${result}.
|
||||||
\r\nAttackers losses: ${getLosses(this.attackers.casualties)}%, defenders losses: ${getLosses(this.defenders.casualties)}%`;
|
\r\nAttackers losses: ${getLosses(this.attackers.casualties)}%, defenders losses: ${getLosses(
|
||||||
|
this.defenders.casualties
|
||||||
|
)}%`;
|
||||||
notes.push({id: `marker${i}`, name: this.name, legend});
|
notes.push({id: `marker${i}`, name: this.name, legend});
|
||||||
|
|
||||||
tip(`${this.name} is over. ${result}`, true, "success", 4000);
|
tip(`${this.name} is over. ${result}`, true, "success", 4000);
|
||||||
|
|
|
||||||
|
|
@ -1175,7 +1175,6 @@ function getAreaUnit(squareMark = "²") {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getArea(rawArea) {
|
function getArea(rawArea) {
|
||||||
const distanceScale = byId("distanceScaleInput")?.value;
|
|
||||||
return rawArea * distanceScale ** 2;
|
return rawArea * distanceScale ** 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -48,8 +48,7 @@ function editLake() {
|
||||||
document.getElementById("lakeArea").value = si(getArea(l.area)) + " " + getAreaUnit();
|
document.getElementById("lakeArea").value = si(getArea(l.area)) + " " + getAreaUnit();
|
||||||
|
|
||||||
const length = d3.polygonLength(l.vertices.map(v => pack.vertices.p[v]));
|
const length = d3.polygonLength(l.vertices.map(v => pack.vertices.p[v]));
|
||||||
document.getElementById("lakeShoreLength").value =
|
document.getElementById("lakeShoreLength").value = si(length * distanceScale) + " " + distanceUnitInput.value;
|
||||||
si(length * distanceScaleInput.value) + " " + distanceUnitInput.value;
|
|
||||||
|
|
||||||
const lakeCells = Array.from(cells.i.filter(i => cells.f[i] === l.i));
|
const lakeCells = Array.from(cells.i.filter(i => cells.f[i] === l.i));
|
||||||
const heights = lakeCells.map(i => cells.h[i]);
|
const heights = lakeCells.map(i => cells.h[i]);
|
||||||
|
|
|
||||||
|
|
@ -1792,7 +1792,6 @@ function toggleScaleBar(event) {
|
||||||
function drawScaleBar(scaleBar, scaleLevel) {
|
function drawScaleBar(scaleBar, scaleLevel) {
|
||||||
if (!scaleBar.size() || scaleBar.style("display") === "none") return;
|
if (!scaleBar.size() || scaleBar.style("display") === "none") return;
|
||||||
|
|
||||||
const distanceScale = +distanceScaleInput.value;
|
|
||||||
const unit = distanceUnitInput.value;
|
const unit = distanceUnitInput.value;
|
||||||
const size = +scaleBar.attr("data-bar-size");
|
const size = +scaleBar.attr("data-bar-size");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ class Measurer {
|
||||||
}
|
}
|
||||||
|
|
||||||
getDash() {
|
getDash() {
|
||||||
return rn(30 / distanceScaleInput.value, 2);
|
return rn(30 / distanceScale, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
drag() {
|
drag() {
|
||||||
|
|
@ -205,7 +205,7 @@ class Ruler extends Measurer {
|
||||||
|
|
||||||
updateLabel() {
|
updateLabel() {
|
||||||
const length = this.getLength();
|
const length = this.getLength();
|
||||||
const text = rn(length * distanceScaleInput.value) + " " + distanceUnitInput.value;
|
const text = rn(length * distanceScale) + " " + distanceUnitInput.value;
|
||||||
const [x, y] = last(this.points);
|
const [x, y] = last(this.points);
|
||||||
this.el.select("text").attr("x", x).attr("y", y).text(text);
|
this.el.select("text").attr("x", x).attr("y", y).text(text);
|
||||||
}
|
}
|
||||||
|
|
@ -337,7 +337,7 @@ class Opisometer extends Measurer {
|
||||||
|
|
||||||
updateLabel() {
|
updateLabel() {
|
||||||
const length = this.el.select("path").node().getTotalLength();
|
const length = this.el.select("path").node().getTotalLength();
|
||||||
const text = rn(length * distanceScaleInput.value) + " " + distanceUnitInput.value;
|
const text = rn(length * distanceScale) + " " + distanceUnitInput.value;
|
||||||
const [x, y] = last(this.points);
|
const [x, y] = last(this.points);
|
||||||
this.el.select("text").attr("x", x).attr("y", y).text(text);
|
this.el.select("text").attr("x", x).attr("y", y).text(text);
|
||||||
}
|
}
|
||||||
|
|
@ -475,7 +475,7 @@ class RouteOpisometer extends Measurer {
|
||||||
|
|
||||||
updateLabel() {
|
updateLabel() {
|
||||||
const length = this.el.select("path").node().getTotalLength();
|
const length = this.el.select("path").node().getTotalLength();
|
||||||
const text = rn(length * distanceScaleInput.value) + " " + distanceUnitInput.value;
|
const text = rn(length * distanceScale) + " " + distanceUnitInput.value;
|
||||||
const [x, y] = last(this.points);
|
const [x, y] = last(this.points);
|
||||||
this.el.select("text").attr("x", x).attr("y", y).text(text);
|
this.el.select("text").attr("x", x).attr("y", y).text(text);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -545,6 +545,7 @@ function applyStoredOptions() {
|
||||||
lock(key);
|
lock(key);
|
||||||
|
|
||||||
if (key === "points") changeCellsDensity(+value);
|
if (key === "points") changeCellsDensity(+value);
|
||||||
|
if (key === "distanceScale") distanceScale = +value;
|
||||||
|
|
||||||
// add saved style presets to options
|
// add saved style presets to options
|
||||||
if (key.slice(0, 5) === "style") applyOption(stylePreset, key, key.slice(5));
|
if (key.slice(0, 5) === "style") applyOption(stylePreset, key, key.slice(5));
|
||||||
|
|
@ -605,7 +606,8 @@ function randomizeOptions() {
|
||||||
|
|
||||||
// 'Units Editor' settings
|
// 'Units Editor' settings
|
||||||
const US = navigator.language === "en-US";
|
const US = navigator.language === "en-US";
|
||||||
if (randomize || !locked("distanceScale")) distanceScaleOutput.value = distanceScaleInput.value = gauss(3, 1, 1, 5);
|
if (randomize || !locked("distanceScale"))
|
||||||
|
distanceScale = distanceScaleOutput.value = distanceScaleInput.value = gauss(3, 1, 1, 5);
|
||||||
if (!stored("distanceUnit")) distanceUnitInput.value = US ? "mi" : "km";
|
if (!stored("distanceUnit")) distanceUnitInput.value = US ? "mi" : "km";
|
||||||
if (!stored("heightUnit")) heightUnit.value = US ? "ft" : "m";
|
if (!stored("heightUnit")) heightUnit.value = US ? "ft" : "m";
|
||||||
if (!stored("temperatureScale")) temperatureScale.value = US ? "°F" : "°C";
|
if (!stored("temperatureScale")) temperatureScale.value = US ? "°F" : "°C";
|
||||||
|
|
|
||||||
|
|
@ -81,7 +81,7 @@ function editRiver(id) {
|
||||||
|
|
||||||
function updateRiverLength(river) {
|
function updateRiverLength(river) {
|
||||||
river.length = rn(elSelected.node().getTotalLength() / 2, 2);
|
river.length = rn(elSelected.node().getTotalLength() / 2, 2);
|
||||||
const lengthUI = `${rn(river.length * distanceScaleInput.value)} ${distanceUnitInput.value}`;
|
const lengthUI = `${rn(river.length * distanceScale)} ${distanceUnitInput.value}`;
|
||||||
byId("riverLength").value = lengthUI;
|
byId("riverLength").value = lengthUI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -91,7 +91,7 @@ function editRiver(id) {
|
||||||
const meanderedPoints = addMeandering(cells);
|
const meanderedPoints = addMeandering(cells);
|
||||||
river.width = getWidth(getOffset(discharge, meanderedPoints.length, widthFactor, sourceWidth));
|
river.width = getWidth(getOffset(discharge, meanderedPoints.length, widthFactor, sourceWidth));
|
||||||
|
|
||||||
const width = `${rn(river.width * distanceScaleInput.value, 3)} ${distanceUnitInput.value}`;
|
const width = `${rn(river.width * distanceScale, 3)} ${distanceUnitInput.value}`;
|
||||||
byId("riverWidth").value = width;
|
byId("riverWidth").value = width;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -235,7 +235,7 @@ function editRiver(id) {
|
||||||
.data()
|
.data()
|
||||||
.map(([x, y]) => findCell(x, y));
|
.map(([x, y]) => findCell(x, y));
|
||||||
const river = getRiver();
|
const river = getRiver();
|
||||||
const riverLen = rn(river.length * distanceScaleInput.value);
|
const riverLen = rn(river.length * distanceScale);
|
||||||
showElevationProfile(points, riverLen, true);
|
showElevationProfile(points, riverLen, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -35,8 +35,8 @@ function overviewRivers() {
|
||||||
|
|
||||||
for (const r of pack.rivers) {
|
for (const r of pack.rivers) {
|
||||||
const discharge = r.discharge + " m³/s";
|
const discharge = r.discharge + " m³/s";
|
||||||
const length = rn(r.length * distanceScaleInput.value) + " " + unit;
|
const length = rn(r.length * distanceScale) + " " + unit;
|
||||||
const width = rn(r.width * distanceScaleInput.value, 3) + " " + unit;
|
const width = rn(r.width * distanceScale, 3) + " " + unit;
|
||||||
const basin = pack.rivers.find(river => river.i === r.basin)?.name;
|
const basin = pack.rivers.find(river => river.i === r.basin)?.name;
|
||||||
|
|
||||||
lines += /* html */ `<div
|
lines += /* html */ `<div
|
||||||
|
|
@ -67,9 +67,9 @@ function overviewRivers() {
|
||||||
const averageDischarge = rn(d3.mean(pack.rivers.map(r => r.discharge)));
|
const averageDischarge = rn(d3.mean(pack.rivers.map(r => r.discharge)));
|
||||||
riversFooterDischarge.innerHTML = averageDischarge + " m³/s";
|
riversFooterDischarge.innerHTML = averageDischarge + " m³/s";
|
||||||
const averageLength = rn(d3.mean(pack.rivers.map(r => r.length)));
|
const averageLength = rn(d3.mean(pack.rivers.map(r => r.length)));
|
||||||
riversFooterLength.innerHTML = averageLength * distanceScaleInput.value + " " + unit;
|
riversFooterLength.innerHTML = averageLength * distanceScale + " " + unit;
|
||||||
const averageWidth = rn(d3.mean(pack.rivers.map(r => r.width)), 3);
|
const averageWidth = rn(d3.mean(pack.rivers.map(r => r.width)), 3);
|
||||||
riversFooterWidth.innerHTML = rn(averageWidth * distanceScaleInput.value, 3) + " " + unit;
|
riversFooterWidth.innerHTML = rn(averageWidth * distanceScale, 3) + " " + unit;
|
||||||
|
|
||||||
// add listeners
|
// add listeners
|
||||||
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseenter", ev => riverHighlightOn(ev)));
|
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseenter", ev => riverHighlightOn(ev)));
|
||||||
|
|
@ -143,8 +143,8 @@ function overviewRivers() {
|
||||||
body.querySelectorAll(":scope > div").forEach(function (el) {
|
body.querySelectorAll(":scope > div").forEach(function (el) {
|
||||||
const d = el.dataset;
|
const d = el.dataset;
|
||||||
const discharge = d.discharge + " m³/s";
|
const discharge = d.discharge + " m³/s";
|
||||||
const length = rn(d.length * distanceScaleInput.value) + " " + distanceUnitInput.value;
|
const length = rn(d.length * distanceScale) + " " + distanceUnitInput.value;
|
||||||
const width = rn(d.width * distanceScaleInput.value, 3) + " " + distanceUnitInput.value;
|
const width = rn(d.width * distanceScale, 3) + " " + distanceUnitInput.value;
|
||||||
data += [d.id, d.name, d.type, discharge, length, width, d.basin].join(",") + "\n";
|
data += [d.id, d.name, d.type, discharge, length, width, d.basin].join(",") + "\n";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,7 +37,7 @@ function editRoute(id) {
|
||||||
// add listeners
|
// add listeners
|
||||||
byId("routeCreateSelectingCells").on("click", showCreationDialog);
|
byId("routeCreateSelectingCells").on("click", showCreationDialog);
|
||||||
byId("routeSplit").on("click", togglePressed);
|
byId("routeSplit").on("click", togglePressed);
|
||||||
byId("routeJoin").on("click", joinRoutes);
|
byId("routeJoin").on("click", openJoinRoutesDialog);
|
||||||
byId("routeElevationProfile").on("click", showRouteElevationProfile);
|
byId("routeElevationProfile").on("click", showRouteElevationProfile);
|
||||||
byId("routeLegend").on("click", editRouteLegend);
|
byId("routeLegend").on("click", editRouteLegend);
|
||||||
byId("routeRemove").on("click", removeRoute);
|
byId("routeRemove").on("click", removeRoute);
|
||||||
|
|
@ -71,9 +71,8 @@ function editRoute(id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateRouteLength(route) {
|
function updateRouteLength(route) {
|
||||||
route.length = rn(elSelected.node().getTotalLength() / 2, 2);
|
route.length = Routes.getLength(route.i);
|
||||||
const lengthUI = `${rn(route.length * distanceScale)} ${distanceUnitInput.value}`;
|
byId("routeLength").value = rn(route.length * distanceScale) + " " + distanceUnitInput.value;
|
||||||
byId("routeLength").value = lengthUI;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawControlPoints(points) {
|
function drawControlPoints(points) {
|
||||||
|
|
@ -247,61 +246,86 @@ function editRoute(id) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function joinRoutes() {
|
function openJoinRoutesDialog() {
|
||||||
const route = getRoute();
|
const route = getRoute();
|
||||||
const firstCell = route.cells.at(0);
|
const firstCell = route.cells.at(0);
|
||||||
const lastCell = route.cells.at(-1);
|
const lastCell = route.cells.at(-1);
|
||||||
|
|
||||||
let joinedRoute = null;
|
const candidateRoutes = pack.routes.filter(r => {
|
||||||
for (const nextRoute of pack.routes) {
|
if (r.i === route.i) return false;
|
||||||
if (joinedRoute) break;
|
if (r.group !== route.group) return false;
|
||||||
if (nextRoute.i === route.i) continue;
|
if (r.cells.at(0) === lastCell) return true;
|
||||||
if (nextRoute.cells.at(0) === lastCell) joinedRoute = nextRoute;
|
if (r.cells.at(-1) === firstCell) return true;
|
||||||
if (nextRoute.cells.at(-1) === firstCell) joinedRoute = nextRoute;
|
if (r.cells.at(0) === firstCell) return true;
|
||||||
if (nextRoute.cells.at(0) === firstCell) joinedRoute = nextRoute;
|
if (r.cells.at(-1) === lastCell) return true;
|
||||||
if (nextRoute.cells.at(-1) === lastCell) joinedRoute = nextRoute;
|
return false;
|
||||||
}
|
});
|
||||||
|
|
||||||
if (joinedRoute) {
|
if (candidateRoutes.length) {
|
||||||
join(route, joinedRoute);
|
const options = candidateRoutes.map(r => {
|
||||||
tip("Routes joined", false, "success", 5000);
|
r.name = r.name || Routes.generateName(r);
|
||||||
|
r.length = r.length || getRouteLength(r.i);
|
||||||
|
const length = rn(r.length * distanceScale) + " " + unit;
|
||||||
|
return `<option value="${r.i}">${r.name} (${length})</option>`;
|
||||||
|
});
|
||||||
|
alertMessage.innerHTML = /* html */ `<div>Route to join with:
|
||||||
|
<select>${options.join("")}</select>
|
||||||
|
</div>`;
|
||||||
|
|
||||||
|
$("#alert").dialog({
|
||||||
|
title: "Join routes",
|
||||||
|
width: fitContent(),
|
||||||
|
position: {my: "center", at: "center", of: "svg"},
|
||||||
|
buttons: {
|
||||||
|
Cancel: () => {
|
||||||
|
$("#alert").dialog("close");
|
||||||
|
},
|
||||||
|
Join: () => {
|
||||||
|
const selectedRouteId = +alertMessage.querySelector("select").value;
|
||||||
|
const selectedRoute = pack.routes.find(r => r.i === selectedRouteId);
|
||||||
|
joinRoutes(route, selectedRoute);
|
||||||
|
tip("Routes joined", false, "success", 5000);
|
||||||
|
$("#alert").dialog("close");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
tip("No routes to join with. Route must start or end at current route's start or end cell", false, "error", 4000);
|
tip("No routes to join with. Route must start or end at current route's start or end cell", false, "error", 4000);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function join(route, joinedRoute) {
|
function joinRoutes(route, joinedRoute) {
|
||||||
if (!route.points) route.points = debug.selectAll("#controlPoints > *").data();
|
if (!route.points) route.points = debug.selectAll("#controlPoints > *").data();
|
||||||
if (!joinedRoute.points) joinedRoute.points = Routes.getPoints(joinedRoute, Routes.preparePointsArray());
|
if (!joinedRoute.points) joinedRoute.points = Routes.getPoints(joinedRoute, Routes.preparePointsArray());
|
||||||
|
|
||||||
if (route.cells.at(-1) === joinedRoute.cells.at(0)) {
|
if (route.cells.at(-1) === joinedRoute.cells.at(0)) {
|
||||||
// joinedRoute starts at the end of current route
|
// joinedRoute starts at the end of current route
|
||||||
route.cells = [...route.cells, ...joinedRoute.cells.slice(1)];
|
route.cells = [...route.cells, ...joinedRoute.cells.slice(1)];
|
||||||
route.points = [...route.points, ...joinedRoute.points.slice(1)];
|
route.points = [...route.points, ...joinedRoute.points.slice(1)];
|
||||||
} else if (route.cells.at(0) === joinedRoute.cells.at(-1)) {
|
} else if (route.cells.at(0) === joinedRoute.cells.at(-1)) {
|
||||||
// joinedRoute ends at the start of current route
|
// joinedRoute ends at the start of current route
|
||||||
route.cells = [...joinedRoute.cells, ...route.cells.slice(1)];
|
route.cells = [...joinedRoute.cells, ...route.cells.slice(1)];
|
||||||
route.points = [...joinedRoute.points, ...route.points.slice(1)];
|
route.points = [...joinedRoute.points, ...route.points.slice(1)];
|
||||||
} else if (route.cells.at(0) === joinedRoute.cells.at(0)) {
|
} else if (route.cells.at(0) === joinedRoute.cells.at(0)) {
|
||||||
// joinedRoute and current route both start at the same cell
|
// joinedRoute and current route both start at the same cell
|
||||||
route.cells = [...route.cells.reverse(), ...joinedRoute.cells.slice(1)];
|
route.cells = [...route.cells.reverse(), ...joinedRoute.cells.slice(1)];
|
||||||
route.points = [...route.points.reverse(), ...joinedRoute.points.slice(1)];
|
route.points = [...route.points.reverse(), ...joinedRoute.points.slice(1)];
|
||||||
} else if (route.cells.at(-1) === joinedRoute.cells.at(-1)) {
|
} else if (route.cells.at(-1) === joinedRoute.cells.at(-1)) {
|
||||||
// joinedRoute and current route both end at the same cell
|
// joinedRoute and current route both end at the same cell
|
||||||
route.cells = [...route.cells, ...joinedRoute.cells.reverse().slice(1)];
|
route.cells = [...route.cells, ...joinedRoute.cells.reverse().slice(1)];
|
||||||
route.points = [...route.points, ...joinedRoute.points.reverse().slice(1)];
|
route.points = [...route.points, ...joinedRoute.points.reverse().slice(1)];
|
||||||
}
|
|
||||||
|
|
||||||
for (let i = 0; i < route.cells.length; i++) {
|
|
||||||
const cellId = route.cells[i];
|
|
||||||
const nextCellId = route.cells[i + 1];
|
|
||||||
if (nextCellId) addConnection(cellId, nextCellId, route.i);
|
|
||||||
}
|
|
||||||
|
|
||||||
Routes.remove(joinedRoute);
|
|
||||||
drawControlPoints(route.points);
|
|
||||||
drawCells();
|
|
||||||
redrawRoute();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < route.cells.length; i++) {
|
||||||
|
const cellId = route.cells[i];
|
||||||
|
const nextCellId = route.cells[i + 1];
|
||||||
|
if (nextCellId) addConnection(cellId, nextCellId, route.i);
|
||||||
|
}
|
||||||
|
|
||||||
|
Routes.remove(joinedRoute);
|
||||||
|
drawControlPoints(route.points);
|
||||||
|
drawCells();
|
||||||
|
redrawRoute();
|
||||||
}
|
}
|
||||||
|
|
||||||
function showCreationDialog() {
|
function showCreationDialog() {
|
||||||
|
|
@ -346,8 +370,8 @@ function editRoute(id) {
|
||||||
|
|
||||||
function showRouteElevationProfile() {
|
function showRouteElevationProfile() {
|
||||||
const route = getRoute();
|
const route = getRoute();
|
||||||
const routeLen = rn(route.length * distanceScaleInput.value);
|
const length = rn(route.length * distanceScale);
|
||||||
showElevationProfile(route.cells, routeLen, false);
|
showElevationProfile(route.cells, length, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
function editRouteLegend() {
|
function editRouteLegend() {
|
||||||
|
|
|
||||||
|
|
@ -29,12 +29,11 @@ function overviewRoutes() {
|
||||||
function routesOverviewAddLines() {
|
function routesOverviewAddLines() {
|
||||||
body.innerHTML = "";
|
body.innerHTML = "";
|
||||||
let lines = "";
|
let lines = "";
|
||||||
const unit = distanceUnitInput.value;
|
|
||||||
|
|
||||||
for (const route of pack.routes) {
|
for (const route of pack.routes) {
|
||||||
route.name = route.name || Routes.generateName(route);
|
route.name = route.name || Routes.generateName(route);
|
||||||
route.length = route.length || getRouteLength(route.i);
|
route.length = route.length || Routes.getLength(route.i);
|
||||||
const length = rn(route.length * distanceScale) + " " + unit;
|
const length = rn(route.length * distanceScale) + " " + distanceUnitInput.value;
|
||||||
|
|
||||||
lines += /* html */ `<div
|
lines += /* html */ `<div
|
||||||
class="states"
|
class="states"
|
||||||
|
|
@ -56,7 +55,7 @@ function overviewRoutes() {
|
||||||
// update footer
|
// update footer
|
||||||
routesFooterNumber.innerHTML = pack.routes.length;
|
routesFooterNumber.innerHTML = pack.routes.length;
|
||||||
const averageLength = rn(d3.mean(pack.routes.map(r => r.length)));
|
const averageLength = rn(d3.mean(pack.routes.map(r => r.length)));
|
||||||
routesFooterLength.innerHTML = averageLength * distanceScale + " " + unit;
|
routesFooterLength.innerHTML = averageLength * distanceScale + " " + distanceUnitInput.value;
|
||||||
|
|
||||||
// add listeners
|
// add listeners
|
||||||
body.querySelectorAll("div.states").forEach(el => el.on("mouseenter", routeHighlightOn));
|
body.querySelectorAll("div.states").forEach(el => el.on("mouseenter", routeHighlightOn));
|
||||||
|
|
@ -68,11 +67,6 @@ function overviewRoutes() {
|
||||||
applySorting(routesHeader);
|
applySorting(routesHeader);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getRouteLength(routeId) {
|
|
||||||
const path = routes.select("#route" + routeId).node();
|
|
||||||
return rn(path.getTotalLength() / 2, 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
function routeHighlightOn(event) {
|
function routeHighlightOn(event) {
|
||||||
if (!layerIsOn("toggleRoutes")) toggleRoutes();
|
if (!layerIsOn("toggleRoutes")) toggleRoutes();
|
||||||
const routeId = +event.target.dataset.id;
|
const routeId = +event.target.dataset.id;
|
||||||
|
|
|
||||||
|
|
@ -503,7 +503,7 @@ styleGridScale.addEventListener("input", function () {
|
||||||
|
|
||||||
function calculateFriendlyGridSize() {
|
function calculateFriendlyGridSize() {
|
||||||
const size = styleGridScale.value * 25;
|
const size = styleGridScale.value * 25;
|
||||||
const friendly = `${rn(size * distanceScaleInput.value, 2)} ${distanceUnitInput.value}`;
|
const friendly = `${rn(size * distanceScale, 2)} ${distanceUnitInput.value}`;
|
||||||
styleGridSizeFriendly.value = friendly;
|
styleGridSizeFriendly.value = friendly;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -258,11 +258,16 @@ window.UISubmap = (function () {
|
||||||
byId("latitudeInput").value = latitudeOutput.value;
|
byId("latitudeInput").value = latitudeOutput.value;
|
||||||
|
|
||||||
// fix scale
|
// fix scale
|
||||||
distanceScaleInput.value = distanceScaleOutput.value = rn((distanceScale = distanceScaleOutput.value / scale), 2);
|
distanceScale =
|
||||||
|
distanceScaleInput.value =
|
||||||
|
distanceScaleOutput.value =
|
||||||
|
rn((distanceScale = distanceScaleOutput.value / scale), 2);
|
||||||
|
|
||||||
populationRateInput.value = populationRateOutput.value = rn(
|
populationRateInput.value = populationRateOutput.value = rn(
|
||||||
(populationRate = populationRateOutput.value / scale),
|
(populationRate = populationRateOutput.value / scale),
|
||||||
2
|
2
|
||||||
);
|
);
|
||||||
|
|
||||||
customization = 0;
|
customization = 0;
|
||||||
startResample(options);
|
startResample(options);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
|
|
|
||||||
|
|
@ -55,6 +55,7 @@ function editUnits() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeDistanceScale() {
|
function changeDistanceScale() {
|
||||||
|
distanceScale = +this.value;
|
||||||
renderScaleBar();
|
renderScaleBar();
|
||||||
calculateFriendlyGridSize();
|
calculateFriendlyGridSize();
|
||||||
}
|
}
|
||||||
|
|
@ -90,10 +91,9 @@ function editUnits() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function restoreDefaultUnits() {
|
function restoreDefaultUnits() {
|
||||||
// distanceScale
|
|
||||||
distanceScale = 3;
|
distanceScale = 3;
|
||||||
byId("distanceScaleOutput").value = 3;
|
byId("distanceScaleOutput").value = distanceScale;
|
||||||
byId("distanceScaleInput").value = 3;
|
byId("distanceScaleInput").value = distanceScale;
|
||||||
unlock("distanceScale");
|
unlock("distanceScale");
|
||||||
|
|
||||||
// units
|
// units
|
||||||
|
|
|
||||||
|
|
@ -105,13 +105,12 @@ function editWorld() {
|
||||||
|
|
||||||
calculateMapCoordinates();
|
calculateMapCoordinates();
|
||||||
const mc = mapCoordinates;
|
const mc = mapCoordinates;
|
||||||
const scale = +distanceScaleInput.value;
|
|
||||||
const unit = distanceUnitInput.value;
|
const unit = distanceUnitInput.value;
|
||||||
const meridian = toKilometer(eqD * 2 * scale);
|
const meridian = toKilometer(eqD * 2 * distanceScale);
|
||||||
byId("mapSize").innerHTML = `${graphWidth}x${graphHeight}`;
|
byId("mapSize").innerHTML = `${graphWidth}x${graphHeight}`;
|
||||||
byId("mapSizeFriendly").innerHTML = `${rn(graphWidth * scale)}x${rn(graphHeight * scale)} ${unit}`;
|
byId("mapSizeFriendly").innerHTML = `${rn(graphWidth * distanceScale)}x${rn(graphHeight * distanceScale)} ${unit}`;
|
||||||
byId("meridianLength").innerHTML = rn(eqD * 2);
|
byId("meridianLength").innerHTML = rn(eqD * 2);
|
||||||
byId("meridianLengthFriendly").innerHTML = `${rn(eqD * 2 * scale)} ${unit}`;
|
byId("meridianLengthFriendly").innerHTML = `${rn(eqD * 2 * distanceScale)} ${unit}`;
|
||||||
byId("meridianLengthEarth").innerHTML = meridian ? " = " + rn(meridian / 200) + "%🌏" : "";
|
byId("meridianLengthEarth").innerHTML = meridian ? " = " + rn(meridian / 200) + "%🌏" : "";
|
||||||
byId("mapCoordinates").innerHTML = `${lat(mc.latN)} ${Math.abs(rn(mc.lonW))}°W; ${lat(mc.latS)} ${rn(mc.lonE)}°E`;
|
byId("mapCoordinates").innerHTML = `${lat(mc.latN)} ${Math.abs(rn(mc.lonW))}°W; ${lat(mc.latS)} ${rn(mc.lonE)}°E`;
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue