mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-23 12:31:24 +01:00
Merge branch 'master' into master
This commit is contained in:
commit
d0d8015c96
23 changed files with 980 additions and 351 deletions
Binary file not shown.
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 58 KiB |
51
index.html
51
index.html
|
|
@ -31,7 +31,6 @@
|
||||||
#loading-text span:nth-child(2), #mapOverlay > span:nth-child(2) {animation-delay: 1s;}
|
#loading-text span:nth-child(2), #mapOverlay > span:nth-child(2) {animation-delay: 1s;}
|
||||||
#loading-text span:nth-child(3), #mapOverlay > span:nth-child(3) {animation-delay: 2s;}
|
#loading-text span:nth-child(3), #mapOverlay > span:nth-child(3) {animation-delay: 2s;}
|
||||||
@keyframes blink {0% {opacity: 0;} 20% {opacity: 1;} 100% {opacity: .1;}}
|
@keyframes blink {0% {opacity: 0;} 20% {opacity: 1;} 100% {opacity: .1;}}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
<link rel="stylesheet" href="index.css">
|
<link rel="stylesheet" href="index.css">
|
||||||
<link rel="stylesheet" href="icons.css">
|
<link rel="stylesheet" href="icons.css">
|
||||||
|
|
@ -227,7 +226,7 @@
|
||||||
<div id="loading">
|
<div id="loading">
|
||||||
<div id="titleName"><t data-t="titleName">Azgaar's</t></div>
|
<div id="titleName"><t data-t="titleName">Azgaar's</t></div>
|
||||||
<div id="title"><t data-t="title">Fantasy Map Generator</t></div>
|
<div id="title"><t data-t="title">Fantasy Map Generator</t></div>
|
||||||
<div id="version"><t data-t="version">v. </t>1.7</div>
|
<div id="version"><t data-t="version">v. </t>1.71</div>
|
||||||
<p id="loading-text"><t data-t="loading">LOADING</t><span>.</span><span>.</span><span>.</span></p>
|
<p id="loading-text"><t data-t="loading">LOADING</t><span>.</span><span>.</span><span>.</span></p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -431,18 +430,24 @@
|
||||||
<td>
|
<td>
|
||||||
<select id="styleTextureInput">
|
<select id="styleTextureInput">
|
||||||
<option value="none">None</option>
|
<option value="none">None</option>
|
||||||
<option value="default" selected>Default</option>
|
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2021/10/folded-paper-big.jpg">Folded paper big</option>
|
||||||
<option value="https://i.imgur.com/EWvXSou.jpg">Folded paper</option>
|
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2021/10/folded-paper-small.jpg">Folded paper small</option>
|
||||||
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/marble-big.jpg">Marble big</option>
|
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2021/10/gray-paper.jpg">Gray paper</option>
|
||||||
|
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2021/10/soiled-paper.jpg">Soiled paper horizontal</option>
|
||||||
|
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2021/10/soiled-paper-e1633784189147.jpg">Soided paper vertical</option>
|
||||||
|
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2021/10/plaster.jpg">Plaster</option>
|
||||||
|
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2021/10/ocean.jpg">Ocean</option>
|
||||||
|
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/antique-small.jpg">Antique small</option>
|
||||||
|
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/antique-big.jpg">Antique big</option>
|
||||||
|
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/pergamena-small.jpg">Pergamena small</option>
|
||||||
|
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2021/10/marble-big.jpg" selected>Marble big</option>
|
||||||
|
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/marble-small.jpg">Marble small</option>
|
||||||
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/marble-blue-small.jpg">Marble Blue</option>
|
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/marble-blue-small.jpg">Marble Blue</option>
|
||||||
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/marble-blue-big.jpg">Marble Blue big</option>
|
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/marble-blue-big.jpg">Marble Blue big</option>
|
||||||
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/stone-small.jpg">Stone small</option>
|
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/stone-small.jpg">Stone small</option>
|
||||||
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/stone-big.jpg">Stone big</option>
|
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/stone-big.jpg">Stone big</option>
|
||||||
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/timbercut-small.jpg">Timber Cut small</option>
|
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/timbercut-small.jpg">Timber Cut small</option>
|
||||||
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/timbercut-big.jpg">Timber Cut big</option>
|
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/timbercut-big.jpg">Timber Cut big</option>
|
||||||
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/antique-small.jpg">Antique small</option>
|
|
||||||
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/antique-big.jpg">Antique big</option>
|
|
||||||
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/pergamena-small.jpg">Pergamena small</option>
|
|
||||||
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/mars-small.jpg">Mars small</option>
|
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/mars-small.jpg">Mars small</option>
|
||||||
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/mars-big.jpg">Mars big</option>
|
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/mars-big.jpg">Mars big</option>
|
||||||
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/mercury-small.jpg">Mercury small</option>
|
<option value="https://i2.wp.com/azgaar.files.wordpress.com/2019/07/mercury-small.jpg">Mercury small</option>
|
||||||
|
|
@ -1430,7 +1435,7 @@
|
||||||
<p>Join our <a href='https://discordapp.com/invite/X7E84HU' target='_blank'>Discord server</a> and <a href="https://www.reddit.com/r/FantasyMapGenerator/" target="_blank">Reddit community</a> to ask questions, get help and share maps.</p>
|
<p>Join our <a href='https://discordapp.com/invite/X7E84HU' target='_blank'>Discord server</a> and <a href="https://www.reddit.com/r/FantasyMapGenerator/" target="_blank">Reddit community</a> to ask questions, get help and share maps.</p>
|
||||||
<p>The project is under active development. Creator and main maintainer: Azgaar. To track the development progress see the <a href="https://trello.com/b/7x832DG4/fantasy-map-generator" target="_blank">devboard</a>. For older versions see the <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog" target="_blank">changelog</a>. Please report bugs <a href="https://github.com/Azgaar/Fantasy-Map-Generator/issues" target="_blank">here</a>. You can also contact me directly via <a href="mailto:azgaar.fmg@yandex.by" target="_blank">email</a>.</p>
|
<p>The project is under active development. Creator and main maintainer: Azgaar. To track the development progress see the <a href="https://trello.com/b/7x832DG4/fantasy-map-generator" target="_blank">devboard</a>. For older versions see the <a href="https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog" target="_blank">changelog</a>. Please report bugs <a href="https://github.com/Azgaar/Fantasy-Map-Generator/issues" target="_blank">here</a>. You can also contact me directly via <a href="mailto:azgaar.fmg@yandex.by" target="_blank">email</a>.</p>
|
||||||
<div style="background-color: #e85b46; padding: .4em; width: max-content; margin: .6em auto 0 auto; border: 1px solid #943838">
|
<div style="background-color: #e85b46; padding: .4em; width: max-content; margin: .6em auto 0 auto; border: 1px solid #943838">
|
||||||
<a href="https://www.patreon.com/azgaar" target="_blank" style="color: white; text-decoration: none; font-family: sans-serif">
|
<a href="https://www.patreon.com/azgaar" onclick="track('click', 'patreon from about')" target="_blank" style="color: white; text-decoration: none; font-family: sans-serif">
|
||||||
<div>
|
<div>
|
||||||
<div style="width: .8em; display: inline-block; padding: 0 .2em; fill: white">
|
<div style="width: .8em; display: inline-block; padding: 0 .2em; fill: white">
|
||||||
<svg viewBox="0 0 569 546">
|
<svg viewBox="0 0 569 546">
|
||||||
|
|
@ -3353,6 +3358,10 @@
|
||||||
<tr>
|
<tr>
|
||||||
<th data-tip="Unit icon">Icon</th>
|
<th data-tip="Unit icon">Icon</th>
|
||||||
<th data-tip="Unit name. If name is changed for existing unit, old unit will be replaced">Unit name</th>
|
<th data-tip="Unit name. If name is changed for existing unit, old unit will be replaced">Unit name</th>
|
||||||
|
<th style="width:5em" data-tip="Select allowed biomes">Biomes</th>
|
||||||
|
<th style="width:5em" data-tip="Select allowed states">States</th>
|
||||||
|
<th style="width:5em" data-tip="Select allowed cultures">Cultures</th>
|
||||||
|
<th style="width:5em" data-tip="Select allowed religions">Religions</th>
|
||||||
<th data-tip="Conscription percentage for rural population">Rural</th>
|
<th data-tip="Conscription percentage for rural population">Rural</th>
|
||||||
<th data-tip="Conscription percentage for urban population">Urban</th>
|
<th data-tip="Conscription percentage for urban population">Urban</th>
|
||||||
<th data-tip="Average number of people in crew (used for total personnel calculation)">Crew</th>
|
<th data-tip="Average number of people in crew (used for total personnel calculation)">Crew</th>
|
||||||
|
|
@ -4332,18 +4341,18 @@
|
||||||
<script src="modules/ui/measurers.js"></script>
|
<script src="modules/ui/measurers.js"></script>
|
||||||
<script src="libs/umami.js"></script>
|
<script src="libs/umami.js"></script>
|
||||||
|
|
||||||
<script async defer src="https://unpkg.com/dropbox@10.8.0/dist/Dropbox-sdk.min.js"></script>
|
<script defer src="https://unpkg.com/dropbox@10.8.0/dist/Dropbox-sdk.min.js"></script>
|
||||||
<script async defer src="modules/ui/general.js"></script>
|
<script defer src="modules/ui/general.js"></script>
|
||||||
<script async defer src="modules/ui/options.js"></script>
|
<script defer src="modules/ui/options.js"></script>
|
||||||
<script async defer src="modules/ui/style.js"></script>
|
<script defer src="modules/ui/style.js"></script>
|
||||||
<script async defer src="modules/load.js"></script>
|
<script defer src="modules/load.js"></script>
|
||||||
<script async defer src="modules/cloud.js"></script>
|
<script defer src="modules/cloud.js"></script>
|
||||||
<script async defer src="main.js"></script>
|
<script defer src="main.js"></script>
|
||||||
<script async defer src="modules/save.js"></script>
|
<script defer src="modules/save.js"></script>
|
||||||
<script async defer src="modules/export.js"></script>
|
<script defer src="modules/export.js"></script>
|
||||||
<script async defer src="modules/relief-icons.js"></script>
|
<script defer src="modules/relief-icons.js"></script>
|
||||||
<script async defer src="modules/ui/tools.js"></script>
|
<script defer src="modules/ui/tools.js"></script>
|
||||||
<script async defer src="modules/ui/world-configurator.js"></script>
|
<script defer src="modules/ui/world-configurator.js"></script>
|
||||||
<script async defer src="modules/ui/heightmap-editor.js"></script>
|
<script async defer src="modules/ui/heightmap-editor.js"></script>
|
||||||
<script async defer src="modules/ui/states-editor.js"></script>
|
<script async defer src="modules/ui/states-editor.js"></script>
|
||||||
<script async defer src="modules/ui/provinces-editor.js"></script>
|
<script async defer src="modules/ui/provinces-editor.js"></script>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
(window => {
|
(window => {
|
||||||
const noTrack = window.localStorage.getItem("noTrack");
|
const noTrack = !location.hostname || window.localStorage.getItem("noTrack");
|
||||||
|
|
||||||
const {
|
const {
|
||||||
screen: {width, height},
|
screen: {width, height},
|
||||||
|
|
|
||||||
42
main.js
42
main.js
|
|
@ -2,7 +2,7 @@
|
||||||
// https://github.com/Azgaar/Fantasy-Map-Generator
|
// https://github.com/Azgaar/Fantasy-Map-Generator
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
const version = "1.7"; // generator version
|
const version = "1.71"; // generator version
|
||||||
document.title += " v" + version;
|
document.title += " v" + version;
|
||||||
|
|
||||||
// Switches to disable/enable logging features
|
// Switches to disable/enable logging features
|
||||||
|
|
@ -111,14 +111,14 @@ legend.on("mousemove", () => tip("Drag to change the position. Click to hide the
|
||||||
// main data variables
|
// main data variables
|
||||||
let grid = {}; // initial grapg based on jittered square grid and data
|
let grid = {}; // initial grapg based on jittered square grid and data
|
||||||
let pack = {}; // packed graph and data
|
let pack = {}; // packed graph and data
|
||||||
let seed,
|
let seed;
|
||||||
mapId,
|
let mapId;
|
||||||
mapHistory = [],
|
let mapHistory = [];
|
||||||
elSelected,
|
let elSelected;
|
||||||
modules = {},
|
let modules = {};
|
||||||
notes = [];
|
let notes = [];
|
||||||
let rulers = new Rulers();
|
let rulers = new Rulers();
|
||||||
let customization = 0; // 0 - no; 1 = heightmap draw; 2 - states draw; 3 - add state/burg; 4 - cultures draw
|
let customization = 0;
|
||||||
|
|
||||||
let biomesData = applyDefaultBiomesSystem();
|
let biomesData = applyDefaultBiomesSystem();
|
||||||
let nameBases = Names.getNameBases(); // cultures-related data
|
let nameBases = Names.getNameBases(); // cultures-related data
|
||||||
|
|
@ -156,20 +156,24 @@ let urbanization = +document.getElementById("urbanizationInput").value;
|
||||||
let urbanDensity = +document.getElementById("urbanDensityInput").value;
|
let urbanDensity = +document.getElementById("urbanDensityInput").value;
|
||||||
|
|
||||||
applyStoredOptions();
|
applyStoredOptions();
|
||||||
let graphWidth = +mapWidthInput.value,
|
|
||||||
graphHeight = +mapHeightInput.value; // voronoi graph extention, cannot be changed arter generation
|
// voronoi graph extention, cannot be changed arter generation
|
||||||
let svgWidth = graphWidth,
|
let graphWidth = +mapWidthInput.value;
|
||||||
svgHeight = graphHeight; // svg canvas resolution, can be changed
|
let graphHeight = +mapHeightInput.value;
|
||||||
|
|
||||||
|
// svg canvas resolution, can be changed
|
||||||
|
let svgWidth = graphWidth;
|
||||||
|
let svgHeight = graphHeight;
|
||||||
|
|
||||||
landmass.append("rect").attr("x", 0).attr("y", 0).attr("width", graphWidth).attr("height", graphHeight);
|
landmass.append("rect").attr("x", 0).attr("y", 0).attr("width", graphWidth).attr("height", graphHeight);
|
||||||
oceanPattern.append("rect").attr("fill", "url(#oceanic)").attr("x", 0).attr("y", 0).attr("width", graphWidth).attr("height", graphHeight);
|
oceanPattern.append("rect").attr("fill", "url(#oceanic)").attr("x", 0).attr("y", 0).attr("width", graphWidth).attr("height", graphHeight);
|
||||||
oceanLayers.append("rect").attr("id", "oceanBase").attr("x", 0).attr("y", 0).attr("width", graphWidth).attr("height", graphHeight);
|
oceanLayers.append("rect").attr("id", "oceanBase").attr("x", 0).attr("y", 0).attr("width", graphWidth).attr("height", graphHeight);
|
||||||
|
|
||||||
void (function removeLoading() {
|
// remove loading screen
|
||||||
d3.select("#loading").transition().duration(4000).style("opacity", 0).remove();
|
d3.select("#loading").transition().duration(4000).style("opacity", 0).remove();
|
||||||
d3.select("#initial").transition().duration(4000).attr("opacity", 0).remove();
|
d3.select("#initial").transition().duration(4000).attr("opacity", 0).remove();
|
||||||
d3.select("#optionsContainer").transition().duration(3000).style("opacity", 1);
|
d3.select("#optionsContainer").transition().duration(3000).style("opacity", 1);
|
||||||
d3.select("#tooltip").transition().duration(4000).style("opacity", 1);
|
d3.select("#tooltip").transition().duration(4000).style("opacity", 1);
|
||||||
})();
|
|
||||||
|
|
||||||
// decide which map should be loaded or generated on page load
|
// decide which map should be loaded or generated on page load
|
||||||
void (function checkLoadParameters() {
|
void (function checkLoadParameters() {
|
||||||
|
|
@ -405,6 +409,7 @@ function showWelcomeMessage() {
|
||||||
alertMessage.innerHTML = `The Fantasy Map Generator is updated up to version <b>${version}</b>.
|
alertMessage.innerHTML = `The Fantasy Map Generator is updated up to version <b>${version}</b>.
|
||||||
This version is compatible with ${changelog}, loaded <i>.map</i> files will be auto-updated.
|
This version is compatible with ${changelog}, loaded <i>.map</i> files will be auto-updated.
|
||||||
<ul>Main changes:
|
<ul>Main changes:
|
||||||
|
<li>Ability to limit military units by biome, state, culture and religion</li>
|
||||||
<li>New marker types</li>
|
<li>New marker types</li>
|
||||||
<li>New markers editor</li>
|
<li>New markers editor</li>
|
||||||
<li>Markers overview screen</li>
|
<li>Markers overview screen</li>
|
||||||
|
|
@ -416,7 +421,7 @@ function showWelcomeMessage() {
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<p>Join our ${discord} and ${reddit} to ask questions, share maps, discuss the Generator and Worlbuilding, report bugs and propose new features.</p>
|
<p>Join our ${discord} and ${reddit} to ask questions, share maps, discuss the Generator and Worlbuilding, report bugs and propose new features.</p>
|
||||||
<span>Thanks for all supporters on ${patreon}!</i></span>`;
|
<span>Thanks for all supporters on <a href="https://www.patreon.com/azgaar" onclick="track('click', 'patreon from update')" target="_blank">Patreon</a>!</i></span>`;
|
||||||
|
|
||||||
$("#alert").dialog({
|
$("#alert").dialog({
|
||||||
resizable: false,
|
resizable: false,
|
||||||
|
|
@ -951,7 +956,7 @@ function calculateTemperatures() {
|
||||||
const lat = Math.abs(mapCoordinates.latN - (y / graphHeight) * mapCoordinates.latT); // [0; 90]
|
const lat = Math.abs(mapCoordinates.latN - (y / graphHeight) * mapCoordinates.latT); // [0; 90]
|
||||||
const initTemp = tEq - int(lat / 90) * tDelta;
|
const initTemp = tEq - int(lat / 90) * tDelta;
|
||||||
for (let i = r; i < r + grid.cellsX; i++) {
|
for (let i = r; i < r + grid.cellsX; i++) {
|
||||||
cells.temp[i] = Math.max(Math.min(initTemp - convertToFriendly(cells.h[i]), 127), -128);
|
cells.temp[i] = minmax(initTemp - convertToFriendly(cells.h[i]), -128, 127);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -1066,7 +1071,7 @@ function generatePrecipitation() {
|
||||||
const precipitation = isPassable ? getPrecipitation(humidity, current, next) : humidity;
|
const precipitation = isPassable ? getPrecipitation(humidity, current, next) : humidity;
|
||||||
cells.prec[current] += precipitation;
|
cells.prec[current] += precipitation;
|
||||||
const evaporation = precipitation > 1.5 ? 1 : 0; // some humidity evaporates back to the atmosphere
|
const evaporation = precipitation > 1.5 ? 1 : 0; // some humidity evaporates back to the atmosphere
|
||||||
humidity = isPassable ? Math.min(Math.max(humidity - precipitation + evaporation, 0), maxPrec) : 0;
|
humidity = isPassable ? minmax(humidity - precipitation + evaporation, 0, maxPrec) : 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1075,7 +1080,7 @@ function generatePrecipitation() {
|
||||||
const normalLoss = Math.max(humidity / (10 * modifier), 1); // precipitation in normal conditions
|
const normalLoss = Math.max(humidity / (10 * modifier), 1); // precipitation in normal conditions
|
||||||
const diff = Math.max(cells.h[i + n] - cells.h[i], 0); // difference in height
|
const diff = Math.max(cells.h[i + n] - cells.h[i], 0); // difference in height
|
||||||
const mod = (cells.h[i + n] / 70) ** 2; // 50 stands for hills, 70 for mountains
|
const mod = (cells.h[i + n] / 70) ** 2; // 50 stands for hills, 70 for mountains
|
||||||
return Math.min(Math.max(normalLoss + diff * mod, 1), humidity);
|
return minmax(normalLoss + diff * mod, 1, humidity);
|
||||||
}
|
}
|
||||||
|
|
||||||
void (function drawWindDirection() {
|
void (function drawWindDirection() {
|
||||||
|
|
@ -1854,7 +1859,6 @@ function showStatistics() {
|
||||||
mapId = Date.now(); // unique map id is it's creation date number
|
mapId = Date.now(); // unique map id is it's creation date number
|
||||||
mapHistory.push({seed, width: graphWidth, height: graphHeight, template, created: mapId});
|
mapHistory.push({seed, width: graphWidth, height: graphHeight, template, created: mapId});
|
||||||
INFO && console.log(stats);
|
INFO && console.log(stats);
|
||||||
track("generate", `Template: ${template} ${templateRandom}. Points: ${pointsInput.dataset.cells}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const regenerateMap = debounce(function (source) {
|
const regenerateMap = debounce(function (source) {
|
||||||
|
|
|
||||||
|
|
@ -416,7 +416,7 @@ window.BurgsAndStates = (function () {
|
||||||
function getRiverCost(r, i, type) {
|
function getRiverCost(r, i, type) {
|
||||||
if (type === "River") return r ? 0 : 100; // penalty for river cultures
|
if (type === "River") return r ? 0 : 100; // penalty for river cultures
|
||||||
if (!r) return 0; // no penalty for others if there is no river
|
if (!r) return 0; // no penalty for others if there is no river
|
||||||
return Math.min(Math.max(cells.fl[i] / 10, 20), 100); // river penalty from 20 to 100 based on flux
|
return minmax(cells.fl[i] / 10, 20, 100); // river penalty from 20 to 100 based on flux
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTypeCost(t, type) {
|
function getTypeCost(t, type) {
|
||||||
|
|
@ -606,15 +606,15 @@ window.BurgsAndStates = (function () {
|
||||||
if (pathLength < s.name.length) {
|
if (pathLength < s.name.length) {
|
||||||
// only short name will fit
|
// only short name will fit
|
||||||
lines = splitInTwo(s.name);
|
lines = splitInTwo(s.name);
|
||||||
ratio = Math.max(Math.min(rn((pathLength / lines[0].length) * 60), 150), 50);
|
ratio = minmax(rn((pathLength / lines[0].length) * 60), 50, 150);
|
||||||
} else if (pathLength > s.fullName.length * 2.5) {
|
} else if (pathLength > s.fullName.length * 2.5) {
|
||||||
// full name will fit in one line
|
// full name will fit in one line
|
||||||
lines = [s.fullName];
|
lines = [s.fullName];
|
||||||
ratio = Math.max(Math.min(rn((pathLength / lines[0].length) * 70), 170), 70);
|
ratio = minmax(rn((pathLength / lines[0].length) * 70), 70, 170);
|
||||||
} else {
|
} else {
|
||||||
// try miltilined label
|
// try miltilined label
|
||||||
lines = splitInTwo(s.fullName);
|
lines = splitInTwo(s.fullName);
|
||||||
ratio = Math.max(Math.min(rn((pathLength / lines[0].length) * 60), 150), 70);
|
ratio = minmax(rn((pathLength / lines[0].length) * 60), 70, 150);
|
||||||
}
|
}
|
||||||
|
|
||||||
// prolongate path if it's too short
|
// prolongate path if it's too short
|
||||||
|
|
@ -665,7 +665,7 @@ window.BurgsAndStates = (function () {
|
||||||
example.text(name);
|
example.text(name);
|
||||||
const left = example.node().getBBox().width / -2; // x offset
|
const left = example.node().getBBox().width / -2; // x offset
|
||||||
el.innerHTML = `<tspan x="${left}px">${name}</tspan>`;
|
el.innerHTML = `<tspan x="${left}px">${name}</tspan>`;
|
||||||
ratio = Math.max(Math.min(rn((pathLength / name.length) * 60), 130), 40);
|
ratio = minmax(rn((pathLength / name.length) * 60), 40, 130);
|
||||||
el.setAttribute("font-size", ratio + "%");
|
el.setAttribute("font-size", ratio + "%");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -738,21 +738,24 @@ window.BurgsAndStates = (function () {
|
||||||
TIME && console.timeEnd("assignColors");
|
TIME && console.timeEnd("assignColors");
|
||||||
};
|
};
|
||||||
|
|
||||||
// generate historical conflicts of each state
|
|
||||||
const generateCampaigns = function () {
|
|
||||||
const wars = {War: 6, Conflict: 2, Campaign: 4, Invasion: 2, Rebellion: 2, Conquest: 2, Intervention: 1, Expedition: 1, Crusade: 1};
|
const wars = {War: 6, Conflict: 2, Campaign: 4, Invasion: 2, Rebellion: 2, Conquest: 2, Intervention: 1, Expedition: 1, Crusade: 1};
|
||||||
|
const generateCampaign = state => {
|
||||||
pack.states.forEach(s => {
|
const neighbors = state.neighbors.length ? state.neighbors : [0];
|
||||||
if (!s.i || s.removed) return;
|
return neighbors
|
||||||
const n = s.neighbors.length ? s.neighbors : [0];
|
|
||||||
s.campaigns = n
|
|
||||||
.map(i => {
|
.map(i => {
|
||||||
const name = i && P(0.8) ? pack.states[i].name : Names.getCultureShort(s.culture);
|
const name = i && P(0.8) ? pack.states[i].name : Names.getCultureShort(state.culture);
|
||||||
const start = gauss(options.year - 100, 150, 1, options.year - 6);
|
const start = gauss(options.year - 100, 150, 1, options.year - 6);
|
||||||
const end = start + gauss(4, 5, 1, options.year - start - 1);
|
const end = start + gauss(4, 5, 1, options.year - start - 1);
|
||||||
return {name: getAdjective(name) + " " + rw(wars), start, end};
|
return {name: getAdjective(name) + " " + rw(wars), start, end};
|
||||||
})
|
})
|
||||||
.sort((a, b) => a.start - b.start);
|
.sort((a, b) => a.start - b.start);
|
||||||
|
};
|
||||||
|
|
||||||
|
// generate historical conflicts of each state
|
||||||
|
const generateCampaigns = function () {
|
||||||
|
pack.states.forEach(s => {
|
||||||
|
if (!s.i || s.removed) return;
|
||||||
|
s.campaigns = generateCampaign(s);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -947,7 +950,17 @@ window.BurgsAndStates = (function () {
|
||||||
});
|
});
|
||||||
|
|
||||||
const monarchy = ["Duchy", "Grand Duchy", "Principality", "Kingdom", "Empire"]; // per expansionism tier
|
const monarchy = ["Duchy", "Grand Duchy", "Principality", "Kingdom", "Empire"]; // per expansionism tier
|
||||||
const republic = {Republic: 75, Federation: 4, Oligarchy: 2, "Most Serene Republic": 2, Tetrarchy: 1, Triumvirate: 1, Diarchy: 1, "Trade Company": 4, Junta: 1}; // weighted random
|
const republic = {
|
||||||
|
Republic: 75,
|
||||||
|
Federation: 4,
|
||||||
|
Oligarchy: 2,
|
||||||
|
"Most Serene Republic": 2,
|
||||||
|
Tetrarchy: 1,
|
||||||
|
Triumvirate: 1,
|
||||||
|
Diarchy: 1,
|
||||||
|
"Trade Company": 4,
|
||||||
|
Junta: 1
|
||||||
|
}; // weighted random
|
||||||
const union = {Union: 3, League: 4, Confederation: 1, "United Kingdom": 1, "United Republic": 1, "United Provinces": 2, Commonwealth: 1, Heptarchy: 1}; // weighted random
|
const union = {Union: 3, League: 4, Confederation: 1, "United Kingdom": 1, "United Republic": 1, "United Provinces": 2, Commonwealth: 1, Heptarchy: 1}; // weighted random
|
||||||
const theocracy = {Theocracy: 20, Brotherhood: 1, Thearchy: 2, See: 1, "Holy State": 1};
|
const theocracy = {Theocracy: 20, Brotherhood: 1, Thearchy: 2, See: 1, "Holy State": 1};
|
||||||
const anarchy = {"Free Territory": 2, Council: 3, Commune: 1, Community: 1};
|
const anarchy = {"Free Territory": 2, Council: 3, Commune: 1, Community: 1};
|
||||||
|
|
@ -957,7 +970,8 @@ window.BurgsAndStates = (function () {
|
||||||
const tier = expTiers[s.i];
|
const tier = expTiers[s.i];
|
||||||
|
|
||||||
const religion = pack.cells.religion[s.center];
|
const religion = pack.cells.religion[s.center];
|
||||||
const isTheocracy = (religion && pack.religions[religion].expansion === "state") || (P(0.1) && ["Organized", "Cult"].includes(pack.religions[religion].type));
|
const isTheocracy =
|
||||||
|
(religion && pack.religions[religion].expansion === "state") || (P(0.1) && ["Organized", "Cult"].includes(pack.religions[religion].type));
|
||||||
const isAnarchy = P(0.01 - tier / 500);
|
const isAnarchy = P(0.01 - tier / 500);
|
||||||
|
|
||||||
if (isTheocracy) s.form = "Theocracy";
|
if (isTheocracy) s.form = "Theocracy";
|
||||||
|
|
@ -1025,7 +1039,25 @@ window.BurgsAndStates = (function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
// state forms requiring Adjective + Name, all other forms use scheme Form + Of + Name
|
// state forms requiring Adjective + Name, all other forms use scheme Form + Of + Name
|
||||||
const adjForms = ["Empire", "Sultanate", "Khaganate", "Shogunate", "Caliphate", "Despotate", "Theocracy", "Oligarchy", "Union", "Confederation", "Trade Company", "League", "Tetrarchy", "Triumvirate", "Diarchy", "Horde", "Marches"];
|
const adjForms = [
|
||||||
|
"Empire",
|
||||||
|
"Sultanate",
|
||||||
|
"Khaganate",
|
||||||
|
"Shogunate",
|
||||||
|
"Caliphate",
|
||||||
|
"Despotate",
|
||||||
|
"Theocracy",
|
||||||
|
"Oligarchy",
|
||||||
|
"Union",
|
||||||
|
"Confederation",
|
||||||
|
"Trade Company",
|
||||||
|
"League",
|
||||||
|
"Tetrarchy",
|
||||||
|
"Triumvirate",
|
||||||
|
"Diarchy",
|
||||||
|
"Horde",
|
||||||
|
"Marches"
|
||||||
|
];
|
||||||
|
|
||||||
const getFullName = function (s) {
|
const getFullName = function (s) {
|
||||||
if (!s.formName) return s.name;
|
if (!s.formName) return s.name;
|
||||||
|
|
@ -1223,5 +1255,23 @@ window.BurgsAndStates = (function () {
|
||||||
TIME && console.timeEnd("generateProvinces");
|
TIME && console.timeEnd("generateProvinces");
|
||||||
};
|
};
|
||||||
|
|
||||||
return {generate, expandStates, normalizeStates, assignColors, drawBurgs, specifyBurgs, defineBurgFeatures, getType, drawStateLabels, collectStatistics, generateCampaigns, generateDiplomacy, defineStateForms, getFullName, generateProvinces, updateCultures};
|
return {
|
||||||
|
generate,
|
||||||
|
expandStates,
|
||||||
|
normalizeStates,
|
||||||
|
assignColors,
|
||||||
|
drawBurgs,
|
||||||
|
specifyBurgs,
|
||||||
|
defineBurgFeatures,
|
||||||
|
getType,
|
||||||
|
drawStateLabels,
|
||||||
|
collectStatistics,
|
||||||
|
generateCampaign,
|
||||||
|
generateCampaigns,
|
||||||
|
generateDiplomacy,
|
||||||
|
defineStateForms,
|
||||||
|
getFullName,
|
||||||
|
generateProvinces,
|
||||||
|
updateCultures
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -117,7 +117,8 @@ window.Cultures = (function () {
|
||||||
if (cells.h[i] > 50) return "Highland"; // no penalty for hills and moutains, high for other elevations
|
if (cells.h[i] > 50) return "Highland"; // no penalty for hills and moutains, high for other elevations
|
||||||
const f = pack.features[cells.f[cells.haven[i]]]; // opposite feature
|
const f = pack.features[cells.f[cells.haven[i]]]; // opposite feature
|
||||||
if (f.type === "lake" && f.cells > 5) return "Lake"; // low water cross penalty and high for growth not along coastline
|
if (f.type === "lake" && f.cells > 5) return "Lake"; // low water cross penalty and high for growth not along coastline
|
||||||
if ((cells.harbor[i] && f.type !== "lake" && P(0.1)) || (cells.harbor[i] === 1 && P(0.6)) || (pack.features[cells.f[i]].group === "isle" && P(0.4))) return "Naval"; // low water cross penalty and high for non-along-coastline growth
|
if ((cells.harbor[i] && f.type !== "lake" && P(0.1)) || (cells.harbor[i] === 1 && P(0.6)) || (pack.features[cells.f[i]].group === "isle" && P(0.4)))
|
||||||
|
return "Naval"; // low water cross penalty and high for non-along-coastline growth
|
||||||
if (cells.r[i] && cells.fl[i] > 100) return "River"; // no River cross penalty, penalty for non-River growth
|
if (cells.r[i] && cells.fl[i] > 100) return "River"; // no River cross penalty, penalty for non-River growth
|
||||||
if (cells.t[i] > 2 && [3, 7, 8, 9, 10, 12].includes(cells.biome[i])) return "Hunting"; // high penalty in non-native biomes
|
if (cells.t[i] > 2 && [3, 7, 8, 9, 10, 12].includes(cells.biome[i])) return "Hunting"; // high penalty in non-native biomes
|
||||||
return "Generic";
|
return "Generic";
|
||||||
|
|
@ -435,7 +436,7 @@ window.Cultures = (function () {
|
||||||
function getRiverCost(r, i, type) {
|
function getRiverCost(r, i, type) {
|
||||||
if (type === "River") return r ? 0 : 100; // penalty for river cultures
|
if (type === "River") return r ? 0 : 100; // penalty for river cultures
|
||||||
if (!r) return 0; // no penalty for others if there is no river
|
if (!r) return 0; // no penalty for others if there is no river
|
||||||
return Math.min(Math.max(cells.fl[i] / 10, 20), 100); // river penalty from 20 to 100 based on flux
|
return minmax(cells.fl[i] / 10, 20, 100); // river penalty from 20 to 100 based on flux
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTypeCost(t, type) {
|
function getTypeCost(t, type) {
|
||||||
|
|
|
||||||
|
|
@ -225,8 +225,8 @@ function parseLoadedData(data) {
|
||||||
if (settings[11]) barPosY.value = settings[11];
|
if (settings[11]) barPosY.value = settings[11];
|
||||||
if (settings[12]) populationRate = populationRateInput.value = populationRateOutput.value = settings[12];
|
if (settings[12]) populationRate = populationRateInput.value = populationRateOutput.value = settings[12];
|
||||||
if (settings[13]) urbanization = urbanizationInput.value = urbanizationOutput.value = settings[13];
|
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[14]) mapSizeInput.value = mapSizeOutput.value = minmax(settings[14], 1, 100);
|
||||||
if (settings[15]) latitudeInput.value = latitudeOutput.value = Math.max(Math.min(settings[15], 100), 0);
|
if (settings[15]) latitudeInput.value = latitudeOutput.value = minmax(settings[15], 0, 100);
|
||||||
if (settings[16]) temperatureEquatorInput.value = temperatureEquatorOutput.value = settings[16];
|
if (settings[16]) temperatureEquatorInput.value = temperatureEquatorOutput.value = settings[16];
|
||||||
if (settings[17]) temperaturePoleInput.value = temperaturePoleOutput.value = settings[17];
|
if (settings[17]) temperaturePoleInput.value = temperaturePoleOutput.value = settings[17];
|
||||||
if (settings[18]) precInput.value = precOutput.value = settings[18];
|
if (settings[18]) precInput.value = precOutput.value = settings[18];
|
||||||
|
|
@ -234,7 +234,7 @@ function parseLoadedData(data) {
|
||||||
if (settings[20]) mapName.value = settings[20];
|
if (settings[20]) mapName.value = settings[20];
|
||||||
if (settings[21]) hideLabels.checked = +settings[21];
|
if (settings[21]) hideLabels.checked = +settings[21];
|
||||||
if (settings[22]) stylePreset.value = settings[22];
|
if (settings[22]) stylePreset.value = settings[22];
|
||||||
if (settings[23]) rescaleLabels.checked = settings[23];
|
if (settings[23]) rescaleLabels.checked = +settings[23];
|
||||||
})();
|
})();
|
||||||
|
|
||||||
void (function parseConfiguration() {
|
void (function parseConfiguration() {
|
||||||
|
|
@ -875,16 +875,23 @@ function parseLoadedData(data) {
|
||||||
const defs = document.getElementById("defs-markers");
|
const defs = document.getElementById("defs-markers");
|
||||||
const markersGroup = document.getElementById("markers");
|
const markersGroup = document.getElementById("markers");
|
||||||
const markerElements = markersGroup.querySelectorAll("use");
|
const markerElements = markersGroup.querySelectorAll("use");
|
||||||
|
const rescale = +markersGroup.getAttribute("rescale");
|
||||||
|
|
||||||
pack.markers = Array.from(markerElements).map((el, i) => {
|
pack.markers = Array.from(markerElements).map((el, i) => {
|
||||||
const id = el.getAttribute("id");
|
const id = el.getAttribute("id");
|
||||||
const note = notes.find(note => note.id === id);
|
const note = notes.find(note => note.id === id);
|
||||||
if (note) note.id = `marker${i}`;
|
if (note) note.id = `marker${i}`;
|
||||||
|
|
||||||
const x = rn(+el.dataset.x, 1);
|
let x = +el.dataset.x;
|
||||||
const y = rn(+el.dataset.y, 1);
|
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 cell = findCell(x, y);
|
||||||
const size = rn(el.dataset.size * 30, 1);
|
const size = rn(rescale ? el.dataset.size * 30 : el.getAttribute("width"), 1);
|
||||||
|
|
||||||
const href = el.href.baseVal;
|
const href = el.href.baseVal;
|
||||||
const type = href.replace("#marker_", "");
|
const type = href.replace("#marker_", "");
|
||||||
|
|
@ -906,6 +913,7 @@ function parseLoadedData(data) {
|
||||||
if (!isNaN(dy) && dy !== 50) marker.dy = dy;
|
if (!isNaN(dy) && dy !== 50) marker.dy = dy;
|
||||||
if (fill && fill !== "#ffffff") marker.fill = fill;
|
if (fill && fill !== "#ffffff") marker.fill = fill;
|
||||||
if (stroke && stroke !== "#000000") marker.stroke = stroke;
|
if (stroke && stroke !== "#000000") marker.stroke = stroke;
|
||||||
|
if (circle.getAttribute("opacity") === "0") marker.pin = "no";
|
||||||
|
|
||||||
return marker;
|
return marker;
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -46,9 +46,11 @@ window.Markers = (function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
const regenerate = () => {
|
const regenerate = () => {
|
||||||
pack.markers = pack.markers.filter(({i, lock}) => {
|
pack.markers = pack.markers.filter(({i, lock, cell}) => {
|
||||||
if (lock) return true;
|
if (lock) {
|
||||||
|
occupied[cell] = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
const id = `marker${i}`;
|
const id = `marker${i}`;
|
||||||
document.getElementById(id)?.remove();
|
document.getElementById(id)?.remove();
|
||||||
const index = notes.findIndex(note => note.id === id);
|
const index = notes.findIndex(note => note.id === id);
|
||||||
|
|
@ -110,12 +112,11 @@ window.Markers = (function () {
|
||||||
const {cells} = pack;
|
const {cells} = pack;
|
||||||
|
|
||||||
let mountains = Array.from(cells.i.filter(i => !occupied[i] && cells.h[i] >= 70).sort((a, b) => cells.h[b] - cells.h[a]));
|
let mountains = Array.from(cells.i.filter(i => !occupied[i] && cells.h[i] >= 70).sort((a, b) => cells.h[b] - cells.h[a]));
|
||||||
let quantity = getQuantity(mountains, 10, 300, multiplier);
|
let quantity = getQuantity(mountains, 10, 500, multiplier);
|
||||||
if (!quantity) return;
|
if (!quantity) return;
|
||||||
const highestMountains = mountains.slice(0, 20);
|
|
||||||
|
|
||||||
while (quantity) {
|
while (quantity) {
|
||||||
const [cell] = extractAnyElement(highestMountains);
|
const [cell] = extractAnyElement(mountains);
|
||||||
const id = addMarker({cell, icon, type, dx: 52, px: 13});
|
const id = addMarker({cell, icon, type, dx: 52, px: 13});
|
||||||
const proper = Names.getCulture(cells.culture[cell]);
|
const proper = Names.getCulture(cells.culture[cell]);
|
||||||
const name = P(0.3) ? "Mount " + proper : Math.random() > 0.3 ? proper + " Volcano" : proper;
|
const name = P(0.3) ? "Mount " + proper : Math.random() > 0.3 ? proper + " Volcano" : proper;
|
||||||
|
|
@ -128,12 +129,11 @@ window.Markers = (function () {
|
||||||
const {cells} = pack;
|
const {cells} = pack;
|
||||||
|
|
||||||
let springs = Array.from(cells.i.filter(i => !occupied[i] && cells.h[i] > 50).sort((a, b) => cells.h[b] - cells.h[a]));
|
let springs = Array.from(cells.i.filter(i => !occupied[i] && cells.h[i] > 50).sort((a, b) => cells.h[b] - cells.h[a]));
|
||||||
let quantity = getQuantity(springs, 30, 800, multiplier);
|
let quantity = getQuantity(springs, 30, 1200, multiplier);
|
||||||
if (!quantity) return;
|
if (!quantity) return;
|
||||||
const highestSprings = springs.slice(0, 40);
|
|
||||||
|
|
||||||
while (quantity) {
|
while (quantity) {
|
||||||
const [cell] = extractAnyElement(highestSprings);
|
const [cell] = extractAnyElement(springs);
|
||||||
const id = addMarker({cell, icon, type, dy: 52});
|
const id = addMarker({cell, icon, type, dy: 52});
|
||||||
const proper = Names.getCulture(cells.culture[cell]);
|
const proper = Names.getCulture(cells.culture[cell]);
|
||||||
const temp = convertTemperature(gauss(35, 15, 20, 100));
|
const temp = convertTemperature(gauss(35, 15, 20, 100));
|
||||||
|
|
@ -181,7 +181,16 @@ window.Markers = (function () {
|
||||||
const river = pack.rivers.find(r => r.i === pack.cells.r[cell]);
|
const river = pack.rivers.find(r => r.i === pack.cells.r[cell]);
|
||||||
const riverName = river ? `${river.name} ${river.type}` : "river";
|
const riverName = river ? `${river.name} ${river.type}` : "river";
|
||||||
const name = river && P(0.2) ? river.name : burg.name;
|
const name = river && P(0.2) ? river.name : burg.name;
|
||||||
notes.push({id, name: `${name} Bridge`, legend: `A stone bridge over the ${riverName} near ${burg.name}`});
|
const weightedAdjectives = {
|
||||||
|
stone: 10,
|
||||||
|
wooden: 1,
|
||||||
|
lengthy: 2,
|
||||||
|
formidable: 2,
|
||||||
|
rickety: 1,
|
||||||
|
beaten: 1,
|
||||||
|
weathered: 1
|
||||||
|
};
|
||||||
|
notes.push({id, name: `${name} Bridge`, legend: `A ${rw(weightedAdjectives)} bridge spans over the ${riverName} near ${burg.name}`});
|
||||||
quantity--;
|
quantity--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -448,7 +457,7 @@ window.Markers = (function () {
|
||||||
const [cell] = extractAnyElement(lighthouses);
|
const [cell] = extractAnyElement(lighthouses);
|
||||||
const id = addMarker({cell, icon, type, px: 14});
|
const id = addMarker({cell, icon, type, px: 14});
|
||||||
const proper = cells.burg[cell] ? pack.burgs[cells.burg[cell]].name : Names.getCulture(cells.culture[cell]);
|
const proper = cells.burg[cell] ? pack.burgs[cells.burg[cell]].name : Names.getCulture(cells.culture[cell]);
|
||||||
notes.push({id, name: getAdjective(proper) + " Lighthouse" + name, legend: `A lighthouse to keep the navigation safe`});
|
notes.push({id, name: getAdjective(proper) + " Lighthouse" + name, legend: `A lighthouse to serve as a beacon for ships in the open sea`});
|
||||||
quantity--;
|
quantity--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -460,11 +469,19 @@ window.Markers = (function () {
|
||||||
const quantity = getQuantity(waterfalls, 1, 5, multiplier);
|
const quantity = getQuantity(waterfalls, 1, 5, multiplier);
|
||||||
if (!quantity) return;
|
if (!quantity) return;
|
||||||
|
|
||||||
|
const descriptions = [
|
||||||
|
"A gorgeous waterfall flows here",
|
||||||
|
"The rapids of an exceptionally beautiful waterfall",
|
||||||
|
"An impressive waterfall has cut through the land",
|
||||||
|
"The cascades of a stunning waterfall",
|
||||||
|
"A river drops down from a great height forming a wonderous waterfall",
|
||||||
|
"A breathtaking waterfall cuts through the landscape"
|
||||||
|
];
|
||||||
for (let i = 0; i < waterfalls.length && i < quantity; i++) {
|
for (let i = 0; i < waterfalls.length && i < quantity; i++) {
|
||||||
const cell = waterfalls[i];
|
const cell = waterfalls[i];
|
||||||
const id = addMarker({cell, icon, type, dy: 54, px: 16});
|
const id = addMarker({cell, icon, type, dy: 54, px: 16});
|
||||||
const proper = cells.burg[cell] ? pack.burgs[cells.burg[cell]].name : Names.getCulture(cells.culture[cell]);
|
const proper = cells.burg[cell] ? pack.burgs[cells.burg[cell]].name : Names.getCulture(cells.culture[cell]);
|
||||||
notes.push({id, name: getAdjective(proper) + " Waterfall" + name, legend: `An extremely beautiful waterfall`});
|
notes.push({id, name: getAdjective(proper) + " Waterfall" + name, legend: `${ra(descriptions)}`});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -478,7 +495,9 @@ window.Markers = (function () {
|
||||||
while (quantity && battlefields.length) {
|
while (quantity && battlefields.length) {
|
||||||
const [cell] = extractAnyElement(battlefields);
|
const [cell] = extractAnyElement(battlefields);
|
||||||
const id = addMarker({cell, icon, type, dy: 52});
|
const id = addMarker({cell, icon, type, dy: 52});
|
||||||
const campaign = ra(states[cells.state[cell]].campaigns);
|
const state = states[cells.state[cell]];
|
||||||
|
if (!state.campaigns) state.campaigns = BurgsAndStates.generateCampaign(state);
|
||||||
|
const campaign = ra(state.campaigns);
|
||||||
const date = generateDate(campaign.start, campaign.end);
|
const date = generateDate(campaign.start, campaign.end);
|
||||||
const name = Names.getCulture(cells.culture[cell]) + " Battlefield";
|
const name = Names.getCulture(cells.culture[cell]) + " Battlefield";
|
||||||
const legend = `A historical battle of the ${campaign.name}. \r\nDate: ${date} ${options.era}`;
|
const legend = `A historical battle of the ${campaign.name}. \r\nDate: ${date} ${options.era}`;
|
||||||
|
|
@ -519,7 +538,7 @@ window.Markers = (function () {
|
||||||
const id = addMarker({cell, icon, type, dy: 48});
|
const id = addMarker({cell, icon, type, dy: 48});
|
||||||
const name = `${lake.name} Monster`;
|
const name = `${lake.name} Monster`;
|
||||||
const length = gauss(10, 5, 5, 100);
|
const length = gauss(10, 5, 5, 100);
|
||||||
const legend = `Rumors said a relic monster of ${length} ${heightUnit.value} long inhabits ${lake.name} Lake. Truth or lie, but folks are afraid to fish in the lake`;
|
const legend = `Rumors say a relic monster of ${length} ${heightUnit.value} long inhabits ${lake.name} Lake. Truth or lie, folks are afraid to fish in the lake`;
|
||||||
notes.push({id, name, legend});
|
notes.push({id, name, legend});
|
||||||
quantity--;
|
quantity--;
|
||||||
}
|
}
|
||||||
|
|
@ -550,15 +569,54 @@ window.Markers = (function () {
|
||||||
let quantity = getQuantity(hills, 30, 600, multiplier);
|
let quantity = getQuantity(hills, 30, 600, multiplier);
|
||||||
if (!quantity) return;
|
if (!quantity) return;
|
||||||
|
|
||||||
const subjects = ["Locals", "Old folks", "Old books", "Tipplers"];
|
const adjectives = [
|
||||||
const species = ["Ogre", "Troll", "Cyclops", "Giant", "Monster", "Beast", "Dragon", "Undead", "Ghoul", "Vampire"];
|
"great",
|
||||||
|
"big",
|
||||||
|
"huge",
|
||||||
|
"prime",
|
||||||
|
"golden",
|
||||||
|
"proud",
|
||||||
|
"lucky",
|
||||||
|
"fat",
|
||||||
|
"giant",
|
||||||
|
"hungry",
|
||||||
|
"magical",
|
||||||
|
"superior",
|
||||||
|
"terrifying",
|
||||||
|
"horrifying",
|
||||||
|
"feared"
|
||||||
|
];
|
||||||
|
const subjects = ["Locals", "Elders", "Inscriptions", "Tipplers", "Legends", "Whispers", "Rumors", "Journeying folk", "Tales"];
|
||||||
|
const species = [
|
||||||
|
"Ogre",
|
||||||
|
"Troll",
|
||||||
|
"Cyclops",
|
||||||
|
"Giant",
|
||||||
|
"Monster",
|
||||||
|
"Beast",
|
||||||
|
"Dragon",
|
||||||
|
"Undead",
|
||||||
|
"Ghoul",
|
||||||
|
"Vampire",
|
||||||
|
"Hag",
|
||||||
|
"Banshee",
|
||||||
|
"Bearded Devil",
|
||||||
|
"Roc",
|
||||||
|
"Hydra",
|
||||||
|
"Warg"
|
||||||
|
];
|
||||||
const modusOperandi = [
|
const modusOperandi = [
|
||||||
"steals their cattle",
|
"steals cattle at night",
|
||||||
"doesn't mind eating children",
|
"prefers eating children",
|
||||||
"doesn't mind of human flesh",
|
"doesn't mind of human flesh",
|
||||||
"keeps the region at bay",
|
"keeps the region at bay",
|
||||||
"eats their kids",
|
"eats kids whole",
|
||||||
"abducts young women"
|
"abducts young women",
|
||||||
|
"terrorizes the region",
|
||||||
|
"harasses travelers in the area",
|
||||||
|
"snatches people from homes",
|
||||||
|
"attacks anyone who dares to approach its lair",
|
||||||
|
"attacks unsuspecting victims"
|
||||||
];
|
];
|
||||||
|
|
||||||
while (quantity) {
|
while (quantity) {
|
||||||
|
|
@ -567,7 +625,7 @@ window.Markers = (function () {
|
||||||
const monster = ra(species);
|
const monster = ra(species);
|
||||||
const toponym = Names.getCulture(cells.culture[cell]);
|
const toponym = Names.getCulture(cells.culture[cell]);
|
||||||
const name = `${toponym} ${monster}`;
|
const name = `${toponym} ${monster}`;
|
||||||
const legend = `${ra(subjects)} tell tales of an old ${monster} who inhabits ${toponym} hills and ${ra(modusOperandi)}`;
|
const legend = `${ra(subjects)} speak of a ${ra(adjectives)} ${monster} who inhabits ${toponym} hills and ${ra(modusOperandi)}`;
|
||||||
notes.push({id, name, legend});
|
notes.push({id, name, legend});
|
||||||
quantity--;
|
quantity--;
|
||||||
}
|
}
|
||||||
|
|
@ -737,7 +795,7 @@ window.Markers = (function () {
|
||||||
let quantity = getQuantity(statues, 80, 1200, multiplier);
|
let quantity = getQuantity(statues, 80, 1200, multiplier);
|
||||||
if (!quantity) return;
|
if (!quantity) return;
|
||||||
|
|
||||||
const variants = ["Statue", "Obelisk", "Monument", "Column", "Monolith", "Pillar", "Megalith", "Stele", "Runestone"];
|
const variants = ["Statue", "Obelisk", "Monument", "Column", "Monolith", "Pillar", "Megalith", "Stele", "Runestone", "Sculpture", "Effigy", "Idol"];
|
||||||
const scripts = {
|
const scripts = {
|
||||||
cypriot: "𐠁𐠂𐠃𐠄𐠅𐠈𐠊𐠋𐠌𐠍𐠎𐠏𐠐𐠑𐠒𐠓𐠔𐠕𐠖𐠗𐠘𐠙𐠚𐠛𐠜𐠝𐠞𐠟𐠠𐠡𐠢𐠣𐠤𐠥𐠦𐠧𐠨𐠩𐠪𐠫𐠬𐠭𐠮𐠯𐠰𐠱𐠲𐠳𐠴𐠵𐠷𐠸𐠼𐠿 ",
|
cypriot: "𐠁𐠂𐠃𐠄𐠅𐠈𐠊𐠋𐠌𐠍𐠎𐠏𐠐𐠑𐠒𐠓𐠔𐠕𐠖𐠗𐠘𐠙𐠚𐠛𐠜𐠝𐠞𐠟𐠠𐠡𐠢𐠣𐠤𐠥𐠦𐠧𐠨𐠩𐠪𐠫𐠬𐠭𐠮𐠯𐠰𐠱𐠲𐠳𐠴𐠵𐠷𐠸𐠼𐠿 ",
|
||||||
geez: "ሀለሐመሠረሰቀበተኀነአከወዐዘየደገጠጰጸፀፈፐ ",
|
geez: "ሀለሐመሠረሰቀበተኀነአከወዐዘየደገጠጰጸፀፈፐ ",
|
||||||
|
|
@ -771,7 +829,21 @@ window.Markers = (function () {
|
||||||
let quantity = getQuantity(ruins, 80, 1200, multiplier);
|
let quantity = getQuantity(ruins, 80, 1200, multiplier);
|
||||||
if (!quantity) return;
|
if (!quantity) return;
|
||||||
|
|
||||||
const types = ["City", "Town", "Pyramid", "Fort"];
|
const types = [
|
||||||
|
"City",
|
||||||
|
"Town",
|
||||||
|
"Settlement",
|
||||||
|
"Pyramid",
|
||||||
|
"Fort",
|
||||||
|
"Stronghold",
|
||||||
|
"Temple",
|
||||||
|
"Sacred site",
|
||||||
|
"Mausoleum",
|
||||||
|
"Outpost",
|
||||||
|
"Fortification",
|
||||||
|
"Fortress",
|
||||||
|
"Castle"
|
||||||
|
];
|
||||||
|
|
||||||
while (quantity) {
|
while (quantity) {
|
||||||
const [cell] = extractAnyElement(ruins);
|
const [cell] = extractAnyElement(ruins);
|
||||||
|
|
@ -779,7 +851,7 @@ window.Markers = (function () {
|
||||||
|
|
||||||
const ruinType = ra(types);
|
const ruinType = ra(types);
|
||||||
const name = `Ruined ${ruinType}`;
|
const name = `Ruined ${ruinType}`;
|
||||||
const legend = `Ruins of an ancient ${ruinType.toLowerCase()}. A good place for a treasures hunt`;
|
const legend = `Ruins of an ancient ${ruinType.toLowerCase()}. Untold riches may lie within.`;
|
||||||
notes.push({id, name, legend});
|
notes.push({id, name, legend});
|
||||||
quantity--;
|
quantity--;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,9 +3,8 @@
|
||||||
window.Military = (function () {
|
window.Military = (function () {
|
||||||
const generate = function () {
|
const generate = function () {
|
||||||
TIME && console.time("generateMilitaryForces");
|
TIME && console.time("generateMilitaryForces");
|
||||||
const cells = pack.cells,
|
const {cells, states} = pack;
|
||||||
p = cells.p,
|
const {p} = cells;
|
||||||
states = pack.states;
|
|
||||||
const valid = states.filter(s => s.i && !s.removed); // valid states
|
const valid = states.filter(s => s.i && !s.removed); // valid states
|
||||||
if (!options.military) options.military = getDefaultOptions();
|
if (!options.military) options.military = getDefaultOptions();
|
||||||
|
|
||||||
|
|
@ -19,7 +18,6 @@ window.Military = (function () {
|
||||||
mounted: {Nomadic: 2.3, Highland: 0.6, Lake: 0.7, Naval: 0.3, Hunting: 0.7, River: 0.8},
|
mounted: {Nomadic: 2.3, Highland: 0.6, Lake: 0.7, Naval: 0.3, Hunting: 0.7, River: 0.8},
|
||||||
machinery: {Nomadic: 0.8, Highland: 1.4, Lake: 1.1, Naval: 1.4, Hunting: 0.4, River: 1.1},
|
machinery: {Nomadic: 0.8, Highland: 1.4, Lake: 1.1, Naval: 1.4, Hunting: 0.4, River: 1.1},
|
||||||
naval: {Nomadic: 0.5, Highland: 0.5, Lake: 1.2, Naval: 1.8, Hunting: 0.7, River: 1.2},
|
naval: {Nomadic: 0.5, Highland: 0.5, Lake: 1.2, Naval: 1.8, Hunting: 0.7, River: 1.2},
|
||||||
// non-default generic:
|
|
||||||
armored: {Nomadic: 1, Highland: 0.5, Lake: 1, Naval: 1, Hunting: 0.7, River: 1.1},
|
armored: {Nomadic: 1, Highland: 0.5, Lake: 1, Naval: 1, Hunting: 0.7, River: 1.1},
|
||||||
aviation: {Nomadic: 0.5, Highland: 0.5, Lake: 1.2, Naval: 1.2, Hunting: 0.6, River: 1.2},
|
aviation: {Nomadic: 0.5, Highland: 0.5, Lake: 1.2, Naval: 1.2, Hunting: 0.6, River: 1.2},
|
||||||
magical: {Nomadic: 1, Highland: 2, Lake: 1, Naval: 1, Hunting: 1, River: 1}
|
magical: {Nomadic: 1, Highland: 2, Lake: 1, Naval: 1, Hunting: 1, River: 1}
|
||||||
|
|
@ -38,27 +36,24 @@ window.Military = (function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
valid.forEach(s => {
|
valid.forEach(s => {
|
||||||
const temp = (s.temp = {}),
|
s.temp = {};
|
||||||
d = s.diplomacy;
|
const d = s.diplomacy;
|
||||||
const expansionRate = Math.min(Math.max(s.expansionism / expn / (s.area / area), 0.25), 4); // how much state expansionism is realized
|
|
||||||
|
const expansionRate = minmax(s.expansionism / expn / (s.area / area), 0.25, 4); // how much state expansionism is realized
|
||||||
const diplomacyRate = d.some(d => d === "Enemy") ? 1 : d.some(d => d === "Rival") ? 0.8 : d.some(d => d === "Suspicion") ? 0.5 : 0.1; // peacefulness
|
const diplomacyRate = d.some(d => d === "Enemy") ? 1 : d.some(d => d === "Rival") ? 0.8 : d.some(d => d === "Suspicion") ? 0.5 : 0.1; // peacefulness
|
||||||
const neighborsRate = Math.min(
|
const neighborsRateRaw = s.neighbors.map(n => (n ? pack.states[n].diplomacy[s.i] : "Suspicion")).reduce((s, r) => (s += rate[r]), 0.5);
|
||||||
Math.max(
|
const neighborsRate = minmax(neighborsRateRaw, 0.3, 3); // neighbors rate
|
||||||
s.neighbors.map(n => (n ? pack.states[n].diplomacy[s.i] : "Suspicion")).reduce((s, r) => (s += rate[r]), 0.5),
|
s.alert = minmax(rn(expansionRate * diplomacyRate * neighborsRate, 2), 0.1, 5); // alert rate (area modifier)
|
||||||
0.3
|
s.temp.platoons = [];
|
||||||
),
|
|
||||||
3
|
|
||||||
); // neighbors rate
|
|
||||||
s.alert = Math.min(Math.max(rn(expansionRate * diplomacyRate * neighborsRate, 2), 0.1), 5); // war alert rate (army modifier)
|
|
||||||
temp.platoons = [];
|
|
||||||
|
|
||||||
// apply overall state modifiers for unit types based on state features
|
// apply overall state modifiers for unit types based on state features
|
||||||
for (const unit of options.military) {
|
for (const unit of options.military) {
|
||||||
if (!stateModifier[unit.type]) continue;
|
if (!stateModifier[unit.type]) continue;
|
||||||
|
|
||||||
let modifier = stateModifier[unit.type][s.type] || 1;
|
let modifier = stateModifier[unit.type][s.type] || 1;
|
||||||
if (unit.type === "mounted" && s.formName.includes("Horde")) modifier *= 2;
|
if (unit.type === "mounted" && s.formName.includes("Horde")) modifier *= 2;
|
||||||
else if (unit.type === "naval" && s.form === "Republic") modifier *= 1.2;
|
else if (unit.type === "naval" && s.form === "Republic") modifier *= 1.2;
|
||||||
temp[unit.name] = modifier * s.alert;
|
s.temp[unit.name] = modifier * s.alert;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -69,66 +64,91 @@ window.Military = (function () {
|
||||||
return "generic";
|
return "generic";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function passUnitLimits(unit, biome, state, culture, religion) {
|
||||||
|
if (unit.biomes && !unit.biomes.includes(biome)) return false;
|
||||||
|
if (unit.states && !unit.states.includes(state)) return false;
|
||||||
|
if (unit.cultures && !unit.cultures.includes(culture)) return false;
|
||||||
|
if (unit.religions && !unit.religions.includes(religion)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
for (const i of cells.i) {
|
for (const i of cells.i) {
|
||||||
if (!cells.pop[i]) continue;
|
if (!cells.pop[i]) continue;
|
||||||
const s = states[cells.state[i]]; // cell state
|
|
||||||
if (!s.i || s.removed) continue;
|
|
||||||
|
|
||||||
let m = cells.pop[i] / 100; // basic rural army in percentages
|
const biome = cells.biome[i];
|
||||||
if (cells.culture[i] !== s.culture) m = s.form === "Union" ? m / 1.2 : m / 2; // non-dominant culture
|
const state = cells.state[i];
|
||||||
if (cells.religion[i] !== cells.religion[s.center]) m = s.form === "Theocracy" ? m / 2.2 : m / 1.4; // non-dominant religion
|
const culture = cells.culture[i];
|
||||||
if (cells.f[i] !== cells.f[s.center]) m = s.type === "Naval" ? m / 1.2 : m / 1.8; // different landmass
|
const religion = cells.religion[i];
|
||||||
|
|
||||||
|
const stateObj = states[state];
|
||||||
|
if (!state || stateObj.removed) continue;
|
||||||
|
|
||||||
|
let modifier = cells.pop[i] / 100; // basic rural army in percentages
|
||||||
|
if (culture !== stateObj.culture) modifier = stateObj.form === "Union" ? modifier / 1.2 : modifier / 2; // non-dominant culture
|
||||||
|
if (religion !== cells.religion[stateObj.center]) modifier = stateObj.form === "Theocracy" ? modifier / 2.2 : modifier / 1.4; // non-dominant religion
|
||||||
|
if (cells.f[i] !== cells.f[stateObj.center]) modifier = stateObj.type === "Naval" ? modifier / 1.2 : modifier / 1.8; // different landmass
|
||||||
const type = getType(i);
|
const type = getType(i);
|
||||||
|
|
||||||
for (const u of options.military) {
|
for (const unit of options.military) {
|
||||||
const perc = +u.rural;
|
const perc = +unit.rural;
|
||||||
if (isNaN(perc) || perc <= 0 || !s.temp[u.name]) continue;
|
if (isNaN(perc) || perc <= 0 || !stateObj.temp[unit.name]) continue;
|
||||||
|
if (!passUnitLimits(unit, biome, state, culture, religion)) continue;
|
||||||
|
|
||||||
const mod = type === "generic" ? 1 : cellTypeModifier[type][u.type]; // cell specific modifier
|
const cellTypeMod = type === "generic" ? 1 : cellTypeModifier[type][unit.type]; // cell specific modifier
|
||||||
const army = m * perc * mod; // rural cell army
|
const army = modifier * perc * cellTypeMod; // rural cell army
|
||||||
const t = rn(army * s.temp[u.name] * populationRate); // total troops
|
const total = rn(army * stateObj.temp[unit.name] * populationRate); // total troops
|
||||||
if (!t) continue;
|
if (!total) continue;
|
||||||
let x = p[i][0],
|
|
||||||
y = p[i][1],
|
let [x, y] = p[i];
|
||||||
n = 0;
|
let n = 0;
|
||||||
if (u.type === "naval") {
|
|
||||||
|
if (unit.type === "naval") {
|
||||||
let haven = cells.haven[i];
|
let haven = cells.haven[i];
|
||||||
(x = p[haven][0]), (y = p[haven][1]);
|
[x, y] = p[haven];
|
||||||
n = 1;
|
n = 1;
|
||||||
} // place naval to sea
|
} // place naval to sea
|
||||||
s.temp.platoons.push({cell: i, a: t, t, x, y, u: u.name, n, s: u.separate, type: u.type});
|
|
||||||
|
stateObj.temp.platoons.push({cell: i, a: total, t: total, x, y, u: unit.name, n, s: unit.separate, type: unit.type});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const b of pack.burgs) {
|
for (const b of pack.burgs) {
|
||||||
if (!b.i || b.removed || !b.state || !b.population) continue;
|
if (!b.i || b.removed || !b.state || !b.population) continue;
|
||||||
const s = states[b.state]; // burg state
|
|
||||||
|
|
||||||
|
const biome = cells.biome[b.cell];
|
||||||
|
const state = b.state;
|
||||||
|
const culture = b.culture;
|
||||||
|
const religion = cells.religion[b.cell];
|
||||||
|
|
||||||
|
const stateObj = states[state];
|
||||||
let m = (b.population * urbanization) / 100; // basic urban army in percentages
|
let m = (b.population * urbanization) / 100; // basic urban army in percentages
|
||||||
if (b.capital) m *= 1.2; // capital has household troops
|
if (b.capital) m *= 1.2; // capital has household troops
|
||||||
if (b.culture !== s.culture) m = s.form === "Union" ? m / 1.2 : m / 2; // non-dominant culture
|
if (culture !== stateObj.culture) m = stateObj.form === "Union" ? m / 1.2 : m / 2; // non-dominant culture
|
||||||
if (cells.religion[b.cell] !== cells.religion[s.center]) m = s.form === "Theocracy" ? m / 2.2 : m / 1.4; // non-dominant religion
|
if (religion !== cells.religion[stateObj.center]) m = stateObj.form === "Theocracy" ? m / 2.2 : m / 1.4; // non-dominant religion
|
||||||
if (cells.f[b.cell] !== cells.f[s.center]) m = s.type === "Naval" ? m / 1.2 : m / 1.8; // different landmass
|
if (cells.f[b.cell] !== cells.f[stateObj.center]) m = stateObj.type === "Naval" ? m / 1.2 : m / 1.8; // different landmass
|
||||||
const type = getType(b.cell);
|
const type = getType(b.cell);
|
||||||
|
|
||||||
for (const u of options.military) {
|
for (const unit of options.military) {
|
||||||
if (u.type === "naval" && !b.port) continue; // only ports produce naval units
|
if (unit.type === "naval" && !b.port) continue; // only ports produce naval units
|
||||||
const perc = +u.urban;
|
const perc = +unit.urban;
|
||||||
if (isNaN(perc) || perc <= 0 || !s.temp[u.name]) continue;
|
if (isNaN(perc) || perc <= 0 || !stateObj.temp[unit.name]) continue;
|
||||||
|
if (!passUnitLimits(unit, biome, state, culture, religion)) continue;
|
||||||
|
|
||||||
const mod = type === "generic" ? 1 : burgTypeModifier[type][u.type]; // cell specific modifier
|
const mod = type === "generic" ? 1 : burgTypeModifier[type][unit.type]; // cell specific modifier
|
||||||
const army = m * perc * mod; // urban cell army
|
const army = m * perc * mod; // urban cell army
|
||||||
const t = rn(army * s.temp[u.name] * populationRate); // total troops
|
const total = rn(army * stateObj.temp[unit.name] * populationRate); // total troops
|
||||||
if (!t) continue;
|
if (!total) continue;
|
||||||
let x = p[b.cell][0],
|
|
||||||
y = p[b.cell][1],
|
let [x, y] = p[b.cell];
|
||||||
n = 0;
|
let n = 0;
|
||||||
if (u.type === "naval") {
|
|
||||||
|
if (unit.type === "naval") {
|
||||||
let haven = cells.haven[b.cell];
|
let haven = cells.haven[b.cell];
|
||||||
(x = p[haven][0]), (y = p[haven][1]);
|
[x, y] = p[haven];
|
||||||
n = 1;
|
n = 1;
|
||||||
} // place naval in sea cell
|
} // place naval to sea
|
||||||
s.temp.platoons.push({cell: b.cell, a: t, t, x, y, u: u.name, n, s: u.separate, type: u.type});
|
|
||||||
|
stateObj.temp.platoons.push({cell: b.cell, a: total, t: total, x, y, u: unit.name, n, s: unit.separate, type: unit.type});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -334,7 +354,13 @@ window.Military = (function () {
|
||||||
|
|
||||||
const getName = function (r, regiments) {
|
const getName = function (r, regiments) {
|
||||||
const cells = pack.cells;
|
const cells = pack.cells;
|
||||||
const proper = r.n ? null : cells.province[r.cell] && pack.provinces[cells.province[r.cell]] ? pack.provinces[cells.province[r.cell]].name : cells.burg[r.cell] && pack.burgs[cells.burg[r.cell]] ? pack.burgs[cells.burg[r.cell]].name : null;
|
const proper = r.n
|
||||||
|
? null
|
||||||
|
: cells.province[r.cell] && pack.provinces[cells.province[r.cell]]
|
||||||
|
? pack.provinces[cells.province[r.cell]].name
|
||||||
|
: cells.burg[r.cell] && pack.burgs[cells.burg[r.cell]]
|
||||||
|
? pack.burgs[cells.burg[r.cell]].name
|
||||||
|
: null;
|
||||||
const number = nth(regiments.filter(reg => reg.n === r.n && reg.i < r.i).length + 1);
|
const number = nth(regiments.filter(reg => reg.n === r.n && reg.i < r.i).length + 1);
|
||||||
const form = r.n ? "Fleet" : "Regiment";
|
const form = r.n ? "Fleet" : "Regiment";
|
||||||
return `${number}${proper ? ` (${proper}) ` : ` `}${form}`;
|
return `${number}${proper ? ` (${proper}) ` : ` `}${form}`;
|
||||||
|
|
@ -351,7 +377,12 @@ window.Military = (function () {
|
||||||
|
|
||||||
const generateNote = function (r, s) {
|
const generateNote = function (r, s) {
|
||||||
const cells = pack.cells;
|
const cells = pack.cells;
|
||||||
const base = cells.burg[r.cell] && pack.burgs[cells.burg[r.cell]] ? pack.burgs[cells.burg[r.cell]].name : cells.province[r.cell] && pack.provinces[cells.province[r.cell]] ? pack.provinces[cells.province[r.cell]].fullName : null;
|
const base =
|
||||||
|
cells.burg[r.cell] && pack.burgs[cells.burg[r.cell]]
|
||||||
|
? pack.burgs[cells.burg[r.cell]].name
|
||||||
|
: cells.province[r.cell] && pack.provinces[cells.province[r.cell]]
|
||||||
|
? pack.provinces[cells.province[r.cell]].fullName
|
||||||
|
: null;
|
||||||
const station = base ? `${r.name} is ${r.n ? "based" : "stationed"} in ${base}. ` : "";
|
const station = base ? `${r.name} is ${r.n ? "based" : "stationed"} in ${base}. ` : "";
|
||||||
|
|
||||||
const composition = r.a
|
const composition = r.a
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,7 @@ window.ReliefIcons = (function () {
|
||||||
function getReliefIcon(i, h) {
|
function getReliefIcon(i, h) {
|
||||||
const temp = grid.cells.temp[pack.cells.g[i]];
|
const temp = grid.cells.temp[pack.cells.g[i]];
|
||||||
const type = h > 70 && temp < 0 ? "mountSnow" : h > 70 ? "mount" : "hill";
|
const type = h > 70 && temp < 0 ? "mountSnow" : h > 70 ? "mount" : "hill";
|
||||||
const size = h > 70 ? (h - 45) * mod : Math.min(Math.max((h - 40) * mod, 3), 6);
|
const size = h > 70 ? (h - 45) * mod : minmax((h - 40) * mod, 3, 6);
|
||||||
return [getIcon(type), size];
|
return [getIcon(type), size];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,22 @@
|
||||||
|
|
||||||
window.Religions = (function () {
|
window.Religions = (function () {
|
||||||
// name generation approach and relative chance to be selected
|
// name generation approach and relative chance to be selected
|
||||||
const approach = {Number: 1, Being: 3, Adjective: 5, "Color + Animal": 5, "Adjective + Animal": 5, "Adjective + Being": 5, "Adjective + Genitive": 1, "Color + Being": 3, "Color + Genitive": 3, "Being + of + Genitive": 2, "Being + of the + Genitive": 1, "Animal + of + Genitive": 1, "Adjective + Being + of + Genitive": 2, "Adjective + Animal + of + Genitive": 2};
|
const approach = {
|
||||||
|
Number: 1,
|
||||||
|
Being: 3,
|
||||||
|
Adjective: 5,
|
||||||
|
"Color + Animal": 5,
|
||||||
|
"Adjective + Animal": 5,
|
||||||
|
"Adjective + Being": 5,
|
||||||
|
"Adjective + Genitive": 1,
|
||||||
|
"Color + Being": 3,
|
||||||
|
"Color + Genitive": 3,
|
||||||
|
"Being + of + Genitive": 2,
|
||||||
|
"Being + of the + Genitive": 1,
|
||||||
|
"Animal + of + Genitive": 1,
|
||||||
|
"Adjective + Being + of + Genitive": 2,
|
||||||
|
"Adjective + Animal + of + Genitive": 2
|
||||||
|
};
|
||||||
|
|
||||||
// turn weighted array into simple array
|
// turn weighted array into simple array
|
||||||
const approaches = [];
|
const approaches = [];
|
||||||
|
|
@ -14,11 +29,254 @@ window.Religions = (function () {
|
||||||
|
|
||||||
const base = {
|
const base = {
|
||||||
number: ["One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve"],
|
number: ["One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve"],
|
||||||
being: ["God", "Goddess", "Lord", "Lady", "Deity", "Creator", "Maker", "Overlord", "Ruler", "Chief", "Master", "Spirit", "Ancestor", "Father", "Forebear", "Forefather", "Mother", "Brother", "Sister", "Elder", "Numen", "Ancient", "Virgin", "Giver", "Council", "Guardian", "Reaper"],
|
being: [
|
||||||
animal: ["Dragon", "Wyvern", "Phoenix", "Unicorn", "Sphinx", "Centaur", "Pegasus", "Kraken", "Basilisk", "Chimera", "Cyclope", "Antelope", "Ape", "Badger", "Bear", "Beaver", "Bison", "Boar", "Buffalo", "Cat", "Cobra", "Crane", "Crocodile", "Crow", "Deer", "Dog", "Eagle", "Elk", "Fox", "Goat", "Goose", "Hare", "Hawk", "Heron", "Horse", "Hyena", "Ibis", "Jackal", "Jaguar", "Lark", "Leopard", "Lion", "Mantis", "Marten", "Moose", "Mule", "Narwhal", "Owl", "Panther", "Rat", "Raven", "Rook", "Scorpion", "Shark", "Sheep", "Snake", "Spider", "Swan", "Tiger", "Turtle", "Viper", "Vulture", "Walrus", "Wolf", "Wolverine", "Worm", "Camel", "Falcon", "Hound", "Ox", "Serpent"],
|
"God",
|
||||||
adjective: ["New", "Good", "High", "Old", "Great", "Big", "Young", "Major", "Strong", "Happy", "Last", "Main", "Huge", "Far", "Beautiful", "Wild", "Fair", "Prime", "Crazy", "Ancient", "Proud", "Secret", "Lucky", "Sad", "Silent", "Latter", "Severe", "Fat", "Holy", "Pure", "Aggressive", "Honest", "Giant", "Mad", "Pregnant", "Distant", "Lost", "Broken", "Blind", "Friendly", "Unknown", "Sleeping", "Slumbering", "Loud", "Hungry", "Wise", "Worried", "Sacred", "Magical", "Superior", "Patient", "Dead", "Deadly", "Peaceful", "Grateful", "Frozen", "Evil", "Scary", "Burning", "Divine", "Bloody", "Dying", "Waking", "Brutal", "Unhappy", "Calm", "Cruel", "Favorable", "Blond", "Explicit", "Disturbing", "Devastating", "Brave", "Sunny", "Troubled", "Flying", "Sustainable", "Marine", "Fatal", "Inherent", "Selected", "Naval", "Cheerful", "Almighty", "Benevolent", "Eternal", "Immutable", "Infallible"],
|
"Goddess",
|
||||||
genitive: ["Day", "Life", "Death", "Night", "Home", "Fog", "Snow", "Winter", "Summer", "Cold", "Springs", "Gates", "Nature", "Thunder", "Lightning", "War", "Ice", "Frost", "Fire", "Doom", "Fate", "Pain", "Heaven", "Justice", "Light", "Love", "Time", "Victory"],
|
"Lord",
|
||||||
theGenitive: ["World", "Word", "South", "West", "North", "East", "Sun", "Moon", "Peak", "Fall", "Dawn", "Eclipse", "Abyss", "Blood", "Tree", "Earth", "Harvest", "Rainbow", "Sea", "Sky", "Stars", "Storm", "Underworld", "Wild"],
|
"Lady",
|
||||||
|
"Deity",
|
||||||
|
"Creator",
|
||||||
|
"Maker",
|
||||||
|
"Overlord",
|
||||||
|
"Ruler",
|
||||||
|
"Chief",
|
||||||
|
"Master",
|
||||||
|
"Spirit",
|
||||||
|
"Ancestor",
|
||||||
|
"Father",
|
||||||
|
"Forebear",
|
||||||
|
"Forefather",
|
||||||
|
"Mother",
|
||||||
|
"Brother",
|
||||||
|
"Sister",
|
||||||
|
"Elder",
|
||||||
|
"Numen",
|
||||||
|
"Ancient",
|
||||||
|
"Virgin",
|
||||||
|
"Giver",
|
||||||
|
"Council",
|
||||||
|
"Guardian",
|
||||||
|
"Reaper"
|
||||||
|
],
|
||||||
|
animal: [
|
||||||
|
"Dragon",
|
||||||
|
"Wyvern",
|
||||||
|
"Phoenix",
|
||||||
|
"Unicorn",
|
||||||
|
"Sphinx",
|
||||||
|
"Centaur",
|
||||||
|
"Pegasus",
|
||||||
|
"Kraken",
|
||||||
|
"Basilisk",
|
||||||
|
"Chimera",
|
||||||
|
"Cyclope",
|
||||||
|
"Antelope",
|
||||||
|
"Ape",
|
||||||
|
"Badger",
|
||||||
|
"Bear",
|
||||||
|
"Beaver",
|
||||||
|
"Bison",
|
||||||
|
"Boar",
|
||||||
|
"Buffalo",
|
||||||
|
"Cat",
|
||||||
|
"Cobra",
|
||||||
|
"Crane",
|
||||||
|
"Crocodile",
|
||||||
|
"Crow",
|
||||||
|
"Deer",
|
||||||
|
"Dog",
|
||||||
|
"Eagle",
|
||||||
|
"Elk",
|
||||||
|
"Fox",
|
||||||
|
"Goat",
|
||||||
|
"Goose",
|
||||||
|
"Hare",
|
||||||
|
"Hawk",
|
||||||
|
"Heron",
|
||||||
|
"Horse",
|
||||||
|
"Hyena",
|
||||||
|
"Ibis",
|
||||||
|
"Jackal",
|
||||||
|
"Jaguar",
|
||||||
|
"Lark",
|
||||||
|
"Leopard",
|
||||||
|
"Lion",
|
||||||
|
"Mantis",
|
||||||
|
"Marten",
|
||||||
|
"Moose",
|
||||||
|
"Mule",
|
||||||
|
"Narwhal",
|
||||||
|
"Owl",
|
||||||
|
"Panther",
|
||||||
|
"Rat",
|
||||||
|
"Raven",
|
||||||
|
"Rook",
|
||||||
|
"Scorpion",
|
||||||
|
"Shark",
|
||||||
|
"Sheep",
|
||||||
|
"Snake",
|
||||||
|
"Spider",
|
||||||
|
"Swan",
|
||||||
|
"Tiger",
|
||||||
|
"Turtle",
|
||||||
|
"Viper",
|
||||||
|
"Vulture",
|
||||||
|
"Walrus",
|
||||||
|
"Wolf",
|
||||||
|
"Wolverine",
|
||||||
|
"Worm",
|
||||||
|
"Camel",
|
||||||
|
"Falcon",
|
||||||
|
"Hound",
|
||||||
|
"Ox",
|
||||||
|
"Serpent"
|
||||||
|
],
|
||||||
|
adjective: [
|
||||||
|
"New",
|
||||||
|
"Good",
|
||||||
|
"High",
|
||||||
|
"Old",
|
||||||
|
"Great",
|
||||||
|
"Big",
|
||||||
|
"Young",
|
||||||
|
"Major",
|
||||||
|
"Strong",
|
||||||
|
"Happy",
|
||||||
|
"Last",
|
||||||
|
"Main",
|
||||||
|
"Huge",
|
||||||
|
"Far",
|
||||||
|
"Beautiful",
|
||||||
|
"Wild",
|
||||||
|
"Fair",
|
||||||
|
"Prime",
|
||||||
|
"Crazy",
|
||||||
|
"Ancient",
|
||||||
|
"Proud",
|
||||||
|
"Secret",
|
||||||
|
"Lucky",
|
||||||
|
"Sad",
|
||||||
|
"Silent",
|
||||||
|
"Latter",
|
||||||
|
"Severe",
|
||||||
|
"Fat",
|
||||||
|
"Holy",
|
||||||
|
"Pure",
|
||||||
|
"Aggressive",
|
||||||
|
"Honest",
|
||||||
|
"Giant",
|
||||||
|
"Mad",
|
||||||
|
"Pregnant",
|
||||||
|
"Distant",
|
||||||
|
"Lost",
|
||||||
|
"Broken",
|
||||||
|
"Blind",
|
||||||
|
"Friendly",
|
||||||
|
"Unknown",
|
||||||
|
"Sleeping",
|
||||||
|
"Slumbering",
|
||||||
|
"Loud",
|
||||||
|
"Hungry",
|
||||||
|
"Wise",
|
||||||
|
"Worried",
|
||||||
|
"Sacred",
|
||||||
|
"Magical",
|
||||||
|
"Superior",
|
||||||
|
"Patient",
|
||||||
|
"Dead",
|
||||||
|
"Deadly",
|
||||||
|
"Peaceful",
|
||||||
|
"Grateful",
|
||||||
|
"Frozen",
|
||||||
|
"Evil",
|
||||||
|
"Scary",
|
||||||
|
"Burning",
|
||||||
|
"Divine",
|
||||||
|
"Bloody",
|
||||||
|
"Dying",
|
||||||
|
"Waking",
|
||||||
|
"Brutal",
|
||||||
|
"Unhappy",
|
||||||
|
"Calm",
|
||||||
|
"Cruel",
|
||||||
|
"Favorable",
|
||||||
|
"Blond",
|
||||||
|
"Explicit",
|
||||||
|
"Disturbing",
|
||||||
|
"Devastating",
|
||||||
|
"Brave",
|
||||||
|
"Sunny",
|
||||||
|
"Troubled",
|
||||||
|
"Flying",
|
||||||
|
"Sustainable",
|
||||||
|
"Marine",
|
||||||
|
"Fatal",
|
||||||
|
"Inherent",
|
||||||
|
"Selected",
|
||||||
|
"Naval",
|
||||||
|
"Cheerful",
|
||||||
|
"Almighty",
|
||||||
|
"Benevolent",
|
||||||
|
"Eternal",
|
||||||
|
"Immutable",
|
||||||
|
"Infallible"
|
||||||
|
],
|
||||||
|
genitive: [
|
||||||
|
"Day",
|
||||||
|
"Life",
|
||||||
|
"Death",
|
||||||
|
"Night",
|
||||||
|
"Home",
|
||||||
|
"Fog",
|
||||||
|
"Snow",
|
||||||
|
"Winter",
|
||||||
|
"Summer",
|
||||||
|
"Cold",
|
||||||
|
"Springs",
|
||||||
|
"Gates",
|
||||||
|
"Nature",
|
||||||
|
"Thunder",
|
||||||
|
"Lightning",
|
||||||
|
"War",
|
||||||
|
"Ice",
|
||||||
|
"Frost",
|
||||||
|
"Fire",
|
||||||
|
"Doom",
|
||||||
|
"Fate",
|
||||||
|
"Pain",
|
||||||
|
"Heaven",
|
||||||
|
"Justice",
|
||||||
|
"Light",
|
||||||
|
"Love",
|
||||||
|
"Time",
|
||||||
|
"Victory"
|
||||||
|
],
|
||||||
|
theGenitive: [
|
||||||
|
"World",
|
||||||
|
"Word",
|
||||||
|
"South",
|
||||||
|
"West",
|
||||||
|
"North",
|
||||||
|
"East",
|
||||||
|
"Sun",
|
||||||
|
"Moon",
|
||||||
|
"Peak",
|
||||||
|
"Fall",
|
||||||
|
"Dawn",
|
||||||
|
"Eclipse",
|
||||||
|
"Abyss",
|
||||||
|
"Blood",
|
||||||
|
"Tree",
|
||||||
|
"Earth",
|
||||||
|
"Harvest",
|
||||||
|
"Rainbow",
|
||||||
|
"Sea",
|
||||||
|
"Sky",
|
||||||
|
"Stars",
|
||||||
|
"Storm",
|
||||||
|
"Underworld",
|
||||||
|
"Wild"
|
||||||
|
],
|
||||||
color: ["Dark", "Light", "Bright", "Golden", "White", "Black", "Red", "Pink", "Purple", "Blue", "Green", "Yellow", "Amber", "Orange", "Brown", "Grey"]
|
color: ["Dark", "Light", "Bright", "Golden", "White", "Black", "Red", "Pink", "Purple", "Blue", "Green", "Yellow", "Amber", "Orange", "Brown", "Grey"]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -29,7 +287,16 @@ window.Religions = (function () {
|
||||||
Heresy: {Heresy: 1}
|
Heresy: {Heresy: 1}
|
||||||
};
|
};
|
||||||
|
|
||||||
const methods = {"Random + type": 3, "Random + ism": 1, "Supreme + ism": 5, "Faith of + Supreme": 5, "Place + ism": 1, "Culture + ism": 2, "Place + ian + type": 6, "Culture + type": 4};
|
const methods = {
|
||||||
|
"Random + type": 3,
|
||||||
|
"Random + ism": 1,
|
||||||
|
"Supreme + ism": 5,
|
||||||
|
"Faith of + Supreme": 5,
|
||||||
|
"Place + ism": 1,
|
||||||
|
"Culture + ism": 2,
|
||||||
|
"Place + ian + type": 6,
|
||||||
|
"Culture + type": 4
|
||||||
|
};
|
||||||
|
|
||||||
const types = {
|
const types = {
|
||||||
Shamanism: {Beliefs: 3, Shamanism: 2, Spirits: 1},
|
Shamanism: {Beliefs: 3, Shamanism: 2, Spirits: 1},
|
||||||
|
|
@ -78,7 +345,10 @@ window.Religions = (function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
const burgs = pack.burgs.filter(b => b.i && !b.removed);
|
const burgs = pack.burgs.filter(b => b.i && !b.removed);
|
||||||
const sorted = burgs.length > +religionsInput.value ? burgs.sort((a, b) => b.population - a.population).map(b => b.cell) : cells.i.filter(i => cells.s[i] > 2).sort((a, b) => cells.s[b] - cells.s[a]);
|
const sorted =
|
||||||
|
burgs.length > +religionsInput.value
|
||||||
|
? burgs.sort((a, b) => b.population - a.population).map(b => b.cell)
|
||||||
|
: cells.i.filter(i => cells.s[i] > 2).sort((a, b) => cells.s[b] - cells.s[a]);
|
||||||
const religionsTree = d3.quadtree();
|
const religionsTree = d3.quadtree();
|
||||||
const spacing = (graphWidth + graphHeight) / 6 / religionsInput.value; // base min distance between towns
|
const spacing = (graphWidth + graphHeight) / 6 / religionsInput.value; // base min distance between towns
|
||||||
const cultsCount = Math.floor((rand(10, 40) / 100) * religionsInput.value);
|
const cultsCount = Math.floor((rand(10, 40) / 100) * religionsInput.value);
|
||||||
|
|
@ -160,9 +430,20 @@ window.Religions = (function () {
|
||||||
const name = getCultName("Heresy", center);
|
const name = getCultName("Heresy", center);
|
||||||
const expansionism = gauss(1.2, 0.5, 0, 5);
|
const expansionism = gauss(1.2, 0.5, 0, 5);
|
||||||
const color = getMixedColor(r.color, 0.4, 0.2); // "url(#hatch6)";
|
const color = getMixedColor(r.color, 0.4, 0.2); // "url(#hatch6)";
|
||||||
religions.push({i: religions.length, name, color, culture, type: "Heresy", form: r.form, deity: r.deity, expansion: "global", expansionism, center, origin: r.i});
|
religions.push({
|
||||||
|
i: religions.length,
|
||||||
|
name,
|
||||||
|
color,
|
||||||
|
culture,
|
||||||
|
type: "Heresy",
|
||||||
|
form: r.form,
|
||||||
|
deity: r.deity,
|
||||||
|
expansion: "global",
|
||||||
|
expansionism,
|
||||||
|
center,
|
||||||
|
origin: r.i
|
||||||
|
});
|
||||||
religionsTree.add([x, y]);
|
religionsTree.add([x, y]);
|
||||||
//debug.append("circle").attr("cx", x).attr("cy", y).attr("r", 2).attr("fill", "green");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -195,7 +476,24 @@ window.Religions = (function () {
|
||||||
name,
|
name,
|
||||||
religions.map(r => r.code)
|
religions.map(r => r.code)
|
||||||
);
|
);
|
||||||
religions.push({i, name, color, culture, type, form: formName, deity, expansion, expansionism: 0, center, cells: 0, area: 0, rural: 0, urban: 0, origin: r, code});
|
religions.push({
|
||||||
|
i,
|
||||||
|
name,
|
||||||
|
color,
|
||||||
|
culture,
|
||||||
|
type,
|
||||||
|
form: formName,
|
||||||
|
deity,
|
||||||
|
expansion,
|
||||||
|
expansionism: 0,
|
||||||
|
center,
|
||||||
|
cells: 0,
|
||||||
|
area: 0,
|
||||||
|
rural: 0,
|
||||||
|
urban: 0,
|
||||||
|
origin: r,
|
||||||
|
code
|
||||||
|
});
|
||||||
cells.religion[center] = i;
|
cells.religion[center] = i;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -292,20 +590,18 @@ window.Religions = (function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
function checkCenters() {
|
function checkCenters() {
|
||||||
const cells = pack.cells,
|
const {cells, religions} = pack;
|
||||||
religions = pack.religions;
|
|
||||||
|
|
||||||
const codes = religions.map(r => r.code);
|
const codes = religions.map(r => r.code);
|
||||||
religions
|
religions.forEach(r => {
|
||||||
.filter(r => r.i)
|
if (!r.i) return;
|
||||||
.forEach(r => {
|
|
||||||
r.code = abbreviate(r.name, codes);
|
r.code = abbreviate(r.name, codes);
|
||||||
|
|
||||||
// move religion center if it's not within religion area after expansion
|
// move religion center if it's not within religion area after expansion
|
||||||
if (cells.religion[r.center] === r.i) return; // in area
|
if (cells.religion[r.center] === r.i) return; // in area
|
||||||
const religCells = cells.i.filter(i => cells.religion[i] === r.i);
|
const religCells = cells.i.filter(i => cells.religion[i] === r.i);
|
||||||
if (!religCells.length) return; // extinct religion
|
if (!religCells.length) return; // extinct religion
|
||||||
r.center = religCells.sort((a, b) => b.pop - a.pop)[0];
|
r.center = religCells.sort((a, b) => cells.pop[b] - cells.pop[a])[0];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,43 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
window.ThreeD = (function () {
|
window.ThreeD = (function () {
|
||||||
// set default options
|
const options = {
|
||||||
const options = {scale: 50, lightness: 0.7, shadow: 0.5, sun: {x: 100, y: 600, z: 1000}, rotateMesh: 0, rotateGlobe: 0.5, skyColor: "#9ecef5", waterColor: "#466eab", extendedWater: 0, labels3d: 0, resolution: 2};
|
scale: 50,
|
||||||
|
lightness: 0.7,
|
||||||
|
shadow: 0.5,
|
||||||
|
sun: {x: 100, y: 600, z: 1000},
|
||||||
|
rotateMesh: 0,
|
||||||
|
rotateGlobe: 0.5,
|
||||||
|
skyColor: "#9ecef5",
|
||||||
|
waterColor: "#466eab",
|
||||||
|
extendedWater: 0,
|
||||||
|
labels3d: 0,
|
||||||
|
resolution: 2
|
||||||
|
};
|
||||||
|
|
||||||
// set variables
|
// set variables
|
||||||
let Renderer, scene, camera, controls, animationFrame, material, texture, geometry, mesh, ambientLight, spotLight, waterPlane, waterMaterial, waterMesh, raycaster;
|
let Renderer,
|
||||||
|
scene,
|
||||||
const drawCtx = document.createElement("canvas").getContext("2d");
|
camera,
|
||||||
const drawSVG = document.createElementNS("http://www.w3.org/2000/svg", "svg");
|
controls,
|
||||||
document.body.appendChild(drawSVG);
|
animationFrame,
|
||||||
|
material,
|
||||||
|
texture,
|
||||||
|
geometry,
|
||||||
|
mesh,
|
||||||
|
ambientLight,
|
||||||
|
spotLight,
|
||||||
|
waterPlane,
|
||||||
|
waterMaterial,
|
||||||
|
waterMesh,
|
||||||
|
raycaster;
|
||||||
|
|
||||||
let labels = [];
|
let labels = [];
|
||||||
let icons = [];
|
let icons = [];
|
||||||
let lines = [];
|
let lines = [];
|
||||||
|
|
||||||
|
const context2d = document.createElement("canvas").getContext("2d");
|
||||||
|
|
||||||
// initiate 3d scene
|
// initiate 3d scene
|
||||||
const create = async function (canvas, type = "viewMesh") {
|
const create = async function (canvas, type = "viewMesh") {
|
||||||
options.isOn = true;
|
options.isOn = true;
|
||||||
|
|
@ -210,16 +233,16 @@ window.ThreeD = (function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createTextLabel({text, font, size, color, quality}) {
|
async function createTextLabel({text, font, size, color, quality}) {
|
||||||
drawCtx.font = `${size * quality}px ${font}`;
|
context2d.font = `${size * quality}px ${font}`;
|
||||||
drawCtx.canvas.width = drawCtx.measureText(text).width;
|
context2d.canvas.width = context2d.measureText(text).width;
|
||||||
drawCtx.canvas.height = size * quality * 1.25; // 25% margin as text can overflow the font size
|
context2d.canvas.height = size * quality * 1.25; // 25% margin as text can overflow the font size
|
||||||
drawCtx.clearRect(0, 0, drawCtx.canvas.width, drawCtx.canvas.height);
|
context2d.clearRect(0, 0, context2d.canvas.width, context2d.canvas.height);
|
||||||
|
|
||||||
drawCtx.font = `${size * quality}px ${font}`;
|
context2d.font = `${size * quality}px ${font}`;
|
||||||
drawCtx.fillStyle = color;
|
context2d.fillStyle = color;
|
||||||
drawCtx.fillText(text, 0, size * quality);
|
context2d.fillText(text, 0, size * quality);
|
||||||
|
|
||||||
return textureToSprite(drawCtx.canvas.toDataURL(), drawCtx.canvas.width / quality, drawCtx.canvas.height / quality);
|
return textureToSprite(context2d.canvas.toDataURL(), context2d.canvas.width / quality, context2d.canvas.height / quality);
|
||||||
}
|
}
|
||||||
|
|
||||||
function get3dCoords(baseX, baseY) {
|
function get3dCoords(baseX, baseY) {
|
||||||
|
|
@ -578,5 +601,21 @@ window.ThreeD = (function () {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return {create, redraw, update, stop, options, setScale, setLightness, setSun, setRotation, toggleLabels, toggleSky, setResolution, setColors, saveScreenshot, saveOBJ};
|
return {
|
||||||
|
create,
|
||||||
|
redraw,
|
||||||
|
update,
|
||||||
|
stop,
|
||||||
|
options,
|
||||||
|
setScale,
|
||||||
|
setLightness,
|
||||||
|
setSun,
|
||||||
|
setRotation,
|
||||||
|
toggleLabels,
|
||||||
|
toggleSky,
|
||||||
|
setResolution,
|
||||||
|
setColors,
|
||||||
|
saveScreenshot,
|
||||||
|
saveOBJ
|
||||||
|
};
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -330,7 +330,7 @@ class Battle {
|
||||||
}
|
}
|
||||||
|
|
||||||
getInitialMorale() {
|
getInitialMorale() {
|
||||||
const powerFee = diff => Math.min(Math.max(100 - diff ** 1.5 * 10 + 10, 50), 100);
|
const powerFee = diff => minmax(100 - diff ** 1.5 * 10 + 10, 50, 100);
|
||||||
const distanceFee = dist => Math.min(d3.mean(dist) / 50, 15);
|
const distanceFee = dist => Math.min(d3.mean(dist) / 50, 15);
|
||||||
const powerDiff = this.defenders.power / this.attackers.power;
|
const powerDiff = this.defenders.power / this.attackers.power;
|
||||||
this.attackers.morale = powerFee(powerDiff) - distanceFee(this.attackers.distances);
|
this.attackers.morale = powerFee(powerDiff) - distanceFee(this.attackers.distances);
|
||||||
|
|
|
||||||
|
|
@ -621,7 +621,7 @@ function editHeightmap() {
|
||||||
const interpolate = d3.interpolateRound(power, 1);
|
const interpolate = d3.interpolateRound(power, 1);
|
||||||
const land = changeOnlyLand.checked;
|
const land = changeOnlyLand.checked;
|
||||||
function lim(v) {
|
function lim(v) {
|
||||||
return Math.max(Math.min(v, 100), land ? 20 : 0);
|
return minmax(v, land ? 20 : 0, 100);
|
||||||
}
|
}
|
||||||
const h = grid.cells.h;
|
const h = grid.cells.h;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ document.addEventListener("keydown", handleKeydown);
|
||||||
document.addEventListener("keyup", handleKeyup);
|
document.addEventListener("keyup", handleKeyup);
|
||||||
|
|
||||||
function handleKeydown(event) {
|
function handleKeydown(event) {
|
||||||
const {key, code, ctrlKey, altKey} = event;
|
const {code, ctrlKey, altKey} = event;
|
||||||
if (altKey && !ctrlKey) event.preventDefault(); // disallow alt key combinations
|
if (altKey && !ctrlKey) event.preventDefault(); // disallow alt key combinations
|
||||||
if (ctrlKey && ["KeyS", "KeyC"].includes(code)) event.preventDefault(); // disallow CTRL + S and CTRL + C
|
if (ctrlKey && ["KeyS", "KeyC"].includes(code)) event.preventDefault(); // disallow CTRL + S and CTRL + C
|
||||||
if (["F1", "F2", "F6", "F9", "Tab"].includes(key)) event.preventDefault(); // disallow default Fn and Tab
|
if (["F1", "F2", "F6", "F9", "Tab"].includes(code)) event.preventDefault(); // disallow default Fn and Tab
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleKeyup(event) {
|
function handleKeyup(event) {
|
||||||
|
|
@ -19,83 +19,82 @@ function handleKeyup(event) {
|
||||||
if (document.getSelection().toString()) return; // don't trigger if user selects text
|
if (document.getSelection().toString()) return; // don't trigger if user selects text
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
|
|
||||||
const {ctrlKey, metaKey, shiftKey, altKey} = event;
|
const {code, key, ctrlKey, metaKey, shiftKey, altKey} = event;
|
||||||
const key = event.key.toUpperCase();
|
|
||||||
const ctrl = ctrlKey || metaKey || key === "Control";
|
const ctrl = ctrlKey || metaKey || key === "Control";
|
||||||
const shift = shiftKey || key === "Shift";
|
const shift = shiftKey || key === "Shift";
|
||||||
const alt = altKey || key === "Alt";
|
const alt = altKey || key === "Alt";
|
||||||
|
|
||||||
if (key === "F1") showInfo();
|
if (code === "F1") showInfo();
|
||||||
else if (key === "F2") regeneratePrompt("hotkey");
|
else if (code === "F2") regeneratePrompt("hotkey");
|
||||||
else if (key === "F6") quickSave();
|
else if (code === "F6") quickSave();
|
||||||
else if (key === "F9") quickLoad();
|
else if (code === "F9") quickLoad();
|
||||||
else if (key === "TAB") toggleOptions(event);
|
else if (code === "Tab") toggleOptions(event);
|
||||||
else if (key === "ESCAPE") closeAllDialogs();
|
else if (code === "Escape") closeAllDialogs();
|
||||||
else if (key === "DELETE") removeElementOnKey();
|
else if (code === "Delete") removeElementOnKey();
|
||||||
else if (key === "O" && document.getElementById("canvas3d")) toggle3dOptions();
|
else if (code === "KeyO" && document.getElementById("canvas3d")) toggle3dOptions();
|
||||||
else if (ctrl && key === "Q") toggleSaveReminder();
|
else if (ctrl && code === "KeyQ") toggleSaveReminder();
|
||||||
else if (ctrl && key === "S") dowloadMap();
|
else if (ctrl && code === "KeyS") dowloadMap();
|
||||||
else if (ctrl && key === "C") saveToDropbox();
|
else if (ctrl && code === "KeyC") saveToDropbox();
|
||||||
else if (ctrl && key === "Z" && undo.offsetParent) undo.click();
|
else if (ctrl && code === "KeyZ" && undo.offsetParent) undo.click();
|
||||||
else if (ctrl && key === "Y" && redo.offsetParent) redo.click();
|
else if (ctrl && code === "KeyY" && redo.offsetParent) redo.click();
|
||||||
else if (shift && key === "H") editHeightmap();
|
else if (shift && code === "KeyH") editHeightmap();
|
||||||
else if (shift && key === "B") editBiomes();
|
else if (shift && code === "KeyB") editBiomes();
|
||||||
else if (shift && key === "S") editStates();
|
else if (shift && code === "KeyS") editStates();
|
||||||
else if (shift && key === "P") editProvinces();
|
else if (shift && code === "KeyP") editProvinces();
|
||||||
else if (shift && key === "D") editDiplomacy();
|
else if (shift && code === "KeyD") editDiplomacy();
|
||||||
else if (shift && key === "C") editCultures();
|
else if (shift && code === "KeyC") editCultures();
|
||||||
else if (shift && key === "N") editNamesbase();
|
else if (shift && code === "KeyN") editNamesbase();
|
||||||
else if (shift && key === "Z") editZones();
|
else if (shift && code === "KeyZ") editZones();
|
||||||
else if (shift && key === "R") editReligions();
|
else if (shift && code === "KeyR") editReligions();
|
||||||
else if (shift && key === "Y") openEmblemEditor();
|
else if (shift && code === "KeyY") openEmblemEditor();
|
||||||
else if (shift && key === "Q") editUnits();
|
else if (shift && code === "KeyQ") editUnits();
|
||||||
else if (shift && key === "O") editNotes();
|
else if (shift && code === "KeyO") editNotes();
|
||||||
else if (shift && key === "T") overviewBurgs();
|
else if (shift && code === "KeyT") overviewBurgs();
|
||||||
else if (shift && key === "V") overviewRivers();
|
else if (shift && code === "KeyV") overviewRivers();
|
||||||
else if (shift && key === "M") overviewMilitary();
|
else if (shift && code === "KeyM") overviewMilitary();
|
||||||
else if (shift && key === "K") overviewMarkers();
|
else if (shift && code === "KeyK") overviewMarkers();
|
||||||
else if (shift && key === "E") viewCellDetails();
|
else if (shift && code === "KeyE") viewCellDetails();
|
||||||
else if (shift && key === "1") toggleAddBurg();
|
else if (key === "!") toggleAddBurg();
|
||||||
else if (shift && key === "2") toggleAddLabel();
|
else if (key === "@") toggleAddLabel();
|
||||||
else if (shift && key === "3") toggleAddRiver();
|
else if (key === "#") toggleAddRiver();
|
||||||
else if (shift && key === "4") toggleAddRoute();
|
else if (key === "$") toggleAddRoute();
|
||||||
else if (shift && key === "5") toggleAddMarker();
|
else if (key === "%") toggleAddMarker();
|
||||||
else if (alt && key === "B") console.table(pack.burgs);
|
else if (alt && code === "KeyB") console.table(pack.burgs);
|
||||||
else if (alt && key === "S") console.table(pack.states);
|
else if (alt && code === "KeyS") console.table(pack.states);
|
||||||
else if (alt && key === "C") console.table(pack.cultures);
|
else if (alt && code === "KeyC") console.table(pack.cultures);
|
||||||
else if (alt && key === "R") console.table(pack.religions);
|
else if (alt && code === "KeyR") console.table(pack.religions);
|
||||||
else if (alt && key === "F") console.table(pack.features);
|
else if (alt && code === "KeyF") console.table(pack.features);
|
||||||
else if (key === "X") toggleTexture();
|
else if (code === "KeyX") toggleTexture();
|
||||||
else if (key === "H") toggleHeight();
|
else if (code === "KeyH") toggleHeight();
|
||||||
else if (key === "B") toggleBiomes();
|
else if (code === "KeyB") toggleBiomes();
|
||||||
else if (key === "E") toggleCells();
|
else if (code === "KeyE") toggleCells();
|
||||||
else if (key === "G") toggleGrid();
|
else if (code === "KeyG") toggleGrid();
|
||||||
else if (key === "O") toggleCoordinates();
|
else if (code === "KeyO") toggleCoordinates();
|
||||||
else if (key === "W") toggleCompass();
|
else if (code === "KeyW") toggleCompass();
|
||||||
else if (key === "V") toggleRivers();
|
else if (code === "KeyV") toggleRivers();
|
||||||
else if (key === "F") toggleRelief();
|
else if (code === "KeyF") toggleRelief();
|
||||||
else if (key === "C") toggleCultures();
|
else if (code === "KeyC") toggleCultures();
|
||||||
else if (key === "S") toggleStates();
|
else if (code === "KeyS") toggleStates();
|
||||||
else if (key === "P") toggleProvinces();
|
else if (code === "KeyP") toggleProvinces();
|
||||||
else if (key === "Z") toggleZones();
|
else if (code === "KeyZ") toggleZones();
|
||||||
else if (key === "D") toggleBorders();
|
else if (code === "KeyD") toggleBorders();
|
||||||
else if (key === "R") toggleReligions();
|
else if (code === "KeyR") toggleReligions();
|
||||||
else if (key === "U") toggleRoutes();
|
else if (code === "KeyU") toggleRoutes();
|
||||||
else if (key === "T") toggleTemp();
|
else if (code === "KeyT") toggleTemp();
|
||||||
else if (key === "N") togglePopulation();
|
else if (code === "KeyN") togglePopulation();
|
||||||
else if (key === "J") toggleIce();
|
else if (code === "KeyJ") toggleIce();
|
||||||
else if (key === "A") togglePrec();
|
else if (code === "KeyA") togglePrec();
|
||||||
else if (key === "Y") toggleEmblems();
|
else if (code === "KeyY") toggleEmblems();
|
||||||
else if (key === "L") toggleLabels();
|
else if (code === "KeyL") toggleLabels();
|
||||||
else if (key === "I") toggleIcons();
|
else if (code === "KeyI") toggleIcons();
|
||||||
else if (key === "M") toggleMilitary();
|
else if (code === "KeyM") toggleMilitary();
|
||||||
else if (key === "K") toggleMarkers();
|
else if (code === "KeyK") toggleMarkers();
|
||||||
else if (key === "=") toggleRulers();
|
else if (code === "Equal") toggleRulers();
|
||||||
else if (key === "/") toggleScaleBar();
|
else if (code === "Slash") toggleScaleBar();
|
||||||
else if (key === "ARROWLEFT") zoom.translateBy(svg, 10, 0);
|
else if (code === "ArrowLeft") zoom.translateBy(svg, 10, 0);
|
||||||
else if (key === "ARROWRIGHT") zoom.translateBy(svg, -10, 0);
|
else if (code === "ArrowRight") zoom.translateBy(svg, -10, 0);
|
||||||
else if (key === "ARROWUP") zoom.translateBy(svg, 0, 10);
|
else if (code === "ArrowUp") zoom.translateBy(svg, 0, 10);
|
||||||
else if (key === "ARROWDOWN") zoom.translateBy(svg, 0, -10);
|
else if (code === "ArrowDown") zoom.translateBy(svg, 0, -10);
|
||||||
else if (key === "+" || key === "-") pressNumpadSign(key);
|
else if (key === "+" || key === "-") pressNumpadSign(key);
|
||||||
else if (key === "0") resetZoom(1000);
|
else if (key === "0") resetZoom(1000);
|
||||||
else if (key === "1") zoom.scaleTo(svg, 1);
|
else if (key === "1") zoom.scaleTo(svg, 1);
|
||||||
|
|
@ -123,7 +122,7 @@ function pressNumpadSign(key) {
|
||||||
else if (religionsManuallyBrush.offsetParent) brush = document.getElementById("religionsManuallyBrush");
|
else if (religionsManuallyBrush.offsetParent) brush = document.getElementById("religionsManuallyBrush");
|
||||||
|
|
||||||
if (brush) {
|
if (brush) {
|
||||||
const value = Math.max(Math.min(+brush.value + change, +brush.max), +brush.min);
|
const value = minmax(+brush.value + change, +brush.min, +brush.max);
|
||||||
brush.value = document.getElementById(brush.id + "Number").value = value;
|
brush.value = document.getElementById(brush.id + "Number").value = value;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,8 +47,7 @@ function changePreset(preset) {
|
||||||
.querySelectorAll("li")
|
.querySelectorAll("li")
|
||||||
.forEach(function (e) {
|
.forEach(function (e) {
|
||||||
if (layers.includes(e.id) && !layerIsOn(e.id)) e.click();
|
if (layers.includes(e.id) && !layerIsOn(e.id)) e.click();
|
||||||
// turn on
|
else if (!layers.includes(e.id) && layerIsOn(e.id)) e.click();
|
||||||
else if (!layers.includes(e.id) && layerIsOn(e.id)) e.click(); // turn off
|
|
||||||
});
|
});
|
||||||
layersPreset.value = preset;
|
layersPreset.value = preset;
|
||||||
localStorage.setItem("preset", preset);
|
localStorage.setItem("preset", preset);
|
||||||
|
|
@ -121,6 +120,7 @@ function restoreLayers() {
|
||||||
if (layerIsOn("toggleReligions")) drawReligions();
|
if (layerIsOn("toggleReligions")) drawReligions();
|
||||||
if (layerIsOn("toggleIce")) drawIce();
|
if (layerIsOn("toggleIce")) drawIce();
|
||||||
if (layerIsOn("toggleEmblems")) drawEmblems();
|
if (layerIsOn("toggleEmblems")) drawEmblems();
|
||||||
|
if (layerIsOn("toggleMarkers")) drawMarkers();
|
||||||
|
|
||||||
// some layers are rendered each time, remove them if they are not on
|
// some layers are rendered each time, remove them if they are not on
|
||||||
if (!layerIsOn("toggleBorders")) borders.selectAll("path").remove();
|
if (!layerIsOn("toggleBorders")) borders.selectAll("path").remove();
|
||||||
|
|
@ -1435,8 +1435,8 @@ function toggleTexture(event) {
|
||||||
turnButtonOn("toggleTexture");
|
turnButtonOn("toggleTexture");
|
||||||
// append default texture image selected by default. Don't append on load to not harm performance
|
// append default texture image selected by default. Don't append on load to not harm performance
|
||||||
if (!texture.selectAll("*").size()) {
|
if (!texture.selectAll("*").size()) {
|
||||||
const x = +styleTextureShiftX.value,
|
const x = +styleTextureShiftX.value;
|
||||||
y = +styleTextureShiftY.value;
|
const y = +styleTextureShiftY.value;
|
||||||
const image = texture
|
const image = texture
|
||||||
.append("image")
|
.append("image")
|
||||||
.attr("id", "textureImage")
|
.attr("id", "textureImage")
|
||||||
|
|
@ -1444,18 +1444,14 @@ function toggleTexture(event) {
|
||||||
.attr("y", y)
|
.attr("y", y)
|
||||||
.attr("width", graphWidth - x)
|
.attr("width", graphWidth - x)
|
||||||
.attr("height", graphHeight - y)
|
.attr("height", graphHeight - y)
|
||||||
.attr("xlink:href", getDefaultTexture())
|
|
||||||
.attr("preserveAspectRatio", "xMidYMid slice");
|
.attr("preserveAspectRatio", "xMidYMid slice");
|
||||||
if (styleTextureInput.value !== "default") getBase64(styleTextureInput.value, base64 => image.attr("xlink:href", base64));
|
getBase64(styleTextureInput.value, base64 => image.attr("xlink:href", base64));
|
||||||
}
|
}
|
||||||
$("#texture").fadeIn();
|
$("#texture").fadeIn();
|
||||||
zoom.scaleBy(svg, 1.00001); // enforce browser re-draw
|
zoom.scaleBy(svg, 1.00001); // enforce browser re-draw
|
||||||
if (event && isCtrlClick(event)) editStyle("texture");
|
if (event && isCtrlClick(event)) editStyle("texture");
|
||||||
} else {
|
} else {
|
||||||
if (event && isCtrlClick(event)) {
|
if (event && isCtrlClick(event)) return editStyle("texture");
|
||||||
editStyle("texture");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$("#texture").fadeOut();
|
$("#texture").fadeOut();
|
||||||
turnButtonOff("toggleTexture");
|
turnButtonOff("toggleTexture");
|
||||||
}
|
}
|
||||||
|
|
@ -1563,7 +1559,7 @@ const getPin = (shape = "bubble", fill = "#fff", stroke = "#000") => {
|
||||||
function drawMarker(marker, rescale = 1) {
|
function drawMarker(marker, rescale = 1) {
|
||||||
const {i, icon, x, y, dx = 50, dy = 50, px = 12, size = 30, pin, fill, stroke} = marker;
|
const {i, icon, x, y, dx = 50, dy = 50, px = 12, size = 30, pin, fill, stroke} = marker;
|
||||||
const id = `marker${i}`;
|
const id = `marker${i}`;
|
||||||
const zoomSize = rescale ? Math.max(rn(size / 5 + 24 / scale, 2), 1) : 1;
|
const zoomSize = rescale ? Math.max(rn(size / 5 + 24 / scale, 2), 1) : size;
|
||||||
const viewX = rn(x - zoomSize / 2, 1);
|
const viewX = rn(x - zoomSize / 2, 1);
|
||||||
const viewY = rn(y - zoomSize, 1);
|
const viewY = rn(y - zoomSize, 1);
|
||||||
const pinHTML = getPin(pin, fill, stroke);
|
const pinHTML = getPin(pin, fill, stroke);
|
||||||
|
|
@ -1674,21 +1670,21 @@ function drawEmblems() {
|
||||||
const validBurgs = burgs.filter(b => b.i && !b.removed && b.coa && b.coaSize != 0);
|
const validBurgs = burgs.filter(b => b.i && !b.removed && b.coa && b.coaSize != 0);
|
||||||
|
|
||||||
const getStateEmblemsSize = () => {
|
const getStateEmblemsSize = () => {
|
||||||
const startSize = Math.min(Math.max((graphHeight + graphWidth) / 40, 10), 100);
|
const startSize = minmax((graphHeight + graphWidth) / 40, 10, 100);
|
||||||
const statesMod = 1 + validStates.length / 100 - (15 - validStates.length) / 200; // states number modifier
|
const statesMod = 1 + validStates.length / 100 - (15 - validStates.length) / 200; // states number modifier
|
||||||
const sizeMod = +document.getElementById("emblemsStateSizeInput").value || 1;
|
const sizeMod = +document.getElementById("emblemsStateSizeInput").value || 1;
|
||||||
return rn((startSize / statesMod) * sizeMod); // target size ~50px on 1536x754 map with 15 states
|
return rn((startSize / statesMod) * sizeMod); // target size ~50px on 1536x754 map with 15 states
|
||||||
};
|
};
|
||||||
|
|
||||||
const getProvinceEmblemsSize = () => {
|
const getProvinceEmblemsSize = () => {
|
||||||
const startSize = Math.min(Math.max((graphHeight + graphWidth) / 100, 5), 70);
|
const startSize = minmax((graphHeight + graphWidth) / 100, 5, 70);
|
||||||
const provincesMod = 1 + validProvinces.length / 1000 - (115 - validProvinces.length) / 1000; // states number modifier
|
const provincesMod = 1 + validProvinces.length / 1000 - (115 - validProvinces.length) / 1000; // states number modifier
|
||||||
const sizeMod = +document.getElementById("emblemsProvinceSizeInput").value || 1;
|
const sizeMod = +document.getElementById("emblemsProvinceSizeInput").value || 1;
|
||||||
return rn((startSize / provincesMod) * sizeMod); // target size ~20px on 1536x754 map with 115 provinces
|
return rn((startSize / provincesMod) * sizeMod); // target size ~20px on 1536x754 map with 115 provinces
|
||||||
};
|
};
|
||||||
|
|
||||||
const getBurgEmblemSize = () => {
|
const getBurgEmblemSize = () => {
|
||||||
const startSize = Math.min(Math.max((graphHeight + graphWidth) / 185, 2), 50);
|
const startSize = minmax((graphHeight + graphWidth) / 185, 2, 50);
|
||||||
const burgsMod = 1 + validBurgs.length / 1000 - (450 - validBurgs.length) / 1000; // states number modifier
|
const burgsMod = 1 + validBurgs.length / 1000 - (450 - validBurgs.length) / 1000; // states number modifier
|
||||||
const sizeMod = +document.getElementById("emblemsBurgSizeInput").value || 1;
|
const sizeMod = +document.getElementById("emblemsBurgSizeInput").value || 1;
|
||||||
return rn((startSize / burgsMod) * sizeMod); // target size ~8.5px on 1536x754 map with 450 burgs
|
return rn((startSize / burgsMod) * sizeMod); // target size ~8.5px on 1536x754 map with 450 burgs
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ function editMarker(markerI) {
|
||||||
|
|
||||||
elSelected = d3.select(element).raise().call(d3.drag().on("start", dragMarker)).classed("draggable", true);
|
elSelected = d3.select(element).raise().call(d3.drag().on("start", dragMarker)).classed("draggable", true);
|
||||||
|
|
||||||
|
if (document.getElementById("notesEditor").offsetParent) editNotes(element.id, element.id);
|
||||||
|
|
||||||
// dom elements
|
// dom elements
|
||||||
const markerType = document.getElementById("markerType");
|
const markerType = document.getElementById("markerType");
|
||||||
const markerIcon = document.getElementById("markerIcon");
|
const markerIcon = document.getElementById("markerIcon");
|
||||||
|
|
|
||||||
|
|
@ -75,14 +75,21 @@ function overviewMilitary() {
|
||||||
const sortData = options.military.map(u => `data-${u.name}="${getForces(u)}"`).join(" ");
|
const sortData = options.military.map(u => `data-${u.name}="${getForces(u)}"`).join(" ");
|
||||||
const lineData = options.military.map(u => `<div data-type="${u.name}" data-tip="State ${u.name} units number">${getForces(u)}</div>`).join(" ");
|
const lineData = options.military.map(u => `<div data-type="${u.name}" data-tip="State ${u.name} units number">${getForces(u)}</div>`).join(" ");
|
||||||
|
|
||||||
lines += `<div class="states" data-id=${s.i} data-state="${s.name}" ${sortData} data-total="${total}" data-population="${population}" data-rate="${rate}" data-alert="${s.alert}">
|
lines += `<div class="states" data-id=${s.i} data-state="${
|
||||||
<svg data-tip="${s.fullName}" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${s.color}" class="fillRect"></svg>
|
s.name
|
||||||
|
}" ${sortData} data-total="${total}" data-population="${population}" data-rate="${rate}" data-alert="${s.alert}">
|
||||||
|
<svg data-tip="${s.fullName}" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${
|
||||||
|
s.color
|
||||||
|
}" class="fillRect"></svg>
|
||||||
<input data-tip="${s.fullName}" style="width:6em" value="${s.name}" readonly>
|
<input data-tip="${s.fullName}" style="width:6em" value="${s.name}" readonly>
|
||||||
${lineData}
|
${lineData}
|
||||||
<div data-type="total" data-tip="Total state military personnel (considering crew)" style="font-weight: bold">${si(total)}</div>
|
<div data-type="total" data-tip="Total state military personnel (considering crew)" style="font-weight: bold">${si(total)}</div>
|
||||||
<div data-type="population" data-tip="State population">${si(population)}</div>
|
<div data-type="population" data-tip="State population">${si(population)}</div>
|
||||||
<div data-type="rate" data-tip="Military personnel rate (% of state population). Depends on war alert">${rn(rate, 2)}%</div>
|
<div data-type="rate" data-tip="Military personnel rate (% of state population). Depends on war alert">${rn(rate, 2)}%</div>
|
||||||
<input data-tip="War Alert. Editable modifier to military forces number, depends of political situation" style="width:4.1em" type="number" min=0 step=.01 value="${rn(s.alert, 2)}">
|
<input data-tip="War Alert. Editable modifier to military forces number, depends of political situation" style="width:4.1em" type="number" min=0 step=.01 value="${rn(
|
||||||
|
s.alert,
|
||||||
|
2
|
||||||
|
)}">
|
||||||
<span data-tip="Show regiments list" class="icon-list-bullet pointer"></span>
|
<span data-tip="Show regiments list" class="icon-list-bullet pointer"></span>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
@ -145,7 +152,15 @@ function overviewMilitary() {
|
||||||
if (!layerIsOn("toggleStates")) return;
|
if (!layerIsOn("toggleStates")) return;
|
||||||
const d = regions.select("#state" + state).attr("d");
|
const d = regions.select("#state" + state).attr("d");
|
||||||
|
|
||||||
const path = debug.append("path").attr("class", "highlight").attr("d", d).attr("fill", "none").attr("stroke", "red").attr("stroke-width", 1).attr("opacity", 1).attr("filter", "url(#blur1)");
|
const path = debug
|
||||||
|
.append("path")
|
||||||
|
.attr("class", "highlight")
|
||||||
|
.attr("d", d)
|
||||||
|
.attr("fill", "none")
|
||||||
|
.attr("stroke", "red")
|
||||||
|
.attr("stroke-width", 1)
|
||||||
|
.attr("opacity", 1)
|
||||||
|
.attr("filter", "url(#blur1)");
|
||||||
|
|
||||||
const l = path.node().getTotalLength(),
|
const l = path.node().getTotalLength(),
|
||||||
dur = (l + 5000) / 2;
|
dur = (l + 5000) / 2;
|
||||||
|
|
@ -199,9 +214,9 @@ function overviewMilitary() {
|
||||||
|
|
||||||
function militaryCustomize() {
|
function militaryCustomize() {
|
||||||
const types = ["melee", "ranged", "mounted", "machinery", "naval", "armored", "aviation", "magical"];
|
const types = ["melee", "ranged", "mounted", "machinery", "naval", "armored", "aviation", "magical"];
|
||||||
const table = document.getElementById("militaryOptions").querySelector("tbody");
|
const tableBody = document.getElementById("militaryOptions").querySelector("tbody");
|
||||||
removeUnitLines();
|
removeUnitLines();
|
||||||
options.military.map(u => addUnitLine(u));
|
options.military.map(unit => addUnitLine(unit));
|
||||||
|
|
||||||
$("#militaryOptions").dialog({
|
$("#militaryOptions").dialog({
|
||||||
title: "Edit Military Units",
|
title: "Edit Military Units",
|
||||||
|
|
@ -218,43 +233,132 @@ function overviewMilitary() {
|
||||||
},
|
},
|
||||||
open: function () {
|
open: function () {
|
||||||
const buttons = $(this).dialog("widget").find(".ui-dialog-buttonset > button");
|
const buttons = $(this).dialog("widget").find(".ui-dialog-buttonset > button");
|
||||||
buttons[0].addEventListener("mousemove", () => tip("Apply military units settings. <span style='color:#cb5858'>All forces will be recalculated!</span>"));
|
buttons[0].addEventListener("mousemove", () =>
|
||||||
|
tip("Apply military units settings. <span style='color:#cb5858'>All forces will be recalculated!</span>")
|
||||||
|
);
|
||||||
buttons[1].addEventListener("mousemove", () => tip("Add new military unit to the table"));
|
buttons[1].addEventListener("mousemove", () => tip("Add new military unit to the table"));
|
||||||
buttons[2].addEventListener("mousemove", () => tip("Restore default military units and settings"));
|
buttons[2].addEventListener("mousemove", () => tip("Restore default military units and settings"));
|
||||||
buttons[3].addEventListener("mousemove", () => tip("Close the window without saving the changes"));
|
buttons[3].addEventListener("mousemove", () => tip("Close the window without saving the changes"));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (modules.overviewMilitaryCustomize) return;
|
||||||
|
modules.overviewMilitaryCustomize = true;
|
||||||
|
|
||||||
|
tableBody.addEventListener("click", event => {
|
||||||
|
const el = event.target;
|
||||||
|
if (el.tagName !== "BUTTON") return;
|
||||||
|
const type = el.dataset.type;
|
||||||
|
|
||||||
|
if (type === "icon") return selectIcon(el.innerHTML, v => (el.innerHTML = v));
|
||||||
|
if (type === "biomes") {
|
||||||
|
const {i, name, color} = biomesData;
|
||||||
|
const biomesArray = Array(i.length).fill(null);
|
||||||
|
const biomes = biomesArray.map((_, i) => ({i, name: name[i], color: color[i]}));
|
||||||
|
return selectLimitation(el, biomes);
|
||||||
|
}
|
||||||
|
if (type === "states") return selectLimitation(el, pack.states);
|
||||||
|
if (type === "cultures") return selectLimitation(el, pack.cultures);
|
||||||
|
if (type === "religions") return selectLimitation(el, pack.religions);
|
||||||
|
});
|
||||||
|
|
||||||
function removeUnitLines() {
|
function removeUnitLines() {
|
||||||
table.querySelectorAll("tr").forEach(el => el.remove());
|
tableBody.querySelectorAll("tr").forEach(el => el.remove());
|
||||||
}
|
}
|
||||||
|
|
||||||
function addUnitLine(u) {
|
function getLimitValue(attr) {
|
||||||
|
return attr?.join(",") || "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLimitText(attr) {
|
||||||
|
return attr?.length ? "some" : "all";
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLimitTip(attr, data) {
|
||||||
|
if (!attr || !attr.length) return "";
|
||||||
|
return attr.map(i => data?.[i]?.name || "").join(", ");
|
||||||
|
}
|
||||||
|
|
||||||
|
function addUnitLine(unit) {
|
||||||
const row = document.createElement("tr");
|
const row = document.createElement("tr");
|
||||||
row.innerHTML = `<td><button type="button" data-tip="Click to select unit icon">${u.icon || " "}</button></td>
|
const typeOptions = types.map(t => `<option ${unit.type === t ? "selected" : ""} value="${t}">${t}</option>`).join(" ");
|
||||||
<td><input data-tip="Type unit name. If name is changed for existing unit, old unit will be replaced" value="${u.name}"></td>
|
const getLimitButton = attr =>
|
||||||
<td><input data-tip="Enter conscription percentage for rural population" type="number" min=0 max=100 step=.01 value="${u.rural}"></td>
|
`<button
|
||||||
<td><input data-tip="Enter conscription percentage for urban population" type="number" min=0 max=100 step=.01 value="${u.urban}"></td>
|
data-tip="Select allowed ${attr}"
|
||||||
<td><input data-tip="Enter average number of people in crew (used for total personnel calculation)" type="number" min=1 step=1 value="${u.crew}"></td>
|
data-type="${attr}"
|
||||||
<td><input data-tip="Enter military power (used for battle simulation)" type="number" min=0 step=.1 value="${u.power}"></td>
|
title="${getLimitTip(unit[attr], pack[attr])}"
|
||||||
<td><select data-tip="Select unit type to apply special rules on forces recalculation">${types.map(t => `<option ${u.type === t ? "selected" : ""} value="${t}">${t}</option>`).join(" ")}</select></td>
|
data-value="${getLimitValue(unit[attr])}">
|
||||||
|
${getLimitText(unit[attr])}
|
||||||
|
</button>`;
|
||||||
|
|
||||||
|
row.innerHTML = `<td><button data-type="icon" data-tip="Click to select unit icon">${unit.icon || " "}</button></td>
|
||||||
|
<td><input data-tip="Type unit name. If name is changed for existing unit, old unit will be replaced" value="${unit.name}"></td>
|
||||||
|
<td>${getLimitButton("biomes")}</td>
|
||||||
|
<td>${getLimitButton("states")}</td>
|
||||||
|
<td>${getLimitButton("cultures")}</td>
|
||||||
|
<td>${getLimitButton("religions")}</td>
|
||||||
|
<td><input data-tip="Enter conscription percentage for rural population" type="number" min=0 max=100 step=.01 value="${unit.rural}"></td>
|
||||||
|
<td><input data-tip="Enter conscription percentage for urban population" type="number" min=0 max=100 step=.01 value="${unit.urban}"></td>
|
||||||
|
<td><input data-tip="Enter average number of people in crew (for total personnel calculation)" type="number" min=1 step=1 value="${unit.crew}"></td>
|
||||||
|
<td><input data-tip="Enter military power (used for battle simulation)" type="number" min=0 step=.1 value="${unit.power}"></td>
|
||||||
|
<td><select data-tip="Select unit type to apply special rules on forces recalculation">${typeOptions}</select></td>
|
||||||
<td data-tip="Check if unit is separate and can be stacked only with units of the same type">
|
<td data-tip="Check if unit is separate and can be stacked only with units of the same type">
|
||||||
<input id="${u.name}Separate" type="checkbox" class="checkbox" ${u.separate ? "checked" : ""}>
|
<input id="${unit.name}Separate" type="checkbox" class="checkbox" ${unit.separate ? "checked" : ""}>
|
||||||
<label for="${u.name}Separate" class="checkbox-label"></label></td>
|
<label for="${unit.name}Separate" class="checkbox-label"></label></td>
|
||||||
<td data-tip="Remove the unit"><span data-tip="Remove unit type" class="icon-trash-empty pointer" onclick="this.parentElement.parentElement.remove();"></span></td>`;
|
<td data-tip="Remove the unit"><span data-tip="Remove unit type" class="icon-trash-empty pointer" onclick="this.parentElement.parentElement.remove();"></span></td>`;
|
||||||
row.querySelector("button").addEventListener("click", function (e) {
|
tableBody.appendChild(row);
|
||||||
selectIcon(this.innerHTML, v => (this.innerHTML = v));
|
|
||||||
});
|
|
||||||
table.appendChild(row);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function restoreDefaultUnits() {
|
function restoreDefaultUnits() {
|
||||||
removeUnitLines();
|
removeUnitLines();
|
||||||
Military.getDefaultOptions().map(u => addUnitLine(u));
|
Military.getDefaultOptions().map(unit => addUnitLine(unit));
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectLimitation(el, data) {
|
||||||
|
const type = el.dataset.type;
|
||||||
|
const value = el.dataset.value;
|
||||||
|
const initial = value ? value.split(",").map(v => +v) : [];
|
||||||
|
|
||||||
|
const lines = data.slice(1).map(
|
||||||
|
({i, name, fullName, color}) =>
|
||||||
|
`<tr data-tip="${name}"><td><span style="color:${color}">⬤</span></td>
|
||||||
|
<td><input id="el${i}" type="checkbox" class="checkbox" ${!initial.length || initial.includes(i) ? "checked" : ""} >
|
||||||
|
<label for="el${i}" class="checkbox-label">${fullName || name}</label>
|
||||||
|
</td></tr>`
|
||||||
|
);
|
||||||
|
alertMessage.innerHTML = `<b>Limit unit by ${type}:</b><div style="margin-top:.3em" class="table"><table><tbody>${lines.join("")}</tbody></table></div>`;
|
||||||
|
|
||||||
|
$("#alert").dialog({
|
||||||
|
width: fitContent(),
|
||||||
|
title: `Limit unit`,
|
||||||
|
buttons: {
|
||||||
|
Invert: function () {
|
||||||
|
alertMessage.querySelectorAll("input").forEach(el => (el.checked = !el.checked));
|
||||||
|
},
|
||||||
|
Apply: function () {
|
||||||
|
const inputs = Array.from(alertMessage.querySelectorAll("input"));
|
||||||
|
const selected = inputs.reduce((acc, input, index) => {
|
||||||
|
if (input.checked) acc.push(index + 1);
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (!selected.length) return tip("Select at least one element", false, "error");
|
||||||
|
|
||||||
|
const allAreSelected = selected.length === inputs.length;
|
||||||
|
el.dataset.value = allAreSelected ? "" : selected.join(",");
|
||||||
|
el.innerHTML = allAreSelected ? "all" : "some";
|
||||||
|
el.setAttribute("title", getLimitTip(selected, data));
|
||||||
|
$(this).dialog("close");
|
||||||
|
},
|
||||||
|
Cancel: function () {
|
||||||
|
$(this).dialog("close");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function applyMilitaryOptions() {
|
function applyMilitaryOptions() {
|
||||||
const unitLines = Array.from(table.querySelectorAll("tr"));
|
const unitLines = Array.from(tableBody.querySelectorAll("tr"));
|
||||||
const names = unitLines.map(r => r.querySelector("input").value.replace(/[&\/\\#, +()$~%.'":*?<>{}]/g, "_"));
|
const names = unitLines.map(r => r.querySelector("input").value.replace(/[&\/\\#, +()$~%.'":*?<>{}]/g, "_"));
|
||||||
if (new Set(names).size !== names.length) {
|
if (new Set(names).size !== names.length) {
|
||||||
tip("All units should have unique names", false, "error");
|
tip("All units should have unique names", false, "error");
|
||||||
|
|
@ -263,14 +367,22 @@ function overviewMilitary() {
|
||||||
|
|
||||||
$("#militaryOptions").dialog("close");
|
$("#militaryOptions").dialog("close");
|
||||||
options.military = unitLines.map((r, i) => {
|
options.military = unitLines.map((r, i) => {
|
||||||
const [icon, name, rural, urban, crew, power, type, separate] = Array.from(r.querySelectorAll("input, select, button")).map(d => {
|
const elements = Array.from(r.querySelectorAll("input, button, select"));
|
||||||
let value = d.value;
|
const [icon, name, biomes, states, cultures, religions, rural, urban, crew, power, type, separate] = elements.map(el => {
|
||||||
if (d.type === "number") value = +d.value || 0;
|
const {type, value} = el.dataset || {};
|
||||||
if (d.type === "checkbox") value = +d.checked || 0;
|
if (type === "icon") return el.innerHTML || "⠀";
|
||||||
if (d.type === "button") value = d.innerHTML || "⠀";
|
if (type) return value ? value.split(",").map(v => parseInt(v)) : null;
|
||||||
return value;
|
if (el.type === "number") return +el.value || 0;
|
||||||
|
if (el.type === "checkbox") return +el.checked || 0;
|
||||||
|
return el.value;
|
||||||
});
|
});
|
||||||
return {icon, name: names[i], rural, urban, crew, power, type, separate};
|
|
||||||
|
const unit = {icon, name: names[i], rural, urban, crew, power, type, separate};
|
||||||
|
if (biomes) unit.biomes = biomes;
|
||||||
|
if (states) unit.states = states;
|
||||||
|
if (cultures) unit.cultures = cultures;
|
||||||
|
if (religions) unit.religions = religions;
|
||||||
|
return unit;
|
||||||
});
|
});
|
||||||
localStorage.setItem("military", JSON.stringify(options.military));
|
localStorage.setItem("military", JSON.stringify(options.military));
|
||||||
Military.generate();
|
Military.generate();
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
function editNotes(id, name) {
|
function editNotes(id, name) {
|
||||||
// update list of objects
|
// update list of objects
|
||||||
const select = document.getElementById("notesSelect");
|
const select = document.getElementById("notesSelect");
|
||||||
|
|
@ -8,11 +9,12 @@ function editNotes(id, name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// initiate pell (html editor)
|
// initiate pell (html editor)
|
||||||
|
const notesText = document.getElementById("notesText");
|
||||||
|
notesText.innerHTML = "";
|
||||||
const editor = Pell.init({
|
const editor = Pell.init({
|
||||||
element: document.getElementById("notesText"),
|
element: notesText,
|
||||||
onChange: html => {
|
onChange: html => {
|
||||||
const id = document.getElementById("notesSelect").value;
|
const note = notes.find(note => note.id === select.value);
|
||||||
const note = notes.find(note => note.id === id);
|
|
||||||
if (!note) return;
|
if (!note) return;
|
||||||
note.legend = html;
|
note.legend = html;
|
||||||
showNote(note);
|
showNote(note);
|
||||||
|
|
@ -43,8 +45,7 @@ function editNotes(id, name) {
|
||||||
title: "Notes Editor",
|
title: "Notes Editor",
|
||||||
minWidth: "40em",
|
minWidth: "40em",
|
||||||
width: "50vw",
|
width: "50vw",
|
||||||
position: {my: "center", at: "center", of: "svg"},
|
position: {my: "center", at: "center", of: "svg"}
|
||||||
close: () => (notesText.innerHTML = "")
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (modules.editNotes) return;
|
if (modules.editNotes) return;
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ if (localStorage.getItem("disable_click_arrow_tooltip")) {
|
||||||
|
|
||||||
// Show options pane on trigger click
|
// Show options pane on trigger click
|
||||||
function showOptions(event) {
|
function showOptions(event) {
|
||||||
track("click", "show options");
|
|
||||||
if (!localStorage.getItem("disable_click_arrow_tooltip")) {
|
if (!localStorage.getItem("disable_click_arrow_tooltip")) {
|
||||||
clearMainTip();
|
clearMainTip();
|
||||||
localStorage.setItem("disable_click_arrow_tooltip", true);
|
localStorage.setItem("disable_click_arrow_tooltip", true);
|
||||||
|
|
@ -76,7 +75,6 @@ document
|
||||||
|
|
||||||
// show popup with a list of Patreon supportes (updated manually, to be replaced with API call)
|
// show popup with a list of Patreon supportes (updated manually, to be replaced with API call)
|
||||||
function showSupporters() {
|
function showSupporters() {
|
||||||
track("click", "show supporters");
|
|
||||||
const supporters = `Aaron Meyer,Ahmad Amerih,AstralJacks,aymeric,Billy Dean Goehring,Branndon Edwards,Chase Mayers,Curt Flood,cyninge,Dino Princip,
|
const supporters = `Aaron Meyer,Ahmad Amerih,AstralJacks,aymeric,Billy Dean Goehring,Branndon Edwards,Chase Mayers,Curt Flood,cyninge,Dino Princip,
|
||||||
E.M. White,es,Fondue,Fritjof Olsson,Gatsu,Johan Fröberg,Jonathan Moore,Joseph Miranda,Kate,KC138,Luke Nelson,Markus Finster,Massimo Vella,Mikey,
|
E.M. White,es,Fondue,Fritjof Olsson,Gatsu,Johan Fröberg,Jonathan Moore,Joseph Miranda,Kate,KC138,Luke Nelson,Markus Finster,Massimo Vella,Mikey,
|
||||||
Nathan Mitchell,Paavi1,Pat,Ryan Westcott,Sasquatch,Shawn Spencer,Sizz_TV,Timothée CALLET,UTG community,Vlad Tomash,Wil Sisney,William Merriott,
|
Nathan Mitchell,Paavi1,Pat,Ryan Westcott,Sasquatch,Shawn Spencer,Sizz_TV,Timothée CALLET,UTG community,Vlad Tomash,Wil Sisney,William Merriott,
|
||||||
|
|
@ -91,19 +89,18 @@ function showSupporters() {
|
||||||
Maxwell Hill,Drunken_Legends,rob bee,Jesse Holmes,YYako,Detocroix,Anoplexian,Hannah,Paul,Sandra Krohn,Lucid,Richard Keating,Allen Varney,Rick Falkvinge,
|
Maxwell Hill,Drunken_Legends,rob bee,Jesse Holmes,YYako,Detocroix,Anoplexian,Hannah,Paul,Sandra Krohn,Lucid,Richard Keating,Allen Varney,Rick Falkvinge,
|
||||||
Seth Fusion,Adam Butler,Gus,StroboWolf,Sadie Blackthorne,Zewen Senpai,Dell McKnight,Oneiris,Darinius Dragonclaw Studios,Christopher Whitney,Rhodes HvZ,
|
Seth Fusion,Adam Butler,Gus,StroboWolf,Sadie Blackthorne,Zewen Senpai,Dell McKnight,Oneiris,Darinius Dragonclaw Studios,Christopher Whitney,Rhodes HvZ,
|
||||||
Jeppe Skov Jensen,María Martín López,Martin Seeger,Annie Rishor,Aram Sabatés,MadNomadMedia,Eric Foley,Vito Martono,James H. Anthony,Kevin Cossutta,
|
Jeppe Skov Jensen,María Martín López,Martin Seeger,Annie Rishor,Aram Sabatés,MadNomadMedia,Eric Foley,Vito Martono,James H. Anthony,Kevin Cossutta,
|
||||||
Thirty-OneR ,ThatGuyGW ,Dee Chiu,MontyBoosh ,Achillain ,Jaden ,SashaTK,Steve Johnson,Eric Foley,Vito Martono,James H. Anthony,Kevin Cossutta,Thirty-OneR,
|
Thirty-OneR,ThatGuyGW,Dee Chiu,MontyBoosh,Achillain,Jaden,SashaTK,Steve Johnson,Pierrick Bertrand,Jared Kennedy,Dylan Devenny,Kyle Robertson,
|
||||||
ThatGuyGW,Dee Chiu,MontyBoosh,Achillain,Jaden,SashaTK,Steve Johnson,Pierrick Bertrand,Jared Kennedy,Dylan Devenny,Kyle Robertson,Andrew Rostaing,Daniel Gill,
|
Andrew Rostaing,Daniel Gill,Char,Jack,Barna Csíkos,Ian Rousseau,Nicholas Grabstas,Tom Van Orden jr,Bryan Brake,Akylos,Riley Seaman,MaxOliver,Evan-DiLeo,
|
||||||
Char,Jack,Barna Csíkos,Ian Rousseau,Nicholas Grabstas,Tom Van Orden jr,Bryan Brake,Akylos,Riley Seaman,MaxOliver,Evan-DiLeo,Alex Debus,Joshua Vaught,
|
Alex Debus,Joshua Vaught,Kyle S,Eric Moore,Dean Dunakin,Uniquenameosaurus,WarWizardGames,Chance Mena,Jan Ka,Miguel Alejandro,Dalton Clark,Simon Drapeau,
|
||||||
Kyle S,Eric Moore,Dean Dunakin,Uniquenameosaurus,WarWizardGames,Chance Mena,Jan Ka,Miguel Alejandro,Dalton Clark,Simon Drapeau,Radovan Zapletal,Jmmat6,
|
Radovan Zapletal,Jmmat6,Justa Badge,Blargh Blarghmoomoo,Vanessa Anjos,Grant A. Murray,Akirsop,Rikard Wolff,Jake Fish,teco 47,Antiroo,Jakob Siegel,
|
||||||
Justa Badge,Blargh Blarghmoomoo,Vanessa Anjos,Grant A. Murray,Akirsop,Rikard Wolff,Jake Fish,teco 47,Antiroo,Jakob Siegel,Guilherme Aguiar,Jarno Hallikainen,
|
Guilherme Aguiar,Jarno Hallikainen,Justin Mcclain,Kristin Chernoff,Rowland Kingman,Esther Busch,Grayson McClead,Austin,Hakon the Viking,Chad Riley,
|
||||||
Justin Mcclain,Kristin Chernoff,Rowland Kingman,Esther Busch,Grayson McClead,Austin,Hakon the Viking,Chad Riley,Cooper Counts,Patrick Jones,Clonetone,
|
Cooper Counts,Patrick Jones,Clonetone,PlayByMail.Net,Brad Wardell,Lance Saba,Egoensis,Brea Richards,Tiber,Chris Bloom,Maxim Lowe,Aquelion,
|
||||||
PlayByMail.Net,Brad Wardell,Lance Saba,Egoensis,Brea Richards,Tiber,Chris Bloom,Maxim Lowe,Aquelion,Page One Project,Spencer Morris,Paul Ingram,
|
Page One Project,Spencer Morris,Paul Ingram,Dust Bunny,Adrian Wright,Eric Alexander Cartaya,GameNight,Thomas Mortensen Hansen,Zklaus,Drinarius,
|
||||||
Dust Bunny,Adrian Wright,Eric Alexander Cartaya,GameNight,Thomas Mortensen Hansen,Zklaus,Drinarius,Ed Wright,Lon Varnadore,Crys Cain,Heaven N Lee,
|
Ed Wright,Lon Varnadore,Crys Cain,Heaven N Lee,Jeffrey Henning,Lazer Elf,Jordan Bellah,Alex Beard,Kass Frisson,Petro Lombaard,Emanuel Pietri,Rox,
|
||||||
Jeffrey Henning,Lazer Elf,Jordan Bellah,Alex Beard,Kass Frisson,Petro Lombaard,Emanuel Pietri,Rox,PinkEvil,Gavin Madrigal,Martin Lorber,Prince of Morgoth,
|
PinkEvil,Gavin Madrigal,Martin Lorber,Prince of Morgoth,Jaryd Armstrong,Andrew Pirkola,ThyHolyDevil,Gary Smith,Tyshaun Wise,Ethan Cook,Jon Stroman,
|
||||||
Jaryd Armstrong,Andrew Pirkola,ThyHolyDevil,Gary Smith,Tyshaun Wise,Ethan Cook,Jon Stroman,Nobody679,良义 金,Chris Gray,Phoenix Boatwright,Mackenzie,
|
Nobody679,良义 金,Chris Gray,Phoenix Boatwright,Mackenzie,Milo Cohen,Jason Matthew Wuerfel,Rasmus Legêne,Andrew Hines,Wexxler,Espen Sæverud,Binks,
|
||||||
Milo Cohen,Jason Matthew Wuerfel,Rasmus Legêne,Andrew Hines,Wexxler,Espen Sæverud,Binks,Dominick Ormsby,Linn Browning,Václav Švec,Alan Buehne,
|
Dominick Ormsby,Linn Browning,Václav Švec,Alan Buehne,George J.Lekkas,Alexandre Boivin,Tommy Mayfield,Skylar Mangum-Turner,Karen Blythe,Stefan Gugerel,
|
||||||
George J.Lekkas,Alexandre Boivin,Tommy Mayfield,Skylar Mangum-Turner,Karen Blythe,Stefan Gugerel,Mike Conley,Xavier privé,Hope You're Well,
|
Mike Conley,Xavier privé,Hope You're Well,Mark Sprietsma,Robert Landry,Nick Mowry,steve hall,Markell,Josh Wren,Neutrix,BLRageQuit,Rocky,Dario Spadavecchia`;
|
||||||
Mark Sprietsma,Robert Landry,Nick Mowry"`;
|
|
||||||
|
|
||||||
const array = supporters
|
const array = supporters
|
||||||
.replace(/(?:\r\n|\r|\n)/g, "")
|
.replace(/(?:\r\n|\r|\n)/g, "")
|
||||||
|
|
@ -476,10 +473,10 @@ function changeDialogsTheme(themeColor, transparency) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeZoomExtent(value) {
|
function changeZoomExtent(value) {
|
||||||
const min = Math.max(+zoomExtentMin.value, 0.01),
|
const min = Math.max(+zoomExtentMin.value, 0.01);
|
||||||
max = Math.min(+zoomExtentMax.value, 200);
|
const max = Math.min(+zoomExtentMax.value, 200);
|
||||||
zoom.scaleExtent([min, max]);
|
zoom.scaleExtent([min, max]);
|
||||||
const scale = Math.max(Math.min(+value, 200), 0.01);
|
const scale = minmax(+value, 0.01, 200);
|
||||||
zoom.scaleTo(svg, scale);
|
zoom.scaleTo(svg, scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -520,7 +517,7 @@ function applyStoredOptions() {
|
||||||
|
|
||||||
uiSizeInput.max = uiSizeOutput.max = getUImaxSize();
|
uiSizeInput.max = uiSizeOutput.max = getUImaxSize();
|
||||||
if (localStorage.getItem("uiSize")) changeUIsize(localStorage.getItem("uiSize"));
|
if (localStorage.getItem("uiSize")) changeUIsize(localStorage.getItem("uiSize"));
|
||||||
else changeUIsize(Math.max(Math.min(rn(mapWidthInput.value / 1280, 1), 2.5), 1));
|
else changeUIsize(minmax(rn(mapWidthInput.value / 1280, 1), 1, 2.5));
|
||||||
|
|
||||||
// search params overwrite stored and default options
|
// search params overwrite stored and default options
|
||||||
const params = new URL(window.location.href).searchParams;
|
const params = new URL(window.location.href).searchParams;
|
||||||
|
|
|
||||||
|
|
@ -335,7 +335,6 @@ styleFilterInput.addEventListener("change", function () {
|
||||||
|
|
||||||
styleTextureInput.addEventListener("change", function () {
|
styleTextureInput.addEventListener("change", function () {
|
||||||
if (this.value === "none") texture.select("image").attr("xlink:href", "");
|
if (this.value === "none") texture.select("image").attr("xlink:href", "");
|
||||||
if (this.value === "default") texture.select("image").attr("xlink:href", getDefaultTexture());
|
|
||||||
else getBase64(this.value, base64 => texture.select("image").attr("xlink:href", base64));
|
else getBase64(this.value, base64 => texture.select("image").attr("xlink:href", base64));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -469,7 +469,10 @@ function addLabelOnClick() {
|
||||||
const name = Names.getCulture(culture);
|
const name = Names.getCulture(culture);
|
||||||
const id = getNextId("label");
|
const id = getNextId("label");
|
||||||
|
|
||||||
let group = labels.select("#addedLabels");
|
// use most recently selected label group
|
||||||
|
let selected = labelGroupSelect.value;
|
||||||
|
const symbol = selected ? "#" + selected : "#addedLabels";
|
||||||
|
let group = labels.select(symbol);
|
||||||
if (!group.size())
|
if (!group.size())
|
||||||
group = labels
|
group = labels
|
||||||
.append("g")
|
.append("g")
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
Loading…
Add table
Add a link
Reference in a new issue