mirror of
https://github.com/openfrontio/OpenFrontIO.git
synced 2026-06-23 05:59:38 +00:00
remove small lakes, use largest body of water as ocean
This commit is contained in:
+46
-46
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+67
-67
File diff suppressed because one or more lines are too long
+26
-26
File diff suppressed because one or more lines are too long
+14
-14
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+116
-20588
File diff suppressed because one or more lines are too long
+58
-5167
File diff suppressed because one or more lines are too long
+204
-204
File diff suppressed because one or more lines are too long
+86
-86
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -57,7 +57,6 @@ async function loadTerrainMap(mapName: string): Promise<void> {
|
||||
.fill(null)
|
||||
.map(() => Array(img.height).fill(null));
|
||||
|
||||
// Iterate through each pixel
|
||||
for (let x = 0; x < img.width; x++) {
|
||||
for (let y = 0; y < img.height; y++) {
|
||||
const color = img.getPixelRGBA(x, y);
|
||||
@@ -208,6 +207,83 @@ function neighbors(x: number, y: number, map: Terrain[][]): Terrain[] {
|
||||
return ns;
|
||||
}
|
||||
|
||||
// Improved processOcean function that identifies the largest body of water
|
||||
function processOcean(map: Terrain[][]) {
|
||||
const visited = new Set<string>();
|
||||
const waterBodies: { coords: Coord[]; size: number }[] = [];
|
||||
|
||||
// Find all distinct water bodies
|
||||
for (let x = 0; x < map.length; x++) {
|
||||
for (let y = 0; y < map[0].length; y++) {
|
||||
if (map[x][y].type === TerrainType.Water) {
|
||||
const key = `${x},${y}`;
|
||||
if (visited.has(key)) continue;
|
||||
|
||||
// Find all connected water tiles
|
||||
const waterBody: Coord[] = [];
|
||||
const queue: Coord[] = [{ x, y }];
|
||||
|
||||
while (queue.length > 0) {
|
||||
const coord = queue.shift()!;
|
||||
const currentKey = `${coord.x},${coord.y}`;
|
||||
|
||||
if (visited.has(currentKey)) continue;
|
||||
visited.add(currentKey);
|
||||
|
||||
if (map[coord.x][coord.y].type === TerrainType.Water) {
|
||||
waterBody.push(coord);
|
||||
|
||||
// Check all four directions
|
||||
for (const [dx, dy] of [
|
||||
[-1, 0],
|
||||
[1, 0],
|
||||
[0, -1],
|
||||
[0, 1],
|
||||
]) {
|
||||
const newX = coord.x + dx;
|
||||
const newY = coord.y + dy;
|
||||
|
||||
if (
|
||||
newX >= 0 &&
|
||||
newX < map.length &&
|
||||
newY >= 0 &&
|
||||
newY < map[0].length
|
||||
) {
|
||||
queue.push({ x: newX, y: newY });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Store this water body if it has any tiles
|
||||
if (waterBody.length > 0) {
|
||||
waterBodies.push({
|
||||
coords: waterBody,
|
||||
size: waterBody.length,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sort water bodies by size (largest first)
|
||||
waterBodies.sort((a, b) => b.size - a.size);
|
||||
|
||||
// Mark the largest water body as ocean
|
||||
if (waterBodies.length > 0) {
|
||||
const largestWaterBody = waterBodies[0];
|
||||
|
||||
// Mark all tiles in the largest water body as ocean
|
||||
for (const coord of largestWaterBody.coords) {
|
||||
map[coord.x][coord.y].ocean = true;
|
||||
}
|
||||
|
||||
console.log(`Identified ocean with ${largestWaterBody.size} water tiles`);
|
||||
} else {
|
||||
console.log("No water bodies found in the map");
|
||||
}
|
||||
}
|
||||
|
||||
function packTerrain(mapName: string, map: Terrain[][]): Uint8Array {
|
||||
const width = map.length;
|
||||
const height = map[0].length;
|
||||
@@ -249,58 +325,14 @@ function packTerrain(mapName: string, map: Terrain[][]): Uint8Array {
|
||||
return packedData;
|
||||
}
|
||||
|
||||
function processOcean(map: Terrain[][]) {
|
||||
const queue: Coord[] = [];
|
||||
if (map[0][0].type == TerrainType.Water) {
|
||||
queue.push({ x: 0, y: 0 });
|
||||
} else if (map[map.length - 1][map[0].length - 1].type == TerrainType.Water) {
|
||||
queue.push({ x: map.length - 1, y: map[0].length - 1 });
|
||||
} else {
|
||||
queue.push({ x: 0, y: map[0].length - 1 });
|
||||
}
|
||||
const visited = new Set<string>();
|
||||
|
||||
while (queue.length > 0) {
|
||||
const coord = queue.shift()!;
|
||||
const key = `${coord.x},${coord.y}`;
|
||||
|
||||
if (visited.has(key)) continue;
|
||||
visited.add(key);
|
||||
|
||||
const terrain = map[coord.x][coord.y];
|
||||
if (terrain.type === TerrainType.Water) {
|
||||
terrain.ocean = true;
|
||||
|
||||
// Check neighbors
|
||||
for (const [dx, dy] of [
|
||||
[-1, 0],
|
||||
[1, 0],
|
||||
[0, -1],
|
||||
[0, 1],
|
||||
]) {
|
||||
const newX = coord.x + dx;
|
||||
const newY = coord.y + dy;
|
||||
|
||||
if (
|
||||
newX >= 0 &&
|
||||
newX < map.length &&
|
||||
newY >= 0 &&
|
||||
newY < map[0].length
|
||||
) {
|
||||
queue.push({ x: newX, y: newY });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getIsland(
|
||||
function getArea(
|
||||
map: Terrain[][],
|
||||
x: number,
|
||||
y: number,
|
||||
visited: Set<string>,
|
||||
targetType: TerrainType,
|
||||
) {
|
||||
let island = [];
|
||||
let area = [];
|
||||
let next = [[x, y]];
|
||||
while (next.length) {
|
||||
const [x, y] = next.pop();
|
||||
@@ -310,24 +342,22 @@ function getIsland(
|
||||
x >= map.length ||
|
||||
y < 0 ||
|
||||
y >= map[0].length ||
|
||||
x < 0 ||
|
||||
x >= map.length ||
|
||||
visited.has(key)
|
||||
)
|
||||
continue;
|
||||
|
||||
if (map[x][y].type == TerrainType.Land) {
|
||||
if (map[x][y].type === targetType) {
|
||||
next.push([x + 1, y]);
|
||||
next.push([x - 1, y]);
|
||||
next.push([x, y + 1]);
|
||||
next.push([x, y - 1]);
|
||||
}
|
||||
|
||||
island.push([x, y]);
|
||||
area.push([x, y]);
|
||||
visited.add(key);
|
||||
}
|
||||
|
||||
return island;
|
||||
return area;
|
||||
}
|
||||
|
||||
function removeSmallIslands(map: Terrain[][]) {
|
||||
@@ -335,14 +365,11 @@ function removeSmallIslands(map: Terrain[][]) {
|
||||
|
||||
for (let x = 0; x < map.length; x++) {
|
||||
for (let y = 0; y < map[0].length; y++) {
|
||||
if (map[x][y].type == TerrainType.Land) {
|
||||
if (map[x][y].type === TerrainType.Land) {
|
||||
const key = `${x},${y}`;
|
||||
|
||||
// PERF: If getIsland already visited that coordinates then it's
|
||||
// useless to go over it again.
|
||||
if (visited.has(key)) continue;
|
||||
|
||||
const island = getIsland(map, x, y, visited);
|
||||
const island = getArea(map, x, y, visited, TerrainType.Land);
|
||||
if (island.length < min_island_size) {
|
||||
island.forEach((pos) => {
|
||||
const x = pos[0];
|
||||
@@ -357,23 +384,30 @@ function removeSmallIslands(map: Terrain[][]) {
|
||||
}
|
||||
|
||||
function removeSmallLakes(mapName: string, map: Terrain[][]) {
|
||||
console.log(`${mapName}: removing lakes ${map.length}, ${map[0].length}`);
|
||||
const visited = new Set<string>();
|
||||
const min_lake_size = 30; // Using same size threshold as islands
|
||||
|
||||
console.log(
|
||||
`${mapName}: removing small lakes ${map.length}, ${map[0].length}`,
|
||||
);
|
||||
|
||||
for (let x = 0; x < map.length; x++) {
|
||||
for (let y = 0; y < map[0].length; y++) {
|
||||
if (map[x][y].type != TerrainType.Water) {
|
||||
continue;
|
||||
}
|
||||
let allLand = true;
|
||||
for (const neighbor of neighbors(x, y, map)) {
|
||||
if (neighbor.type != TerrainType.Land) {
|
||||
allLand = false;
|
||||
if (map[x][y].type === TerrainType.Water && !map[x][y].ocean) {
|
||||
const key = `${x},${y}`;
|
||||
if (visited.has(key)) continue;
|
||||
|
||||
const lake = getArea(map, x, y, visited, TerrainType.Water);
|
||||
if (lake.length < min_lake_size) {
|
||||
lake.forEach((pos) => {
|
||||
const x = pos[0];
|
||||
const y = pos[1];
|
||||
map[x][y].type = TerrainType.Land;
|
||||
map[x][y].magnitude = 0;
|
||||
map[x][y].ocean = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
if (allLand) {
|
||||
map[x][y].type = TerrainType.Land;
|
||||
map[x][y].magnitude = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user