Merge branch 'master' of github.com:goteguru/Fantasy-Map-Generator

This commit is contained in:
Mészáros Gergely 2021-08-12 00:49:40 +02:00
commit 07ff6d030f
28 changed files with 2705 additions and 1754 deletions

View file

@ -997,12 +997,6 @@ body button.noicon {
stroke-width: 0.4; stroke-width: 0.4;
} }
#controlCells > .occupied {
fill: #ff828240;
stroke: #ff8282;
stroke-width: 0.4;
}
#vertices > circle { #vertices > circle {
fill: #ff0000; fill: #ff0000;
stroke: #841f1f; stroke: #841f1f;
@ -1285,17 +1279,26 @@ div.slider .ui-slider-handle {
display: none !important; display: none !important;
} }
.burgs-table {
max-height: 75vh;
overflow-x: hidden;
overflow-y: scroll;
}
.table { .table {
max-height: 75vh; max-height: 75vh;
max-width: 75vw; max-width: 75vw;
overflow-x: hidden; overflow-x: hidden;
overflow-y: auto; overflow-y: auto;
scrollbar-width: thin;
}
.table::-webkit-scrollbar {
width: 6px;
background-color: transparent;
}
.table::-webkit-scrollbar-thumb {
background-color: #aaa;
border-radius: 6px;
}
.table::-webkit-scrollbar-thumb:hover {
background: #666;
} }
.overflow { .overflow {

View file

@ -910,6 +910,13 @@
<label for="hideLabels" class="checkbox-label">Toggle visibility automatically</label> <label for="hideLabels" class="checkbox-label">Toggle visibility automatically</label>
</td> </td>
</tr> </tr>
<tr data-tip="Allow system to rescale labels on zoom">
<td colspan=2>
<input id="rescaleLabels" class="checkbox" type="checkbox" onchange="invokeActiveZooming()" checked>
<label for="rescaleLabels" class="checkbox-label">Rescale on zoom</label>
</td>
</tr>
</tbody> </tbody>
</table> </table>
@ -1000,17 +1007,18 @@
<td>Map template</td> <td>Map template</td>
<td> <td>
<select id="templateInput" data-stored="template"> <select id="templateInput" data-stored="template">
<option value="Volcano">Volcano</option> <option value="volcano">Volcano</option>
<option value="High Island">High Island</option> <option value="highIsland">High Island</option>
<option value="Low Island">Low Island</option> <option value="lowIsland">Low Island</option>
<option value="Continents">Two Continents</option> <option value="continents">Two Continents</option>
<option value="Archipelago">Archipelago</option> <option value="archipelago">Archipelago</option>
<option value="Atoll">Atoll</option> <option value="atoll">Atoll</option>
<option value="Mediterranean">Mediterranean</option> <option value="mediterranean">Mediterranean</option>
<option value="Peninsula">Peninsula</option> <option value="peninsula">Peninsula</option>
<option value="Pangea">Pangea</option> <option value="pangea">Pangea</option>
<option value="Isthmus">Isthmus</option> <option value="isthmus">Isthmus</option>
<option value="Shattered">Shattered</option> <option value="shattered">Shattered</option>
<option value="taklamakan">Taklamakan</option>
</select> </select>
</td> </td>
<td></td> <td></td>
@ -1665,7 +1673,7 @@
</div> </div>
<div id="riverCreator" class="dialog" style="display: none"> <div id="riverCreator" class="dialog" style="display: none">
<div id="riverCreatorBody"></div> <div id="riverCreatorBody" class="table"></div>
<div id="riverCreatorBottom"> <div id="riverCreatorBottom">
<button id="riverCreatorComplete" data-tip="Complete river creation" class="icon-check"></button> <button id="riverCreatorComplete" data-tip="Complete river creation" class="icon-check"></button>
<button id="riverCreatorCancel" data-tip="Cancel the creation" class="icon-cancel"></button> <button id="riverCreatorCancel" data-tip="Cancel the creation" class="icon-cancel"></button>
@ -2332,18 +2340,19 @@
<div id="templateEditor" class="dialog stable" style="display: none"> <div id="templateEditor" class="dialog stable" style="display: none">
<div id="templateTop"> <div id="templateTop">
<i>Select template: </i><select id="templateSelect" style="width:16em" data-prev="templateCustom" data-tip="Select base template"> <i>Select template: </i><select id="templateSelect" style="width:16em" data-prev="templateCustom" data-tip="Select base template">
<option value="templateCustom" selected>Custom</option> <option value="custom" selected>Custom</option>
<option value="templateVolcano">Volcano</option> <option value="volcano">Volcano</option>
<option value="templateHighIsland">High Island</option> <option value="highIsland">High Island</option>
<option value="templateLowIsland">Low Island</option> <option value="lowIsland">Low Island</option>
<option value="templateContinents">Two Continents</option> <option value="continents">Two Continents</option>
<option value="templateArchipelago">Archipelago</option> <option value="archipelago">Archipelago</option>
<option value="templateAtoll">Atoll</option> <option value="atoll">Atoll</option>
<option value="templateMediterranean">Mediterranean</option> <option value="mediterranean">Mediterranean</option>
<option value="templatePeninsula">Peninsula</option> <option value="peninsula">Peninsula</option>
<option value="templatePangea">Pangea</option> <option value="pangea">Pangea</option>
<option value="templateIsthmus">Isthmus</option> <option value="isthmus">Isthmus</option>
<option value="templateShattered">Shattered</option> <option value="shattered">Shattered</option>
<option value="taklamakan">Taklamakan</option>
</select> </select>
</div> </div>
<div id="templateTools"> <div id="templateTools">
@ -3186,7 +3195,7 @@
<div style="left:31.2em" data-tip="Click to sort by burg type" class="sortable alphabetically" data-sortby="type">Type&nbsp;</div> <div style="left:31.2em" data-tip="Click to sort by burg type" class="sortable alphabetically" data-sortby="type">Type&nbsp;</div>
</div> </div>
<div id="burgsBody" class="burgs-table"></div> <div id="burgsBody" class="table"></div>
<div id="burgsFilters" data-tip="Apply a filter"> <div id="burgsFilters" data-tip="Apply a filter">
<span>State: </span> <span>State: </span>
@ -3221,7 +3230,7 @@
<div style="left:30em" data-tip="Click to sort by river basin" class="sortable alphabetically" data-sortby="basin">Basin&nbsp;</div> <div style="left:30em" data-tip="Click to sort by river basin" class="sortable alphabetically" data-sortby="basin">Basin&nbsp;</div>
</div> </div>
<div id="riversBody" class="burgs-table"></div> <div id="riversBody" class="table"></div>
<div id="riversFooter" class="totalLine"> <div id="riversFooter" class="totalLine">
<div data-tip="Rivers number" style="margin-left: 4px">Rivers:&nbsp;<span id="riversFooterNumber">0</span></div> <div data-tip="Rivers number" style="margin-left: 4px">Rivers:&nbsp;<span id="riversFooterNumber">0</span></div>
@ -4187,6 +4196,7 @@
<script src="libs/delaunator.min.js"></script> <script src="libs/delaunator.min.js"></script>
<script src="modules/utils.js"></script> <script src="modules/utils.js"></script>
<script src="modules/voronoi.js"></script> <script src="modules/voronoi.js"></script>
<script src="modules/heightmap-templates.js"></script>
<script src="modules/heightmap-generator.js"></script> <script src="modules/heightmap-generator.js"></script>
<script src="modules/ocean-layers.js"></script> <script src="modules/ocean-layers.js"></script>
<script src="modules/river-generator.js"></script> <script src="modules/river-generator.js"></script>

View file

@ -1,163 +1,157 @@
(function (global, factory) { "use strict";
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.Pell = factory());
}(this, (function () { 'use strict';
const defaultParagraphSeparatorString = 'defaultParagraphSeparator' window.Pell = (function () {
const formatBlock = 'formatBlock' const defaultParagraphSeparatorString = "defaultParagraphSeparator";
const addEventListener = (parent, type, listener) => parent.addEventListener(type, listener) const formatBlock = "formatBlock";
const appendChild = (parent, child) => parent.appendChild(child) const addEventListener = (parent, type, listener) => parent.addEventListener(type, listener);
const createElement = tag => document.createElement(tag) const appendChild = (parent, child) => parent.appendChild(child);
const queryCommandState = command => document.queryCommandState(command) const createElement = tag => document.createElement(tag);
const queryCommandValue = command => document.queryCommandValue(command) const queryCommandState = command => document.queryCommandState(command);
const exec = (command, value = null) => document.execCommand(command, false, value) const queryCommandValue = command => document.queryCommandValue(command);
const exec = (command, value = null) => document.execCommand(command, false, value);
const defaultActions = { const defaultActions = {
bold: { bold: {
icon: '<b>B</b>', icon: "<b>B</b>",
title: 'Bold', title: "Bold",
state: () => queryCommandState('bold'), state: () => queryCommandState("bold"),
result: () => exec('bold') result: () => exec("bold")
}, },
italic: { italic: {
icon: '<i>I</i>', icon: "<i>I</i>",
title: 'Italic', title: "Italic",
state: () => queryCommandState('italic'), state: () => queryCommandState("italic"),
result: () => exec('italic') result: () => exec("italic")
}, },
underline: { underline: {
icon: '<u>U</u>', icon: "<u>U</u>",
title: 'Underline', title: "Underline",
state: () => queryCommandState('underline'), state: () => queryCommandState("underline"),
result: () => exec('underline') result: () => exec("underline")
}, },
strikethrough: { strikethrough: {
icon: '<strike>S</strike>', icon: "<strike>S</strike>",
title: 'Strike-through', title: "Strike-through",
state: () => queryCommandState('strikeThrough'), state: () => queryCommandState("strikeThrough"),
result: () => exec('strikeThrough') result: () => exec("strikeThrough")
}, },
heading1: { heading1: {
icon: '<b>H<sub>1</sub></b>', icon: "<b>H<sub>1</sub></b>",
title: 'Heading 1', title: "Heading 1",
result: () => exec(formatBlock, '<h1>') result: () => exec(formatBlock, "<h1>")
}, },
heading2: { heading2: {
icon: '<b>H<sub>2</sub></b>', icon: "<b>H<sub>2</sub></b>",
title: 'Heading 2', title: "Heading 2",
result: () => exec(formatBlock, '<h2>') result: () => exec(formatBlock, "<h2>")
}, },
paragraph: { paragraph: {
icon: '&#182;', icon: "&#182;",
title: 'Paragraph', title: "Paragraph",
result: () => exec(formatBlock, '<p>') result: () => exec(formatBlock, "<p>")
}, },
quote: { quote: {
icon: '&#8220; &#8221;', icon: "&#8220; &#8221;",
title: 'Quote', title: "Quote",
result: () => exec(formatBlock, '<blockquote>') result: () => exec(formatBlock, "<blockquote>")
}, },
olist: { olist: {
icon: '&#35;', icon: "&#35;",
title: 'Ordered List', title: "Ordered List",
result: () => exec('insertOrderedList') result: () => exec("insertOrderedList")
}, },
ulist: { ulist: {
icon: '&#8226;', icon: "&#8226;",
title: 'Unordered List', title: "Unordered List",
result: () => exec('insertUnorderedList') result: () => exec("insertUnorderedList")
}, },
code: { code: {
icon: '&lt;/&gt;', icon: "&lt;/&gt;",
title: 'Code', title: "Code",
result: () => exec(formatBlock, '<pre>') result: () => exec(formatBlock, "<pre>")
}, },
line: { line: {
icon: '&#8213;', icon: "&#8213;",
title: 'Horizontal Line', title: "Horizontal Line",
result: () => exec('insertHorizontalRule') result: () => exec("insertHorizontalRule")
}, },
link: { link: {
icon: '&#128279;', icon: "&#128279;",
title: 'Link', title: "Link",
result: () => navigator.clipboard.readText().then(url => exec('createLink', url)) result: () => navigator.clipboard.readText().then(url => exec("createLink", url))
}, },
image: { image: {
icon: '&#128247;', icon: "&#128247;",
title: 'Image', title: "Image",
result: () => { result: () => {
navigator.clipboard.readText().then(url => exec('insertImage', url)) navigator.clipboard.readText().then(url => exec("insertImage", url));
exec('enableObjectResizing') exec("enableObjectResizing");
} }
} }
} };
const defaultClasses = { const defaultClasses = {
actionbar: 'pell-actionbar', actionbar: "pell-actionbar",
button: 'pell-button', button: "pell-button",
content: 'pell-content', content: "pell-content",
selected: 'pell-button-selected' selected: "pell-button-selected"
} };
const init = settings => { const init = settings => {
const actions = settings.actions const actions = settings.actions
? ( ? settings.actions.map(action => {
settings.actions.map(action => { if (typeof action === "string") return defaultActions[action];
if (typeof action === 'string') return defaultActions[action] else if (defaultActions[action.name]) return {...defaultActions[action.name], ...action};
else if (defaultActions[action.name]) return { ...defaultActions[action.name], ...action } return action;
return action
}) })
) : Object.keys(defaultActions).map(action => defaultActions[action]);
: Object.keys(defaultActions).map(action => defaultActions[action])
const classes = { ...defaultClasses, ...settings.classes }
const defaultParagraphSeparator = settings[defaultParagraphSeparatorString] || 'div'
const actionbar = createElement('div')
actionbar.className = classes.actionbar
appendChild(settings.element, actionbar)
const content = settings.element.content = createElement('div')
content.contentEditable = true
content.className = classes.content
content.oninput = ({ target: { firstChild } }) => {
if (firstChild && firstChild.nodeType === 3) exec(formatBlock, `<${defaultParagraphSeparator}>`)
else if (content.innerHTML === '<br>') content.innerHTML = ''
settings.onChange(content.innerHTML)
}
content.onkeydown = event => {
if (event.key === 'Enter' && queryCommandValue(formatBlock) === 'blockquote') {
setTimeout(() => exec(formatBlock, `<${defaultParagraphSeparator}>`), 0)
}
}
appendChild(settings.element, content)
actions.forEach(action => {
const button = createElement('button')
button.className = classes.button
button.innerHTML = action.icon
button.title = action.title
button.setAttribute('type', 'button')
button.onclick = () => action.result() && content.focus()
if (action.state) {
const handler = () => button.classList[action.state() ? 'add' : 'remove'](classes.selected)
addEventListener(content, 'keyup', handler)
addEventListener(content, 'mouseup', handler)
addEventListener(button, 'click', handler)
}
appendChild(actionbar, button)
})
if (settings.styleWithCSS) exec('styleWithCSS')
exec(defaultParagraphSeparatorString, defaultParagraphSeparator)
return settings.element
}
return {exec, init}
}))); const classes = {...defaultClasses, ...settings.classes};
const defaultParagraphSeparator = settings[defaultParagraphSeparatorString] || "div";
const actionbar = createElement("div");
actionbar.className = classes.actionbar;
appendChild(settings.element, actionbar);
const content = (settings.element.content = createElement("div"));
content.contentEditable = true;
content.className = classes.content;
content.oninput = ({target: {firstChild}}) => {
if (firstChild && firstChild.nodeType === 3) exec(formatBlock, `<${defaultParagraphSeparator}>`);
else if (content.innerHTML === "<br>") content.innerHTML = "";
settings.onChange(content.innerHTML);
};
content.onkeydown = event => {
if (event.key === "Enter" && queryCommandValue(formatBlock) === "blockquote") {
setTimeout(() => exec(formatBlock, `<${defaultParagraphSeparator}>`), 0);
}
};
appendChild(settings.element, content);
actions.forEach(action => {
const button = createElement("button");
button.className = classes.button;
button.innerHTML = action.icon;
button.title = action.title;
button.setAttribute("type", "button");
button.onclick = () => action.result() && content.focus();
if (action.state) {
const handler = () => button.classList[action.state() ? "add" : "remove"](classes.selected);
addEventListener(content, "keyup", handler);
addEventListener(content, "mouseup", handler);
addEventListener(button, "click", handler);
}
appendChild(actionbar, button);
});
if (settings.styleWithCSS) exec("styleWithCSS");
exec(defaultParagraphSeparatorString, defaultParagraphSeparator);
return settings.element;
};
return {exec, init};
})();

View file

@ -2,7 +2,7 @@
// https://github.com/Azgaar/Fantasy-Map-Generator // https://github.com/Azgaar/Fantasy-Map-Generator
"use strict"; "use strict";
const version = "1.651"; // generator version1 const version = "1.652"; // generator version1
document.title += " v" + version; document.title += " v" + version;
// Switches to disable/enable logging features // Switches to disable/enable logging features
@ -479,7 +479,8 @@ function invokeActiveZooming() {
if (this.id === "burgLabels") return; if (this.id === "burgLabels") return;
const desired = +this.dataset.size; const desired = +this.dataset.size;
const relative = Math.max(rn((desired + desired / scale) / 2, 2), 1); const relative = Math.max(rn((desired + desired / scale) / 2, 2), 1);
this.setAttribute("font-size", relative); if (rescaleLabels.checked) this.setAttribute("font-size", relative);
const hidden = hideLabels.checked && (relative * scale < 6 || relative * scale > 60); const hidden = hideLabels.checked && (relative * scale < 6 || relative * scale > 60);
if (hidden) this.classList.add("hidden"); if (hidden) this.classList.add("hidden");
else this.classList.remove("hidden"); else this.classList.remove("hidden");

View file

@ -1,12 +1,9 @@
(function (global, factory) { "use strict";
typeof exports === "object" && typeof module !== "undefined" ? (module.exports = factory()) : typeof define === "function" && define.amd ? define(factory) : (global.BurgsAndStates = factory());
})(this, function () {
"use strict";
window.BurgsAndStates = (function () {
const generate = function () { const generate = function () {
const cells = pack.cells, const {cells, cultures} = pack;
cultures = pack.cultures, const n = cells.i.length;
n = cells.i.length;
cells.burg = new Uint16Array(n); // cell burg cells.burg = new Uint16Array(n); // cell burg
cells.road = new Uint16Array(n); // cell road power cells.road = new Uint16Array(n); // cell road power
@ -37,7 +34,8 @@
let count = +regionsInput.value; let count = +regionsInput.value;
let burgs = [0]; let burgs = [0];
const score = new Int16Array(cells.s.map(s => s * Math.random())); // cell score for capitals placement const rand = () => 0.5 + Math.random() * 0.5;
const score = new Int16Array(cells.s.map(s => s * rand())); // cell score for capitals placement
const sorted = cells.i.filter(i => score[i] > 0 && cells.culture[i]).sort((a, b) => score[b] - score[a]); // filtered and sorted array of indexes const sorted = cells.i.filter(i => score[i] > 0 && cells.culture[i]).sort((a, b) => score[b] - score[a]); // filtered and sorted array of indexes
if (sorted.length < count * 10) { if (sorted.length < count * 10) {
@ -54,9 +52,8 @@
let spacing = (graphWidth + graphHeight) / 2 / count; // min distance between capitals let spacing = (graphWidth + graphHeight) / 2 / count; // min distance between capitals
for (let i = 0; burgs.length <= count; i++) { for (let i = 0; burgs.length <= count; i++) {
const cell = sorted[i], const cell = sorted[i];
x = cells.p[cell][0], const [x, y] = cells.p[cell];
y = cells.p[cell][1];
if (burgsTree.find(x, y, spacing) === undefined) { if (burgsTree.find(x, y, spacing) === undefined) {
burgs.push({cell, x, y}); burgs.push({cell, x, y});
@ -66,7 +63,9 @@
if (i === sorted.length - 1) { if (i === sorted.length - 1) {
WARN && console.warn("Cannot place capitals with current spacing. Trying again with reduced spacing"); WARN && console.warn("Cannot place capitals with current spacing. Trying again with reduced spacing");
burgsTree = d3.quadtree(); burgsTree = d3.quadtree();
(i = -1), (burgs = [0]), (spacing /= 1.2); i = -1;
burgs = [0];
spacing /= 1.2;
} }
} }
@ -1224,4 +1223,4 @@
}; };
return {generate, expandStates, normalizeStates, assignColors, drawBurgs, specifyBurgs, defineBurgFeatures, getType, drawStateLabels, collectStatistics, generateCampaigns, generateDiplomacy, defineStateForms, getFullName, generateProvinces, updateCultures}; return {generate, expandStates, normalizeStates, assignColors, drawBurgs, specifyBurgs, defineBurgFeatures, getType, drawStateLabels, collectStatistics, generateCampaigns, generateDiplomacy, defineStateForms, getFullName, generateProvinces, updateCultures};
}); })();

View file

@ -1,170 +1,364 @@
(function (global, factory) { "use strict";
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.COA = factory());
}(this, (function () {'use strict';
window.COA = (function () {
const tinctures = { const tinctures = {
field: { metals: 3, colours: 4, stains: +P(.03), patterns: 1 }, field: {metals: 3, colours: 4, stains: +P(0.03), patterns: 1},
division: { metals: 5, colours: 8, stains: +P(.03), patterns: 1 }, division: {metals: 5, colours: 8, stains: +P(0.03), patterns: 1},
charge: { metals: 2, colours: 3, stains: +P(.05), patterns: 0 }, charge: {metals: 2, colours: 3, stains: +P(0.05), patterns: 0},
metals: { argent: 3, or: 2 }, metals: {argent: 3, or: 2},
colours: { gules: 5, azure: 4, sable: 3, purpure: 3, vert: 2 }, colours: {gules: 5, azure: 4, sable: 3, purpure: 3, vert: 2},
stains: { murrey: 1, sanguine: 1, tenné: 1 }, stains: {murrey: 1, sanguine: 1, tenné: 1},
patterns: { patterns: {
semy: 8, ermine: 6, semy: 8,
vair: 4, counterVair: 1, vairInPale: 1, vairEnPointe: 2, vairAncien: 2, ermine: 6,
potent: 2, counterPotent: 1, potentInPale: 1, potentEnPointe: 1, vair: 4,
chequy: 8, lozengy: 5, fusily: 2, pally: 8, barry: 10, gemelles: 1, counterVair: 1,
bendy: 8, bendySinister: 4, palyBendy: 2, barryBendy: 1, vairInPale: 1,
pappellony: 2, pappellony2: 3, scaly: 1, plumetty: 1, vairEnPointe: 2,
masoned: 6, fretty: 3, grillage: 1, chainy: 1, maily: 2, honeycombed: 1 } vairAncien: 2,
} potent: 2,
counterPotent: 1,
potentInPale: 1,
potentEnPointe: 1,
chequy: 8,
lozengy: 5,
fusily: 2,
pally: 8,
barry: 10,
gemelles: 1,
bendy: 8,
bendySinister: 4,
palyBendy: 2,
barryBendy: 1,
pappellony: 2,
pappellony2: 3,
scaly: 1,
plumetty: 1,
masoned: 6,
fretty: 3,
grillage: 1,
chainy: 1,
maily: 2,
honeycombed: 1
}
};
const charges = { const charges = {
// categories selection // categories selection
types: { conventional: 30, crosses: 10, animals: 2, animalHeads: 1, birds: 2, fantastic: 3, plants: 1, agriculture: 1, arms: 3, bodyparts: 1, people: 1, architecture: 1, miscellaneous: 3, inescutcheon: 3 }, types: {conventional: 30, crosses: 10, animals: 2, animalHeads: 1, birds: 2, fantastic: 3, plants: 1, agriculture: 1, arms: 3, bodyparts: 1, people: 1, architecture: 1, miscellaneous: 3, inescutcheon: 3},
single: { conventional: 12, crosses: 8, plants: 2, animals: 10, animalHeads: 2, birds: 4, fantastic: 7, agriculture: 1, arms: 6, bodyparts: 1, people: 2, architecture: 1, miscellaneous: 10, inescutcheon: 5 }, single: {conventional: 12, crosses: 8, plants: 2, animals: 10, animalHeads: 2, birds: 4, fantastic: 7, agriculture: 1, arms: 6, bodyparts: 1, people: 2, architecture: 1, miscellaneous: 10, inescutcheon: 5},
semy: { conventional: 12, crosses: 3, plants: 1 }, semy: {conventional: 12, crosses: 3, plants: 1},
// generic categories // generic categories
conventional: { conventional: {
lozenge: 2, fusil: 4, mascle: 4, rustre: 2, lozengeFaceted: 3, lozengePloye: 1, roundel: 4, roundel2: 3, annulet: 4, lozenge: 2,
mullet: 5, mulletPierced: 1, mulletFaceted: 1, mullet4: 3, mullet6: 4, mullet6Pierced: 1, mullet6Faceted: 1, mullet7: 1, mullet8: 1, mullet10: 1, fusil: 4,
estoile: 1, compassRose: 1, billet: 5, delf: 0, triangle: 3, trianglePierced: 1, goutte: 4, heart: 4, pique: 2, carreau: 1, trefle: 2, mascle: 4,
fleurDeLis: 6, sun: 3, sunInSplendour: 1, crescent: 5, fountain: 1 rustre: 2,
lozengeFaceted: 3,
lozengePloye: 1,
roundel: 4,
roundel2: 3,
annulet: 4,
mullet: 5,
mulletPierced: 1,
mulletFaceted: 1,
mullet4: 3,
mullet6: 4,
mullet6Pierced: 1,
mullet6Faceted: 1,
mullet7: 1,
mullet8: 1,
mullet10: 1,
estoile: 1,
compassRose: 1,
billet: 5,
delf: 0,
triangle: 3,
trianglePierced: 1,
goutte: 4,
heart: 4,
pique: 2,
carreau: 1,
trefle: 2,
fleurDeLis: 6,
sun: 3,
sunInSplendour: 1,
crescent: 5,
fountain: 1
}, },
crosses: { crosses: {
crossHummetty: 15, crossVoided: 1, crossPattee: 2, crossPatteeAlisee: 1, crossFormee: 1, crossFormee2: 2, crossPotent: 2, crossJerusalem:1, crossHummetty: 15,
crosslet: 1, crossClechy: 3, crossBottony: 1, crossFleury: 3, crossPatonce: 1, crossPommy: 1, crossGamma: 1, crossArrowed: 1, crossFitchy: 1, crossVoided: 1,
crossCercelee: 1, crossMoline: 2, crossFourchy: 1, crossAvellane: 1, crossErminee: 1, crossBiparted: 1, crossMaltese: 3, crossTemplar: 2, crossPattee: 2,
crossCeltic: 1, crossCeltic2: 1, crossTriquetra: 1, crossCarolingian: 1, crossOccitan: 1, crossSaltire: 3, crossBurgundy: 1, crossPatteeAlisee: 1,
crossLatin: 3, crossPatriarchal: 1, crossOrthodox: 1, crossCalvary: 1, crossDouble: 1, crossTau: 1, crossSantiago: 1, crossAnkh: 1 crossFormee: 1,
crossFormee2: 2,
crossPotent: 2,
crossJerusalem: 1,
crosslet: 1,
crossClechy: 3,
crossBottony: 1,
crossFleury: 3,
crossPatonce: 1,
crossPommy: 1,
crossGamma: 1,
crossArrowed: 1,
crossFitchy: 1,
crossCercelee: 1,
crossMoline: 2,
crossFourchy: 1,
crossAvellane: 1,
crossErminee: 1,
crossBiparted: 1,
crossMaltese: 3,
crossTemplar: 2,
crossCeltic: 1,
crossCeltic2: 1,
crossTriquetra: 1,
crossCarolingian: 1,
crossOccitan: 1,
crossSaltire: 3,
crossBurgundy: 1,
crossLatin: 3,
crossPatriarchal: 1,
crossOrthodox: 1,
crossCalvary: 1,
crossDouble: 1,
crossTau: 1,
crossSantiago: 1,
crossAnkh: 1
}, },
animals: { animals: {
lionRampant: 5, lionPassant: 2, lionPassantGuardant: 1, wolfRampant: 1, wolfPassant: 1, wolfStatant: 1, greyhoundCourant: 1, boarRampant: 1, lionRampant: 5,
horseRampant: 2, horseSalient: 1, bearRampant: 2, bearPassant: 1, bullPassant: 1, goat: 1, lamb: 1, elephant: 1, camel: 1 lionPassant: 2,
lionPassantGuardant: 1,
wolfRampant: 1,
wolfPassant: 1,
wolfStatant: 1,
greyhoundCourant: 1,
boarRampant: 1,
horseRampant: 2,
horseSalient: 1,
bearRampant: 2,
bearPassant: 1,
bullPassant: 1,
goat: 1,
lamb: 1,
elephant: 1,
camel: 1
}, },
animalHeads: { wolfHeadErased: 1, bullHeadCaboshed: 1, deerHeadCaboshed: 1, lionHeadCaboshed: 2 }, animalHeads: {wolfHeadErased: 1, bullHeadCaboshed: 1, deerHeadCaboshed: 1, lionHeadCaboshed: 2},
fantastic: { dragonPassant: 2, dragonRampant: 2, wyvern: 1, wyvernWithWingsDisplayed: 1, griffinPassant: 1, griffinRampant: 1, eagleTwoHeards: 2, unicornRampant: 1, pegasus: 1, serpent: 1 }, fantastic: {dragonPassant: 2, dragonRampant: 2, wyvern: 1, wyvernWithWingsDisplayed: 1, griffinPassant: 1, griffinRampant: 1, eagleTwoHeards: 2, unicornRampant: 1, pegasus: 1, serpent: 1},
birds: { eagle: 9, raven: 1, cock: 3, parrot: 1, swan: 2, swanErased: 1, heron: 1, owl: 1 }, birds: {eagle: 9, raven: 1, cock: 3, parrot: 1, swan: 2, swanErased: 1, heron: 1, owl: 1},
plants: { tree: 1, oak: 1, cinquefoil: 1, rose: 1 }, plants: {tree: 1, oak: 1, cinquefoil: 1, rose: 1},
agriculture: { garb: 1, rake: 1 }, agriculture: {garb: 1, rake: 1},
arms: { sword: 5, sabre: 1, sabresCrossed: 1, hatchet: 2, axe: 2, lochaberAxe: 1, mallet: 1, bowWithArrow: 2, bow: 1, arrow: 1, arrowsSheaf: 1, helmet: 2 }, arms: {sword: 5, sabre: 1, sabresCrossed: 1, hatchet: 2, axe: 2, lochaberAxe: 1, mallet: 1, bowWithArrow: 2, bow: 1, arrow: 1, arrowsSheaf: 1, helmet: 2},
bodyparts: { hand: 4, head: 1, headWreathed: 1 }, bodyparts: {hand: 4, head: 1, headWreathed: 1},
people: { cavalier: 3, monk: 1, angel: 2 }, people: {cavalier: 3, monk: 1, angel: 2},
architecture: { tower: 1, castle: 1 }, architecture: {tower: 1, castle: 1},
miscellaneous: { miscellaneous: {
crown: 3, orb: 1, chalice: 1, key: 1, buckle: 1, bugleHorn: 1, bugleHorn2: 1, bell: 2, pot: 1, bucket: 1, horseshoe: 3, crown: 3,
attire: 1, stagsAttires: 1, ramsHorn: 1, cowHorns: 2, wing: 1, wingSword: 1, lute: 1, harp: 1, wheel: 2, crosier: 1, fasces: 1, log: 1 orb: 1,
chalice: 1,
key: 1,
buckle: 1,
bugleHorn: 1,
bugleHorn2: 1,
bell: 2,
pot: 1,
bucket: 1,
horseshoe: 3,
attire: 1,
stagsAttires: 1,
ramsHorn: 1,
cowHorns: 2,
wing: 1,
wingSword: 1,
lute: 1,
harp: 1,
wheel: 2,
crosier: 1,
fasces: 1,
log: 1
}, },
// selection based on culture type: // selection based on culture type:
Naval: { anchor: 3, boat: 1, lymphad: 2, armillarySphere: 1, escallop: 1, dolphin: 1 }, Naval: {anchor: 3, boat: 1, lymphad: 2, armillarySphere: 1, escallop: 1, dolphin: 1},
Highland: { tower: 1, raven: 1, wolfHeadErased: 1, wolfPassant: 1, goat: 1, axe: 1 }, Highland: {tower: 1, raven: 1, wolfHeadErased: 1, wolfPassant: 1, goat: 1, axe: 1},
River: { tower: 1, garb: 1, rake: 1, boat: 1, pike: 2, bullHeadCaboshed: 1 }, River: {tower: 1, garb: 1, rake: 1, boat: 1, pike: 2, bullHeadCaboshed: 1},
Lake: { cancer: 2, escallop: 1, pike: 2, heron: 1, boat: 1, boat2: 2 }, Lake: {cancer: 2, escallop: 1, pike: 2, heron: 1, boat: 1, boat2: 2},
Nomadic: { pot: 1, buckle: 1, wheel: 2, sabre: 2, sabresCrossed: 1, bow: 2, arrow: 1, horseRampant: 1, horseSalient: 1, crescent: 1, camel: 3 }, Nomadic: {pot: 1, buckle: 1, wheel: 2, sabre: 2, sabresCrossed: 1, bow: 2, arrow: 1, horseRampant: 1, horseSalient: 1, crescent: 1, camel: 3},
Hunting: { bugleHorn: 2, bugleHorn2: 1, stagsAttires: 2, attire: 2, hatchet: 1, bowWithArrow: 1, arrowsSheaf: 1, deerHeadCaboshed: 1, wolfStatant: 1, oak: 1 }, Hunting: {bugleHorn: 2, bugleHorn2: 1, stagsAttires: 2, attire: 2, hatchet: 1, bowWithArrow: 1, arrowsSheaf: 1, deerHeadCaboshed: 1, wolfStatant: 1, oak: 1},
// selection based on type // selection based on type
City: { key: 3, bell: 2, lute: 1, tower: 1, castle: 1, mallet: 1 }, City: {key: 3, bell: 2, lute: 1, tower: 1, castle: 1, mallet: 1},
Capital: { crown: 4, orb: 1, lute: 1, castle: 3, tower: 1 }, Capital: {crown: 4, orb: 1, lute: 1, castle: 3, tower: 1},
Сathedra: { chalice: 1, orb: 1, crosier: 2, lamb: 1, monk: 2, angel: 3, crossLatin: 2, crossPatriarchal: 1, crossOrthodox: 1, crossCalvary: 1 }, Сathedra: {chalice: 1, orb: 1, crosier: 2, lamb: 1, monk: 2, angel: 3, crossLatin: 2, crossPatriarchal: 1, crossOrthodox: 1, crossCalvary: 1},
// specific cases // specific cases
natural: { fountain: "azure", garb: "or", raven: "sable" }, // charges to mainly use predefined colours natural: {fountain: "azure", garb: "or", raven: "sable"}, // charges to mainly use predefined colours
sinister: [ // charges that can be sinister sinister: [
"crossGamma", "lionRampant", "lionPassant", "wolfRampant", "wolfPassant", "wolfStatant", "wolfHeadErased", "greyhoundСourant", "boarRampant", // charges that can be sinister
"horseRampant", "horseSalient", "bullPassant", "bearRampant", "bearPassant", "goat", "lamb", "elephant", "eagle", "raven", "cock", "parrot", "crossGamma",
"swan", "swanErased", "heron", "pike", "dragonPassant", "dragonRampant", "wyvern", "wyvernWithWingsDisplayed", "griffinPassant", "griffinRampant", "lionRampant",
"unicornRampant", "pegasus", "serpent", "hatchet", "lochaberAxe", "hand", "wing", "wingSword", "lute", "harp", "bow", "head", "headWreathed", "lionPassant",
"knight", "lymphad", "log", "crosier", "dolphin", "sabre", "monk", "owl", "axe", "camel", "fasces", "lionPassantGuardant", "helmet"], "wolfRampant",
reversed: [ // charges that can be reversed "wolfPassant",
"goutte", "mullet", "mullet7", "crescent", "crossTau", "cancer", "sword", "sabresCrossed", "hand", "wolfStatant",
"horseshoe", "bowWithArrow", "arrow", "arrowsSheaf", "rake", "crossTriquetra", "crossLatin", "crossTau" "wolfHeadErased",
"greyhoundСourant",
"boarRampant",
"horseRampant",
"horseSalient",
"bullPassant",
"bearRampant",
"bearPassant",
"goat",
"lamb",
"elephant",
"eagle",
"raven",
"cock",
"parrot",
"swan",
"swanErased",
"heron",
"pike",
"dragonPassant",
"dragonRampant",
"wyvern",
"wyvernWithWingsDisplayed",
"griffinPassant",
"griffinRampant",
"unicornRampant",
"pegasus",
"serpent",
"hatchet",
"lochaberAxe",
"hand",
"wing",
"wingSword",
"lute",
"harp",
"bow",
"head",
"headWreathed",
"knight",
"lymphad",
"log",
"crosier",
"dolphin",
"sabre",
"monk",
"owl",
"axe",
"camel",
"fasces",
"lionPassantGuardant",
"helmet"
],
reversed: [
// charges that can be reversed
"goutte",
"mullet",
"mullet7",
"crescent",
"crossTau",
"cancer",
"sword",
"sabresCrossed",
"hand",
"horseshoe",
"bowWithArrow",
"arrow",
"arrowsSheaf",
"rake",
"crossTriquetra",
"crossLatin",
"crossTau"
] ]
} };
const positions = { const positions = {
conventional: { e: 20, abcdefgzi: 3, beh: 3, behdf: 2, acegi: 1, kn: 3, bhdf: 1, jeo: 1, abc: 3, jln: 6, jlh: 3, kmo: 2, jleh: 1, def: 3, abcpqh: 4, ABCDEFGHIJKL: 1 }, conventional: {e: 20, abcdefgzi: 3, beh: 3, behdf: 2, acegi: 1, kn: 3, bhdf: 1, jeo: 1, abc: 3, jln: 6, jlh: 3, kmo: 2, jleh: 1, def: 3, abcpqh: 4, ABCDEFGHIJKL: 1},
complex: { e: 40, beh: 1, kn: 1, jeo: 1, abc: 2, jln: 7, jlh: 2, def: 1, abcpqh: 1 }, complex: {e: 40, beh: 1, kn: 1, jeo: 1, abc: 2, jln: 7, jlh: 2, def: 1, abcpqh: 1},
divisions: { divisions: {
perPale: { e: 15, pq: 5, jo: 2, jl: 2, ABCDEFGHIJKL: 1 }, perPale: {e: 15, pq: 5, jo: 2, jl: 2, ABCDEFGHIJKL: 1},
perFess: { e: 12, kn: 4, jkl: 2, gizgiz: 1, jlh: 3, kmo: 1, ABCDEFGHIJKL: 1 }, perFess: {e: 12, kn: 4, jkl: 2, gizgiz: 1, jlh: 3, kmo: 1, ABCDEFGHIJKL: 1},
perBend: { e: 5, lm: 5, bcfdgh: 1 }, perBend: {e: 5, lm: 5, bcfdgh: 1},
perBendSinister: { e: 1, jo: 1 }, perBendSinister: {e: 1, jo: 1},
perCross: { e: 4, jlmo: 1, j: 1, jo: 2, jl: 1 }, perCross: {e: 4, jlmo: 1, j: 1, jo: 2, jl: 1},
perChevron: { e: 1, jlh: 1, dfk: 1, dfbh: 2, bdefh: 1 }, perChevron: {e: 1, jlh: 1, dfk: 1, dfbh: 2, bdefh: 1},
perChevronReversed: { e: 1, mok: 2, dfh: 2, dfbh: 1, bdefh: 1 }, perChevronReversed: {e: 1, mok: 2, dfh: 2, dfbh: 1, bdefh: 1},
perSaltire: { bhdf: 8, e: 3, abcdefgzi: 1, bh: 1, df: 1, ABCDEFGHIJKL: 1 }, perSaltire: {bhdf: 8, e: 3, abcdefgzi: 1, bh: 1, df: 1, ABCDEFGHIJKL: 1},
perPile: { ee: 3, be: 2, abceh: 1, abcabc: 1, jleh: 1 } perPile: {ee: 3, be: 2, abceh: 1, abcabc: 1, jleh: 1}
}, },
ordinariesOn: { ordinariesOn: {
pale: { ee: 12, beh: 10, kn: 3, bb: 1 }, pale: {ee: 12, beh: 10, kn: 3, bb: 1},
fess: { ee: 1, def: 3 }, fess: {ee: 1, def: 3},
bar: { defdefdef: 1 }, bar: {defdefdef: 1},
fessCotissed: { ee: 1, def: 3 }, fessCotissed: {ee: 1, def: 3},
fessDoubleCotissed: { ee: 1, defdef: 3 }, fessDoubleCotissed: {ee: 1, defdef: 3},
bend: { ee: 2, jo: 1, joe: 1 }, bend: {ee: 2, jo: 1, joe: 1},
bendSinister: { ee: 1, lm: 1, lem: 4 }, bendSinister: {ee: 1, lm: 1, lem: 4},
bendlet: { joejoejoe: 1 }, bendlet: {joejoejoe: 1},
bendletSinister: { lemlemlem: 1 }, bendletSinister: {lemlemlem: 1},
bordure: { ABCDEFGHIJKL: 1 }, bordure: {ABCDEFGHIJKL: 1},
chief: { abc: 5, bbb: 1 }, chief: {abc: 5, bbb: 1},
quarter: { jjj: 1 }, quarter: {jjj: 1},
canton: { yyyy: 1 }, canton: {yyyy: 1},
cross: { eeee: 1, behdfbehdf: 3, behbehbeh: 2 }, cross: {eeee: 1, behdfbehdf: 3, behbehbeh: 2},
crossParted: { e: 5, ee: 1 }, crossParted: {e: 5, ee: 1},
saltire: { ee: 5, jlemo: 1 }, saltire: {ee: 5, jlemo: 1},
saltireParted: { e: 5, ee: 1 }, saltireParted: {e: 5, ee: 1},
pall: { ee: 1, jleh: 5, jlhh: 3 }, pall: {ee: 1, jleh: 5, jlhh: 3},
pallReversed: { ee: 1, bemo: 5 }, pallReversed: {ee: 1, bemo: 5},
pile: { bbb: 1 }, pile: {bbb: 1},
pileInBend: { eeee: 1, eeoo: 1 }, pileInBend: {eeee: 1, eeoo: 1},
pileInBendSinister: { eeee: 1, eemm: 1 } pileInBendSinister: {eeee: 1, eemm: 1}
}, },
ordinariesOff: { ordinariesOff: {
pale: { yyy: 1 }, pale: {yyy: 1},
fess: { abc: 3, abcz: 1 }, fess: {abc: 3, abcz: 1},
bar: { abc: 2, abcgzi: 1, jlh: 5, bgi: 2, ach: 1 }, bar: {abc: 2, abcgzi: 1, jlh: 5, bgi: 2, ach: 1},
gemelle: { abc: 1 }, gemelle: {abc: 1},
bend: { ccg: 2, ccc: 1 }, bend: {ccg: 2, ccc: 1},
bendSinister: { aai: 2, aaa: 1 }, bendSinister: {aai: 2, aaa: 1},
bendlet: { ccg: 2, ccc: 1 }, bendlet: {ccg: 2, ccc: 1},
bendletSinister: { aai: 2, aaa: 1 }, bendletSinister: {aai: 2, aaa: 1},
bordure: { e: 4, jleh:2, kenken: 1, peqpeq: 1 }, bordure: {e: 4, jleh: 2, kenken: 1, peqpeq: 1},
orle: { e: 4, jleh: 1, kenken: 1, peqpeq: 1 }, orle: {e: 4, jleh: 1, kenken: 1, peqpeq: 1},
chief: { emo: 2, emoz: 1, ez: 2 }, chief: {emo: 2, emoz: 1, ez: 2},
terrace: { e: 5, def: 1, bdf: 3 }, terrace: {e: 5, def: 1, bdf: 3},
mount: { e: 5, def: 1, bdf: 3 }, mount: {e: 5, def: 1, bdf: 3},
point: { e: 2, def: 1, bdf: 3, acbdef: 1 }, point: {e: 2, def: 1, bdf: 3, acbdef: 1},
flaunches: { e: 3, kn: 1, beh: 3 }, flaunches: {e: 3, kn: 1, beh: 3},
gyron: { bh: 1 }, gyron: {bh: 1},
quarter: { e: 1 }, quarter: {e: 1},
canton: { e: 5, beh: 1, def: 1, bdefh: 1, kn: 1 }, canton: {e: 5, beh: 1, def: 1, bdefh: 1, kn: 1},
cross: { acgi: 1 }, cross: {acgi: 1},
pall: { BCKFEILGJbdmfo: 1 }, pall: {BCKFEILGJbdmfo: 1},
pallReversed: { aczac: 1 }, pallReversed: {aczac: 1},
chevron: { ach: 3, hhh: 1 }, chevron: {ach: 3, hhh: 1},
chevronReversed: { bbb: 1 }, chevronReversed: {bbb: 1},
pile: { acdfgi: 1, acac: 1 }, pile: {acdfgi: 1, acac: 1},
pileInBend: { cg: 1 }, pileInBend: {cg: 1},
pileInBendSinister: { ai: 1 }, pileInBendSinister: {ai: 1},
label: { defgzi: 2, eh: 3, defdefhmo: 1, egiegi: 1, pqn: 5 } label: {defgzi: 2, eh: 3, defdefhmo: 1, egiegi: 1, pqn: 5}
}, },
// charges // charges
inescutcheon: { e: 4, jln: 1 }, inescutcheon: {e: 4, jln: 1},
mascle: { e: 15, abcdefgzi: 3, beh: 3, bdefh: 4, acegi: 1, kn: 3, joe: 2, abc: 3, jlh: 8, jleh: 1, df: 3, abcpqh: 4, pqe: 3, eknpq: 3 }, mascle: {e: 15, abcdefgzi: 3, beh: 3, bdefh: 4, acegi: 1, kn: 3, joe: 2, abc: 3, jlh: 8, jleh: 1, df: 3, abcpqh: 4, pqe: 3, eknpq: 3},
lionRampant: { e: 10, def: 2, abc: 2, bdefh: 1, kn: 1, jlh: 2, abcpqh: 1 }, lionRampant: {e: 10, def: 2, abc: 2, bdefh: 1, kn: 1, jlh: 2, abcpqh: 1},
lionPassant: { e: 10, def: 1, abc: 1, bdefh: 1, jlh: 1, abcpqh: 1 }, lionPassant: {e: 10, def: 1, abc: 1, bdefh: 1, jlh: 1, abcpqh: 1},
wolfPassant: { e: 10, def: 1, abc: 1, bdefh: 1, jlh: 1, abcpqh: 1 }, wolfPassant: {e: 10, def: 1, abc: 1, bdefh: 1, jlh: 1, abcpqh: 1},
greyhoundСourant: { e: 10, def: 1, abc: 1, bdefh: 1, jlh: 1, abcpqh: 1 }, greyhoundСourant: {e: 10, def: 1, abc: 1, bdefh: 1, jlh: 1, abcpqh: 1},
griffinRampant: { e: 10, def: 2, abc: 2, bdefh: 1, kn: 1, jlh: 2, abcpqh: 1 }, griffinRampant: {e: 10, def: 2, abc: 2, bdefh: 1, kn: 1, jlh: 2, abcpqh: 1},
griffinPassant: { e: 10, def: 1, abc: 1, bdefh: 1, jlh: 1, abcpqh: 1 }, griffinPassant: {e: 10, def: 1, abc: 1, bdefh: 1, jlh: 1, abcpqh: 1},
boarRampant: { e: 12, beh: 1, kn: 1, jln: 2 }, boarRampant: {e: 12, beh: 1, kn: 1, jln: 2},
eagle: { e: 15, beh: 1, kn: 1, abc: 1, jlh: 2, def: 2, pq: 1 }, eagle: {e: 15, beh: 1, kn: 1, abc: 1, jlh: 2, def: 2, pq: 1},
raven: { e: 15, beh: 1, kn: 1, jeo: 1, abc: 3, jln: 3, def: 1 }, raven: {e: 15, beh: 1, kn: 1, jeo: 1, abc: 3, jln: 3, def: 1},
wyvern: { e: 10, jln: 1 }, wyvern: {e: 10, jln: 1},
garb: { e: 1, def: 3, abc: 2, beh: 1, kn: 1, jln: 3, jleh: 1, abcpqh: 1, joe: 1, lme: 1 }, garb: {e: 1, def: 3, abc: 2, beh: 1, kn: 1, jln: 3, jleh: 1, abcpqh: 1, joe: 1, lme: 1},
crown: { e: 10, abcdefgzi: 1, beh: 3, behdf: 2, acegi: 1, kn: 1, pq: 2, abc: 1, jln: 4, jleh: 1, def: 2, abcpqh: 3 }, crown: {e: 10, abcdefgzi: 1, beh: 3, behdf: 2, acegi: 1, kn: 1, pq: 2, abc: 1, jln: 4, jleh: 1, def: 2, abcpqh: 3},
hand: { e: 10, jln: 2, kn: 1, jeo: 1, abc: 2, pqe: 1 }, hand: {e: 10, jln: 2, kn: 1, jeo: 1, abc: 2, pqe: 1},
armillarySphere: {e: 1}, armillarySphere: {e: 1},
tree: {e: 1}, tree: {e: 1},
lymphad: {e: 1}, lymphad: {e: 1},
@ -175,33 +369,92 @@
}; };
const lines = { const lines = {
straight: 50, wavy: 8, engrailed: 4, invecked: 3, rayonne: 3, embattled: 1, raguly: 1, urdy: 1, dancetty: 1, indented: 2, straight: 50,
dentilly: 1, bevilled: 1, angled: 1, flechy: 1, barby: 1, enclavy: 1, escartely: 1, arched: 2, archedReversed: 1, nowy: 1, nowyReversed: 1, wavy: 8,
embattledGhibellin: 1, embattledNotched: 1, embattledGrady: 1, dovetailedIndented: 1, dovetailed: 1, engrailed: 4,
potenty: 1, potentyDexter: 1, potentySinister: 1, nebuly: 2, seaWaves: 1, dragonTeeth: 1, firTrees: 1 invecked: 3,
rayonne: 3,
embattled: 1,
raguly: 1,
urdy: 1,
dancetty: 1,
indented: 2,
dentilly: 1,
bevilled: 1,
angled: 1,
flechy: 1,
barby: 1,
enclavy: 1,
escartely: 1,
arched: 2,
archedReversed: 1,
nowy: 1,
nowyReversed: 1,
embattledGhibellin: 1,
embattledNotched: 1,
embattledGrady: 1,
dovetailedIndented: 1,
dovetailed: 1,
potenty: 1,
potentyDexter: 1,
potentySinister: 1,
nebuly: 2,
seaWaves: 1,
dragonTeeth: 1,
firTrees: 1
}; };
const divisions = { const divisions = {
variants: { perPale: 5, perFess: 5, perBend: 2, perBendSinister: 1, perChevron: 1, perChevronReversed: 1, perCross: 5, perPile: 1, perSaltire: 1, gyronny: 1, chevronny: 1 }, variants: {perPale: 5, perFess: 5, perBend: 2, perBendSinister: 1, perChevron: 1, perChevronReversed: 1, perCross: 5, perPile: 1, perSaltire: 1, gyronny: 1, chevronny: 1},
perPale: lines, perPale: lines,
perFess: lines, perFess: lines,
perBend: lines, perBend: lines,
perBendSinister: lines, perBendSinister: lines,
perChevron: lines, perChevron: lines,
perChevronReversed: lines, perChevronReversed: lines,
perCross: { straight: 20, wavy: 5, engrailed: 4, invecked: 3, rayonne: 1, embattled: 1, raguly: 1, urdy: 1, indented: 2, dentilly: 1, bevilled: 1, angled: 1, embattledGhibellin: 1, embattledGrady: 1, dovetailedIndented: 1, dovetailed: 1, potenty: 1, potentyDexter: 1, potentySinister: 1, nebuly: 1 }, perCross: {straight: 20, wavy: 5, engrailed: 4, invecked: 3, rayonne: 1, embattled: 1, raguly: 1, urdy: 1, indented: 2, dentilly: 1, bevilled: 1, angled: 1, embattledGhibellin: 1, embattledGrady: 1, dovetailedIndented: 1, dovetailed: 1, potenty: 1, potentyDexter: 1, potentySinister: 1, nebuly: 1},
perPile: lines perPile: lines
}; };
const ordinaries = { const ordinaries = {
lined: { lined: {
pale: 7, fess: 5, bend: 3, bendSinister: 2, chief: 5, bar: 2, gemelle: 1, fessCotissed: 1, fessDoubleCotissed: 1, pale: 7,
bendlet: 2, bendletSinister: 1, terrace: 3, cross: 6, crossParted: 1, saltire: 2, saltireParted: 1 fess: 5,
bend: 3,
bendSinister: 2,
chief: 5,
bar: 2,
gemelle: 1,
fessCotissed: 1,
fessDoubleCotissed: 1,
bendlet: 2,
bendletSinister: 1,
terrace: 3,
cross: 6,
crossParted: 1,
saltire: 2,
saltireParted: 1
}, },
straight: { straight: {
bordure: 8, orle: 4, mount: 1, point: 2, flaunches: 1, gore: 1, bordure: 8,
gyron: 1, quarter: 1, canton: 2, pall: 3, pallReversed: 2, chevron: 4, chevronReversed: 3, orle: 4,
pile: 2, pileInBend: 2, pileInBendSinister: 1, piles: 1, pilesInPoint: 2, label: 1 mount: 1,
point: 2,
flaunches: 1,
gore: 1,
gyron: 1,
quarter: 1,
canton: 2,
pall: 3,
pallReversed: 2,
chevron: 4,
chevronReversed: 3,
pile: 2,
pileInBend: 2,
pileInBendSinister: 1,
piles: 1,
pilesInPoint: 2,
label: 1
} }
}; };
@ -215,60 +468,61 @@
simple: {round: 12, oval: 6, vesicaPiscis: 1, square: 1, diamond: 2, no: 0}, simple: {round: 12, oval: 6, vesicaPiscis: 1, square: 1, diamond: 2, no: 0},
fantasy: {fantasy1: 2, fantasy2: 2, fantasy3: 1, fantasy4: 1, fantasy5: 3}, fantasy: {fantasy1: 2, fantasy2: 2, fantasy3: 1, fantasy4: 1, fantasy5: 3},
middleEarth: {noldor: 1, gondor: 1, easterling: 1, erebor: 1, ironHills: 1, urukHai: 1, moriaOrc: 1} middleEarth: {noldor: 1, gondor: 1, easterling: 1, erebor: 1, ironHills: 1, urukHai: 1, moriaOrc: 1}
} };
const generate = function(parent, kinship, dominion, type) { const generate = function (parent, kinship, dominion, type) {
if (!parent || parent === "custom") { if (!parent || parent === "custom") {
parent = null; parent = null;
kinship = 0; kinship = 0;
dominion = 0; dominion = 0;
} }
let usedPattern = null, usedTinctures = []; let usedPattern = null,
usedTinctures = [];
const t1 = P(kinship) ? parent.t1 : getTincture("field"); const t1 = P(kinship) ? parent.t1 : getTincture("field");
if (t1.includes("-")) usedPattern = t1; if (t1.includes("-")) usedPattern = t1;
const coa = {t1}; const coa = {t1};
let charge = P(usedPattern ? .5 : .93) ? true : false; // 80% for charge let charge = P(usedPattern ? 0.5 : 0.93) ? true : false; // 80% for charge
const linedOrdinary = charge && P(.3) || P(.5) ? parent?.ordinaries && P(kinship) ? parent.ordinaries[0].ordinary : rw(ordinaries.lined) : null; const linedOrdinary = (charge && P(0.3)) || P(0.5) ? (parent?.ordinaries && P(kinship) ? parent.ordinaries[0].ordinary : rw(ordinaries.lined)) : null;
const ordinary = !charge && P(.65) || P(.3) ? linedOrdinary ? linedOrdinary : rw(ordinaries.straight) : null; // 36% for ordinary const ordinary = (!charge && P(0.65)) || P(0.3) ? (linedOrdinary ? linedOrdinary : rw(ordinaries.straight)) : null; // 36% for ordinary
const rareDivided = ["chief", "terrace", "chevron", "quarter", "flaunches"].includes(ordinary); const rareDivided = ["chief", "terrace", "chevron", "quarter", "flaunches"].includes(ordinary);
const divisioned = rareDivided ? P(.03) : charge && ordinary ? P(.03) : charge ? P(.3) : ordinary ? P(.7) : P(.995); // 33% for division const divisioned = rareDivided ? P(0.03) : charge && ordinary ? P(0.03) : charge ? P(0.3) : ordinary ? P(0.7) : P(0.995); // 33% for division
const division = divisioned ? parent?.division && P(kinship - .1) ? parent.division.division : rw(divisions.variants) : null; const division = divisioned ? (parent?.division && P(kinship - 0.1) ? parent.division.division : rw(divisions.variants)) : null;
if (charge) charge = if (charge) charge = parent?.charges && P(kinship - 0.1) ? parent.charges[0].charge : type && type !== "Generic" && P(0.2) ? rw(charges[type]) : selectCharge();
parent?.charges && P(kinship - .1) ? parent.charges[0].charge :
type && type !== "Generic" && P(.2) ? rw(charges[type]) :
selectCharge();
if (division) { if (division) {
const t = getTincture("division", usedTinctures, P(.98) ? coa.t1 : null); const t = getTincture("division", usedTinctures, P(0.98) ? coa.t1 : null);
coa.division = {division, t}; coa.division = {division, t};
if (divisions[division]) coa.division.line = usedPattern || (ordinary && P(.7)) ? "straight" : rw(divisions[division]); if (divisions[division]) coa.division.line = usedPattern || (ordinary && P(0.7)) ? "straight" : rw(divisions[division]);
} }
if (ordinary) { if (ordinary) {
coa.ordinaries = [{ordinary, t: getTincture("charge", usedTinctures, coa.t1)}]; coa.ordinaries = [{ordinary, t: getTincture("charge", usedTinctures, coa.t1)}];
if (linedOrdinary) coa.ordinaries[0].line = usedPattern || (division && P(.7)) ? "straight" : rw(lines); if (linedOrdinary) coa.ordinaries[0].line = usedPattern || (division && P(0.7)) ? "straight" : rw(lines);
if (division && !charge && !usedPattern && P(.5) && ordinary !== "bordure" && ordinary !== "orle") { if (division && !charge && !usedPattern && P(0.5) && ordinary !== "bordure" && ordinary !== "orle") {
if (P(.8)) coa.ordinaries[0].divided = "counter"; // 40% if (P(0.8)) coa.ordinaries[0].divided = "counter";
else if (P(.6)) coa.ordinaries[0].divided = "field"; // 6% // 40%
else if (P(0.6)) coa.ordinaries[0].divided = "field";
// 6%
else coa.ordinaries[0].divided = "division"; // 4% else coa.ordinaries[0].divided = "division"; // 4%
} }
} }
if (charge) { if (charge) {
let p = "e", t = "gules"; let p = "e",
t = "gules";
const ordinaryT = coa.ordinaries ? coa.ordinaries[0].t : null; const ordinaryT = coa.ordinaries ? coa.ordinaries[0].t : null;
if (positions.ordinariesOn[ordinary] && P(.8)) { if (positions.ordinariesOn[ordinary] && P(0.8)) {
// place charge over ordinary (use tincture of field type) // place charge over ordinary (use tincture of field type)
p = rw(positions.ordinariesOn[ordinary]); p = rw(positions.ordinariesOn[ordinary]);
while (charges.natural[charge] === ordinaryT) charge = selectCharge(); while (charges.natural[charge] === ordinaryT) charge = selectCharge();
t = !usedPattern && P(.3) ? coa.t1 : getTincture("charge", [], ordinaryT); t = !usedPattern && P(0.3) ? coa.t1 : getTincture("charge", [], ordinaryT);
} else if (positions.ordinariesOff[ordinary] && P(.95)) { } else if (positions.ordinariesOff[ordinary] && P(0.95)) {
// place charge out of ordinary (use tincture of ordinary type) // place charge out of ordinary (use tincture of ordinary type)
p = rw(positions.ordinariesOff[ordinary]); p = rw(positions.ordinariesOff[ordinary]);
while (charges.natural[charge] === coa.t1) charge = selectCharge(); while (charges.natural[charge] === coa.t1) charge = selectCharge();
t = !usedPattern && P(.3) ? ordinaryT : getTincture("charge", usedTinctures, coa.t1); t = !usedPattern && P(0.3) ? ordinaryT : getTincture("charge", usedTinctures, coa.t1);
} else if (positions.divisions[division]) { } else if (positions.divisions[division]) {
// place charge in fields made by division // place charge in fields made by division
p = rw(positions.divisions[division]); p = rw(positions.divisions[division]);
@ -289,43 +543,41 @@
if (charges.natural[charge]) t = charges.natural[charge]; // natural tincture if (charges.natural[charge]) t = charges.natural[charge]; // natural tincture
coa.charges = [{charge, t, p}]; coa.charges = [{charge, t, p}];
if (p === "ABCDEFGHIKL" && P(.95)) { if (p === "ABCDEFGHIKL" && P(0.95)) {
// add central charge if charge is in bordure // add central charge if charge is in bordure
coa.charges[0].charge = rw(charges.conventional); coa.charges[0].charge = rw(charges.conventional);
const charge = selectCharge(charges.single); const charge = selectCharge(charges.single);
const t = getTincture("charge", usedTinctures, coa.t1); const t = getTincture("charge", usedTinctures, coa.t1);
coa.charges.push({charge, t, p: "e"}); coa.charges.push({charge, t, p: "e"});
} else if (P(.8) && charge === "inescutcheon") { } else if (P(0.8) && charge === "inescutcheon") {
// add charge to inescutcheon // add charge to inescutcheon
const charge = selectCharge(charges.types); const charge = selectCharge(charges.types);
const t2 = getTincture("charge", [], t); const t2 = getTincture("charge", [], t);
coa.charges.push({charge, t: t2, p, size:.5}); coa.charges.push({charge, t: t2, p, size: 0.5});
} else if (division && !ordinary) { } else if (division && !ordinary) {
const allowCounter = !usedPattern && (!coa.line || coa.line === "straight"); const allowCounter = !usedPattern && (!coa.line || coa.line === "straight");
// dimidiation: second charge at division basic positons // dimidiation: second charge at division basic positons
if (P(.3) && ["perPale", "perFess"].includes(division) && coa.line === "straight") { if (P(0.3) && ["perPale", "perFess"].includes(division) && coa.line === "straight") {
coa.charges[0].divided = "field"; coa.charges[0].divided = "field";
if (P(.95)) { if (P(0.95)) {
const p2 = p === "e" || P(.5) ? "e" : rw(positions.divisions[division]); const p2 = p === "e" || P(0.5) ? "e" : rw(positions.divisions[division]);
const charge = selectCharge(charges.single); const charge = selectCharge(charges.single);
const t = getTincture("charge", usedTinctures, coa.division.t); const t = getTincture("charge", usedTinctures, coa.division.t);
coa.charges.push({charge, t, p: p2, divided: "division"}); coa.charges.push({charge, t, p: p2, divided: "division"});
} }
} } else if (allowCounter && P(0.4)) coa.charges[0].divided = "counter";
else if (allowCounter && P(.4)) coa.charges[0].divided = "counter"; // counterchanged, 40% // counterchanged, 40%
else if (["perPale", "perFess", "perBend", "perBendSinister"].includes(division) && P(.8)) { // place 2 charges in division standard positions else if (["perPale", "perFess", "perBend", "perBendSinister"].includes(division) && P(0.8)) {
const [p1, p2] = division === "perPale" ? ["p", "q"] : // place 2 charges in division standard positions
division === "perFess" ? ["k", "n"] : const [p1, p2] = division === "perPale" ? ["p", "q"] : division === "perFess" ? ["k", "n"] : division === "perBend" ? ["l", "m"] : ["j", "o"]; // perBendSinister
division === "perBend" ? ["l", "m"] :
["j", "o"]; // perBendSinister
coa.charges[0].p = p1; coa.charges[0].p = p1;
const charge = selectCharge(charges.single); const charge = selectCharge(charges.single);
const t = getTincture("charge", usedTinctures, coa.division.t); const t = getTincture("charge", usedTinctures, coa.division.t);
coa.charges.push({charge, t, p: p2}); coa.charges.push({charge, t, p: p2});
} } else if (["perCross", "perSaltire"].includes(division) && P(0.5)) {
else if (["perCross", "perSaltire"].includes(division) && P(.5)) { // place 4 charges in division standard positions // place 4 charges in division standard positions
const [p1, p2, p3, p4] = division === "perCross" ? ["j", "l", "m", "o"] : ["b", "d", "f", "h"]; const [p1, p2, p3, p4] = division === "perCross" ? ["j", "l", "m", "o"] : ["b", "d", "f", "h"];
coa.charges[0].p = p1; coa.charges[0].p = p1;
@ -338,8 +590,7 @@
const c4 = selectCharge(charges.single); const c4 = selectCharge(charges.single);
const t4 = getTincture("charge", [], coa.t1); const t4 = getTincture("charge", [], coa.t1);
coa.charges.push({charge: c2, t: t2, p: p2}, {charge: c3, t: t3, p: p3}, {charge: c4, t: t4, p: p4}); coa.charges.push({charge: c2, t: t2, p: p2}, {charge: c3, t: t3, p: p3}, {charge: c4, t: t4, p: p4});
} } else if (allowCounter && p.length > 1) coa.charges[0].divided = "counter"; // counterchanged, 40%
else if (allowCounter && p.length > 1) coa.charges[0].divided = "counter"; // counterchanged, 40%
} }
coa.charges.forEach(c => defineChargeAttributes(c)); coa.charges.forEach(c => defineChargeAttributes(c));
@ -351,8 +602,8 @@
c.p = [...new Set(c.p)].join(""); c.p = [...new Set(c.p)].join("");
// define orientation // define orientation
if (P(.02) && charges.sinister.includes(c.charge)) c.sinister = 1; if (P(0.02) && charges.sinister.includes(c.charge)) c.sinister = 1;
if (P(.02) && charges.reversed.includes(c.charge)) c.reversed = 1; if (P(0.02) && charges.reversed.includes(c.charge)) c.reversed = 1;
} }
} }
@ -380,24 +631,26 @@
if (!coa.charges) coa.charges = []; if (!coa.charges) coa.charges = [];
coa.charges.push({charge, t: t2, p: "y", size: 0.5}); coa.charges.push({charge, t: t2, p: "y", size: 0.5});
coa.ordinaries ? coa.ordinaries.push(canton) : coa.ordinaries = [canton]; coa.ordinaries ? coa.ordinaries.push(canton) : (coa.ordinaries = [canton]);
} }
function selectCharge(set) { function selectCharge(set) {
const type = set ? rw(set) : ordinary || divisioned ? rw(charges.types): rw(charges.single); const type = set ? rw(set) : ordinary || divisioned ? rw(charges.types) : rw(charges.single);
return type === "inescutcheon" ? "inescutcheon" : rw(charges[type]); return type === "inescutcheon" ? "inescutcheon" : rw(charges[type]);
} }
// select tincture: element type (field, division, charge), used field tinctures, field type to follow RoT // select tincture: element type (field, division, charge), used field tinctures, field type to follow RoT
function getTincture(element, fields = [], RoT) { function getTincture(element, fields = [], RoT) {
const base = RoT ? RoT.includes("-") ? RoT.split("-")[1] : RoT : null; const base = RoT ? (RoT.includes("-") ? RoT.split("-")[1] : RoT) : null;
let type = rw(tinctures[element]); // metals, colours, stains, patterns let type = rw(tinctures[element]); // metals, colours, stains, patterns
if (RoT && type !== "patterns") type = getType(base) === "metals" ? "colours" : "metals"; // follow RoT if (RoT && type !== "patterns") type = getType(base) === "metals" ? "colours" : "metals"; // follow RoT
if (type === "metals" && fields.includes("or") && fields.includes("argent")) type = "colours"; // exclude metals overuse if (type === "metals" && fields.includes("or") && fields.includes("argent")) type = "colours"; // exclude metals overuse
let tincture = rw(tinctures[type]); let tincture = rw(tinctures[type]);
while (tincture === base || fields.includes(tincture)) {tincture = rw(tinctures[type]);} // follow RoT while (tincture === base || fields.includes(tincture)) {
tincture = rw(tinctures[type]);
} // follow RoT
if (type !== "patterns" && element !== "charge") usedTinctures.push(tincture); // add field tincture if (type !== "patterns" && element !== "charge") usedTinctures.push(tincture); // add field tincture
@ -425,39 +678,60 @@
if (Object.keys(tinctures.stains).includes(tincture)) return "stains"; if (Object.keys(tinctures.stains).includes(tincture)) return "stains";
else return "pattern"; else return "pattern";
} }
} }
function definePattern(pattern, element, size = "") { function definePattern(pattern, element, size = "") {
let t1 = null, t2 = null; let t1 = null,
if (P(.1)) size = "-small"; t2 = null;
else if (P(.1)) size = "-smaller"; if (P(0.1)) size = "-small";
else if (P(.01)) size = "-big"; else if (P(0.1)) size = "-smaller";
else if (P(.005)) size = "-smallest"; else if (P(0.01)) size = "-big";
else if (P(0.005)) size = "-smallest";
// apply standard tinctures // apply standard tinctures
if (P(.5) && ["vair", "vairInPale", "vairEnPointe"].includes(pattern)) {t1 = "azure"; t2 = "argent";} if (P(0.5) && ["vair", "vairInPale", "vairEnPointe"].includes(pattern)) {
else if (P(.8) && pattern === "ermine") {t1 = "argent"; t2 = "sable";} t1 = "azure";
else if (pattern === "pappellony") { t2 = "argent";
if (P(.2)) {t1 = "gules"; t2 = "or";} } else if (P(0.8) && pattern === "ermine") {
else if (P(.2)) {t1 = "argent"; t2 = "sable";} t1 = "argent";
else if (P(.2)) {t1 = "azure"; t2 = "argent";} t2 = "sable";
} } else if (pattern === "pappellony") {
else if (pattern === "masoned") { if (P(0.2)) {
if (P(.3)) {t1 = "gules"; t2 = "argent";} t1 = "gules";
else if (P(.3)) {t1 = "argent"; t2 = "sable";} t2 = "or";
else if (P(.1)) {t1 = "or"; t2 = "sable";} } else if (P(0.2)) {
} t1 = "argent";
else if (pattern === "fretty") { t2 = "sable";
if (t2 === "sable" || P(.35)) {t1 = "argent"; t2 = "gules";} } else if (P(0.2)) {
else if (P(.25)) {t1 = "sable"; t2 = "or";} t1 = "azure";
else if (P(.15)) {t1 = "gules"; t2 = "argent";} t2 = "argent";
} }
else if (pattern === "semy") pattern += "_of_" + selectCharge(charges.semy); } else if (pattern === "masoned") {
if (P(0.3)) {
t1 = "gules";
t2 = "argent";
} else if (P(0.3)) {
t1 = "argent";
t2 = "sable";
} else if (P(0.1)) {
t1 = "or";
t2 = "sable";
}
} else if (pattern === "fretty") {
if (t2 === "sable" || P(0.35)) {
t1 = "argent";
t2 = "gules";
} else if (P(0.25)) {
t1 = "sable";
t2 = "or";
} else if (P(0.15)) {
t1 = "gules";
t2 = "argent";
}
} else if (pattern === "semy") pattern += "_of_" + selectCharge(charges.semy);
if (!t1 || !t2) { if (!t1 || !t2) {
const startWithMetal = P(.7); const startWithMetal = P(0.7);
t1 = startWithMetal ? rw(tinctures.metals) : rw(tinctures.colours); t1 = startWithMetal ? rw(tinctures.metals) : rw(tinctures.colours);
t2 = startWithMetal ? rw(tinctures.colours) : rw(tinctures.metals); t2 = startWithMetal ? rw(tinctures.colours) : rw(tinctures.metals);
} }
@ -474,28 +748,30 @@
function replaceTincture(t, n) { function replaceTincture(t, n) {
const type = getType(t); const type = getType(t);
while (!n || n === t) {n = rw(tinctures[type]);} while (!n || n === t) {
n = rw(tinctures[type]);
}
return n; return n;
} }
function getSize(p, o = null, d = null) { function getSize(p, o = null, d = null) {
if (p === "e" && (o === "bordure" || o === "orle")) return 1.1; if (p === "e" && (o === "bordure" || o === "orle")) return 1.1;
if (p === "e") return 1.5; if (p === "e") return 1.5;
if (p === "jln" || p === "jlh") return .7; if (p === "jln" || p === "jlh") return 0.7;
if (p === "abcpqh" || p === "ez" || p === "be") return .5; if (p === "abcpqh" || p === "ez" || p === "be") return 0.5;
if (["a", "b", "c", "d", "f", "g", "h", "i", "bh", "df"].includes(p)) return .5; if (["a", "b", "c", "d", "f", "g", "h", "i", "bh", "df"].includes(p)) return 0.5;
if (["j", "l", "m", "o", "jlmo"].includes(p) && d === "perCross") return .6; if (["j", "l", "m", "o", "jlmo"].includes(p) && d === "perCross") return 0.6;
if (p.length > 10) return .18; // >10 (bordure) if (p.length > 10) return 0.18; // >10 (bordure)
if (p.length > 7) return .3; // 8, 9, 10 if (p.length > 7) return 0.3; // 8, 9, 10
if (p.length > 4) return .4; // 5, 6, 7 if (p.length > 4) return 0.4; // 5, 6, 7
if (p.length > 2) return .5; // 3, 4 if (p.length > 2) return 0.5; // 3, 4
return .7; // 1, 2 return 0.7; // 1, 2
} }
return coa; return coa;
} };
const getShield = function(culture, state) { const getShield = function (culture, state) {
const emblemShape = document.getElementById("emblemShape"); const emblemShape = document.getElementById("emblemShape");
const shapeGroup = emblemShape.selectedOptions[0]?.parentNode.label || "Diversiform"; const shapeGroup = emblemShape.selectedOptions[0]?.parentNode.label || "Diversiform";
if (shapeGroup !== "Diversiform") return emblemShape.value; if (shapeGroup !== "Diversiform") return emblemShape.value;
@ -504,11 +780,10 @@
if (pack.cultures[culture].shield) return pack.cultures[culture].shield; if (pack.cultures[culture].shield) return pack.cultures[culture].shield;
console.error("Shield shape is not defined on culture level", pack.cultures[culture]); console.error("Shield shape is not defined on culture level", pack.cultures[culture]);
return "heater"; return "heater";
} };
const toString = coa => JSON.stringify(coa).replaceAll("#", "%23"); const toString = coa => JSON.stringify(coa).replaceAll("#", "%23");
const copy = coa => JSON.parse(JSON.stringify(coa)); const copy = coa => JSON.parse(JSON.stringify(coa));
return {generate, toString, copy, getShield, shields}; return {generate, toString, copy, getShield, shields};
})();
})));

