Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Urquhart routes #1072

Merged
merged 43 commits into from
Aug 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
6776e5b
feat: routes generation
Mar 24, 2024
71e53bd
feat: routes rendering
Mar 24, 2024
dfd80f2
feat: searoutes fix, changing reGraph
Apr 26, 2024
22edfb0
feat: searoute - change pathfinding algo
Apr 27, 2024
597f6dd
feat: routes - cleanup code
Apr 27, 2024
b47fa6b
feat: routes - change data format
Apr 27, 2024
681d97b
feat: routes - add routes to json export
Apr 28, 2024
d6c01c8
feat: edit routes - start
Apr 29, 2024
68b4cfd
feat: edit routes - main
May 4, 2024
9b46e7b
feat: edit routes - EP
May 4, 2024
865c10c
feat: edit routes - remove route
May 4, 2024
6936f37
feat: route - generate names
May 4, 2024
29e7ab0
feat: route - continue
May 16, 2024
4e79e02
Refactor route merging logic for improved performance
May 23, 2024
fc9bc73
feat: routes - show name in tooltip
Jun 2, 2024
a3a858d
Merge branch 'master' of https://github.com/Azgaar/Fantasy-Map-Genera…
Jun 2, 2024
f6f7beb
feat: routes - create route dialog
Jun 2, 2024
b303588
Merge branch 'master' of https://github.com/Azgaar/Fantasy-Map-Genera…
Jun 19, 2024
cd45c3a
feat: update data on control point remove
Jun 23, 2024
41354a8
feat: routes editor - split route
Jun 23, 2024
432bc8b
feat: add join route functionality to routes editor
Jun 24, 2024
2695afc
Merge branch 'master' of https://github.com/Azgaar/Fantasy-Map-Genera…
Jun 24, 2024
ffadf2a
feat: Add join route functionality to routes editor
Jun 26, 2024
7be5513
feat: Update join route tooltip in routes editor
Jun 26, 2024
c437077
Merge branch 'master' of https://github.com/Azgaar/Fantasy-Map-Genera…
Jul 29, 2024
364ccc1
Merge branch 'master' of https://github.com/Azgaar/Fantasy-Map-Genera…
Aug 1, 2024
28bc6cc
feat: routes overview - sort by length
Aug 1, 2024
834d6f6
feat: routes overview - fix distanceScale value
Aug 1, 2024
ff27937
feat: routes overview - create route
Aug 1, 2024
e6741b3
Refactor getMiddlePoint function to getCloseToEdgePoint
Aug 2, 2024
077248e
Merge branch 'master' of https://github.com/Azgaar/Fantasy-Map-Genera…
Aug 7, 2024
19723bd
feat: routes - change data format, fix issues
Aug 9, 2024
16b441c
feat: routes - regenerateRoutes
Aug 9, 2024
6907c9d
feat: routes - add route on burg creation
Aug 11, 2024
982f003
chore - remove merge conflict markers
Aug 11, 2024
b5a34af
chore - remove merge conflict markers
Aug 11, 2024
a83d7ba
feat: routes name - no unnamed burg names
Aug 11, 2024
b4653c1
feat: routes - lock routes
Aug 11, 2024
8bd8e29
fix: routes - split routes
Aug 11, 2024
da2d6fc
feat: routes - tip correction
Aug 11, 2024
126393f
feat: routes - auto-update part 1
Aug 13, 2024
e09c0e5
feat: routes - return old rePacj logic to not break auto-update
Aug 13, 2024
9e7fd25
feat: routes - auto-update - add connections
Aug 13, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion index.css
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,6 @@ i.icon-lock {
cursor: pointer;
}

#routeEditor > *,
#labelEditor div {
display: inline-block;
}
Expand Down Expand Up @@ -1618,20 +1617,28 @@ div.states > .riverType {

#burgBody > div > div,
#riverBody > div,
#routeBody > div,
#lakeBody > div {
padding: 0.1em;
}

#riverBody div.label,
#riverBody input,
#riverBody select,
#routeBody div.label,
#lakeBody div.label,
#lakeBody input,
#lakeBody select {
display: inline-block;
width: 7em;
}

#routeBody input,
#routeBody select {
display: inline-block;
width: 10em;
}

#stateNameEditor div.label,
#provinceNameEditor div.label,
#regimentBody div.label,
Expand Down
336 changes: 214 additions & 122 deletions index.html

Large diffs are not rendered by default.

