refactor: migrate ice module

This commit is contained in:
Marc Emmanuel 2026-02-08 09:58:32 +01:00
parent f48178a93c
commit 2018a9f01d
4 changed files with 37 additions and 48 deletions

View file

@ -8494,7 +8494,6 @@
<script defer src="config/heightmap-templates.js"></script>
<script defer src="config/precreated-heightmaps.js"></script>
<script defer src="modules/ice.js?v=1.111.0"></script>
<script defer src="modules/military-generator.js?v=1.107.0"></script>
<script defer src="modules/markers-generator.js?v=1.107.0"></script>
<script defer src="modules/coa-generator.js?v=1.99.00"></script>

159
src/modules/ice.ts Normal file
View file

@ -0,0 +1,159 @@
import Alea from "alea";
import { min } from 'd3';
import { clipPoly, getGridPolygon, getIsolines, lerp, minmax, normalize, P, ra, rand, rn } from "../utils";
import { Point } from "./voronoi";
declare global {
var Ice: IceModule;
}
class IceModule {
// Find next available id for new ice element idealy filling gaps
private getNextId() {
if (pack.ice.length === 0) return 0;
// find gaps in existing ids
const existingIds = pack.ice.map(e => e.i).sort((a, b) => a - b);
for (let id = 0; id < existingIds[existingIds.length - 1]; id++) {
if (!existingIds.includes(id)) return id;
}
return existingIds[existingIds.length - 1] + 1;
}
// Clear all ice
private clear() {
pack.ice = [];
}
// Generate glaciers and icebergs based on temperature and height
public generate() {
this.clear();
const { cells, features } = grid;
const { temp, h } = cells;
Math.random = Alea(seed);
const ICEBERG_MAX_TEMP = 0;
const GLACIER_MAX_TEMP = -8;
const minMaxTemp = min<number>(temp)!;
// Generate glaciers on cold land
{
const type = "iceShield";
const getType = (cellId: number) =>
h[cellId] >= 20 && temp[cellId] <= GLACIER_MAX_TEMP ? type : null;
const isolines = getIsolines(grid, getType, { polygons: true });
if (isolines[type]?.polygons) {
isolines[type].polygons.forEach((points: Point[]) => {
const clipped = clipPoly(points, graphWidth, graphHeight);
pack.ice.push({
i: this.getNextId(),
points: clipped,
type: "glacier"
});
});
}
}
// Generate icebergs on cold water
for (const cellId of grid.cells.i) {
const t = temp[cellId];
if (h[cellId] >= 20) continue; // no icebergs on land
if (t > ICEBERG_MAX_TEMP) continue; // too warm: no icebergs
if (features[cells.f[cellId]].type === "lake") continue; // no icebergs on lakes
if (P(0.8)) continue; // skip most of eligible cells
const randomFactor = 0.8 + rand() * 0.4; // random size factor
let baseSize = (1 - normalize(t, minMaxTemp, 1)) * 0.8; // size: 0 = zero, 1 = full
if (cells.t[cellId] === -1) baseSize /= 1.3; // coastline: smaller icebergs
const size = minmax(rn(baseSize * randomFactor, 2), 0.1, 1);
const [cx, cy] = grid.points[cellId];
const points = getGridPolygon(cellId, grid).map(([x, y]: Point) => [
rn(lerp(cx, x, size), 2),
rn(lerp(cy, y, size), 2)
]);
pack.ice.push({
i: this.getNextId(),
points,
type: "iceberg",
cellId,
size
});
}
}
addIceberg(cellId: number, size: number) {
const [cx, cy] = grid.points[cellId];
const points = getGridPolygon(cellId, grid).map(([x, y]: Point) => [
rn(lerp(cx, x, size), 2),
rn(lerp(cy, y, size), 2)
]);
const id = this.getNextId();
pack.ice.push({
i: id,
points,
type: "iceberg",
cellId,
size
});
redrawIceberg(id);
}
removeIce(id: number) {
const index = pack.ice.findIndex(element => element.i === id);
if (index !== -1) {
const type = pack.ice.find(element => element.i === id).type;
pack.ice.splice(index, 1);
if (type === "glacier") {
redrawGlacier(id);
} else {
redrawIceberg(id);
}
}
}
randomizeIcebergShape(id: number) {
const iceberg = pack.ice.find(element => element.i === id);
if (!iceberg) return;
const cellId = iceberg.cellId;
const size = iceberg.size;
const [cx, cy] = grid.points[cellId];
// Get a different random cell for the polygon template
const i = ra(grid.cells.i);
const cn = grid.points[i];
const poly = getGridPolygon(i, grid).map((p: Point) => [p[0] - cn[0], p[1] - cn[1]]);
const points = poly.map((p: Point) => [
rn(cx + p[0] * size, 2),
rn(cy + p[1] * size, 2)
]);
iceberg.points = points;
}
changeIcebergSize(id: number, newSize: number) {
const iceberg = pack.ice.find(element => element.i === id);
if (!iceberg) return;
const cellId = iceberg.cellId;
const [cx, cy] = grid.points[cellId];
const oldSize = iceberg.size;
const flat = iceberg.points.flat();
const pairs = [];
while (flat.length) pairs.push(flat.splice(0, 2));
const poly = pairs.map(p => [(p[0] - cx) / oldSize, (p[1] - cy) / oldSize]);
const points = poly.map(p => [
rn(cx + p[0] * newSize, 2),
rn(cy + p[1] * newSize, 2)
]);
iceberg.points = points;
iceberg.size = newSize;
}
}
window.Ice = new IceModule();

View file

@ -13,3 +13,4 @@ import "./states-generator";
import "./zones-generator";
import "./religions-generator";
import "./provinces-generator";
import "./ice";

View file

@ -7,13 +7,13 @@ import { minmax, rn } from "./numberUtils";
* @param {number} max - maximum value
* @return {number} random integer between min and max
*/
export const rand = (min: number, max?: number): number => {
export const rand = (min?: number, max?: number): number => {
if (min === undefined && max === undefined) return Math.random();
if (max === undefined) {
max = min;
min = 0;
}
return Math.floor(Math.random() * (max - min + 1)) + min;
return Math.floor(Math.random() * (max! - min! + 1)) + min!;
};
/**