File diff suppressed because it is too large Load diff

View file

@ -1,13 +1,10 @@
(function (global, factory) { "use strict";
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.Cultures = factory());
}(this, (function () {'use strict';
window.Cultures = (function () {
let cells; let cells;
const generate = function() { const generate = function () {
TIME && console.time('generateCultures'); TIME && console.time("generateCultures");
cells = pack.cells; cells = pack.cells;
cells.culture = new Uint16Array(cells.i.length); // cell cultures cells.culture = new Uint16Array(cells.i.length); // cell cultures
let count = Math.min(+culturesInput.value, +culturesSet.selectedOptions[0].dataset.max); let count = Math.min(+culturesInput.value, +culturesSet.selectedOptions[0].dataset.max);
@ -17,13 +14,19 @@
count = Math.floor(populated.length / 50); count = Math.floor(populated.length / 50);
if (!count) { if (!count) {
WARN && console.warn(`There are no populated cells. Cannot generate cultures`); WARN && console.warn(`There are no populated cells. Cannot generate cultures`);
pack.cultures = [{name:"Wildlands", i:0, base:1, shield:"round"}]; pack.cultures = [{name: "Wildlands", i: 0, base: 1, shield: "round"}];
alertMessage.innerHTML = ` alertMessage.innerHTML = `
The climate is harsh and people cannot live in this world.<br> The climate is harsh and people cannot live in this world.<br>
No cultures, states and burgs will be created.<br> No cultures, states and burgs will be created.<br>
Please consider changing climate settings in the World Configurator`; Please consider changing climate settings in the World Configurator`;
$("#alert").dialog({resizable: false, title: "Extreme climate warning", $("#alert").dialog({
buttons: {Ok: function() {$(this).dialog("close");}} resizable: false,
title: "Extreme climate warning",
buttons: {
Ok: function () {
$(this).dialog("close");
}
}
}); });
return; return;
} else { } else {
@ -32,22 +35,28 @@
There are only ${populated.length} populated cells and it's insufficient livable area.<br> There are only ${populated.length} populated cells and it's insufficient livable area.<br>
Only ${count} out of ${culturesInput.value} requested cultures will be generated.<br> Only ${count} out of ${culturesInput.value} requested cultures will be generated.<br>
Please consider changing climate settings in the World Configurator`; Please consider changing climate settings in the World Configurator`;
$("#alert").dialog({resizable: false, title: "Extreme climate warning", $("#alert").dialog({
buttons: {Ok: function() {$(this).dialog("close");}} resizable: false,
title: "Extreme climate warning",
buttons: {
Ok: function () {
$(this).dialog("close");
}
}
}); });
} }
} }
const cultures = pack.cultures = selectCultures(count); const cultures = (pack.cultures = selectCultures(count));
const centers = d3.quadtree(); const centers = d3.quadtree();
const colors = getColors(count); const colors = getColors(count);
const emblemShape = document.getElementById("emblemShape").value; const emblemShape = document.getElementById("emblemShape").value;
const codes = []; const codes = [];
cultures.forEach(function(c, i) { cultures.forEach(function (c, i) {
const cell = c.center = placeCenter(c.sort ? c.sort : (i) => cells.s[i]); const cell = (c.center = placeCenter(c.sort ? c.sort : i => cells.s[i]));
centers.add(cells.p[cell]); centers.add(cells.p[cell]);
c.i = i+1; c.i = i + 1;
delete c.odd; delete c.odd;
delete c.sort; delete c.sort;
c.color = colors[i]; c.color = colors[i];
@ -56,20 +65,24 @@
c.origin = 0; c.origin = 0;
c.code = abbreviate(c.name, codes); c.code = abbreviate(c.name, codes);
codes.push(c.code); codes.push(c.code);
cells.culture[cell] = i+1; cells.culture[cell] = i + 1;
if (emblemShape === "random") c.shield = getRandomShield(); if (emblemShape === "random") c.shield = getRandomShield();
}); });
function placeCenter(v) { function placeCenter(v) {
let c, spacing = (graphWidth + graphHeight) / 2 / count; let c,
const sorted = [...populated].sort((a, b) => v(b) - v(a)), max = Math.floor(sorted.length / 2); spacing = (graphWidth + graphHeight) / 2 / count;
do {c = sorted[biased(0, max, 5)]; spacing *= .9;} const sorted = [...populated].sort((a, b) => v(b) - v(a)),
while (centers.find(cells.p[c][0], cells.p[c][1], spacing) !== undefined); max = Math.floor(sorted.length / 2);
do {
c = sorted[biased(0, max, 5)];
spacing *= 0.9;
} while (centers.find(cells.p[c][0], cells.p[c][1], spacing) !== undefined);
return c; return c;
} }
// the first culture with id 0 is for wildlands // the first culture with id 0 is for wildlands
cultures.unshift({name:"Wildlands", i:0, base:1, origin:null, shield:"round"}); cultures.unshift({name: "Wildlands", i: 0, base: 1, origin: null, shield: "round"});
// make sure all bases exist in nameBases // make sure all bases exist in nameBases
if (!nameBases.length) { if (!nameBases.length) {
@ -77,7 +90,7 @@
nameBases = Names.getNameBases(); nameBases = Names.getNameBases();
} }
cultures.forEach(c => c.base = c.base % nameBases.length); cultures.forEach(c => (c.base = c.base % nameBases.length));
function selectCultures(c) { function selectCultures(c) {
let def = getDefault(c); let def = getDefault(c);
@ -87,11 +100,11 @@
const count = Math.min(c, def.length); const count = Math.min(c, def.length);
const cultures = []; const cultures = [];
for (let culture, rnd, i=0; cultures.length < count && i < 200; i++) { for (let culture, rnd, i = 0; cultures.length < count && i < 200; i++) {
do { do {
rnd = rand(def.length-1); rnd = rand(def.length - 1);
culture = def[rnd]; culture = def[rnd];
} while (!P(culture.odd)) } while (!P(culture.odd));
cultures.push(culture); cultures.push(culture);
def.splice(rnd, 1); def.splice(rnd, 1);
} }
@ -100,31 +113,31 @@
// set culture type based on culture center position // set culture type based on culture center position
function defineCultureType(i) { function defineCultureType(i) {
if (cells.h[i] < 70 && [1,2,4].includes(cells.biome[i])) return "Nomadic"; // high penalty in forest biomes and near coastline if (cells.h[i] < 70 && [1, 2, 4].includes(cells.biome[i])) return "Nomadic"; // high penalty in forest biomes and near coastline
if (cells.h[i] > 50) return "Highland"; // no penalty for hills and moutains, high for other elevations if (cells.h[i] > 50) return "Highland"; // no penalty for hills and moutains, high for other elevations
const f = pack.features[cells.f[cells.haven[i]]]; // opposite feature const f = pack.features[cells.f[cells.haven[i]]]; // opposite feature
if (f.type === "lake" && f.cells > 5) return "Lake" // low water cross penalty and high for growth not along coastline if (f.type === "lake" && f.cells > 5) return "Lake"; // low water cross penalty and high for growth not along coastline
if (cells.harbor[i] && f.type !== "lake" && P(.1) || (cells.harbor[i] === 1 && P(.6)) || (pack.features[cells.f[i]].group === "isle" && P(.4))) return "Naval"; // low water cross penalty and high for non-along-coastline growth if ((cells.harbor[i] && f.type !== "lake" && P(0.1)) || (cells.harbor[i] === 1 && P(0.6)) || (pack.features[cells.f[i]].group === "isle" && P(0.4))) return "Naval"; // low water cross penalty and high for non-along-coastline growth
if (cells.r[i] && cells.fl[i] > 100) return "River"; // no River cross penalty, penalty for non-River growth if (cells.r[i] && cells.fl[i] > 100) return "River"; // no River cross penalty, penalty for non-River growth
if (cells.t[i] > 2 && [3,7,8,9,10,12].includes(cells.biome[i])) return "Hunting"; // high penalty in non-native biomes if (cells.t[i] > 2 && [3, 7, 8, 9, 10, 12].includes(cells.biome[i])) return "Hunting"; // high penalty in non-native biomes
return "Generic"; return "Generic";
} }
function defineCultureExpansionism(type) { function defineCultureExpansionism(type) {
let base = 1; // Generic let base = 1; // Generic
if (type === "Lake") base = .8; else if (type === "Lake") base = 0.8;
if (type === "Naval") base = 1.5; else else if (type === "Naval") base = 1.5;
if (type === "River") base = .9; else else if (type === "River") base = 0.9;
if (type === "Nomadic") base = 1.5; else else if (type === "Nomadic") base = 1.5;
if (type === "Hunting") base = .7; else else if (type === "Hunting") base = 0.7;
if (type === "Highland") base = 1.2; else if (type === "Highland") base = 1.2;
return rn((Math.random() * powerInput.value / 2 + 1) * base, 1); return rn(((Math.random() * powerInput.value) / 2 + 1) * base, 1);
} }
TIME && console.timeEnd('generateCultures'); TIME && console.timeEnd("generateCultures");
} };
const add = function(center) { const add = function (center) {
const defaultCultures = getDefault(); const defaultCultures = getDefault();
let culture, base, name; let culture, base, name;
@ -139,7 +152,10 @@
name = Names.getCulture(culture, 5, 8, ""); name = Names.getCulture(culture, 5, 8, "");
base = pack.cultures[culture].base; base = pack.cultures[culture].base;
} }
const code = abbreviate(name, pack.cultures.map(c => c.code)); const code = abbreviate(
name,
pack.cultures.map(c => c.code)
);
const i = pack.cultures.length; const i = pack.cultures.length;
const color = d3.color(d3.scaleSequential(d3.interpolateRainbow)(Math.random())).hex(); const color = d3.color(d3.scaleSequential(d3.interpolateRainbow)(Math.random())).hex();
@ -148,220 +164,231 @@
const emblemShape = document.getElementById("emblemShape").value; const emblemShape = document.getElementById("emblemShape").value;
if (emblemShape === "random") shield = getRandomShield(); if (emblemShape === "random") shield = getRandomShield();
pack.cultures.push({name, color, base, center, i, expansionism:1, type:"Generic", cells:0, area:0, rural:0, urban:0, origin:0, code, shield}); pack.cultures.push({name, color, base, center, i, expansionism: 1, type: "Generic", cells: 0, area: 0, rural: 0, urban: 0, origin: 0, code, shield});
} };
const getDefault = function(count) { const getDefault = function (count) {
// generic sorting functions // generic sorting functions
const cells = pack.cells, s = cells.s, sMax = d3.max(s), t = cells.t, h = cells.h, temp = grid.cells.temp; const cells = pack.cells,
const n = cell => Math.ceil(s[cell] / sMax * 3) // normalized cell score s = cells.s,
const td = (cell, goal) => {const d = Math.abs(temp[cells.g[cell]] - goal); return d ? d+1 : 1;} // temperature difference fee sMax = d3.max(s),
const bd = (cell, biomes, fee = 4) => biomes.includes(cells.biome[cell]) ? 1 : fee; // biome difference fee t = cells.t,
const sf = (cell, fee = 4) => cells.haven[cell] && pack.features[cells.f[cells.haven[cell]]].type !== "lake" ? 1 : fee; // not on sea coast fee h = cells.h,
temp = grid.cells.temp;
const n = cell => Math.ceil((s[cell] / sMax) * 3); // normalized cell score
const td = (cell, goal) => {
const d = Math.abs(temp[cells.g[cell]] - goal);
return d ? d + 1 : 1;
}; // temperature difference fee
const bd = (cell, biomes, fee = 4) => (biomes.includes(cells.biome[cell]) ? 1 : fee); // biome difference fee
const sf = (cell, fee = 4) => (cells.haven[cell] && pack.features[cells.f[cells.haven[cell]]].type !== "lake" ? 1 : fee); // not on sea coast fee
if (culturesSet.value === "european") { if (culturesSet.value === "european") {
return [ return [
{name:"Shwazen", base:0, odd:1, sort: i => n(i) / td(i, 10) / bd(i, [6, 8]), shield:"swiss"}, {name: "Shwazen", base: 0, odd: 1, sort: i => n(i) / td(i, 10) / bd(i, [6, 8]), shield: "swiss"},
{name:"Angshire", base:1, odd:1, sort: i => n(i) / td(i, 10) / sf(i), shield:"wedged"}, {name: "Angshire", base: 1, odd: 1, sort: i => n(i) / td(i, 10) / sf(i), shield: "wedged"},
{name:"Luari", base:2, odd:1, sort: i => n(i) / td(i, 12) / bd(i, [6, 8]), shield:"french"}, {name: "Luari", base: 2, odd: 1, sort: i => n(i) / td(i, 12) / bd(i, [6, 8]), shield: "french"},
{name:"Tallian", base:3, odd:1, sort: i => n(i) / td(i, 15), shield:"horsehead"}, {name: "Tallian", base: 3, odd: 1, sort: i => n(i) / td(i, 15), shield: "horsehead"},
{name:"Astellian", base:4, odd:1, sort: i => n(i) / td(i, 16), shield:"spanish"}, {name: "Astellian", base: 4, odd: 1, sort: i => n(i) / td(i, 16), shield: "spanish"},
{name:"Slovan", base:5, odd:1, sort: i => n(i) / td(i, 6) * t[i], shield:"polish"}, {name: "Slovan", base: 5, odd: 1, sort: i => (n(i) / td(i, 6)) * t[i], shield: "polish"},
{name:"Norse", base:6, odd:1, sort: i => n(i) / td(i, 5), shield:"heater"}, {name: "Norse", base: 6, odd: 1, sort: i => n(i) / td(i, 5), shield: "heater"},
{name:"Elladan", base:7, odd:1, sort: i => n(i) / td(i, 18) * h[i], shield:"boeotian"}, {name: "Elladan", base: 7, odd: 1, sort: i => (n(i) / td(i, 18)) * h[i], shield: "boeotian"},
{name:"Romian", base:8, odd:.2, sort: i => n(i) / td(i, 15) / t[i], shield:"roman"}, {name: "Romian", base: 8, odd: 0.2, sort: i => n(i) / td(i, 15) / t[i], shield: "roman"},
{name:"Soumi", base:9, odd:1, sort: i => n(i) / td(i, 5) / bd(i, [9]) * t[i], shield:"pavise"}, {name: "Soumi", base: 9, odd: 1, sort: i => (n(i) / td(i, 5) / bd(i, [9])) * t[i], shield: "pavise"},
{name:"Portuzian", base:13, odd:1, sort: i => n(i) / td(i, 17) / sf(i), shield:"renaissance"}, {name: "Portuzian", base: 13, odd: 1, sort: i => n(i) / td(i, 17) / sf(i), shield: "renaissance"},
{name:"Vengrian", base: 15, odd:1, sort: i => n(i) / td(i, 11) / bd(i, [4]) * t[i], shield:"horsehead2"}, {name: "Vengrian", base: 15, odd: 1, sort: i => (n(i) / td(i, 11) / bd(i, [4])) * t[i], shield: "horsehead2"},
{name:"Turchian", base: 16, odd:.05, sort: i => n(i) / td(i, 14), shield:"round"}, {name: "Turchian", base: 16, odd: 0.05, sort: i => n(i) / td(i, 14), shield: "round"},
{name:"Euskati", base: 20, odd:.05, sort: i => n(i) / td(i, 15) * h[i], shield:"oldFrench"}, {name: "Euskati", base: 20, odd: 0.05, sort: i => (n(i) / td(i, 15)) * h[i], shield: "oldFrench"},
{name:"Keltan", base: 22, odd:.05, sort: i => n(i) / td(i, 11) / bd(i, [6, 8]) * t[i], shield:"oval"} {name: "Keltan", base: 22, odd: 0.05, sort: i => (n(i) / td(i, 11) / bd(i, [6, 8])) * t[i], shield: "oval"}
]; ];
} }
if (culturesSet.value === "oriental") { if (culturesSet.value === "oriental") {
return [ return [
{name:"Koryo", base:10, odd:1, sort: i => n(i) / td(i, 12) / t[i], shield:"round"}, {name: "Koryo", base: 10, odd: 1, sort: i => n(i) / td(i, 12) / t[i], shield: "round"},
{name:"Hantzu", base:11, odd:1, sort: i => n(i) / td(i, 13), shield:"banner"}, {name: "Hantzu", base: 11, odd: 1, sort: i => n(i) / td(i, 13), shield: "banner"},
{name:"Yamoto", base:12, odd:1, sort: i => n(i) / td(i, 15) / t[i], shield:"round"}, {name: "Yamoto", base: 12, odd: 1, sort: i => n(i) / td(i, 15) / t[i], shield: "round"},
{name:"Turchian", base: 16, odd:1, sort: i => n(i) / td(i, 12), shield:"round"}, {name: "Turchian", base: 16, odd: 1, sort: i => n(i) / td(i, 12), shield: "round"},
{name:"Berberan", base: 17, odd:.2, sort: i => n(i) / td(i, 19) / bd(i, [1, 2, 3], 7) * t[i], shield:"oval"}, {name: "Berberan", base: 17, odd: 0.2, sort: i => (n(i) / td(i, 19) / bd(i, [1, 2, 3], 7)) * t[i], shield: "oval"},
{name:"Eurabic", base: 18, odd:1, sort: i => n(i) / td(i, 26) / bd(i, [1, 2], 7) * t[i], shield:"oval"}, {name: "Eurabic", base: 18, odd: 1, sort: i => (n(i) / td(i, 26) / bd(i, [1, 2], 7)) * t[i], shield: "oval"},
{name:"Efratic", base: 23, odd:.1, sort: i => n(i) / td(i, 22) * t[i], shield:"round"}, {name: "Efratic", base: 23, odd: 0.1, sort: i => (n(i) / td(i, 22)) * t[i], shield: "round"},
{name:"Tehrani", base: 24, odd:1, sort: i => n(i) / td(i, 18) * h[i], shield:"round"}, {name: "Tehrani", base: 24, odd: 1, sort: i => (n(i) / td(i, 18)) * h[i], shield: "round"},
{name:"Maui", base: 25, odd:.2, sort: i => n(i) / td(i, 24) / sf(i) / t[i], shield:"vesicaPiscis"}, {name: "Maui", base: 25, odd: 0.2, sort: i => n(i) / td(i, 24) / sf(i) / t[i], shield: "vesicaPiscis"},
{name:"Carnatic", base: 26, odd:.5, sort: i => n(i) / td(i, 26), shield:"round"}, {name: "Carnatic", base: 26, odd: 0.5, sort: i => n(i) / td(i, 26), shield: "round"},
{name:"Vietic", base: 29, odd:.8, sort: i => n(i) / td(i, 25) / bd(i, [7], 7) / t[i], shield:"banner"}, {name: "Vietic", base: 29, odd: 0.8, sort: i => n(i) / td(i, 25) / bd(i, [7], 7) / t[i], shield: "banner"},
{name:"Guantzu", base:30, odd:.5, sort: i => n(i) / td(i, 17), shield:"banner"}, {name: "Guantzu", base: 30, odd: 0.5, sort: i => n(i) / td(i, 17), shield: "banner"},
{name:"Ulus", base:31, odd:1, sort: i => n(i) / td(i, 5) / bd(i, [2, 4, 10], 7) * t[i], shield:"banner"} {name: "Ulus", base: 31, odd: 1, sort: i => (n(i) / td(i, 5) / bd(i, [2, 4, 10], 7)) * t[i], shield: "banner"}
]; ];
} }
if (culturesSet.value === "english") { if (culturesSet.value === "english") {
const getName = () => Names.getBase(1, 5, 9, "", 0); const getName = () => Names.getBase(1, 5, 9, "", 0);
return [ return [
{name:getName(), base:1, odd:1, shield:"heater"}, {name: getName(), base: 1, odd: 1, shield: "heater"},
{name:getName(), base:1, odd:1, shield:"wedged"}, {name: getName(), base: 1, odd: 1, shield: "wedged"},
{name:getName(), base:1, odd:1, shield:"swiss"}, {name: getName(), base: 1, odd: 1, shield: "swiss"},
{name:getName(), base:1, odd:1, shield:"oldFrench"}, {name: getName(), base: 1, odd: 1, shield: "oldFrench"},
{name:getName(), base:1, odd:1, shield:"swiss"}, {name: getName(), base: 1, odd: 1, shield: "swiss"},
{name:getName(), base:1, odd:1, shield:"spanish"}, {name: getName(), base: 1, odd: 1, shield: "spanish"},
{name:getName(), base:1, odd:1, shield:"hessen"}, {name: getName(), base: 1, odd: 1, shield: "hessen"},
{name:getName(), base:1, odd:1, shield:"fantasy5"}, {name: getName(), base: 1, odd: 1, shield: "fantasy5"},
{name:getName(), base:1, odd:1, shield:"fantasy4"}, {name: getName(), base: 1, odd: 1, shield: "fantasy4"},
{name:getName(), base:1, odd:1, shield:"fantasy1"} {name: getName(), base: 1, odd: 1, shield: "fantasy1"}
]; ];
} }
if (culturesSet.value === "antique") { if (culturesSet.value === "antique") {
return [ return [
{name:"Roman", base:8, odd:1, sort: i => n(i) / td(i, 14) / t[i], shield:"roman"}, // Roman {name: "Roman", base: 8, odd: 1, sort: i => n(i) / td(i, 14) / t[i], shield: "roman"}, // Roman
{name:"Roman", base:8, odd:1, sort: i => n(i) / td(i, 15) / sf(i), shield:"roman"}, // Roman {name: "Roman", base: 8, odd: 1, sort: i => n(i) / td(i, 15) / sf(i), shield: "roman"}, // Roman
{name:"Roman", base:8, odd:1, sort: i => n(i) / td(i, 16) / sf(i), shield:"roman"}, // Roman {name: "Roman", base: 8, odd: 1, sort: i => n(i) / td(i, 16) / sf(i), shield: "roman"}, // Roman
{name:"Roman", base:8, odd:1, sort: i => n(i) / td(i, 17) / t[i], shield:"roman"}, // Roman {name: "Roman", base: 8, odd: 1, sort: i => n(i) / td(i, 17) / t[i], shield: "roman"}, // Roman
{name:"Hellenic", base:7, odd:1, sort: i => n(i) / td(i, 18) / sf(i) * h[i], shield:"boeotian"}, // Greek {name: "Hellenic", base: 7, odd: 1, sort: i => (n(i) / td(i, 18) / sf(i)) * h[i], shield: "boeotian"}, // Greek
{name:"Hellenic", base:7, odd:1, sort: i => n(i) / td(i, 19) / sf(i) * h[i], shield:"boeotian"}, // Greek {name: "Hellenic", base: 7, odd: 1, sort: i => (n(i) / td(i, 19) / sf(i)) * h[i], shield: "boeotian"}, // Greek
{name:"Macedonian", base:7, odd:.5, sort: i => n(i) / td(i, 12) * h[i], shield:"round"}, // Greek {name: "Macedonian", base: 7, odd: 0.5, sort: i => (n(i) / td(i, 12)) * h[i], shield: "round"}, // Greek
{name:"Celtic", base:22, odd:1, sort: i => n(i) / td(i, 11) ** .5 / bd(i, [6, 8]), shield:"round"}, {name: "Celtic", base: 22, odd: 1, sort: i => n(i) / td(i, 11) ** 0.5 / bd(i, [6, 8]), shield: "round"},
{name:"Germanic", base:0, odd:1, sort: i => n(i) / td(i, 10) ** .5 / bd(i, [6, 8]), shield:"round"}, {name: "Germanic", base: 0, odd: 1, sort: i => n(i) / td(i, 10) ** 0.5 / bd(i, [6, 8]), shield: "round"},
{name:"Persian", base:24, odd:.8, sort: i => n(i) / td(i, 18) * h[i], shield:"oval"}, // Iranian {name: "Persian", base: 24, odd: 0.8, sort: i => (n(i) / td(i, 18)) * h[i], shield: "oval"}, // Iranian
{name:"Scythian", base:24, odd:.5, sort: i => n(i) / td(i, 11) ** .5 / bd(i, [4]), shield:"round"}, // Iranian {name: "Scythian", base: 24, odd: 0.5, sort: i => n(i) / td(i, 11) ** 0.5 / bd(i, [4]), shield: "round"}, // Iranian
{name:"Cantabrian", base: 20, odd:.5, sort: i => n(i) / td(i, 16) * h[i], shield:"oval"}, // Basque {name: "Cantabrian", base: 20, odd: 0.5, sort: i => (n(i) / td(i, 16)) * h[i], shield: "oval"}, // Basque
{name:"Estian", base: 9, odd:.2, sort: i => n(i) / td(i, 5) * t[i], shield:"pavise"}, // Finnic {name: "Estian", base: 9, odd: 0.2, sort: i => (n(i) / td(i, 5)) * t[i], shield: "pavise"}, // Finnic
{name:"Carthaginian", base: 17, odd:.3, sort: i => n(i) / td(i, 19) / sf(i), shield:"oval"}, // Berber {name: "Carthaginian", base: 17, odd: 0.3, sort: i => n(i) / td(i, 19) / sf(i), shield: "oval"}, // Berber
{name:"Mesopotamian", base: 23, odd:.2, sort: i => n(i) / td(i, 22) / bd(i, [1, 2, 3]), shield:"oval"} // Mesopotamian {name: "Mesopotamian", base: 23, odd: 0.2, sort: i => n(i) / td(i, 22) / bd(i, [1, 2, 3]), shield: "oval"} // Mesopotamian
]; ];
} }
if (culturesSet.value === "highFantasy") { if (culturesSet.value === "highFantasy") {
return [ return [
// fantasy races // fantasy races
{name:"Quenian (Elfish)", base: 33, odd:1, sort: i => n(i) / bd(i, [6,7,8,9], 10) * t[i], shield:"gondor"}, // Elves {name: "Quenian (Elfish)", base: 33, odd: 1, sort: i => (n(i) / bd(i, [6, 7, 8, 9], 10)) * t[i], shield: "gondor"}, // Elves
{name:"Eldar (Elfish)", base: 33, odd:1, sort: i => n(i) / bd(i, [6,7,8,9], 10) * t[i], shield:"noldor"}, // Elves {name: "Eldar (Elfish)", base: 33, odd: 1, sort: i => (n(i) / bd(i, [6, 7, 8, 9], 10)) * t[i], shield: "noldor"}, // Elves
{name:"Trow (Dark Elfish)", base: 34, odd:.9, sort: i => n(i) / bd(i, [7,8,9,12], 10) * t[i], shield:"hessen"}, // Dark Elves {name: "Trow (Dark Elfish)", base: 34, odd: 0.9, sort: i => (n(i) / bd(i, [7, 8, 9, 12], 10)) * t[i], shield: "hessen"}, // Dark Elves
{name:"Lothian (Dark Elfish)", base: 34, odd:.3, sort: i => n(i) / bd(i, [7,8,9,12], 10) * t[i], shield:"wedged"}, // Dark Elves {name: "Lothian (Dark Elfish)", base: 34, odd: 0.3, sort: i => (n(i) / bd(i, [7, 8, 9, 12], 10)) * t[i], shield: "wedged"}, // Dark Elves
{name:"Dunirr (Dwarven)", base: 35, odd:1, sort: i => n(i) + h[i], shield:"ironHills"}, // Dwarfs {name: "Dunirr (Dwarven)", base: 35, odd: 1, sort: i => n(i) + h[i], shield: "ironHills"}, // Dwarfs
{name:"Khazadur (Dwarven)", base: 35, odd:1, sort: i => n(i) + h[i], shield:"erebor"}, // Dwarfs {name: "Khazadur (Dwarven)", base: 35, odd: 1, sort: i => n(i) + h[i], shield: "erebor"}, // Dwarfs
{name:"Kobold (Goblin)", base: 36, odd:1, sort: i => t[i] - s[i], shield:"moriaOrc"}, // Goblin {name: "Kobold (Goblin)", base: 36, odd: 1, sort: i => t[i] - s[i], shield: "moriaOrc"}, // Goblin
{name:"Uruk (Orkish)", base: 37, odd:1, sort: i => h[i] * t[i], shield:"urukHai"}, // Orc {name: "Uruk (Orkish)", base: 37, odd: 1, sort: i => h[i] * t[i], shield: "urukHai"}, // Orc
{name:"Ugluk (Orkish)", base: 37, odd:.5, sort: i => h[i] * t[i] / bd(i, [1,2,10,11]), shield:"moriaOrc"}, // Orc {name: "Ugluk (Orkish)", base: 37, odd: 0.5, sort: i => (h[i] * t[i]) / bd(i, [1, 2, 10, 11]), shield: "moriaOrc"}, // Orc
{name:"Yotunn (Giants)", base: 38, odd:.7, sort: i => td(i, -10), shield:"pavise"}, // Giant {name: "Yotunn (Giants)", base: 38, odd: 0.7, sort: i => td(i, -10), shield: "pavise"}, // Giant
{name:"Rake (Drakonic)", base: 39, odd:.7, sort: i => -s[i], shield:"fantasy2"}, // Draconic {name: "Rake (Drakonic)", base: 39, odd: 0.7, sort: i => -s[i], shield: "fantasy2"}, // Draconic
{name:"Arago (Arachnid)", base: 40, odd:.7, sort: i => t[i] - s[i], shield:"horsehead2"}, // Arachnid {name: "Arago (Arachnid)", base: 40, odd: 0.7, sort: i => t[i] - s[i], shield: "horsehead2"}, // Arachnid
{name:"Aj'Snaga (Serpents)", base: 41, odd:.7, sort: i => n(i) / bd(i, [12], 10), shield:"fantasy1"}, // Serpents {name: "Aj'Snaga (Serpents)", base: 41, odd: 0.7, sort: i => n(i) / bd(i, [12], 10), shield: "fantasy1"}, // Serpents
// fantasy human // fantasy human
{name:"Anor (Human)", base:32, odd:1, sort: i => n(i) / td(i, 10), shield:"fantasy5"}, {name: "Anor (Human)", base: 32, odd: 1, sort: i => n(i) / td(i, 10), shield: "fantasy5"},
{name:"Dail (Human)", base:32, odd:1, sort: i => n(i) / td(i, 13), shield:"roman"}, {name: "Dail (Human)", base: 32, odd: 1, sort: i => n(i) / td(i, 13), shield: "roman"},
{name:"Rohand (Human)", base:16, odd:1, sort: i => n(i) / td(i, 16), shield:"round"}, {name: "Rohand (Human)", base: 16, odd: 1, sort: i => n(i) / td(i, 16), shield: "round"},
{name:"Dulandir (Human)", base:31, odd:1, sort: i => n(i) / td(i, 5) / bd(i, [2, 4, 10], 7) * t[i], shield:"easterling"}, {name: "Dulandir (Human)", base: 31, odd: 1, sort: i => (n(i) / td(i, 5) / bd(i, [2, 4, 10], 7)) * t[i], shield: "easterling"}
]; ];
} }
if (culturesSet.value === "darkFantasy") { if (culturesSet.value === "darkFantasy") {
return [ return [
// common real-world English // common real-world English
{name:"Angshire", base:1, odd:1, sort: i => n(i) / td(i, 10) / sf(i), shield:"heater"}, {name: "Angshire", base: 1, odd: 1, sort: i => n(i) / td(i, 10) / sf(i), shield: "heater"},
{name:"Enlandic", base:1, odd:1, sort: i => n(i) / td(i, 12), shield:"heater"}, {name: "Enlandic", base: 1, odd: 1, sort: i => n(i) / td(i, 12), shield: "heater"},
{name:"Westen", base:1, odd:1, sort: i => n(i) / td(i, 10), shield:"heater"}, {name: "Westen", base: 1, odd: 1, sort: i => n(i) / td(i, 10), shield: "heater"},
{name:"Nortumbic", base:1, odd:1, sort: i => n(i) / td(i, 7), shield:"heater"}, {name: "Nortumbic", base: 1, odd: 1, sort: i => n(i) / td(i, 7), shield: "heater"},
{name:"Mercian", base:1, odd:1, sort: i => n(i) / td(i, 9), shield:"heater"}, {name: "Mercian", base: 1, odd: 1, sort: i => n(i) / td(i, 9), shield: "heater"},
{name:"Kentian", base:1, odd:1, sort: i => n(i) / td(i, 12), shield:"heater"}, {name: "Kentian", base: 1, odd: 1, sort: i => n(i) / td(i, 12), shield: "heater"},
// rare real-world western // rare real-world western
{name:"Norse", base:6, odd:.7, sort: i => n(i) / td(i, 5) / sf(i), shield:"oldFrench"}, {name: "Norse", base: 6, odd: 0.7, sort: i => n(i) / td(i, 5) / sf(i), shield: "oldFrench"},
{name:"Schwarzen", base:0, odd:.3, sort: i => n(i) / td(i, 10) / bd(i, [6, 8]), shield:"gonfalon"}, {name: "Schwarzen", base: 0, odd: 0.3, sort: i => n(i) / td(i, 10) / bd(i, [6, 8]), shield: "gonfalon"},
{name:"Luarian", base:2, odd:.3, sort: i => n(i) / td(i, 12) / bd(i, [6, 8]), shield:"oldFrench"}, {name: "Luarian", base: 2, odd: 0.3, sort: i => n(i) / td(i, 12) / bd(i, [6, 8]), shield: "oldFrench"},
{name:"Hetallian", base:3, odd:.3, sort: i => n(i) / td(i, 15), shield:"oval"}, {name: "Hetallian", base: 3, odd: 0.3, sort: i => n(i) / td(i, 15), shield: "oval"},
{name:"Astellian", base:4, odd:.3, sort: i => n(i) / td(i, 16), shield:"spanish"}, {name: "Astellian", base: 4, odd: 0.3, sort: i => n(i) / td(i, 16), shield: "spanish"},
// rare real-world exotic // rare real-world exotic
{name:"Kiswaili", base:28, odd:.05, sort: i => n(i) / td(i, 29) / bd(i, [1, 3, 5, 7]), shield:"vesicaPiscis"}, {name: "Kiswaili", base: 28, odd: 0.05, sort: i => n(i) / td(i, 29) / bd(i, [1, 3, 5, 7]), shield: "vesicaPiscis"},
{name:"Yoruba", base:21, odd:.05, sort: i => n(i) / td(i, 15) / bd(i, [5, 7]), shield:"vesicaPiscis"}, {name: "Yoruba", base: 21, odd: 0.05, sort: i => n(i) / td(i, 15) / bd(i, [5, 7]), shield: "vesicaPiscis"},
{name:"Koryo", base:10, odd:.05, sort: i => n(i) / td(i, 12) / t[i], shield:"round"}, {name: "Koryo", base: 10, odd: 0.05, sort: i => n(i) / td(i, 12) / t[i], shield: "round"},
{name:"Hantzu", base:11, odd:.05, sort: i => n(i) / td(i, 13), shield:"banner"}, {name: "Hantzu", base: 11, odd: 0.05, sort: i => n(i) / td(i, 13), shield: "banner"},
{name:"Yamoto", base:12, odd:.05, sort: i => n(i) / td(i, 15) / t[i], shield:"round"}, {name: "Yamoto", base: 12, odd: 0.05, sort: i => n(i) / td(i, 15) / t[i], shield: "round"},
{name:"Guantzu", base:30, odd:.05, sort: i => n(i) / td(i, 17), shield:"banner"}, {name: "Guantzu", base: 30, odd: 0.05, sort: i => n(i) / td(i, 17), shield: "banner"},
{name:"Ulus", base:31, odd:.05, sort: i => n(i) / td(i, 5) / bd(i, [2, 4, 10], 7) * t[i], shield:"banner"}, {name: "Ulus", base: 31, odd: 0.05, sort: i => (n(i) / td(i, 5) / bd(i, [2, 4, 10], 7)) * t[i], shield: "banner"},
{name:"Turan", base: 16, odd:.05, sort: i => n(i) / td(i, 12), shield:"round"}, {name: "Turan", base: 16, odd: 0.05, sort: i => n(i) / td(i, 12), shield: "round"},
{name:"Berberan", base: 17, odd:.05, sort: i => n(i) / td(i, 19) / bd(i, [1, 2, 3], 7) * t[i], shield:"round"}, {name: "Berberan", base: 17, odd: 0.05, sort: i => (n(i) / td(i, 19) / bd(i, [1, 2, 3], 7)) * t[i], shield: "round"},
{name:"Eurabic", base: 18, odd:.05, sort: i => n(i) / td(i, 26) / bd(i, [1, 2], 7) * t[i], shield:"round"}, {name: "Eurabic", base: 18, odd: 0.05, sort: i => (n(i) / td(i, 26) / bd(i, [1, 2], 7)) * t[i], shield: "round"},
{name:"Slovan", base:5, odd:.05, sort: i => n(i) / td(i, 6) * t[i], shield:"round"}, {name: "Slovan", base: 5, odd: 0.05, sort: i => (n(i) / td(i, 6)) * t[i], shield: "round"},
{name:"Keltan", base: 22, odd:.1, sort: i => n(i) / td(i, 11) ** .5 / bd(i, [6, 8]), shield:"vesicaPiscis"}, {name: "Keltan", base: 22, odd: 0.1, sort: i => n(i) / td(i, 11) ** 0.5 / bd(i, [6, 8]), shield: "vesicaPiscis"},
{name:"Elladan", base:7, odd:.2, sort: i => n(i) / td(i, 18) / sf(i) * h[i], shield:"boeotian"}, {name: "Elladan", base: 7, odd: 0.2, sort: i => (n(i) / td(i, 18) / sf(i)) * h[i], shield: "boeotian"},
{name:"Romian", base:8, odd:.2, sort: i => n(i) / td(i, 14) / t[i], shield:"roman"}, {name: "Romian", base: 8, odd: 0.2, sort: i => n(i) / td(i, 14) / t[i], shield: "roman"},
// fantasy races // fantasy races
{name:"Eldar", base: 33, odd:.5, sort: i => n(i) / bd(i, [6,7,8,9], 10) * t[i], shield:"fantasy5"}, // Elves {name: "Eldar", base: 33, odd: 0.5, sort: i => (n(i) / bd(i, [6, 7, 8, 9], 10)) * t[i], shield: "fantasy5"}, // Elves
{name:"Trow", base: 34, odd:.8, sort: i => n(i) / bd(i, [7,8,9,12], 10) * t[i], shield:"hessen"}, // Dark Elves {name: "Trow", base: 34, odd: 0.8, sort: i => (n(i) / bd(i, [7, 8, 9, 12], 10)) * t[i], shield: "hessen"}, // Dark Elves
{name:"Durinn", base: 35, odd:.8, sort: i => n(i) + h[i], shield:"erebor"}, // Dwarven {name: "Durinn", base: 35, odd: 0.8, sort: i => n(i) + h[i], shield: "erebor"}, // Dwarven
{name:"Kobblin", base: 36, odd:.8, sort: i => t[i] - s[i], shield:"moriaOrc"}, // Goblin {name: "Kobblin", base: 36, odd: 0.8, sort: i => t[i] - s[i], shield: "moriaOrc"}, // Goblin
{name:"Uruk", base: 37, odd:.8, sort: i => h[i] * t[i] / bd(i, [1,2,10,11]), shield:"urukHai"}, // Orc {name: "Uruk", base: 37, odd: 0.8, sort: i => (h[i] * t[i]) / bd(i, [1, 2, 10, 11]), shield: "urukHai"}, // Orc
{name:"Yotunn", base: 38, odd:.8, sort: i => td(i, -10), shield:"pavise"}, // Giant {name: "Yotunn", base: 38, odd: 0.8, sort: i => td(i, -10), shield: "pavise"}, // Giant
{name:"Drake", base: 39, odd:.9, sort: i => -s[i], shield:"fantasy2"}, // Draconic {name: "Drake", base: 39, odd: 0.9, sort: i => -s[i], shield: "fantasy2"}, // Draconic
{name:"Rakhnid", base: 40, odd:.9, sort: i => t[i] - s[i], shield:"horsehead2"}, // Arachnid {name: "Rakhnid", base: 40, odd: 0.9, sort: i => t[i] - s[i], shield: "horsehead2"}, // Arachnid
{name:"Aj'Snaga", base: 41, odd:.9, sort: i => n(i) / bd(i, [12], 10), shield:"fantasy1"}, // Serpents {name: "Aj'Snaga", base: 41, odd: 0.9, sort: i => n(i) / bd(i, [12], 10), shield: "fantasy1"} // Serpents
] ];
} }
if (culturesSet.value === "random") { if (culturesSet.value === "random") {
return d3.range(count).map(function() { return d3.range(count).map(function () {
const rnd = rand(nameBases.length-1); const rnd = rand(nameBases.length - 1);
const name = Names.getBaseShort(rnd); const name = Names.getBaseShort(rnd);
return {name, base:rnd, odd:1, shield:getRandomShield()} return {name, base: rnd, odd: 1, shield: getRandomShield()};
}); });
} }
// all-world // all-world
return [ return [
{name:"Shwazen", base:0, odd:.7, sort: i => n(i) / td(i, 10) / bd(i, [6, 8]), shield:"hessen"}, {name: "Shwazen", base: 0, odd: 0.7, sort: i => n(i) / td(i, 10) / bd(i, [6, 8]), shield: "hessen"},
{name:"Angshire", base:1, odd:1, sort: i => n(i) / td(i, 10) / sf(i), shield:"heater"}, {name: "Angshire", base: 1, odd: 1, sort: i => n(i) / td(i, 10) / sf(i), shield: "heater"},
{name:"Luari", base:2, odd:.6, sort: i => n(i) / td(i, 12) / bd(i, [6, 8]), shield:"oldFrench"}, {name: "Luari", base: 2, odd: 0.6, sort: i => n(i) / td(i, 12) / bd(i, [6, 8]), shield: "oldFrench"},
{name:"Tallian", base:3, odd:.6, sort: i => n(i) / td(i, 15), shield:"horsehead2"}, {name: "Tallian", base: 3, odd: 0.6, sort: i => n(i) / td(i, 15), shield: "horsehead2"},
{name:"Astellian", base:4, odd:.6, sort: i => n(i) / td(i, 16), shield:"spanish"}, {name: "Astellian", base: 4, odd: 0.6, sort: i => n(i) / td(i, 16), shield: "spanish"},
{name:"Slovan", base:5, odd:.7, sort: i => n(i) / td(i, 6) * t[i], shield:"round"}, {name: "Slovan", base: 5, odd: 0.7, sort: i => (n(i) / td(i, 6)) * t[i], shield: "round"},
{name:"Norse", base:6, odd:.7, sort: i => n(i) / td(i, 5), shield:"heater"}, {name: "Norse", base: 6, odd: 0.7, sort: i => n(i) / td(i, 5), shield: "heater"},
{name:"Elladan", base:7, odd:.7, sort: i => n(i) / td(i, 18) * h[i], shield:"boeotian"}, {name: "Elladan", base: 7, odd: 0.7, sort: i => (n(i) / td(i, 18)) * h[i], shield: "boeotian"},
{name:"Romian", base:8, odd:.7, sort: i => n(i) / td(i, 15), shield:"roman"}, {name: "Romian", base: 8, odd: 0.7, sort: i => n(i) / td(i, 15), shield: "roman"},
{name:"Soumi", base:9, odd:.3, sort: i => n(i) / td(i, 5) / bd(i, [9]) * t[i], shield:"pavise"}, {name: "Soumi", base: 9, odd: 0.3, sort: i => (n(i) / td(i, 5) / bd(i, [9])) * t[i], shield: "pavise"},
{name:"Koryo", base:10, odd:.1, sort: i => n(i) / td(i, 12) / t[i], shield:"round"}, {name: "Koryo", base: 10, odd: 0.1, sort: i => n(i) / td(i, 12) / t[i], shield: "round"},
{name:"Hantzu", base:11, odd:.1, sort: i => n(i) / td(i, 13), shield:"banner"}, {name: "Hantzu", base: 11, odd: 0.1, sort: i => n(i) / td(i, 13), shield: "banner"},
{name:"Yamoto", base:12, odd:.1, sort: i => n(i) / td(i, 15) / t[i], shield:"round"}, {name: "Yamoto", base: 12, odd: 0.1, sort: i => n(i) / td(i, 15) / t[i], shield: "round"},
{name:"Portuzian", base:13, odd:.4, sort: i => n(i) / td(i, 17) / sf(i), shield:"spanish"}, {name: "Portuzian", base: 13, odd: 0.4, sort: i => n(i) / td(i, 17) / sf(i), shield: "spanish"},
{name:"Nawatli", base:14, odd:.1, sort: i => h[i] / td(i, 18) / bd(i, [7]), shield:"square"}, {name: "Nawatli", base: 14, odd: 0.1, sort: i => h[i] / td(i, 18) / bd(i, [7]), shield: "square"},
{name:"Vengrian", base: 15, odd:.2, sort: i => n(i) / td(i, 11) / bd(i, [4]) * t[i], shield:"wedged"}, {name: "Vengrian", base: 15, odd: 0.2, sort: i => (n(i) / td(i, 11) / bd(i, [4])) * t[i], shield: "wedged"},
{name:"Turchian", base: 16, odd:.2, sort: i => n(i) / td(i, 13), shield:"round"}, {name: "Turchian", base: 16, odd: 0.2, sort: i => n(i) / td(i, 13), shield: "round"},
{name:"Berberan", base: 17, odd:.1, sort: i => n(i) / td(i, 19) / bd(i, [1, 2, 3], 7) * t[i], shield:"round"}, {name: "Berberan", base: 17, odd: 0.1, sort: i => (n(i) / td(i, 19) / bd(i, [1, 2, 3], 7)) * t[i], shield: "round"},
{name:"Eurabic", base: 18, odd:.2, sort: i => n(i) / td(i, 26) / bd(i, [1, 2], 7) * t[i], shield:"round"}, {name: "Eurabic", base: 18, odd: 0.2, sort: i => (n(i) / td(i, 26) / bd(i, [1, 2], 7)) * t[i], shield: "round"},
{name:"Inuk", base: 19, odd:.05, sort: i => td(i, -1) / bd(i, [10, 11]) / sf(i), shield:"square"}, {name: "Inuk", base: 19, odd: 0.05, sort: i => td(i, -1) / bd(i, [10, 11]) / sf(i), shield: "square"},
{name:"Euskati", base: 20, odd:.05, sort: i => n(i) / td(i, 15) * h[i], shield:"spanish"}, {name: "Euskati", base: 20, odd: 0.05, sort: i => (n(i) / td(i, 15)) * h[i], shield: "spanish"},
{name:"Yoruba", base: 21, odd:.05, sort: i => n(i) / td(i, 15) / bd(i, [5, 7]), shield:"vesicaPiscis"}, {name: "Yoruba", base: 21, odd: 0.05, sort: i => n(i) / td(i, 15) / bd(i, [5, 7]), shield: "vesicaPiscis"},
{name:"Keltan", base: 22, odd:.05, sort: i => n(i) / td(i, 11) / bd(i, [6, 8]) * t[i], shield:"vesicaPiscis"}, {name: "Keltan", base: 22, odd: 0.05, sort: i => (n(i) / td(i, 11) / bd(i, [6, 8])) * t[i], shield: "vesicaPiscis"},
{name:"Efratic", base: 23, odd:.05, sort: i => n(i) / td(i, 22) * t[i], shield:"diamond"}, {name: "Efratic", base: 23, odd: 0.05, sort: i => (n(i) / td(i, 22)) * t[i], shield: "diamond"},
{name:"Tehrani", base: 24, odd:.1, sort: i => n(i) / td(i, 18) * h[i], shield:"round"}, {name: "Tehrani", base: 24, odd: 0.1, sort: i => (n(i) / td(i, 18)) * h[i], shield: "round"},
{name:"Maui", base: 25, odd:.05, sort: i => n(i) / td(i, 24) / sf(i) / t[i], shield:"round"}, {name: "Maui", base: 25, odd: 0.05, sort: i => n(i) / td(i, 24) / sf(i) / t[i], shield: "round"},
{name:"Carnatic", base: 26, odd:.05, sort: i => n(i) / td(i, 26), shield:"round"}, {name: "Carnatic", base: 26, odd: 0.05, sort: i => n(i) / td(i, 26), shield: "round"},
{name:"Inqan", base: 27, odd:.05, sort: i => h[i] / td(i, 13), shield:"square"}, {name: "Inqan", base: 27, odd: 0.05, sort: i => h[i] / td(i, 13), shield: "square"},
{name:"Kiswaili", base: 28, odd:.1, sort: i => n(i) / td(i, 29) / bd(i, [1, 3, 5, 7]), shield:"vesicaPiscis"}, {name: "Kiswaili", base: 28, odd: 0.1, sort: i => n(i) / td(i, 29) / bd(i, [1, 3, 5, 7]), shield: "vesicaPiscis"},
{name:"Vietic", base: 29, odd:.1, sort: i => n(i) / td(i, 25) / bd(i, [7], 7) / t[i], shield:"banner"}, {name: "Vietic", base: 29, odd: 0.1, sort: i => n(i) / td(i, 25) / bd(i, [7], 7) / t[i], shield: "banner"},
{name:"Guantzu", base:30, odd:.1, sort: i => n(i) / td(i, 17), shield:"banner"}, {name: "Guantzu", base: 30, odd: 0.1, sort: i => n(i) / td(i, 17), shield: "banner"},
{name:"Ulus", base:31, odd:.1, sort: i => n(i) / td(i, 5) / bd(i, [2, 4, 10], 7) * t[i], shield:"banner"} {name: "Ulus", base: 31, odd: 0.1, sort: i => (n(i) / td(i, 5) / bd(i, [2, 4, 10], 7)) * t[i], shield: "banner"}
]; ];
} };
// 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; cells = pack.cells;
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) { pack.cultures.forEach(function (c) {
if (!c.i || c.removed) return; if (!c.i || c.removed) return;
queue.queue({e:c.center, p:0, c:c.i}); 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 neutral = (cells.i.length / 5000) * 3000 * neutralInput.value; // limit cost for culture growth
const cost = []; const cost = [];
while (queue.length) { while (queue.length) {
const next = queue.dequeue(), n = next.e, p = next.p, c = next.c; const next = queue.dequeue(),
n = next.e,
p = next.p,
c = next.c;
const type = pack.cultures[c].type; const type = pack.cultures[c].type;
cells.c[n].forEach(function(e) { cells.c[n].forEach(function (e) {
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[n] ? 0 : 20; // penalty on biome change
@ -375,13 +402,13 @@
if (!cost[e] || totalCost < cost[e]) { if (!cost[e] || totalCost < cost[e]) {
if (cells.s[e] > 0) cells.culture[e] = c; // assign culture to populated cell if (cells.s[e] > 0) cells.culture[e] = c; // assign culture to populated cell
cost[e] = totalCost; cost[e] = totalCost;
queue.queue({e, p:totalCost, c}); queue.queue({e, p: totalCost, c});
} }
}); });
} }
TIME && console.timeEnd('expandCultures'); TIME && console.timeEnd("expandCultures");
} };
function getBiomeCost(c, biome, type) { function getBiomeCost(c, biome, type) {
if (cells.biome[pack.cultures[c].center] === biome) return 10; // tiny penalty for native biome if (cells.biome[pack.cultures[c].center] === biome) return 10; // tiny penalty for native biome
@ -391,7 +418,8 @@
} }
function getHeightCost(i, h, type) { function getHeightCost(i, h, type) {
const f = pack.features[cells.f[i]], a = cells.area[i]; const f = pack.features[cells.f[i]],
a = cells.area[i];
if (type === "Lake" && f.type === "lake") return 10; // no lake crossing penalty for Lake cultures if (type === "Lake" && f.type === "lake") return 10; // no lake crossing penalty for Lake cultures
if (type === "Naval" && h < 20) return a * 2; // low sea/lake crossing penalty for Naval cultures if (type === "Naval" && h < 20) return a * 2; // low sea/lake crossing penalty for Naval cultures
if (type === "Nomadic" && h < 20) return a * 50; // giant sea/lake crossing penalty for Nomads if (type === "Nomadic" && h < 20) return a * 50; // giant sea/lake crossing penalty for Nomads
@ -407,21 +435,20 @@
function getRiverCost(r, i, type) { function getRiverCost(r, i, type) {
if (type === "River") return r ? 0 : 100; // penalty for river cultures if (type === "River") return r ? 0 : 100; // penalty for river cultures
if (!r) return 0; // no penalty for others if there is no river if (!r) return 0; // no penalty for others if there is no river
return Math.min(Math.max(cells.fl[i] / 10, 20), 100) // river penalty from 20 to 100 based on flux return Math.min(Math.max(cells.fl[i] / 10, 20), 100); // river penalty from 20 to 100 based on flux
} }
function getTypeCost(t, type) { function getTypeCost(t, type) {
if (t === 1) return type === "Naval" || type === "Lake" ? 0 : type === "Nomadic" ? 60 : 20; // penalty for coastline if (t === 1) return type === "Naval" || type === "Lake" ? 0 : type === "Nomadic" ? 60 : 20; // penalty for coastline
if (t === 2) return type === "Naval" || type === "Nomadic" ? 30 : 0; // low penalty for land level 2 for Navals and nomads if (t === 2) return type === "Naval" || type === "Nomadic" ? 30 : 0; // low penalty for land level 2 for Navals and nomads
if (t !== -1) return type === "Naval" || type === "Lake" ? 100 : 0; // penalty for mainland for navals if (t !== -1) return type === "Naval" || type === "Lake" ? 100 : 0; // penalty for mainland for navals
return 0; return 0;
} }
const getRandomShield = function() { const getRandomShield = function () {
const type = rw(COA.shields.types); const type = rw(COA.shields.types);
return rw(COA.shields[type]); return rw(COA.shields[type]);
} };
return {generate, add, expand, getDefault, getRandomShield}; return {generate, add, expand, getDefault, getRandomShield};
})();
})));

View file

@ -1,8 +1,6 @@
(function (global, factory) { "use strict";
typeof exports === "object" && typeof module !== "undefined" ? (module.exports = factory()) : typeof define === "function" && define.amd ? define(factory) : (global.HeightmapGenerator = factory());
})(this, function () {
"use strict";
window.HeightmapGenerator = (function () {
let cells, p; let cells, p;
const generate = function () { const generate = function () {
@ -12,262 +10,63 @@
cells.h = new Uint8Array(grid.points.length); cells.h = new Uint8Array(grid.points.length);
const template = document.getElementById("templateInput").value; const template = document.getElementById("templateInput").value;
switch (template) { const templateString = HeightmapTemplates[template];
case "Volcano": const steps = templateString.split("\n");
templateVolcano();
break; if (!steps.length) throw new Error(`Heightmap template: no steps. Template: ${template}. Steps: ${steps}`);
case "High Island":
templateHighIsland(); for (const step of steps) {
break; const elements = step.trim().split(" ");
case "Low Island": if (elements.length < 2) throw new Error(`Heightmap template: steps < 2. Template: ${template}. Step: ${elements}`);
templateLowIsland(); addStep(...elements);
break;
case "Continents":
templateContinents();
break;
case "Archipelago":
templateArchipelago();
break;
case "Atoll":
templateAtoll();
break;
case "Mediterranean":
templateMediterranean();
break;
case "Peninsula":
templatePeninsula();
break;
case "Pangea":
templatePangea();
break;
case "Isthmus":
templateIsthmus();
break;
case "Shattered":
templateShattered();
break;
} }
TIME && console.timeEnd("generateHeightmap"); TIME && console.timeEnd("generateHeightmap");
}; };
// parse template step
function addStep(a1, a2, a3, a4, a5) { function addStep(a1, a2, a3, a4, a5) {
if (a1 === "Hill") return addHill(a2, a3, a4, a5); if (a1 === "Hill") return addHill(a2, a3, a4, a5);
if (a1 === "Pit") return addPit(a2, a3, a4, a5); if (a1 === "Pit") return addPit(a2, a3, a4, a5);
if (a1 === "Range") return addRange(a2, a3, a4, a5); if (a1 === "Range") return addRange(a2, a3, a4, a5);
if (a1 === "Trough") return addTrough(a2, a3, a4, a5); if (a1 === "Trough") return addTrough(a2, a3, a4, a5);
if (a1 === "Strait") return addStrait(a2, a3); if (a1 === "Strait") return addStrait(a2, a3);
if (a1 === "Add") return modify(a3, a2, 1); if (a1 === "Add") return modify(a3, +a2, 1);
if (a1 === "Multiply") return modify(a3, 0, a2); if (a1 === "Multiply") return modify(a3, 0, +a2);
if (a1 === "Smooth") return smooth(a2); if (a1 === "Smooth") return smooth(a2);
} }
// Heighmap Template: Volcano
function templateVolcano() {
addStep("Hill", "1", "90-100", "44-56", "40-60");
addStep("Multiply", 0.8, "50-100");
addStep("Range", "1.5", "30-55", "45-55", "40-60");
addStep("Smooth", 2);
addStep("Hill", "1.5", "25-35", "25-30", "20-75");
addStep("Hill", "1", "25-35", "75-80", "25-75");
addStep("Hill", "0.5", "20-25", "10-15", "20-25");
}
// Heighmap Template: High Island
function templateHighIsland() {
addStep("Hill", "1", "90-100", "65-75", "47-53");
addStep("Add", 5, "all");
addStep("Hill", "6", "20-23", "25-55", "45-55");
addStep("Range", "1", "40-50", "45-55", "45-55");
addStep("Smooth", 2);
addStep("Trough", "2-3", "20-30", "20-30", "20-30");
addStep("Trough", "2-3", "20-30", "60-80", "70-80");
addStep("Hill", "1", "10-15", "60-60", "50-50");
addStep("Hill", "1.5", "13-16", "15-20", "20-75");
addStep("Multiply", 0.8, "20-100");
addStep("Range", "1.5", "30-40", "15-85", "30-40");
addStep("Range", "1.5", "30-40", "15-85", "60-70");
addStep("Pit", "2-3", "10-15", "15-85", "20-80");
}
// Heighmap Template: Low Island
function templateLowIsland() {
addStep("Hill", "1", "90-99", "60-80", "45-55");
addStep("Hill", "4-5", "25-35", "20-65", "40-60");
addStep("Range", "1", "40-50", "45-55", "45-55");
addStep("Smooth", 3);
addStep("Trough", "1.5", "20-30", "15-85", "20-30");
addStep("Trough", "1.5", "20-30", "15-85", "70-80");
addStep("Hill", "1.5", "10-15", "5-15", "20-80");
addStep("Hill", "1", "10-15", "85-95", "70-80");
addStep("Pit", "3-5", "10-15", "15-85", "20-80");
addStep("Multiply", 0.4, "20-100");
}
// Heighmap Template: Continents
function templateContinents() {
addStep("Hill", "1", "80-85", "75-80", "40-60");
addStep("Hill", "1", "80-85", "20-25", "40-60");
addStep("Multiply", 0.22, "20-100");
addStep("Hill", "5-6", "15-20", "25-75", "20-82");
addStep("Range", ".8", "30-60", "5-15", "20-45");
addStep("Range", ".8", "30-60", "5-15", "55-80");
addStep("Range", "0-3", "30-60", "80-90", "20-80");
addStep("Trough", "3-4", "15-20", "15-85", "20-80");
addStep("Strait", "2", "vertical");
addStep("Smooth", 2);
addStep("Trough", "1-2", "5-10", "45-55", "45-55");
addStep("Pit", "3-4", "10-15", "15-85", "20-80");
addStep("Hill", "1", "5-10", "40-60", "40-60");
}
// Heighmap Template: Archipelago
function templateArchipelago() {
addStep("Add", 11, "all");
addStep("Range", "2-3", "40-60", "20-80", "20-80");
addStep("Hill", "5", "15-20", "10-90", "30-70");
addStep("Hill", "2", "10-15", "10-30", "20-80");
addStep("Hill", "2", "10-15", "60-90", "20-80");
addStep("Smooth", 3);
addStep("Trough", "10", "20-30", "5-95", "5-95");
addStep("Strait", "2", "vertical");
addStep("Strait", "2", "horizontal");
}
// Heighmap Template: Atoll
function templateAtoll() {
addStep("Hill", "1", "75-80", "50-60", "45-55");
addStep("Hill", "1.5", "30-50", "25-75", "30-70");
addStep("Hill", ".5", "30-50", "25-35", "30-70");
addStep("Smooth", 1);
addStep("Multiply", 0.2, "25-100");
addStep("Hill", ".5", "10-20", "50-55", "48-52");
}
// Heighmap Template: Mediterranean
function templateMediterranean() {
addStep("Range", "3-4", "30-50", "0-100", "0-10");
addStep("Range", "3-4", "30-50", "0-100", "90-100");
addStep("Hill", "5-6", "30-70", "0-100", "0-5");
addStep("Hill", "5-6", "30-70", "0-100", "95-100");
addStep("Smooth", 1);
addStep("Hill", "2-3", "30-70", "0-5", "20-80");
addStep("Hill", "2-3", "30-70", "95-100", "20-80");
addStep("Multiply", 0.8, "land");
addStep("Trough", "3-5", "40-50", "0-100", "0-10");
addStep("Trough", "3-5", "40-50", "0-100", "90-100");
}
// Heighmap Template: Peninsula
function templatePeninsula() {
addStep("Range", "2-3", "20-35", "40-50", "0-15");
addStep("Add", 5, "all");
addStep("Hill", "1", "90-100", "10-90", "0-5");
addStep("Add", 13, "all");
addStep("Hill", "3-4", "3-5", "5-95", "80-100");
addStep("Hill", "1-2", "3-5", "5-95", "40-60");
addStep("Trough", "5-6", "10-25", "5-95", "5-95");
addStep("Smooth", 3);
}
// Heighmap Template: Pangea
function templatePangea() {
addStep("Hill", "1-2", "25-40", "15-50", "0-10");
addStep("Hill", "1-2", "5-40", "50-85", "0-10");
addStep("Hill", "1-2", "25-40", "50-85", "90-100");
addStep("Hill", "1-2", "5-40", "15-50", "90-100");
addStep("Hill", "8-12", "20-40", "20-80", "48-52");
addStep("Smooth", 2);
addStep("Multiply", 0.7, "land");
addStep("Trough", "3-4", "25-35", "5-95", "10-20");
addStep("Trough", "3-4", "25-35", "5-95", "80-90");
addStep("Range", "5-6", "30-40", "10-90", "35-65");
}
// Heighmap Template: Isthmus
function templateIsthmus() {
addStep("Hill", "5-10", "15-30", "0-30", "0-20");
addStep("Hill", "5-10", "15-30", "10-50", "20-40");
addStep("Hill", "5-10", "15-30", "30-70", "40-60");
addStep("Hill", "5-10", "15-30", "50-90", "60-80");
addStep("Hill", "5-10", "15-30", "70-100", "80-100");
addStep("Smooth", 2);
addStep("Trough", "4-8", "15-30", "0-30", "0-20");
addStep("Trough", "4-8", "15-30", "10-50", "20-40");
addStep("Trough", "4-8", "15-30", "30-70", "40-60");
addStep("Trough", "4-8", "15-30", "50-90", "60-80");
addStep("Trough", "4-8", "15-30", "70-100", "80-100");
}
// Heighmap Template: Shattered
function templateShattered() {
addStep("Hill", "8", "35-40", "15-85", "30-70");
addStep("Trough", "10-20", "40-50", "5-95", "5-95");
addStep("Range", "5-7", "30-40", "10-90", "20-80");
addStep("Pit", "12-20", "30-40", "15-85", "20-80");
}
function getBlobPower() { function getBlobPower() {
switch (+pointsInput.dataset.cells) { const cells = +pointsInput.dataset.cells;
case 1000: if (cells === 1000) return 0.93;
return 0.93; if (cells === 2000) return 0.95;
case 2000: if (cells === 5000) return 0.96;
return 0.95; if (cells === 10000) return 0.98;
case 5000: if (cells === 20000) return 0.985;
return 0.96; if (cells === 30000) return 0.987;
case 10000: if (cells === 40000) return 0.9892;
return 0.98; if (cells === 50000) return 0.9911;
case 20000: if (cells === 60000) return 0.9921;
return 0.985; if (cells === 70000) return 0.9934;
case 30000: if (cells === 80000) return 0.9942;
return 0.987; if (cells === 90000) return 0.9946;
case 40000: if (cells === 100000) return 0.995;
return 0.9892;
case 50000:
return 0.9911;
case 60000:
return 0.9921;
case 70000:
return 0.9934;
case 80000:
return 0.9942;
case 90000:
return 0.9946;
case 100000:
return 0.995;
}
} }
function getLinePower() { function getLinePower() {
switch (+pointsInput.dataset.cells) { const cells = +pointsInput.dataset.cells;
case 1000: if (cells === 1000) return 0.74;
return 0.74; if (cells === 2000) return 0.75;
case 2000: if (cells === 5000) return 0.78;
return 0.75; if (cells === 10000) return 0.81;
case 5000: if (cells === 20000) return 0.82;
return 0.78; if (cells === 30000) return 0.83;
case 10000: if (cells === 40000) return 0.84;
return 0.81; if (cells === 50000) return 0.855;
case 20000: if (cells === 60000) return 0.87;
return 0.82; if (cells === 70000) return 0.885;
case 30000: if (cells === 80000) return 0.91;
return 0.83; if (cells === 90000) return 0.92;
case 40000: if (cells === 100000) return 0.93;
return 0.84;
case 50000:
return 0.855;
case 60000:
return 0.87;
case 70000:
return 0.885;
case 80000:
return 0.91;
case 90000:
return 0.92;
case 100000:
return 0.93;
}
} }
const addHill = function (count, height, rangeX, rangeY) { const addHill = function (count, height, rangeX, rangeY) {
@ -610,4 +409,4 @@
} }
return {generate, addHill, addRange, addTrough, addStrait, addPit, smooth, modify}; return {generate, addHill, addRange, addTrough, addStrait, addPit, smooth, modify};
}); })();

View file

@ -0,0 +1,123 @@
"use strict";
window.HeightmapTemplates = (function () {
const volcano = `Hill 1 90-100 44-56 40-60
Multiply 0.8 50-100 0 0
Range 1.5 30-55 45-55 40-60
Smooth 2 0 0 0
Hill 1.5 25-35 25-30 20-75
Hill 1 25-35 75-80 25-75
Hill 0.5 20-25 10-15 20-25`;
const highIsland = `Hill 1 90-100 65-75 47-53
Add 5 all 0 0
Hill 6 20-23 25-55 45-55
Range 1 40-50 45-55 45-55
Smooth 2 0 0 0
Trough 2-3 20-30 20-30 20-30
Trough 2-3 20-30 60-80 70-80
Hill 1 10-15 60-60 50-50
Hill 1.5 13-16 15-20 20-75
Multiply 0.8 20-100 0 0
Range 1.5 30-40 15-85 30-40
Range 1.5 30-40 15-85 60-70
Pit 2-3 10-15 15-85 20-80`;
const lowIsland = `Hill 1 90-99 60-80 45-55
Hill 4-5 25-35 20-65 40-60
Range 1 40-50 45-55 45-55
Smooth 3 0 0 0
Trough 1.5 20-30 15-85 20-30
Trough 1.5 20-30 15-85 70-80
Hill 1.5 10-15 5-15 20-80
Hill 1 10-15 85-95 70-80
Pit 3-5 10-15 15-85 20-80
Multiply 0.4 20-100 0 0`;
const continents = `Hill 1 80-85 75-80 40-60
Hill 1 80-85 20-25 40-60
Multiply 0.22 20-100 0 0
Hill 5-6 15-20 25-75 20-82
Range .8 30-60 5-15 20-45
Range .8 30-60 5-15 55-80
Range 0-3 30-60 80-90 20-80
Trough 3-4 15-20 15-85 20-80
Strait 2 vertical 0 0
Smooth 2 0 0 0
Trough 1-2 5-10 45-55 45-55
Pit 3-4 10-15 15-85 20-80
Hill 1 5-10 40-60 40-60`;
const archipelago = `Add 11 all 0 0
Range 2-3 40-60 20-80 20-80
Hill 5 15-20 10-90 30-70
Hill 2 10-15 10-30 20-80
Hill 2 10-15 60-90 20-80
Smooth 3 0 0 0
Trough 10 20-30 5-95 5-95
Strait 2 vertical 0 0
Strait 2 horizontal 0 0`;
const atoll = `Hill 1 75-80 50-60 45-55
Hill 1.5 30-50 25-75 30-70
Hill .5 30-50 25-35 30-70
Smooth 1 0 0 0
Multiply 0.2 25-100 0 0
Hill .5 10-20 50-55 48-52`;
const mediterranean = `Range 3-4 30-50 0-100 0-10
Range 3-4 30-50 0-100 90-100
Hill 5-6 30-70 0-100 0-5
Hill 5-6 30-70 0-100 95-100
Smooth 1 0 0 0
Hill 2-3 30-70 0-5 20-80
Hill 2-3 30-70 95-100 20-80
Multiply 0.8 land 0 0
Trough 3-5 40-50 0-100 0-10
Trough 3-5 40-50 0-100 90-100`;
const peninsula = `Range 2-3 20-35 40-50 0-15
Add 5 all 0 0
Hill 1 90-100 10-90 0-5
Add 13 all 0 0
Hill 3-4 3-5 5-95 80-100
Hill 1-2 3-5 5-95 40-60
Trough 5-6 10-25 5-95 5-95
Smooth 3 0 0 0`;
const pangea = `Hill 1-2 25-40 15-50 0-10
Hill 1-2 5-40 50-85 0-10
Hill 1-2 25-40 50-85 90-100
Hill 1-2 5-40 15-50 90-100
Hill 8-12 20-40 20-80 48-52
Smooth 2 0 0 0
Multiply 0.7 land 0 0
Trough 3-4 25-35 5-95 10-20
Trough 3-4 25-35 5-95 80-90
Range 5-6 30-40 10-90 35-65`;
const isthmus = `Hill 5-10 15-30 0-30 0-20
Hill 5-10 15-30 10-50 20-40
Hill 5-10 15-30 30-70 40-60
Hill 5-10 15-30 50-90 60-80
Hill 5-10 15-30 70-100 80-100
Smooth 2 0 0 0
Trough 4-8 15-30 0-30 0-20
Trough 4-8 15-30 10-50 20-40
Trough 4-8 15-30 30-70 40-60
Trough 4-8 15-30 50-90 60-80
Trough 4-8 15-30 70-100 80-100`;
const shattered = `Hill 8 35-40 15-85 30-70
Trough 10-20 40-50 5-95 5-95
Range 5-7 30-40 10-90 20-80
Pit 12-20 30-40 15-85 20-80`;
const taklamakan = `Hill 1-3 20-30 30-70 30-70
Hill 2-4 60-85 0-5 0-100
Hill 2-4 60-85 95-100 0-100
Hill 3-4 60-85 20-80 0-5
Hill 3-4 60-85 20-80 95-100`;
return {volcano, highIsland, lowIsland, continents, archipelago, atoll, mediterranean, peninsula, peninsula, pangea, isthmus, shattered, taklamakan};
})();

View file

@ -1,8 +1,6 @@
(function (global, factory) { "use strict";
typeof exports === "object" && typeof module !== "undefined" ? (module.exports = factory()) : typeof define === "function" && define.amd ? define(factory) : (global.Lakes = factory());
})(this, function () {
"use strict";
window.Lakes = (function () {
const setClimateData = function (h) { const setClimateData = function (h) {
const cells = pack.cells; const cells = pack.cells;
const lakeOutCells = new Uint16Array(cells.i.length); const lakeOutCells = new Uint16Array(cells.i.length);
@ -149,4 +147,4 @@
} }
return {setClimateData, cleanupLakeData, prepareLakeData, defineGroup, generateName, getName, getShoreline}; return {setClimateData, cleanupLakeData, prepareLakeData, defineGroup, generateName, getName, getShoreline};
}); })();

View file

@ -142,6 +142,7 @@ function parseLoadedData(data) {
if (settings[20]) mapName.value = settings[20]; if (settings[20]) mapName.value = settings[20];
if (settings[21]) hideLabels.checked = +settings[21]; if (settings[21]) hideLabels.checked = +settings[21];
if (settings[22]) stylePreset.value = settings[22]; if (settings[22]) stylePreset.value = settings[22];
if (settings[23]) rescaleLabels.checked = settings[23];
})(); })();
void (function parseConfiguration() { void (function parseConfiguration() {

View file

@ -1,8 +1,6 @@
(function (global, factory) { "use strict";
typeof exports === "object" && typeof module !== "undefined" ? (module.exports = factory()) : typeof define === "function" && define.amd ? define(factory) : (global.Military = factory());
})(this, function () {
"use strict";
window.Military = (function () {
const generate = function () { const generate = function () {
TIME && console.time("generateMilitaryForces"); TIME && console.time("generateMilitaryForces");
const cells = pack.cells, const cells = pack.cells,
@ -371,4 +369,4 @@
}; };
return {generate, getDefaultOptions, getName, generateNote, drawRegiments, drawRegiment, moveRegiment, getTotal, getEmblem}; return {generate, getDefaultOptions, getName, generateNote, drawRegiments, drawRegiment, moveRegiment, getTotal, getEmblem};
}); })();

View file

@ -1,7 +1,6 @@
(function (global, factory) { "use strict";
typeof exports === "object" && typeof module !== "undefined" ? (module.exports = factory()) : typeof define === "function" && define.amd ? define(factory) : (global.Names = factory());
})(this, function () { window.Names = (function () {
"use strict";
let chains = []; let chains = [];
// calculate Markov chain for a namesbase // calculate Markov chain for a namesbase
@ -294,4 +293,4 @@
}; };
return {getBase, getCulture, getCultureShort, getBaseShort, getState, updateChain, clearChains, getNameBases, getMapName, calculateChain}; return {getBase, getCulture, getCultureShort, getBaseShort, getState, updateChain, clearChains, getNameBases, getMapName, calculateChain};
}); })();

View file

@ -1,8 +1,6 @@
(function (global, factory) { "use strict";
typeof exports === "object" && typeof module !== "undefined" ? (module.exports = factory()) : typeof define === "function" && define.amd ? define(factory) : (global.OceanLayers = factory());
})(this, function () {
"use strict";
window.OceanLayers = (function () {
let cells, vertices, pointsN, used; let cells, vertices, pointsN, used;
const OceanLayers = function OceanLayers() { const OceanLayers = function OceanLayers() {
@ -91,4 +89,4 @@
} }
return OceanLayers; return OceanLayers;
}); })();

View file

@ -1,8 +1,6 @@
(function (global, factory) { "use strict";
typeof exports === "object" && typeof module !== "undefined" ? (module.exports = factory()) : typeof define === "function" && define.amd ? define(factory) : (global.ReliefIcons = factory());
})(this, function () {
"use strict";
window.ReliefIcons = (function () {
const ReliefIcons = function () { const ReliefIcons = function () {
TIME && console.time("drawRelief"); TIME && console.time("drawRelief");
terrain.selectAll("*").remove(); terrain.selectAll("*").remove();
@ -127,4 +125,4 @@
} }
return ReliefIcons; return ReliefIcons;
}); })();

View file

@ -1,19 +1,13 @@
(function (global, factory) { "use strict";
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.Religions = factory());
}(this, (function () {'use strict';
window.Religions = (function () {
// name generation approach and relative chance to be selected // name generation approach and relative chance to be selected
const approach = {"Number":1, "Being":3, "Adjective":5, "Color + Animal":5, const approach = {Number: 1, Being: 3, Adjective: 5, "Color + Animal": 5, "Adjective + Animal": 5, "Adjective + Being": 5, "Adjective + Genitive": 1, "Color + Being": 3, "Color + Genitive": 3, "Being + of + Genitive": 2, "Being + of the + Genitive": 1, "Animal + of + Genitive": 1, "Adjective + Being + of + Genitive": 2, "Adjective + Animal + of + Genitive": 2};
"Adjective + Animal":5, "Adjective + Being":5, "Adjective + Genitive":1,
"Color + Being":3, "Color + Genitive":3, "Being + of + Genitive":2, "Being + of the + Genitive":1,
"Animal + of + Genitive":1, "Adjective + Being + of + Genitive":2, "Adjective + Animal + of + Genitive":2};
// turn weighted array into simple array // turn weighted array into simple array
const approaches = []; const approaches = [];
for (const a in approach) { for (const a in approach) {
for (let j=0; j < approach[a]; j++) { for (let j = 0; j < approach[a]; j++) {
approaches.push(a); approaches.push(a);
} }
} }
@ -21,7 +15,7 @@
const base = { const base = {
number: ["One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve"], number: ["One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve"],
being: ["God", "Goddess", "Lord", "Lady", "Deity", "Creator", "Maker", "Overlord", "Ruler", "Chief", "Master", "Spirit", "Ancestor", "Father", "Forebear", "Forefather", "Mother", "Brother", "Sister", "Elder", "Numen", "Ancient", "Virgin", "Giver", "Council", "Guardian", "Reaper"], being: ["God", "Goddess", "Lord", "Lady", "Deity", "Creator", "Maker", "Overlord", "Ruler", "Chief", "Master", "Spirit", "Ancestor", "Father", "Forebear", "Forefather", "Mother", "Brother", "Sister", "Elder", "Numen", "Ancient", "Virgin", "Giver", "Council", "Guardian", "Reaper"],
animal: ["Dragon", "Wyvern", "Phoenix", "Unicorn", "Sphinx", "Centaur", "Pegasus", "Kraken", "Basilisk", "Chimera", "Cyclope", "Antelope", "Ape", "Badger", "Bear", "Beaver", "Bison", "Boar", "Buffalo", "Cat", "Cobra", "Crane", "Crocodile", "Crow", "Deer", "Dog", "Eagle", "Elk", "Fox", "Goat", "Goose", "Hare", "Hawk", "Heron", "Horse", "Hyena", "Ibis", "Jackal", "Jaguar", "Lark", "Leopard", "Lion", "Mantis", "Marten", "Moose", "Mule", "Narwhal", "Owl", "Panther", "Rat", "Raven", "Rook", "Scorpion", "Shark", "Sheep", "Snake", "Spider", "Swan", "Tiger", "Turtle", "Viper", "Vulture", "Walrus", "Wolf", "Wolverine", "Worm", "Camel", "Falcon", "Hound", "Ox", "Serpent"], animal: ["Dragon", "Wyvern", "Phoenix", "Unicorn", "Sphinx", "Centaur", "Pegasus", "Kraken", "Basilisk", "Chimera", "Cyclope", "Antelope", "Ape", "Badger", "Bear", "Beaver", "Bison", "Boar", "Buffalo", "Cat", "Cobra", "Crane", "Crocodile", "Crow", "Deer", "Dog", "Eagle", "Elk", "Fox", "Goat", "Goose", "Hare", "Hawk", "Heron", "Horse", "Hyena", "Ibis", "Jackal", "Jaguar", "Lark", "Leopard", "Lion", "Mantis", "Marten", "Moose", "Mule", "Narwhal", "Owl", "Panther", "Rat", "Raven", "Rook", "Scorpion", "Shark", "Sheep", "Snake", "Spider", "Swan", "Tiger", "Turtle", "Viper", "Vulture", "Walrus", "Wolf", "Wolverine", "Worm", "Camel", "Falcon", "Hound", "Ox", "Serpent"],
adjective: ["New", "Good", "High", "Old", "Great", "Big", "Young", "Major", "Strong", "Happy", "Last", "Main", "Huge", "Far", "Beautiful", "Wild", "Fair", "Prime", "Crazy", "Ancient", "Proud", "Secret", "Lucky", "Sad", "Silent", "Latter", "Severe", "Fat", "Holy", "Pure", "Aggressive", "Honest", "Giant", "Mad", "Pregnant", "Distant", "Lost", "Broken", "Blind", "Friendly", "Unknown", "Sleeping", "Slumbering", "Loud", "Hungry", "Wise", "Worried", "Sacred", "Magical", "Superior", "Patient", "Dead", "Deadly", "Peaceful", "Grateful", "Frozen", "Evil", "Scary", "Burning", "Divine", "Bloody", "Dying", "Waking", "Brutal", "Unhappy", "Calm", "Cruel", "Favorable", "Blond", "Explicit", "Disturbing", "Devastating", "Brave", "Sunny", "Troubled", "Flying", "Sustainable", "Marine", "Fatal", "Inherent", "Selected", "Naval", "Cheerful", "Almighty", "Benevolent", "Eternal", "Immutable", "Infallible"], adjective: ["New", "Good", "High", "Old", "Great", "Big", "Young", "Major", "Strong", "Happy", "Last", "Main", "Huge", "Far", "Beautiful", "Wild", "Fair", "Prime", "Crazy", "Ancient", "Proud", "Secret", "Lucky", "Sad", "Silent", "Latter", "Severe", "Fat", "Holy", "Pure", "Aggressive", "Honest", "Giant", "Mad", "Pregnant", "Distant", "Lost", "Broken", "Blind", "Friendly", "Unknown", "Sleeping", "Slumbering", "Loud", "Hungry", "Wise", "Worried", "Sacred", "Magical", "Superior", "Patient", "Dead", "Deadly", "Peaceful", "Grateful", "Frozen", "Evil", "Scary", "Burning", "Divine", "Bloody", "Dying", "Waking", "Brutal", "Unhappy", "Calm", "Cruel", "Favorable", "Blond", "Explicit", "Disturbing", "Devastating", "Brave", "Sunny", "Troubled", "Flying", "Sustainable", "Marine", "Fatal", "Inherent", "Selected", "Naval", "Cheerful", "Almighty", "Benevolent", "Eternal", "Immutable", "Infallible"],
genitive: ["Day", "Life", "Death", "Night", "Home", "Fog", "Snow", "Winter", "Summer", "Cold", "Springs", "Gates", "Nature", "Thunder", "Lightning", "War", "Ice", "Frost", "Fire", "Doom", "Fate", "Pain", "Heaven", "Justice", "Light", "Love", "Time", "Victory"], genitive: ["Day", "Life", "Death", "Night", "Home", "Fog", "Snow", "Winter", "Summer", "Cold", "Springs", "Gates", "Nature", "Thunder", "Lightning", "War", "Ice", "Frost", "Fire", "Doom", "Fate", "Pain", "Heaven", "Justice", "Light", "Love", "Time", "Victory"],
theGenitive: ["World", "Word", "South", "West", "North", "East", "Sun", "Moon", "Peak", "Fall", "Dawn", "Eclipse", "Abyss", "Blood", "Tree", "Earth", "Harvest", "Rainbow", "Sea", "Sky", "Stars", "Storm", "Underworld", "Wild"], theGenitive: ["World", "Word", "South", "West", "North", "East", "Sun", "Moon", "Peak", "Fall", "Dawn", "Eclipse", "Abyss", "Blood", "Tree", "Earth", "Harvest", "Rainbow", "Sea", "Sky", "Stars", "Storm", "Underworld", "Wild"],
@ -29,64 +23,70 @@
}; };
const forms = { const forms = {
Folk:{"Shamanism":2, "Animism":2, "Ancestor worship":1, "Polytheism":2}, Folk: {Shamanism: 2, Animism: 2, "Ancestor worship": 1, Polytheism: 2},
Organized:{"Polytheism":5, "Dualism":1, "Monotheism":4, "Non-theism":1}, Organized: {Polytheism: 5, Dualism: 1, Monotheism: 4, "Non-theism": 1},
Cult:{"Cult":1, "Dark Cult":1}, Cult: {Cult: 1, "Dark Cult": 1},
Heresy:{"Heresy":1} Heresy: {Heresy: 1}
}; };
const methods = {"Random + type":3, "Random + ism":1, "Supreme + ism":5, "Faith of + Supreme":5, "Place + ism":1, "Culture + ism":2, "Place + ian + type":6, "Culture + type":4}; const methods = {"Random + type": 3, "Random + ism": 1, "Supreme + ism": 5, "Faith of + Supreme": 5, "Place + ism": 1, "Culture + ism": 2, "Place + ian + type": 6, "Culture + type": 4};
const types = { const types = {
"Shamanism":{"Beliefs":3, "Shamanism":2, "Spirits":1}, Shamanism: {Beliefs: 3, Shamanism: 2, Spirits: 1},
"Animism":{"Spirits":1, "Beliefs":1}, Animism: {Spirits: 1, Beliefs: 1},
"Ancestor worship":{"Beliefs":1, "Forefathers":2, "Ancestors":2}, "Ancestor worship": {Beliefs: 1, Forefathers: 2, Ancestors: 2},
"Polytheism":{"Deities":3, "Faith":1, "Gods":1, "Pantheon":1}, Polytheism: {Deities: 3, Faith: 1, Gods: 1, Pantheon: 1},
"Dualism":{"Religion":3, "Faith":1, "Cult":1}, Dualism: {Religion: 3, Faith: 1, Cult: 1},
"Monotheism":{"Religion":1, "Church":1}, Monotheism: {Religion: 1, Church: 1},
"Non-theism":{"Beliefs":3, "Spirits":1}, "Non-theism": {Beliefs: 3, Spirits: 1},
"Cult":{"Cult":4, "Sect":4, "Worship":1, "Orden":1, "Coterie":1, "Arcanum":1}, Cult: {Cult: 4, Sect: 4, Worship: 1, Orden: 1, Coterie: 1, Arcanum: 1},
"Dark Cult":{"Cult":2, "Sect":2, "Occultism":1, "Idols":1, "Coven":1, "Circle":1, "Blasphemy":1}, "Dark Cult": {Cult: 2, Sect: 2, Occultism: 1, Idols: 1, Coven: 1, Circle: 1, Blasphemy: 1},
"Heresy":{"Heresy":3, "Sect":2, "Schism":1, "Dissenters":1, "Circle":1, "Brotherhood":1, "Society":1, "Iconoclasm":1, "Dissent":1, "Apostates":1} Heresy: {Heresy: 3, Sect: 2, Schism: 1, Dissenters: 1, Circle: 1, Brotherhood: 1, Society: 1, Iconoclasm: 1, Dissent: 1, Apostates: 1}
}; };
const generate = function() { const generate = function () {
TIME && console.time('generateReligions'); TIME && console.time("generateReligions");
const cells = pack.cells, states = pack.states, cultures = pack.cultures; const cells = pack.cells,
const religions = pack.religions = []; states = pack.states,
cultures = pack.cultures;
const religions = (pack.religions = []);
cells.religion = new Uint16Array(cells.culture); // cell religion; initially based on culture cells.religion = new Uint16Array(cells.culture); // cell religion; initially based on culture
// add folk religions // add folk religions
pack.cultures.forEach(c => { pack.cultures.forEach(c => {
if (!c.i) {religions.push({i: 0, name: "No religion"}); return;} if (!c.i) {
if (c.removed) {religions.push({i: c.i, name: "Extinct religion for "+c.name, color:getMixedColor(c.color, .1, 0), removed:true}); return;} religions.push({i: 0, name: "No religion"});
return;
}
if (c.removed) {
religions.push({i: c.i, name: "Extinct religion for " + c.name, color: getMixedColor(c.color, 0.1, 0), removed: true});
return;
}
const form = rw(forms.Folk); const form = rw(forms.Folk);
const name = c.name + " " + rw(types[form]); const name = c.name + " " + rw(types[form]);
const deity = form === "Animism" ? null : getDeityName(c.i); const deity = form === "Animism" ? null : getDeityName(c.i);
const color = getMixedColor(c.color, .1, 0); // `url(#hatch${rand(8,13)})`; const color = getMixedColor(c.color, 0.1, 0); // `url(#hatch${rand(8,13)})`;
religions.push({i: c.i, name, color, culture: c.i, type:"Folk", form, deity, center: c.center, origin:0}); religions.push({i: c.i, name, color, culture: c.i, type: "Folk", form, deity, center: c.center, origin: 0});
}); });
if (religionsInput.value == 0 || pack.cultures.length < 2) { if (religionsInput.value == 0 || pack.cultures.length < 2) {
religions.filter(r => r.i).forEach(r => r.code = abbreviate(r.name)); religions.filter(r => r.i).forEach(r => (r.code = abbreviate(r.name)));
return; return;
} }
const burgs = pack.burgs.filter(b => b.i && !b.removed); const burgs = pack.burgs.filter(b => b.i && !b.removed);
const sorted = burgs.length > +religionsInput.value 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]);
? burgs.sort((a, b) => b.population - a.population).map(b => b.cell)
: cells.i.filter(i => cells.s[i] > 2).sort((a, b) => cells.s[b] - cells.s[a]);
const religionsTree = d3.quadtree(); const religionsTree = d3.quadtree();
const spacing = (graphWidth + graphHeight) / 6 / religionsInput.value; // base min distance between towns const spacing = (graphWidth + graphHeight) / 6 / religionsInput.value; // base min distance between towns
const cultsCount = Math.floor(rand(10, 40) / 100 * religionsInput.value); const cultsCount = Math.floor((rand(10, 40) / 100) * religionsInput.value);
const count = +religionsInput.value - cultsCount + religions.length; const count = +religionsInput.value - cultsCount + religions.length;
// generate organized religions // generate organized religions
for (let i=0; religions.length < count && i < 1000; i++) { for (let i = 0; religions.length < count && i < 1000; i++) {
let center = sorted[biased(0, sorted.length-1, 5)]; // religion center let center = sorted[biased(0, sorted.length - 1, 5)]; // religion center
const form = rw(forms.Organized); const form = rw(forms.Organized);
const state = cells.state[center]; const state = cells.state[center];
const culture = cells.culture[center]; const culture = cells.culture[center];
@ -96,34 +96,36 @@
if (expansion === "state" && !state) expansion = "global"; if (expansion === "state" && !state) expansion = "global";
if (expansion === "culture" && !culture) expansion = "global"; if (expansion === "culture" && !culture) expansion = "global";
if (expansion === "state" && Math.random() > .5) center = states[state].center; if (expansion === "state" && Math.random() > 0.5) center = states[state].center;
if (expansion === "culture" && Math.random() > .5) center = cultures[culture].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]); if (!cells.burg[center] && cells.c[center].some(c => cells.burg[c])) center = cells.c[center].find(c => cells.burg[c]);
const x = cells.p[center][0], y = cells.p[center][1]; const x = cells.p[center][0],
y = cells.p[center][1];
const s = spacing * gauss(1, .3, .2, 2, 2); // randomize to make the placement not uniform 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 if (religionsTree.find(x, y, s) !== undefined) continue; // to close to existing religion
// add "Old" to name of the folk religion on this culture // add "Old" to name of the folk religion on this culture
const folk = religions.find(r => r.culture === culture && r.type === "Folk"); const folk = religions.find(r => r.culture === culture && r.type === "Folk");
if (folk && expansion === "culture" && folk.name.slice(0,3) !== "Old") folk.name = "Old " + folk.name; if (folk && expansion === "culture" && folk.name.slice(0, 3) !== "Old") folk.name = "Old " + folk.name;
const origin = folk ? folk.i : 0; const origin = folk ? folk.i : 0;
const expansionism = rand(3, 8); const expansionism = rand(3, 8);
const color = getMixedColor(religions[origin].color, .3, 0); // `url(#hatch${rand(0,5)})`; const color = getMixedColor(religions[origin].color, 0.3, 0); // `url(#hatch${rand(0,5)})`;
religions.push({i: religions.length, name, color, culture, type:"Organized", form, deity, expansion, expansionism, center, origin}); religions.push({i: religions.length, name, color, culture, type: "Organized", form, deity, expansion, expansionism, center, origin});
religionsTree.add([x, y]); religionsTree.add([x, y]);
} }
// generate cults // generate cults
for (let i=0; religions.length < count + cultsCount && i < 1000; i++) { for (let i = 0; religions.length < count + cultsCount && i < 1000; i++) {
const form = rw(forms.Cult); const form = rw(forms.Cult);
let center = sorted[biased(0, sorted.length-1, 1)]; // religion center 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]); if (!cells.burg[center] && cells.c[center].some(c => cells.burg[c])) center = cells.c[center].find(c => cells.burg[c]);
const x = cells.p[center][0], y = cells.p[center][1]; const x = cells.p[center][0],
y = cells.p[center][1];
const s = spacing * gauss(2, .3, 1, 3, 2); // randomize to make the placement not uniform 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 if (religionsTree.find(x, y, s) !== undefined) continue; // to close to existing religion
const culture = cells.culture[center]; const culture = cells.culture[center];
@ -131,9 +133,9 @@
const origin = folk ? folk.i : 0; const origin = folk ? folk.i : 0;
const deity = getDeityName(culture); const deity = getDeityName(culture);
const name = getCultName(form, center); const name = getCultName(form, center);
const expansionism = gauss(1.1, .5, 0, 5); const expansionism = gauss(1.1, 0.5, 0, 5);
const color = getMixedColor(cultures[culture].color, .5, 0); // "url(#hatch7)"; 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, origin}); religions.push({i: religions.length, name, color, culture, type: "Cult", form, deity, expansion: "global", expansionism, center, origin});
religionsTree.add([x, y]); religionsTree.add([x, y]);
//debug.append("circle").attr("cx", x).attr("cy", y).attr("r", 2).attr("fill", "red"); //debug.append("circle").attr("cx", x).attr("cy", y).attr("r", 2).attr("fill", "red");
} }
@ -141,72 +143,90 @@
expandReligions(); expandReligions();
// generate heresies // generate heresies
religions.filter(r => r.type === "Organized").forEach(r => { religions
if (r.expansionism < 3) return; .filter(r => r.type === "Organized")
const count = gauss(0, 1, 0, 3); .forEach(r => {
for (let i=0; i < count; i++) { if (r.expansionism < 3) return;
let center = ra(cells.i.filter(i => cells.religion[i] === r.i && cells.c[i].some(c => cells.religion[c] !== r.i))); const count = gauss(0, 1, 0, 3);
if (!center) continue; for (let i = 0; i < count; i++) {
if (!cells.burg[center] && cells.c[center].some(c => cells.burg[c])) center = cells.c[center].find(c => cells.burg[c]); let center = ra(cells.i.filter(i => cells.religion[i] === r.i && cells.c[i].some(c => cells.religion[c] !== r.i)));
const x = cells.p[center][0], y = cells.p[center][1]; if (!center) continue;
if (religionsTree.find(x, y, spacing / 10) !== undefined) continue; // to close to other if (!cells.burg[center] && cells.c[center].some(c => cells.burg[c])) center = cells.c[center].find(c => cells.burg[c]);
const x = cells.p[center][0],
y = cells.p[center][1];
if (religionsTree.find(x, y, spacing / 10) !== undefined) continue; // to close to other
const culture = cells.culture[center]; const culture = cells.culture[center];
const name = getCultName("Heresy", center); const name = getCultName("Heresy", center);
const expansionism = gauss(1.2, .5, 0, 5); const expansionism = gauss(1.2, 0.5, 0, 5);
const color = getMixedColor(r.color, .4, .2); // "url(#hatch6)"; const color = getMixedColor(r.color, 0.4, 0.2); // "url(#hatch6)";
religions.push({i: religions.length, name, color, culture, type:"Heresy", form:r.form, deity: r.deity, expansion:"global", expansionism, center, origin:r.i}); religions.push({i: religions.length, name, color, culture, type: "Heresy", form: r.form, deity: r.deity, expansion: "global", expansionism, center, origin: r.i});
religionsTree.add([x, y]); religionsTree.add([x, y]);
//debug.append("circle").attr("cx", x).attr("cy", y).attr("r", 2).attr("fill", "green"); //debug.append("circle").attr("cx", x).attr("cy", y).attr("r", 2).attr("fill", "green");
} }
}); });
expandHeresies(); expandHeresies();
checkCenters(); checkCenters();
TIME && console.timeEnd('generateReligions'); TIME && console.timeEnd("generateReligions");
} };
const add = function(center) { const add = function (center) {
const cells = pack.cells, religions = pack.religions; const cells = pack.cells,
religions = pack.religions;
const r = cells.religion[center]; const r = cells.religion[center];
const i = religions.length; const i = religions.length;
const culture = cells.culture[center]; const culture = cells.culture[center];
const color = getMixedColor(religions[r].color, .3, 0); const color = getMixedColor(religions[r].color, 0.3, 0);
const type = religions[r].type === "Organized" ? rw({Organized:4, Cult:1, Heresy:2}) : rw({Organized:5, Cult:2}); const type = religions[r].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 = type === "Heresy" ? religions[r].deity : form === "Non-theism" ? null : getDeityName(culture); const deity = type === "Heresy" ? religions[r].deity : form === "Non-theism" ? null : getDeityName(culture);
let name, expansion; let name, expansion;
if (type === "Organized") [name, expansion] = getReligionName(form, deity, center) if (type === "Organized") [name, expansion] = getReligionName(form, deity, center);
else {name = getCultName(form, center); expansion = "global";} else {
name = getCultName(form, center);
expansion = "global";
}
const formName = type === "Heresy" ? religions[r].form : form; const formName = type === "Heresy" ? religions[r].form : form;
const code = abbreviate(name, religions.map(r => r.code)); const code = abbreviate(
religions.push({i, name, color, culture, type, form:formName, deity, expansion, expansionism:0, center, cells:0, area:0, rural:0, urban:0, origin:r, code}); name,
religions.map(r => r.code)
);
religions.push({i, name, color, culture, type, form: formName, deity, expansion, expansionism: 0, center, cells: 0, area: 0, rural: 0, urban: 0, origin: r, code});
cells.religion[center] = i; cells.religion[center] = i;
} };
// growth algorithm to assign cells to religions // growth algorithm to assign cells to religions
const expandReligions = function() { const expandReligions = function () {
const cells = pack.cells, religions = pack.religions; const cells = pack.cells,
religions = pack.religions;
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 = [];
religions.filter(r => r.type === "Organized" || r.type === "Cult").forEach(r => { religions
cells.religion[r.center] = r.i; .filter(r => r.type === "Organized" || r.type === "Cult")
queue.queue({e:r.center, p:0, r:r.i, s: cells.state[r.center], c:r.culture}); .forEach(r => {
cost[r.center] = 1; cells.religion[r.center] = r.i;
}); queue.queue({e: r.center, p: 0, r: r.i, s: cells.state[r.center], c: r.culture});
cost[r.center] = 1;
});
const neutral = cells.i.length / 5000 * 200 * gauss(1, .3, .2, 2, 2) * neutralInput.value; // limit cost for organized religions growth const neutral = (cells.i.length / 5000) * 200 * gauss(1, 0.3, 0.2, 2, 2) * neutralInput.value; // limit cost for organized religions growth
const popCost = d3.max(cells.pop) / 3; // enougth population to spered religion without penalty const popCost = d3.max(cells.pop) / 3; // enougth population to spered religion without penalty
while (queue.length) { while (queue.length) {
const next = queue.dequeue(), n = next.e, p = next.p, r = next.r, c = next.c, s = next.s; const next = queue.dequeue(),
n = next.e,
p = next.p,
r = next.r,
c = next.c,
s = next.s;
const expansion = religions[r].expansion; const expansion = religions[r].expansion;
cells.c[n].forEach(function(e) { cells.c[n].forEach(function (e) {
if (expansion === "culture" && c !== cells.culture[e]) return; if (expansion === "culture" && c !== cells.culture[e]) return;
if (expansion === "state" && s !== cells.state[e]) return; if (expansion === "state" && s !== cells.state[e]) return;
@ -215,88 +235,101 @@
const biomeCost = cells.road[e] ? 1 : biomesData.cost[cells.biome[e]]; const biomeCost = cells.road[e] ? 1 : biomesData.cost[cells.biome[e]];
const populationCost = Math.max(rn(popCost - cells.pop[e]), 0); const populationCost = Math.max(rn(popCost - cells.pop[e]), 0);
const heightCost = Math.max(cells.h[e], 20) - 20; const heightCost = Math.max(cells.h[e], 20) - 20;
const waterCost = cells.h[e] < 20 ? cells.road[e] ? 50 : 1000 : 0; const waterCost = cells.h[e] < 20 ? (cells.road[e] ? 50 : 1000) : 0;
const totalCost = p + (cultureCost + stateCost + biomeCost + populationCost + heightCost + waterCost) / religions[r].expansionism; const totalCost = p + (cultureCost + stateCost + biomeCost + populationCost + heightCost + waterCost) / religions[r].expansionism;
if (totalCost > neutral) return; if (totalCost > neutral) return;
if (!cost[e] || totalCost < cost[e]) { if (!cost[e] || totalCost < cost[e]) {
if (cells.h[e] >= 20 && cells.culture[e]) cells.religion[e] = r; // assign religion to cell if (cells.h[e] >= 20 && cells.culture[e]) cells.religion[e] = r; // assign religion to cell
cost[e] = totalCost; cost[e] = totalCost;
queue.queue({e, p:totalCost, r, c, s}); queue.queue({e, p: totalCost, r, c, s});
} }
}); });
} }
} };
// growth algorithm to assign cells to heresies // growth algorithm to assign cells to heresies
const expandHeresies = function() { const expandHeresies = function () {
const cells = pack.cells, religions = pack.religions; const cells = pack.cells,
religions = pack.religions;
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 = [];
religions.filter(r => r.type === "Heresy").forEach(r => { religions
const b = cells.religion[r.center]; // "base" religion id .filter(r => r.type === "Heresy")
cells.religion[r.center] = r.i; // heresy id .forEach(r => {
queue.queue({e:r.center, p:0, r:r.i, b}); const b = cells.religion[r.center]; // "base" religion id
cost[r.center] = 1; cells.religion[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 neutral = (cells.i.length / 5000) * 500 * neutralInput.value; // limit cost for heresies growth
while (queue.length) { while (queue.length) {
const next = queue.dequeue(), n = next.e, p = next.p, r = next.r, b = next.b; const next = queue.dequeue(),
n = next.e,
p = next.p,
r = next.r,
b = next.b;
cells.c[n].forEach(function(e) { cells.c[n].forEach(function (e) {
const religionCost = cells.religion[e] === b ? 0 : 2000; const religionCost = cells.religion[e] === b ? 0 : 2000;
const biomeCost = cells.road[e] ? 0 : biomesData.cost[cells.biome[e]]; const biomeCost = cells.road[e] ? 0 : biomesData.cost[cells.biome[e]];
const heightCost = Math.max(cells.h[e], 20) - 20; const heightCost = Math.max(cells.h[e], 20) - 20;
const waterCost = cells.h[e] < 20 ? cells.road[e] ? 50 : 1000 : 0; const waterCost = cells.h[e] < 20 ? (cells.road[e] ? 50 : 1000) : 0;
const totalCost = p + (religionCost + biomeCost + heightCost + waterCost) / Math.max(religions[r].expansionism, .1); const totalCost = p + (religionCost + biomeCost + heightCost + waterCost) / Math.max(religions[r].expansionism, 0.1);
if (totalCost > neutral) return; if (totalCost > neutral) return;
if (!cost[e] || totalCost < cost[e]) { if (!cost[e] || totalCost < cost[e]) {
if (cells.h[e] >= 20 && cells.culture[e]) cells.religion[e] = r; // assign religion to cell if (cells.h[e] >= 20 && cells.culture[e]) cells.religion[e] = r; // assign religion to cell
cost[e] = totalCost; cost[e] = totalCost;
queue.queue({e, p:totalCost, r}); queue.queue({e, p: totalCost, r});
} }
}); });
} }
} };
function checkCenters() { function checkCenters() {
const cells = pack.cells, religions = pack.religions; const cells = pack.cells,
religions = pack.religions;
const codes = religions.map(r => r.code); const codes = religions.map(r => r.code);
religions.filter(r => r.i).forEach(r => { religions
r.code = abbreviate(r.name, codes); .filter(r => r.i)
.forEach(r => {
r.code = abbreviate(r.name, codes);
// move religion center if it's not within religion area after expansion // move religion center if it's not within religion area after expansion
if (cells.religion[r.center] === r.i) return; // in area if (cells.religion[r.center] === r.i) return; // in area
const religCells = cells.i.filter(i => cells.religion[i] === r.i); const religCells = cells.i.filter(i => cells.religion[i] === r.i);
if (!religCells.length) return; // extinct religion if (!religCells.length) return; // extinct religion
r.center = religCells.sort((a,b) => b.pop - a.pop)[0]; r.center = religCells.sort((a, b) => b.pop - a.pop)[0];
}); });
} }
function updateCultures() { function updateCultures() {
TIME && console.time('updateCulturesForReligions'); 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'); TIME && console.timeEnd("updateCulturesForReligions");
} }
// get supreme deity name // get supreme deity name
const getDeityName = function(culture) { const getDeityName = function (culture) {
if (culture === undefined) {ERROR && console.error("Please define a culture"); return;} if (culture === undefined) {
ERROR && console.error("Please define a culture");
return;
}
const meaning = generateMeaning(); const meaning = generateMeaning();
const cultureName = Names.getCulture(culture, null, null, "", .8); const cultureName = Names.getCulture(culture, null, null, "", 0.8);
return cultureName + ", The " + meaning; return cultureName + ", The " + meaning;
} };
function generateMeaning() { function generateMeaning() {
const a = ra(approaches); // select generation approach const a = ra(approaches); // select generation approach
@ -318,21 +351,29 @@
function getReligionName(form, deity, center) { function getReligionName(form, deity, center) {
const cells = pack.cells; const cells = pack.cells;
const random = function() {return Names.getCulture(cells.culture[center], null, null, "", 0);} const random = function () {
const type = function() {return rw(types[form]);} return Names.getCulture(cells.culture[center], null, null, "", 0);
const supreme = function() {return deity.split(/[ ,]+/)[0];} };
const place = function(adj) { const type = function () {
return rw(types[form]);
};
const supreme = function () {
return deity.split(/[ ,]+/)[0];
};
const place = function (adj) {
const base = cells.burg[center] ? pack.burgs[cells.burg[center]].name : pack.states[cells.state[center]].name; const base = cells.burg[center] ? pack.burgs[cells.burg[center]].name : pack.states[cells.state[center]].name;
let name = trimVowels(base.split(/[ ,]+/)[0]); let name = trimVowels(base.split(/[ ,]+/)[0]);
return adj ? getAdjective(name) : name; return adj ? getAdjective(name) : name;
} };
const culture = function() {return pack.cultures[cells.culture[center]].name;} const culture = function () {
return pack.cultures[cells.culture[center]].name;
};
const m = rw(methods); const m = rw(methods);
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"];
if (m === "Faith of + Supreme" && deity) return [ra(['Faith', 'Way', 'Path', 'Word', 'Witnesses']) + " of " + supreme(), "global"]; if (m === "Faith of + Supreme" && deity) return [ra(["Faith", "Way", "Path", "Word", "Witnesses"]) + " of " + supreme(), "global"];
if (m === "Place + ism") return [place() + "ism", "state"]; if (m === "Place + ism") return [place() + "ism", "state"];
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"];
@ -342,14 +383,19 @@
function getCultName(form, center) { function getCultName(form, center) {
const cells = pack.cells; const cells = pack.cells;
const type = function() {return rw(types[form]);} const type = function () {
const random = function() {return trimVowels(Names.getCulture(cells.culture[center], null, null, "", 0).split(/[ ,]+/)[0]);} return rw(types[form]);
const burg = function() {return trimVowels(pack.burgs[cells.burg[center]].name.split(/[ ,]+/)[0]);} };
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 (cells.burg[center]) return burg() + "ian " + type();
if (Math.random() > .5) return random() + "ian " + type(); if (Math.random() > 0.5) return random() + "ian " + type();
return type() + " of the " + generateMeaning(); return type() + " of the " + generateMeaning();
}; }
return {generate, add, getDeityName, expandReligions, updateCultures}; return {generate, add, getDeityName, expandReligions, updateCultures};
})();
})));

View file

@ -1,8 +1,6 @@
(function (global, factory) { "use strict";
typeof exports === "object" && typeof module !== "undefined" ? (module.exports = factory()) : typeof define === "function" && define.amd ? define(factory) : (global.Rivers = factory());
})(this, function () {
"use strict";
window.Rivers = (function () {
const generate = function (allowErosion = true) { const generate = function (allowErosion = true) {
TIME && console.time("generateRivers"); TIME && console.time("generateRivers");
Math.random = aleaPRNG(seed); Math.random = aleaPRNG(seed);
@ -442,4 +440,4 @@
}; };
return {generate, alterHeights, resolveDepressions, addMeandering, getRiverPath, specify, getName, getType, getBasin, getWidth, getOffset, getApproximateLength, getRiverPoints, remove}; return {generate, alterHeights, resolveDepressions, addMeandering, getRiverPath, specify, getName, getType, getBasin, getWidth, getOffset, getApproximateLength, getRiverPoints, remove};
}); })();

View file

@ -1,14 +1,9 @@
(function (global, factory) { window.Routes = (function () {
typeof exports === "object" && typeof module !== "undefined" ? (module.exports = factory()) : typeof define === "function" && define.amd ? define(factory) : (global.Routes = factory());
})(this, function () {
"use strict";
const getRoads = function () { const getRoads = function () {
TIME && console.time("generateMainRoads"); TIME && console.time("generateMainRoads");
const cells = pack.cells; const cells = pack.cells;
const burgs = pack.burgs.filter(b => b.i && !b.removed); const burgs = pack.burgs.filter(b => b.i && !b.removed);
const capitals = burgs.filter(b => b.capital) const capitals = burgs.filter(b => b.capital).sort((a, b) => a.population - b.population);
.sort((a,b) => a.population - b.population);
if (capitals.length < 2) return []; // not enough capitals to build main roads if (capitals.length < 2) return []; // not enough capitals to build main roads
const paths = []; // array to store path segments const paths = []; // array to store path segments
@ -271,4 +266,4 @@
} }
return [from, exit, false]; return [from, exit, false];
} }
}); })();

View file

@ -374,7 +374,7 @@ function getMapData() {
const dateString = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate(); const dateString = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
const license = "File can be loaded in azgaar.github.io/Fantasy-Map-Generator"; const license = "File can be loaded in azgaar.github.io/Fantasy-Map-Generator";
const params = [version, license, dateString, seed, graphWidth, graphHeight, mapId].join("|"); const params = [version, license, dateString, seed, graphWidth, graphHeight, mapId].join("|");
const settings = [distanceUnitInput.value, distanceScaleInput.value, areaUnit.value, heightUnit.value, heightExponentInput.value, temperatureScale.value, barSizeInput.value, barLabel.value, barBackOpacity.value, barBackColor.value, barPosX.value, barPosY.value, populationRate, urbanization, mapSizeOutput.value, latitudeOutput.value, temperatureEquatorOutput.value, temperaturePoleOutput.value, precOutput.value, JSON.stringify(options), mapName.value, +hideLabels.checked, stylePreset.value].join("|"); const settings = [distanceUnitInput.value, distanceScaleInput.value, areaUnit.value, heightUnit.value, heightExponentInput.value, temperatureScale.value, barSizeInput.value, barLabel.value, barBackOpacity.value, barBackColor.value, barPosX.value, barPosY.value, populationRate, urbanization, mapSizeOutput.value, latitudeOutput.value, temperatureEquatorOutput.value, temperaturePoleOutput.value, precOutput.value, JSON.stringify(options), mapName.value, +hideLabels.checked, stylePreset.value, +rescaleLabels.checked].join("|");
const coords = JSON.stringify(mapCoordinates); const coords = JSON.stringify(mapCoordinates);
const biomes = [biomesData.color, biomesData.habitability, biomesData.name].join("|"); const biomes = [biomesData.color, biomesData.habitability, biomesData.name].join("|");
const notesData = JSON.stringify(notes); const notesData = JSON.stringify(notes);

View file

@ -1,8 +1,6 @@
(function (global, factory) { "use strict";
typeof exports === "object" && typeof module !== "undefined" ? (module.exports = factory()) : typeof define === "function" && define.amd ? define(factory) : (global.ThreeD = factory());
})(this, function () {
"use strict";
window.ThreeD = (function () {
// set default options // set default options
const options = {scale: 50, lightness: 0.7, shadow: 0.5, sun: {x: 100, y: 600, z: 1000}, rotateMesh: 0, rotateGlobe: 0.5, skyColor: "#9ecef5", waterColor: "#466eab", extendedWater: 0, labels3d: 0, resolution: 2}; const options = {scale: 50, lightness: 0.7, shadow: 0.5, sun: {x: 100, y: 600, z: 1000}, rotateMesh: 0, rotateGlobe: 0.5, skyColor: "#9ecef5", waterColor: "#466eab", extendedWater: 0, labels3d: 0, resolution: 2};
@ -581,4 +579,4 @@
} }
return {create, redraw, update, stop, options, setScale, setLightness, setSun, setRotation, toggleLabels, toggleSky, setResolution, setColors, saveScreenshot, saveOBJ}; return {create, redraw, update, stop, options, setScale, setLightness, setSun, setRotation, toggleLabels, toggleSky, setResolution, setColors, saveScreenshot, saveOBJ};
}); })();

View file

@ -1,4 +1,3 @@
// heightmap-editor module. To be added to window as for now
"use strict"; "use strict";
function editHeightmap() { function editHeightmap() {
@ -821,118 +820,15 @@ function editHeightmap() {
body.setAttribute("data-changed", 0); body.setAttribute("data-changed", 0);
body.innerHTML = ""; body.innerHTML = "";
if (template === "templateVolcano") { const templateString = HeightmapTemplates[template];
addStep("Hill", "1", "90-100", "44-56", "40-60"); if (!templateString) return;
addStep("Multiply", 0.8, "50-100");
addStep("Range", "1.5", "30-55", "45-55", "40-60"); const steps = templateString.split("\n");
addStep("Smooth", 2); if (!steps.length) return tip(`Heightmap template: no steps defined`, false, "error");
addStep("Hill", "1.5", "25-35", "25-30", "20-75");
addStep("Hill", "1", "25-35", "75-80", "25-75"); for (const step of steps) {
addStep("Hill", "0.5", "20-25", "10-15", "20-25"); const elements = step.trim().split(" ");
} else if (template === "templateHighIsland") { addStep(...elements);
addStep("Hill", "1", "90-100", "65-75", "47-53");
addStep("Add", 5, "all");
addStep("Hill", "6", "20-23", "25-55", "45-55");
addStep("Range", "1", "40-50", "45-55", "45-55");
addStep("Smooth", 2);
addStep("Trough", "2-3", "20-30", "20-30", "20-30");
addStep("Trough", "2-3", "20-30", "60-80", "70-80");
addStep("Hill", "1", "10-15", "60-60", "50-50");
addStep("Hill", "1.5", "13-16", "15-20", "20-75");
addStep("Multiply", 0.8, "20-100");
addStep("Range", "1.5", "30-40", "15-85", "30-40");
addStep("Range", "1.5", "30-40", "15-85", "60-70");
addStep("Pit", "2-3", "10-15", "15-85", "20-80");
} else if (template === "templateLowIsland") {
addStep("Hill", "1", "90-99", "60-80", "45-55");
addStep("Hill", "4-5", "25-35", "20-65", "40-60");
addStep("Range", "1", "40-50", "45-55", "45-55");
addStep("Smooth", 3);
addStep("Trough", "1.5", "20-30", "15-85", "20-30");
addStep("Trough", "1.5", "20-30", "15-85", "70-80");
addStep("Hill", "1.5", "10-15", "5-15", "20-80");
addStep("Hill", "1", "10-15", "85-95", "70-80");
addStep("Pit", "3-5", "10-15", "15-85", "20-80");
addStep("Multiply", 0.4, "20-100");
} else if (template === "templateContinents") {
addStep("Hill", "1", "80-85", "75-80", "40-60");
addStep("Hill", "1", "80-85", "20-25", "40-60");
addStep("Multiply", 0.22, "20-100");
addStep("Hill", "5-6", "15-20", "25-75", "20-82");
addStep("Range", ".8", "30-60", "5-15", "20-45");
addStep("Range", ".8", "30-60", "5-15", "55-80");
addStep("Range", "0-3", "30-60", "80-90", "20-80");
addStep("Trough", "3-4", "15-20", "15-85", "20-80");
addStep("Strait", "2", "vertical");
addStep("Smooth", 2);
addStep("Trough", "1-2", "5-10", "45-55", "45-55");
addStep("Pit", "3-4", "10-15", "15-85", "20-80");
addStep("Hill", "1", "5-10", "40-60", "40-60");
} else if (template === "templateArchipelago") {
addStep("Add", 11, "all");
addStep("Range", "2-3", "40-60", "20-80", "20-80");
addStep("Hill", "5", "15-20", "10-90", "30-70");
addStep("Hill", "2", "10-15", "10-30", "20-80");
addStep("Hill", "2", "10-15", "60-90", "20-80");
addStep("Smooth", 3);
addStep("Trough", "10", "20-30", "5-95", "5-95");
addStep("Strait", "2", "vertical");
addStep("Strait", "2", "horizontal");
} else if (template === "templateAtoll") {
addStep("Hill", "1", "75-80", "50-60", "45-55");
addStep("Hill", "1.5", "30-50", "25-75", "30-70");
addStep("Hill", ".5", "30-50", "25-35", "30-70");
addStep("Smooth", 1);
addStep("Multiply", 0.2, "25-100");
addStep("Hill", ".5", "10-20", "50-55", "48-52");
} else if (template === "templateMediterranean") {
addStep("Range", "3-4", "30-50", "0-100", "0-10");
addStep("Range", "3-4", "30-50", "0-100", "90-100");
addStep("Hill", "5-6", "30-70", "0-100", "0-5");
addStep("Hill", "5-6", "30-70", "0-100", "95-100");
addStep("Smooth", 1);
addStep("Hill", "2-3", "30-70", "0-5", "20-80");
addStep("Hill", "2-3", "30-70", "95-100", "20-80");
addStep("Multiply", 0.8, "land");
addStep("Trough", "3-5", "40-50", "0-100", "0-10");
addStep("Trough", "3-5", "40-50", "0-100", "90-100");
} else if (template === "templatePeninsula") {
addStep("Range", "2-3", "20-35", "40-50", "0-15");
addStep("Add", 5, "all");
addStep("Hill", "1", "90-100", "10-90", "0-5");
addStep("Add", 13, "all");
addStep("Hill", "3-4", "3-5", "5-95", "80-100");
addStep("Hill", "1-2", "3-5", "5-95", "40-60");
addStep("Trough", "5-6", "10-25", "5-95", "5-95");
addStep("Smooth", 3);
} else if (template === "templatePangea") {
addStep("Hill", "1-2", "25-40", "15-50", "0-10");
addStep("Hill", "1-2", "5-40", "50-85", "0-10");
addStep("Hill", "1-2", "25-40", "50-85", "90-100");
addStep("Hill", "1-2", "5-40", "15-50", "90-100");
addStep("Hill", "8-12", "20-40", "20-80", "48-52");
addStep("Smooth", 2);
addStep("Multiply", 0.7, "land");
addStep("Trough", "3-4", "25-35", "5-95", "10-20");
addStep("Trough", "3-4", "25-35", "5-95", "80-90");
addStep("Range", "5-6", "30-40", "10-90", "35-65");
} else if (template === "templateIsthmus") {
addStep("Hill", "5-10", "15-30", "0-30", "0-20");
addStep("Hill", "5-10", "15-30", "10-50", "20-40");
addStep("Hill", "5-10", "15-30", "30-70", "40-60");
addStep("Hill", "5-10", "15-30", "50-90", "60-80");
addStep("Hill", "5-10", "15-30", "70-100", "80-100");
addStep("Smooth", 2);
addStep("Trough", "4-8", "15-30", "0-30", "0-20");
addStep("Trough", "4-8", "15-30", "10-50", "20-40");
addStep("Trough", "4-8", "15-30", "30-70", "40-60");
addStep("Trough", "4-8", "15-30", "50-90", "60-80");
addStep("Trough", "4-8", "15-30", "70-100", "80-100");
} else if (template === "templateShattered") {
addStep("Hill", "8", "35-40", "15-85", "30-70");
addStep("Trough", "10-20", "40-50", "5-95", "5-95");
addStep("Range", "5-7", "30-40", "10-90", "20-80");
addStep("Pit", "12-20", "30-40", "15-85", "20-80");
} }
} }

View file

@ -122,9 +122,10 @@ function restoreLayers() {
if (layerIsOn("toggleIce")) drawIce(); if (layerIsOn("toggleIce")) drawIce();
if (layerIsOn("toggleEmblems")) drawEmblems(); if (layerIsOn("toggleEmblems")) drawEmblems();
// states are getting rendered each time, if it's not required than layers should be hidden // some layers are rendered each time, remove them if they are not on
if (!layerIsOn("toggleBorders")) $("#borders").fadeOut(); if (!layerIsOn("toggleBorders")) borders.selectAll("path").remove();
if (!layerIsOn("toggleStates")) regions.style("display", "none").selectAll("path").remove(); if (!layerIsOn("toggleStates")) regions.selectAll("path").remove();
if (!layerIsOn("toggleRivers")) rivers.selectAll("*").remove();
} }
function toggleHeight(event) { function toggleHeight(event) {
@ -1034,13 +1035,14 @@ function drawBorders() {
TIME && console.time("drawBorders"); TIME && console.time("drawBorders");
borders.selectAll("path").remove(); borders.selectAll("path").remove();
const cells = pack.cells, const {cells, vertices} = pack;
vertices = pack.vertices, const n = cells.i.length;
n = cells.i.length;
const sPath = [], const sPath = [];
pPath = []; const pPath = [];
const sUsed = new Array(pack.states.length).fill("").map(a => []);
const pUsed = new Array(pack.provinces.length).fill("").map(a => []); const sUsed = new Array(pack.states.length).fill("").map(_ => []);
const pUsed = new Array(pack.provinces.length).fill("").map(_ => []);
for (let i = 0; i < cells.i.length; i++) { for (let i = 0; i < cells.i.length; i++) {
if (!cells.state[i]) continue; if (!cells.state[i]) continue;

View file

@ -98,7 +98,8 @@ function showSupporters() {
PlayByMail.Net,Brad Wardell,Lance Saba,Egoensis,Brea Richards,Tiber,Chris Bloom,Maxim Lowe,Aquelion,Page One Project,Spencer Morris,Paul Ingram, PlayByMail.Net,Brad Wardell,Lance Saba,Egoensis,Brea Richards,Tiber,Chris Bloom,Maxim Lowe,Aquelion,Page One Project,Spencer Morris,Paul Ingram,
Dust Bunny,Adrian Wright,Eric Alexander Cartaya,GameNight,Thomas Mortensen Hansen,Zklaus,Drinarius,Ed Wright,Lon Varnadore,Crys Cain,Heaven N Lee, Dust Bunny,Adrian Wright,Eric Alexander Cartaya,GameNight,Thomas Mortensen Hansen,Zklaus,Drinarius,Ed Wright,Lon Varnadore,Crys Cain,Heaven N Lee,
Jeffrey Henning,Lazer Elf,Jordan Bellah,Alex Beard,Kass Frisson,Petro Lombaard,Emanuel Pietri,Rox,PinkEvil,Gavin Madrigal,Martin Lorber,Prince of Morgoth, Jeffrey Henning,Lazer Elf,Jordan Bellah,Alex Beard,Kass Frisson,Petro Lombaard,Emanuel Pietri,Rox,PinkEvil,Gavin Madrigal,Martin Lorber,Prince of Morgoth,
Jaryd Armstrong,Andrew Pirkola,ThyHolyDevil,Gary Smith,Tyshaun Wise,Ethan Cook,Jon Stroman,Nobody679,良义 ,Chris Gray,Phoenix Boatwright`; Jaryd Armstrong,Andrew Pirkola,ThyHolyDevil,Gary Smith,Tyshaun Wise,Ethan Cook,Jon Stroman,Nobody679,良义 ,Chris Gray,Phoenix Boatwright,Mackenzie,
"Milo Cohen,Jason Matthew Wuerfel,Rasmus Legêne,Andrew Hines,Wexxler,Espen Sæverud,Binks,Dominick Ormsby,Linn Browning,Václav Švec,Alan Buehne,George J.Lekkas"`;
const array = supporters const array = supporters
.replace(/(?:\r\n|\r|\n)/g, "") .replace(/(?:\r\n|\r|\n)/g, "")
@ -541,17 +542,18 @@ function randomizeOptions() {
// select heightmap template pseudo-randomly // select heightmap template pseudo-randomly
function randomizeHeightmapTemplate() { function randomizeHeightmapTemplate() {
const templates = { const templates = {
Volcano: 3, volcano: 3,
"High Island": 22, highIsland: 22,
"Low Island": 9, lowIsland: 9,
Continents: 20, continents: 19,
Archipelago: 25, archipelago: 23,
Mediterranean: 3, mediterranean: 5,
Peninsula: 3, peninsula: 3,
Pangea: 5, pangea: 5,
Isthmus: 2, isthmus: 2,
Atoll: 1, atoll: 1,
Shattered: 7 shattered: 7,
taklamakan: 1
}; };
document.getElementById("templateInput").value = rw(templates); document.getElementById("templateInput").value = rw(templates);
} }

View file

@ -79,7 +79,7 @@ function createRiver() {
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 = last(rivers).i + 1; const riverId = rivers.length ? last(rivers).i + 1 : 1;
const parent = cells.r[last(riverCells)] || riverId; const parent = cells.r[last(riverCells)] || riverId;
riverCells.forEach(cell => { riverCells.forEach(cell => {

View file

@ -111,7 +111,7 @@ function editRiver(id) {
debug debug
.select("#controlCells") .select("#controlCells")
.selectAll(`polygon.${type}`) .selectAll(`polygon.${type}`)
.data(cells) .data(cells.filter(i => pack.cells.i[i]))
.join("polygon") .join("polygon")
.attr("points", d => getPackPolygon(d)) .attr("points", d => getPackPolygon(d))
.attr("class", type); .attr("class", type);
@ -124,18 +124,13 @@ function editRiver(id) {
const initCell = +this.dataset.cell; const initCell = +this.dataset.cell;
const index = +this.dataset.i; const index = +this.dataset.i;
const occupiedCells = i.filter(i => r[i] && !river.cells.includes(i));
drawCells(occupiedCells, "occupied");
let movedToCell = null; let movedToCell = null;
d3.event.on("drag", function () { d3.event.on("drag", function () {
const {x, y} = d3.event; const {x, y} = d3.event;
const currentCell = findCell(x, y); const currentCell = findCell(x, y);
if (initCell !== currentCell) { movedToCell = initCell !== currentCell ? currentCell : null;
if (occupiedCells.includes(currentCell)) return;
movedToCell = currentCell;
} else movedToCell = null;
this.setAttribute("cx", x); this.setAttribute("cx", x);
this.setAttribute("cy", y); this.setAttribute("cy", y);
@ -149,15 +144,15 @@ function editRiver(id) {
river.cells[index] = movedToCell; river.cells[index] = movedToCell;
drawCells(river.cells, "current"); drawCells(river.cells, "current");
// swap river data if (!r[movedToCell]) {
r[initCell] = 0; // swap river data
r[movedToCell] = river.i; r[initCell] = 0;
const sourceFlux = fl[initCell]; r[movedToCell] = river.i;
fl[initCell] = fl[movedToCell]; const sourceFlux = fl[initCell];
fl[movedToCell] = sourceFlux; fl[initCell] = fl[movedToCell];
fl[movedToCell] = sourceFlux;
}
} }
debug.select("#controlCells").selectAll("polygon.available, polygon.occupied").remove();
}); });
} }

View file

@ -539,7 +539,7 @@ function addRiverOnClick() {
const {alterHeights, resolveDepressions, addMeandering, getRiverPath, getBasin, getName, getType, getWidth, getOffset, getApproximateLength} = Rivers; const {alterHeights, resolveDepressions, addMeandering, getRiverPath, getBasin, getName, getType, getWidth, getOffset, getApproximateLength} = Rivers;
const riverCells = []; const riverCells = [];
let riverId = last(rivers).i + 1; let riverId = rivers.length ? last(rivers).i + 1 : 1;
let parent = riverId; let parent = riverId;
const initialFlux = grid.cells.prec[cells.g[i]]; const initialFlux = grid.cells.prec[cells.g[i]];

View file

@ -1,4 +1,5 @@
"use strict"; "use strict";
function editZones() { function editZones() {
closeDialogs(); closeDialogs();
if (!layerIsOn("toggleZones")) toggleZones(); if (!layerIsOn("toggleZones")) toggleZones();