// Functions to save and load the map
'use strict';
function quickLoad() {
ldb.get('lastMap', (blob) => {
if (blob) {
loadMapPrompt(blob);
} else {
tip('No map stored. Save map to storage first', true, 'error', 2000);
ERROR && console.error('No map stored');
}
});
}
function loadMapPrompt(blob) {
const workingTime = (Date.now() - last(mapHistory).created) / 60000; // minutes
if (workingTime < 5) {
loadLastSavedMap();
return;
}
alertMessage.innerHTML = `Are you sure you want to load saved map?
All unsaved changes made to the current map will be lost`;
$('#alert').dialog({
resizable: false,
title: 'Load saved map',
buttons: {
Cancel: function () {
$(this).dialog('close');
},
Load: function () {
loadLastSavedMap();
$(this).dialog('close');
}
}
});
function loadLastSavedMap() {
WARN && console.warn('Load last saved map');
try {
uploadMap(blob);
} catch (error) {
ERROR && console.error(error);
tip('Cannot load last saved map', true, 'error', 2000);
}
}
}
function uploadMap(file, callback) {
uploadMap.timeStart = performance.now();
const fileReader = new FileReader();
fileReader.onload = function (fileLoadedEvent) {
if (callback) callback();
document.getElementById('coas').innerHTML = ''; // remove auto-generated emblems
const dataLoaded = fileLoadedEvent.target.result;
const data = dataLoaded.split('\r\n');
const mapVersion = data[0].split('|')[0] || data[0];
if (mapVersion === version) {
parseLoadedData(data);
return;
}
const archive = link('https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog', 'archived version');
const parsed = parseFloat(mapVersion);
let message = '',
load = false;
if (isNaN(parsed) || data.length < 26 || !data[5]) {
message = `The file you are trying to load is outdated or not a valid .map file.
Please try to open it using an ${archive}`;
} else if (parsed < 0.7) {
message = `The map version you are trying to load (${mapVersion}) is too old and cannot be updated to the current version.
Please keep using an ${archive}`;
} else {
load = true;
message = `The map version (${mapVersion}) does not match the Generator version (${version}).
Click OK to get map auto-updated. In case of issues please keep using an ${archive} of the Generator`;
}
alertMessage.innerHTML = message;
$('#alert').dialog({
title: 'Version conflict',
width: '38em',
buttons: {
OK: function () {
$(this).dialog('close');
if (load) parseLoadedData(data);
}
}
});
};
fileReader.readAsText(file, 'UTF-8');
}
function parseLoadedData(data) {
try {
// exit customization
if (window.closeDialogs) closeDialogs();
customization = 0;
if (customizationMenu.offsetParent) styleTab.click();
const reliefIcons = document.getElementById('defs-relief').innerHTML; // save relief icons
const hatching = document.getElementById('hatching').cloneNode(true); // save hatching
void (function parseParameters() {
const params = data[0].split('|');
if (params[3]) {
seed = params[3];
optionsSeed.value = seed;
}
if (params[4]) graphWidth = +params[4];
if (params[5]) graphHeight = +params[5];
mapId = params[6] ? +params[6] : Date.now();
})();
INFO && console.group('Loaded Map ' + seed);
void (function parseSettings() {
const settings = data[1].split('|');
if (settings[0]) applyOption(distanceUnitInput, settings[0]);
if (settings[1]) distanceScaleInput.value = distanceScaleOutput.value = settings[1];
if (settings[2]) areaUnit.value = settings[2];
if (settings[3]) applyOption(heightUnit, settings[3]);
if (settings[4]) heightExponentInput.value = heightExponentOutput.value = settings[4];
if (settings[5]) temperatureScale.value = settings[5];
if (settings[6]) barSizeInput.value = barSizeOutput.value = settings[6];
if (settings[7] !== undefined) barLabel.value = settings[7];
if (settings[8] !== undefined) barBackOpacity.value = settings[8];
if (settings[9]) barBackColor.value = settings[9];
if (settings[10]) barPosX.value = settings[10];
if (settings[11]) barPosY.value = settings[11];
if (settings[12]) populationRate = populationRateInput.value = populationRateOutput.value = settings[12];
if (settings[13]) urbanization = urbanizationInput.value = urbanizationOutput.value = settings[13];
if (settings[14]) mapSizeInput.value = mapSizeOutput.value = Math.max(Math.min(settings[14], 100), 1);
if (settings[15]) latitudeInput.value = latitudeOutput.value = Math.max(Math.min(settings[15], 100), 0);
if (settings[16]) temperatureEquatorInput.value = temperatureEquatorOutput.value = settings[16];
if (settings[17]) temperaturePoleInput.value = temperaturePoleOutput.value = settings[17];
if (settings[18]) precInput.value = precOutput.value = settings[18];
if (settings[19]) options = JSON.parse(settings[19]);
if (settings[20]) mapName.value = settings[20];
if (settings[21]) hideLabels.checked = +settings[21];
})();
void (function parseConfiguration() {
if (data[2]) mapCoordinates = JSON.parse(data[2]);
if (data[4]) notes = JSON.parse(data[4]);
if (data[33]) rulers.fromString(data[33]);
const biomes = data[3].split('|');
biomesData = applyDefaultBiomesSystem();
biomesData.color = biomes[0].split(',');
biomesData.habitability = biomes[1].split(',').map((h) => +h);
biomesData.name = biomes[2].split(',');
// push custom biomes if any
for (let i = biomesData.i.length; i < biomesData.name.length; i++) {
biomesData.i.push(biomesData.i.length);
biomesData.iconsDensity.push(0);
biomesData.icons.push([]);
biomesData.cost.push(50);
}
})();
void (function replaceSVG() {
svg.remove();
document.body.insertAdjacentHTML('afterbegin', data[5]);
})();
void (function redefineElements() {
svg = d3.select('#map');
defs = svg.select('#deftemp');
viewbox = svg.select('#viewbox');
scaleBar = svg.select('#scaleBar');
legend = svg.select('#legend');
ocean = viewbox.select('#ocean');
oceanLayers = ocean.select('#oceanLayers');
oceanPattern = ocean.select('#oceanPattern');
lakes = viewbox.select('#lakes');
landmass = viewbox.select('#landmass');
texture = viewbox.select('#texture');
terrs = viewbox.select('#terrs');
biomes = viewbox.select('#biomes');
ice = viewbox.select('#ice');
cells = viewbox.select('#cells');
gridOverlay = viewbox.select('#gridOverlay');
coordinates = viewbox.select('#coordinates');
compass = viewbox.select('#compass');
rivers = viewbox.select('#rivers');
terrain = viewbox.select('#terrain');
relig = viewbox.select('#relig');
cults = viewbox.select('#cults');
regions = viewbox.select('#regions');
statesBody = regions.select('#statesBody');
statesHalo = regions.select('#statesHalo');
provs = viewbox.select('#provs');
zones = viewbox.select('#zones');
borders = viewbox.select('#borders');
stateBorders = borders.select('#stateBorders');
provinceBorders = borders.select('#provinceBorders');
routes = viewbox.select('#routes');
roads = routes.select('#roads');
trails = routes.select('#trails');
searoutes = routes.select('#searoutes');
temperature = viewbox.select('#temperature');
coastline = viewbox.select('#coastline');
prec = viewbox.select('#prec');
population = viewbox.select('#population');
emblems = viewbox.select('#emblems');
labels = viewbox.select('#labels');
icons = viewbox.select('#icons');
burgIcons = icons.select('#burgIcons');
anchors = icons.select('#anchors');
armies = viewbox.select('#armies');
markers = viewbox.select('#markers');
ruler = viewbox.select('#ruler');
fogging = viewbox.select('#fogging');
debug = viewbox.select('#debug');
burgLabels = labels.select('#burgLabels');
})();
void (function parseGridData() {
grid = JSON.parse(data[6]);
calculateVoronoi(grid, grid.points);
grid.cells.h = Uint8Array.from(data[7].split(','));
grid.cells.prec = Uint8Array.from(data[8].split(','));
grid.cells.f = Uint16Array.from(data[9].split(','));
grid.cells.t = Int8Array.from(data[10].split(','));
grid.cells.temp = Int8Array.from(data[11].split(','));
})();
void (function parsePackData() {
pack = {};
reGraph();
reMarkFeatures();
pack.features = JSON.parse(data[12]);
pack.cultures = JSON.parse(data[13]);
pack.states = JSON.parse(data[14]);
pack.burgs = JSON.parse(data[15]);
pack.religions = data[29] ? JSON.parse(data[29]) : [{i: 0, name: 'No religion'}];
pack.provinces = data[30] ? JSON.parse(data[30]) : [0];
pack.rivers = data[32] ? JSON.parse(data[32]) : [];
pack.resources = data[35] ? JSON.parse(data[35]) : [];
const cells = pack.cells;
cells.biome = Uint8Array.from(data[16].split(','));
cells.burg = Uint16Array.from(data[17].split(','));
cells.conf = Uint8Array.from(data[18].split(','));
cells.culture = Uint16Array.from(data[19].split(','));
cells.fl = Uint16Array.from(data[20].split(','));
cells.pop = Float32Array.from(data[21].split(','));
cells.r = Uint16Array.from(data[22].split(','));
cells.road = Uint16Array.from(data[23].split(','));
cells.s = Uint16Array.from(data[24].split(','));
cells.state = Uint16Array.from(data[25].split(','));
cells.religion = data[26] ? Uint16Array.from(data[26].split(',')) : new Uint16Array(cells.i.length);
cells.province = data[27] ? Uint16Array.from(data[27].split(',')) : new Uint16Array(cells.i.length);
cells.crossroad = data[28] ? Uint16Array.from(data[28].split(',')) : new Uint16Array(cells.i.length);
cells.resource = data[34] ? Uint8Array.from(data[34].split(',')) : new Uint8Array(cells.i.length);
if (data[31]) {
const namesDL = data[31].split('/');
namesDL.forEach((d, i) => {
const e = d.split('|');
if (!e.length) return;
const b = e[5].split(',').length > 2 || !nameBases[i] ? e[5] : nameBases[i].b;
nameBases[i] = {name: e[0], min: e[1], max: e[2], d: e[3], m: e[4], b};
});
}
})();
const notHidden = (selection) => selection.node() && selection.style('display') !== 'none';
const hasChildren = (selection) => selection.node()?.hasChildNodes();
const hasChild = (selection, selector) => selection.node()?.querySelector(selector);
const turnOn = (el) => document.getElementById(el).classList.remove('buttonoff');
void (function restoreLayersState() {
// turn all layers off
document
.getElementById('mapLayers')
.querySelectorAll('li')
.forEach((el) => el.classList.add('buttonoff'));
// turn on active layers
if (notHidden(texture) && hasChild(texture, 'image')) turnOn('toggleTexture');
if (hasChildren(terrs)) turnOn('toggleHeight');
if (hasChildren(biomes)) turnOn('toggleBiomes');
if (hasChildren(cells)) turnOn('toggleCells');
if (hasChildren(gridOverlay)) turnOn('toggleGrid');
if (hasChildren(coordinates)) turnOn('toggleCoordinates');
if (notHidden(compass) && hasChild(compass, 'use')) turnOn('toggleCompass');
if (notHidden(rivers)) turnOn('toggleRivers');
if (notHidden(terrain) && hasChildren(terrain)) turnOn('toggleRelief');
if (hasChildren(relig)) turnOn('toggleReligions');
if (hasChildren(cults)) turnOn('toggleCultures');
if (hasChildren(statesBody)) turnOn('toggleStates');
if (hasChildren(provs)) turnOn('toggleProvinces');
if (hasChildren(zones) && notHidden(zones)) turnOn('toggleZones');
if (notHidden(borders) && hasChild(compass, 'use')) turnOn('toggleBorders');
if (notHidden(routes) && hasChild(routes, 'path')) turnOn('toggleRoutes');
if (hasChildren(temperature)) turnOn('toggleTemp');
if (hasChild(population, 'line')) turnOn('togglePopulation');
if (hasChildren(ice)) turnOn('toggleIce');
if (hasChild(prec, 'circle')) turnOn('togglePrec');
if (hasChildren(goods)) turnOn('toggleResources');
if (notHidden(emblems) && hasChild(emblems, 'use')) turnOn('toggleEmblems');
if (notHidden(labels)) turnOn('toggleLabels');
if (notHidden(icons)) turnOn('toggleIcons');
if (hasChildren(armies) && notHidden(armies)) turnOn('toggleMilitary');
if (hasChildren(markers) && notHidden(markers)) turnOn('toggleMarkers');
if (notHidden(ruler)) turnOn('toggleRulers');
if (notHidden(scaleBar)) turnOn('toggleScaleBar');
getCurrentPreset();
})();
void (function restoreEvents() {
scaleBar.on('mousemove', () => tip('Click to open Units Editor')).on('click', () => editUnits());
legend.on('mousemove', () => tip('Drag to change the position. Click to hide the legend')).on('click', () => clearLegend());
})();
void (function resolveVersionConflicts() {
const version = parseFloat(data[0].split('|')[0]);
if (version < 0.9) {
// 0.9 has additional relief icons to be included into older maps
document.getElementById('defs-relief').innerHTML = reliefIcons;
}
if (version < 1) {
// 1.0 adds a new religions layer
relig = viewbox.insert('g', '#terrain').attr('id', 'relig');
Religions.generate();
// 1.0 adds a legend box
legend = svg.append('g').attr('id', 'legend');
legend
.attr('font-family', 'Almendra SC')
.attr('data-font', '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');
// 1.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');
// 1.0 adds 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();
// 1.0 adds hatching
document.getElementsByTagName('defs')[0].appendChild(hatching);
// 1.0 adds 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()) {
addMarkers();
turnButtonOn('toggleMarkers');
}
// 1.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');
// 1.0 changes states opacity bask to regions level
if (statesBody.attr('opacity')) {
regions.attr('opacity', statesBody.attr('opacity'));
statesBody.attr('opacity', null);
}
// 1.0 changed labels to multi-lined
labels.selectAll('textPath').each(function () {
const text = this.textContent;
const shift = this.getComputedTextLength() / -1.5;
this.innerHTML = `
generate a new random map or cancel the loading
${parseError(error)}
`; $('#alert').dialog({ resizable: false, title: 'Loading error', maxWidth: '50em', buttons: { 'Select file': function () { $(this).dialog('close'); mapToLoad.click(); }, 'New map': function () { $(this).dialog('close'); regenerateMap(); }, Cancel: function () { $(this).dialog('close'); } }, position: {my: 'center', at: 'center', of: 'svg'} }); } }