diff --git a/main.js b/main.js index 7a5ae7a5..d28ceac5 100644 --- a/main.js +++ b/main.js @@ -1652,9 +1652,10 @@ function addZones(number = 1) { const burg = ra(burgs.filter(b => !used[b.cell] && b.i && !b.removed)); // random burg if (!burg) return; - const cellsArray = [], - cost = [], - power = rand(20, 37); + const cellsArray = []; + const cost = []; + const power = rand(20, 37); + const queue = new PriorityQueue({comparator: (a, b) => a.p - b.p}); queue.queue({e: burg.cell, p: 0}); @@ -1663,15 +1664,14 @@ function addZones(number = 1) { if (cells.burg[next.e] || cells.pop[next.e]) cellsArray.push(next.e); used[next.e] = 1; - cells.c[next.e].forEach(function (e) { - const r = cells.route[next.e]; - const c = r ? 5 : 100; + cells.c[next.e].forEach(nextCellId => { + const c = Routes.getRoute(next.e, nextCellId) ? 5 : 100; const p = next.p + c; if (p > power) return; - if (!cost[e] || p < cost[e]) { - cost[e] = p; - queue.queue({e, p}); + if (!cost[nextCellId] || p < cost[nextCellId]) { + cost[nextCellId] = p; + queue.queue({e: nextCellId, p}); } }); } @@ -1792,7 +1792,7 @@ function addZones(number = 1) { } function addAvalanche() { - const routes = cells.i.filter(i => !used[i] && cells.route[i] && cells.h[i] >= 70); + const routes = cells.i.filter(i => !used[i] && Routes.isConnected(i) && cells.h[i] >= 70); if (!routes.length) return; const cell = +ra(routes); diff --git a/modules/biomes.js b/modules/biomes.js index d7c95f77..06280fad 100644 --- a/modules/biomes.js +++ b/modules/biomes.js @@ -1,24 +1,8 @@ "use strict"; -const MIN_LAND_HEIGHT = 20; - -const names = [ - "Marine", - "Hot desert", - "Cold desert", - "Savanna", - "Grassland", - "Tropical seasonal forest", - "Temperate deciduous forest", - "Tropical rainforest", - "Temperate rainforest", - "Taiga", - "Tundra", - "Glacier", - "Wetland" -]; - window.Biomes = (function () { + const MIN_LAND_HEIGHT = 20; + const getDefault = () => { const name = [ "Marine", @@ -52,7 +36,7 @@ window.Biomes = (function () { "#0b9131" ]; const habitability = [0, 4, 10, 22, 30, 50, 100, 80, 90, 12, 4, 0, 12]; - const iconsDensity = [0, 3, 2, 120, 120, 120, 120, 150, 150, 100, 5, 0, 150]; + const iconsDensity = [0, 3, 2, 120, 120, 120, 120, 150, 150, 100, 5, 0, 250]; const icons = [ {}, {dune: 3, cactus: 6, deadTree: 1}, diff --git a/modules/dynamic/auto-update.js b/modules/dynamic/auto-update.js index ab156f18..7b3a4eed 100644 --- a/modules/dynamic/auto-update.js +++ b/modules/dynamic/auto-update.js @@ -846,9 +846,9 @@ export function resolveVersionConflicts(version) { if (version < 1.98) { // v1.98.00 changed routes generation algorithm and data format - // 1. cells.road => cells.route; 1 = MAIN; 2 = TRAIL; 3 = SEA; + // 1. cells.road => cells.routes and now it an object of objects {i1: {i2: routeId, i3: routeId}} // 2. cells.crossroad is removed - // 3. pack.routes is added + // 3. pack.routes is added as an array of objects // 4. rendering is changed } } diff --git a/modules/dynamic/export-json.js b/modules/dynamic/export-json.js index 1c403bd1..88cead8c 100644 --- a/modules/dynamic/export-json.js +++ b/modules/dynamic/export-json.js @@ -103,7 +103,7 @@ function getSettings() { } function getPackCellsData() { - const dataArrays = { + const data = { v: pack.cells.v, c: pack.cells.c, p: pack.cells.p, @@ -122,7 +122,7 @@ function getPackCellsData() { pop: Array.from(pack.cells.pop), culture: Array.from(pack.cells.culture), burg: Array.from(pack.cells.burg), - route: Array.from(pack.cells.route), + routes: pack.cells.routes, state: Array.from(pack.cells.state), religion: Array.from(pack.cells.religion), province: Array.from(pack.cells.province) @@ -131,28 +131,28 @@ function getPackCellsData() { return { cells: Array.from(pack.cells.i).map(cellId => ({ i: cellId, - v: dataArrays.v[cellId], - c: dataArrays.c[cellId], - p: dataArrays.p[cellId], - g: dataArrays.g[cellId], - h: dataArrays.h[cellId], - area: dataArrays.area[cellId], - f: dataArrays.f[cellId], - t: dataArrays.t[cellId], - haven: dataArrays.haven[cellId], - harbor: dataArrays.harbor[cellId], - fl: dataArrays.fl[cellId], - r: dataArrays.r[cellId], - conf: dataArrays.conf[cellId], - biome: dataArrays.biome[cellId], - s: dataArrays.s[cellId], - pop: dataArrays.pop[cellId], - culture: dataArrays.culture[cellId], - burg: dataArrays.burg[cellId], - route: dataArrays.route[cellId], - state: dataArrays.state[cellId], - religion: dataArrays.religion[cellId], - province: dataArrays.province[cellId] + v: data.v[cellId], + c: data.c[cellId], + p: data.p[cellId], + g: data.g[cellId], + h: data.h[cellId], + area: data.area[cellId], + f: data.f[cellId], + t: data.t[cellId], + haven: data.haven[cellId], + harbor: data.harbor[cellId], + fl: data.fl[cellId], + r: data.r[cellId], + conf: data.conf[cellId], + biome: data.biome[cellId], + s: data.s[cellId], + pop: data.pop[cellId], + culture: data.culture[cellId], + burg: data.burg[cellId], + routes: data.routes[cellId], + state: data.state[cellId], + religion: data.religion[cellId], + province: data.province[cellId] })), vertices: Array.from(pack.vertices.p).map((_, vertexId) => ({ i: vertexId, diff --git a/modules/io/load.js b/modules/io/load.js index 239507a4..ea5b8a5c 100644 --- a/modules/io/load.js +++ b/modules/io/load.js @@ -374,6 +374,7 @@ async function parseLoadedData(data, mapVersion) { pack.provinces = data[30] ? JSON.parse(data[30]) : [0]; pack.rivers = data[32] ? JSON.parse(data[32]) : []; pack.markers = data[35] ? JSON.parse(data[35]) : []; + pack.routes = data[37] ? JSON.parse(data[37]) : []; const cells = pack.cells; cells.biome = Uint8Array.from(data[16].split(",")); @@ -383,12 +384,13 @@ async function parseLoadedData(data, mapVersion) { cells.fl = Uint16Array.from(data[20].split(",")); cells.pop = Float32Array.from(data[21].split(",")); cells.r = Uint16Array.from(data[22].split(",")); - cells.route = Uint8Array.from(data[23].split(",")); + // data[23] for deprecated cells.road cells.s = Uint16Array.from(data[24].split(",")); cells.state = Uint16Array.from(data[25].split(",")); cells.religion = data[26] ? Uint16Array.from(data[26].split(",")) : new Uint16Array(cells.i.length); cells.province = data[27] ? Uint16Array.from(data[27].split(",")) : new Uint16Array(cells.i.length); // data[28] for deprecated cells.crossroad + cells.routes = data[36] ? JSON.parse(data[36]) : {}; if (data[31]) { const namesDL = data[31].split("/"); diff --git a/modules/io/save.js b/modules/io/save.js index 45bf6018..a3a5cbea 100644 --- a/modules/io/save.js +++ b/modules/io/save.js @@ -97,6 +97,8 @@ function prepareMapData() { const provinces = JSON.stringify(pack.provinces); const rivers = JSON.stringify(pack.rivers); const markers = JSON.stringify(pack.markers); + const cellRoutes = JSON.stringify(pack.cells.routes); + const routes = JSON.stringify(pack.routes); // store name array only if not the same as default const defaultNB = Names.getNameBases(); @@ -135,7 +137,7 @@ function prepareMapData() { pack.cells.fl, pop, pack.cells.r, - pack.cells.route, + [], // deprecated pack.cells.road pack.cells.s, pack.cells.state, pack.cells.religion, @@ -147,7 +149,9 @@ function prepareMapData() { rivers, rulersString, fonts, - markers + markers, + cellRoutes, + routes ].join("\r\n"); return mapData; } diff --git a/modules/markers-generator.js b/modules/markers-generator.js index 585a9153..d08f8807 100644 --- a/modules/markers-generator.js +++ b/modules/markers-generator.js @@ -27,7 +27,7 @@ window.Markers = (function () { {type: "water-sources", icon: "💧", min: 1, each: 1000, multiplier: 1, list: listWaterSources, add: addWaterSource}, {type: "mines", icon: "⛏️", dx: 48, px: 13, min: 1, each: 15, multiplier: 1, list: listMines, add: addMine}, {type: "bridges", icon: "🌉", px: 14, min: 1, each: 5, multiplier: 1, list: listBridges, add: addBridge}, - {type: "inns", icon: "🍻", px: 14, min: 1, each: 100, multiplier: 1, list: listInns, add: addInn}, + {type: "inns", icon: "🍻", px: 14, min: 1, each: 10, multiplier: 1, list: listInns, add: addInn}, {type: "lighthouses", icon: "🚨", px: 14, min: 1, each: 2, multiplier: 1, list: listLighthouses, add: addLighthouse}, {type: "waterfalls", icon: "⟱", dy: 54, px: 16, min: 1, each: 5, multiplier: 1, list: listWaterfalls, add: addWaterfall}, {type: "battlefields", icon: "⚔️", dy: 52, min: 50, each: 700, multiplier: 1, list: listBattlefields, add: addBattlefield}, @@ -279,7 +279,8 @@ window.Markers = (function () { } function listInns({cells}) { - return cells.i.filter(i => !occupied[i] && cells.h[i] >= 20 && cells.route[i] === 1 && cells.pop[i] > 10); + const crossRoads = cells.i.filter(i => !occupied[i] && cells.pop[i] > 5 && Routes.isCrossroad(i)); + return crossRoads; } function addInn(id, cell) { @@ -542,7 +543,7 @@ window.Markers = (function () { function listLighthouses({cells}) { return cells.i.filter( - i => !occupied[i] && cells.harbor[i] > 6 && cells.c[i].some(c => cells.h[c] < 20 && cells.route[c]) + i => !occupied[i] && cells.harbor[i] > 6 && cells.c[i].some(c => cells.h[c] < 20 && Routes.isConnected(c)) ); } @@ -642,7 +643,7 @@ window.Markers = (function () { function listSeaMonsters({cells, features}) { return cells.i.filter( - i => !occupied[i] && cells.h[i] < 20 && cells.route[i] && features[cells.f[i]].type === "ocean" + i => !occupied[i] && cells.h[i] < 20 && Routes.isConnected(i) && features[cells.f[i]].type === "ocean" ); } @@ -792,7 +793,7 @@ window.Markers = (function () { cells.religion[i] && cells.biome[i] === 1 && cells.pop[i] > 1 && - cells.route[i] + Routes.isConnected(i) ); } @@ -807,7 +808,7 @@ window.Markers = (function () { } function listBrigands({cells}) { - return cells.i.filter(i => !occupied[i] && cells.culture[i] && cells.route[i] === 1); + return cells.i.filter(i => !occupied[i] && cells.culture[i] && Routes.hasRoad(i)); } function addBrigands(id, cell) { @@ -867,7 +868,7 @@ window.Markers = (function () { // Pirates spawn on sea routes function listPirates({cells}) { - return cells.i.filter(i => !occupied[i] && cells.h[i] < 20 && cells.route[i]); + return cells.i.filter(i => !occupied[i] && cells.h[i] < 20 && Routes.isConnected(i)); } function addPirates(id, cell) { @@ -961,7 +962,7 @@ window.Markers = (function () { } function listCircuses({cells}) { - return cells.i.filter(i => !occupied[i] && cells.culture[i] && cells.h[i] >= 20 && pack.cells.route[i]); + return cells.i.filter(i => !occupied[i] && cells.culture[i] && cells.h[i] >= 20 && Routes.isConnected(i)); } function addCircuse(id, cell) { @@ -1254,16 +1255,16 @@ window.Markers = (function () { const name = `${toponym} ${type}`; const legend = ra([ - "A foreboding necropolis shrouded in perpetual darkness, where eerie whispers echo through the winding corridors and spectral guardians stand watch over the tombs of long-forgotten souls", - "A towering necropolis adorned with macabre sculptures and guarded by formidable undead sentinels. Its ancient halls house the remains of fallen heroes, entombed alongside their cherished relics", - "This ethereal necropolis seems suspended between the realms of the living and the dead. Wisps of mist dance around the tombstones, while haunting melodies linger in the air, commemorating the departed", - "Rising from the desolate landscape, this sinister necropolis is a testament to necromantic power. Its skeletal spires cast ominous shadows, concealing forbidden knowledge and arcane secrets", - "An eerie necropolis where nature intertwines with death. Overgrown tombstones are entwined by thorny vines, and mournful spirits wander among the fading petals of once-vibrant flowers", - "A labyrinthine necropolis where each step echoes with haunting murmurs. The walls are adorned with ancient runes, and restless spirits guide or hinder those who dare to delve into its depths", - "This cursed necropolis is veiled in perpetual twilight, perpetuating a sense of impending doom. Dark enchantments shroud the tombs, and the moans of anguished souls resound through its crumbling halls", - "A sprawling necropolis built within a labyrinthine network of catacombs. Its halls are lined with countless alcoves, each housing the remains of the departed, while the distant sound of rattling bones fills the air", - "A desolate necropolis where an eerie stillness reigns. Time seems frozen amidst the decaying mausoleums, and the silence is broken only by the whispers of the wind and the rustle of tattered banners", - "A foreboding necropolis perched atop a jagged cliff, overlooking a desolate wasteland. Its towering walls harbor restless spirits, and the imposing gates bear the marks of countless battles and ancient curses" + "A foreboding necropolis shrouded in perpetual darkness, where eerie whispers echo through the winding corridors and spectral guardians stand watch over the tombs of long-forgotten souls.", + "A towering necropolis adorned with macabre sculptures and guarded by formidable undead sentinels. Its ancient halls house the remains of fallen heroes, entombed alongside their cherished relics.", + "This ethereal necropolis seems suspended between the realms of the living and the dead. Wisps of mist dance around the tombstones, while haunting melodies linger in the air, commemorating the departed.", + "Rising from the desolate landscape, this sinister necropolis is a testament to necromantic power. Its skeletal spires cast ominous shadows, concealing forbidden knowledge and arcane secrets.", + "An eerie necropolis where nature intertwines with death. Overgrown tombstones are entwined by thorny vines, and mournful spirits wander among the fading petals of once-vibrant flowers.", + "A labyrinthine necropolis where each step echoes with haunting murmurs. The walls are adorned with ancient runes, and restless spirits guide or hinder those who dare to delve into its depths.", + "This cursed necropolis is veiled in perpetual twilight, perpetuating a sense of impending doom. Dark enchantments shroud the tombs, and the moans of anguished souls resound through its crumbling halls.", + "A sprawling necropolis built within a labyrinthine network of catacombs. Its halls are lined with countless alcoves, each housing the remains of the departed, while the distant sound of rattling bones fills the air.", + "A desolate necropolis where an eerie stillness reigns. Time seems frozen amidst the decaying mausoleums, and the silence is broken only by the whispers of the wind and the rustle of tattered banners.", + "A foreboding necropolis perched atop a jagged cliff, overlooking a desolate wasteland. Its towering walls harbor restless spirits, and the imposing gates bear the marks of countless battles and ancient curses." ]); notes.push({id, name, legend}); diff --git a/modules/religions-generator.js b/modules/religions-generator.js index 238fcddf..ca60466b 100644 --- a/modules/religions-generator.js +++ b/modules/religions-generator.js @@ -692,7 +692,7 @@ window.Religions = (function () { // growth algorithm to assign cells to religions function expandReligions(religions) { - const cells = pack.cells; + const {cells, routes} = pack; const religionIds = spreadFolkReligions(religions); const queue = new PriorityQueue({comparator: (a, b) => a.p - b.p}); @@ -700,8 +700,6 @@ window.Religions = (function () { const maxExpansionCost = (cells.i.length / 20) * neutralInput.value; // limit cost for organized religions growth - const biomePassageCost = cellId => biomesData.cost[cells.biome[cellId]]; - religions .filter(r => r.i && !r.lock && r.type !== "Folk" && !r.removed) .forEach(r => { @@ -712,11 +710,6 @@ window.Religions = (function () { const religionsMap = new Map(religions.map(r => [r.i, r])); - const isMainRoad = cellId => cells.route[cellId] === 1; - const isTrail = cellId => cells.route[cellId] === 2; - const isSeaRoute = cellId => cells.route[cellId] === 3; - const isWater = cellId => cells.h[cellId] < 20; - while (queue.length) { const {e: cellId, p, r, s: state} = queue.dequeue(); const {culture, expansion, expansionism} = religionsMap.get(r); @@ -728,7 +721,7 @@ window.Religions = (function () { const cultureCost = culture !== cells.culture[nextCell] ? 10 : 0; const stateCost = state !== cells.state[nextCell] ? 10 : 0; - const passageCost = getPassageCost(nextCell); + const passageCost = getPassageCost(cellId, nextCell); const cellCost = cultureCost + stateCost + passageCost; const totalCost = p + 10 + cellCost / expansionism; @@ -745,11 +738,18 @@ window.Religions = (function () { return religionIds; - function getPassageCost(cellId) { - if (isWater(cellId)) return isSeaRoute ? 50 : 500; - if (isMainRoad(cellId)) return 1; - const biomeCost = biomePassageCost(cellId); - return isTrail(cellId) ? biomeCost / 1.5 : biomeCost; + function getPassageCost(cellId, nextCellId) { + const route = Routes.getRoute(cellId, nextCellId); + if (isWater(cellId)) return route ? 50 : 500; + + const biomePassageCost = biomesData.cost[cells.biome[nextCellId]]; + + if (route) { + if (route.group === "roads") return 1; + return biomePassageCost / 3; // trails and other routes + } + + return biomePassageCost; } } diff --git a/modules/routes-generator.js b/modules/routes-generator.js index 9dbd6679..9611ba32 100644 --- a/modules/routes-generator.js +++ b/modules/routes-generator.js @@ -1,40 +1,14 @@ -// suggested data format - -// pack.cells.connectivity = { -// cellId1: { -// toCellId2: routeId2, -// toCellId3: routeId2, -// }, -// cellId2: { -// toCellId1: routeId2, -// toCellId3: routeId1, -// } -// } - -// pack.routes = [ -// {i, group: "roads", feature: featureId, cells: [cellId], points?: [[x, y], [x, y]]} -// ]; - window.Routes = (function () { - const ROUTES = { - MAIN_ROAD: 1, - TRAIL: 2, - SEA_ROUTE: 3 - }; - function generate() { - const {cells, burgs} = pack; - const cellRoutes = new Uint8Array(cells.h.length); - - const {capitalsByFeature, burgsByFeature, portsByFeature} = sortBurgsByFeature(burgs); + const {capitalsByFeature, burgsByFeature, portsByFeature} = sortBurgsByFeature(pack.burgs); const connections = new Map(); const mainRoads = generateMainRoads(); const trails = generateTrails(); const seaRoutes = generateSeaRoutes(); - cells.route = cellRoutes; pack.routes = combineRoutes(); + pack.cells.routes = buildLinks(pack.routes); function sortBurgsByFeature(burgs) { const burgsByFeature = {}; @@ -71,7 +45,7 @@ window.Routes = (function () { const segments = findPathSegments({isWater: false, connections, start, exit}); for (const segment of segments) { - addConnections(segment, ROUTES.MAIN_ROAD); + addConnections(segment); mainRoads.push({feature: Number(key), cells: segment}); } }); @@ -94,7 +68,7 @@ window.Routes = (function () { const segments = findPathSegments({isWater: false, connections, start, exit}); for (const segment of segments) { - addConnections(segment, ROUTES.TRAIL); + addConnections(segment); trails.push({feature: Number(key), cells: segment}); } }); @@ -117,7 +91,7 @@ window.Routes = (function () { const exit = featurePorts[toId].cell; const segments = findPathSegments({isWater: true, connections, start, exit}); for (const segment of segments) { - addConnections(segment, ROUTES.SEA_ROUTE); + addConnections(segment); seaRoutes.push({feature: Number(featureId), cells: segment}); } }); @@ -127,7 +101,7 @@ window.Routes = (function () { return seaRoutes; } - function addConnections(segment, routeTypeId) { + function addConnections(segment) { for (let i = 0; i < segment.length; i++) { const cellId = segment[i]; const nextCellId = segment[i + 1]; @@ -135,7 +109,6 @@ window.Routes = (function () { connections.set(`${cellId}-${nextCellId}`, true); connections.set(`${nextCellId}-${cellId}`, true); } - if (!cellRoutes[cellId]) cellRoutes[cellId] = routeTypeId; } } @@ -165,10 +138,29 @@ window.Routes = (function () { return routes; } + + function buildLinks(routes) { + const links = {}; + + for (const {cells, i: routeId} of routes) { + for (let i = 0; i < cells.length; i++) { + const cellId = cells[i]; + const nextCellId = cells[i + 1]; + if (nextCellId) { + if (!links[cellId]) links[cellId] = {}; + links[cellId][nextCellId] = routeId; + + if (!links[nextCellId]) links[nextCellId] = {}; + links[nextCellId][cellId] = routeId; + } + } + } + + return links; + } } const MIN_PASSABLE_SEA_TEMP = -4; - const TYPE_MODIFIERS = { "-1": 1, // coastline "-2": 1.8, // sea @@ -339,5 +331,36 @@ window.Routes = (function () { return edges; } - return {generate}; + // utility functions + function isConnected(cellId) { + const {routes} = pack.cells; + return routes[cellId] && Object.keys(routes[cellId]).length > 0; + } + + function areConnected(from, to) { + const routeId = pack.cells.routes[from]?.[to]; + return routeId !== undefined; + } + + function getRoute(from, to) { + const routeId = pack.cells.routes[from]?.[to]; + return routeId === undefined ? null : pack.routes[routeId]; + } + + function hasRoad(cellId) { + const connections = pack.cells.routes[cellId]; + if (!connections) return false; + return Object.values(connections).some(routeId => pack.routes[routeId].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 + ); + } + + return {generate, isConnected, areConnected, getRoute, hasRoad, isCrossroad}; })(); diff --git a/modules/submap.js b/modules/submap.js index 6dfa6049..975175e9 100644 --- a/modules/submap.js +++ b/modules/submap.js @@ -145,7 +145,6 @@ window.Submap = (function () { cells.state = new Uint16Array(pn); cells.burg = new Uint16Array(pn); cells.religion = new Uint16Array(pn); - cells.route = new Uint8Array(pn); cells.province = new Uint16Array(pn); stage("Resampling culture, state and religion map."); diff --git a/modules/ui/editors.js b/modules/ui/editors.js index b304ce81..05359f15 100644 --- a/modules/ui/editors.js +++ b/modules/ui/editors.js @@ -326,8 +326,7 @@ function createMfcgLink(burg) { const citadel = +burg.citadel; const urban_castle = +(citadel && each(2)(i)); - const hub = +cells.route[cell] === 1; - + const hub = Routes.isCrossroad(cell); const walls = +burg.walls; const plaza = +burg.plaza; const temple = +burg.temple; @@ -371,10 +370,12 @@ function createVillageGeneratorLink(burg) { else if (cells.r[cell]) tags.push("river"); else if (pop < 200 && each(4)(cell)) tags.push("pond"); - const roadsAround = cells.c[cell].filter(c => cells.h[c] >= 20 && cells.route[c]).length; - if (roadsAround > 1) tags.push("highway"); - else if (roadsAround === 1) tags.push("dead end"); - else tags.push("isolated"); + const connections = pack.cells.routes[cell] || {}; + const roads = Object.values(connections).filter(routeId => { + const route = pack.routes[routeId]; + return route.group === "roads" || route.group === "trails"; + }).length; + tags.push(roads > 1 ? "highway" : roads === 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/heightmap-editor.js b/modules/ui/heightmap-editor.js index c99e4bdd..2cec7437 100644 --- a/modules/ui/heightmap-editor.js +++ b/modules/ui/heightmap-editor.js @@ -282,7 +282,7 @@ function editHeightmap(options) { const l = grid.cells.i.length; const biome = new Uint8Array(l); const pop = new Uint16Array(l); - const route = new Uint8Array(l); + const routes = {}; const s = new Uint16Array(l); const burg = new Uint16Array(l); const state = new Uint16Array(l); @@ -300,7 +300,7 @@ function editHeightmap(options) { biome[g] = pack.cells.biome[i]; culture[g] = pack.cells.culture[i]; pop[g] = pack.cells.pop[i]; - route[g] = pack.cells.route[i]; + routes[g] = pack.cells.routes[i]; s[g] = pack.cells.s[i]; state[g] = pack.cells.state[i]; province[g] = pack.cells.province[i]; @@ -352,7 +352,7 @@ function editHeightmap(options) { // assign saved pack data from grid back to pack const n = pack.cells.i.length; pack.cells.pop = new Float32Array(n); - pack.cells.route = new Uint8Array(n); + pack.cells.routes = {}; pack.cells.s = new Uint16Array(n); pack.cells.burg = new Uint16Array(n); pack.cells.state = new Uint16Array(n); @@ -387,7 +387,7 @@ function editHeightmap(options) { if (!isLand) continue; pack.cells.culture[i] = culture[g]; pack.cells.pop[i] = pop[g]; - pack.cells.route[i] = route[g]; + pack.cells.routes[i] = routes[g]; pack.cells.s[i] = s[g]; pack.cells.state[i] = state[g]; pack.cells.province[i] = province[g]; diff --git a/modules/ui/measurers.js b/modules/ui/measurers.js index d2d01c19..e083936c 100644 --- a/modules/ui/measurers.js +++ b/modules/ui/measurers.js @@ -486,7 +486,7 @@ class RouteOpisometer extends Measurer { const cells = pack.cells; const c = findCell(mousePoint[0], mousePoint[1]); - if (!cells.route[c] && !d3.event.sourceEvent.shiftKey) return; + if (!Routes.isConnected(c) && !d3.event.sourceEvent.shiftKey) return; context.trackCell(c, rigth); }); diff --git a/modules/ui/units-editor.js b/modules/ui/units-editor.js index 6f0332bf..86079026 100644 --- a/modules/ui/units-editor.js +++ b/modules/ui/units-editor.js @@ -179,13 +179,15 @@ function editUnits() { tip("Draw a curve along routes to measure length. Hold Shift to measure away from roads.", true); unitsBottom.querySelectorAll(".pressed").forEach(button => button.classList.remove("pressed")); this.classList.add("pressed"); + viewbox.style("cursor", "crosshair").call( d3.drag().on("start", function () { const cells = pack.cells; const burgs = pack.burgs; const point = d3.mouse(this); const c = findCell(point[0], point[1]); - if (cells.route[c] || d3.event.sourceEvent.shiftKey) { + + if (Routes.isConnected(c) || d3.event.sourceEvent.shiftKey) { const b = cells.burg[c]; const x = b ? burgs[b].x : cells.p[c][0]; const y = b ? burgs[b].y : cells.p[c][1]; @@ -194,7 +196,7 @@ function editUnits() { d3.event.on("drag", function () { const point = d3.mouse(this); const c = findCell(point[0], point[1]); - if (cells.route[c] || d3.event.sourceEvent.shiftKey) { + if (Routes.isConnected(c) || d3.event.sourceEvent.shiftKey) { routeOpisometer.trackCell(c, true); } });