mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2026-02-04 17:41:23 +01:00
Ice Layer Data Model (#1262)
* prototype for ice seperation * feat: migrate ice data to new data model and update version to 1.110.0 * refactor: update ice data handling and rendering for improved performance * feat: integrate ice generation and recalculation in heightmap editing * fix ice selection(hopefully) * fix ice selection better(pls) * refactor: remove redundant element selection in ice editing functions * fix: clear ice data before generating glaciers and icebergs * sparse array implementation with reduced updates * fix logic chech in modules/dynamic/auto-update.js Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * fix: migrate ice data to new data model structure * refactor: streamline ice generation process and clean up rendering functions * refactor: simplify ice rendering logic by removing redundant clearing of old SVG * fix: update editIce function to accept element parameter and improve logic for glacier handling * ice drawing with only type on less occuring glaciers * feat: add compactPackData function to filter out undefined glaciers and icebergs * fix: clear existing ice elements before redrawing in editHeightmap function * fix compact problems on autosave * refactor: unify ice data structure and streamline ice element handling * refactor: improve getNextId function to fill gaps in ice element IDs(optional commit) * just to be sure * bump version in html * fix index.html script import --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
parent
b223dc62da
commit
e597d905eb
13 changed files with 402 additions and 118 deletions
|
|
@ -632,6 +632,8 @@ async function generate(options) {
|
||||||
Biomes.define();
|
Biomes.define();
|
||||||
Features.defineGroups();
|
Features.defineGroups();
|
||||||
|
|
||||||
|
Ice.generate();
|
||||||
|
|
||||||
rankCells();
|
rankCells();
|
||||||
Cultures.generate();
|
Cultures.generate();
|
||||||
Cultures.expand();
|
Cultures.expand();
|
||||||
|
|
|
||||||
|
|
@ -1036,4 +1036,74 @@ export function resolveVersionConflicts(mapVersion) {
|
||||||
delete options.showMFCGMap;
|
delete options.showMFCGMap;
|
||||||
delete options.villageMaxPopulation;
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
170
public/modules/ice.js
Normal file
170
public/modules/ice.js
Normal file
|
|
@ -0,0 +1,170 @@
|
||||||
|
"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
|
||||||
|
};
|
||||||
|
})();
|
||||||
|
|
@ -406,6 +406,7 @@ async function parseLoadedData(data, mapVersion) {
|
||||||
pack.cells.province = data[27] ? Uint16Array.from(data[27].split(",")) : new Uint16Array(pack.cells.i.length);
|
pack.cells.province = data[27] ? Uint16Array.from(data[27].split(",")) : new Uint16Array(pack.cells.i.length);
|
||||||
// data[28] had deprecated cells.crossroad
|
// data[28] had deprecated cells.crossroad
|
||||||
pack.cells.routes = data[36] ? JSON.parse(data[36]) : {};
|
pack.cells.routes = data[36] ? JSON.parse(data[36]) : {};
|
||||||
|
pack.ice = data[39] ? JSON.parse(data[39]) : [];
|
||||||
|
|
||||||
if (data[31]) {
|
if (data[31]) {
|
||||||
const namesDL = data[31].split("/");
|
const namesDL = data[31].split("/");
|
||||||
|
|
@ -449,7 +450,7 @@ async function parseLoadedData(data, mapVersion) {
|
||||||
if (isVisible(routes) && hasChild(routes, "path")) turnOn("toggleRoutes");
|
if (isVisible(routes) && hasChild(routes, "path")) turnOn("toggleRoutes");
|
||||||
if (hasChildren(temperature)) turnOn("toggleTemperature");
|
if (hasChildren(temperature)) turnOn("toggleTemperature");
|
||||||
if (hasChild(population, "line")) turnOn("togglePopulation");
|
if (hasChild(population, "line")) turnOn("togglePopulation");
|
||||||
if (hasChildren(ice)) turnOn("toggleIce");
|
if (isVisible(ice)) turnOn("toggleIce");
|
||||||
if (hasChild(prec, "circle")) turnOn("togglePrecipitation");
|
if (hasChild(prec, "circle")) turnOn("togglePrecipitation");
|
||||||
if (isVisible(emblems) && hasChild(emblems, "use")) turnOn("toggleEmblems");
|
if (isVisible(emblems) && hasChild(emblems, "use")) turnOn("toggleEmblems");
|
||||||
if (isVisible(labels)) turnOn("toggleLabels");
|
if (isVisible(labels)) turnOn("toggleLabels");
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ async function saveMap(method) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function prepareMapData() {
|
function prepareMapData() {
|
||||||
|
|
||||||
const date = new Date();
|
const date = new Date();
|
||||||
const dateString = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
|
const dateString = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
|
||||||
const license = "File can be loaded in azgaar.github.io/Fantasy-Map-Generator";
|
const license = "File can be loaded in azgaar.github.io/Fantasy-Map-Generator";
|
||||||
|
|
@ -102,6 +103,7 @@ function prepareMapData() {
|
||||||
const cellRoutes = JSON.stringify(pack.cells.routes);
|
const cellRoutes = JSON.stringify(pack.cells.routes);
|
||||||
const routes = JSON.stringify(pack.routes);
|
const routes = JSON.stringify(pack.routes);
|
||||||
const zones = JSON.stringify(pack.zones);
|
const zones = JSON.stringify(pack.zones);
|
||||||
|
const ice = JSON.stringify(pack.ice);
|
||||||
|
|
||||||
// store name array only if not the same as default
|
// store name array only if not the same as default
|
||||||
const defaultNB = Names.getNameBases();
|
const defaultNB = Names.getNameBases();
|
||||||
|
|
@ -155,7 +157,8 @@ function prepareMapData() {
|
||||||
markers,
|
markers,
|
||||||
cellRoutes,
|
cellRoutes,
|
||||||
routes,
|
routes,
|
||||||
zones
|
zones,
|
||||||
|
ice
|
||||||
].join("\r\n");
|
].join("\r\n");
|
||||||
return mapData;
|
return mapData;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
70
public/modules/renderers/draw-ice.js
Normal file
70
public/modules/renderers/draw-ice.js
Normal file
|
|
@ -0,0 +1,70 @@
|
||||||
|
"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 `<polygon points="${glacier.points}" type="glacier" data-id="${glacier.i}" ${glacier.offset ? `transform="translate(${glacier.offset[0]},${glacier.offset[1]})"` : ""}/>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getIcebergHtml(iceberg) {
|
||||||
|
return `<polygon points="${iceberg.points}" data-id="${iceberg.i}" ${iceberg.offset ? `transform="translate(${iceberg.offset[0]},${iceberg.offset[1]})"` : ""}/>`;
|
||||||
|
}
|
||||||
|
|
@ -26,7 +26,7 @@ function clicked() {
|
||||||
else if (ancestor.id === "labels" && el.tagName === "tspan") editLabel();
|
else if (ancestor.id === "labels" && el.tagName === "tspan") editLabel();
|
||||||
else if (grand.id === "burgLabels") editBurg();
|
else if (grand.id === "burgLabels") editBurg();
|
||||||
else if (grand.id === "burgIcons") editBurg();
|
else if (grand.id === "burgIcons") editBurg();
|
||||||
else if (parent.id === "ice") editIce();
|
else if (parent.id === "ice") editIce(el);
|
||||||
else if (parent.id === "terrain") editReliefIcon();
|
else if (parent.id === "terrain") editReliefIcon();
|
||||||
else if (grand.id === "markers" || great.id === "markers") editMarker();
|
else if (grand.id === "markers" || great.id === "markers") editMarker();
|
||||||
else if (grand.id === "coastline") editCoastline();
|
else if (grand.id === "coastline") editCoastline();
|
||||||
|
|
|
||||||
|
|
@ -259,6 +259,8 @@ function editHeightmap(options) {
|
||||||
Rivers.specify();
|
Rivers.specify();
|
||||||
Lakes.defineNames();
|
Lakes.defineNames();
|
||||||
|
|
||||||
|
Ice.generate();
|
||||||
|
|
||||||
Military.generate();
|
Military.generate();
|
||||||
Markers.generate();
|
Markers.generate();
|
||||||
Zones.generate();
|
Zones.generate();
|
||||||
|
|
@ -465,6 +467,10 @@ function editHeightmap(options) {
|
||||||
.attr("id", d => base + d);
|
.attr("id", d => base + d);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// recalculate ice
|
||||||
|
Ice.generate();
|
||||||
|
ice.selectAll("*").remove();
|
||||||
|
|
||||||
TIME && console.timeEnd("restoreRiskedData");
|
TIME && console.timeEnd("restoreRiskedData");
|
||||||
INFO && console.groupEnd("Edit Heightmap");
|
INFO && console.groupEnd("Edit Heightmap");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,21 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
function editIce() {
|
function editIce(element) {
|
||||||
if (customization) return;
|
if (customization) return;
|
||||||
|
if (elSelected && element === elSelected.node()) return;
|
||||||
|
|
||||||
closeDialogs(".stable");
|
closeDialogs(".stable");
|
||||||
if (!layerIsOn("toggleIce")) toggleIce();
|
if (!layerIsOn("toggleIce")) toggleIce();
|
||||||
|
|
||||||
elSelected = d3.select(d3.event.target);
|
elSelected = d3.select(d3.event.target);
|
||||||
const type = elSelected.attr("type") ? "Glacier" : "Iceberg";
|
const id = +elSelected.attr("data-id");
|
||||||
document.getElementById("iceRandomize").style.display = type === "Glacier" ? "none" : "inline-block";
|
const iceElement = pack.ice.find(el => el.i === id);
|
||||||
document.getElementById("iceSize").style.display = type === "Glacier" ? "none" : "inline-block";
|
const isGlacier = elSelected.attr("type") === "glacier";
|
||||||
if (type === "Iceberg") document.getElementById("iceSize").value = +elSelected.attr("size");
|
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 || "";
|
||||||
|
|
||||||
ice.selectAll("*").classed("draggable", true).call(d3.drag().on("drag", dragElement));
|
ice.selectAll("*").classed("draggable", true).call(d3.drag().on("drag", dragElement));
|
||||||
|
|
||||||
$("#iceEditor").dialog({
|
$("#iceEditor").dialog({
|
||||||
|
|
@ -20,7 +27,6 @@ function editIce() {
|
||||||
|
|
||||||
if (modules.editIce) return;
|
if (modules.editIce) return;
|
||||||
modules.editIce = true;
|
modules.editIce = true;
|
||||||
|
|
||||||
// add listeners
|
// add listeners
|
||||||
document.getElementById("iceEditStyle").addEventListener("click", () => editStyle("ice"));
|
document.getElementById("iceEditStyle").addEventListener("click", () => editStyle("ice"));
|
||||||
document.getElementById("iceRandomize").addEventListener("click", randomizeShape);
|
document.getElementById("iceRandomize").addEventListener("click", randomizeShape);
|
||||||
|
|
@ -28,29 +34,18 @@ function editIce() {
|
||||||
document.getElementById("iceNew").addEventListener("click", toggleAdd);
|
document.getElementById("iceNew").addEventListener("click", toggleAdd);
|
||||||
document.getElementById("iceRemove").addEventListener("click", removeIce);
|
document.getElementById("iceRemove").addEventListener("click", removeIce);
|
||||||
|
|
||||||
|
|
||||||
function randomizeShape() {
|
function randomizeShape() {
|
||||||
const c = grid.points[+elSelected.attr("cell")];
|
const selectedId = +elSelected.attr("data-id");
|
||||||
const s = +elSelected.attr("size");
|
Ice.randomizeIcebergShape(selectedId);
|
||||||
const i = ra(grid.cells.i),
|
redrawIceberg(selectedId);
|
||||||
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() {
|
function changeSize() {
|
||||||
const c = grid.points[+elSelected.attr("cell")];
|
const newSize = +this.value;
|
||||||
const s = +elSelected.attr("size");
|
const selectedId = +elSelected.attr("data-id");
|
||||||
const flat = elSelected
|
Ice.changeIcebergSize(selectedId, newSize);
|
||||||
.attr("points")
|
redrawIceberg(selectedId);
|
||||||
.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() {
|
function toggleAdd() {
|
||||||
|
|
@ -67,17 +62,15 @@ function editIce() {
|
||||||
function addIcebergOnClick() {
|
function addIcebergOnClick() {
|
||||||
const [x, y] = d3.mouse(this);
|
const [x, y] = d3.mouse(this);
|
||||||
const i = findGridCell(x, y, grid);
|
const i = findGridCell(x, y, grid);
|
||||||
const [cx, cy] = grid.points[i];
|
|
||||||
const size = +document.getElementById("iceSize")?.value || 1;
|
const size = +document.getElementById("iceSize")?.value || 1;
|
||||||
|
|
||||||
const points = getGridPolygon(i).map(([x, y]) => [rn(lerp(cx, x, size), 2), rn(lerp(cy, y, size), 2)]);
|
Ice.addIceberg(i, size);
|
||||||
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();
|
if (d3.event.shiftKey === false) toggleAdd();
|
||||||
}
|
}
|
||||||
|
|
||||||
function removeIce() {
|
function removeIce() {
|
||||||
const type = elSelected.attr("type") ? "Glacier" : "Iceberg";
|
const type = elSelected.attr("type") === "glacier" ? "Glacier" : "Iceberg";
|
||||||
alertMessage.innerHTML = /* html */ `Are you sure you want to remove the ${type}?`;
|
alertMessage.innerHTML = /* html */ `Are you sure you want to remove the ${type}?`;
|
||||||
$("#alert").dialog({
|
$("#alert").dialog({
|
||||||
resizable: false,
|
resizable: false,
|
||||||
|
|
@ -85,7 +78,7 @@ function editIce() {
|
||||||
buttons: {
|
buttons: {
|
||||||
Remove: function () {
|
Remove: function () {
|
||||||
$(this).dialog("close");
|
$(this).dialog("close");
|
||||||
elSelected.remove();
|
Ice.removeIce(+elSelected.attr("data-id"));
|
||||||
$("#iceEditor").dialog("close");
|
$("#iceEditor").dialog("close");
|
||||||
},
|
},
|
||||||
Cancel: function () {
|
Cancel: function () {
|
||||||
|
|
@ -96,14 +89,24 @@ function editIce() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function dragElement() {
|
function dragElement() {
|
||||||
const tr = parseTransform(this.getAttribute("transform"));
|
const selectedId = +elSelected.attr("data-id");
|
||||||
const dx = +tr[0] - d3.event.x,
|
const initialTransform = parseTransform(this.getAttribute("transform"));
|
||||||
dy = +tr[1] - d3.event.y;
|
const dx = initialTransform[0] - d3.event.x;
|
||||||
|
const dy = initialTransform[1] - d3.event.y;
|
||||||
|
|
||||||
d3.event.on("drag", function () {
|
d3.event.on("drag", function () {
|
||||||
const x = d3.event.x,
|
const x = d3.event.x;
|
||||||
y = d3.event.y;
|
const y = d3.event.y;
|
||||||
this.setAttribute("transform", `translate(${dx + x},${dy + 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;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,3 +117,4 @@ function editIce() {
|
||||||
unselect();
|
unselect();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -417,49 +417,6 @@ 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) {
|
function toggleCultures(event) {
|
||||||
const cultures = pack.cultures.filter(c => c.i && !c.removed);
|
const cultures = pack.cultures.filter(c => c.i && !c.removed);
|
||||||
const empty = !cults.selectAll("path").size();
|
const empty = !cults.selectAll("path").size();
|
||||||
|
|
|
||||||
|
|
@ -555,7 +555,7 @@ function regenerateMilitary() {
|
||||||
|
|
||||||
function regenerateIce() {
|
function regenerateIce() {
|
||||||
if (!layerIsOn("toggleIce")) toggleIce();
|
if (!layerIsOn("toggleIce")) toggleIce();
|
||||||
ice.selectAll("*").remove();
|
Ice.generate();
|
||||||
drawIce();
|
drawIce();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@
|
||||||
* Example: 1.102.2 -> Major version 1, Minor version 102, Patch version 2
|
* Example: 1.102.2 -> Major version 1, Minor version 102, Patch version 2
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const VERSION = "1.110.0";
|
const VERSION = "1.111.0";
|
||||||
if (parseMapVersion(VERSION) !== VERSION) alert("versioning.js: Invalid format or parsing function");
|
if (parseMapVersion(VERSION) !== VERSION) alert("versioning.js: Invalid format or parsing function");
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -8515,16 +8515,16 @@
|
||||||
<script defer src="libs/lineclip.min.js?v1.105.0"></script>
|
<script defer src="libs/lineclip.min.js?v1.105.0"></script>
|
||||||
<script defer src="libs/simplify.js?v1.105.6"></script>
|
<script defer src="libs/simplify.js?v1.105.6"></script>
|
||||||
<script defer src="modules/fonts.js?v=1.99.03"></script>
|
<script defer src="modules/fonts.js?v=1.99.03"></script>
|
||||||
<script defer src="modules/ui/layers.js?v=1.108.4"></script>
|
<script defer src="modules/ui/layers.js?v=1.111.0"></script>
|
||||||
<script defer src="modules/ui/measurers.js?v=1.99.00"></script>
|
<script defer src="modules/ui/measurers.js?v=1.99.00"></script>
|
||||||
<script defer src="modules/ui/style-presets.js?v=1.100.00"></script>
|
<script defer src="modules/ui/style-presets.js?v=1.100.00"></script>
|
||||||
<script defer src="modules/ui/general.js?v=1.100.00"></script>
|
<script defer src="modules/ui/general.js?v=1.100.00"></script>
|
||||||
<script defer src="modules/ui/options.js?v=1.106.2"></script>
|
<script defer src="modules/ui/options.js?v=1.106.2"></script>
|
||||||
<script defer src="main.js?v=1.108.1"></script>
|
<script defer src="main.js?v=1.111.0"></script>
|
||||||
|
|
||||||
<script defer src="modules/ui/style.js?v=1.108.4"></script>
|
<script defer src="modules/ui/style.js?v=1.108.4"></script>
|
||||||
<script defer src="modules/ui/editors.js?v=1.108.5"></script>
|
<script defer src="modules/ui/editors.js?v=1.111.0"></script>
|
||||||
<script defer src="modules/ui/tools.js?v=1.108.5"></script>
|
<script defer src="modules/ui/tools.js?v=1.111.0"></script>
|
||||||
<script defer src="modules/ui/world-configurator.js?v=1.105.4"></script>
|
<script defer src="modules/ui/world-configurator.js?v=1.105.4"></script>
|
||||||
<script defer src="modules/ui/heightmap-editor.js?v=1.105.2"></script>
|
<script defer src="modules/ui/heightmap-editor.js?v=1.105.2"></script>
|
||||||
<script defer src="modules/ui/provinces-editor.js?v=1.108.1"></script>
|
<script defer src="modules/ui/provinces-editor.js?v=1.108.1"></script>
|
||||||
|
|
@ -8535,7 +8535,7 @@
|
||||||
<script defer src="modules/ui/routes-editor.js?v=1.104.3"></script>
|
<script defer src="modules/ui/routes-editor.js?v=1.104.3"></script>
|
||||||
<script defer src="modules/ui/routes-creator.js?v=1.104.3"></script>
|
<script defer src="modules/ui/routes-creator.js?v=1.104.3"></script>
|
||||||
<script defer src="modules/ui/route-group-editor.js?v=1.103.8"></script>
|
<script defer src="modules/ui/route-group-editor.js?v=1.103.8"></script>
|
||||||
<script defer src="modules/ui/ice-editor.js?v=1.99.00"></script>
|
<script defer src="modules/ui/ice-editor.js?v=1.111.0"></script>
|
||||||
<script defer src="modules/ui/lakes-editor.js?v=1.106.0"></script>
|
<script defer src="modules/ui/lakes-editor.js?v=1.106.0"></script>
|
||||||
<script defer src="modules/ui/coastline-editor.js?v=1.99.00"></script>
|
<script defer src="modules/ui/coastline-editor.js?v=1.99.00"></script>
|
||||||
<script defer src="modules/ui/labels-editor.js?v=1.106.0"></script>
|
<script defer src="modules/ui/labels-editor.js?v=1.106.0"></script>
|
||||||
|
|
@ -8549,12 +8549,12 @@
|
||||||
<script defer src="modules/ui/ai-generator.js?v=1.108.8"></script>
|
<script defer src="modules/ui/ai-generator.js?v=1.108.8"></script>
|
||||||
<script defer src="modules/ui/diplomacy-editor.js?v=1.99.00"></script>
|
<script defer src="modules/ui/diplomacy-editor.js?v=1.99.00"></script>
|
||||||
<script defer src="modules/ui/zones-editor.js?v=1.105.20"></script>
|
<script defer src="modules/ui/zones-editor.js?v=1.105.20"></script>
|
||||||
<script defer src="modules/ui/burgs-overview.js?v=1.110.0"></script>
|
<script defer src="modules/ui/burgs-overview.js?v=1.111.0"></script>
|
||||||
<script defer src="modules/ui/routes-overview.js?v=1.110.0"></script>
|
<script defer src="modules/ui/routes-overview.js?v=1.111.0"></script>
|
||||||
<script defer src="modules/ui/rivers-overview.js?v=1.110.0"></script>
|
<script defer src="modules/ui/rivers-overview.js?v=1.111.0"></script>
|
||||||
<script defer src="modules/ui/military-overview.js?v=1.108.5"></script>
|
<script defer src="modules/ui/military-overview.js?v=1.108.5"></script>
|
||||||
<script defer src="modules/ui/regiments-overview.js?v=1.108.5"></script>
|
<script defer src="modules/ui/regiments-overview.js?v=1.108.5"></script>
|
||||||
<script defer src="modules/ui/markers-overview.js?v=1.110.0"></script>
|
<script defer src="modules/ui/markers-overview.js?v=1.111.0"></script>
|
||||||
<script defer src="modules/ui/regiment-editor.js?v=1.108.5"></script>
|
<script defer src="modules/ui/regiment-editor.js?v=1.108.5"></script>
|
||||||
<script defer src="modules/ui/battle-screen.js?v=1.108.5"></script>
|
<script defer src="modules/ui/battle-screen.js?v=1.108.5"></script>
|
||||||
<script defer src="modules/ui/emblems-editor.js?v=1.99.00"></script>
|
<script defer src="modules/ui/emblems-editor.js?v=1.99.00"></script>
|
||||||
|
|
@ -8566,8 +8566,8 @@
|
||||||
<script defer src="modules/coa-renderer.js?v=1.99.00"></script>
|
<script defer src="modules/coa-renderer.js?v=1.99.00"></script>
|
||||||
<script defer src="libs/rgbquant.min.js"></script>
|
<script defer src="libs/rgbquant.min.js"></script>
|
||||||
<script defer src="libs/jquery.ui.touch-punch.min.js"></script>
|
<script defer src="libs/jquery.ui.touch-punch.min.js"></script>
|
||||||
<script defer src="modules/io/save.js?v=1.107.4"></script>
|
<script defer src="modules/io/save.js?v=1.111.0"></script>
|
||||||
<script defer src="modules/io/load.js?v=1.109.4"></script>
|
<script defer src="modules/io/load.js?v=1.111.0"></script>
|
||||||
<script defer src="modules/io/cloud.js?v=1.106.0"></script>
|
<script defer src="modules/io/cloud.js?v=1.106.0"></script>
|
||||||
<script defer src="modules/io/export.js?v=1.108.13"></script>
|
<script defer src="modules/io/export.js?v=1.108.13"></script>
|
||||||
|
|
||||||
|
|
@ -8583,5 +8583,6 @@
|
||||||
<script defer src="modules/renderers/draw-burg-labels.js?v=1.109.4"></script>
|
<script defer src="modules/renderers/draw-burg-labels.js?v=1.109.4"></script>
|
||||||
<script defer src="modules/renderers/draw-burg-icons.js?v=1.109.4"></script>
|
<script defer src="modules/renderers/draw-burg-icons.js?v=1.109.4"></script>
|
||||||
<script defer src="modules/renderers/draw-relief-icons.js?v=1.108.4"></script>
|
<script defer src="modules/renderers/draw-relief-icons.js?v=1.108.4"></script>
|
||||||
|
<script defer src="modules/renderers/draw-ice.js?v=1.111.0"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue