diff --git a/public/main.js b/public/main.js
index 7dbb9585..6da462d5 100644
--- a/public/main.js
+++ b/public/main.js
@@ -632,8 +632,6 @@ async function generate(options) {
Biomes.define();
Features.defineGroups();
- Ice.generate();
-
rankCells();
Cultures.generate();
Cultures.expand();
diff --git a/public/modules/dynamic/auto-update.js b/public/modules/dynamic/auto-update.js
index 6e252330..a3190e3b 100644
--- a/public/modules/dynamic/auto-update.js
+++ b/public/modules/dynamic/auto-update.js
@@ -253,8 +253,8 @@ export function resolveVersionConflicts(mapVersion) {
const source = findCell(s.x, s.y);
const mouth = findCell(e.x, e.y);
const name = Rivers.getName(mouth);
- const type = length < 25 ? rw({ Creek: 9, River: 3, Brook: 3, Stream: 1 }) : "River";
- pack.rivers.push({ i, parent: 0, length, source, mouth, basin: i, name, type });
+ const type = length < 25 ? rw({Creek: 9, River: 3, Brook: 3, Stream: 1}) : "River";
+ pack.rivers.push({i, parent: 0, length, source, mouth, basin: i, name, type});
});
}
@@ -270,7 +270,7 @@ export function resolveVersionConflicts(mapVersion) {
const era = Names.getBaseShort(P(0.7) ? 1 : rand(nameBases.length)) + " Era";
const eraShort = era[0] + "E";
const military = Military.getDefaultOptions();
- options = { winds, year, era, eraShort, military };
+ options = {winds, year, era, eraShort, military};
// v1.3 added campaings data for all states
States.generateCampaigns();
@@ -481,7 +481,7 @@ export function resolveVersionConflicts(mapVersion) {
if (isOlderThan("1.65.0")) {
// v1.65 changed rivers data
d3.select("#rivers").attr("style", null); // remove style to unhide layer
- const { cells, rivers } = pack;
+ const {cells, rivers} = pack;
const defaultWidthFactor = rn(1 / (pointsInput.dataset.cells / 10000) ** 0.25, 2);
for (const river of rivers) {
@@ -497,8 +497,8 @@ export function resolveVersionConflicts(mapVersion) {
for (let i = 0; i <= segments; i++) {
const shift = increment * i;
- const { x: x1, y: y1 } = node.getPointAtLength(length + shift);
- const { x: x2, y: y2 } = node.getPointAtLength(length - shift);
+ const {x: x1, y: y1} = node.getPointAtLength(length + shift);
+ const {x: x2, y: y2} = node.getPointAtLength(length - shift);
const x = rn((x1 + x2) / 2, 1);
const y = rn((y1 + y2) / 2, 1);
@@ -565,7 +565,7 @@ export function resolveVersionConflicts(mapVersion) {
const fill = circle && circle.getAttribute("fill");
const stroke = circle && circle.getAttribute("stroke");
- const marker = { i, icon, type, x, y, size, cell };
+ const marker = {i, icon, type, x, y, size, cell};
if (size && size !== 30) marker.size = size;
if (!isNaN(px) && px !== 12) marker.px = px;
if (!isNaN(dx) && dx !== 50) marker.dx = dx;
@@ -631,7 +631,7 @@ export function resolveVersionConflicts(mapVersion) {
if (isOlderThan("1.88.0")) {
// v1.87 may have incorrect shield for some reason
- pack.states.forEach(({ coa }) => {
+ pack.states.forEach(({coa}) => {
if (coa?.shield === "state") delete coa.shield;
});
}
@@ -639,13 +639,13 @@ export function resolveVersionConflicts(mapVersion) {
if (isOlderThan("1.91.0")) {
// from 1.91.00 custom coa is moved to coa object
pack.states.forEach(state => {
- if (state.coa === "custom") state.coa = { custom: true };
+ if (state.coa === "custom") state.coa = {custom: true};
});
pack.provinces.forEach(province => {
- if (province.coa === "custom") province.coa = { custom: true };
+ if (province.coa === "custom") province.coa = {custom: true};
});
pack.burgs.forEach(burg => {
- if (burg.coa === "custom") burg.coa = { custom: true };
+ if (burg.coa === "custom") burg.coa = {custom: true};
});
// from 1.91.00 emblems don't have transform attribute
@@ -747,7 +747,7 @@ export function resolveVersionConflicts(mapVersion) {
const skip = terrs.attr("skip");
const relax = terrs.attr("relax");
- const curveTypes = { 0: "curveBasisClosed", 1: "curveLinear", 2: "curveStep" };
+ const curveTypes = {0: "curveBasisClosed", 1: "curveLinear", 2: "curveStep"};
const curve = curveTypes[terrs.attr("curve")] || "curveBasisClosed";
terrs
@@ -882,7 +882,7 @@ export function resolveVersionConflicts(mapVersion) {
const secondCellId = points[1][2];
const feature = pack.cells.f[secondCellId];
- pack.routes.push({ i: pack.routes.length, group, feature, points });
+ pack.routes.push({i: pack.routes.length, group, feature, points});
}
}
routes.selectAll("path").remove();
@@ -914,7 +914,7 @@ export function resolveVersionConflicts(mapVersion) {
const type = this.dataset.type;
const color = this.getAttribute("fill");
const cells = this.dataset.cells.split(",").map(Number);
- pack.zones.push({ i, name, type, cells, color });
+ pack.zones.push({i, name, type, cells, color});
});
zones.style("display", null).selectAll("*").remove();
if (layerIsOn("toggleZones")) drawZones();
@@ -975,7 +975,7 @@ export function resolveVersionConflicts(mapVersion) {
if (isOlderThan("1.109.0")) {
// v1.109.0 added customizable burg groups and icons
- options.burgs = { groups: [] };
+ options.burgs = {groups: []};
burgIcons.selectAll("circle, use").each(function () {
const group = this.parentNode.id;
@@ -987,7 +987,7 @@ export function resolveVersionConflicts(mapVersion) {
burgIcons.selectAll("g").each(function (_el, index) {
const name = this.id;
const isDefault = name === "towns";
- options.burgs.groups.push({ name, active: true, order: index + 1, isDefault, preview: "watabou-city" });
+ options.burgs.groups.push({name, active: true, order: index + 1, isDefault, preview: "watabou-city"});
if (!this.dataset.icon) this.dataset.icon = "#icon-circle";
const size = Number(this.getAttribute("size") || 2) * 2;
@@ -1036,74 +1036,4 @@ export function resolveVersionConflicts(mapVersion) {
delete options.showMFCGMap;
delete options.villageMaxPopulation;
}
-
- if (isOlderThan("1.111.0")) {
- // v1.111.0 moved ice data from SVG to data model
- // Migrate old ice SVG elements to new pack.ice structure
- if (!pack.ice) {
- pack.ice = [];
- let iceId = 0;
-
- const iceLayer = document.getElementById("ice");
- if (iceLayer) {
- // Migrate glaciers (type="iceShield")
- iceLayer.querySelectorAll("polygon[type='iceShield']").forEach(polygon => {
- // Parse points string "x1,y1 x2,y2 x3,y3 ..." into array [[x1,y1], [x2,y2], ...]
- const points = [...polygon.points].map(svgPoint => [svgPoint.x, svgPoint.y]);
-
- const transform = polygon.getAttribute("transform");
- const iceElement = {
- i: iceId++,
- points,
- type: "glacier"
- };
- if (transform) {
- iceElement.offset = parseTransform(transform);
- }
- pack.ice.push(iceElement);
- });
-
- // Migrate icebergs
- iceLayer.querySelectorAll("polygon:not([type])").forEach(polygon => {
- const cellId = +polygon.getAttribute("cell");
- const size = +polygon.getAttribute("size");
-
- // points string must exist, cell attribute must be present, and size must be non-zero
- if (polygon.getAttribute("cell") === null || !size) return;
-
- // Parse points string "x1,y1 x2,y2 x3,y3 ..." into array [[x1,y1], [x2,y2], ...]
- const points = [...polygon.points].map(svgPoint => [svgPoint.x, svgPoint.y]);
-
- const transform = polygon.getAttribute("transform");
- const iceElement = {
- i: iceId++,
- points,
- type: "iceberg",
- cellId,
- size
- };
- if (transform) {
- iceElement.offset = parseTransform(transform);
- }
- pack.ice.push(iceElement);
- });
-
- // Clear old SVG elements
- iceLayer.querySelectorAll("*").forEach(el => el.remove());
- } else {
- // If ice layer element doesn't exist, create it
- ice = viewbox.insert("g", "#coastline").attr("id", "ice");
- ice
- .attr("opacity", null)
- .attr("fill", "#e8f0f6")
- .attr("stroke", "#e8f0f6")
- .attr("stroke-width", 1)
- .attr("filter", "url(#dropShadow05)");
- }
-
- // Re-render ice from migrated data
- if (layerIsOn("toggleIce")) drawIce();
- }
-
- }
}
diff --git a/public/modules/ice.js b/public/modules/ice.js
deleted file mode 100644
index 90c7c3e6..00000000
--- a/public/modules/ice.js
+++ /dev/null
@@ -1,170 +0,0 @@
-"use strict";
-
-// Ice layer data model - separates ice data from SVG rendering
-window.Ice = (function () {
-
- // Find next available id for new ice element idealy filling gaps
- function 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;
- }
-
- // Generate glaciers and icebergs based on temperature and height
- function generate() {
- clear();
- const { cells, features } = grid;
- const { temp, h } = cells;
- Math.random = aleaPRNG(seed);
-
- const ICEBERG_MAX_TEMP = 0;
- const GLACIER_MAX_TEMP = -8;
- const minMaxTemp = d3.min(temp);
-
- // Generate glaciers on cold land
- {
- const type = "iceShield";
- const getType = cellId =>
- 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 => {
- const clipped = clipPoly(points);
- pack.ice.push({
- i: 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).map(([x, y]) => [
- rn(lerp(cx, x, size), 2),
- rn(lerp(cy, y, size), 2)
- ]);
-
- pack.ice.push({
- i: getNextId(),
- points,
- type: "iceberg",
- cellId,
- size
- });
- }
- }
-
- function addIceberg(cellId, size) {
- const [cx, cy] = grid.points[cellId];
- const points = getGridPolygon(cellId).map(([x, y]) => [
- rn(lerp(cx, x, size), 2),
- rn(lerp(cy, y, size), 2)
- ]);
- const id = getNextId();
- pack.ice.push({
- i: id,
- points,
- type: "iceberg",
- cellId,
- size
- });
- redrawIceberg(id);
- }
-
- function removeIce(id) {
- 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);
- }
-
- }
- }
-
- function updateIceberg(id, points, size) {
- const iceberg = pack.ice.find(element => element.i === id);
- if (iceberg) {
- iceberg.points = points;
- iceberg.size = size;
- }
- }
-
- function randomizeIcebergShape(id) {
- 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).map(p => [p[0] - cn[0], p[1] - cn[1]]);
- const points = poly.map(p => [
- rn(cx + p[0] * size, 2),
- rn(cy + p[1] * size, 2)
- ]);
-
- iceberg.points = points;
- }
-
- function changeIcebergSize(id, newSize) {
- 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;
- }
-
- // Clear all ice
- function clear() {
- pack.ice = [];
- }
-
- return {
- generate,
- addIceberg,
- removeIce,
- updateIceberg,
- randomizeIcebergShape,
- changeIcebergSize,
- clear
- };
-})();
diff --git a/public/modules/io/load.js b/public/modules/io/load.js
index 9b401733..689757b2 100644
--- a/public/modules/io/load.js
+++ b/public/modules/io/load.js
@@ -406,7 +406,6 @@ async function parseLoadedData(data, mapVersion) {
pack.cells.province = data[27] ? Uint16Array.from(data[27].split(",")) : new Uint16Array(pack.cells.i.length);
// data[28] had deprecated cells.crossroad
pack.cells.routes = data[36] ? JSON.parse(data[36]) : {};
- pack.ice = data[39] ? JSON.parse(data[39]) : [];
if (data[31]) {
const namesDL = data[31].split("/");
@@ -450,7 +449,7 @@ async function parseLoadedData(data, mapVersion) {
if (isVisible(routes) && hasChild(routes, "path")) turnOn("toggleRoutes");
if (hasChildren(temperature)) turnOn("toggleTemperature");
if (hasChild(population, "line")) turnOn("togglePopulation");
- if (isVisible(ice)) turnOn("toggleIce");
+ if (hasChildren(ice)) turnOn("toggleIce");
if (hasChild(prec, "circle")) turnOn("togglePrecipitation");
if (isVisible(emblems) && hasChild(emblems, "use")) turnOn("toggleEmblems");
if (isVisible(labels)) turnOn("toggleLabels");
diff --git a/public/modules/io/save.js b/public/modules/io/save.js
index 25cd7493..304fef59 100644
--- a/public/modules/io/save.js
+++ b/public/modules/io/save.js
@@ -32,13 +32,12 @@ async function saveMap(method) {
$(this).dialog("close");
}
},
- position: { my: "center", at: "center", of: "svg" }
+ position: {my: "center", at: "center", of: "svg"}
});
}
}
function prepareMapData() {
-
const date = new Date();
const dateString = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
const license = "File can be loaded in azgaar.github.io/Fantasy-Map-Generator";
@@ -90,8 +89,8 @@ function prepareMapData() {
const serializedSVG = new XMLSerializer().serializeToString(cloneEl);
- const { spacing, cellsX, cellsY, boundary, points, features, cellsDesired } = grid;
- const gridGeneral = JSON.stringify({ spacing, cellsX, cellsY, boundary, points, features, cellsDesired });
+ const {spacing, cellsX, cellsY, boundary, points, features, cellsDesired} = grid;
+ const gridGeneral = JSON.stringify({spacing, cellsX, cellsY, boundary, points, features, cellsDesired});
const packFeatures = JSON.stringify(pack.features);
const cultures = JSON.stringify(pack.cultures);
const states = JSON.stringify(pack.states);
@@ -103,7 +102,6 @@ function prepareMapData() {
const cellRoutes = JSON.stringify(pack.cells.routes);
const routes = JSON.stringify(pack.routes);
const zones = JSON.stringify(pack.zones);
- const ice = JSON.stringify(pack.ice);
// store name array only if not the same as default
const defaultNB = Names.getNameBases();
@@ -157,22 +155,21 @@ function prepareMapData() {
markers,
cellRoutes,
routes,
- zones,
- ice
+ zones
].join("\r\n");
return mapData;
}
// save map file to indexedDB
async function saveToStorage(mapData, showTip = false) {
- const blob = new Blob([mapData], { type: "text/plain" });
+ const blob = new Blob([mapData], {type: "text/plain"});
await ldb.set("lastMap", blob);
showTip && tip("Map is saved to the browser storage", false, "success");
}
// download map file
function saveToMachine(mapData, filename) {
- const blob = new Blob([mapData], { type: "text/plain" });
+ const blob = new Blob([mapData], {type: "text/plain"});
const URL = window.URL.createObjectURL(blob);
const link = document.createElement("a");
diff --git a/public/modules/renderers/draw-ice.js b/public/modules/renderers/draw-ice.js
deleted file mode 100644
index 4b35f75c..00000000
--- a/public/modules/renderers/draw-ice.js
+++ /dev/null
@@ -1,70 +0,0 @@
-"use strict";
-
-// Ice layer renderer - renders ice from data model to SVG
-function drawIce() {
- TIME && console.time("drawIce");
-
- // Clear existing ice SVG
- ice.selectAll("*").remove();
-
- let html = "";
-
- // Draw all ice elements
- pack.ice.forEach(iceElement => {
- if (iceElement.type === "glacier") {
- html += getGlacierHtml(iceElement);
- } else if (iceElement.type === "iceberg") {
- html += getIcebergHtml(iceElement);
- }
- });
-
- ice.html(html);
-
- TIME && console.timeEnd("drawIce");
-}
-
-function redrawIceberg(id) {
- TIME && console.time("redrawIceberg");
- const iceberg = pack.ice.find(element => element.i === id);
- let el = ice.selectAll(`polygon[data-id="${id}"]:not([type="glacier"])`);
- if (!iceberg && !el.empty()) {
- el.remove();
- } else {
- if (el.empty()) {
- // Create new element if it doesn't exist
- const polygon = getIcebergHtml(iceberg);
- ice.node().insertAdjacentHTML("beforeend", polygon);
- el = ice.selectAll(`polygon[data-id="${id}"]:not([type="glacier"])`);
- }
- el.attr("points", iceberg.points);
- el.attr("transform", iceberg.offset ? `translate(${iceberg.offset[0]},${iceberg.offset[1]})` : null);
- }
- TIME && console.timeEnd("redrawIceberg");
-}
-
-function redrawGlacier(id) {
- TIME && console.time("redrawGlacier");
- const glacier = pack.ice.find(element => element.i === id);
- let el = ice.selectAll(`polygon[data-id="${id}"][type="glacier"]`);
- if (!glacier && !el.empty()) {
- el.remove();
- } else {
- if (el.empty()) {
- // Create new element if it doesn't exist
- const polygon = getGlacierHtml(glacier);
- ice.node().insertAdjacentHTML("beforeend", polygon);
- el = ice.selectAll(`polygon[data-id="${id}"][type="glacier"]`);
- }
- el.attr("points", glacier.points);
- el.attr("transform", glacier.offset ? `translate(${glacier.offset[0]},${glacier.offset[1]})` : null);
- }
- TIME && console.timeEnd("redrawGlacier");
-}
-
-function getGlacierHtml(glacier) {
- return ``;
-}
-
-function getIcebergHtml(iceberg) {
- return ``;
-}
\ No newline at end of file
diff --git a/public/modules/ui/editors.js b/public/modules/ui/editors.js
index 50eaf1c7..77c391ee 100644
--- a/public/modules/ui/editors.js
+++ b/public/modules/ui/editors.js
@@ -26,7 +26,7 @@ function clicked() {
else if (ancestor.id === "labels" && el.tagName === "tspan") editLabel();
else if (grand.id === "burgLabels") editBurg();
else if (grand.id === "burgIcons") editBurg();
- else if (parent.id === "ice") editIce(el);
+ else if (parent.id === "ice") editIce();
else if (parent.id === "terrain") editReliefIcon();
else if (grand.id === "markers" || great.id === "markers") editMarker();
else if (grand.id === "coastline") editCoastline();
diff --git a/public/modules/ui/heightmap-editor.js b/public/modules/ui/heightmap-editor.js
index 5c3f1fc3..d655e39d 100644
--- a/public/modules/ui/heightmap-editor.js
+++ b/public/modules/ui/heightmap-editor.js
@@ -259,8 +259,6 @@ function editHeightmap(options) {
Rivers.specify();
Lakes.defineNames();
- Ice.generate();
-
Military.generate();
Markers.generate();
Zones.generate();
@@ -467,10 +465,6 @@ function editHeightmap(options) {
.attr("id", d => base + d);
});
- // recalculate ice
- Ice.generate();
- ice.selectAll("*").remove();
-
TIME && console.timeEnd("restoreRiskedData");
INFO && console.groupEnd("Edit Heightmap");
}
diff --git a/public/modules/ui/ice-editor.js b/public/modules/ui/ice-editor.js
index 16818b4c..a9e6ff28 100644
--- a/public/modules/ui/ice-editor.js
+++ b/public/modules/ui/ice-editor.js
@@ -1,32 +1,26 @@
"use strict";
-function editIce(element) {
+function editIce() {
if (customization) return;
- if (elSelected && element === elSelected.node()) return;
-
closeDialogs(".stable");
if (!layerIsOn("toggleIce")) toggleIce();
elSelected = d3.select(d3.event.target);
- const id = +elSelected.attr("data-id");
- const iceElement = pack.ice.find(el => el.i === id);
- const isGlacier = elSelected.attr("type") === "glacier";
- const type = isGlacier ? "Glacier" : "Iceberg";
-
- document.getElementById("iceRandomize").style.display = isGlacier ? "none" : "inline-block";
- document.getElementById("iceSize").style.display = isGlacier ? "none" : "inline-block";
- if (!isGlacier) document.getElementById("iceSize").value = iceElement?.size || "";
-
+ const type = elSelected.attr("type") ? "Glacier" : "Iceberg";
+ document.getElementById("iceRandomize").style.display = type === "Glacier" ? "none" : "inline-block";
+ document.getElementById("iceSize").style.display = type === "Glacier" ? "none" : "inline-block";
+ if (type === "Iceberg") document.getElementById("iceSize").value = +elSelected.attr("size");
ice.selectAll("*").classed("draggable", true).call(d3.drag().on("drag", dragElement));
$("#iceEditor").dialog({
title: "Edit " + type,
resizable: false,
- position: { my: "center top+60", at: "top", of: d3.event, collision: "fit" },
+ position: {my: "center top+60", at: "top", of: d3.event, collision: "fit"},
close: closeEditor
});
if (modules.editIce) return;
modules.editIce = true;
+
// add listeners
document.getElementById("iceEditStyle").addEventListener("click", () => editStyle("ice"));
document.getElementById("iceRandomize").addEventListener("click", randomizeShape);
@@ -34,18 +28,29 @@ function editIce(element) {
document.getElementById("iceNew").addEventListener("click", toggleAdd);
document.getElementById("iceRemove").addEventListener("click", removeIce);
-
function randomizeShape() {
- const selectedId = +elSelected.attr("data-id");
- Ice.randomizeIcebergShape(selectedId);
- redrawIceberg(selectedId);
+ const c = grid.points[+elSelected.attr("cell")];
+ const s = +elSelected.attr("size");
+ const i = ra(grid.cells.i),
+ cn = grid.points[i];
+ const poly = getGridPolygon(i).map(p => [p[0] - cn[0], p[1] - cn[1]]);
+ const points = poly.map(p => [rn(c[0] + p[0] * s, 2), rn(c[1] + p[1] * s, 2)]);
+ elSelected.attr("points", points);
}
function changeSize() {
- const newSize = +this.value;
- const selectedId = +elSelected.attr("data-id");
- Ice.changeIcebergSize(selectedId, newSize);
- redrawIceberg(selectedId);
+ const c = grid.points[+elSelected.attr("cell")];
+ const s = +elSelected.attr("size");
+ const flat = elSelected
+ .attr("points")
+ .split(",")
+ .map(el => +el);
+ const pairs = [];
+ while (flat.length) pairs.push(flat.splice(0, 2));
+ const poly = pairs.map(p => [(p[0] - c[0]) / s, (p[1] - c[1]) / s]);
+ const size = +this.value;
+ const points = poly.map(p => [rn(c[0] + p[0] * size, 2), rn(c[1] + p[1] * size, 2)]);
+ elSelected.attr("points", points).attr("size", size);
}
function toggleAdd() {
@@ -62,15 +67,17 @@ function editIce(element) {
function addIcebergOnClick() {
const [x, y] = d3.mouse(this);
const i = findGridCell(x, y, grid);
+ const [cx, cy] = grid.points[i];
const size = +document.getElementById("iceSize")?.value || 1;
- Ice.addIceberg(i, size);
-
+ const points = getGridPolygon(i).map(([x, y]) => [rn(lerp(cx, x, size), 2), rn(lerp(cy, y, size), 2)]);
+ const iceberg = ice.append("polygon").attr("points", points).attr("cell", i).attr("size", size);
+ iceberg.call(d3.drag().on("drag", dragElement));
if (d3.event.shiftKey === false) toggleAdd();
}
function removeIce() {
- const type = elSelected.attr("type") === "glacier" ? "Glacier" : "Iceberg";
+ const type = elSelected.attr("type") ? "Glacier" : "Iceberg";
alertMessage.innerHTML = /* html */ `Are you sure you want to remove the ${type}?`;
$("#alert").dialog({
resizable: false,
@@ -78,7 +85,7 @@ function editIce(element) {
buttons: {
Remove: function () {
$(this).dialog("close");
- Ice.removeIce(+elSelected.attr("data-id"));
+ elSelected.remove();
$("#iceEditor").dialog("close");
},
Cancel: function () {
@@ -89,24 +96,14 @@ function editIce(element) {
}
function dragElement() {
- const selectedId = +elSelected.attr("data-id");
- const initialTransform = parseTransform(this.getAttribute("transform"));
- const dx = initialTransform[0] - d3.event.x;
- const dy = initialTransform[1] - d3.event.y;
+ const tr = parseTransform(this.getAttribute("transform"));
+ const dx = +tr[0] - d3.event.x,
+ dy = +tr[1] - d3.event.y;
d3.event.on("drag", function () {
- const x = d3.event.x;
- const y = d3.event.y;
- const transform = `translate(${dx + x},${dy + y})`;
- this.setAttribute("transform", transform);
-
- // Update data model with new position
- const offset = [dx + x, dy + y];
- const iceData = pack.ice.find(element => element.i === selectedId);
- if (iceData) {
- // Store offset for visual positioning, actual geometry stays in points
- iceData.offset = offset;
- }
+ const x = d3.event.x,
+ y = d3.event.y;
+ this.setAttribute("transform", `translate(${dx + x},${dy + y})`);
});
}
@@ -117,4 +114,3 @@ function editIce(element) {
unselect();
}
}
-
diff --git a/public/modules/ui/layers.js b/public/modules/ui/layers.js
index f2f04a4b..5037a5ee 100644
--- a/public/modules/ui/layers.js
+++ b/public/modules/ui/layers.js
@@ -417,6 +417,49 @@ function toggleIce(event) {
}
}
+function drawIce() {
+ TIME && console.time("drawIce");
+
+ const {cells, features} = grid;
+ const {temp, h} = cells;
+ Math.random = aleaPRNG(seed);
+
+ const ICEBERG_MAX_TEMP = 0;
+ const GLACIER_MAX_TEMP = -8;
+ const minMaxTemp = d3.min(temp);
+
+ // cold land: draw glaciers
+ {
+ const type = "iceShield";
+ const getType = cellId => (h[cellId] >= 20 && temp[cellId] <= GLACIER_MAX_TEMP ? type : null);
+ const isolines = getIsolines(grid, getType, {polygons: true});
+ isolines[type]?.polygons?.forEach(points => {
+ const clipped = clipPoly(points);
+ ice.append("polygon").attr("points", clipped).attr("type", type);
+ });
+ }
+
+ // cold water: draw icebergs
+ 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 icebers 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 size, 1 = full size
+ if (cells.t[cellId] === -1) baseSize /= 1.3; // coasline: smaller icebergs
+ const size = minmax(rn(baseSize * randomFactor, 2), 0.1, 1);
+
+ const [cx, cy] = grid.points[cellId];
+ const points = getGridPolygon(cellId).map(([x, y]) => [rn(lerp(cx, x, size), 2), rn(lerp(cy, y, size), 2)]);
+ ice.append("polygon").attr("points", points).attr("cell", cellId).attr("size", size);
+ }
+
+ TIME && console.timeEnd("drawIce");
+}
+
function toggleCultures(event) {
const cultures = pack.cultures.filter(c => c.i && !c.removed);
const empty = !cults.selectAll("path").size();
diff --git a/public/modules/ui/tools.js b/public/modules/ui/tools.js
index eade993f..a3df5c00 100644
--- a/public/modules/ui/tools.js
+++ b/public/modules/ui/tools.js
@@ -555,7 +555,7 @@ function regenerateMilitary() {
function regenerateIce() {
if (!layerIsOn("toggleIce")) toggleIce();
- Ice.generate();
+ ice.selectAll("*").remove();
drawIce();
}
diff --git a/public/versioning.js b/public/versioning.js
index 8069c818..11fcde66 100644
--- a/public/versioning.js
+++ b/public/versioning.js
@@ -13,7 +13,7 @@
* Example: 1.102.2 -> Major version 1, Minor version 102, Patch version 2
*/
-const VERSION = "1.111.0";
+const VERSION = "1.110.0";
if (parseMapVersion(VERSION) !== VERSION) alert("versioning.js: Invalid format or parsing function");
{
diff --git a/src/index.html b/src/index.html
index f75cbbfc..21d84187 100644
--- a/src/index.html
+++ b/src/index.html
@@ -8515,16 +8515,16 @@
-
+
-
+
-
-
+
+
@@ -8535,7 +8535,7 @@
-
+
@@ -8549,12 +8549,12 @@
-
-
-
+
+
+
-
+
@@ -8566,8 +8566,8 @@
-
-
+
+
@@ -8583,6 +8583,5 @@
-