57 changes: 57 additions & 0 deletions libs/flatqueue.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
!(function (t, s) {
"object" == typeof exports && "undefined" != typeof module
? (module.exports = s())
: "function" == typeof define && define.amd
? define(s)
: ((t = "undefined" != typeof globalThis ? globalThis : t || self).FlatQueue = s());
})(this, function () {
"use strict";
return class {
constructor() {
(this.ids = []), (this.values = []), (this.length = 0);
}
clear() {
this.length = 0;
}
push(t, s) {
let i = this.length++;
for (; i > 0; ) {
const t = (i - 1) >> 1,
e = this.values[t];
if (s >= e) break;
(this.ids[i] = this.ids[t]), (this.values[i] = e), (i = t);
}
(this.ids[i] = t), (this.values[i] = s);
}
pop() {
if (0 === this.length) return;
const t = this.ids[0];
if ((this.length--, this.length > 0)) {
const t = (this.ids[0] = this.ids[this.length]),
s = (this.values[0] = this.values[this.length]),
i = this.length >> 1;
let e = 0;
for (; e < i; ) {
let t = 1 + (e << 1);
const i = t + 1;
let h = this.ids[t],
l = this.values[t];
const n = this.values[i];
if ((i < this.length && n < l && ((t = i), (h = this.ids[i]), (l = n)), l >= s)) break;
(this.ids[e] = h), (this.values[e] = l), (e = t);
}
(this.ids[e] = t), (this.values[e] = s);
}
return t;
}
peek() {
if (0 !== this.length) return this.ids[0];
}
peekValue() {
if (0 !== this.length) return this.values[0];
}
shrink() {
this.ids.length = this.values.length = this.length;
}
};
});
32 changes: 17 additions & 15 deletions main.js
Original file line number Diff line number Diff line change
Expand Up @@ -647,6 +647,7 @@ async function generate(options) {
Cultures.generate();
Cultures.expand();
BurgsAndStates.generate();
Routes.generate();
Religions.generate();
BurgsAndStates.defineStateForms();
BurgsAndStates.generateProvinces();
Expand Down Expand Up @@ -1175,10 +1176,11 @@ function reGraph() {
for (const i of gridCells.i) {
const height = gridCells.h[i];
const type = gridCells.t[i];

if (height < 20 && type !== -1 && type !== -2) continue; // exclude all deep ocean points
if (type === -2 && (i % 4 === 0 || features[gridCells.f[i]].type === "lake")) continue; // exclude non-coastal lake points
const [x, y] = points[i];

const [x, y] = points[i];
addNewPoint(i, x, y, height);

// add additional points for cells along coast
Expand Down Expand Up @@ -1406,8 +1408,8 @@ function reMarkFeatures() {
queue[0] = cells.f.findIndex(f => !f); // find unmarked cell
}

// markupPackLand
markup(pack.cells, 3, 1, 0);
markup(pack.cells, 3, 1, 0); // markupPackLand
markup(pack.cells, -2, -1, -10); // markupPackWater

function defineHaven(i) {
const water = cells.c[i].filter(c => cells.h[c] < 20);
Expand Down Expand Up @@ -1645,9 +1647,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});

Expand All @@ -1656,15 +1659,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.road[next.e];
const c = r ? Math.max(10 - r, 1) : 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});
}
});
}
Expand Down Expand Up @@ -1785,10 +1787,10 @@ function addZones(number = 1) {
}

function addAvalanche() {
const roads = cells.i.filter(i => !used[i] && cells.road[i] && cells.h[i] >= 70);
if (!roads.length) return;
const routes = cells.i.filter(i => !used[i] && Routes.isConnected(i) && cells.h[i] >= 70);
if (!routes.length) return;

const cell = +ra(roads);
const cell = +ra(routes);
const cellsArray = [],
queue = [cell],
power = rand(3, 15);
Expand Down
22 changes: 3 additions & 19 deletions modules/biomes.js
Original file line number Diff line number Diff line change
@@ -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",
Expand Down Expand Up @@ -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},
Expand Down
37 changes: 23 additions & 14 deletions modules/burgs-and-states.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,27 +6,20 @@ window.BurgsAndStates = (() => {
const n = cells.i.length;

cells.burg = new Uint16Array(n); // cell burg
cells.road = new Uint16Array(n); // cell road power
cells.crossroad = new Uint16Array(n); // cell crossroad power

const burgs = (pack.burgs = placeCapitals());
pack.states = createStates();
const capitalRoutes = Routes.getRoads();

placeTowns();
expandStates();
normalizeStates();
const townRoutes = Routes.getTrails();
specifyBurgs();

const oceanRoutes = Routes.getSearoutes();

collectStatistics();
assignColors();

generateCampaigns();
generateDiplomacy();
Routes.draw(capitalRoutes, townRoutes, oceanRoutes);
drawBurgs();

function placeCapitals() {
Expand Down Expand Up @@ -138,9 +131,8 @@ window.BurgsAndStates = (() => {
while (burgsAdded < burgsNumber && spacing > 1) {
for (let i = 0; burgsAdded < burgsNumber && i < sorted.length; i++) {
if (cells.burg[sorted[i]]) continue;
const cell = sorted[i],
x = cells.p[cell][0],
y = cells.p[cell][1];
const cell = sorted[i];
const [x, y] = cells.p[cell];
const s = spacing * gauss(1, 0.3, 0.2, 2, 2); // randomize to make placement not uniform
if (burgsTree.find(x, y, s) !== undefined) continue; // to close to existing burg
const burg = burgs.length;
Expand Down Expand Up @@ -183,12 +175,12 @@ window.BurgsAndStates = (() => {
} else b.port = 0;

// define burg population (keep urbanization at about 10% rate)
b.population = rn(Math.max((cells.s[i] + cells.road[i] / 2) / 8 + b.i / 1000 + (i % 100) / 1000, 0.1), 3);
b.population = rn(Math.max(cells.s[i] / 8 + b.i / 1000 + (i % 100) / 1000, 0.1), 3);
if (b.capital) b.population = rn(b.population * 1.3, 3); // increase capital population

if (b.port) {
b.population = b.population * 1.3; // increase port population
const [x, y] = getMiddlePoint(i, haven);
const [x, y] = getCloseToEdgePoint(i, haven);
b.x = x;
b.y = y;
}
Expand Down Expand Up @@ -229,6 +221,23 @@ window.BurgsAndStates = (() => {
TIME && console.timeEnd("specifyBurgs");
};

function getCloseToEdgePoint(cell1, cell2) {
const {cells, vertices} = pack;

const [x0, y0] = cells.p[cell1];

const commonVertices = cells.v[cell1].filter(vertex => vertices.c[vertex].some(cell => cell === cell2));
const [x1, y1] = vertices.p[commonVertices[0]];
const [x2, y2] = vertices.p[commonVertices[1]];
const xEdge = (x1 + x2) / 2;
const yEdge = (y1 + y2) / 2;

const x = rn(x0 + 0.95 * (xEdge - x0), 2);
const y = rn(y0 + 0.95 * (yEdge - y0), 2);

return [x, y];
}

const getType = (i, port) => {
const cells = pack.cells;
if (port) return "Naval";
Expand All @@ -244,11 +253,11 @@ window.BurgsAndStates = (() => {
return "Generic";
};

const defineBurgFeatures = newburg => {
const defineBurgFeatures = burg => {
const {cells} = pack;

pack.burgs
.filter(b => (newburg ? b.i == newburg.i : b.i && !b.removed && !b.lock))
.filter(b => (burg ? b.i == burg.i : b.i && !b.removed && !b.lock))
.forEach(b => {
const pop = b.population;
b.citadel = Number(b.capital || (pop > 50 && P(0.75)) || (pop > 15 && P(0.5)) || P(0.1));
Expand Down
55 changes: 55 additions & 0 deletions modules/dynamic/auto-update.js
Original file line number Diff line number Diff line change
Expand Up @@ -860,4 +860,59 @@ export function resolveVersionConflicts(version) {
shiftCompass();
}
}

if (version < 1.99) {
// v1.99.00 changed routes generation algorithm and data format
delete cells.road;
delete cells.crossroad;

pack.routes = [];
const POINT_DISTANCE = grid.spacing * 0.75;

routes.selectAll("g").each(function () {
const group = this.id;
if (!group) return;

for (const node of this.querySelectorAll("path")) {
const totalLength = node.getTotalLength();
if (!totalLength) debugger;
const increment = totalLength / Math.ceil(totalLength / POINT_DISTANCE);
const points = [];

for (let i = 0; i <= totalLength + 0.1; i += increment) {
const point = node.getPointAtLength(i);
const x = rn(point.x, 2);
const y = rn(point.y, 2);
const cellId = findCell(x, y);
points.push([x, y, cellId]);
}

if (points.length < 2) return;

const secondCellId = points[1][2];
const feature = pack.cells.f[secondCellId];

pack.routes.push({i: pack.routes.length, group, feature, points});
}
});

routes.selectAll("path").remove();
if (layerIsOn("toggleRoutes")) drawRoutes();

const links = (pack.cells.routes = {});
for (const route of pack.routes) {
for (let i = 0; i < route.points.length - 1; i++) {
const cellId = route.points[i][2];
const nextCellId = route.points[i + 1][2];

if (cellId !== nextCellId) {
if (!links[cellId]) links[cellId] = {};
links[cellId][nextCellId] = route.i;

if (!links[nextCellId]) links[nextCellId] = {};
links[nextCellId][cellId] = route.i;
}
}
}
}
}
2 changes: 1 addition & 1 deletion modules/dynamic/editors/states-editor.js
Original file line number Diff line number Diff line change
Expand Up @@ -966,7 +966,7 @@ function dragStateBrush() {
const p = d3.mouse(this);
moveCircle(p[0], p[1], r);

const found = r > 5 ? findAll(p[0], p[1], r) : [findCell(p[0], p[1], r)];
const found = r > 5 ? findAll(p[0], p[1], r) : [findCell(p[0], p[1])];
const selection = found.filter(isLand);
if (selection) changeStateForSelection(selection);
});
Expand Down
Loading