From 5904e9e7c6984a8f8ac4c7695fa47e98ff3ca012 Mon Sep 17 00:00:00 2001 From: Azgaar Date: Fri, 20 Sep 2024 14:16:07 +0200 Subject: [PATCH] fix: routes (v1.104.3) --- index.html | 16 +++---- modules/io/load.js | 83 +++++++++++++++++++++-------------- modules/routes-generator.js | 26 ++++++++--- modules/ui/editors.js | 8 ++-- modules/ui/general.js | 8 ++-- modules/ui/options.js | 3 +- modules/ui/routes-editor.js | 3 +- modules/ui/routes-overview.js | 14 +++--- versioning.js | 2 +- 9 files changed, 98 insertions(+), 65 deletions(-) diff --git a/index.html b/index.html index 1fd06ef8..ff2c1b07 100644 --- a/index.html +++ b/index.html @@ -8047,7 +8047,7 @@ - + @@ -8063,13 +8063,13 @@ - - + + - + @@ -8078,8 +8078,8 @@ - - + + @@ -8095,7 +8095,7 @@ - + @@ -8111,7 +8111,7 @@ - + diff --git a/modules/io/load.js b/modules/io/load.js index 2c6c06a6..a932fa98 100644 --- a/modules/io/load.js +++ b/modules/io/load.js @@ -481,7 +481,7 @@ async function parseLoadedData(data, mapVersion) { const cells = pack.cells; if (pack.cells.i.length !== pack.cells.state.length) { - const message = "Data integrity check. Striping issue detected. To fix edit the heightmap in ERASE mode"; + const message = "[Data integrity] Striping issue detected. To fix edit the heightmap in ERASE mode"; ERROR && console.error(message); } @@ -489,7 +489,7 @@ async function parseLoadedData(data, mapVersion) { invalidStates.forEach(s => { const invalidCells = cells.i.filter(i => cells.state[i] === s); invalidCells.forEach(i => (cells.state[i] = 0)); - ERROR && console.error("Data integrity check. Invalid state", s, "is assigned to cells", invalidCells); + ERROR && console.error("[Data integrity] Invalid state", s, "is assigned to cells", invalidCells); }); const invalidProvinces = [...new Set(cells.province)].filter( @@ -498,14 +498,14 @@ async function parseLoadedData(data, mapVersion) { invalidProvinces.forEach(p => { const invalidCells = cells.i.filter(i => cells.province[i] === p); invalidCells.forEach(i => (cells.province[i] = 0)); - ERROR && console.error("Data integrity check. Invalid province", p, "is assigned to cells", invalidCells); + ERROR && console.error("[Data integrity] Invalid province", p, "is assigned to cells", invalidCells); }); const invalidCultures = [...new Set(cells.culture)].filter(c => !pack.cultures[c] || pack.cultures[c].removed); invalidCultures.forEach(c => { const invalidCells = cells.i.filter(i => cells.culture[i] === c); invalidCells.forEach(i => (cells.province[i] = 0)); - ERROR && console.error("Data integrity check. Invalid culture", c, "is assigned to cells", invalidCells); + ERROR && console.error("[Data integrity] Invalid culture", c, "is assigned to cells", invalidCells); }); const invalidReligions = [...new Set(cells.religion)].filter( @@ -514,14 +514,14 @@ async function parseLoadedData(data, mapVersion) { invalidReligions.forEach(r => { const invalidCells = cells.i.filter(i => cells.religion[i] === r); invalidCells.forEach(i => (cells.religion[i] = 0)); - ERROR && console.error("Data integrity check. Invalid religion", r, "is assigned to cells", invalidCells); + ERROR && console.error("[Data integrity] Invalid religion", r, "is assigned to cells", invalidCells); }); const invalidFeatures = [...new Set(cells.f)].filter(f => f && !pack.features[f]); invalidFeatures.forEach(f => { const invalidCells = cells.i.filter(i => cells.f[i] === f); // No fix as for now - ERROR && console.error("Data integrity check. Invalid feature", f, "is assigned to cells", invalidCells); + ERROR && console.error("[Data integrity] Invalid feature", f, "is assigned to cells", invalidCells); }); const invalidBurgs = [...new Set(cells.burg)].filter( @@ -530,7 +530,7 @@ async function parseLoadedData(data, mapVersion) { invalidBurgs.forEach(burgId => { const invalidCells = cells.i.filter(i => cells.burg[i] === burgId); invalidCells.forEach(i => (cells.burg[i] = 0)); - ERROR && console.error("Data integrity check. Invalid burg", burgId, "is assigned to cells", invalidCells); + ERROR && console.error("[Data integrity] Invalid burg", burgId, "is assigned to cells", invalidCells); }); const invalidRivers = [...new Set(cells.r)].filter(r => r && !pack.rivers.find(river => river.i === r)); @@ -538,21 +538,20 @@ async function parseLoadedData(data, mapVersion) { const invalidCells = cells.i.filter(i => cells.r[i] === r); invalidCells.forEach(i => (cells.r[i] = 0)); rivers.select("river" + r).remove(); - ERROR && console.error("Data integrity check. Invalid river", r, "is assigned to cells", invalidCells); + ERROR && console.error("[Data integrity] Invalid river", r, "is assigned to cells", invalidCells); }); pack.burgs.forEach(burg => { if (typeof burg.capital === "boolean") burg.capital = Number(burg.capital); if (!burg.i && burg.lock) { - ERROR && console.error(`Data integrity check. Burg 0 is marked as locked, removing the status`); + ERROR && console.error(`[Data integrity] Burg 0 is marked as locked, removing the status`); delete burg.lock; return; } if (burg.removed && burg.lock) { - ERROR && - console.error(`Data integrity check. Removed burg ${burg.i} is marked as locked. Unlocking the burg`); + ERROR && console.error(`[Data integrity] Removed burg ${burg.i} is marked as locked. Unlocking the burg`); delete burg.lock; return; } @@ -561,36 +560,34 @@ async function parseLoadedData(data, mapVersion) { if (burg.cell === undefined || burg.x === undefined || burg.y === undefined) { ERROR && - console.error( - `Data integrity check. Burg ${burg.i} is missing cell info or coordinates. Removing the burg` - ); + console.error(`[Data integrity] Burg ${burg.i} is missing cell info or coordinates. Removing the burg`); burg.removed = true; } if (burg.port < 0) { - ERROR && console.error("Data integrity check. Burg", burg.i, "has invalid port value", burg.port); + ERROR && console.error("[Data integrity] Burg", burg.i, "has invalid port value", burg.port); burg.port = 0; } if (burg.cell >= cells.i.length) { - ERROR && console.error("Data integrity check. Burg", burg.i, "is linked to invalid cell", burg.cell); + ERROR && console.error("[Data integrity] Burg", burg.i, "is linked to invalid cell", burg.cell); burg.cell = findCell(burg.x, burg.y); cells.i.filter(i => cells.burg[i] === burg.i).forEach(i => (cells.burg[i] = 0)); cells.burg[burg.cell] = burg.i; } if (burg.state && !pack.states[burg.state]) { - ERROR && console.error("Data integrity check. Burg", burg.i, "is linked to invalid state", burg.state); + ERROR && console.error("[Data integrity] Burg", burg.i, "is linked to invalid state", burg.state); burg.state = 0; } if (burg.state && pack.states[burg.state].removed) { - ERROR && console.error("Data integrity check. Burg", burg.i, "is linked to removed state", burg.state); + ERROR && console.error("[Data integrity] Burg", burg.i, "is linked to removed state", burg.state); burg.state = 0; } if (burg.state === undefined) { - ERROR && console.error("Data integrity check. Burg", burg.i, "has no state data"); + ERROR && console.error("[Data integrity] Burg", burg.i, "has no state data"); burg.state = 0; } }); @@ -604,7 +601,7 @@ async function parseLoadedData(data, mapVersion) { if (!state.i && capitalBurgs.length) { ERROR && console.error( - `Data integrity check. Neutral burgs (${capitalBurgs + `[Data integrity] Neutral burgs (${capitalBurgs .map(b => b.i) .join(", ")}) marked as capitals. Moving them to towns` ); @@ -618,7 +615,7 @@ async function parseLoadedData(data, mapVersion) { } if (capitalBurgs.length > 1) { - const message = `Data integrity check. State ${state.i} has multiple capitals (${capitalBurgs + const message = `[Data integrity] State ${state.i} has multiple capitals (${capitalBurgs .map(b => b.i) .join(", ")}) assigned. Keeping the first as capital and moving others to towns`; ERROR && console.error(message); @@ -634,7 +631,7 @@ async function parseLoadedData(data, mapVersion) { if (state.i && stateBurgs.length && !capitalBurgs.length) { ERROR && - console.error(`Data integrity check. State ${state.i} has no capital. Assigning the first burg as capital`); + console.error(`[Data integrity] State ${state.i} has no capital. Assigning the first burg as capital`); stateBurgs[0].capital = 1; moveBurgToGroup(stateBurgs[0].i, "cities"); } @@ -643,18 +640,38 @@ async function parseLoadedData(data, mapVersion) { pack.provinces.forEach(p => { if (!p.i || p.removed) return; if (pack.states[p.state] && !pack.states[p.state].removed) return; - ERROR && console.error("Data integrity check. Province", p.i, "is linked to removed state", p.state); - p.removed = true; // remove incorrect province + ERROR && + console.error( + `[Data integrity] Province ${p.i} is linked to removed state ${p.state}. Removing the province` + ); + p.removed = true; }); - pack.routes.forEach(({i, points}) => { - if (!points || points.length < 2) { - ERROR && - console.error( - "Data integrity check. Route", - i, - "has less than 2 points. Route will be ignored on layer rendering" - ); + pack.routes.forEach(route => { + if (!route.points || route.points.length < 2) { + ERROR && console.error(`[Data integrity] Route ${route.i} has less than 2 points. Removing the route`); + Routes.remove(route); + } + + for (const from in pack.cells.routes) { + const value = pack.cells.routes[from]; + if (!value) continue; + + if (Object.keys(value).length === 0) { + // remove empty object + delete pack.cells.routes[from]; + continue; + } + + for (const to in value) { + const routeId = value[to]; + const route = pack.routes.find(r => r.i === routeId); + if (!route) { + ERROR && + console.error(`[Data integrity] Route ${routeId} from ${from} to ${to} is missing. Removing the route`); + delete pack.cells.routes[from][to]; + } + } } }); @@ -664,7 +681,7 @@ async function parseLoadedData(data, mapVersion) { pack.markers.forEach(marker => { if (markerIds[marker.i]) { - ERROR && console.error("Data integrity check. Marker", marker.i, "has non-unique id. Changing to", nextId); + ERROR && console.error("[Data integrity] Marker", marker.i, "has non-unique id. Changing to", nextId); const domElements = document.querySelectorAll("#marker" + marker.i); if (domElements[1]) domElements[1].id = "marker" + nextId; // rename 2nd dom element diff --git a/modules/routes-generator.js b/modules/routes-generator.js index edd41b4f..8a33dd10 100644 --- a/modules/routes-generator.js +++ b/modules/routes-generator.js @@ -496,7 +496,7 @@ window.Routes = (function () { // utility functions function isConnected(cellId) { - const {routes} = pack.cells; + const routes = pack.cells.routes; return routes[cellId] && Object.keys(routes[cellId]).length > 0; } @@ -507,22 +507,34 @@ window.Routes = (function () { function getRoute(from, to) { const routeId = pack.cells.routes[from]?.[to]; - return routeId === undefined ? null : pack.routes[routeId]; + if (routeId === undefined) return null; + + const route = pack.routes.find(route => route.i === routeId); + if (!route) return null; + + return route; } function hasRoad(cellId) { const connections = pack.cells.routes[cellId]; if (!connections) return false; - return Object.values(connections).some(routeId => pack.routes[routeId].group === "roads"); + + return Object.values(connections).some(routeId => { + const route = pack.routes.find(route => route.i === routeId); + if (!route) return false; + return route.group === "roads"; + }); } function isCrossroad(cellId) { const connections = pack.cells.routes[cellId]; if (!connections) return false; - return ( - Object.keys(connections).length > 3 || - Object.values(connections).filter(routeId => pack.routes[routeId]?.group === "roads").length > 2 - ); + if (Object.keys(connections).length > 3) return true; + const roadConnections = Object.values(connections).filter(routeId => { + const route = pack.routes.find(route => route.i === routeId); + return route?.group === "roads"; + }); + return roadConnections.length > 2; } // name generator data diff --git a/modules/ui/editors.js b/modules/ui/editors.js index 2e761d7a..cb7b07c4 100644 --- a/modules/ui/editors.js +++ b/modules/ui/editors.js @@ -393,12 +393,12 @@ function createVillageGeneratorLink(burg) { else if (cells.r[cell]) tags.push("river"); else if (pop < 200 && each(4)(cell)) tags.push("pond"); - const connections = pack.cells.routes[cell] || {}; - const roads = Object.values(connections).filter(routeId => { - const route = pack.routes[routeId]; + const roadsNumber = Object.values(pack.cells.routes[cell] || {}).filter(routeId => { + const route = pack.routes.find(route => route.i === routeId); + if (!route) return false; return route.group === "roads" || route.group === "trails"; }).length; - tags.push(roads > 1 ? "highway" : roads === 1 ? "dead end" : "isolated"); + tags.push(roadsNumber > 1 ? "highway" : roadsNumber === 1 ? "dead end" : "isolated"); const biome = cells.biome[cell]; const arableBiomes = cells.r[cell] ? [1, 2, 3, 4, 5, 6, 7, 8] : [5, 6, 7, 8]; diff --git a/modules/ui/general.js b/modules/ui/general.js index 8d5fc28e..6e276bd6 100644 --- a/modules/ui/general.js +++ b/modules/ui/general.js @@ -153,9 +153,11 @@ function showMapTooltip(point, e, i, g) { if (group === "routes") { const routeId = +e.target.id.slice(5); - const name = pack.routes[routeId]?.name; - if (name) return tip(`${name}. Click to edit the Route`); - return tip("Click to edit the Route"); + const route = pack.routes.find(route => route.i === routeId); + if (route) { + if (route.name) return tip(`${route.name}. Click to edit the Route`); + return tip("Click to edit the Route"); + } } if (group === "terrain") return tip("Click to edit the Relief Icon"); diff --git a/modules/ui/options.js b/modules/ui/options.js index f10cf6fd..a6640aed 100644 --- a/modules/ui/options.js +++ b/modules/ui/options.js @@ -250,8 +250,7 @@ const voiceInterval = setInterval(function () { select.options.add(new Option(voice.name, i, false)); }); if (stored("speakerVoice")) select.value = stored("speakerVoice"); - // se voice to store - else select.value = voices.findIndex(voice => voice.lang === "en-US"); // or to first found English-US + else select.value = voices.findIndex(voice => voice.lang === "en-US"); }, 1000); function testSpeaker() { diff --git a/modules/ui/routes-editor.js b/modules/ui/routes-editor.js index 4ea119fb..5556c812 100644 --- a/modules/ui/routes-editor.js +++ b/modules/ui/routes-editor.js @@ -174,9 +174,10 @@ function editRoute(id) { function handleControlPointClick() { const controlPoint = d3.select(this); - const point = controlPoint.datum(); const route = getRoute(); + if (route.points.length < 3) return; // can't remove or split point if only 2 points in route + const index = route.points.indexOf(point); const isSplitMode = byId("routeSplit").classList.contains("pressed"); diff --git a/modules/ui/routes-overview.js b/modules/ui/routes-overview.js index 252b2275..cf731068 100644 --- a/modules/ui/routes-overview.js +++ b/modules/ui/routes-overview.js @@ -32,6 +32,7 @@ function overviewRoutes() { let lines = ""; for (const route of pack.routes) { + if (!route.points || route.points.length < 2) continue; route.name = route.name || Routes.generateName(route); route.length = route.length || Routes.getLength(route.i); const length = rn(route.length * distanceScale) + " " + distanceUnitInput.value; @@ -92,8 +93,8 @@ function overviewRoutes() { } function zoomToRoute() { - const r = +this.parentNode.dataset.id; - const route = routes.select("#route" + r).node(); + const routeId = +this.parentNode.dataset.id; + const route = routes.select("#route" + routeId).node(); highlightElement(route, 3); } @@ -111,15 +112,16 @@ function overviewRoutes() { } function openRouteEditor() { - const id = "route" + this.parentNode.dataset.id; - editRoute(id); + const routeId = "route" + this.parentNode.dataset.id; + editRoute(routeId); } function toggleLockStatus() { const routeId = +this.parentNode.dataset.id; - const route = pack.routes[routeId]; - route.lock = !route.lock; + const route = pack.routes.find(route => route.i === routeId); + if (!route) return; + route.lock = !route.lock; if (this.classList.contains("icon-lock")) { this.classList.remove("icon-lock"); this.classList.add("icon-lock-open"); diff --git a/versioning.js b/versioning.js index 52036ad1..6d6d5ccc 100644 --- a/versioning.js +++ b/versioning.js @@ -12,7 +12,7 @@ * * Example: 1.102.2 -> Major version 1, Minor version 102, Patch version 2 */ -const VERSION = "1.104.2"; +const VERSION = "1.104.3"; if (parseMapVersion(VERSION) !== VERSION) alert("versioning.js: Invalid format or parsing function"); {