'use strict'; function editResources() { if (customization) return; closeDialogs('#resourcesEditor, .stable'); if (!layerIsOn('toggleResources')) toggleResources(); const body = document.getElementById('resourcesBody'); resourcesEditorAddLines(); if (modules.editResources) return; modules.editResources = true; $('#resourcesEditor').dialog({ title: 'Resources Editor', resizable: false, width: fitContent(), close: closeResourcesEditor, position: {my: 'right top', at: 'right-10 top+10', of: 'svg'} }); // add listeners document.getElementById('resourcesEditorRefresh').addEventListener('click', resourcesEditorAddLines); document.getElementById('resourcesRegenerate').addEventListener('click', regenerateCurrentResources); document.getElementById('resourcesLegend').addEventListener('click', toggleLegend); document.getElementById('resourcesPercentage').addEventListener('click', togglePercentageMode); document.getElementById('resourcesAssign').addEventListener('click', enterResourceAssignMode); document.getElementById('resourcesAdd').addEventListener('click', resourceAdd); document.getElementById('resourcesRestore').addEventListener('click', resourcesRestoreDefaults); document.getElementById('resourcesExport').addEventListener('click', downloadResourcesData); document.getElementById('resourcesUnpinAll').addEventListener('click', unpinAllResources); body.addEventListener('click', function (ev) { const el = ev.target, cl = el.classList, line = el.parentNode; const resource = Resources.get(+line.dataset.id); if (cl.contains('resourceCategory')) return changeCategory(resource, line, el); if (cl.contains('resourceModel')) return changeModel(resource, line, el); if (cl.contains('resourceBonus')) return changeBonus(resource, line, el); if (cl.contains('icon-pin')) return pinResource(resource, el); if (cl.contains('icon-trash-empty')) return removeResource(resource, line); }); body.addEventListener('change', function (ev) { const el = ev.target, cl = el.classList, line = el.parentNode; const resource = Resources.get(+line.dataset.id); if (cl.contains('resourceName')) return changeName(resource, el.value, line); if (cl.contains('resourceValue')) return changeValue(resource, el.value, line); if (cl.contains('resourceChance')) return changeChance(resource, el.value, line); }); function getBonusIcon(bonus) { if (bonus === 'fleet') return ``; if (bonus === 'defence') return ``; if (bonus === 'prestige') return ``; if (bonus === 'artillery') return ``; if (bonus === 'infantry') return ``; if (bonus === 'population') return ``; if (bonus === 'archers') return ``; if (bonus === 'cavalry') return ``; } // add line for each resource function resourcesEditorAddLines() { const addTitle = (string, max) => (string.length < max ? '' : `title="${string}"`); let lines = ''; for (const r of pack.resources) { const stroke = Resources.getStroke(r.color); const model = r.model.replaceAll('_', ' '); const bonusArray = Object.entries(r.bonus) .map((e) => Array(e[1]).fill(e[0])) .flat(); const bonusHTML = bonusArray.map((bonus) => getBonusIcon(bonus)).join(''); const bonusString = Object.entries(r.bonus) .map((e) => e.join(': ')) .join('; '); lines += `
${r.category}
${r.cells}
${model}
${bonusHTML || "place"}
`; } body.innerHTML = lines; // update footer document.getElementById('resourcesNumber').innerHTML = pack.resources.length; // add listeners // body.querySelectorAll("div.resources").forEach(el => el.addEventListener("mouseenter", ev => resourceHighlightOn(ev))); // body.querySelectorAll("div.resources").forEach(el => el.addEventListener("mouseleave", ev => resourceHighlightOff(ev))); body.querySelectorAll('div.states').forEach((el) => el.addEventListener('click', selectResourceOnLineClick)); body.querySelectorAll('svg.icon').forEach((el) => el.addEventListener('click', resourceChangeColor)); if (body.dataset.type === 'percentage') { body.dataset.type = 'absolute'; togglePercentageMode(); } applySorting(resourcesHeader); $('#resourcesEditor').dialog({width: fitContent()}); } function changeCategory(resource, line, el) { const categories = [...new Set(pack.resources.map((r) => r.category))].sort(); const categoryOptions = (category) => categories.map((c) => ``).join(''); alertMessage.innerHTML = `
Select category:
Custom category:
`; $('#alert').dialog({ resizable: false, title: 'Change category', buttons: { Cancel: function () { $(this).dialog('close'); }, Apply: function () { applyChanges(); $(this).dialog('close'); } } }); function applyChanges() { const custom = document.getElementById('resouceCategoryAdd').value; const select = document.getElementById('resouceCategorySelect').value; const category = custom ? capitalize(custom) : select; resource.category = line.dataset.category = el.innerHTML = category; } } function changeModel(resource, line, el) { const defaultModels = Resources.defaultModels; const model = line.dataset.model; const modelOptions = Object.keys(defaultModels) .sort() .map((m) => ``) .join(''); const wikiURL = 'https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Resources:-spread-functions'; const onSelect = "resouceModelFunction.innerHTML = Resources.defaultModels[this.value] || ' '; resouceModelCustomName.value = ''; resouceModelCustomFunction.value = ''"; alertMessage.innerHTML = `
Predefined models
Name:
Function:
${defaultModels[model] || ' '}
Custom model
Name:
Function:
`; $('#alert').dialog({ resizable: false, title: 'Change spread model', buttons: { Help: () => openURL(wikiURL), Cancel: function () { $(this).dialog('close'); }, Apply: function () { applyChanges(this); } } }); function applyChanges(dialog) { const customName = document.getElementById('resouceModelCustomName').value; const customFn = document.getElementById('resouceModelCustomFunction').value; const message = document.getElementById('resourceModelMessage'); if (customName && !customFn) return (message.innerHTML = 'Error. Custom model function is required'); if (!customName && customFn) return (message.innerHTML = 'Error. Custom model name is required'); message.innerHTML = ''; if (customName && customFn) { try { const allMethods = '{' + Object.keys(Resources.methods).join(', ') + '}'; const fn = new Function(allMethods, 'return ' + customFn); fn({...Resources.methods}); } catch (err) { message.innerHTML = 'Error. ' + err.message || err; return; } resource.model = line.dataset.model = el.innerHTML = customName; el.setAttribute('title', customName.length > 7 ? customName : ''); resource.custom = customFn; $(dialog).dialog('close'); return; } const model = document.getElementById('resouceModelSelect').value; if (!model) return (message.innerHTML = 'Error. Model is not set'); resource.model = line.dataset.model = el.innerHTML = model; el.setAttribute('title', model.length > 7 ? model : ''); $(dialog).dialog('close'); } } function changeBonus(resource, line, el) { const bonuses = [...new Set(pack.resources.map((r) => Object.keys(r.bonus)).flat())].sort(); const inputs = bonuses.map( (bonus) => `
${getBonusIcon(bonus)}
${capitalize(bonus)}
` ); alertMessage.innerHTML = inputs.join(''); $('#alert').dialog({ resizable: false, title: 'Change bonus', buttons: { Cancel: function () { $(this).dialog('close'); }, Apply: function () { applyChanges(); $(this).dialog('close'); } } }); function applyChanges() { const bonusObj = {}; bonuses.forEach((bonus) => { const el = document.getElementById('resourceBonus_' + bonus); const value = parseInt(el.value); if (isNaN(value) || !value) return; bonusObj[bonus] = value; }); const bonusArray = Object.entries(bonusObj).map(e => Array(e[1]).fill(e[0])).flat(); //prettier-ignore const bonusHTML = bonusArray.map((bonus) => getBonusIcon(bonus)).join(''); const bonusString = Object.entries(bonusObj).map((e) => e.join(': ')).join('; '); //prettier-ignore resource.bonus = bonusObj; el.innerHTML = bonusHTML || "place"; line.dataset.bonus = bonusString; el.setAttribute('title', bonusString); } } function changeName(resource, name, line) { resource.name = line.dataset.name = name; } function changeValue(resource, value, line) { resource.value = line.dataset.value = +value; } function changeChance(resource, chance, line) { resource.chance = line.dataset.chance = +chance; } function resourceChangeColor() { const circle = this.querySelector('circle'); const resource = Resources.get(+this.parentNode.dataset.id); const callback = function (fill) { const stroke = Resources.getStroke(fill); circle.setAttribute('fill', fill); circle.setAttribute('stroke', stroke); resource.color = fill; resource.stroke = stroke; goods.selectAll(`circle[data-i='${resource.i}']`).attr('fill', fill).attr('stroke', stroke); }; openPicker(resource.color, callback, {allowHatching: false}); } function regenerateCurrentResources() { const message = 'Are you sure you want to regenerate resources?
This action cannot be reverted'; confirmationDialog({title: 'Regenerate resources', message, confirm: 'Regenerate', onConfirm: regenerateResources}); } function resourcesRestoreDefaults() { const message = 'Are you sure you want to restore default resources?
This action cannot be reverted'; const onConfirm = () => { delete pack.resources; regenerateResources(); }; confirmationDialog({title: 'Restore default resources', message, confirm: 'Restore', onConfirm}); } function toggleLegend() { if (legend.selectAll('*').size()) { clearLegend(); return; } const data = pack.resources .filter((r) => r.i && r.cells) .sort((a, b) => b.cells - a.cells) .map((r) => [r.i, r.color, r.name]); drawLegend('Resources', data); } function togglePercentageMode() { if (body.dataset.type === 'absolute') { body.dataset.type = 'percentage'; const totalCells = pack.cells.resource.filter((r) => r !== 0).length; body.querySelectorAll(':scope > div').forEach(function (el) { el.querySelector('.cells').innerHTML = rn((+el.dataset.cells / totalCells) * 100) + '%'; }); } else { body.dataset.type = 'absolute'; resourcesEditorAddLines(); } } function enterResourceAssignMode() { if (this.classList.contains('pressed')) return exitResourceAssignMode(); customization = 14; this.classList.add('pressed'); if (!layerIsOn('toggleResources')) toggleResources(); if (!layerIsOn('toggleCells')) { const toggler = document.getElementById('toggleCells'); toggler.dataset.forced = true; toggleCells(); } document .getElementById('resourcesEditor') .querySelectorAll('.hide') .forEach((el) => el.classList.add('hidden')); document.getElementById('resourcesFooter').style.display = 'none'; body.querySelectorAll('.resourceName, .resourceCategory, .resourceChance, .resourceCells, svg').forEach((e) => (e.style.pointerEvents = 'none')); $('#resourcesEditor').dialog({position: {my: 'right top', at: 'right-10 top+10', of: 'svg', collision: 'fit'}}); tip('Select resource line in editor, click on cells to remove or add a resource', true); viewbox.on('click', changeResourceOnCellClick); body.querySelector('div').classList.add('selected'); const someArePinned = pack.resources.some((resource) => resource.pinned); if (someArePinned) unpinAllResources(); } function selectResourceOnLineClick() { if (customization !== 14) return; //if (this.parentNode.id !== "statesBodySection") return; body.querySelector('div.selected').classList.remove('selected'); this.classList.add('selected'); } function changeResourceOnCellClick() { const point = d3.mouse(this); const i = findCell(point[0], point[1]); const selected = body.querySelector('div.selected'); if (!selected) return; if (pack.cells.resource[i]) { const resourceToRemove = Resources.get(pack.cells.resource[i]); if (resourceToRemove) resourceToRemove.cells -= 1; body.querySelector("div.states[data-id='" + resourceToRemove.i + "'] > .resourceCells").innerHTML = resourceToRemove.cells; pack.cells.resource[i] = 0; } else { const resourceId = +selected.dataset.id; const resource = Resources.get(resourceId); resource.cells += 1; body.querySelector("div.states[data-id='" + resourceId + "'] > .resourceCells").innerHTML = resource.cells; pack.cells.resource[i] = resourceId; } goods.selectAll('*').remove(); drawResources(); } function exitResourceAssignMode(close) { customization = 0; document.getElementById('resourcesAssign').classList.remove('pressed'); if (layerIsOn('toggleCells')) { const toggler = document.getElementById('toggleCells'); if (toggler.dataset.forced) toggleCells(); delete toggler.dataset.forced; } document .getElementById('resourcesEditor') .querySelectorAll('.hide') .forEach((el) => el.classList.remove('hidden')); document.getElementById('resourcesFooter').style.display = 'block'; body.querySelectorAll('.resourceName, .resourceCategory, .resourceChance, .resourceCells, svg').forEach((e) => delete e.style.pointerEvents); !close && $('#resourcesEditor').dialog({position: {my: 'right top', at: 'right-10 top+10', of: 'svg', collision: 'fit'}}); restoreDefaultEvents(); clearMainTip(); const selected = body.querySelector('div.selected'); if (selected) selected.classList.remove('selected'); } function resourceAdd() { let i = last(pack.resources).i; while (Resources.get(i)) { i++; } const resource = {i, name: 'Resource' + i, category: 'Unknown', icon: 'resource-unknown', color: '#ff5959', value: 1, chance: 10, model: 'habitability', bonus: {population: 1}, cells: 0}; pack.resources.push(resource); tip('Resource is added', false, 'success', 3000); resourcesEditorAddLines(); } function downloadResourcesData() { let data = 'Id,Resource,Color,Category,Value,Bonus,Chance,Model,Cells\n'; // headers body.querySelectorAll(':scope > div').forEach(function (el) { data += el.dataset.id + ','; data += el.dataset.name + ','; data += el.dataset.color + ','; data += el.dataset.category + ','; data += el.dataset.value + ','; data += el.dataset.bonus + ','; data += el.dataset.chance + ','; data += el.dataset.model + ','; data += el.dataset.cells + '\n'; }); const name = getFileName('Resources') + '.csv'; downloadFile(data, name); } function pinResource(resource, el) { const pin = el.classList.contains('inactive'); el.classList.toggle('inactive'); if (pin) resource.pinned = pin; else delete resource.pinned; goods.selectAll('*').remove(); drawResources(); // manage top unpin all button state const someArePinned = pack.resources.some((resource) => resource.pinned); const unpinAll = document.getElementById('resourcesUnpinAll'); someArePinned ? unpinAll.classList.remove('hidden') : unpinAll.classList.add('hidden'); } function unpinAllResources() { pack.resources.forEach((resource) => delete resource.pinned); goods.selectAll('*').remove(); drawResources(); document.getElementById('resourcesUnpinAll').classList.add('hidden'); body.querySelectorAll(':scope > div > span.icon-pin').forEach((el) => el.classList.add('inactive')); } function removeResource(res, line) { if (customization) return; const message = 'Are you sure you want to remove the resource?
This action cannot be reverted'; const onConfirm = () => { for (const i of pack.cells.i) { if (pack.cells.resource[i] === res.i) { pack.cells.resource[i] = 0; } } pack.resources = pack.resources.filter((resource) => resource.i !== res.i); line.remove(); goods.selectAll('*').remove(); drawResources(); }; confirmationDialog({title: 'Remove resource', message, confirm: 'Remove', onConfirm}); } function closeResourcesEditor() { if (customization === 14) exitResourceAssignMode('close'); unpinAllResources(); body.innerHTML = ''; } }