mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-22 03:51:23 +01:00
commit
8fd9b82554
30 changed files with 1761 additions and 613 deletions
|
|
@ -2,9 +2,9 @@
|
||||||
|
|
||||||
# Fantasy Map Generator
|
# Fantasy Map Generator
|
||||||
|
|
||||||
Azgaar's _Fantasy Map Generator_ is a free web application generating interactive and highly customizable svg maps based on voronoi diagram.
|
Azgaar's _Fantasy Map Generator_ is a free web application that helps fantasy writers, game masters, and cartographers create and edit fantasy maps.
|
||||||
|
|
||||||
Project is under development, the current version is available on [Github Pages](https://azgaar.github.io/Fantasy-Map-Generator).
|
Link: [azgaar.github.io/Fantasy-Map-Generator](https://azgaar.github.io/Fantasy-Map-Generator).
|
||||||
|
|
||||||
Refer to the [project wiki](https://github.com/Azgaar/Fantasy-Map-Generator/wiki) for guidance. The current progress is tracked in [Trello](https://trello.com/b/7x832DG4/fantasy-map-generator). Some details are covered in my old blog [_Fantasy Maps for fun and glory_](https://azgaar.wordpress.com).
|
Refer to the [project wiki](https://github.com/Azgaar/Fantasy-Map-Generator/wiki) for guidance. The current progress is tracked in [Trello](https://trello.com/b/7x832DG4/fantasy-map-generator). Some details are covered in my old blog [_Fantasy Maps for fun and glory_](https://azgaar.wordpress.com).
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +0,0 @@
|
||||||
Azgaar's Fantasy Map Generator
|
|
||||||
|
|
||||||
Developed by Azgaar (azgaar.fmg@yandex.com) and contributors
|
|
||||||
|
|
||||||
Minsk, 2017-2021. MIT License
|
|
||||||
|
|
||||||
https://github.com/Azgaar/Fantasy-Map-Generator
|
|
||||||
|
|
||||||
To run the tool unzip ALL files and open index.html in browser
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
To get heightmap with correct height scale:
|
To get heightmap with correct height scale:
|
||||||
1. Open tangrams.github.io
|
1. Open https://tangrams.github.io/heightmapper
|
||||||
2. Toggle off auto-exposure
|
2. Toggle off auto-exposure
|
||||||
3. Set max elevation to 2000
|
3. Set max elevation to 2000
|
||||||
4. Set min elevation to -500
|
4. Set min elevation to -500
|
||||||
|
|
|
||||||
14
index.css
14
index.css
|
|
@ -1082,12 +1082,16 @@ tr.battleSurvivors {
|
||||||
font-size: 0.9em;
|
font-size: 0.9em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#battleBody div.battlePhases,
|
|
||||||
#battleBottom div.battleTypes {
|
#battleBottom div.battleTypes {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
background-color: #ffffff30;
|
background-color: #ffffff30;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#battleBody div.battlePhases {
|
||||||
|
position: absolute;
|
||||||
|
background-color: #ffffff30;
|
||||||
|
}
|
||||||
|
|
||||||
#battleBody div.battlePhases > button,
|
#battleBody div.battlePhases > button,
|
||||||
#battleBottom div.battleTypes > button {
|
#battleBottom div.battleTypes > button {
|
||||||
width: 3.2em;
|
width: 3.2em;
|
||||||
|
|
@ -2045,6 +2049,7 @@ div.textual span,
|
||||||
}
|
}
|
||||||
|
|
||||||
#notesLegend {
|
#notesLegend {
|
||||||
|
width: auto;
|
||||||
height: 87%;
|
height: 87%;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
@ -2239,7 +2244,6 @@ svg.button {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.dontAsk {
|
.dontAsk {
|
||||||
margin: 0.9em 0 0 0.6em;
|
margin: 0.9em 0 0 0.6em;
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
|
@ -2338,7 +2342,7 @@ svg.button {
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
body {
|
body {
|
||||||
background: #25252a;
|
background: #25252a;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
75
index.html
75
index.html
|
|
@ -3,14 +3,33 @@
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
|
||||||
<title>Azgaar's Fantasy Map Generator</title>
|
<title>Azgaar's Fantasy Map Generator</title>
|
||||||
<meta name="application-name" content="Azgaar's Fantasy Map Generator" />
|
<meta name="application-name" content="Azgaar's Fantasy Map Generator" />
|
||||||
<meta name="author" content="Azgaar (Max Ganiev)" />
|
<meta name="author" content="Azgaar" />
|
||||||
<meta name="description" content="Azgaar's Fantasy Map Generator and Editor" />
|
<meta
|
||||||
|
name="description"
|
||||||
|
content="Free web app that helps fantasy writers, game masters, and cartographers create and edit fantasy maps"
|
||||||
|
/>
|
||||||
|
|
||||||
<meta property="og:url" content="https://azgaar.github.io/Fantasy-Map-Generator" />
|
<meta property="og:url" content="https://azgaar.github.io/Fantasy-Map-Generator" />
|
||||||
<meta property="og:title" content="Azgaar's Fantasy Map Generator" />
|
<meta property="og:title" content="Azgaar's Fantasy Map Generator" />
|
||||||
<meta property="og:description" content="Web application generating interactive and customizable maps" />
|
<meta
|
||||||
|
property="og:description"
|
||||||
|
content="Free web app that helps fantasy writers, game masters, and cartographers create and edit fantasy maps"
|
||||||
|
/>
|
||||||
<meta property="og:image" content="images/preview.png" />
|
<meta property="og:image" content="images/preview.png" />
|
||||||
|
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta property="twitter:domain" content="azgaar.github.io" />
|
||||||
|
<meta property="twitter:url" content="https://azgaar.github.io/Fantasy-Map-Generator/" />
|
||||||
|
<meta name="twitter:title" content="Azgaar's Fantasy Map Generator" />
|
||||||
|
<meta
|
||||||
|
name="twitter:description"
|
||||||
|
content="Free web app that helps fantasy writers, game masters, and cartographers create and edit fantasy maps"
|
||||||
|
/>
|
||||||
|
<meta name="twitter:image" content="images/preview.png" />
|
||||||
|
|
||||||
<link rel="icon" type="image/png" href="images/icons/favicon-32x32.png" sizes="32x32" />
|
<link rel="icon" type="image/png" href="images/icons/favicon-32x32.png" sizes="32x32" />
|
||||||
<link rel="icon" type="image/png" href="images/icons/favicon-16x16.png" sizes="16x16" />
|
<link rel="icon" type="image/png" href="images/icons/favicon-16x16.png" sizes="16x16" />
|
||||||
<link rel="apple-touch-icon" href="images/icons/maskable_icon_x192.png" />
|
<link rel="apple-touch-icon" href="images/icons/maskable_icon_x192.png" />
|
||||||
|
|
@ -108,7 +127,7 @@
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<link rel="preload" href="index.css?v=1.89.00" as="style" onload="this.onload=null; this.rel='stylesheet'" />
|
<link rel="preload" href="index.css?v=1.89.13" as="style" onload="this.onload=null; this.rel='stylesheet'" />
|
||||||
<link rel="preload" href="icons.css" as="style" onload="this.onload=null; this.rel='stylesheet'" />
|
<link rel="preload" href="icons.css" as="style" onload="this.onload=null; this.rel='stylesheet'" />
|
||||||
<link rel="preload" href="libs/jquery-ui.css" as="style" onload="this.onload=null; this.rel='stylesheet'" />
|
<link rel="preload" href="libs/jquery-ui.css" as="style" onload="this.onload=null; this.rel='stylesheet'" />
|
||||||
</head>
|
</head>
|
||||||
|
|
@ -1237,10 +1256,6 @@
|
||||||
<td><select id="styleStatesBodyFilter" /></td>
|
<td><select id="styleStatesBodyFilter" /></td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
||||||
<tr style="margin-top: 1em">
|
|
||||||
<td><em>Halo is disabled if "Rendering" option is set to "Best performance"</em></td>
|
|
||||||
</tr>
|
|
||||||
|
|
||||||
<tr data-tip="Set states halo effect width">
|
<tr data-tip="Set states halo effect width">
|
||||||
<td>Halo width</td>
|
<td>Halo width</td>
|
||||||
<td>
|
<td>
|
||||||
|
|
@ -1446,7 +1461,7 @@
|
||||||
</p>
|
</p>
|
||||||
<table>
|
<table>
|
||||||
<tr
|
<tr
|
||||||
data-tip="Canvas width and height in pixels. Defines map size on generation, then map size cannot be changed and canvas size changes only visible area. Keep canvas size equal to screen size or less to improve performance. The best aspect ratio for maps is 2:1"
|
data-tip="Canvas width and height in pixels. Defines map size on generation that cannot be changed later. Always keep canvas size equal to your screen size or less. The best option is to use the default value. For full-globe maps use aspect ratio 2:1"
|
||||||
>
|
>
|
||||||
<td></td>
|
<td></td>
|
||||||
<td>Canvas size</td>
|
<td>Canvas size</td>
|
||||||
|
|
@ -1458,7 +1473,7 @@
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<i
|
<i
|
||||||
data-tip="Toggle between screen size and initial canvas size"
|
data-tip="Toggle between the current screen size and the initial canvas size"
|
||||||
id="toggleFullscreen"
|
id="toggleFullscreen"
|
||||||
class="icon-resize-full-alt"
|
class="icon-resize-full-alt"
|
||||||
></i>
|
></i>
|
||||||
|
|
@ -1883,8 +1898,8 @@
|
||||||
<td>Rendering</td>
|
<td>Rendering</td>
|
||||||
<td>
|
<td>
|
||||||
<select id="shapeRendering" data-stored="shapeRendering">
|
<select id="shapeRendering" data-stored="shapeRendering">
|
||||||
<option value="geometricPrecision" selected>Best quality</option>
|
<option value="geometricPrecision">Best quality</option>
|
||||||
<option value="optimizeSpeed">Best performace</option>
|
<option value="optimizeSpeed" selected>Best performace</option>
|
||||||
</select>
|
</select>
|
||||||
</td>
|
</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
|
|
@ -2749,7 +2764,7 @@
|
||||||
<div id="iceEditor" class="dialog" style="display: none">
|
<div id="iceEditor" class="dialog" style="display: none">
|
||||||
<button id="iceEditStyle" data-tip="Edit style in Style Editor" class="icon-brush"></button>
|
<button id="iceEditStyle" data-tip="Edit style in Style Editor" class="icon-brush"></button>
|
||||||
<button id="iceRandomize" data-tip="Randomize Iceberg shape" class="icon-shuffle"></button>
|
<button id="iceRandomize" data-tip="Randomize Iceberg shape" class="icon-shuffle"></button>
|
||||||
<input id="iceSize" data-tip="Change Iceberg size" type="range" min=".05" max="1" step=".01" />
|
<input id="iceSize" data-tip="Change Iceberg size" type="range" min=".05" max="2" step=".01" />
|
||||||
<button id="iceNew" data-tip="Add an Iceberg (click on map)" class="icon-plus"></button>
|
<button id="iceNew" data-tip="Add an Iceberg (click on map)" class="icon-plus"></button>
|
||||||
<button
|
<button
|
||||||
id="iceRemove"
|
id="iceRemove"
|
||||||
|
|
@ -5602,7 +5617,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="iconSelector" style="display: none" class="dialog">
|
<div id="iconSelector" style="display: none" class="dialog">
|
||||||
<table id="iconTable" class="table pointer" style="font-size: 2em; text-align: center"></table>
|
<table id="iconTable" class="table pointer" style="font-size: 2em; text-align: center; width: 100%"></table>
|
||||||
<div style="font-style: italic; font-size: 1.2em; margin: 0.4em 0 0 0.4em">
|
<div style="font-style: italic; font-size: 1.2em; margin: 0.4em 0 0 0.4em">
|
||||||
<span>Select from the list or paste a Unicode character here: </span>
|
<span>Select from the list or paste a Unicode character here: </span>
|
||||||
<input id="iconInput" style="width: 2em" />
|
<input id="iconInput" style="width: 2em" />
|
||||||
|
|
@ -7830,7 +7845,7 @@
|
||||||
<script src="utils/colorUtils.js"></script>
|
<script src="utils/colorUtils.js"></script>
|
||||||
<script src="utils/graphUtils.js?v=1.88.02"></script>
|
<script src="utils/graphUtils.js?v=1.88.02"></script>
|
||||||
<script src="utils/nodeUtils.js"></script>
|
<script src="utils/nodeUtils.js"></script>
|
||||||
<script src="utils/numberUtils.js"></script>
|
<script src="utils/numberUtils.js?v=1.89.08"></script>
|
||||||
<script src="utils/polyfills.js"></script>
|
<script src="utils/polyfills.js"></script>
|
||||||
<script src="utils/probabilityUtils.js?v=1.88.00"></script>
|
<script src="utils/probabilityUtils.js?v=1.88.00"></script>
|
||||||
<script src="utils/stringUtils.js"></script>
|
<script src="utils/stringUtils.js"></script>
|
||||||
|
|
@ -7841,14 +7856,14 @@
|
||||||
<script src="config/heightmap-templates.js"></script>
|
<script src="config/heightmap-templates.js"></script>
|
||||||
<script src="config/precreated-heightmaps.js"></script>
|
<script src="config/precreated-heightmaps.js"></script>
|
||||||
<script src="modules/heightmap-generator.js?v=1.88.00"></script>
|
<script src="modules/heightmap-generator.js?v=1.88.00"></script>
|
||||||
<script src="modules/ocean-layers.js?v=1.87.15"></script>
|
<script src="modules/ocean-layers.js?v=1.89.08"></script>
|
||||||
<script src="modules/river-generator.js"></script>
|
<script src="modules/river-generator.js?v=1.89.13"></script>
|
||||||
<script src="modules/lakes.js"></script>
|
<script src="modules/lakes.js"></script>
|
||||||
<script src="modules/names-generator.js?v=1.87.14"></script>
|
<script src="modules/names-generator.js?v=1.87.14"></script>
|
||||||
<script src="modules/cultures-generator.js?v=1.89.00"></script>
|
<script src="modules/cultures-generator.js?v=1.89.10"></script>
|
||||||
<script src="modules/burgs-and-states.js?v=1.89.00"></script>
|
<script src="modules/burgs-and-states.js?v=1.89.07"></script>
|
||||||
<script src="modules/routes-generator.js"></script>
|
<script src="modules/routes-generator.js"></script>
|
||||||
<script src="modules/religions-generator.js?v=1.89.00"></script>
|
<script src="modules/religions-generator.js?v=1.89.15"></script>
|
||||||
<script src="modules/military-generator.js"></script>
|
<script src="modules/military-generator.js"></script>
|
||||||
<script src="modules/markers-generator.js?v=1.87.13"></script>
|
<script src="modules/markers-generator.js?v=1.87.13"></script>
|
||||||
<script src="modules/coa-generator.js"></script>
|
<script src="modules/coa-generator.js"></script>
|
||||||
|
|
@ -7859,34 +7874,34 @@
|
||||||
<script src="modules/fonts.js"></script>
|
<script src="modules/fonts.js"></script>
|
||||||
<script src="modules/ui/layers.js"></script>
|
<script src="modules/ui/layers.js"></script>
|
||||||
<script src="modules/ui/measurers.js?v=1.87.02"></script>
|
<script src="modules/ui/measurers.js?v=1.87.02"></script>
|
||||||
<script src="modules/ui/stylePresets.js"></script>
|
<script src="modules/ui/stylePresets.js?v=1.89.11"></script>
|
||||||
|
|
||||||
<script src="modules/ui/general.js?v=1.87.03"></script>
|
<script src="modules/ui/general.js?v=1.87.03"></script>
|
||||||
<script src="modules/ui/options.js?v=1.88.02"></script>
|
<script src="modules/ui/options.js?v=1.88.14"></script>
|
||||||
<script src="main.js?v=1.88.02"></script>
|
<script src="main.js?v=1.89.12"></script>
|
||||||
|
|
||||||
<script defer src="modules/relief-icons.js"></script>
|
<script defer src="modules/relief-icons.js"></script>
|
||||||
<script defer src="modules/ui/style.js"></script>
|
<script defer src="modules/ui/style.js"></script>
|
||||||
<script defer src="modules/ui/editors.js?v=1.87.07"></script>
|
<script defer src="modules/ui/editors.js?v=1.89.12"></script>
|
||||||
<script defer src="modules/ui/tools.js?v=1.89.00"></script>
|
<script defer src="modules/ui/tools.js?v=1.89.13"></script>
|
||||||
<script defer src="modules/ui/world-configurator.js"></script>
|
<script defer src="modules/ui/world-configurator.js"></script>
|
||||||
<script defer src="modules/ui/heightmap-editor.js?v=1.88.03"></script>
|
<script defer src="modules/ui/heightmap-editor.js?v=1.89.06"></script>
|
||||||
<script defer src="modules/ui/provinces-editor.js?v=1.89.00"></script>
|
<script defer src="modules/ui/provinces-editor.js?v=1.89.00"></script>
|
||||||
<script defer src="modules/ui/biomes-editor.js"></script>
|
<script defer src="modules/ui/biomes-editor.js"></script>
|
||||||
<script defer src="modules/ui/namesbase-editor.js?v=1.87.10"></script>
|
<script defer src="modules/ui/namesbase-editor.js?v=1.87.10"></script>
|
||||||
<script defer src="modules/ui/elevation-profile.js"></script>
|
<script defer src="modules/ui/elevation-profile.js"></script>
|
||||||
<script defer src="modules/ui/temperature-graph.js"></script>
|
<script defer src="modules/ui/temperature-graph.js"></script>
|
||||||
<script defer src="modules/ui/routes-editor.js"></script>
|
<script defer src="modules/ui/routes-editor.js?v=1.89.04"></script>
|
||||||
<script defer src="modules/ui/ice-editor.js"></script>
|
<script defer src="modules/ui/ice-editor.js?v=1.89.08"></script>
|
||||||
<script defer src="modules/ui/lakes-editor.js?v=1.87.10"></script>
|
<script defer src="modules/ui/lakes-editor.js?v=1.87.10"></script>
|
||||||
<script defer src="modules/ui/coastline-editor.js"></script>
|
<script defer src="modules/ui/coastline-editor.js"></script>
|
||||||
<script defer src="modules/ui/labels-editor.js"></script>
|
<script defer src="modules/ui/labels-editor.js"></script>
|
||||||
<script defer src="modules/ui/rivers-editor.js"></script>
|
<script defer src="modules/ui/rivers-editor.js"></script>
|
||||||
<script defer src="modules/ui/rivers-creator.js"></script>
|
<script defer src="modules/ui/rivers-creator.js?v=1.89.13"></script>
|
||||||
<script defer src="modules/ui/relief-editor.js"></script>
|
<script defer src="modules/ui/relief-editor.js"></script>
|
||||||
<script defer src="modules/ui/burg-editor.js"></script>
|
<script defer src="modules/ui/burg-editor.js"></script>
|
||||||
<script defer src="modules/ui/units-editor.js"></script>
|
<script defer src="modules/ui/units-editor.js"></script>
|
||||||
<script defer src="modules/ui/notes-editor.js"></script>
|
<script defer src="modules/ui/notes-editor.js?v=1.89.03"></script>
|
||||||
<script defer src="modules/ui/diplomacy-editor.js?v=1.88.04"></script>
|
<script defer src="modules/ui/diplomacy-editor.js?v=1.88.04"></script>
|
||||||
<script defer src="modules/ui/zones-editor.js"></script>
|
<script defer src="modules/ui/zones-editor.js"></script>
|
||||||
<script defer src="modules/ui/burgs-overview.js"></script>
|
<script defer src="modules/ui/burgs-overview.js"></script>
|
||||||
|
|
|
||||||
2
main.js
2
main.js
|
|
@ -191,7 +191,6 @@ let populationRate = +document.getElementById("populationRateInput").value;
|
||||||
let distanceScale = +document.getElementById("distanceScaleInput").value;
|
let distanceScale = +document.getElementById("distanceScaleInput").value;
|
||||||
let urbanization = +document.getElementById("urbanizationInput").value;
|
let urbanization = +document.getElementById("urbanizationInput").value;
|
||||||
let urbanDensity = +document.getElementById("urbanDensityInput").value;
|
let urbanDensity = +document.getElementById("urbanDensityInput").value;
|
||||||
let statesNeutral = 1; // statesEditor growth parameter
|
|
||||||
|
|
||||||
applyStoredOptions();
|
applyStoredOptions();
|
||||||
|
|
||||||
|
|
@ -695,6 +694,7 @@ async function generate(options) {
|
||||||
if (shouldRegenerateGrid(grid, precreatedSeed)) grid = precreatedGraph || generateGrid();
|
if (shouldRegenerateGrid(grid, precreatedSeed)) grid = precreatedGraph || generateGrid();
|
||||||
else delete grid.cells.h;
|
else delete grid.cells.h;
|
||||||
grid.cells.h = await HeightmapGenerator.generate(grid);
|
grid.cells.h = await HeightmapGenerator.generate(grid);
|
||||||
|
pack = {}; // reset pack
|
||||||
|
|
||||||
markFeatures();
|
markFeatures();
|
||||||
markupGridOcean();
|
markupGridOcean();
|
||||||
|
|
|
||||||
|
|
@ -359,7 +359,7 @@ window.BurgsAndStates = (function () {
|
||||||
TIME && console.timeEnd("drawBurgs");
|
TIME && console.timeEnd("drawBurgs");
|
||||||
};
|
};
|
||||||
|
|
||||||
// growth algorithm to assign cells to states like we did for cultures
|
// expand cultures across the map (Dijkstra-like algorithm)
|
||||||
const expandStates = function () {
|
const expandStates = function () {
|
||||||
TIME && console.time("expandStates");
|
TIME && console.time("expandStates");
|
||||||
const {cells, states, cultures, burgs} = pack;
|
const {cells, states, cultures, burgs} = pack;
|
||||||
|
|
@ -367,18 +367,28 @@ window.BurgsAndStates = (function () {
|
||||||
cells.state = cells.state || new Uint16Array(cells.i.length);
|
cells.state = cells.state || new Uint16Array(cells.i.length);
|
||||||
const queue = new PriorityQueue({comparator: (a, b) => a.p - b.p});
|
const queue = new PriorityQueue({comparator: (a, b) => a.p - b.p});
|
||||||
const cost = [];
|
const cost = [];
|
||||||
const neutral = (cells.i.length / 5000) * 2500 * neutralInput.value * statesNeutral; // limit cost for state growth
|
|
||||||
|
|
||||||
states
|
const globalNeutralRate = byId("neutralInput")?.valueAsNumber || 1;
|
||||||
.filter(s => s.i && !s.removed)
|
const statesNeutralRate = byId("statesNeutral")?.valueAsNumber || 1;
|
||||||
.forEach(s => {
|
const neutral = (cells.i.length / 2) * globalNeutralRate * statesNeutralRate; // limit cost for state growth
|
||||||
const capitalCell = burgs[s.capital].cell;
|
|
||||||
cells.state[capitalCell] = s.i;
|
// remove state from all cells except of locked
|
||||||
const cultureCenter = cultures[s.culture].center;
|
for (const cellId of cells.i) {
|
||||||
const b = cells.biome[cultureCenter]; // state native biome
|
const state = states[cells.state[cellId]];
|
||||||
queue.queue({e: s.center, p: 0, s: s.i, b});
|
if (state.lock) continue;
|
||||||
cost[s.center] = 1;
|
cells.state[cellId] = 0;
|
||||||
});
|
}
|
||||||
|
|
||||||
|
for (const state of states) {
|
||||||
|
if (!state.i || state.removed) continue;
|
||||||
|
|
||||||
|
const capitalCell = burgs[state.capital].cell;
|
||||||
|
cells.state[capitalCell] = state.i;
|
||||||
|
const cultureCenter = cultures[state.culture].center;
|
||||||
|
const b = cells.biome[cultureCenter]; // state native biome
|
||||||
|
queue.queue({e: state.center, p: 0, s: state.i, b});
|
||||||
|
cost[state.center] = 1;
|
||||||
|
}
|
||||||
|
|
||||||
while (queue.length) {
|
while (queue.length) {
|
||||||
const next = queue.dequeue();
|
const next = queue.dequeue();
|
||||||
|
|
@ -608,7 +618,7 @@ window.BurgsAndStates = (function () {
|
||||||
if (list && !list.includes(state.i)) continue;
|
if (list && !list.includes(state.i)) continue;
|
||||||
|
|
||||||
byId(`stateLabel${state.i}`)?.remove();
|
byId(`stateLabel${state.i}`)?.remove();
|
||||||
byId(`textPath_stateLabel6${state.i}`)?.remove();
|
byId(`textPath_stateLabel${state.i}`)?.remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
const example = g.append("text").attr("x", 0).attr("x", 0).text("Average");
|
const example = g.append("text").attr("x", 0).attr("x", 0).text("Average");
|
||||||
|
|
|
||||||
|
|
@ -116,23 +116,25 @@ window.Cultures = (function () {
|
||||||
|
|
||||||
cultures.forEach(c => (c.base = c.base % nameBases.length));
|
cultures.forEach(c => (c.base = c.base % nameBases.length));
|
||||||
|
|
||||||
function selectCultures(c) {
|
function selectCultures(culturesNumber) {
|
||||||
let def = getDefault(c);
|
let def = getDefault(culturesNumber);
|
||||||
if (c === def.length) return def;
|
|
||||||
if (def.every(d => d.odd === 1)) return def.splice(0, c);
|
|
||||||
|
|
||||||
const count = Math.min(c, def.length);
|
|
||||||
const cultures = [];
|
const cultures = [];
|
||||||
|
|
||||||
pack.cultures?.forEach(function (culture) {
|
pack.cultures?.forEach(function (culture) {
|
||||||
if (culture.lock) cultures.push(culture);
|
if (culture.lock) cultures.push(culture);
|
||||||
});
|
});
|
||||||
|
|
||||||
for (let culture, rnd, i = 0; cultures.length < count && i < 200; i++) {
|
if (!cultures.length) {
|
||||||
|
if (culturesNumber === def.length) return def;
|
||||||
|
if (def.every(d => d.odd === 1)) return def.splice(0, culturesNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let culture, rnd, i = 0; cultures.length < culturesNumber && def.length > 0;) {
|
||||||
do {
|
do {
|
||||||
rnd = rand(def.length - 1);
|
rnd = rand(def.length - 1);
|
||||||
culture = def[rnd];
|
culture = def[rnd];
|
||||||
} while (!P(culture.odd));
|
i++;
|
||||||
|
} while (i < 200 && !P(culture.odd));
|
||||||
cultures.push(culture);
|
cultures.push(culture);
|
||||||
def.splice(rnd, 1);
|
def.splice(rnd, 1);
|
||||||
}
|
}
|
||||||
|
|
@ -507,28 +509,37 @@ window.Cultures = (function () {
|
||||||
// expand cultures across the map (Dijkstra-like algorithm)
|
// expand cultures across the map (Dijkstra-like algorithm)
|
||||||
const expand = function () {
|
const expand = function () {
|
||||||
TIME && console.time("expandCultures");
|
TIME && console.time("expandCultures");
|
||||||
cells = pack.cells;
|
const {cells, cultures} = pack;
|
||||||
|
|
||||||
const queue = new PriorityQueue({comparator: (a, b) => a.p - b.p});
|
const queue = new PriorityQueue({comparator: (a, b) => a.p - b.p});
|
||||||
pack.cultures.forEach(function (c) {
|
|
||||||
if (!c.i || c.removed || c.lock) return;
|
|
||||||
queue.queue({e: c.center, p: 0, c: c.i});
|
|
||||||
});
|
|
||||||
|
|
||||||
const neutral = (cells.i.length / 5000) * 3000 * neutralInput.value; // limit cost for culture growth
|
|
||||||
const cost = [];
|
const cost = [];
|
||||||
|
|
||||||
|
const neutralRate = byId("neutralRate")?.valueAsNumber || 1;
|
||||||
|
const neutral = cells.i.length * 0.6 * neutralRate; // limit cost for culture growth
|
||||||
|
|
||||||
|
// remove culture from all cells except of locked
|
||||||
|
for (const cellId of cells.i) {
|
||||||
|
const culture = cultures[cells.culture[cellId]];
|
||||||
|
if (culture.lock) continue;
|
||||||
|
cells.culture[cellId] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const culture of cultures) {
|
||||||
|
if (!culture.i || culture.removed) continue;
|
||||||
|
queue.queue({e: culture.center, p: 0, c: culture.i});
|
||||||
|
}
|
||||||
|
|
||||||
while (queue.length) {
|
while (queue.length) {
|
||||||
const next = queue.dequeue(),
|
const {e, p, c} = queue.dequeue();
|
||||||
n = next.e,
|
const {type} = pack.cultures[c];
|
||||||
p = next.p,
|
|
||||||
c = next.c;
|
cells.c[e].forEach(e => {
|
||||||
const type = pack.cultures[c].type;
|
const culture = cells.culture[e];
|
||||||
cells.c[n].forEach(e => {
|
if (culture?.lock) return; // do not overwrite cell of locked culture
|
||||||
if (pack.cultures[cells.culture[e]]?.lock) return;
|
|
||||||
|
|
||||||
const biome = cells.biome[e];
|
const biome = cells.biome[e];
|
||||||
const biomeCost = getBiomeCost(c, biome, type);
|
const biomeCost = getBiomeCost(c, biome, type);
|
||||||
const biomeChangeCost = biome === cells.biome[n] ? 0 : 20; // penalty on biome change
|
const biomeChangeCost = biome === cells.biome[e] ? 0 : 20; // penalty on biome change
|
||||||
const heightCost = getHeightCost(e, cells.h[e], type);
|
const heightCost = getHeightCost(e, cells.h[e], type);
|
||||||
const riverCost = getRiverCost(cells.r[e], e, type);
|
const riverCost = getRiverCost(cells.r[e], e, type);
|
||||||
const typeCost = getTypeCost(cells.t[e], type);
|
const typeCost = getTypeCost(cells.t[e], type);
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ export function open() {
|
||||||
|
|
||||||
function insertEditorHtml() {
|
function insertEditorHtml() {
|
||||||
const editorHtml = /* html */ `<div id="culturesEditor" class="dialog stable">
|
const editorHtml = /* html */ `<div id="culturesEditor" class="dialog stable">
|
||||||
<div id="culturesHeader" class="header" style="grid-template-columns: 10em 7em 8em 4em 8em 5em 8em 8em">
|
<div id="culturesHeader" class="header" style="grid-template-columns: 10em 7em 9em 4em 8em 5em 7em 8em">
|
||||||
<div data-tip="Click to sort by culture name" class="sortable alphabetically" data-sortby="name">Culture </div>
|
<div data-tip="Click to sort by culture name" class="sortable alphabetically" data-sortby="name">Culture </div>
|
||||||
<div data-tip="Click to sort by type" class="sortable alphabetically" data-sortby="type">Type </div>
|
<div data-tip="Click to sort by type" class="sortable alphabetically" data-sortby="type">Type </div>
|
||||||
<div data-tip="Click to sort by culture namesbase" class="sortable" data-sortby="base">Namesbase </div>
|
<div data-tip="Click to sort by culture namesbase" class="sortable" data-sortby="base">Namesbase </div>
|
||||||
|
|
@ -171,6 +171,7 @@ function culturesEditorAddLines() {
|
||||||
value="${c.name}" autocorrect="off" spellcheck="false" />
|
value="${c.name}" autocorrect="off" spellcheck="false" />
|
||||||
<span class="icon-cw placeholder"></span>
|
<span class="icon-cw placeholder"></span>
|
||||||
<select class="cultureType placeholder">${getTypeOptions(c.type)}</select>
|
<select class="cultureType placeholder">${getTypeOptions(c.type)}</select>
|
||||||
|
<span data-tip="Click to re-generate names for burgs with this culture assigned" class="icon-arrows-cw hide"></span>
|
||||||
<select data-tip="Culture namesbase. Click to change. Click on arrows to re-generate names"
|
<select data-tip="Culture namesbase. Click to change. Click on arrows to re-generate names"
|
||||||
class="cultureBase">${getBaseOptions(c.base)}</select>
|
class="cultureBase">${getBaseOptions(c.base)}</select>
|
||||||
<span data-tip="Cells count" class="icon-check-empty hide"></span>
|
<span data-tip="Cells count" class="icon-check-empty hide"></span>
|
||||||
|
|
@ -181,8 +182,7 @@ function culturesEditorAddLines() {
|
||||||
<div data-tip="Culture area" class="cultureArea hide" style="width: 6em">${si(area)} ${unit}</div>
|
<div data-tip="Culture area" class="cultureArea hide" style="width: 6em">${si(area)} ${unit}</div>
|
||||||
<span data-tip="${populationTip}" class="icon-male hide"></span>
|
<span data-tip="${populationTip}" class="icon-male hide"></span>
|
||||||
<div data-tip="${populationTip}" class="culturePopulation hide pointer"
|
<div data-tip="${populationTip}" class="culturePopulation hide pointer"
|
||||||
style="width: 5em">${si(population)}</div>
|
style="width: 4em">${si(population)}</div>
|
||||||
<span data-tip="Click to re-generate names for burgs with this culture assigned" class="icon-arrows-cw hide"></span>
|
|
||||||
${getShapeOptions(selectShape, c.shield)}
|
${getShapeOptions(selectShape, c.shield)}
|
||||||
</div>`;
|
</div>`;
|
||||||
continue;
|
continue;
|
||||||
|
|
@ -207,6 +207,7 @@ function culturesEditorAddLines() {
|
||||||
<span data-tip="Regenerate culture name" class="icon-cw hiddenIcon" style="visibility: hidden"></span>
|
<span data-tip="Regenerate culture name" class="icon-cw hiddenIcon" style="visibility: hidden"></span>
|
||||||
<select data-tip="Culture type. Defines growth model. Click to change"
|
<select data-tip="Culture type. Defines growth model. Click to change"
|
||||||
class="cultureType">${getTypeOptions(c.type)}</select>
|
class="cultureType">${getTypeOptions(c.type)}</select>
|
||||||
|
<span data-tip="Click to re-generate names for burgs with this culture assigned" class="icon-arrows-cw hide"></span>
|
||||||
<select data-tip="Culture namesbase. Click to change. Click on arrows to re-generate names"
|
<select data-tip="Culture namesbase. Click to change. Click on arrows to re-generate names"
|
||||||
class="cultureBase">${getBaseOptions(c.base)}</select>
|
class="cultureBase">${getBaseOptions(c.base)}</select>
|
||||||
<span data-tip="Cells count" class="icon-check-empty hide"></span>
|
<span data-tip="Cells count" class="icon-check-empty hide"></span>
|
||||||
|
|
@ -225,10 +226,9 @@ function culturesEditorAddLines() {
|
||||||
<div data-tip="Culture area" class="cultureArea hide" style="width: 6em">${si(area)} ${unit}</div>
|
<div data-tip="Culture area" class="cultureArea hide" style="width: 6em">${si(area)} ${unit}</div>
|
||||||
<span data-tip="${populationTip}" class="icon-male hide"></span>
|
<span data-tip="${populationTip}" class="icon-male hide"></span>
|
||||||
<div data-tip="${populationTip}" class="culturePopulation hide pointer"
|
<div data-tip="${populationTip}" class="culturePopulation hide pointer"
|
||||||
style="width: 5em">${si(population)}</div>
|
style="width: 4em">${si(population)}</div>
|
||||||
<span data-tip="Click to re-generate names for burgs with this culture assigned" class="icon-arrows-cw hide"></span>
|
|
||||||
${getShapeOptions(selectShape, c.shield)}
|
${getShapeOptions(selectShape, c.shield)}
|
||||||
<span data-tip="Lock culture" class="icon-lock${c.lock ? '' : '-open'} hide"></span>
|
<span data-tip="Lock culture" class="icon-lock${c.lock ? "" : "-open"} hide"></span>
|
||||||
<span data-tip="Remove culture" class="icon-trash-empty hide"></span>
|
<span data-tip="Remove culture" class="icon-trash-empty hide"></span>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
@ -251,7 +251,7 @@ function culturesEditorAddLines() {
|
||||||
$body.querySelectorAll("fill-box").forEach($el => $el.on("click", cultureChangeColor));
|
$body.querySelectorAll("fill-box").forEach($el => $el.on("click", cultureChangeColor));
|
||||||
$body.querySelectorAll("div > input.cultureName").forEach($el => $el.on("input", cultureChangeName));
|
$body.querySelectorAll("div > input.cultureName").forEach($el => $el.on("input", cultureChangeName));
|
||||||
$body.querySelectorAll("div > span.icon-cw").forEach($el => $el.on("click", cultureRegenerateName));
|
$body.querySelectorAll("div > span.icon-cw").forEach($el => $el.on("click", cultureRegenerateName));
|
||||||
$body.querySelectorAll("div > input.cultureExpan").forEach($el => $el.on("input", cultureChangeExpansionism));
|
$body.querySelectorAll("div > input.cultureExpan").forEach($el => $el.on("change", cultureChangeExpansionism));
|
||||||
$body.querySelectorAll("div > select.cultureType").forEach($el => $el.on("change", cultureChangeType));
|
$body.querySelectorAll("div > select.cultureType").forEach($el => $el.on("change", cultureChangeType));
|
||||||
$body.querySelectorAll("div > select.cultureBase").forEach($el => $el.on("change", cultureChangeBase));
|
$body.querySelectorAll("div > select.cultureBase").forEach($el => $el.on("change", cultureChangeBase));
|
||||||
$body.querySelectorAll("div > select.cultureEmblems").forEach($el => $el.on("change", cultureChangeEmblemsShape));
|
$body.querySelectorAll("div > select.cultureEmblems").forEach($el => $el.on("change", cultureChangeEmblemsShape));
|
||||||
|
|
@ -590,16 +590,23 @@ function drawCultureCenters() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function cultureCenterDrag() {
|
function cultureCenterDrag() {
|
||||||
const $el = d3.select(this);
|
|
||||||
const cultureId = +this.id.slice(13);
|
const cultureId = +this.id.slice(13);
|
||||||
d3.event.on("drag", () => {
|
const tr = parseTransform(this.getAttribute("transform"));
|
||||||
|
const x0 = +tr[0] - d3.event.x;
|
||||||
|
const y0 = +tr[1] - d3.event.y;
|
||||||
|
|
||||||
|
function handleDrag() {
|
||||||
const {x, y} = d3.event;
|
const {x, y} = d3.event;
|
||||||
$el.attr("cx", x).attr("cy", y);
|
this.setAttribute("transform", `translate(${x0 + x},${y0 + y})`);
|
||||||
const cell = findCell(x, y);
|
const cell = findCell(x, y);
|
||||||
if (pack.cells.h[cell] < 20) return; // ignore dragging on water
|
if (pack.cells.h[cell] < 20) return; // ignore dragging on water
|
||||||
|
|
||||||
pack.cultures[cultureId].center = cell;
|
pack.cultures[cultureId].center = cell;
|
||||||
recalculateCultures();
|
recalculateCultures();
|
||||||
});
|
}
|
||||||
|
|
||||||
|
const dragDebounced = debounce(handleDrag, 50);
|
||||||
|
d3.event.on("drag", dragDebounced);
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleLegend() {
|
function toggleLegend() {
|
||||||
|
|
@ -666,17 +673,10 @@ async function showHierarchy() {
|
||||||
function recalculateCultures(must) {
|
function recalculateCultures(must) {
|
||||||
if (!must && !culturesAutoChange.checked) return;
|
if (!must && !culturesAutoChange.checked) return;
|
||||||
|
|
||||||
pack.cells.culture = new Uint16Array(pack.cells.i.length);
|
|
||||||
pack.cultures.forEach(function (c) {
|
|
||||||
if (!c.i || c.removed) return;
|
|
||||||
pack.cells.culture[c.center] = c.i;
|
|
||||||
});
|
|
||||||
|
|
||||||
Cultures.expand();
|
Cultures.expand();
|
||||||
drawCultures();
|
drawCultures();
|
||||||
pack.burgs.forEach(b => (b.culture = pack.cells.culture[b.cell]));
|
pack.burgs.forEach(b => (b.culture = pack.cells.culture[b.cell]));
|
||||||
refreshCulturesEditor();
|
refreshCulturesEditor();
|
||||||
document.querySelector("input.cultureExpan").focus(); // to not trigger hotkeys
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function enterCultureManualAssignent() {
|
function enterCultureManualAssignent() {
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,10 @@ addListeners();
|
||||||
|
|
||||||
export function open() {
|
export function open() {
|
||||||
closeDialogs("#religionsEditor, .stable");
|
closeDialogs("#religionsEditor, .stable");
|
||||||
if (!layerIsOn("toggleReligions")) toggleCultures();
|
if (!layerIsOn("toggleReligions")) toggleReligions();
|
||||||
if (layerIsOn("toggleStates")) toggleStates();
|
if (layerIsOn("toggleStates")) toggleStates();
|
||||||
if (layerIsOn("toggleBiomes")) toggleBiomes();
|
if (layerIsOn("toggleBiomes")) toggleBiomes();
|
||||||
if (layerIsOn("toggleCultures")) toggleReligions();
|
if (layerIsOn("toggleCultures")) toggleCultures();
|
||||||
if (layerIsOn("toggleProvinces")) toggleProvinces();
|
if (layerIsOn("toggleProvinces")) toggleProvinces();
|
||||||
|
|
||||||
refreshReligionsEditor();
|
refreshReligionsEditor();
|
||||||
|
|
@ -23,13 +23,15 @@ export function open() {
|
||||||
|
|
||||||
function insertEditorHtml() {
|
function insertEditorHtml() {
|
||||||
const editorHtml = /* html */ `<div id="religionsEditor" class="dialog stable">
|
const editorHtml = /* html */ `<div id="religionsEditor" class="dialog stable">
|
||||||
<div id="religionsHeader" class="header" style="grid-template-columns: 13em 6em 7em 18em 5em 6em">
|
<div id="religionsHeader" class="header" style="grid-template-columns: 13em 6em 7em 18em 6em 7em 6em 7em">
|
||||||
<div data-tip="Click to sort by religion name" class="sortable alphabetically" data-sortby="name">Religion </div>
|
<div data-tip="Click to sort by religion name" class="sortable alphabetically" data-sortby="name">Religion </div>
|
||||||
<div data-tip="Click to sort by religion type" class="sortable alphabetically icon-sort-name-down" data-sortby="type">Type </div>
|
<div data-tip="Click to sort by religion type" class="sortable alphabetically icon-sort-name-down" data-sortby="type">Type </div>
|
||||||
<div data-tip="Click to sort by religion form" class="sortable alphabetically hide" data-sortby="form">Form </div>
|
<div data-tip="Click to sort by religion form" class="sortable alphabetically hide" data-sortby="form">Form </div>
|
||||||
<div data-tip="Click to sort by supreme deity" class="sortable alphabetically hide" data-sortby="deity">Supreme Deity </div>
|
<div data-tip="Click to sort by supreme deity" class="sortable alphabetically hide" data-sortby="deity">Supreme Deity </div>
|
||||||
<div data-tip="Click to sort by religion area" class="sortable hide" data-sortby="area">Area </div>
|
<div data-tip="Click to sort by religion area" class="sortable hide" data-sortby="area">Area </div>
|
||||||
<div data-tip="Click to sort by number of believers (religion area population)" class="sortable hide" data-sortby="population">Believers </div>
|
<div data-tip="Click to sort by number of believers (religion area population)" class="sortable hide" data-sortby="population">Believers </div>
|
||||||
|
<div data-tip="Click to sort by potential extent type" class="sortable alphabetically hide" data-sortby="expansion">Potential </div>
|
||||||
|
<div data-tip="Click to sort by expansionism" class="sortable hide" data-sortby="expansionism">Expansion </div>
|
||||||
</div>
|
</div>
|
||||||
<div id="religionsBody" class="table" data-type="absolute"></div>
|
<div id="religionsBody" class="table" data-type="absolute"></div>
|
||||||
|
|
||||||
|
|
@ -88,6 +90,11 @@ function insertEditorHtml() {
|
||||||
</div>
|
</div>
|
||||||
<button id="religionsAdd" data-tip="Add a new religion. Hold Shift to add multiple" class="icon-plus"></button>
|
<button id="religionsAdd" data-tip="Add a new religion. Hold Shift to add multiple" class="icon-plus"></button>
|
||||||
<button id="religionsExport" data-tip="Download religions-related data" class="icon-download"></button>
|
<button id="religionsExport" data-tip="Download religions-related data" class="icon-download"></button>
|
||||||
|
<button id="religionsRecalculate" data-tip="Recalculate religions based on current values of growth-related attributes" class="icon-retweet"></button>
|
||||||
|
<span data-tip="Allow religion center, extent, and expansionism changes to take an immediate effect">
|
||||||
|
<input id="religionsAutoChange" class="checkbox" type="checkbox" />
|
||||||
|
<label for="religionsAutoChange" class="checkbox-label"><i>auto-apply changes</i></label>
|
||||||
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
||||||
|
|
@ -109,6 +116,7 @@ function addListeners() {
|
||||||
byId("religionsManuallyCancel").on("click", () => exitReligionsManualAssignment());
|
byId("religionsManuallyCancel").on("click", () => exitReligionsManualAssignment());
|
||||||
byId("religionsAdd").on("click", enterAddReligionMode);
|
byId("religionsAdd").on("click", enterAddReligionMode);
|
||||||
byId("religionsExport").on("click", downloadReligionsCsv);
|
byId("religionsExport").on("click", downloadReligionsCsv);
|
||||||
|
byId("religionsRecalculate").on("click", () => recalculateReligions(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
function refreshReligionsEditor() {
|
function refreshReligionsEditor() {
|
||||||
|
|
@ -166,9 +174,10 @@ function religionsEditorAddLines() {
|
||||||
data-type=""
|
data-type=""
|
||||||
data-form=""
|
data-form=""
|
||||||
data-deity=""
|
data-deity=""
|
||||||
|
data-expansion=""
|
||||||
data-expansionism=""
|
data-expansionism=""
|
||||||
>
|
>
|
||||||
<svg width="11" height="11" class="placeholder"></svg>
|
<svg width="9" height="9" class="placeholder"></svg>
|
||||||
<input data-tip="Religion name. Click and type to change" class="religionName italic" style="width: 11em"
|
<input data-tip="Religion name. Click and type to change" class="religionName italic" style="width: 11em"
|
||||||
value="${r.name}" autocorrect="off" spellcheck="false" />
|
value="${r.name}" autocorrect="off" spellcheck="false" />
|
||||||
<select data-tip="Religion type" class="religionType placeholder" style="width: 5em">
|
<select data-tip="Religion type" class="religionType placeholder" style="width: 5em">
|
||||||
|
|
@ -178,9 +187,11 @@ function religionsEditorAddLines() {
|
||||||
<span data-tip="Click to re-generate supreme deity" class="icon-arrows-cw placeholder hide"></span>
|
<span data-tip="Click to re-generate supreme deity" class="icon-arrows-cw placeholder hide"></span>
|
||||||
<input data-tip="Religion supreme deity" class="religionDeity placeholder hide" style="width: 17em" value="" autocorrect="off" spellcheck="false" />
|
<input data-tip="Religion supreme deity" class="religionDeity placeholder hide" style="width: 17em" value="" autocorrect="off" spellcheck="false" />
|
||||||
<span data-tip="Religion area" style="padding-right: 4px" class="icon-map-o hide"></span>
|
<span data-tip="Religion area" style="padding-right: 4px" class="icon-map-o hide"></span>
|
||||||
<div data-tip="Religion area" class="religionArea hide" style="width: 5em">${si(area) + unit}</div>
|
<div data-tip="Religion area" class="religionArea hide" style="width: 6em">${si(area) + unit}</div>
|
||||||
<span data-tip="${populationTip}" class="icon-male hide"></span>
|
<span data-tip="${populationTip}" class="icon-male hide"></span>
|
||||||
<div data-tip="${populationTip}" class="religionPopulation hide pointer">${si(population)}</div>
|
<div data-tip="${populationTip}" class="religionPopulation hide pointer" style="width: 5em">${si(
|
||||||
|
population
|
||||||
|
)}</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
@ -195,6 +206,7 @@ function religionsEditorAddLines() {
|
||||||
data-type="${r.type}"
|
data-type="${r.type}"
|
||||||
data-form="${r.form}"
|
data-form="${r.form}"
|
||||||
data-deity="${r.deity || ""}"
|
data-deity="${r.deity || ""}"
|
||||||
|
data-expansion="${r.expansion}"
|
||||||
data-expansionism="${r.expansionism}"
|
data-expansionism="${r.expansionism}"
|
||||||
>
|
>
|
||||||
<fill-box fill="${r.color}"></fill-box>
|
<fill-box fill="${r.color}"></fill-box>
|
||||||
|
|
@ -209,13 +221,13 @@ function religionsEditorAddLines() {
|
||||||
<input data-tip="Religion supreme deity" class="religionDeity hide" style="width: 17em"
|
<input data-tip="Religion supreme deity" class="religionDeity hide" style="width: 17em"
|
||||||
value="${r.deity || ""}" autocorrect="off" spellcheck="false" />
|
value="${r.deity || ""}" autocorrect="off" spellcheck="false" />
|
||||||
<span data-tip="Religion area" style="padding-right: 4px" class="icon-map-o hide"></span>
|
<span data-tip="Religion area" style="padding-right: 4px" class="icon-map-o hide"></span>
|
||||||
<div data-tip="Religion area" class="religionArea hide" style="width: 5em">${si(area) + unit}</div>
|
<div data-tip="Religion area" class="religionArea hide" style="width: 6em">${si(area) + unit}</div>
|
||||||
<span data-tip="${populationTip}" class="icon-male hide"></span>
|
<span data-tip="${populationTip}" class="icon-male hide"></span>
|
||||||
<div data-tip="${populationTip}" class="religionPopulation hide pointer">${si(population)}</div>
|
<div data-tip="${populationTip}" class="religionPopulation hide pointer" style="width: 5em">${si(
|
||||||
<span
|
population
|
||||||
data-tip="Lock religion, will regenerate the origin folk and organized religion if they are not also locked"
|
)}</div>
|
||||||
class="icon-lock${r.lock ? '' : '-open'} hide"
|
${getExpansionColumns(r)}
|
||||||
></span>
|
<span data-tip="Lock this religion" class="icon-lock${r.lock ? "" : "-open"} hide"></span>
|
||||||
<span data-tip="Remove religion" class="icon-trash-empty hide"></span>
|
<span data-tip="Remove religion" class="icon-trash-empty hide"></span>
|
||||||
</div>`;
|
</div>`;
|
||||||
}
|
}
|
||||||
|
|
@ -245,6 +257,8 @@ function religionsEditorAddLines() {
|
||||||
$body.querySelectorAll("div > input.religionDeity").forEach(el => el.on("input", religionChangeDeity));
|
$body.querySelectorAll("div > input.religionDeity").forEach(el => el.on("input", religionChangeDeity));
|
||||||
$body.querySelectorAll("div > span.icon-arrows-cw").forEach(el => el.on("click", regenerateDeity));
|
$body.querySelectorAll("div > span.icon-arrows-cw").forEach(el => el.on("click", regenerateDeity));
|
||||||
$body.querySelectorAll("div > div.religionPopulation").forEach(el => el.on("click", changePopulation));
|
$body.querySelectorAll("div > div.religionPopulation").forEach(el => el.on("click", changePopulation));
|
||||||
|
$body.querySelectorAll("div > select.religionExtent").forEach(el => el.on("change", religionChangeExtent));
|
||||||
|
$body.querySelectorAll("div > input.religionExpantion").forEach(el => el.on("change", religionChangeExpansionism));
|
||||||
$body.querySelectorAll("div > span.icon-trash-empty").forEach(el => el.on("click", religionRemovePrompt));
|
$body.querySelectorAll("div > span.icon-trash-empty").forEach(el => el.on("click", religionRemovePrompt));
|
||||||
$body.querySelectorAll("div > span.icon-lock").forEach($el => $el.on("click", updateLockStatus));
|
$body.querySelectorAll("div > span.icon-lock").forEach($el => $el.on("click", updateLockStatus));
|
||||||
$body.querySelectorAll("div > span.icon-lock-open").forEach($el => $el.on("click", updateLockStatus));
|
$body.querySelectorAll("div > span.icon-lock-open").forEach($el => $el.on("click", updateLockStatus));
|
||||||
|
|
@ -253,6 +267,7 @@ function religionsEditorAddLines() {
|
||||||
$body.dataset.type = "absolute";
|
$body.dataset.type = "absolute";
|
||||||
togglePercentageMode();
|
togglePercentageMode();
|
||||||
}
|
}
|
||||||
|
|
||||||
applySorting(religionsHeader);
|
applySorting(religionsHeader);
|
||||||
$("#religionsEditor").dialog({width: fitContent()});
|
$("#religionsEditor").dialog({width: fitContent()});
|
||||||
}
|
}
|
||||||
|
|
@ -264,6 +279,41 @@ function getTypeOptions(type) {
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getExpansionColumns(r) {
|
||||||
|
if (r.type === "Folk") {
|
||||||
|
const tip =
|
||||||
|
"Folk religions are not competitive and do not expand. Initially they cover all cells of their parent culture, but get ousted by organized religions when they expand";
|
||||||
|
return /* html */ `
|
||||||
|
<span data-tip="${tip}" class="icon-resize-full-alt hide" style="padding-right: 2px"></span>
|
||||||
|
<span data-tip="${tip}" class="religionExtent hide" style="width: 5em">culture</span>
|
||||||
|
<span data-tip="${tip}" class="icon-resize-full hide"></span>
|
||||||
|
<input data-tip="${tip}" class="religionExpantion hide" disabled type="number" value='0' />`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return /* html */ `
|
||||||
|
<span data-tip="Potential religion extent" class="icon-resize-full-alt hide" style="padding-right: 2px"></span>
|
||||||
|
<select data-tip="Potential religion extent" class="religionExtent hide" style="width: 5em">
|
||||||
|
${getExtentOptions(r.expansion)}
|
||||||
|
</select>
|
||||||
|
<span data-tip="Religion expansionism. Defines competitive size" class="icon-resize-full hide"></span>
|
||||||
|
<input
|
||||||
|
data-tip="Religion expansionism. Defines competitive size. Click to change, then click Recalculate to apply change"
|
||||||
|
class="religionExpantion hide"
|
||||||
|
type="number"
|
||||||
|
min="0"
|
||||||
|
max="99"
|
||||||
|
step=".1"
|
||||||
|
value=${r.expansionism}
|
||||||
|
/>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getExtentOptions(type) {
|
||||||
|
let options = "";
|
||||||
|
const types = ["global", "state", "culture"];
|
||||||
|
types.forEach(t => (options += `<option ${type === t ? "selected" : ""} value="${t}">${t}</option>`));
|
||||||
|
return options;
|
||||||
|
}
|
||||||
|
|
||||||
const religionHighlightOn = debounce(event => {
|
const religionHighlightOn = debounce(event => {
|
||||||
const religionId = Number(event.id || event.target.dataset.id);
|
const religionId = Number(event.id || event.target.dataset.id);
|
||||||
const $el = $body.querySelector(`div[data-id='${religionId}']`);
|
const $el = $body.querySelector(`div[data-id='${religionId}']`);
|
||||||
|
|
@ -272,20 +322,19 @@ const religionHighlightOn = debounce(event => {
|
||||||
if (!layerIsOn("toggleReligions")) return;
|
if (!layerIsOn("toggleReligions")) return;
|
||||||
if (customization) return;
|
if (customization) return;
|
||||||
|
|
||||||
const animate = d3.transition().duration(1500).ease(d3.easeSinIn);
|
const animate = d3.transition().duration(2000).ease(d3.easeSinIn);
|
||||||
relig
|
relig
|
||||||
.select("#religion" + religionId)
|
.select("#religion" + religionId)
|
||||||
.raise()
|
.raise()
|
||||||
.transition(animate)
|
.transition(animate)
|
||||||
.attr("stroke-width", 2.5)
|
.attr("stroke-width", 2.5)
|
||||||
.attr("stroke", "#c13119");
|
.attr("stroke", "#d0240f");
|
||||||
debug
|
debug
|
||||||
.select("#religionsCenter" + religionId)
|
.select("#religionsCenter" + religionId)
|
||||||
.raise()
|
.raise()
|
||||||
.transition(animate)
|
.transition(animate)
|
||||||
.attr("r", 8)
|
.attr("r", 3)
|
||||||
.attr("stroke-width", 2)
|
.attr("stroke", "#d0240f");
|
||||||
.attr("stroke", "#c13119");
|
|
||||||
}, 200);
|
}, 200);
|
||||||
|
|
||||||
function religionHighlightOff(event) {
|
function religionHighlightOff(event) {
|
||||||
|
|
@ -301,8 +350,7 @@ function religionHighlightOff(event) {
|
||||||
debug
|
debug
|
||||||
.select("#religionsCenter" + religionId)
|
.select("#religionsCenter" + religionId)
|
||||||
.transition()
|
.transition()
|
||||||
.attr("r", 4)
|
.attr("r", 2)
|
||||||
.attr("stroke-width", 1.2)
|
|
||||||
.attr("stroke", null);
|
.attr("stroke", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -434,6 +482,20 @@ function changePopulation() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function religionChangeExtent() {
|
||||||
|
const religion = +this.parentNode.dataset.id;
|
||||||
|
this.parentNode.dataset.expansion = this.value;
|
||||||
|
pack.religions[religion].expansion = this.value;
|
||||||
|
recalculateReligions();
|
||||||
|
}
|
||||||
|
|
||||||
|
function religionChangeExpansionism() {
|
||||||
|
const religion = +this.parentNode.dataset.id;
|
||||||
|
this.parentNode.dataset.expansionism = this.value;
|
||||||
|
pack.religions[religion].expansionism = +this.value;
|
||||||
|
recalculateReligions();
|
||||||
|
}
|
||||||
|
|
||||||
function religionRemovePrompt() {
|
function religionRemovePrompt() {
|
||||||
if (customization) return;
|
if (customization) return;
|
||||||
|
|
||||||
|
|
@ -471,11 +533,14 @@ function drawReligionCenters() {
|
||||||
const religionCenters = debug
|
const religionCenters = debug
|
||||||
.append("g")
|
.append("g")
|
||||||
.attr("id", "religionCenters")
|
.attr("id", "religionCenters")
|
||||||
.attr("stroke-width", 1.2)
|
.attr("stroke-width", 0.8)
|
||||||
.attr("stroke", "#444444")
|
.attr("stroke", "#444444")
|
||||||
.style("cursor", "move");
|
.style("cursor", "move");
|
||||||
|
|
||||||
const data = pack.religions.filter(r => r.i && r.center && r.cells && !r.removed);
|
let data = pack.religions.filter(r => r.i && r.center && !r.removed);
|
||||||
|
const showExtinct = $body.dataset.extinct === "show";
|
||||||
|
if (!showExtinct) data = data.filter(r => r.cells > 0);
|
||||||
|
|
||||||
religionCenters
|
religionCenters
|
||||||
.selectAll("circle")
|
.selectAll("circle")
|
||||||
.data(data)
|
.data(data)
|
||||||
|
|
@ -483,7 +548,7 @@ function drawReligionCenters() {
|
||||||
.append("circle")
|
.append("circle")
|
||||||
.attr("id", d => "religionsCenter" + d.i)
|
.attr("id", d => "religionsCenter" + d.i)
|
||||||
.attr("data-id", d => d.i)
|
.attr("data-id", d => d.i)
|
||||||
.attr("r", 4)
|
.attr("r", 2)
|
||||||
.attr("fill", d => d.color)
|
.attr("fill", d => d.color)
|
||||||
.attr("cx", d => pack.cells.p[d.center][0])
|
.attr("cx", d => pack.cells.p[d.center][0])
|
||||||
.attr("cy", d => pack.cells.p[d.center][1])
|
.attr("cy", d => pack.cells.p[d.center][1])
|
||||||
|
|
@ -499,15 +564,23 @@ function drawReligionCenters() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function religionCenterDrag() {
|
function religionCenterDrag() {
|
||||||
const $el = d3.select(this);
|
|
||||||
const religionId = +this.dataset.id;
|
const religionId = +this.dataset.id;
|
||||||
d3.event.on("drag", () => {
|
const tr = parseTransform(this.getAttribute("transform"));
|
||||||
|
const x0 = +tr[0] - d3.event.x;
|
||||||
|
const y0 = +tr[1] - d3.event.y;
|
||||||
|
|
||||||
|
function handleDrag() {
|
||||||
const {x, y} = d3.event;
|
const {x, y} = d3.event;
|
||||||
$el.attr("cx", x).attr("cy", y);
|
this.setAttribute("transform", `translate(${x0 + x},${y0 + y})`);
|
||||||
const cell = findCell(x, y);
|
const cell = findCell(x, y);
|
||||||
if (pack.cells.h[cell] < 20) return; // ignore dragging on water
|
if (pack.cells.h[cell] < 20) return; // ignore dragging on water
|
||||||
|
|
||||||
pack.religions[religionId].center = cell;
|
pack.religions[religionId].center = cell;
|
||||||
});
|
recalculateReligions();
|
||||||
|
}
|
||||||
|
|
||||||
|
const dragDebounced = debounce(handleDrag, 50);
|
||||||
|
d3.event.on("drag", dragDebounced);
|
||||||
}
|
}
|
||||||
|
|
||||||
function toggleLegend() {
|
function toggleLegend() {
|
||||||
|
|
@ -578,13 +651,14 @@ async function showHierarchy() {
|
||||||
function toggleExtinct() {
|
function toggleExtinct() {
|
||||||
$body.dataset.extinct = $body.dataset.extinct !== "show" ? "show" : "hide";
|
$body.dataset.extinct = $body.dataset.extinct !== "show" ? "show" : "hide";
|
||||||
religionsEditorAddLines();
|
religionsEditorAddLines();
|
||||||
|
drawReligionCenters();
|
||||||
}
|
}
|
||||||
|
|
||||||
function enterReligionsManualAssignent() {
|
function enterReligionsManualAssignent() {
|
||||||
if (!layerIsOn("toggleReligions")) toggleReligions();
|
if (!layerIsOn("toggleReligions")) toggleReligions();
|
||||||
customization = 7;
|
customization = 7;
|
||||||
relig.append("g").attr("id", "temp");
|
relig.append("g").attr("id", "temp");
|
||||||
document.querySelectorAll("#religionsBottom > button").forEach(el => (el.style.display = "none"));
|
document.querySelectorAll("#religionsBottom > *").forEach(el => (el.style.display = "none"));
|
||||||
byId("religionsManuallyButtons").style.display = "inline-block";
|
byId("religionsManuallyButtons").style.display = "inline-block";
|
||||||
debug.select("#religionCenters").style("display", "none");
|
debug.select("#religionCenters").style("display", "none");
|
||||||
|
|
||||||
|
|
@ -686,7 +760,7 @@ function exitReligionsManualAssignment(close) {
|
||||||
customization = 0;
|
customization = 0;
|
||||||
relig.select("#temp").remove();
|
relig.select("#temp").remove();
|
||||||
removeCircle();
|
removeCircle();
|
||||||
document.querySelectorAll("#religionsBottom > button").forEach(el => (el.style.display = "inline-block"));
|
document.querySelectorAll("#religionsBottom > *").forEach(el => (el.style.display = "inline-block"));
|
||||||
byId("religionsManuallyButtons").style.display = "none";
|
byId("religionsManuallyButtons").style.display = "none";
|
||||||
|
|
||||||
byId("religionsEditor")
|
byId("religionsEditor")
|
||||||
|
|
@ -740,15 +814,15 @@ function addReligion() {
|
||||||
|
|
||||||
function downloadReligionsCsv() {
|
function downloadReligionsCsv() {
|
||||||
const unit = getAreaUnit("2");
|
const unit = getAreaUnit("2");
|
||||||
const headers = `Id,Name,Color,Type,Form,Supreme Deity,Area ${unit},Believers,Origins`;
|
const headers = `Id,Name,Color,Type,Form,Supreme Deity,Area ${unit},Believers,Origins,Potential,Expansionism`;
|
||||||
const lines = Array.from($body.querySelectorAll(":scope > div"));
|
const lines = Array.from($body.querySelectorAll(":scope > div"));
|
||||||
const data = lines.map($line => {
|
const data = lines.map($line => {
|
||||||
const {id, name, color, type, form, deity, area, population} = $line.dataset;
|
const {id, name, color, type, form, deity, area, population, expansion, expansionism} = $line.dataset;
|
||||||
const deityText = '"' + deity + '"';
|
const deityText = '"' + deity + '"';
|
||||||
const {origins} = pack.religions[+id];
|
const {origins} = pack.religions[+id];
|
||||||
const originList = (origins || []).filter(origin => origin).map(origin => pack.religions[origin].name);
|
const originList = (origins || []).filter(origin => origin).map(origin => pack.religions[origin].name);
|
||||||
const originText = '"' + originList.join(", ") + '"';
|
const originText = '"' + originList.join(", ") + '"';
|
||||||
return [id, name, color, type, form, deityText, area, population, originText].join(",");
|
return [id, name, color, type, form, deityText, area, population, originText, expansion, expansionism].join(",");
|
||||||
});
|
});
|
||||||
const csvData = [headers].concat(data).join("\n");
|
const csvData = [headers].concat(data).join("\n");
|
||||||
|
|
||||||
|
|
@ -773,3 +847,13 @@ function updateLockStatus() {
|
||||||
classList.toggle("icon-lock-open");
|
classList.toggle("icon-lock-open");
|
||||||
classList.toggle("icon-lock");
|
classList.toggle("icon-lock");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function recalculateReligions(must) {
|
||||||
|
if (!must && !religionsAutoChange.checked) return;
|
||||||
|
|
||||||
|
Religions.recalculate();
|
||||||
|
|
||||||
|
drawReligions();
|
||||||
|
refreshReligionsEditor();
|
||||||
|
drawReligionCenters();
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -163,8 +163,6 @@ function addListeners() {
|
||||||
const line = $element.parentNode;
|
const line = $element.parentNode;
|
||||||
const state = +line.dataset.id;
|
const state = +line.dataset.id;
|
||||||
if (classList.contains("stateCapital")) stateChangeCapitalName(state, line, $element.value);
|
if (classList.contains("stateCapital")) stateChangeCapitalName(state, line, $element.value);
|
||||||
else if (classList.contains("cultureType")) stateChangeType(state, line, $element.value);
|
|
||||||
else if (classList.contains("statePower")) stateChangeExpansionism(state, line, $element.value);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$body.on("change", function (ev) {
|
$body.on("change", function (ev) {
|
||||||
|
|
@ -173,6 +171,8 @@ function addListeners() {
|
||||||
const line = $element.parentNode;
|
const line = $element.parentNode;
|
||||||
const state = +line.dataset.id;
|
const state = +line.dataset.id;
|
||||||
if (classList.contains("stateCulture")) stateChangeCulture(state, line, $element.value);
|
if (classList.contains("stateCulture")) stateChangeCulture(state, line, $element.value);
|
||||||
|
else if (classList.contains("cultureType")) stateChangeType(state, line, $element.value);
|
||||||
|
else if (classList.contains("statePower")) stateChangeExpansionism(state, line, $element.value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -883,7 +883,6 @@ function changeStatesGrowthRate() {
|
||||||
const growthRate = +this.value;
|
const growthRate = +this.value;
|
||||||
byId("statesNeutral").value = growthRate;
|
byId("statesNeutral").value = growthRate;
|
||||||
byId("statesNeutralNumber").value = growthRate;
|
byId("statesNeutralNumber").value = growthRate;
|
||||||
statesNeutral = growthRate;
|
|
||||||
tip("Growth rate: " + growthRate);
|
tip("Growth rate: " + growthRate);
|
||||||
recalculateStates(false);
|
recalculateStates(false);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,44 +1,481 @@
|
||||||
const capitalize = text => text.charAt(0).toUpperCase() + text.slice(1);
|
export const supporters = `ken burgan
|
||||||
|
Sera's Nafitlaan
|
||||||
const format = rawList =>
|
Richard Rogers
|
||||||
rawList
|
Hylobate
|
||||||
.replace(/(?:\r\n|\r|\n)/g, "")
|
Colin deSousa
|
||||||
.split(",")
|
Aurelia De La Silla
|
||||||
.map(name => capitalize(name.trim()))
|
Maciej Kontny
|
||||||
.sort();
|
Ricky L Cain
|
||||||
|
Iggyflare
|
||||||
export const supporters = format(`
|
Garrett Renner
|
||||||
Aaron Meyer,Ahmad Amerih,AstralJacks,aymeric,Billy Dean Goehring,Branndon Edwards,Chase Mayers,Curt Flood,cyninge,Dino Princip,
|
Michael Harris
|
||||||
E.M. White,es,Fondue,Fritjof Olsson,Gatsu,Johan Fröberg,Jonathan Moore,Joseph Miranda,Kate,KC138,Luke Nelson,Markus Finster,Massimo Vella,Mikey,
|
Joshua Maly
|
||||||
Nathan Mitchell,Paavi1,Pat,Ryan Westcott,Sasquatch,Shawn Spencer,Sizz_TV,Timothée CALLET,UTG community,Vlad Tomash,Wil Sisney,William Merriott,
|
Nigel Guest
|
||||||
Xariun,Gun Metal Games,Scott Marner,Spencer Sherman,Valerii Matskevych,Alloyed Clavicle,Stewart Walsh,Ruthlyn Mollett (Javan),Benjamin Mair-Pratt,
|
Theo Hodges
|
||||||
Diagonath,Alexander Thomas,Ashley Wilson-Savoury,William Henry,Preston Brooks,JOSHUA QUALTIERI,Hilton Williams,Katharina Haase,Hisham Bedri,
|
BERTHEAS Frédéric
|
||||||
Ian arless,Karnat,Bird,Kevin,Jessica Thomas,Steve Hyatt,Logicspren,Alfred García,Jonathan Killstring,John Ackley,Invad3r233,Norbert Žigmund,Jennifer,
|
lilMoni
|
||||||
PoliticsBuff,_gfx_,Maggie,Connor McMartin,Jared McDaris,BlastWind,Franc Casanova Ferrer,Dead & Devil,Michael Carmody,Valerie Elise,naikibens220,
|
Δημήτρης Μάρκογιαννακης
|
||||||
Jordon Phillips,William Pucs,The Dungeon Masters,Brady R Rathbun,J,Shadow,Matthew Tiffany,Huw Williams,Joseph Hamilton,FlippantFeline,Tamashi Toh,
|
Lee S.
|
||||||
kms,Stephen Herron,MidnightMoon,Whakomatic x,Barished,Aaron bateson,Brice Moss,Diklyquill,PatronUser,Michael Greiner,Steven Bennett,Jacob Harrington,
|
Chris Dibbs
|
||||||
Miguel C.,Reya C.,Giant Monster Games,Noirbard,Brian Drennen,Ben Craigie,Alex Smolin,Endwords,Joshua E Goodwin,SirTobit ,Allen S. Rout,Allen Bull Bear,
|
jarrad tait
|
||||||
Pippa Mitchell,R K,G0atfather,Ryan Lege,Caner Oleas Pekgönenç,Bradley Edwards,Tertiary ,Austin Miller,Jesse Holmes,Jan Dvořák,Marten F,Erin D. Smale,
|
Jacen Solo
|
||||||
Maxwell Hill,Drunken_Legends,rob bee,Jesse Holmes,YYako,Detocroix,Anoplexian,Hannah,Paul,Sandra Krohn,Lucid,Richard Keating,Allen Varney,Rick Falkvinge,
|
Hannes Rotestam
|
||||||
Seth Fusion,Adam Butler,Gus,StroboWolf,Sadie Blackthorne,Zewen Senpai,Dell McKnight,Oneiris,Darinius Dragonclaw Studios,Christopher Whitney,Rhodes HvZ,
|
Preston Hicks
|
||||||
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,Pierrick Bertrand,Jared Kennedy,Dylan Devenny,Kyle Robertson,
|
Will Fink
|
||||||
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,
|
ControlFreq
|
||||||
Alex Debus,Joshua Vaught,Kyle S,Eric Moore,Dean Dunakin,Uniquenameosaurus,WarWizardGames,Chance Mena,Jan Ka,Miguel Alejandro,Dalton Clark,Simon Drapeau,
|
IllAngel
|
||||||
Radovan Zapletal,Jmmat6,Justa Badge,Blargh Blarghmoomoo,Vanessa Anjos,Grant A. Murray,Akirsop,Rikard Wolff,Jake Fish,teco 47,Antiroo,Jakob Siegel,
|
John Giardina
|
||||||
Guilherme Aguiar,Jarno Hallikainen,Justin Mcclain,Kristin Chernoff,Rowland Kingman,Esther Busch,Grayson McClead,Austin,Hakon the Viking,Chad Riley,
|
Thiago Prado
|
||||||
Cooper Counts,Patrick Jones,Clonetone,PlayByMail.Net,Brad Wardell,Lance Saba,Egoensis,Brea Richards,Tiber,Chris Bloom,Maxim Lowe,Aquelion,
|
Zhang Dijon
|
||||||
Page One Project,Spencer Morris,Paul Ingram,Dust Bunny,Adrian Wright,Eric Alexander Cartaya,GameNight,Thomas Mortensen Hansen,Zklaus,Drinarius,
|
NoBurny
|
||||||
Ed Wright,Lon Varnadore,Crys Cain,Heaven N Lee,Jeffrey Henning,Lazer Elf,Jordan Bellah,Alex Beard,Kass Frisson,Petro Lombaard,Emanuel Pietri,Rox,
|
thibault tersinet
|
||||||
PinkEvil,Gavin Madrigal,Martin Lorber,Prince of Morgoth,Jaryd Armstrong,Andrew Pirkola,ThyHolyDevil,Gary Smith,Tyshaun Wise,Ethan Cook,Jon Stroman,
|
scarletsky
|
||||||
Nobody679,良义 金,Chris Gray,Phoenix Boatwright,Mackenzie,Milo Cohen,Jason Matthew Wuerfel,Rasmus Legêne,Andrew Hines,Wexxler,Espen Sæverud,Binks,
|
Nich Smith
|
||||||
Dominick Ormsby,Linn Browning,Václav Švec,Alan Buehne,George J.Lekkas,Alexandre Boivin,Tommy Mayfield,Skylar Mangum-Turner,Karen Blythe,Stefan Gugerel,
|
Omegus
|
||||||
Mike Conley,Xavier privé,Hope You're Well,Mark Sprietsma,Robert Landry,Nick Mowry,steve hall,Markell,Josh Wren,Neutrix,BLRageQuit,Rocky,
|
Karl Abrahamsson
|
||||||
Dario Spadavecchia,Bas Kroot,John Patrick Callahan Jr,Alexandra Vesey,D,Exp1nt,james,Braxton Istace,w,Rurikid,AntiBlock,Redsauz,BigE0021,
|
Sara Fernandes
|
||||||
Jonathan Williams,ojacid .,Brian Wilson,A Patreon of the Ahts,Shubham Jakhotiya,www15o,Jan Bundesmann,Angelique Badger,Joshua Xiong,Moist mongol,
|
peetey897
|
||||||
Frank Fewkes,jason baldrick,Game Master Pro,Andrew Kircher,Preston Mitchell,Chris Kohut,Emarandzeb,Trentin Bergeron,Damon Gallaty,Pleaseworkforonce,
|
Cooper Janse
|
||||||
Jordan,William Markus,Sidr Dim,Alexander Whittaker,The Next Level,Patrick Valverde,Markus Peham,Daniel Cooper,the Beagles of Neorbus,Marley Moule,
|
G F
|
||||||
Maximilian Schielke,Johnathan Xavier Hutchinson,Ele,Rita,Randy Ross,John Wick,RedSpaz,cameron cannon,Ian Grau-Fay,Kyle Barrett,Charlotte Wiland,
|
Glen Aultman-Bettridge
|
||||||
David Kaul,E. Jason Davis,Cyberate,Atenfox,Sea Wolf,Holly Loveless,Roekai,Alden Z,angel carrillo,Sam Spoerle,S A Rudy,Bird Law Expert,Mira Cyr,
|
Nathan Rogers
|
||||||
Aaron Blair,Neyimadd,RLKZ1022,DerWolf,Kenji Yamada,Zion,Robert Rinne,Actual_Dio,Kyarou
|
Benjamin Mock
|
||||||
`);
|
CadmiumMan
|
||||||
|
Kirk Edwards
|
||||||
|
Leigh G
|
||||||
|
Thom Colyer
|
||||||
|
Frederik
|
||||||
|
C pstj
|
||||||
|
Zachary Pecora
|
||||||
|
Trevor D'Arcey
|
||||||
|
Ryan Gauvin
|
||||||
|
Shawn Moore
|
||||||
|
Jim Channon
|
||||||
|
Kyarou
|
||||||
|
Actual_Dio
|
||||||
|
Jim B Johnson
|
||||||
|
Robert Rinne
|
||||||
|
Zion
|
||||||
|
Kenji Yamada
|
||||||
|
DerWolf
|
||||||
|
RLKZ1022
|
||||||
|
Neyimadd
|
||||||
|
Aaron Blair
|
||||||
|
Mira Cyr
|
||||||
|
Bird Law Expert
|
||||||
|
S A Rudy
|
||||||
|
Sam Spoerle
|
||||||
|
angel carrillo
|
||||||
|
Alden Z
|
||||||
|
Holly Loveless
|
||||||
|
Sea Wolf
|
||||||
|
Atenfox
|
||||||
|
Cyberate
|
||||||
|
E. Jason Davis
|
||||||
|
Caro Lyns
|
||||||
|
David Kaul
|
||||||
|
Charlotte Wiland
|
||||||
|
Kyle Barrett
|
||||||
|
Ian Grau-Fay
|
||||||
|
cameron cannon
|
||||||
|
RedSpaz
|
||||||
|
John Wick
|
||||||
|
Randy Ross
|
||||||
|
Rita
|
||||||
|
Ele
|
||||||
|
Johnathan Xavier Hutchinson
|
||||||
|
Andrew Stein
|
||||||
|
Ghettov Milan
|
||||||
|
Malke
|
||||||
|
TameMoon
|
||||||
|
Daniel Cooper
|
||||||
|
Markus Peham
|
||||||
|
The Next Level
|
||||||
|
Alexander Whittaker
|
||||||
|
Sidr Dim
|
||||||
|
William Markus
|
||||||
|
Jordan
|
||||||
|
Pleaseworkforonce
|
||||||
|
Damon Gallaty
|
||||||
|
Trentin Bergeron
|
||||||
|
Emarandzeb
|
||||||
|
Laulajatar
|
||||||
|
Dale McBane
|
||||||
|
Chris Kohut
|
||||||
|
Preston Mitchell
|
||||||
|
Andrew Kircher
|
||||||
|
Frank Fewkes
|
||||||
|
Moist mongol
|
||||||
|
Joshua Xiong
|
||||||
|
Jan Bundesmann
|
||||||
|
www15o
|
||||||
|
Game Master Pro
|
||||||
|
jason baldrick
|
||||||
|
Exp1nt
|
||||||
|
w
|
||||||
|
Shubham Jakhotiya
|
||||||
|
Braxton Istace
|
||||||
|
LesterThePossum
|
||||||
|
Rurikid
|
||||||
|
ojacid .
|
||||||
|
james
|
||||||
|
A Patreon of the Ahts
|
||||||
|
BigE0021
|
||||||
|
Angelique Badger
|
||||||
|
Jonathan Williams
|
||||||
|
AntiBlock
|
||||||
|
Redsauz
|
||||||
|
Florian Kelber
|
||||||
|
John Patrick Callahan Jr
|
||||||
|
Alexandra Vesey
|
||||||
|
Bas Kroot
|
||||||
|
Dzmitry Malyshau
|
||||||
|
PedanticSteve
|
||||||
|
Josh Wren
|
||||||
|
BLRageQuit
|
||||||
|
Dario Spadavecchia
|
||||||
|
Neutrix
|
||||||
|
Markell
|
||||||
|
Rocky
|
||||||
|
Robert Landry
|
||||||
|
Skylar Mangum-Turner
|
||||||
|
Nick Mowry
|
||||||
|
Anjen Pai
|
||||||
|
Hope You're Well
|
||||||
|
Alexandre Boivin
|
||||||
|
Racussa
|
||||||
|
Mike Conley
|
||||||
|
Karen Blythe
|
||||||
|
Mark Sprietsma
|
||||||
|
Xavier privé
|
||||||
|
Tommy Mayfield
|
||||||
|
Václav Švec
|
||||||
|
Binks
|
||||||
|
Mackenzie
|
||||||
|
Linn Browning
|
||||||
|
Writer's Consultant Page by George J.Lekkas
|
||||||
|
Andrew Hines
|
||||||
|
Wexxler
|
||||||
|
Jason Matthew Wuerfel
|
||||||
|
Milo Cohen
|
||||||
|
Alan Buehne
|
||||||
|
Dominick Ormsby
|
||||||
|
Espen Sæverud
|
||||||
|
Rasmus Legêne
|
||||||
|
rbbalderama
|
||||||
|
Nobody679
|
||||||
|
Prince of Morgoth
|
||||||
|
Jaryd Armstrong
|
||||||
|
Gary Smith
|
||||||
|
ThyHolyDevil
|
||||||
|
良义 金
|
||||||
|
Andrew Pirkola
|
||||||
|
Dig
|
||||||
|
Chris Gray
|
||||||
|
Tyshaun Wise
|
||||||
|
Phoenix
|
||||||
|
Ethan Cook
|
||||||
|
Jordan Bellah
|
||||||
|
Petro Lombaard
|
||||||
|
Kass Frisson
|
||||||
|
Lazer Elf
|
||||||
|
Gavin Madrigal
|
||||||
|
Rox
|
||||||
|
PinkEvil
|
||||||
|
Martin Lorber
|
||||||
|
Emanuel Pietri
|
||||||
|
Alex Beard
|
||||||
|
Jeffrey Henning
|
||||||
|
Eric Alexander Cartaya
|
||||||
|
Dust Bunny
|
||||||
|
GameNight
|
||||||
|
Beingus
|
||||||
|
Crys Cain
|
||||||
|
Lon Varnadore
|
||||||
|
Thomas Mortensen Hansen
|
||||||
|
Drinarius
|
||||||
|
Ed Wright
|
||||||
|
Adrian Wright
|
||||||
|
Zklaus
|
||||||
|
Chris Bloom
|
||||||
|
PlayByMail.Net
|
||||||
|
Maxim Lowe
|
||||||
|
Aquelion
|
||||||
|
Tiber
|
||||||
|
Daydream1013
|
||||||
|
Page One Project
|
||||||
|
Clonetone
|
||||||
|
Egoensis
|
||||||
|
Brad Wardell
|
||||||
|
Heaven N Lee
|
||||||
|
BarnabyJones
|
||||||
|
Paul Ingram
|
||||||
|
Lance Saba
|
||||||
|
Chad Riley
|
||||||
|
Austin
|
||||||
|
Rowland Kingman
|
||||||
|
Decimus Vitalis
|
||||||
|
Grayson McClead
|
||||||
|
Battleturtle1
|
||||||
|
Kristin Chernoff
|
||||||
|
Justin Mcclain
|
||||||
|
Patrick Jones
|
||||||
|
Esther Busch
|
||||||
|
Chance Mena
|
||||||
|
JimmyTheBob
|
||||||
|
Antiroo
|
||||||
|
Dalton Clark
|
||||||
|
Guilherme Aguiar
|
||||||
|
Simon Drapeau
|
||||||
|
Akirsop
|
||||||
|
Radovan Zapletal
|
||||||
|
Vanessa Anjos
|
||||||
|
Rikard Wolff
|
||||||
|
Justa Badge
|
||||||
|
teco 47
|
||||||
|
Jake
|
||||||
|
Miguel Alejandro
|
||||||
|
Blargh Blarghmoomoo
|
||||||
|
Jakob Siegel
|
||||||
|
Grant A. Murray
|
||||||
|
Jarno Hallikainen
|
||||||
|
Jan Ka
|
||||||
|
Joshua Vaught
|
||||||
|
MaxOliver
|
||||||
|
WarWizardGames
|
||||||
|
Evan-DiLeo
|
||||||
|
Eric Moore
|
||||||
|
Kyle S
|
||||||
|
Alex Debus
|
||||||
|
Uniquenameosaurus
|
||||||
|
Dean Dunakin
|
||||||
|
Jack
|
||||||
|
Bryan Brake
|
||||||
|
McNeil Atticus Inksmudge
|
||||||
|
Char
|
||||||
|
Tom Van Orden jr
|
||||||
|
Kendall Patterson
|
||||||
|
Akylos
|
||||||
|
Barna Csíkos
|
||||||
|
Nicholas Grabstas
|
||||||
|
OldFarkas
|
||||||
|
Riley Seaman
|
||||||
|
Daniel Gill
|
||||||
|
Kyle Robertson
|
||||||
|
Natasha Taylor
|
||||||
|
Pierrick Bertrand
|
||||||
|
Jared.K
|
||||||
|
Dylan Devenny
|
||||||
|
logic_error
|
||||||
|
SashaTK
|
||||||
|
Steve Johnson
|
||||||
|
MontyBoosh
|
||||||
|
Achillain
|
||||||
|
Jaden
|
||||||
|
Vito Martono
|
||||||
|
Thirty-OneR
|
||||||
|
Eric Foley
|
||||||
|
ThatGuyGW
|
||||||
|
Dee Chiu
|
||||||
|
James H. Anthony
|
||||||
|
Kevin Cossutta
|
||||||
|
MadNomadMedia
|
||||||
|
Darinius Dragonclaw Studios
|
||||||
|
Tsahyla (Triston Lightyear)
|
||||||
|
Christopher Whitney
|
||||||
|
María Martín López
|
||||||
|
Annie Rishor
|
||||||
|
Aram Sabatés
|
||||||
|
Jeppe Skov Jensen
|
||||||
|
Martin Seeger
|
||||||
|
Oneiris (Oni)
|
||||||
|
EternalDeiwos
|
||||||
|
Richard Keating
|
||||||
|
StroboWolf
|
||||||
|
Rick Falkvinge
|
||||||
|
Zewen Senpai
|
||||||
|
Adam Butler
|
||||||
|
Kassidy
|
||||||
|
Sadie Blackthorne
|
||||||
|
ErrorForever
|
||||||
|
Seth Fusion
|
||||||
|
Gus
|
||||||
|
Paul
|
||||||
|
Lucid
|
||||||
|
Allen Varney
|
||||||
|
Hannah May
|
||||||
|
Sankroh
|
||||||
|
Eliot Miller
|
||||||
|
Detocroix
|
||||||
|
Meg Ziegler
|
||||||
|
rob bee
|
||||||
|
Anoplexian
|
||||||
|
Marten F
|
||||||
|
Erin D. Smale
|
||||||
|
Johnpaul Morrow
|
||||||
|
Roekai
|
||||||
|
Drunken_Legends
|
||||||
|
Jesse Holmes
|
||||||
|
Maxwell Hill
|
||||||
|
Jan Dvořák
|
||||||
|
SirTobit
|
||||||
|
G0atfather
|
||||||
|
Allen S. Rout
|
||||||
|
Pippa Mitchell
|
||||||
|
Austin Miller
|
||||||
|
Caner Oleas Pekgönenç
|
||||||
|
Alison Bull Bear
|
||||||
|
Bradley Edwards
|
||||||
|
Tertiary
|
||||||
|
Daniel
|
||||||
|
Joshua E Goodwin
|
||||||
|
Shaun Alexander
|
||||||
|
Ryan Lege
|
||||||
|
Myrrhlin
|
||||||
|
Jesper Cockx
|
||||||
|
Noirbard
|
||||||
|
Dice
|
||||||
|
Brian Drennen
|
||||||
|
Giant Monster Games
|
||||||
|
Reya C.
|
||||||
|
Krk
|
||||||
|
Endwords
|
||||||
|
Jacob Harrington
|
||||||
|
RK
|
||||||
|
Michael Greiner
|
||||||
|
Steven Bennett
|
||||||
|
Brice Moss
|
||||||
|
Whakomatic x
|
||||||
|
Stephen Herron
|
||||||
|
kosmobius
|
||||||
|
ZizRenanim
|
||||||
|
Barished
|
||||||
|
Maur Razimtheth
|
||||||
|
Aaron bateson
|
||||||
|
Diklyquill
|
||||||
|
Shawn Taylor
|
||||||
|
Brady R Rathbun
|
||||||
|
FlippantFeline
|
||||||
|
Shadow
|
||||||
|
J
|
||||||
|
Tamashi Toh
|
||||||
|
Huw Williams
|
||||||
|
Graves
|
||||||
|
ShadeByTheSea
|
||||||
|
The Dungeon Masters
|
||||||
|
Valerie Elise
|
||||||
|
Empi3
|
||||||
|
William Pucs
|
||||||
|
Michael Carmody
|
||||||
|
Marco Veldman
|
||||||
|
naikibens220
|
||||||
|
Jordon Phillips
|
||||||
|
_gfx_
|
||||||
|
F. Casanova
|
||||||
|
Jared McDaris
|
||||||
|
BlastWind
|
||||||
|
Taldonix
|
||||||
|
Connor McMartin
|
||||||
|
Nexoness
|
||||||
|
Guy
|
||||||
|
Maggie
|
||||||
|
AdvancedAzrielAngel
|
||||||
|
Alfred García
|
||||||
|
Norbert Žigmund
|
||||||
|
Jennifer
|
||||||
|
Titanium Tomes
|
||||||
|
John Ackley
|
||||||
|
Invad3r233
|
||||||
|
Jonathan Killstring
|
||||||
|
Jessica Thomas
|
||||||
|
Nikita Kondratjuks
|
||||||
|
Steve Hyatt
|
||||||
|
PoliticsBuff
|
||||||
|
Ian arless
|
||||||
|
Karnat
|
||||||
|
Hilton Williams
|
||||||
|
Kevin
|
||||||
|
Katharina Haase
|
||||||
|
Hisham Bedri
|
||||||
|
Bird
|
||||||
|
JOSHUA QUALTIERI
|
||||||
|
Preston Brooks
|
||||||
|
Troy Schuler
|
||||||
|
DerGeisterbär
|
||||||
|
L. V. Werneck
|
||||||
|
Marcus Hellyrr
|
||||||
|
yami
|
||||||
|
Daniel Eric Crosby
|
||||||
|
Augusto Chiarle
|
||||||
|
Doug Churchman
|
||||||
|
David Roza
|
||||||
|
Alexander Thomas
|
||||||
|
Ashley Wilson-Savoury
|
||||||
|
Nathan L Myers
|
||||||
|
Theresa Walsh
|
||||||
|
JP Roberts III
|
||||||
|
William Henry
|
||||||
|
OldbeanOldboy
|
||||||
|
Javasharp
|
||||||
|
Diagonath
|
||||||
|
Gun Metal Games
|
||||||
|
Scott Marner
|
||||||
|
Alloyed Clavicle
|
||||||
|
Valerii Matskevych
|
||||||
|
Spencer Sherman
|
||||||
|
Nolan Moore
|
||||||
|
James Schellenger
|
||||||
|
Pat
|
||||||
|
Dino Princip
|
||||||
|
Shawn Spencer
|
||||||
|
Timothée CALLET
|
||||||
|
KC138
|
||||||
|
Nylian
|
||||||
|
Kate
|
||||||
|
Markus Finster
|
||||||
|
CanadianGold
|
||||||
|
AstralJacks
|
||||||
|
Keith Marshall
|
||||||
|
Scott Davis
|
||||||
|
Joseph Miranda
|
||||||
|
Shaptarshi Joarder
|
||||||
|
Branndon
|
||||||
|
EP
|
||||||
|
Johan Fröberg
|
||||||
|
Sasquatch
|
||||||
|
Chase Mayers
|
||||||
|
Sizz_TV
|
||||||
|
Ryan Westcott
|
||||||
|
Nathan Mitchell
|
||||||
|
Curt Flood
|
||||||
|
Mikey
|
||||||
|
E.M. White
|
||||||
|
Billy
|
||||||
|
Vlad Tomash
|
||||||
|
Xariun
|
||||||
|
Luke Nelson
|
||||||
|
W Maxwell Cassity-Guilliom
|
||||||
|
Marty H
|
||||||
|
Aaron Meyer
|
||||||
|
Max Amillios
|
||||||
|
chris
|
||||||
|
cyninge
|
||||||
|
Omegavoid
|
||||||
|
Fritjof Olsson
|
||||||
|
Crazypedia
|
||||||
|
Duncan Thomson
|
||||||
|
William Merriott
|
||||||
|
Gold Tamarin
|
||||||
|
Lhoris
|
||||||
|
Jonathan
|
||||||
|
Jon
|
||||||
|
Massimo Vella
|
||||||
|
Feuver
|
||||||
|
aymeric
|
||||||
|
Eric Schumann
|
||||||
|
Rei
|
||||||
|
Fondue
|
||||||
|
Paavi1
|
||||||
|
Wil Sisney
|
||||||
|
David Patterson`;
|
||||||
|
|
|
||||||
|
|
@ -304,15 +304,34 @@ window.Religions = (function () {
|
||||||
Heresy: {Heresy: 1}
|
Heresy: {Heresy: 1}
|
||||||
};
|
};
|
||||||
|
|
||||||
const methods = {
|
const namingMethods = {
|
||||||
"Random + type": 3,
|
Folk: {
|
||||||
"Random + ism": 1,
|
"Culture + type": 1
|
||||||
"Supreme + ism": 5,
|
},
|
||||||
"Faith of + Supreme": 5,
|
|
||||||
"Place + ism": 1,
|
Organized: {
|
||||||
"Culture + ism": 2,
|
"Random + type": 3,
|
||||||
"Place + ian + type": 6,
|
"Random + ism": 1,
|
||||||
"Culture + type": 4
|
"Supreme + ism": 5,
|
||||||
|
"Faith of + Supreme": 5,
|
||||||
|
"Place + ism": 1,
|
||||||
|
"Culture + ism": 2,
|
||||||
|
"Place + ian + type": 6,
|
||||||
|
"Culture + type": 4
|
||||||
|
},
|
||||||
|
|
||||||
|
Cult: {
|
||||||
|
"Burg + ian + type": 2,
|
||||||
|
"Random + ian + type": 1,
|
||||||
|
"Type + of the + meaning": 2
|
||||||
|
},
|
||||||
|
|
||||||
|
Heresy: {
|
||||||
|
"Burg + ian + type": 3,
|
||||||
|
"Random + ism": 3,
|
||||||
|
"Random + ian + type": 2,
|
||||||
|
"Type + of the + meaning": 1
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const types = {
|
const types = {
|
||||||
|
|
@ -342,381 +361,416 @@ window.Religions = (function () {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const generate = function () {
|
const expansionismMap = {
|
||||||
|
Folk: () => 0,
|
||||||
|
Organized: () => gauss(5, 3, 0, 10, 1),
|
||||||
|
Cult: () => gauss(0.5, 0.5, 0, 5, 1),
|
||||||
|
Heresy: () => gauss(1, 0.5, 0, 5, 1)
|
||||||
|
};
|
||||||
|
|
||||||
|
function generate() {
|
||||||
TIME && console.time("generateReligions");
|
TIME && console.time("generateReligions");
|
||||||
const {cells, states, cultures} = pack;
|
const lockedReligions = pack.religions?.filter(r => r.i && r.lock && !r.removed) || [];
|
||||||
|
|
||||||
const religionIds = new Uint16Array(cells.culture); // cell religion; initially based on culture
|
const folkReligions = generateFolkReligions();
|
||||||
const religions = [];
|
const organizedReligions = generateOrganizedReligions(+religionsInput.value, lockedReligions);
|
||||||
|
|
||||||
// add folk religions
|
const namedReligions = specifyReligions([...folkReligions, ...organizedReligions]);
|
||||||
pack.cultures.forEach(c => {
|
const indexedReligions = combineReligions(namedReligions, lockedReligions);
|
||||||
const newId = c.i;
|
const religionIds = expandReligions(indexedReligions);
|
||||||
if (!newId) return religions.push({i: 0, name: "No religion"});
|
const religions = defineOrigins(religionIds, indexedReligions);
|
||||||
|
|
||||||
if (c.removed) {
|
pack.religions = religions;
|
||||||
religions.push({
|
pack.cells.religion = religionIds;
|
||||||
i: c.i,
|
|
||||||
name: "Extinct religion for " + c.name,
|
|
||||||
color: getMixedColor(c.color, 0.1, 0),
|
|
||||||
removed: true
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pack.religions) {
|
|
||||||
const lockedFolkReligion = pack.religions.find(
|
|
||||||
r => r.culture === c.i && !r.removed && r.lock && r.type === "Folk"
|
|
||||||
);
|
|
||||||
|
|
||||||
if (lockedFolkReligion) {
|
|
||||||
for (const i of cells.i) {
|
|
||||||
if (cells.religion[i] === lockedFolkReligion.i) religionIds[i] = newId;
|
|
||||||
}
|
|
||||||
|
|
||||||
lockedFolkReligion.i = newId;
|
|
||||||
religions.push(lockedFolkReligion);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const form = rw(forms.Folk);
|
|
||||||
const name = c.name + " " + rw(types[form]);
|
|
||||||
const deity = form === "Animism" ? null : getDeityName(c.i);
|
|
||||||
const color = getMixedColor(c.color, 0.1, 0); // `url(#hatch${rand(8,13)})`;
|
|
||||||
religions.push({
|
|
||||||
i: newId,
|
|
||||||
name,
|
|
||||||
color,
|
|
||||||
culture: newId,
|
|
||||||
type: "Folk",
|
|
||||||
form,
|
|
||||||
deity,
|
|
||||||
center: c.center,
|
|
||||||
origins: [0]
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (religionsInput.value == 0 || pack.cultures.length < 2)
|
|
||||||
return religions.filter(r => r.i).forEach(r => (r.code = abbreviate(r.name)));
|
|
||||||
|
|
||||||
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 religionsTree = d3.quadtree();
|
|
||||||
const spacing = (graphWidth + graphHeight) / 6 / religionsInput.value; // base min distance between towns
|
|
||||||
const cultsCount = Math.floor((rand(10, 40) / 100) * religionsInput.value);
|
|
||||||
const count = +religionsInput.value - cultsCount + religions.length;
|
|
||||||
|
|
||||||
function getReligionsInRadius({x, y, r, max}) {
|
|
||||||
if (max === 0) return [0];
|
|
||||||
const cellsInRadius = findAll(x, y, r);
|
|
||||||
const religions = unique(cellsInRadius.map(i => religionIds[i]).filter(r => r));
|
|
||||||
return religions.length ? religions.slice(0, max) : [0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// restore locked non-folk religions
|
|
||||||
if (pack.religions) {
|
|
||||||
const lockedNonFolkReligions = pack.religions.filter(r => r.lock && !r.removed && r.type !== "Folk");
|
|
||||||
for (const religion of lockedNonFolkReligions) {
|
|
||||||
const newId = religions.length;
|
|
||||||
for (const i of cells.i) {
|
|
||||||
if (cells.religion[i] === religion.i) religionIds[i] = newId;
|
|
||||||
}
|
|
||||||
|
|
||||||
religion.i = newId;
|
|
||||||
religion.origins = religion.origins.filter(origin => origin < newId);
|
|
||||||
religionsTree.add(cells.p[religion.center]);
|
|
||||||
religions.push(religion);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate organized religions
|
|
||||||
for (let i = 0; religions.length < count && i < 1000; i++) {
|
|
||||||
let center = sorted[biased(0, sorted.length - 1, 5)]; // religion center
|
|
||||||
const form = rw(forms.Organized);
|
|
||||||
const state = cells.state[center];
|
|
||||||
const culture = cells.culture[center];
|
|
||||||
|
|
||||||
const deity = form === "Non-theism" ? null : getDeityName(culture);
|
|
||||||
let [name, expansion] = getReligionName(form, deity, center);
|
|
||||||
if (expansion === "state" && !state) expansion = "global";
|
|
||||||
if (expansion === "culture" && !culture) expansion = "global";
|
|
||||||
|
|
||||||
if (expansion === "state" && Math.random() > 0.5) center = states[state].center;
|
|
||||||
if (expansion === "culture" && Math.random() > 0.5) center = cultures[culture].center;
|
|
||||||
|
|
||||||
if (!cells.burg[center] && cells.c[center].some(c => cells.burg[c]))
|
|
||||||
center = cells.c[center].find(c => cells.burg[c]);
|
|
||||||
const [x, y] = cells.p[center];
|
|
||||||
|
|
||||||
const s = spacing * gauss(1, 0.3, 0.2, 2, 2); // randomize to make the placement not uniform
|
|
||||||
if (religionsTree.find(x, y, s) !== undefined) continue; // to close to existing religion
|
|
||||||
|
|
||||||
// add "Old" to name of the folk religion on this culture
|
|
||||||
const isFolkBased = expansion === "culture" || P(0.5);
|
|
||||||
const folk = isFolkBased && religions.find(r => r.culture === culture && r.type === "Folk");
|
|
||||||
if (folk && expansion === "culture" && folk.name.slice(0, 3) !== "Old") folk.name = "Old " + folk.name;
|
|
||||||
|
|
||||||
const origins = folk ? [folk.i] : getReligionsInRadius({x, y, r: 150 / count, max: 2});
|
|
||||||
const expansionism = rand(3, 8);
|
|
||||||
const baseColor = religions[culture]?.color || states[state]?.color || getRandomColor();
|
|
||||||
const color = getMixedColor(baseColor, 0.3, 0);
|
|
||||||
|
|
||||||
religions.push({
|
|
||||||
i: religions.length,
|
|
||||||
name,
|
|
||||||
color,
|
|
||||||
culture,
|
|
||||||
type: "Organized",
|
|
||||||
form,
|
|
||||||
deity,
|
|
||||||
expansion,
|
|
||||||
expansionism,
|
|
||||||
center,
|
|
||||||
origins
|
|
||||||
});
|
|
||||||
religionsTree.add([x, y]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// generate cults
|
|
||||||
for (let i = 0; religions.length < count + cultsCount && i < 1000; i++) {
|
|
||||||
const form = rw(forms.Cult);
|
|
||||||
let center = sorted[biased(0, sorted.length - 1, 1)]; // religion center
|
|
||||||
if (!cells.burg[center] && cells.c[center].some(c => cells.burg[c]))
|
|
||||||
center = cells.c[center].find(c => cells.burg[c]);
|
|
||||||
const [x, y] = cells.p[center];
|
|
||||||
|
|
||||||
const s = spacing * gauss(2, 0.3, 1, 3, 2); // randomize to make the placement not uniform
|
|
||||||
if (religionsTree.find(x, y, s) !== undefined) continue; // to close to existing religion
|
|
||||||
|
|
||||||
const culture = cells.culture[center];
|
|
||||||
const origins = getReligionsInRadius({x, y, r: 300 / count, max: rand(0, 4)});
|
|
||||||
|
|
||||||
const deity = getDeityName(culture);
|
|
||||||
const name = getCultName(form, center);
|
|
||||||
const expansionism = gauss(1.1, 0.5, 0, 5);
|
|
||||||
const color = getMixedColor(cultures[culture].color, 0.5, 0); // "url(#hatch7)";
|
|
||||||
religions.push({
|
|
||||||
i: religions.length,
|
|
||||||
name,
|
|
||||||
color,
|
|
||||||
culture,
|
|
||||||
type: "Cult",
|
|
||||||
form,
|
|
||||||
deity,
|
|
||||||
expansion: "global",
|
|
||||||
expansionism,
|
|
||||||
center,
|
|
||||||
origins
|
|
||||||
});
|
|
||||||
religionsTree.add([x, y]);
|
|
||||||
}
|
|
||||||
|
|
||||||
expandReligions();
|
|
||||||
|
|
||||||
// generate heresies
|
|
||||||
religions
|
|
||||||
.filter(r => r.type === "Organized")
|
|
||||||
.forEach(r => {
|
|
||||||
if (r.expansionism < 3) return;
|
|
||||||
const count = gauss(0, 1, 0, 3);
|
|
||||||
for (let i = 0; i < count; i++) {
|
|
||||||
let center = ra(cells.i.filter(i => religionIds[i] === r.i && cells.c[i].some(c => religionIds[c] !== r.i)));
|
|
||||||
if (!center) continue;
|
|
||||||
if (!cells.burg[center] && cells.c[center].some(c => cells.burg[c]))
|
|
||||||
center = cells.c[center].find(c => cells.burg[c]);
|
|
||||||
const [x, y] = cells.p[center];
|
|
||||||
if (religionsTree.find(x, y, spacing / 10) !== undefined) continue; // to close to other
|
|
||||||
|
|
||||||
const culture = cells.culture[center];
|
|
||||||
const name = getCultName("Heresy", center);
|
|
||||||
const expansionism = gauss(1.2, 0.5, 0, 5);
|
|
||||||
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,
|
|
||||||
origins: [r.i]
|
|
||||||
});
|
|
||||||
religionsTree.add([x, y]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
expandHeresies();
|
|
||||||
|
|
||||||
checkCenters();
|
checkCenters();
|
||||||
|
|
||||||
cells.religion = religionIds;
|
|
||||||
pack.religions = religions;
|
|
||||||
|
|
||||||
TIME && console.timeEnd("generateReligions");
|
TIME && console.timeEnd("generateReligions");
|
||||||
|
}
|
||||||
|
|
||||||
// growth algorithm to assign cells to religions
|
function generateFolkReligions() {
|
||||||
function expandReligions() {
|
return pack.cultures
|
||||||
const queue = new PriorityQueue({comparator: (a, b) => a.p - b.p});
|
.filter(c => c.i && !c.removed)
|
||||||
const cost = [];
|
.map(culture => ({type: "Folk", form: rw(forms.Folk), culture: culture.i, center: culture.center}));
|
||||||
|
}
|
||||||
|
|
||||||
religions
|
function generateOrganizedReligions(desiredReligionNumber, lockedReligions) {
|
||||||
.filter(r => !r.lock && (r.type === "Organized" || r.type === "Cult"))
|
const cells = pack.cells;
|
||||||
.forEach(r => {
|
const lockedReligionCount = lockedReligions.filter(({type}) => type !== "Folk").length || 0;
|
||||||
religionIds[r.center] = r.i;
|
const requiredReligionsNumber = desiredReligionNumber - lockedReligionCount;
|
||||||
queue.queue({e: r.center, p: 0, r: r.i, s: cells.state[r.center], c: r.culture});
|
if (requiredReligionsNumber < 1) return [];
|
||||||
cost[r.center] = 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
const neutral = (cells.i.length / 5000) * 200 * gauss(1, 0.3, 0.2, 2, 2) * neutralInput.value; // limit cost for organized religions growth
|
const candidateCells = getCandidateCells();
|
||||||
const popCost = d3.max(cells.pop) / 3; // enougth population to spered religion without penalty
|
const religionCores = placeReligions();
|
||||||
|
|
||||||
while (queue.length) {
|
const cultsCount = Math.floor((rand(1, 4) / 10) * religionCores.length); // 10-40%
|
||||||
const {e, p, r, c, s} = queue.dequeue();
|
const heresiesCount = Math.floor((rand(0, 3) / 10) * religionCores.length); // 0-30%
|
||||||
const expansion = religions[r].expansion;
|
const organizedCount = religionCores.length - cultsCount - heresiesCount;
|
||||||
|
|
||||||
cells.c[e].forEach(nextCell => {
|
const getType = index => {
|
||||||
if (expansion === "culture" && c !== cells.culture[nextCell]) return;
|
if (index < organizedCount) return "Organized";
|
||||||
if (expansion === "state" && s !== cells.state[nextCell]) return;
|
if (index < organizedCount + cultsCount) return "Cult";
|
||||||
if (religions[religionIds[nextCell]]?.lock) return;
|
return "Heresy";
|
||||||
|
};
|
||||||
|
|
||||||
const cultureCost = c !== cells.culture[nextCell] ? 10 : 0;
|
return religionCores.map((cellId, index) => {
|
||||||
const stateCost = s !== cells.state[nextCell] ? 10 : 0;
|
const type = getType(index);
|
||||||
const biomeCost = cells.road[nextCell] ? 1 : biomesData.cost[cells.biome[nextCell]];
|
const form = rw(forms[type]);
|
||||||
const populationCost = Math.max(rn(popCost - cells.pop[nextCell]), 0);
|
const cultureId = cells.culture[cellId];
|
||||||
const heightCost = Math.max(cells.h[nextCell], 20) - 20;
|
|
||||||
const waterCost = cells.h[nextCell] < 20 ? (cells.road[nextCell] ? 50 : 1000) : 0;
|
|
||||||
const totalCost =
|
|
||||||
p +
|
|
||||||
(cultureCost + stateCost + biomeCost + populationCost + heightCost + waterCost) / religions[r].expansionism;
|
|
||||||
if (totalCost > neutral) return;
|
|
||||||
|
|
||||||
if (!cost[nextCell] || totalCost < cost[nextCell]) {
|
return {type, form, culture: cultureId, center: cellId};
|
||||||
if (cells.h[nextCell] >= 20 && cells.culture[nextCell]) religionIds[nextCell] = r; // assign religion to cell
|
});
|
||||||
cost[nextCell] = totalCost;
|
|
||||||
queue.queue({e: nextCell, p: totalCost, r, c, s});
|
function placeReligions() {
|
||||||
}
|
const religionCells = [];
|
||||||
});
|
const religionsTree = d3.quadtree();
|
||||||
|
|
||||||
|
// pre-populate with locked centers
|
||||||
|
lockedReligions.forEach(({center}) => religionsTree.add(cells.p[center]));
|
||||||
|
|
||||||
|
// min distance between religion inceptions
|
||||||
|
const spacing = (graphWidth + graphHeight) / 2 / desiredReligionNumber;
|
||||||
|
|
||||||
|
for (const cellId of candidateCells) {
|
||||||
|
const [x, y] = cells.p[cellId];
|
||||||
|
|
||||||
|
if (religionsTree.find(x, y, spacing) === undefined) {
|
||||||
|
religionCells.push(cellId);
|
||||||
|
religionsTree.add([x, y]);
|
||||||
|
|
||||||
|
if (religionCells.length === requiredReligionsNumber) return religionCells;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WARN && console.warn(`Placed only ${religionCells.length} of ${requiredReligionsNumber} religions`);
|
||||||
|
return religionCells;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCandidateCells() {
|
||||||
|
const validBurgs = pack.burgs.filter(b => b.i && !b.removed);
|
||||||
|
|
||||||
|
if (validBurgs.length >= requiredReligionsNumber)
|
||||||
|
return validBurgs.sort((a, b) => b.population - a.population).map(burg => burg.cell);
|
||||||
|
return cells.i.filter(i => cells.s[i] > 2).sort((a, b) => cells.s[b] - cells.s[a]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function specifyReligions(newReligions) {
|
||||||
|
const {cells, cultures} = pack;
|
||||||
|
|
||||||
|
const rawReligions = newReligions.map(({type, form, culture: cultureId, center}) => {
|
||||||
|
const supreme = getDeityName(cultureId);
|
||||||
|
const deity = form === "Non-theism" || form === "Animism" ? null : supreme;
|
||||||
|
|
||||||
|
const stateId = cells.state[center];
|
||||||
|
|
||||||
|
let [name, expansion] = generateReligionName(type, form, supreme, center);
|
||||||
|
if (expansion === "state" && !stateId) expansion = "global";
|
||||||
|
|
||||||
|
const expansionism = expansionismMap[type]();
|
||||||
|
const color = getReligionColor(cultures[cultureId], type);
|
||||||
|
|
||||||
|
return {name, type, form, culture: cultureId, center, deity, expansion, expansionism, color};
|
||||||
|
});
|
||||||
|
|
||||||
|
return rawReligions;
|
||||||
|
|
||||||
|
function getReligionColor(culture, type) {
|
||||||
|
if (!culture.i) return getRandomColor();
|
||||||
|
|
||||||
|
if (type === "Folk") return culture.color;
|
||||||
|
if (type === "Heresy") return getMixedColor(culture.color, 0.35, 0.2);
|
||||||
|
if (type === "Cult") return getMixedColor(culture.color, 0.5, 0);
|
||||||
|
return getMixedColor(culture.color, 0.25, 0.4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// indexes, conditionally renames, and abbreviates religions
|
||||||
|
function combineReligions(namedReligions, lockedReligions) {
|
||||||
|
const indexedReligions = [{name: "No religion", i: 0}];
|
||||||
|
|
||||||
|
const {lockedReligionQueue, highestLockedIndex, codes, numberLockedFolk} = parseLockedReligions();
|
||||||
|
const maxIndex = Math.max(
|
||||||
|
highestLockedIndex,
|
||||||
|
namedReligions.length + lockedReligions.length + 1 - numberLockedFolk
|
||||||
|
);
|
||||||
|
|
||||||
|
for (let index = 1, progress = 0; index < maxIndex; index = indexedReligions.length) {
|
||||||
|
// place locked religion back at its old index
|
||||||
|
if (index === lockedReligionQueue[0]?.i) {
|
||||||
|
const nextReligion = lockedReligionQueue.shift();
|
||||||
|
indexedReligions.push(nextReligion);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// slot the new religions
|
||||||
|
if (progress < namedReligions.length) {
|
||||||
|
const nextReligion = namedReligions[progress];
|
||||||
|
progress++;
|
||||||
|
|
||||||
|
if (
|
||||||
|
nextReligion.type === "Folk" &&
|
||||||
|
lockedReligions.some(({type, culture}) => type === "Folk" && culture === nextReligion.culture)
|
||||||
|
)
|
||||||
|
continue; // when there is a locked Folk religion for this culture discard duplicate
|
||||||
|
|
||||||
|
const newName = renameOld(nextReligion);
|
||||||
|
const code = abbreviate(newName, codes);
|
||||||
|
codes.push(code);
|
||||||
|
indexedReligions.push({...nextReligion, i: index, name: newName, code});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
indexedReligions.push({i: index, type: "Folk", culture: 0, name: "Removed religion", removed: true});
|
||||||
|
}
|
||||||
|
return indexedReligions;
|
||||||
|
|
||||||
|
function parseLockedReligions() {
|
||||||
|
// copy and sort the locked religions list
|
||||||
|
const lockedReligionQueue = lockedReligions
|
||||||
|
.map(religion => {
|
||||||
|
// and filter their origins to locked religions
|
||||||
|
let newOrigin = religion.origins.filter(n => lockedReligions.some(({i: index}) => index === n));
|
||||||
|
if (newOrigin === []) newOrigin = [0];
|
||||||
|
return {...religion, origins: newOrigin};
|
||||||
|
})
|
||||||
|
.sort((a, b) => a.i - b.i);
|
||||||
|
|
||||||
|
const highestLockedIndex = Math.max(...lockedReligions.map(r => r.i));
|
||||||
|
const codes = lockedReligions.length > 0 ? lockedReligions.map(r => r.code) : [];
|
||||||
|
const numberLockedFolk = lockedReligions.filter(({type}) => type === "Folk").length;
|
||||||
|
|
||||||
|
return {lockedReligionQueue, highestLockedIndex, codes, numberLockedFolk};
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepend 'Old' to names of folk religions which have organized competitors
|
||||||
|
function renameOld({name, type, culture: cultureId}) {
|
||||||
|
if (type !== "Folk") return name;
|
||||||
|
|
||||||
|
const haveOrganized =
|
||||||
|
namedReligions.some(
|
||||||
|
({type, culture, expansion}) => culture === cultureId && type === "Organized" && expansion === "culture"
|
||||||
|
) ||
|
||||||
|
lockedReligions.some(
|
||||||
|
({type, culture, expansion}) => culture === cultureId && type === "Organized" && expansion === "culture"
|
||||||
|
);
|
||||||
|
if (haveOrganized && name.slice(0, 3) !== "Old") return `Old ${name}`;
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// finally generate and stores origins trees
|
||||||
|
function defineOrigins(religionIds, indexedReligions) {
|
||||||
|
const religionOriginsParamsMap = {
|
||||||
|
Organized: {clusterSize: 100, maxReligions: 2},
|
||||||
|
Cult: {clusterSize: 50, maxReligions: 3},
|
||||||
|
Heresy: {clusterSize: 50, maxReligions: 4}
|
||||||
|
};
|
||||||
|
|
||||||
|
const origins = indexedReligions.map(({i, type, culture: cultureId, expansion, center}) => {
|
||||||
|
if (i === 0) return null; // no religion
|
||||||
|
if (type === "Folk") return [0]; // folk religions originate from its parent culture only
|
||||||
|
|
||||||
|
const folkReligion = indexedReligions.find(({culture, type}) => type === "Folk" && culture === cultureId);
|
||||||
|
const isFolkBased = folkReligion && cultureId && expansion === "culture" && each(2)(center);
|
||||||
|
if (isFolkBased) return [folkReligion.i];
|
||||||
|
|
||||||
|
const {clusterSize, maxReligions} = religionOriginsParamsMap[type];
|
||||||
|
const fallbackOrigin = folkReligion?.i || 0;
|
||||||
|
return getReligionsInRadius(pack.cells.c, center, religionIds, i, clusterSize, maxReligions, fallbackOrigin);
|
||||||
|
});
|
||||||
|
|
||||||
|
return indexedReligions.map((religion, index) => ({...religion, origins: origins[index]}));
|
||||||
|
}
|
||||||
|
|
||||||
|
function getReligionsInRadius(neighbors, center, religionIds, religionId, clusterSize, maxReligions, fallbackOrigin) {
|
||||||
|
const foundReligions = new Set();
|
||||||
|
const queue = [center];
|
||||||
|
const checked = {};
|
||||||
|
|
||||||
|
for (let size = 0; queue.length && size < clusterSize; size++) {
|
||||||
|
const cellId = queue.shift();
|
||||||
|
checked[cellId] = true;
|
||||||
|
|
||||||
|
for (const neibId of neighbors[cellId]) {
|
||||||
|
if (checked[neibId]) continue;
|
||||||
|
checked[neibId] = true;
|
||||||
|
|
||||||
|
const neibReligion = religionIds[neibId];
|
||||||
|
if (neibReligion && neibReligion < religionId) foundReligions.add(neibReligion);
|
||||||
|
if (foundReligions.size >= maxReligions) return [...foundReligions];
|
||||||
|
queue.push(neibId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// growth algorithm to assign cells to heresies
|
return foundReligions.size ? [...foundReligions] : [fallbackOrigin];
|
||||||
function expandHeresies() {
|
}
|
||||||
const queue = new PriorityQueue({comparator: (a, b) => a.p - b.p});
|
|
||||||
const cost = [];
|
|
||||||
|
|
||||||
religions
|
// growth algorithm to assign cells to religions
|
||||||
.filter(r => !r.lock && r.type === "Heresy")
|
function expandReligions(religions) {
|
||||||
.forEach(r => {
|
const cells = pack.cells;
|
||||||
const b = religionIds[r.center]; // "base" religion id
|
const religionIds = spreadFolkReligions(religions);
|
||||||
religionIds[r.center] = r.i; // heresy id
|
|
||||||
queue.queue({e: r.center, p: 0, r: r.i, b});
|
|
||||||
cost[r.center] = 1;
|
|
||||||
});
|
|
||||||
|
|
||||||
const neutral = (cells.i.length / 5000) * 500 * neutralInput.value; // limit cost for heresies growth
|
const queue = new PriorityQueue({comparator: (a, b) => a.p - b.p});
|
||||||
|
const cost = [];
|
||||||
|
|
||||||
while (queue.length) {
|
const maxExpansionCost = (cells.i.length / 20) * neutralInput.value; // limit cost for organized religions growth
|
||||||
const {e, p, r, b} = queue.dequeue();
|
|
||||||
|
|
||||||
cells.c[e].forEach(nextCell => {
|
const biomePassageCost = cellId => biomesData.cost[cells.biome[cellId]];
|
||||||
if (religions[religionIds[nextCell]]?.lock) return;
|
|
||||||
const religionCost = religionIds[nextCell] === b ? 0 : 2000;
|
|
||||||
const biomeCost = cells.road[nextCell] ? 0 : biomesData.cost[cells.biome[nextCell]];
|
|
||||||
const heightCost = Math.max(cells.h[nextCell], 20) - 20;
|
|
||||||
const waterCost = cells.h[nextCell] < 20 ? (cells.road[nextCell] ? 50 : 1000) : 0;
|
|
||||||
const totalCost =
|
|
||||||
p + (religionCost + biomeCost + heightCost + waterCost) / Math.max(religions[r].expansionism, 0.1);
|
|
||||||
|
|
||||||
if (totalCost > neutral) return;
|
religions
|
||||||
|
.filter(r => r.i && !r.lock && r.type !== "Folk" && !r.removed)
|
||||||
|
.forEach(r => {
|
||||||
|
religionIds[r.center] = r.i;
|
||||||
|
queue.queue({e: r.center, p: 0, r: r.i, s: cells.state[r.center]});
|
||||||
|
cost[r.center] = 1;
|
||||||
|
});
|
||||||
|
|
||||||
if (!cost[nextCell] || totalCost < cost[nextCell]) {
|
const religionsMap = new Map(religions.map(r => [r.i, r]));
|
||||||
if (cells.h[nextCell] >= 20 && cells.culture[nextCell]) religionIds[nextCell] = r; // assign religion to cell
|
|
||||||
cost[nextCell] = totalCost;
|
|
||||||
queue.queue({e: nextCell, p: totalCost, r});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkCenters() {
|
const isMainRoad = cellId => cells.road[cellId] - cells.crossroad[cellId] > 4;
|
||||||
const codes = religions.map(r => r.code);
|
const isTrail = cellId => cells.h[cellId] > 19 && cells.road[cellId] - cells.crossroad[cellId] === 1;
|
||||||
religions.forEach(r => {
|
const isSeaRoute = cellId => cells.h[cellId] < 20 && cells.road[cellId];
|
||||||
if (!r.i) return;
|
const isWater = cellId => cells.h[cellId] < 20;
|
||||||
r.code = abbreviate(r.name, codes);
|
|
||||||
|
|
||||||
// move religion center if it's not within religion area after expansion
|
while (queue.length) {
|
||||||
if (religionIds[r.center] === r.i) return; // in area
|
const {e: cellId, p, r, s: state} = queue.dequeue();
|
||||||
const firstCell = cells.i.find(i => religionIds[i] === r.i);
|
const {culture, expansion, expansionism} = religionsMap.get(r);
|
||||||
if (firstCell) r.center = firstCell; // move center, othervise it's an extinct religion
|
|
||||||
|
cells.c[cellId].forEach(nextCell => {
|
||||||
|
if (expansion === "culture" && culture !== cells.culture[nextCell]) return;
|
||||||
|
if (expansion === "state" && state !== cells.state[nextCell]) return;
|
||||||
|
if (religionsMap.get(religionIds[nextCell])?.lock) return;
|
||||||
|
|
||||||
|
const cultureCost = culture !== cells.culture[nextCell] ? 10 : 0;
|
||||||
|
const stateCost = state !== cells.state[nextCell] ? 10 : 0;
|
||||||
|
const passageCost = getPassageCost(nextCell);
|
||||||
|
|
||||||
|
const cellCost = cultureCost + stateCost + passageCost;
|
||||||
|
const totalCost = p + 10 + cellCost / expansionism;
|
||||||
|
if (totalCost > maxExpansionCost) return;
|
||||||
|
|
||||||
|
if (!cost[nextCell] || totalCost < cost[nextCell]) {
|
||||||
|
if (cells.culture[nextCell]) religionIds[nextCell] = r; // assign religion to cell
|
||||||
|
cost[nextCell] = totalCost;
|
||||||
|
|
||||||
|
queue.queue({e: nextCell, p: totalCost, r, s: state});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
return religionIds;
|
||||||
|
|
||||||
|
function getPassageCost(cellId) {
|
||||||
|
if (isWater(cellId)) return isSeaRoute ? 50 : 500;
|
||||||
|
if (isMainRoad(cellId)) return 1;
|
||||||
|
const biomeCost = biomePassageCost(cellId);
|
||||||
|
return isTrail(cellId) ? biomeCost / 1.5 : biomeCost;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// folk religions initially get all cells of their culture, and locked religions are retained
|
||||||
|
function spreadFolkReligions(religions) {
|
||||||
|
const cells = pack.cells;
|
||||||
|
const hasPrior = cells.religion && true;
|
||||||
|
const religionIds = new Uint16Array(cells.i.length);
|
||||||
|
|
||||||
|
const folkReligions = religions.filter(religion => religion.type === "Folk" && !religion.removed);
|
||||||
|
const cultureToReligionMap = new Map(folkReligions.map(({i, culture}) => [culture, i]));
|
||||||
|
|
||||||
|
for (const cellId of cells.i) {
|
||||||
|
const oldId = (hasPrior && cells.religion[cellId]) || 0;
|
||||||
|
if (oldId && religions[oldId]?.lock && !religions[oldId]?.removed) {
|
||||||
|
religionIds[cellId] = oldId;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const cultureId = cells.culture[cellId];
|
||||||
|
religionIds[cellId] = cultureToReligionMap.get(cultureId) || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return religionIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkCenters() {
|
||||||
|
const cells = pack.cells;
|
||||||
|
pack.religions.forEach(r => {
|
||||||
|
if (!r.i) return;
|
||||||
|
// move religion center if it's not within religion area after expansion
|
||||||
|
if (cells.religion[r.center] === r.i) return; // in area
|
||||||
|
const firstCell = cells.i.find(i => cells.religion[i] === r.i);
|
||||||
|
const cultureHome = pack.cultures[r.culture]?.center;
|
||||||
|
if (firstCell) r.center = firstCell; // move center, othervise it's an extinct religion
|
||||||
|
else if (r.type === "Folk" && cultureHome) r.center = cultureHome; // reset extinct culture centers
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function recalculate() {
|
||||||
|
const newReligionIds = expandReligions(pack.religions);
|
||||||
|
pack.cells.religion = newReligionIds;
|
||||||
|
|
||||||
|
checkCenters();
|
||||||
|
}
|
||||||
|
|
||||||
const add = function (center) {
|
const add = function (center) {
|
||||||
const {cells, religions} = pack;
|
const {cells, cultures, religions} = pack;
|
||||||
const religionId = cells.religion[center];
|
const religionId = cells.religion[center];
|
||||||
|
const i = religions.length;
|
||||||
|
|
||||||
const culture = cells.culture[center];
|
const cultureId = cells.culture[center];
|
||||||
const color = getMixedColor(religions[religionId].color, 0.3, 0);
|
const missingFolk =
|
||||||
|
cultureId !== 0 &&
|
||||||
|
!religions.some(({type, culture, removed}) => type === "Folk" && culture === cultureId && !removed);
|
||||||
|
const color = missingFolk ? cultures[cultureId].color : getMixedColor(religions[religionId].color, 0.3, 0);
|
||||||
|
|
||||||
const type =
|
const type = missingFolk
|
||||||
religions[religionId].type === "Organized" ? rw({Organized: 4, Cult: 1, Heresy: 2}) : rw({Organized: 5, Cult: 2});
|
? "Folk"
|
||||||
|
: religions[religionId].type === "Organized"
|
||||||
|
? rw({Organized: 4, Cult: 1, Heresy: 2})
|
||||||
|
: rw({Organized: 5, Cult: 2});
|
||||||
const form = rw(forms[type]);
|
const form = rw(forms[type]);
|
||||||
const deity =
|
const deity =
|
||||||
type === "Heresy" ? religions[religionId].deity : form === "Non-theism" ? null : getDeityName(culture);
|
type === "Heresy"
|
||||||
|
? religions[religionId].deity
|
||||||
|
: form === "Non-theism" || form === "Animism"
|
||||||
|
? null
|
||||||
|
: getDeityName(cultureId);
|
||||||
|
|
||||||
let name, expansion;
|
const [name, expansion] = generateReligionName(type, form, deity, center);
|
||||||
if (type === "Organized") [name, expansion] = getReligionName(form, deity, center);
|
|
||||||
else {
|
|
||||||
name = getCultName(form, center);
|
|
||||||
expansion = "global";
|
|
||||||
}
|
|
||||||
|
|
||||||
const formName = type === "Heresy" ? religions[religionId].form : form;
|
const formName = type === "Heresy" ? religions[religionId].form : form;
|
||||||
const code = abbreviate(
|
const code = abbreviate(
|
||||||
name,
|
name,
|
||||||
religions.map(r => r.code)
|
religions.map(r => r.code)
|
||||||
);
|
);
|
||||||
|
const influences = getReligionsInRadius(cells.c, center, cells.religion, i, 25, 3, 0);
|
||||||
|
const origins = type === "Folk" ? [0] : influences;
|
||||||
|
|
||||||
const i = religions.length;
|
|
||||||
religions.push({
|
religions.push({
|
||||||
i,
|
i,
|
||||||
name,
|
name,
|
||||||
color,
|
color,
|
||||||
culture,
|
culture: cultureId,
|
||||||
type,
|
type,
|
||||||
form: formName,
|
form: formName,
|
||||||
deity,
|
deity,
|
||||||
expansion,
|
expansion,
|
||||||
expansionism: 0,
|
expansionism: expansionismMap[type](),
|
||||||
center,
|
center,
|
||||||
cells: 0,
|
cells: 0,
|
||||||
area: 0,
|
area: 0,
|
||||||
rural: 0,
|
rural: 0,
|
||||||
urban: 0,
|
urban: 0,
|
||||||
origins: [religionId],
|
origins,
|
||||||
code
|
code
|
||||||
});
|
});
|
||||||
cells.religion[center] = i;
|
cells.religion[center] = i;
|
||||||
};
|
};
|
||||||
|
|
||||||
function updateCultures() {
|
function updateCultures() {
|
||||||
TIME && console.time("updateCulturesForReligions");
|
|
||||||
pack.religions = pack.religions.map((religion, index) => {
|
pack.religions = pack.religions.map((religion, index) => {
|
||||||
if (index === 0) {
|
if (index === 0) return religion;
|
||||||
return religion;
|
|
||||||
}
|
|
||||||
return {...religion, culture: pack.cells.culture[religion.center]};
|
return {...religion, culture: pack.cells.culture[religion.center]};
|
||||||
});
|
});
|
||||||
TIME && console.timeEnd("updateCulturesForReligions");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// get supreme deity name
|
// get supreme deity name
|
||||||
|
|
@ -735,22 +789,24 @@ window.Religions = (function () {
|
||||||
if (a === "Number") return ra(base.number);
|
if (a === "Number") return ra(base.number);
|
||||||
if (a === "Being") return ra(base.being);
|
if (a === "Being") return ra(base.being);
|
||||||
if (a === "Adjective") return ra(base.adjective);
|
if (a === "Adjective") return ra(base.adjective);
|
||||||
if (a === "Color + Animal") return ra(base.color) + " " + ra(base.animal);
|
if (a === "Color + Animal") return `${ra(base.color)} ${ra(base.animal)}`;
|
||||||
if (a === "Adjective + Animal") return ra(base.adjective) + " " + ra(base.animal);
|
if (a === "Adjective + Animal") return `${ra(base.adjective)} ${ra(base.animal)}`;
|
||||||
if (a === "Adjective + Being") return ra(base.adjective) + " " + ra(base.being);
|
if (a === "Adjective + Being") return `${ra(base.adjective)} ${ra(base.being)}`;
|
||||||
if (a === "Adjective + Genitive") return ra(base.adjective) + " " + ra(base.genitive);
|
if (a === "Adjective + Genitive") return `${ra(base.adjective)} ${ra(base.genitive)}`;
|
||||||
if (a === "Color + Being") return ra(base.color) + " " + ra(base.being);
|
if (a === "Color + Being") return `${ra(base.color)} ${ra(base.being)}`;
|
||||||
if (a === "Color + Genitive") return ra(base.color) + " " + ra(base.genitive);
|
if (a === "Color + Genitive") return `${ra(base.color)} ${ra(base.genitive)}`;
|
||||||
if (a === "Being + of + Genitive") return ra(base.being) + " of " + ra(base.genitive);
|
if (a === "Being + of + Genitive") return `${ra(base.being)} of ${ra(base.genitive)}`;
|
||||||
if (a === "Being + of the + Genitive") return ra(base.being) + " of the " + ra(base.theGenitive);
|
if (a === "Being + of the + Genitive") return `${ra(base.being)} of the ${ra(base.theGenitive)}`;
|
||||||
if (a === "Animal + of + Genitive") return ra(base.animal) + " of " + ra(base.genitive);
|
if (a === "Animal + of + Genitive") return `${ra(base.animal)} of ${ra(base.genitive)}`;
|
||||||
if (a === "Adjective + Being + of + Genitive")
|
if (a === "Adjective + Being + of + Genitive")
|
||||||
return ra(base.adjective) + " " + ra(base.being) + " of " + ra(base.genitive);
|
return `${ra(base.adjective)} ${ra(base.being)} of ${ra(base.genitive)}`;
|
||||||
if (a === "Adjective + Animal + of + Genitive")
|
if (a === "Adjective + Animal + of + Genitive")
|
||||||
return ra(base.adjective) + " " + ra(base.animal) + " of " + ra(base.genitive);
|
return `${ra(base.adjective)} ${ra(base.animal)} of ${ra(base.genitive)}`;
|
||||||
|
|
||||||
|
ERROR && console.error("Unkown generation approach");
|
||||||
}
|
}
|
||||||
|
|
||||||
function getReligionName(form, deity, center) {
|
function generateReligionName(variety, form, deity, center) {
|
||||||
const {cells, cultures, burgs, states} = pack;
|
const {cells, cultures, burgs, states} = pack;
|
||||||
|
|
||||||
const random = () => Names.getCulture(cells.culture[center], null, null, "", 0);
|
const random = () => Names.getCulture(cells.culture[center], null, null, "", 0);
|
||||||
|
|
@ -766,7 +822,7 @@ window.Religions = (function () {
|
||||||
return adj ? getAdjective(name) : name;
|
return adj ? getAdjective(name) : name;
|
||||||
};
|
};
|
||||||
|
|
||||||
const m = rw(methods);
|
const m = rw(namingMethods[variety]);
|
||||||
if (m === "Random + type") return [random() + " " + type(), "global"];
|
if (m === "Random + type") return [random() + " " + type(), "global"];
|
||||||
if (m === "Random + ism") return [trimVowels(random()) + "ism", "global"];
|
if (m === "Random + ism") return [trimVowels(random()) + "ism", "global"];
|
||||||
if (m === "Supreme + ism" && deity) return [trimVowels(supreme()) + "ism", "global"];
|
if (m === "Supreme + ism" && deity) return [trimVowels(supreme()) + "ism", "global"];
|
||||||
|
|
@ -776,24 +832,11 @@ window.Religions = (function () {
|
||||||
if (m === "Culture + ism") return [trimVowels(culture()) + "ism", "culture"];
|
if (m === "Culture + ism") return [trimVowels(culture()) + "ism", "culture"];
|
||||||
if (m === "Place + ian + type") return [place("adj") + " " + type(), "state"];
|
if (m === "Place + ian + type") return [place("adj") + " " + type(), "state"];
|
||||||
if (m === "Culture + type") return [culture() + " " + type(), "culture"];
|
if (m === "Culture + type") return [culture() + " " + type(), "culture"];
|
||||||
|
if (m === "Burg + ian + type") return [`${place("adj")} ${type()}`, "global"];
|
||||||
|
if (m === "Random + ian + type") return [`${getAdjective(random())} ${type()}`, "global"];
|
||||||
|
if (m === "Type + of the + meaning") return [`${type()} of the ${generateMeaning()}`, "global"];
|
||||||
return [trimVowels(random()) + "ism", "global"]; // else
|
return [trimVowels(random()) + "ism", "global"]; // else
|
||||||
}
|
}
|
||||||
|
|
||||||
function getCultName(form, center) {
|
return {generate, add, getDeityName, updateCultures, recalculate};
|
||||||
const cells = pack.cells;
|
|
||||||
const type = function () {
|
|
||||||
return rw(types[form]);
|
|
||||||
};
|
|
||||||
const random = function () {
|
|
||||||
return trimVowels(Names.getCulture(cells.culture[center], null, null, "", 0).split(/[ ,]+/)[0]);
|
|
||||||
};
|
|
||||||
const burg = function () {
|
|
||||||
return trimVowels(pack.burgs[cells.burg[center]].name.split(/[ ,]+/)[0]);
|
|
||||||
};
|
|
||||||
if (cells.burg[center]) return burg() + "ian " + type();
|
|
||||||
if (Math.random() > 0.5) return random() + "ian " + type();
|
|
||||||
return type() + " of the " + generateMeaning();
|
|
||||||
}
|
|
||||||
|
|
||||||
return {generate, add, getDeityName, updateCultures};
|
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -48,7 +48,9 @@ window.Rivers = (function () {
|
||||||
cells.fl[i] += prec[cells.g[i]] / cellsNumberModifier; // add flux from precipitation
|
cells.fl[i] += prec[cells.g[i]] / cellsNumberModifier; // add flux from precipitation
|
||||||
|
|
||||||
// create lake outlet if lake is not in deep depression and flux > evaporation
|
// create lake outlet if lake is not in deep depression and flux > evaporation
|
||||||
const lakes = lakeOutCells[i] ? features.filter(feature => i === feature.outCell && feature.flux > feature.evaporation) : [];
|
const lakes = lakeOutCells[i]
|
||||||
|
? features.filter(feature => i === feature.outCell && feature.flux > feature.evaporation)
|
||||||
|
: [];
|
||||||
for (const lake of lakes) {
|
for (const lake of lakes) {
|
||||||
const lakeCell = cells.c[i].find(c => h[c] < 20 && cells.f[c] === lake.i);
|
const lakeCell = cells.c[i].find(c => h[c] < 20 && cells.f[c] === lake.i);
|
||||||
cells.fl[lakeCell] += Math.max(lake.flux - lake.evaporation, 0); // not evaporated lake water drains to outlet
|
cells.fl[lakeCell] += Math.max(lake.flux - lake.evaporation, 0); // not evaporated lake water drains to outlet
|
||||||
|
|
@ -191,7 +193,18 @@ window.Rivers = (function () {
|
||||||
const length = getApproximateLength(meanderedPoints);
|
const length = getApproximateLength(meanderedPoints);
|
||||||
const width = getWidth(getOffset(discharge, meanderedPoints.length, widthFactor, 0));
|
const width = getWidth(getOffset(discharge, meanderedPoints.length, widthFactor, 0));
|
||||||
|
|
||||||
pack.rivers.push({i: riverId, source, mouth, discharge, length, width, widthFactor, sourceWidth: 0, parent, cells: riverCells});
|
pack.rivers.push({
|
||||||
|
i: riverId,
|
||||||
|
source,
|
||||||
|
mouth,
|
||||||
|
discharge,
|
||||||
|
length,
|
||||||
|
width,
|
||||||
|
widthFactor,
|
||||||
|
sourceWidth: 0,
|
||||||
|
parent,
|
||||||
|
cells: riverCells
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -479,6 +492,10 @@ window.Rivers = (function () {
|
||||||
return getBasin(parent);
|
return getBasin(parent);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getNextId = function (rivers) {
|
||||||
|
return rivers.length ? Math.max(...rivers.map(r => r.i)) + 1 : 1;
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
generate,
|
generate,
|
||||||
alterHeights,
|
alterHeights,
|
||||||
|
|
@ -493,6 +510,7 @@ window.Rivers = (function () {
|
||||||
getOffset,
|
getOffset,
|
||||||
getApproximateLength,
|
getApproximateLength,
|
||||||
getRiverPoints,
|
getRiverPoints,
|
||||||
remove
|
remove,
|
||||||
|
getNextId
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
|
||||||
|
|
@ -1176,18 +1176,18 @@ function refreshAllEditors() {
|
||||||
// dynamically loaded editors
|
// dynamically loaded editors
|
||||||
async function editStates() {
|
async function editStates() {
|
||||||
if (customization) return;
|
if (customization) return;
|
||||||
const Editor = await import("../dynamic/editors/states-editor.js?v=12062022");
|
const Editor = await import("../dynamic/editors/states-editor.js?v=1.89.05");
|
||||||
Editor.open();
|
Editor.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function editCultures() {
|
async function editCultures() {
|
||||||
if (customization) return;
|
if (customization) return;
|
||||||
const Editor = await import("../dynamic/editors/cultures-editor.js?v=1.88.06");
|
const Editor = await import("../dynamic/editors/cultures-editor.js?v=1.89.09");
|
||||||
Editor.open();
|
Editor.open();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function editReligions() {
|
async function editReligions() {
|
||||||
if (customization) return;
|
if (customization) return;
|
||||||
const Editor = await import("../dynamic/editors/religions-editor.js?v=1.88.06");
|
const Editor = await import("../dynamic/editors/religions-editor.js?v=1.89.10");
|
||||||
Editor.open();
|
Editor.open();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -204,6 +204,13 @@ function editHeightmap(options) {
|
||||||
INFO && console.group("Edit Heightmap");
|
INFO && console.group("Edit Heightmap");
|
||||||
TIME && console.time("regenerateErasedData");
|
TIME && console.time("regenerateErasedData");
|
||||||
|
|
||||||
|
// remove data
|
||||||
|
pack.cultures = [];
|
||||||
|
pack.burgs = [];
|
||||||
|
pack.states = [];
|
||||||
|
pack.provinces = [];
|
||||||
|
pack.religions = [];
|
||||||
|
|
||||||
const erosionAllowed = allowErosion.checked;
|
const erosionAllowed = allowErosion.checked;
|
||||||
markFeatures();
|
markFeatures();
|
||||||
markupGridOcean();
|
markupGridOcean();
|
||||||
|
|
@ -231,8 +238,10 @@ function editHeightmap(options) {
|
||||||
Lakes.defineGroup();
|
Lakes.defineGroup();
|
||||||
defineBiomes();
|
defineBiomes();
|
||||||
rankCells();
|
rankCells();
|
||||||
|
|
||||||
Cultures.generate();
|
Cultures.generate();
|
||||||
Cultures.expand();
|
Cultures.expand();
|
||||||
|
|
||||||
BurgsAndStates.generate();
|
BurgsAndStates.generate();
|
||||||
Religions.generate();
|
Religions.generate();
|
||||||
BurgsAndStates.defineStateForms();
|
BurgsAndStates.defineStateForms();
|
||||||
|
|
|
||||||
|
|
@ -67,11 +67,11 @@ function editIce() {
|
||||||
function addIcebergOnClick() {
|
function addIcebergOnClick() {
|
||||||
const [x, y] = d3.mouse(this);
|
const [x, y] = d3.mouse(this);
|
||||||
const i = findGridCell(x, y, grid);
|
const i = findGridCell(x, y, grid);
|
||||||
const c = grid.points[i];
|
const [cx, cy] = grid.points[i];
|
||||||
const s = +document.getElementById("iceSize").value;
|
const size = +document.getElementById("iceSize")?.value || 1;
|
||||||
|
|
||||||
const points = getGridPolygon(i).map(p => [(p[0] + (c[0] - p[0]) / s) | 0, (p[1] + (c[1] - p[1]) / s) | 0]);
|
const points = getGridPolygon(i).map(([x, y]) => [rn(lerp(cx, x, size), 2), rn(lerp(cy, y, size), 2)]);
|
||||||
const iceberg = ice.append("polygon").attr("points", points).attr("cell", i).attr("size", s);
|
const iceberg = ice.append("polygon").attr("points", points).attr("cell", i).attr("size", size);
|
||||||
iceberg.call(d3.drag().on("drag", dragElement));
|
iceberg.call(d3.drag().on("drag", dragElement));
|
||||||
if (d3.event.shiftKey === false) toggleAdd();
|
if (d3.event.shiftKey === false) toggleAdd();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -671,11 +671,10 @@ function toggleIce(event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawIce() {
|
function drawIce() {
|
||||||
const cells = grid.cells,
|
const {cells, vertices} = grid;
|
||||||
vertices = grid.vertices,
|
const {temp, h} = cells;
|
||||||
n = cells.i.length,
|
const n = cells.i.length;
|
||||||
temp = cells.temp,
|
|
||||||
h = cells.h;
|
|
||||||
const used = new Uint8Array(cells.i.length);
|
const used = new Uint8Array(cells.i.length);
|
||||||
Math.random = aleaPRNG(seed);
|
Math.random = aleaPRNG(seed);
|
||||||
|
|
||||||
|
|
@ -700,23 +699,22 @@ function drawIce() {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tNormalized = normalize(t, -8, 2);
|
||||||
|
const randomFactor = t > -5 ? 0.4 + rand() * 1.2 : 1;
|
||||||
|
|
||||||
// mildly cold: iceberd
|
// mildly cold: iceberd
|
||||||
if (P(normalize(t, -7, 2.5))) continue; // t[-5; 2] cold: skip some cells
|
if (P(tNormalized ** 0.5 * randomFactor)) continue; // cold: skip some cells
|
||||||
if (grid.features[cells.f[i]].type === "lake") continue; // lake: no icebers
|
if (grid.features[cells.f[i]].type === "lake") continue; // lake: no icebers
|
||||||
let size = (6.5 + t) / 10; // iceberg size: 0 = full size, 1 = zero size
|
|
||||||
if (cells.t[i] === -1) size *= 1.3; // coasline: smaller icebers
|
let size = 1 - tNormalized; // iceberg size: 0 = zero size, 1 = full size
|
||||||
size = Math.min(size * (0.4 + rand() * 1.2), 0.95); // randomize iceberg size
|
if (cells.t[i] === -1) size /= 1.3; // coasline: smaller icebers
|
||||||
resizePolygon(i, size);
|
resizePolygon(i, minmax(rn(size * randomFactor, 2), 0.08, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
function resizePolygon(i, s) {
|
function resizePolygon(i, size) {
|
||||||
const c = grid.points[i];
|
const [cx, cy] = grid.points[i];
|
||||||
const points = getGridPolygon(i).map(p => [(p[0] + (c[0] - p[0]) * s) | 0, (p[1] + (c[1] - p[1]) * s) | 0]);
|
const points = getGridPolygon(i).map(([x, y]) => [rn(lerp(cx, x, size), 2), rn(lerp(cy, y, size), 2)]);
|
||||||
ice
|
ice.append("polygon").attr("points", points).attr("cell", i).attr("size", size);
|
||||||
.append("polygon")
|
|
||||||
.attr("points", points)
|
|
||||||
.attr("cell", i)
|
|
||||||
.attr("size", rn(1 - s, 2));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// connect vertices to chain
|
// connect vertices to chain
|
||||||
|
|
|
||||||
|
|
@ -42,12 +42,11 @@ function editNotes(id, name) {
|
||||||
|
|
||||||
$("#notesEditor").dialog({
|
$("#notesEditor").dialog({
|
||||||
title: "Notes Editor",
|
title: "Notes Editor",
|
||||||
width: "minmax(80vw, 540px)",
|
width: window.innerWidth * 0.8,
|
||||||
height: window.innerHeight * 0.75,
|
height: window.innerHeight * 0.75,
|
||||||
position: {my: "center", at: "center", of: "svg"},
|
position: {my: "center", at: "center", of: "svg"},
|
||||||
close: removeEditor
|
close: removeEditor
|
||||||
});
|
});
|
||||||
$("[aria-describedby='notesEditor']").css("top", "10vh");
|
|
||||||
|
|
||||||
if (modules.editNotes) return;
|
if (modules.editNotes) return;
|
||||||
modules.editNotes = true;
|
modules.editNotes = true;
|
||||||
|
|
|
||||||
|
|
@ -77,12 +77,15 @@ document
|
||||||
// show popup with a list of Patreon supportes (updated manually)
|
// show popup with a list of Patreon supportes (updated manually)
|
||||||
async function showSupporters() {
|
async function showSupporters() {
|
||||||
const {supporters} = await import("../dynamic/supporters.js?v=19062022");
|
const {supporters} = await import("../dynamic/supporters.js?v=19062022");
|
||||||
|
const list = supporters.split("\n").sort();
|
||||||
|
const columns = window.innerWidth < 800 ? 2 : 5;
|
||||||
|
|
||||||
alertMessage.innerHTML =
|
alertMessage.innerHTML =
|
||||||
"<ul style='column-count: 5; column-gap: 2em'>" + supporters.map(n => `<li>${n}</li>`).join("") + "</ul>";
|
`<ul style='column-count: ${columns}; column-gap: 2em'>` + list.map(n => `<li>${n}</li>`).join("") + "</ul>";
|
||||||
$("#alert").dialog({
|
$("#alert").dialog({
|
||||||
resizable: false,
|
resizable: false,
|
||||||
title: "Patreon Supporters",
|
title: "Patreon Supporters",
|
||||||
width: "54vw",
|
width: "min-width",
|
||||||
position: {my: "center", at: "center", of: "svg"}
|
position: {my: "center", at: "center", of: "svg"}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -157,9 +160,20 @@ optionsContent.addEventListener("click", function (event) {
|
||||||
});
|
});
|
||||||
|
|
||||||
function mapSizeInputChange() {
|
function mapSizeInputChange() {
|
||||||
|
const $mapWidthInput = byId("mapWidthInput");
|
||||||
|
const $mapHeightInput = byId("mapHeightInput");
|
||||||
|
|
||||||
changeMapSize();
|
changeMapSize();
|
||||||
localStorage.setItem("mapWidth", mapWidthInput.value);
|
localStorage.setItem("mapWidth", $mapWidthInput.value);
|
||||||
localStorage.setItem("mapHeight", mapHeightInput.value);
|
localStorage.setItem("mapHeight", $mapHeightInput.value);
|
||||||
|
|
||||||
|
const tooWide = +$mapWidthInput.value > window.innerWidth;
|
||||||
|
const tooHigh = +$mapHeightInput.value > window.innerHeight;
|
||||||
|
|
||||||
|
if (tooWide || tooHigh) {
|
||||||
|
const message = `Canvas size is larger than actual window size (${window.innerWidth} x ${window.innerHeight}). It can affect the performance if you are going to create a new map`;
|
||||||
|
tip(message, false, "warn", 4000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// change svg size on manual size change or window resize, do not change graph size
|
// change svg size on manual size change or window resize, do not change graph size
|
||||||
|
|
@ -534,7 +548,7 @@ function applyStoredOptions() {
|
||||||
options.stateLabelsMode = stateLabelsModeInput.value;
|
options.stateLabelsMode = stateLabelsModeInput.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
// randomize options if randomization is allowed (not locked or options='default')
|
// randomize options if randomization is allowed (not locked or queryParam options='default')
|
||||||
function randomizeOptions() {
|
function randomizeOptions() {
|
||||||
const randomize = new URL(window.location.href).searchParams.get("options") === "default"; // ignore stored options
|
const randomize = new URL(window.location.href).searchParams.get("options") === "default"; // ignore stored options
|
||||||
|
|
||||||
|
|
@ -546,7 +560,7 @@ function randomizeOptions() {
|
||||||
manorsInput.value = 1000;
|
manorsInput.value = 1000;
|
||||||
manorsOutput.value = "auto";
|
manorsOutput.value = "auto";
|
||||||
}
|
}
|
||||||
if (randomize || !locked("religions")) religionsInput.value = religionsOutput.value = gauss(5, 2, 2, 10);
|
if (randomize || !locked("religions")) religionsInput.value = religionsOutput.value = gauss(6, 3, 2, 10);
|
||||||
if (randomize || !locked("power")) powerInput.value = powerOutput.value = gauss(4, 2, 0, 10, 2);
|
if (randomize || !locked("power")) powerInput.value = powerOutput.value = gauss(4, 2, 0, 10, 2);
|
||||||
if (randomize || !locked("neutral")) neutralInput.value = neutralOutput.value = rn(1 + Math.random(), 1);
|
if (randomize || !locked("neutral")) neutralInput.value = neutralOutput.value = rn(1 + Math.random(), 1);
|
||||||
if (randomize || !locked("cultures")) culturesInput.value = culturesOutput.value = gauss(12, 3, 5, 30);
|
if (randomize || !locked("cultures")) culturesInput.value = culturesOutput.value = gauss(12, 3, 5, 30);
|
||||||
|
|
@ -602,17 +616,17 @@ function randomizeCultureSet() {
|
||||||
function setRendering(value) {
|
function setRendering(value) {
|
||||||
viewbox.attr("shape-rendering", value);
|
viewbox.attr("shape-rendering", value);
|
||||||
|
|
||||||
if (value === "optimizeSpeed") {
|
// if (value === "optimizeSpeed") {
|
||||||
// block some styles
|
// // block some styles
|
||||||
coastline.select("#sea_island").style("filter", "none");
|
// coastline.select("#sea_island").style("filter", "none");
|
||||||
statesHalo.style("display", "none");
|
// statesHalo.style("display", "none");
|
||||||
emblems.style("opacity", 1);
|
// emblems.style("opacity", 1);
|
||||||
} else {
|
// } else {
|
||||||
// remove style block
|
// // remove style block
|
||||||
coastline.select("#sea_island").style("filter", null);
|
// coastline.select("#sea_island").style("filter", null);
|
||||||
statesHalo.style("display", null);
|
// statesHalo.style("display", null);
|
||||||
emblems.style("opacity", null);
|
// emblems.style("opacity", null);
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
// generate current year and era name
|
// generate current year and era name
|
||||||
|
|
|
||||||
|
|
@ -74,12 +74,13 @@ function createRiver() {
|
||||||
|
|
||||||
function addRiver() {
|
function addRiver() {
|
||||||
const {rivers, cells} = pack;
|
const {rivers, cells} = pack;
|
||||||
const {addMeandering, getApproximateLength, getWidth, getOffset, getName, getRiverPath, getBasin} = Rivers;
|
const {addMeandering, getApproximateLength, getWidth, getOffset, getName, getRiverPath, getBasin, getNextId} =
|
||||||
|
Rivers;
|
||||||
|
|
||||||
const riverCells = createRiver.cells;
|
const riverCells = createRiver.cells;
|
||||||
if (riverCells.length < 2) return tip("Add at least 2 cells", false, "error");
|
if (riverCells.length < 2) return tip("Add at least 2 cells", false, "error");
|
||||||
|
|
||||||
const riverId = rivers.length ? last(rivers).i + 1 : 1;
|
const riverId = getNextId(rivers);
|
||||||
const parent = cells.r[last(riverCells)] || riverId;
|
const parent = cells.r[last(riverCells)] || riverId;
|
||||||
|
|
||||||
riverCells.forEach(cell => {
|
riverCells.forEach(cell => {
|
||||||
|
|
@ -100,12 +101,30 @@ function createRiver() {
|
||||||
const name = getName(mouth);
|
const name = getName(mouth);
|
||||||
const basin = getBasin(parent);
|
const basin = getBasin(parent);
|
||||||
|
|
||||||
rivers.push({i: riverId, source, mouth, discharge, length, width, widthFactor, sourceWidth, parent, cells: riverCells, basin, name, type: "River"});
|
rivers.push({
|
||||||
|
i: riverId,
|
||||||
|
source,
|
||||||
|
mouth,
|
||||||
|
discharge,
|
||||||
|
length,
|
||||||
|
width,
|
||||||
|
widthFactor,
|
||||||
|
sourceWidth,
|
||||||
|
parent,
|
||||||
|
cells: riverCells,
|
||||||
|
basin,
|
||||||
|
name,
|
||||||
|
type: "River"
|
||||||
|
});
|
||||||
const id = "river" + riverId;
|
const id = "river" + riverId;
|
||||||
|
|
||||||
// render river
|
// render river
|
||||||
lineGen.curve(d3.curveCatmullRom.alpha(0.1));
|
lineGen.curve(d3.curveCatmullRom.alpha(0.1));
|
||||||
viewbox.select("#rivers").append("path").attr("id", id).attr("d", getRiverPath(meanderedPoints, widthFactor, sourceWidth));
|
viewbox
|
||||||
|
.select("#rivers")
|
||||||
|
.append("path")
|
||||||
|
.attr("id", id)
|
||||||
|
.attr("d", getRiverPath(meanderedPoints, widthFactor, sourceWidth));
|
||||||
|
|
||||||
editRiver(id);
|
editRiver(id);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
const CONTROL_POINST_DISTANCE = 10;
|
||||||
|
|
||||||
function editRoute(onClick) {
|
function editRoute(onClick) {
|
||||||
if (customization) return;
|
if (customization) return;
|
||||||
if (!onClick && elSelected && d3.event.target.id === elSelected.attr("id")) return;
|
if (!onClick && elSelected && d3.event.target.id === elSelected.attr("id")) return;
|
||||||
|
|
@ -47,13 +50,13 @@ function editRoute(onClick) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function drawControlPoints(node) {
|
function drawControlPoints(node) {
|
||||||
const l = node.getTotalLength();
|
const totalLength = node.getTotalLength();
|
||||||
const increment = l / Math.ceil(l / 4);
|
const increment = totalLength / Math.ceil(totalLength / CONTROL_POINST_DISTANCE);
|
||||||
for (let i = 0; i <= l; i += increment) {
|
for (let i = 0; i <= totalLength; i += increment) {
|
||||||
const point = node.getPointAtLength(i);
|
const point = node.getPointAtLength(i);
|
||||||
addControlPoint([point.x, point.y]);
|
addControlPoint([point.x, point.y]);
|
||||||
}
|
}
|
||||||
routeLength.innerHTML = rn(l * distanceScaleInput.value) + " " + distanceUnitInput.value;
|
routeLength.innerHTML = rn(totalLength * distanceScaleInput.value) + " " + distanceUnitInput.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addControlPoint(point, before = null) {
|
function addControlPoint(point, before = null) {
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,27 @@
|
||||||
// UI module to control the style presets
|
// UI module to control the style presets
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const systemPresets = ["default", "ancient", "gloom", "light", "watercolor", "clean", "atlas", "cyberpunk", "monochrome"];
|
const systemPresets = [
|
||||||
|
"default",
|
||||||
|
"ancient",
|
||||||
|
"gloom",
|
||||||
|
"pale",
|
||||||
|
"light",
|
||||||
|
"watercolor",
|
||||||
|
"clean",
|
||||||
|
"atlas",
|
||||||
|
"cyberpunk",
|
||||||
|
"monochrome"
|
||||||
|
];
|
||||||
const customPresetPrefix = "fmgStyle_";
|
const customPresetPrefix = "fmgStyle_";
|
||||||
|
|
||||||
// add style presets to list
|
// add style presets to list
|
||||||
{
|
{
|
||||||
const systemOptions = systemPresets.map(styleName => `<option value="${styleName}">${styleName}</option>`);
|
const systemOptions = systemPresets.map(styleName => `<option value="${styleName}">${styleName}</option>`);
|
||||||
const storedStyles = Object.keys(localStorage).filter(key => key.startsWith(customPresetPrefix));
|
const storedStyles = Object.keys(localStorage).filter(key => key.startsWith(customPresetPrefix));
|
||||||
const customOptions = storedStyles.map(styleName => `<option value="${styleName}">${styleName.replace(customPresetPrefix, "")} [custom]</option>`);
|
const customOptions = storedStyles.map(
|
||||||
|
styleName => `<option value="${styleName}">${styleName.replace(customPresetPrefix, "")} [custom]</option>`
|
||||||
|
);
|
||||||
const options = systemOptions.join("") + customOptions.join("");
|
const options = systemOptions.join("") + customOptions.join("");
|
||||||
document.getElementById("stylePreset").innerHTML = options;
|
document.getElementById("stylePreset").innerHTML = options;
|
||||||
}
|
}
|
||||||
|
|
@ -37,7 +50,8 @@ async function getStylePreset(desiredPreset) {
|
||||||
const isValid = JSON.isValid(storedStyleJSON);
|
const isValid = JSON.isValid(storedStyleJSON);
|
||||||
if (isValid) return [desiredPreset, JSON.parse(storedStyleJSON)];
|
if (isValid) return [desiredPreset, JSON.parse(storedStyleJSON)];
|
||||||
|
|
||||||
ERROR && console.error(`Custom style ${desiredPreset} stored in localStorage is not valid. Applying default style`);
|
ERROR &&
|
||||||
|
console.error(`Custom style ${desiredPreset} stored in localStorage is not valid. Applying default style`);
|
||||||
presetToLoad = "default";
|
presetToLoad = "default";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -145,8 +159,31 @@ function addStylePreset() {
|
||||||
"#stateBorders": ["opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter"],
|
"#stateBorders": ["opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter"],
|
||||||
"#provinceBorders": ["opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter"],
|
"#provinceBorders": ["opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter"],
|
||||||
"#cells": ["opacity", "stroke", "stroke-width", "filter", "mask"],
|
"#cells": ["opacity", "stroke", "stroke-width", "filter", "mask"],
|
||||||
"#gridOverlay": ["opacity", "scale", "dx", "dy", "type", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "transform", "filter", "mask"],
|
"#gridOverlay": [
|
||||||
"#coordinates": ["opacity", "data-size", "font-size", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter", "mask"],
|
"opacity",
|
||||||
|
"scale",
|
||||||
|
"dx",
|
||||||
|
"dy",
|
||||||
|
"type",
|
||||||
|
"stroke",
|
||||||
|
"stroke-width",
|
||||||
|
"stroke-dasharray",
|
||||||
|
"stroke-linecap",
|
||||||
|
"transform",
|
||||||
|
"filter",
|
||||||
|
"mask"
|
||||||
|
],
|
||||||
|
"#coordinates": [
|
||||||
|
"opacity",
|
||||||
|
"data-size",
|
||||||
|
"font-size",
|
||||||
|
"stroke",
|
||||||
|
"stroke-width",
|
||||||
|
"stroke-dasharray",
|
||||||
|
"stroke-linecap",
|
||||||
|
"filter",
|
||||||
|
"mask"
|
||||||
|
],
|
||||||
"#compass": ["opacity", "transform", "filter", "mask", "shape-rendering"],
|
"#compass": ["opacity", "transform", "filter", "mask", "shape-rendering"],
|
||||||
"#rose": ["transform"],
|
"#rose": ["transform"],
|
||||||
"#relig": ["opacity", "stroke", "stroke-width", "filter"],
|
"#relig": ["opacity", "stroke", "stroke-width", "filter"],
|
||||||
|
|
@ -174,7 +211,17 @@ function addStylePreset() {
|
||||||
"#statesBody": ["opacity", "filter"],
|
"#statesBody": ["opacity", "filter"],
|
||||||
"#statesHalo": ["opacity", "data-width", "stroke-width", "filter"],
|
"#statesHalo": ["opacity", "data-width", "stroke-width", "filter"],
|
||||||
"#provs": ["opacity", "fill", "font-size", "font-family", "filter"],
|
"#provs": ["opacity", "fill", "font-size", "font-family", "filter"],
|
||||||
"#temperature": ["opacity", "font-size", "fill", "fill-opacity", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "filter"],
|
"#temperature": [
|
||||||
|
"opacity",
|
||||||
|
"font-size",
|
||||||
|
"fill",
|
||||||
|
"fill-opacity",
|
||||||
|
"stroke",
|
||||||
|
"stroke-width",
|
||||||
|
"stroke-dasharray",
|
||||||
|
"stroke-linecap",
|
||||||
|
"filter"
|
||||||
|
],
|
||||||
"#ice": ["opacity", "fill", "stroke", "stroke-width", "filter"],
|
"#ice": ["opacity", "fill", "stroke", "stroke-width", "filter"],
|
||||||
"#emblems": ["opacity", "stroke-width", "filter"],
|
"#emblems": ["opacity", "stroke-width", "filter"],
|
||||||
"#texture": ["opacity", "filter", "mask"],
|
"#texture": ["opacity", "filter", "mask"],
|
||||||
|
|
@ -184,16 +231,65 @@ function addStylePreset() {
|
||||||
"#oceanBase": ["fill"],
|
"#oceanBase": ["fill"],
|
||||||
"#oceanicPattern": ["href", "opacity"],
|
"#oceanicPattern": ["href", "opacity"],
|
||||||
"#terrs": ["opacity", "scheme", "terracing", "skip", "relax", "curve", "filter", "mask"],
|
"#terrs": ["opacity", "scheme", "terracing", "skip", "relax", "curve", "filter", "mask"],
|
||||||
"#legend": ["data-size", "font-size", "font-family", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap", "data-x", "data-y", "data-columns"],
|
"#legend": [
|
||||||
|
"data-size",
|
||||||
|
"font-size",
|
||||||
|
"font-family",
|
||||||
|
"stroke",
|
||||||
|
"stroke-width",
|
||||||
|
"stroke-dasharray",
|
||||||
|
"stroke-linecap",
|
||||||
|
"data-x",
|
||||||
|
"data-y",
|
||||||
|
"data-columns"
|
||||||
|
],
|
||||||
"#legendBox": ["fill", "fill-opacity"],
|
"#legendBox": ["fill", "fill-opacity"],
|
||||||
"#burgLabels > #cities": ["opacity", "fill", "text-shadow", "data-size", "font-size", "font-family"],
|
"#burgLabels > #cities": ["opacity", "fill", "text-shadow", "data-size", "font-size", "font-family"],
|
||||||
"#burgIcons > #cities": ["opacity", "fill", "fill-opacity", "size", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap"],
|
"#burgIcons > #cities": [
|
||||||
|
"opacity",
|
||||||
|
"fill",
|
||||||
|
"fill-opacity",
|
||||||
|
"size",
|
||||||
|
"stroke",
|
||||||
|
"stroke-width",
|
||||||
|
"stroke-dasharray",
|
||||||
|
"stroke-linecap"
|
||||||
|
],
|
||||||
"#anchors > #cities": ["opacity", "fill", "size", "stroke", "stroke-width"],
|
"#anchors > #cities": ["opacity", "fill", "size", "stroke", "stroke-width"],
|
||||||
"#burgLabels > #towns": ["opacity", "fill", "text-shadow", "data-size", "font-size", "font-family"],
|
"#burgLabels > #towns": ["opacity", "fill", "text-shadow", "data-size", "font-size", "font-family"],
|
||||||
"#burgIcons > #towns": ["opacity", "fill", "fill-opacity", "size", "stroke", "stroke-width", "stroke-dasharray", "stroke-linecap"],
|
"#burgIcons > #towns": [
|
||||||
|
"opacity",
|
||||||
|
"fill",
|
||||||
|
"fill-opacity",
|
||||||
|
"size",
|
||||||
|
"stroke",
|
||||||
|
"stroke-width",
|
||||||
|
"stroke-dasharray",
|
||||||
|
"stroke-linecap"
|
||||||
|
],
|
||||||
"#anchors > #towns": ["opacity", "fill", "size", "stroke", "stroke-width"],
|
"#anchors > #towns": ["opacity", "fill", "size", "stroke", "stroke-width"],
|
||||||
"#labels > #states": ["opacity", "fill", "stroke", "stroke-width", "text-shadow", "data-size", "font-size", "font-family", "filter"],
|
"#labels > #states": [
|
||||||
"#labels > #addedLabels": ["opacity", "fill", "stroke", "stroke-width", "text-shadow", "data-size", "font-size", "font-family", "filter"],
|
"opacity",
|
||||||
|
"fill",
|
||||||
|
"stroke",
|
||||||
|
"stroke-width",
|
||||||
|
"text-shadow",
|
||||||
|
"data-size",
|
||||||
|
"font-size",
|
||||||
|
"font-family",
|
||||||
|
"filter"
|
||||||
|
],
|
||||||
|
"#labels > #addedLabels": [
|
||||||
|
"opacity",
|
||||||
|
"fill",
|
||||||
|
"stroke",
|
||||||
|
"stroke-width",
|
||||||
|
"text-shadow",
|
||||||
|
"data-size",
|
||||||
|
"font-size",
|
||||||
|
"font-family",
|
||||||
|
"filter"
|
||||||
|
],
|
||||||
"#fogging": ["opacity", "fill", "filter"]
|
"#fogging": ["opacity", "fill", "filter"]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -238,7 +334,8 @@ function addStylePreset() {
|
||||||
if (!styleJSON) return tip("Please provide a style JSON", false, "error");
|
if (!styleJSON) return tip("Please provide a style JSON", false, "error");
|
||||||
if (!JSON.isValid(styleJSON)) return tip("JSON string is not valid, please check the format", false, "error");
|
if (!JSON.isValid(styleJSON)) return tip("JSON string is not valid, please check the format", false, "error");
|
||||||
if (!desiredName) return tip("Please provide a preset name", false, "error");
|
if (!desiredName) return tip("Please provide a preset name", false, "error");
|
||||||
if (styleSaverTip.innerHTML === "default") return tip("You cannot overwrite default preset, please change the name", false, "error");
|
if (styleSaverTip.innerHTML === "default")
|
||||||
|
return tip("You cannot overwrite default preset, please change the name", false, "error");
|
||||||
|
|
||||||
const presetName = customPresetPrefix + desiredName;
|
const presetName = customPresetPrefix + desiredName;
|
||||||
applyOption(stylePreset, presetName, desiredName + " [custom]");
|
applyOption(stylePreset, presetName, desiredName + " [custom]");
|
||||||
|
|
|
||||||
|
|
@ -74,10 +74,8 @@ toolsContent.addEventListener("click", function (event) {
|
||||||
});
|
});
|
||||||
|
|
||||||
function processFeatureRegeneration(event, button) {
|
function processFeatureRegeneration(event, button) {
|
||||||
if (button === "regenerateStateLabels") {
|
if (button === "regenerateStateLabels") BurgsAndStates.drawStateLabels();
|
||||||
BurgsAndStates.drawStateLabels();
|
else if (button === "regenerateReliefIcons") {
|
||||||
if (!layerIsOn("toggleLabels")) toggleLabels();
|
|
||||||
} else if (button === "regenerateReliefIcons") {
|
|
||||||
ReliefIcons();
|
ReliefIcons();
|
||||||
if (!layerIsOn("toggleRelief")) toggleRelief();
|
if (!layerIsOn("toggleRelief")) toggleRelief();
|
||||||
} else if (button === "regenerateRoutes") {
|
} else if (button === "regenerateRoutes") {
|
||||||
|
|
@ -628,10 +626,11 @@ function addRiverOnClick() {
|
||||||
getType,
|
getType,
|
||||||
getWidth,
|
getWidth,
|
||||||
getOffset,
|
getOffset,
|
||||||
getApproximateLength
|
getApproximateLength,
|
||||||
|
getNextId
|
||||||
} = Rivers;
|
} = Rivers;
|
||||||
const riverCells = [];
|
const riverCells = [];
|
||||||
let riverId = rivers.length ? last(rivers).i + 1 : 1;
|
let riverId = getNextId(rivers);
|
||||||
let parent = riverId;
|
let parent = riverId;
|
||||||
|
|
||||||
const initialFlux = grid.cells.prec[cells.g[i]];
|
const initialFlux = grid.cells.prec[cells.g[i]];
|
||||||
|
|
|
||||||
|
|
@ -226,7 +226,7 @@
|
||||||
"opacity": 0.4,
|
"opacity": 0.4,
|
||||||
"data-width": 10,
|
"data-width": 10,
|
||||||
"stroke-width": 10,
|
"stroke-width": 10,
|
||||||
"filter": "blur(5px)"
|
"filter": "blur(3.5px)"
|
||||||
},
|
},
|
||||||
"#provs": {
|
"#provs": {
|
||||||
"opacity": 0.7,
|
"opacity": 0.7,
|
||||||
|
|
|
||||||
|
|
@ -192,18 +192,18 @@
|
||||||
"filter": null
|
"filter": null
|
||||||
},
|
},
|
||||||
"#roads": {
|
"#roads": {
|
||||||
"opacity": 0.9,
|
"opacity": 0.8,
|
||||||
"stroke": "#3c1d0b",
|
"stroke": "#95481a",
|
||||||
"stroke-width": 1.37,
|
"stroke-width": 0.8,
|
||||||
"stroke-dasharray": 2,
|
"stroke-dasharray": 2,
|
||||||
"stroke-linecap": "inherit",
|
"stroke-linecap": "inherit",
|
||||||
"filter": null,
|
"filter": null,
|
||||||
"mask": null
|
"mask": null
|
||||||
},
|
},
|
||||||
"#trails": {
|
"#trails": {
|
||||||
"opacity": 0.9,
|
"opacity": 0.8,
|
||||||
"stroke": "#95481a",
|
"stroke": "#95481a",
|
||||||
"stroke-width": 0.88,
|
"stroke-width": 0.5,
|
||||||
"stroke-dasharray": ".8 1.6",
|
"stroke-dasharray": ".8 1.6",
|
||||||
"stroke-linecap": "butt",
|
"stroke-linecap": "butt",
|
||||||
"filter": null,
|
"filter": null,
|
||||||
|
|
|
||||||
389
styles/pale.json
Normal file
389
styles/pale.json
Normal file
|
|
@ -0,0 +1,389 @@
|
||||||
|
{
|
||||||
|
"#map": {
|
||||||
|
"background-color": "#000000",
|
||||||
|
"filter": null,
|
||||||
|
"data-filter": null
|
||||||
|
},
|
||||||
|
"#armies": {
|
||||||
|
"font-size": 9,
|
||||||
|
"box-size": 4.5,
|
||||||
|
"stroke": "#000",
|
||||||
|
"stroke-width": 0,
|
||||||
|
"fill-opacity": 1,
|
||||||
|
"filter": "url(#dropShadow05)"
|
||||||
|
},
|
||||||
|
"#biomes": {
|
||||||
|
"opacity": 0.6,
|
||||||
|
"filter": null,
|
||||||
|
"mask": "url(#land)"
|
||||||
|
},
|
||||||
|
"#stateBorders": {
|
||||||
|
"opacity": 0.6,
|
||||||
|
"stroke": "#4c483e",
|
||||||
|
"stroke-width": 0.8,
|
||||||
|
"stroke-dasharray": "1 2.5",
|
||||||
|
"stroke-linecap": "square",
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#provinceBorders": {
|
||||||
|
"opacity": 0.6,
|
||||||
|
"stroke": "#56566d",
|
||||||
|
"stroke-width": 0.2,
|
||||||
|
"stroke-dasharray": 0.5,
|
||||||
|
"stroke-linecap": "butt",
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#cells": {
|
||||||
|
"opacity": null,
|
||||||
|
"stroke": "#808080",
|
||||||
|
"stroke-width": 0.1,
|
||||||
|
"filter": null,
|
||||||
|
"mask": null
|
||||||
|
},
|
||||||
|
"#gridOverlay": {
|
||||||
|
"opacity": 0.5,
|
||||||
|
"scale": 1,
|
||||||
|
"dx": 0,
|
||||||
|
"dy": 0,
|
||||||
|
"type": "pointyHex",
|
||||||
|
"stroke": "#808080",
|
||||||
|
"stroke-width": 1,
|
||||||
|
"stroke-dasharray": null,
|
||||||
|
"stroke-linecap": null,
|
||||||
|
"transform": null,
|
||||||
|
"filter": null,
|
||||||
|
"mask": null
|
||||||
|
},
|
||||||
|
"#coordinates": {
|
||||||
|
"opacity": 0.7,
|
||||||
|
"data-size": 15,
|
||||||
|
"font-size": 15,
|
||||||
|
"stroke": "#734d37",
|
||||||
|
"stroke-width": 1.5,
|
||||||
|
"stroke-dasharray": 5,
|
||||||
|
"stroke-linecap": "square",
|
||||||
|
"filter": null,
|
||||||
|
"mask": ""
|
||||||
|
},
|
||||||
|
"#compass": {
|
||||||
|
"opacity": 0.6,
|
||||||
|
"transform": null,
|
||||||
|
"filter": null,
|
||||||
|
"mask": "url(#water)",
|
||||||
|
"shape-rendering": "optimizespeed"
|
||||||
|
},
|
||||||
|
"#rose": {
|
||||||
|
"transform": null
|
||||||
|
},
|
||||||
|
"#relig": {
|
||||||
|
"opacity": 0.5,
|
||||||
|
"stroke": null,
|
||||||
|
"stroke-width": 0,
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#cults": {
|
||||||
|
"opacity": 0.5,
|
||||||
|
"stroke": "#777777",
|
||||||
|
"stroke-width": 0,
|
||||||
|
"stroke-dasharray": null,
|
||||||
|
"stroke-linecap": null,
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#landmass": {
|
||||||
|
"opacity": 1,
|
||||||
|
"fill": "#f4f2f0",
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#markers": {
|
||||||
|
"opacity": null,
|
||||||
|
"rescale": 1,
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#prec": {
|
||||||
|
"opacity": null,
|
||||||
|
"stroke": "#000000",
|
||||||
|
"stroke-width": 0.1,
|
||||||
|
"fill": "#2554ef",
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#population": {
|
||||||
|
"opacity": null,
|
||||||
|
"stroke-width": 1.6,
|
||||||
|
"stroke-dasharray": null,
|
||||||
|
"stroke-linecap": "butt",
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#rural": {
|
||||||
|
"stroke": "#0000ff"
|
||||||
|
},
|
||||||
|
"#urban": {
|
||||||
|
"stroke": "#ff0000"
|
||||||
|
},
|
||||||
|
"#freshwater": {
|
||||||
|
"opacity": 0.8,
|
||||||
|
"fill": "#98b6cd",
|
||||||
|
"stroke": "#718798",
|
||||||
|
"stroke-width": 0.5,
|
||||||
|
"filter": "url(#dropShadow05)"
|
||||||
|
},
|
||||||
|
"#salt": {
|
||||||
|
"opacity": 0.5,
|
||||||
|
"fill": "#409b8a",
|
||||||
|
"stroke": "#388985",
|
||||||
|
"stroke-width": 0.7,
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#sinkhole": {
|
||||||
|
"opacity": 1,
|
||||||
|
"fill": "#5bc9fd",
|
||||||
|
"stroke": "#53a3b0",
|
||||||
|
"stroke-width": 0.7,
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#frozen": {
|
||||||
|
"opacity": 0.95,
|
||||||
|
"fill": "#cdd4e7",
|
||||||
|
"stroke": "#cfe0eb",
|
||||||
|
"stroke-width": 0,
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#lava": {
|
||||||
|
"opacity": 0.7,
|
||||||
|
"fill": "#90270d",
|
||||||
|
"stroke": "#f93e0c",
|
||||||
|
"stroke-width": 2,
|
||||||
|
"filter": "url(#crumpled)"
|
||||||
|
},
|
||||||
|
"#dry": {
|
||||||
|
"opacity": 1,
|
||||||
|
"fill": "#c9bfa7",
|
||||||
|
"stroke": "#8e816f",
|
||||||
|
"stroke-width": 0.7,
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#sea_island": {
|
||||||
|
"opacity": 1,
|
||||||
|
"stroke": "#242424",
|
||||||
|
"stroke-width": 0.1,
|
||||||
|
"filter": "url(#dropShadow)",
|
||||||
|
"auto-filter": 1
|
||||||
|
},
|
||||||
|
"#lake_island": {
|
||||||
|
"opacity": 1,
|
||||||
|
"stroke": "#7c8eaf",
|
||||||
|
"stroke-width": 0.1,
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#terrain": {
|
||||||
|
"opacity": 0.8,
|
||||||
|
"set": "simple",
|
||||||
|
"size": 0.7,
|
||||||
|
"density": 0.3,
|
||||||
|
"filter": null,
|
||||||
|
"mask": ""
|
||||||
|
},
|
||||||
|
"#rivers": {
|
||||||
|
"opacity": 1,
|
||||||
|
"filter": null,
|
||||||
|
"fill": "#6dabba"
|
||||||
|
},
|
||||||
|
"#ruler": {
|
||||||
|
"opacity": null,
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#roads": {
|
||||||
|
"opacity": 0.9,
|
||||||
|
"stroke": "#d06324",
|
||||||
|
"stroke-width": 0.6,
|
||||||
|
"stroke-dasharray": "1 2",
|
||||||
|
"stroke-linecap": "round",
|
||||||
|
"filter": null,
|
||||||
|
"mask": null
|
||||||
|
},
|
||||||
|
"#trails": {
|
||||||
|
"opacity": 0.9,
|
||||||
|
"stroke": "#d06324",
|
||||||
|
"stroke-width": 0.5,
|
||||||
|
"stroke-dasharray": ".5 2",
|
||||||
|
"stroke-linecap": "round",
|
||||||
|
"filter": null,
|
||||||
|
"mask": null
|
||||||
|
},
|
||||||
|
"#searoutes": {
|
||||||
|
"opacity": 1,
|
||||||
|
"stroke": "#e5edff",
|
||||||
|
"stroke-width": 0.5,
|
||||||
|
"stroke-dasharray": "2 3",
|
||||||
|
"stroke-linecap": "round",
|
||||||
|
"filter": null,
|
||||||
|
"mask": null
|
||||||
|
},
|
||||||
|
"#statesBody": {
|
||||||
|
"opacity": 0.15,
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#statesHalo": {
|
||||||
|
"opacity": 0.3,
|
||||||
|
"data-width": 10,
|
||||||
|
"stroke-width": 10,
|
||||||
|
"filter": "blur(3.5px)"
|
||||||
|
},
|
||||||
|
"#provs": {
|
||||||
|
"opacity": 0.4,
|
||||||
|
"fill": "#000000",
|
||||||
|
"font-size": 8,
|
||||||
|
"font-family": "Arima Madurai",
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#temperature": {
|
||||||
|
"opacity": null,
|
||||||
|
"font-size": "8px",
|
||||||
|
"fill": "#000000",
|
||||||
|
"fill-opacity": 0.3,
|
||||||
|
"stroke": null,
|
||||||
|
"stroke-width": 1.8,
|
||||||
|
"stroke-dasharray": null,
|
||||||
|
"stroke-linecap": null,
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#ice": {
|
||||||
|
"opacity": 0.9,
|
||||||
|
"fill": "#e8f0f6",
|
||||||
|
"stroke": "#e8f0f6",
|
||||||
|
"stroke-width": 0.1,
|
||||||
|
"filter": "url(#dropShadow05)"
|
||||||
|
},
|
||||||
|
"#emblems": {
|
||||||
|
"opacity": 0.9,
|
||||||
|
"stroke-width": 1,
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#texture": {
|
||||||
|
"opacity": 0.39,
|
||||||
|
"filter": null,
|
||||||
|
"mask": "url(#land)"
|
||||||
|
},
|
||||||
|
"#textureImage": {
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
},
|
||||||
|
"#zones": {
|
||||||
|
"opacity": 0.6,
|
||||||
|
"stroke": "#333333",
|
||||||
|
"stroke-width": 0,
|
||||||
|
"stroke-dasharray": null,
|
||||||
|
"stroke-linecap": "butt",
|
||||||
|
"filter": null,
|
||||||
|
"mask": null
|
||||||
|
},
|
||||||
|
"#oceanLayers": {
|
||||||
|
"filter": "url(#dropShadow05)",
|
||||||
|
"layers": "-6,-3,-1"
|
||||||
|
},
|
||||||
|
"#oceanBase": {
|
||||||
|
"fill": "#7ca4b6"
|
||||||
|
},
|
||||||
|
"#oceanicPattern": {
|
||||||
|
"href": "./images/kiwiroo.png",
|
||||||
|
"opacity": 0.3
|
||||||
|
},
|
||||||
|
"#terrs": {
|
||||||
|
"opacity": 0.7,
|
||||||
|
"scheme": "bright",
|
||||||
|
"terracing": 0,
|
||||||
|
"skip": 2,
|
||||||
|
"relax": 1,
|
||||||
|
"curve": 0,
|
||||||
|
"filter": "",
|
||||||
|
"mask": "url(#land)"
|
||||||
|
},
|
||||||
|
"#legend": {
|
||||||
|
"data-size": 13,
|
||||||
|
"font-size": 13,
|
||||||
|
"font-family": "Arima Madurai",
|
||||||
|
"stroke": "#812929",
|
||||||
|
"stroke-width": 2.5,
|
||||||
|
"stroke-dasharray": "0 4 10 4",
|
||||||
|
"stroke-linecap": "round",
|
||||||
|
"data-x": 54.73,
|
||||||
|
"data-y": 62.98,
|
||||||
|
"data-columns": 8
|
||||||
|
},
|
||||||
|
"#burgLabels > #cities": {
|
||||||
|
"opacity": 0.8,
|
||||||
|
"fill": "#3a3a3a",
|
||||||
|
"text-shadow": "white 0px 0px 4px",
|
||||||
|
"data-size": 7,
|
||||||
|
"font-size": 7,
|
||||||
|
"font-family": "Arima Madurai"
|
||||||
|
},
|
||||||
|
"#burgIcons > #cities": {
|
||||||
|
"opacity": 1,
|
||||||
|
"fill": "#ffffff",
|
||||||
|
"fill-opacity": 0.7,
|
||||||
|
"size": 1.5,
|
||||||
|
"stroke": "#4f4f4f",
|
||||||
|
"stroke-width": 0.2,
|
||||||
|
"stroke-dasharray": "",
|
||||||
|
"stroke-linecap": "butt"
|
||||||
|
},
|
||||||
|
"#anchors > #cities": {
|
||||||
|
"opacity": 1,
|
||||||
|
"fill": "#ffffff",
|
||||||
|
"size": 3,
|
||||||
|
"stroke": "#3e3e4b",
|
||||||
|
"stroke-width": 1.2
|
||||||
|
},
|
||||||
|
"#burgLabels > #towns": {
|
||||||
|
"opacity": 0.8,
|
||||||
|
"fill": "#3e3e4b",
|
||||||
|
"text-shadow": "white 0px 0px 4px",
|
||||||
|
"data-size": 4,
|
||||||
|
"font-size": 4,
|
||||||
|
"font-family": "Arima Madurai"
|
||||||
|
},
|
||||||
|
"#burgIcons > #towns": {
|
||||||
|
"opacity": 1,
|
||||||
|
"fill": "#ffffff",
|
||||||
|
"fill-opacity": 0.7,
|
||||||
|
"size": 0.6,
|
||||||
|
"stroke": "#4f4f4f",
|
||||||
|
"stroke-width": 0.12,
|
||||||
|
"stroke-dasharray": "",
|
||||||
|
"stroke-linecap": "butt"
|
||||||
|
},
|
||||||
|
"#anchors > #towns": {
|
||||||
|
"opacity": 1,
|
||||||
|
"fill": "#ffffff",
|
||||||
|
"size": 1.2,
|
||||||
|
"stroke": "#3e3e4b",
|
||||||
|
"stroke-width": 1
|
||||||
|
},
|
||||||
|
"#labels > #states": {
|
||||||
|
"opacity": 0.8,
|
||||||
|
"fill": "#3e3e3e",
|
||||||
|
"stroke": "#000000",
|
||||||
|
"stroke-width": 0,
|
||||||
|
"text-shadow": "white 0px 0px 6px",
|
||||||
|
"data-size": 14,
|
||||||
|
"font-size": 14,
|
||||||
|
"font-family": "Arima Madurai",
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#labels > #addedLabels": {
|
||||||
|
"opacity": 1,
|
||||||
|
"fill": "#f24706",
|
||||||
|
"stroke": "#701b05",
|
||||||
|
"stroke-width": 0.1,
|
||||||
|
"text-shadow": "white 0px 0px 4px",
|
||||||
|
"data-size": 6,
|
||||||
|
"font-size": 6,
|
||||||
|
"font-family": "Arima Madurai",
|
||||||
|
"filter": null
|
||||||
|
},
|
||||||
|
"#fogging": {
|
||||||
|
"opacity": 1,
|
||||||
|
"fill": "#30426f",
|
||||||
|
"filter": null
|
||||||
|
}
|
||||||
|
}
|
||||||
8
sw.js
8
sw.js
|
|
@ -8,7 +8,10 @@ const {ExpirationPlugin} = workbox.expiration;
|
||||||
const DAY = 24 * 60 * 60;
|
const DAY = 24 * 60 * 60;
|
||||||
|
|
||||||
const getPolitics = ({entries, days}) => {
|
const getPolitics = ({entries, days}) => {
|
||||||
return [new CacheableResponsePlugin({statuses: [0, 200]}), new ExpirationPlugin({maxEntries: entries, maxAgeSeconds: days * DAY})];
|
return [
|
||||||
|
new CacheableResponsePlugin({statuses: [0, 200]}),
|
||||||
|
new ExpirationPlugin({maxEntries: entries, maxAgeSeconds: days * DAY})
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
registerRoute(
|
registerRoute(
|
||||||
|
|
@ -21,7 +24,8 @@ registerRoute(
|
||||||
);
|
);
|
||||||
|
|
||||||
registerRoute(
|
registerRoute(
|
||||||
({request, url}) => request.destination === "script" && !url.pathname.endsWith("min.js") && !url.pathname.includes("versioning.js"),
|
({request, url}) =>
|
||||||
|
request.destination === "script" && !url.pathname.endsWith("min.js") && !url.pathname.includes("versioning.js"),
|
||||||
new CacheFirst({
|
new CacheFirst({
|
||||||
cacheName: "fmg-scripts",
|
cacheName: "fmg-scripts",
|
||||||
plugins: getPolitics({entries: 100, days: 30})
|
plugins: getPolitics({entries: 100, days: 30})
|
||||||
|
|
|
||||||
|
|
@ -20,3 +20,7 @@ function lim(v) {
|
||||||
function normalize(val, min, max) {
|
function normalize(val, min, max) {
|
||||||
return minmax((val - min) / (max - min), 0, 1);
|
return minmax((val - min) / (max - min), 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function lerp(a, b, t) {
|
||||||
|
return a + (b - a) * t;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
// version and caching control
|
// version and caching control
|
||||||
const version = "1.89.00"; // generator version, update each time
|
const version = "1.89.15"; // generator version, update each time
|
||||||
|
|
||||||
{
|
{
|
||||||
document.title += " v" + version;
|
document.title += " v" + version;
|
||||||
|
|
@ -28,6 +28,7 @@ const version = "1.89.00"; // generator version, update each time
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<strong>Latest changes:</strong>
|
<strong>Latest changes:</strong>
|
||||||
|
<li>Religions can be edited and redrawn like cultures</li>
|
||||||
<li>Lock states, provinces, cultures, and religions from regeneration</li>
|
<li>Lock states, provinces, cultures, and religions from regeneration</li>
|
||||||
<li>Heightmap brushes: linear edit option</li>
|
<li>Heightmap brushes: linear edit option</li>
|
||||||
<li>Data Charts screen</li>
|
<li>Data Charts screen</li>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue