Fantasy-Map-Generator/modules/dynamic/auto-update.js
Azgaar 374c21b3d7
Ocean heightmap and Scale bar styling change [v1.96] (#1045)
* Scale bar styling (#1025)

* feat: style scale bar

* feat: style scale bar - style presets

---------

Co-authored-by: Azgaar <azgaar.fmg@yandex.com>

* Ocean heightmap to v1.96 (#1044)

* feat: allow to render ocean heightmap

* feat: allow to render ocean heightmap - test

* feat: allow to render ocean heightmap - fix issue

* feat: allow to render ocean heightmap - cleanup

---------

Co-authored-by: Azgaar <azgaar.fmg@yandex.com>

* fix: scale bar size

* fix: remove mask on terrs lavel

* fix: regenerate heigtmap preview to use current graph size

* Add the name of culture and namesbase in the name editor dialog (#1033)

* Add the name of culture and namesbase in the name editor dialog

Added the name of the culture and  namesbase in the dialog "name editor".
This tells information on the "click to generate a culture-specific name"
It tells you the culture before changing name.

* cultureName into cultureId + cultureName

And deleted the incomplete code of showing culture name on datatip

* refactor: leave culture name only

---------

Co-authored-by: Azgaar <azgaar.fmg@yandex.com>

* Added Burgs column to province editor (#1031)

* Added Burgs column to province editor

Added to province editor:
+ Burgs column
+ the number of Burgs, p.burgs.length
+ "icon-dot-circled" to go to overviewBurgs.
+ overviewBurgs Filtered by state id.
+ Fixed some typos.

* fixed code as Azgaar suggested

+ Corrected provincesHeader distance in em.
+ const stateId = pack.provinces[p].state;
- Deleted cell count.

* deleted HTML code for provincesFooter cells

- Deleted Total land cells number HTML from provincesFooter.

* deleting totalCells in the code, maybe i will add provinceCells in the future.

Deleted lines for const totalCells and for (+cells / totalCells) * 100 + "%";

* refactor: cleanup

* refactor: cleanup

---------

Co-authored-by: Azgaar <azgaar.fmg@yandex.com>

* fix: burgs overview - add MFCG link back

* feat: add more details to burgs export

* feat: don't show auto-update dialog

* feat: pump version

* fix: #1041

* feat: update style presets

---------

Co-authored-by: Azgaar <azgaar.fmg@yandex.com>
Co-authored-by: Ángel Montero Lamas <angel.montero1@gmail.com>
2024-02-24 19:12:48 +04:00

830 lines
27 KiB
JavaScript

"use strict";
// update old map file to the current version
export function resolveVersionConflicts(version) {
if (version < 1) {
// v1.0 added a new religions layer
relig = viewbox.insert("g", "#terrain").attr("id", "relig");
Religions.generate();
// v1.0 added a legend box
legend = svg.append("g").attr("id", "legend");
legend
.attr("font-family", "Almendra SC")
.attr("font-size", 13)
.attr("data-size", 13)
.attr("data-x", 99)
.attr("data-y", 93)
.attr("stroke-width", 2.5)
.attr("stroke", "#812929")
.attr("stroke-dasharray", "0 4 10 4")
.attr("stroke-linecap", "round");
// v1.0 separated drawBorders fron drawStates()
stateBorders = borders.append("g").attr("id", "stateBorders");
provinceBorders = borders.append("g").attr("id", "provinceBorders");
borders
.attr("opacity", null)
.attr("stroke", null)
.attr("stroke-width", null)
.attr("stroke-dasharray", null)
.attr("stroke-linecap", null)
.attr("filter", null);
stateBorders
.attr("opacity", 0.8)
.attr("stroke", "#56566d")
.attr("stroke-width", 1)
.attr("stroke-dasharray", "2")
.attr("stroke-linecap", "butt");
provinceBorders
.attr("opacity", 0.8)
.attr("stroke", "#56566d")
.attr("stroke-width", 0.5)
.attr("stroke-dasharray", "1")
.attr("stroke-linecap", "butt");
// v1.0 added state relations, provinces, forms and full names
provs = viewbox.insert("g", "#borders").attr("id", "provs").attr("opacity", 0.6);
BurgsAndStates.collectStatistics();
BurgsAndStates.generateCampaigns();
BurgsAndStates.generateDiplomacy();
BurgsAndStates.defineStateForms();
drawStates();
BurgsAndStates.generateProvinces();
drawBorders();
if (!layerIsOn("toggleBorders")) $("#borders").fadeOut();
if (!layerIsOn("toggleStates")) regions.attr("display", "none").selectAll("path").remove();
// v1.0 added zones layer
zones = viewbox.insert("g", "#borders").attr("id", "zones").attr("display", "none");
zones
.attr("opacity", 0.6)
.attr("stroke", null)
.attr("stroke-width", 0)
.attr("stroke-dasharray", null)
.attr("stroke-linecap", "butt");
addZones();
if (!markers.selectAll("*").size()) {
Markers.generate();
turnButtonOn("toggleMarkers");
}
// v1.0 add fogging layer (state focus)
fogging = viewbox
.insert("g", "#ruler")
.attr("id", "fogging-cont")
.attr("mask", "url(#fog)")
.append("g")
.attr("id", "fogging")
.style("display", "none");
fogging.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%");
defs
.append("mask")
.attr("id", "fog")
.append("rect")
.attr("x", 0)
.attr("y", 0)
.attr("width", "100%")
.attr("height", "100%")
.attr("fill", "white");
// v1.0 changes states opacity bask to regions level
if (statesBody.attr("opacity")) {
regions.attr("opacity", statesBody.attr("opacity"));
statesBody.attr("opacity", null);
}
// v1.0 changed labels to multi-lined
labels.selectAll("textPath").each(function () {
const text = this.textContent;
const shift = this.getComputedTextLength() / -1.5;
this.innerHTML = /* html */ `<tspan x="${shift}">${text}</tspan>`;
});
// v1.0 added new biome - Wetland
biomesData.name.push("Wetland");
biomesData.color.push("#0b9131");
biomesData.habitability.push(12);
}
if (version < 1.1) {
// v1.0 initial code had a bug with religion layer id
if (!relig.size()) relig = viewbox.insert("g", "#terrain").attr("id", "relig");
// v1.0 initially has Sympathy status then relaced with Friendly
for (const s of pack.states) {
if (!s.diplomacy) continue;
s.diplomacy = s.diplomacy.map(r => (r === "Sympathy" ? "Friendly" : r));
}
// labels should be toggled via style attribute, so remove display attribute
labels.attr("display", null);
// v1.0 added religions heirarchy tree
if (pack.religions[1] && !pack.religions[1].code) {
pack.religions
.filter(r => r.i)
.forEach(r => {
r.origin = 0;
r.code = r.name.slice(0, 2);
});
}
if (!document.getElementById("freshwater")) {
lakes.append("g").attr("id", "freshwater");
lakes
.select("#freshwater")
.attr("opacity", 0.5)
.attr("fill", "#a6c1fd")
.attr("stroke", "#5f799d")
.attr("stroke-width", 0.7)
.attr("filter", null);
}
if (!document.getElementById("salt")) {
lakes.append("g").attr("id", "salt");
lakes
.select("#salt")
.attr("opacity", 0.5)
.attr("fill", "#409b8a")
.attr("stroke", "#388985")
.attr("stroke-width", 0.7)
.attr("filter", null);
}
// v1.1 added new lake and coast groups
if (!document.getElementById("sinkhole")) {
lakes.append("g").attr("id", "sinkhole");
lakes.append("g").attr("id", "frozen");
lakes.append("g").attr("id", "lava");
lakes
.select("#sinkhole")
.attr("opacity", 1)
.attr("fill", "#5bc9fd")
.attr("stroke", "#53a3b0")
.attr("stroke-width", 0.7)
.attr("filter", null);
lakes
.select("#frozen")
.attr("opacity", 0.95)
.attr("fill", "#cdd4e7")
.attr("stroke", "#cfe0eb")
.attr("stroke-width", 0)
.attr("filter", null);
lakes
.select("#lava")
.attr("opacity", 0.7)
.attr("fill", "#90270d")
.attr("stroke", "#f93e0c")
.attr("stroke-width", 2)
.attr("filter", "url(#crumpled)");
coastline.append("g").attr("id", "sea_island");
coastline.append("g").attr("id", "lake_island");
coastline
.select("#sea_island")
.attr("opacity", 0.5)
.attr("stroke", "#1f3846")
.attr("stroke-width", 0.7)
.attr("filter", "url(#dropShadow)");
coastline
.select("#lake_island")
.attr("opacity", 1)
.attr("stroke", "#7c8eaf")
.attr("stroke-width", 0.35)
.attr("filter", null);
}
// v1.1 features stores more data
defs.select("#land").selectAll("path").remove();
defs.select("#water").selectAll("path").remove();
coastline.selectAll("path").remove();
lakes.selectAll("path").remove();
drawCoastline();
}
if (version < 1.11) {
// v1.11 added new attributes
terrs.attr("scheme", "bright").attr("terracing", 0).attr("skip", 5).attr("relax", 0).attr("curve", 0);
svg.select("#oceanic > *").attr("id", "oceanicPattern");
oceanLayers.attr("layers", "-6,-3,-1");
gridOverlay.attr("type", "pointyHex").attr("size", 10);
// v1.11 added cultures heirarchy tree
if (pack.cultures[1] && !pack.cultures[1].code) {
pack.cultures
.filter(c => c.i)
.forEach(c => {
c.origin = 0;
c.code = c.name.slice(0, 2);
});
}
// v1.11 had an issue with fogging being displayed on load
unfog();
// v1.2 added new terrain attributes
if (!terrain.attr("set")) terrain.attr("set", "simple");
if (!terrain.attr("size")) terrain.attr("size", 1);
if (!terrain.attr("density")) terrain.attr("density", 0.4);
}
if (version < 1.21) {
// v1.11 replaced "display" attribute by "display" style
viewbox.selectAll("g").each(function () {
if (this.hasAttribute("display")) {
this.removeAttribute("display");
this.style.display = "none";
}
});
// v1.21 added rivers data to pack
pack.rivers = []; // rivers data
rivers.selectAll("path").each(function () {
const i = +this.id.slice(5);
const length = this.getTotalLength() / 2;
const s = this.getPointAtLength(length),
e = this.getPointAtLength(0);
const source = findCell(s.x, s.y),
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});
});
}
if (version < 1.22) {
// v1.22 changed state neighbors from Set object to array
BurgsAndStates.collectStatistics();
}
if (version < 1.3) {
// v1.3 added global options object
const winds = options.slice(); // previostly wind was saved in settings[19]
const year = rand(100, 2000);
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};
// v1.3 added campaings data for all states
BurgsAndStates.generateCampaigns();
// v1.3 added militry layer
armies = viewbox.insert("g", "#icons").attr("id", "armies");
armies
.attr("opacity", 1)
.attr("fill-opacity", 1)
.attr("font-size", 6)
.attr("box-size", 3)
.attr("stroke", "#000")
.attr("stroke-width", 0.3);
turnButtonOn("toggleMilitary");
Military.generate();
}
if (version < 1.4) {
// v1.35 added dry lakes
if (!lakes.select("#dry").size()) {
lakes.append("g").attr("id", "dry");
lakes
.select("#dry")
.attr("opacity", 1)
.attr("fill", "#c9bfa7")
.attr("stroke", "#8e816f")
.attr("stroke-width", 0.7)
.attr("filter", null);
}
// v1.4 added ice layer
ice = viewbox.insert("g", "#coastline").attr("id", "ice").style("display", "none");
ice
.attr("opacity", null)
.attr("fill", "#e8f0f6")
.attr("stroke", "#e8f0f6")
.attr("stroke-width", 1)
.attr("filter", "url(#dropShadow05)");
drawIce();
// v1.4 added icon and power attributes for units
for (const unit of options.military) {
if (!unit.icon) unit.icon = getUnitIcon(unit.type);
if (!unit.power) unit.power = unit.crew;
}
function getUnitIcon(type) {
if (type === "naval") return "🌊";
if (type === "ranged") return "🏹";
if (type === "mounted") return "🐴";
if (type === "machinery") return "💣";
if (type === "armored") return "🐢";
if (type === "aviation") return "🦅";
if (type === "magical") return "🔮";
else return "⚔️";
}
// v1.4 added state reference for regiments
pack.states.filter(s => s.military).forEach(s => s.military.forEach(r => (r.state = s.i)));
}
if (version < 1.5) {
// not need to store default styles from v 1.5
localStorage.removeItem("styleClean");
localStorage.removeItem("styleGloom");
localStorage.removeItem("styleAncient");
localStorage.removeItem("styleMonochrome");
// v1.5 cultures has shield attribute
pack.cultures.forEach(culture => {
if (culture.removed) return;
culture.shield = Cultures.getRandomShield();
});
// v1.5 added burg type value
pack.burgs.forEach(burg => {
if (!burg.i || burg.removed) return;
burg.type = BurgsAndStates.getType(burg.cell, burg.port);
});
// v1.5 added emblems
defs.append("g").attr("id", "defs-emblems");
emblems = viewbox.insert("g", "#population").attr("id", "emblems").style("display", "none");
emblems.append("g").attr("id", "burgEmblems");
emblems.append("g").attr("id", "provinceEmblems");
emblems.append("g").attr("id", "stateEmblems");
regenerateEmblems();
toggleEmblems();
// v1.5 changed releif icons data
terrain.selectAll("use").each(function () {
const type = this.getAttribute("data-type") || this.getAttribute("xlink:href");
this.removeAttribute("xlink:href");
this.removeAttribute("data-type");
this.removeAttribute("data-size");
this.setAttribute("href", type);
});
}
if (version < 1.6) {
// v1.6 changed rivers data
for (const river of pack.rivers) {
const el = document.getElementById("river" + river.i);
if (el) {
river.widthFactor = +el.getAttribute("data-width");
el.removeAttribute("data-width");
el.removeAttribute("data-increment");
river.discharge = pack.cells.fl[river.mouth] || 1;
river.width = rn(river.length / 100, 2);
river.sourceWidth = 0.1;
} else {
Rivers.remove(river.i);
}
}
// v1.6 changed lakes data
for (const f of pack.features) {
if (f.type !== "lake") continue;
if (f.evaporation) continue;
f.flux = f.flux || f.cells * 3;
f.temp = grid.cells.temp[pack.cells.g[f.firstCell]];
f.height = f.height || d3.min(pack.cells.c[f.firstCell].map(c => pack.cells.h[c]).filter(h => h >= 20));
const height = (f.height - 18) ** heightExponentInput.value;
const evaporation = ((700 * (f.temp + 0.006 * height)) / 50 + 75) / (80 - f.temp);
f.evaporation = rn(evaporation * f.cells);
f.name = f.name || Lakes.getName(f);
delete f.river;
}
}
if (version < 1.61) {
// v1.61 changed rulers data
ruler.style("display", null);
rulers = new Rulers();
ruler.selectAll(".ruler > .white").each(function () {
const x1 = +this.getAttribute("x1");
const y1 = +this.getAttribute("y1");
const x2 = +this.getAttribute("x2");
const y2 = +this.getAttribute("y2");
if (isNaN(x1) || isNaN(y1) || isNaN(x2) || isNaN(y2)) return;
const points = [
[x1, y1],
[x2, y2]
];
rulers.create(Ruler, points);
});
ruler.selectAll("g.opisometer").each(function () {
const pointsString = this.dataset.points;
if (!pointsString) return;
const points = JSON.parse(pointsString);
rulers.create(Opisometer, points);
});
ruler.selectAll("path.planimeter").each(function () {
const length = this.getTotalLength();
if (length < 30) return;
const step = length > 1000 ? 40 : length > 400 ? 20 : 10;
const increment = length / Math.ceil(length / step);
const points = [];
for (let i = 0; i <= length; i += increment) {
const point = this.getPointAtLength(i);
points.push([point.x | 0, point.y | 0]);
}
rulers.create(Planimeter, points);
});
ruler.selectAll("*").remove();
if (rulers.data.length) {
turnButtonOn("toggleRulers");
rulers.draw();
} else turnButtonOff("toggleRulers");
// 1.61 changed oceanicPattern from rect to image
const pattern = document.getElementById("oceanic");
const filter = pattern.firstElementChild.getAttribute("filter");
const href = filter ? "./images/" + filter.replace("url(#", "").replace(")", "") + ".png" : "";
pattern.innerHTML = /* html */ `<image id="oceanicPattern" href=${href} width="100" height="100" opacity="0.2"></image>`;
}
if (version < 1.62) {
// v1.62 changed grid data
gridOverlay.attr("size", null);
}
if (version < 1.63) {
// v1.63 changed ocean pattern opacity element
const oceanPattern = document.getElementById("oceanPattern");
if (oceanPattern) oceanPattern.removeAttribute("opacity");
const oceanicPattern = document.getElementById("oceanicPattern");
if (!oceanicPattern.getAttribute("opacity")) oceanicPattern.setAttribute("opacity", 0.2);
// v 1.63 moved label text-shadow from css to editable inline style
burgLabels.select("#cities").style("text-shadow", "white 0 0 4px");
burgLabels.select("#towns").style("text-shadow", "white 0 0 4px");
labels.select("#states").style("text-shadow", "white 0 0 4px");
labels.select("#addedLabels").style("text-shadow", "white 0 0 4px");
}
if (version < 1.64) {
// v1.64 change states style
const opacity = regions.attr("opacity");
const filter = regions.attr("filter");
statesBody.attr("opacity", opacity).attr("filter", filter);
statesHalo.attr("opacity", opacity).attr("filter", "blur(5px)");
regions.attr("opacity", null).attr("filter", null);
}
if (version < 1.65) {
// v1.65 changed rivers data
d3.select("#rivers").attr("style", null); // remove style to unhide layer
const {cells, rivers} = pack;
const defaultWidthFactor = rn(1 / (pointsInput.dataset.cells / 10000) ** 0.25, 2);
for (const river of rivers) {
const node = document.getElementById("river" + river.i);
if (node && !river.cells) {
const riverCells = [];
const riverPoints = [];
const length = node.getTotalLength() / 2;
if (!length) continue;
const segments = Math.ceil(length / 6);
const increment = length / segments;
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 = rn((x1 + x2) / 2, 1);
const y = rn((y1 + y2) / 2, 1);
const cell = findCell(x, y);
riverPoints.push([x, y]);
riverCells.push(cell);
}
river.cells = riverCells;
river.points = riverPoints;
}
river.widthFactor = defaultWidthFactor;
cells.i.forEach(i => {
const riverInWater = cells.r[i] && cells.h[i] < 20;
if (riverInWater) cells.r[i] = 0;
});
}
}
if (version < 1.652) {
// remove style to unhide layers
rivers.attr("style", null);
borders.attr("style", null);
}
if (version < 1.7) {
// v1.7 changed markers data
const defs = document.getElementById("defs-markers");
const markersGroup = document.getElementById("markers");
if (defs && markersGroup) {
const markerElements = markersGroup.querySelectorAll("use");
const rescale = +markersGroup.getAttribute("rescale");
pack.markers = Array.from(markerElements).map((el, i) => {
const id = el.getAttribute("id");
const note = notes.find(note => note.id === id);
if (note) note.id = `marker${i}`;
let x = +el.dataset.x;
let y = +el.dataset.y;
const transform = el.getAttribute("transform");
if (transform) {
const [dx, dy] = parseTransform(transform);
if (dx) x += +dx;
if (dy) y += +dy;
}
const cell = findCell(x, y);
const size = rn(rescale ? el.dataset.size * 30 : el.getAttribute("width"), 1);
const href = el.href.baseVal;
const type = href.replace("#marker_", "");
const symbol = defs?.querySelector(`symbol${href}`);
const text = symbol?.querySelector("text");
const circle = symbol?.querySelector("circle");
const icon = text?.innerHTML;
const px = text && Number(text.getAttribute("font-size")?.replace("px", ""));
const dx = text && Number(text.getAttribute("x")?.replace("%", ""));
const dy = text && Number(text.getAttribute("y")?.replace("%", ""));
const fill = circle && circle.getAttribute("fill");
const stroke = circle && circle.getAttribute("stroke");
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;
if (!isNaN(dy) && dy !== 50) marker.dy = dy;
if (fill && fill !== "#ffffff") marker.fill = fill;
if (stroke && stroke !== "#000000") marker.stroke = stroke;
if (circle?.getAttribute("opacity") === "0") marker.pin = "no";
return marker;
});
markersGroup.style.display = null;
defs?.remove();
markerElements.forEach(el => el.remove());
if (layerIsOn("markers")) drawMarkers();
}
}
if (version < 1.72) {
// v1.72 renamed custom style presets
const storedStyles = Object.keys(localStorage).filter(key => key.startsWith("style"));
storedStyles.forEach(styleName => {
const style = localStorage.getItem(styleName);
const newStyleName = styleName.replace(/^style/, customPresetPrefix);
localStorage.setItem(newStyleName, style);
localStorage.removeItem(styleName);
});
}
if (version < 1.73) {
// v1.73 moved the hatching patterns out of the user's SVG
document.getElementById("hatching")?.remove();
// v1.73 added zone type to UI, ensure type is populated
const zones = Array.from(document.querySelectorAll("#zones > g"));
zones.forEach(zone => {
if (!zone.dataset.type) zone.dataset.type = "Unknown";
});
}
if (version < 1.84) {
// v1.84.0 added grid.cellsDesired to stored data
if (!grid.cellsDesired) grid.cellsDesired = rn((graphWidth * graphHeight) / grid.spacing ** 2, -3);
}
if (version < 1.85) {
// v1.84.0 moved intial screen out of maon svg
svg.select("#initial").remove();
}
if (version < 1.86) {
// v1.86.0 added multi-origin culture and religion hierarchy trees
for (const culture of pack.cultures) {
culture.origins = [culture.origin];
delete culture.origin;
}
for (const religion of pack.religions) {
religion.origins = [religion.origin];
delete religion.origin;
}
}
if (version < 1.88) {
// v1.87 may have incorrect shield for some reason
pack.states.forEach(({coa}) => {
if (coa?.shield === "state") delete coa.shield;
});
}
if (version < 1.91) {
// from 1.91.00 custom coa is moved to coa object
pack.states.forEach(state => {
if (state.coa === "custom") state.coa = {custom: true};
});
pack.provinces.forEach(province => {
if (province.coa === "custom") province.coa = {custom: true};
});
pack.burgs.forEach(burg => {
if (burg.coa === "custom") burg.coa = {custom: true};
});
// from 1.91.00 emblems don't have transform attribute
emblems.selectAll("use").each(function () {
const transform = this.getAttribute("transform");
if (!transform) return;
const [dx, dy] = parseTransform(transform);
const x = Number(this.getAttribute("x")) + Number(dx);
const y = Number(this.getAttribute("y")) + Number(dy);
this.setAttribute("x", x);
this.setAttribute("y", y);
this.removeAttribute("transform");
});
// from 1.91.00 coaSize is moved to coa object
pack.states.forEach(state => {
if (state.coaSize && state.coa) {
state.coa.size = state.coaSize;
delete state.coaSize;
}
});
pack.provinces.forEach(province => {
if (province.coaSize && province.coa) {
province.coa.size = province.coaSize;
delete province.coaSize;
}
});
pack.burgs.forEach(burg => {
if (burg.coaSize && burg.coa) {
burg.coa.size = burg.coaSize;
delete burg.coaSize;
}
});
}
if (version < 1.92) {
// v1.92 change labels text-anchor from 'start' to 'middle'
labels.selectAll("tspan").each(function () {
this.setAttribute("x", 0);
});
}
if (version < 1.94) {
// from v1.94.00 texture image is removed when layer is off
texture.style("display", null);
const textureImage = texture.select("image");
if (textureImage.size()) {
// restore parameters
const x = Number(textureImage.attr("x") || 0);
const y = Number(textureImage.attr("y") || 0);
const href = textureImage.attr("xlink:href") || textureImage.attr("href") || textureImage.attr("src");
// save parameters to parent element
texture.attr("data-href", href).attr("data-x", x).attr("data-y", y);
// recreate image in expected format
textureImage.remove();
drawTexture();
}
}
if (version < 1.95) {
// v1.95.00 added vignette visual layer
const mask = defs.append("mask").attr("id", "vignette-mask");
mask.append("rect").attr("fill", "white").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%");
mask
.append("rect")
.attr("id", "vignette-rect")
.attr("fill", "black")
.attr("x", "0.3%")
.attr("y", "0.4%")
.attr("width", "99.4%")
.attr("height", "99.2%")
.attr("rx", "5%")
.attr("ry", "5%")
.attr("filter", "blur(20px)");
const vignette = svg
.append("g")
.attr("id", "vignette")
.attr("mask", "url(#vignette-mask)")
.attr("opacity", 0.3)
.attr("fill", "#000000")
.style("display", "none");
vignette.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%");
}
if (version < 1.96) {
// v1.96 added ocean rendering for heightmap
terrs.selectAll("*").remove();
const opacity = terrs.attr("opacity");
const filter = terrs.attr("filter");
const scheme = terrs.attr("scheme");
const terracing = terrs.attr("terracing");
const skip = terrs.attr("skip");
const relax = terrs.attr("relax");
const curveTypes = {0: "curveBasisClosed", 1: "curveLinear", 2: "curveStep"};
const curve = curveTypes[terrs.attr("curve")] || "curveBasisClosed";
terrs
.attr("mask", null)
.attr("scheme", null)
.attr("terracing", null)
.attr("skip", null)
.attr("relax", null)
.attr("curve", null);
terrs
.append("g")
.attr("id", "oceanHeights")
.attr("data-render", 0)
.attr("opacity", opacity)
.attr("filter", filter)
.attr("scheme", scheme)
.attr("terracing", 0)
.attr("skip", 0)
.attr("relax", 1)
.attr("curve", curve);
terrs
.append("g")
.attr("id", "landHeights")
.attr("opacity", opacity)
.attr("scheme", scheme)
.attr("filter", filter)
.attr("terracing", terracing)
.attr("skip", skip)
.attr("relax", relax)
.attr("curve", curve)
.attr("mask", "url(#land)");
if (layerIsOn("toggleHeight")) drawHeightmap();
// v1.96.00 moved scaleBar options from units editor to style
d3.select("#scaleBar").remove();
scaleBar = svg
.insert("g", "#viewbox + *")
.attr("id", "scaleBar")
.attr("opacity", 1)
.attr("fill", "#353540")
.attr("data-bar-size", 2)
.attr("font-size", 10)
.attr("data-x", 99)
.attr("data-y", 99)
.attr("data-label", "");
scaleBar
.append("rect")
.attr("id", "scaleBarBack")
.attr("opacity", 0.2)
.attr("fill", "#ffffff")
.attr("stroke", "#000000")
.attr("stroke-width", 1)
.attr("filter", "url(#blur5)")
.attr("data-top", 20)
.attr("data-right", 15)
.attr("data-bottom", 15)
.attr("data-left", 10);
drawScaleBar(scaleBar, scale);
fitScaleBar(scaleBar, svgWidth, svgHeight);
if (!layerIsOn("toggleScaleBar")) scaleBar.style("display", "none");
// v1.96.00 changed coloring approach for regiments
armies.selectAll(":scope > g").each(function () {
const fill = this.getAttribute("fill");
if (!fill) return;
const darkerColor = d3.color(fill).darker().hex();
this.setAttribute("color", darkerColor);
this.querySelectorAll("g > rect:nth-child(2)").forEach(rect => {
rect.setAttribute("fill", "currentColor");
});
});
}
}