replace old iife by a simpler ones

This commit is contained in:
Azgaar 2021-08-04 23:49:02 +03:00
parent 37e5b803bc
commit 460bb29e49
15 changed files with 2430 additions and 1313 deletions

View file

@ -1,163 +1,157 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.Pell = factory());
}(this, (function () { 'use strict';
"use strict";
const defaultParagraphSeparatorString = 'defaultParagraphSeparator'
const formatBlock = 'formatBlock'
const addEventListener = (parent, type, listener) => parent.addEventListener(type, listener)
const appendChild = (parent, child) => parent.appendChild(child)
const createElement = tag => document.createElement(tag)
const queryCommandState = command => document.queryCommandState(command)
const queryCommandValue = command => document.queryCommandValue(command)
const exec = (command, value = null) => document.execCommand(command, false, value)
window.Pell = (function () {
const defaultParagraphSeparatorString = "defaultParagraphSeparator";
const formatBlock = "formatBlock";
const addEventListener = (parent, type, listener) => parent.addEventListener(type, listener);
const appendChild = (parent, child) => parent.appendChild(child);
const createElement = tag => document.createElement(tag);
const queryCommandState = command => document.queryCommandState(command);
const queryCommandValue = command => document.queryCommandValue(command);
const exec = (command, value = null) => document.execCommand(command, false, value);
const defaultActions = {
bold: {
icon: '<b>B</b>',
title: 'Bold',
state: () => queryCommandState('bold'),
result: () => exec('bold')
icon: "<b>B</b>",
title: "Bold",
state: () => queryCommandState("bold"),
result: () => exec("bold")
},
italic: {
icon: '<i>I</i>',
title: 'Italic',
state: () => queryCommandState('italic'),
result: () => exec('italic')
icon: "<i>I</i>",
title: "Italic",
state: () => queryCommandState("italic"),
result: () => exec("italic")
},
underline: {
icon: '<u>U</u>',
title: 'Underline',
state: () => queryCommandState('underline'),
result: () => exec('underline')
icon: "<u>U</u>",
title: "Underline",
state: () => queryCommandState("underline"),
result: () => exec("underline")
},
strikethrough: {
icon: '<strike>S</strike>',
title: 'Strike-through',
state: () => queryCommandState('strikeThrough'),
result: () => exec('strikeThrough')
icon: "<strike>S</strike>",
title: "Strike-through",
state: () => queryCommandState("strikeThrough"),
result: () => exec("strikeThrough")
},
heading1: {
icon: '<b>H<sub>1</sub></b>',
title: 'Heading 1',
result: () => exec(formatBlock, '<h1>')
icon: "<b>H<sub>1</sub></b>",
title: "Heading 1",
result: () => exec(formatBlock, "<h1>")
},
heading2: {
icon: '<b>H<sub>2</sub></b>',
title: 'Heading 2',
result: () => exec(formatBlock, '<h2>')
icon: "<b>H<sub>2</sub></b>",
title: "Heading 2",
result: () => exec(formatBlock, "<h2>")
},
paragraph: {
icon: '&#182;',
title: 'Paragraph',
result: () => exec(formatBlock, '<p>')
icon: "&#182;",
title: "Paragraph",
result: () => exec(formatBlock, "<p>")
},
quote: {
icon: '&#8220; &#8221;',
title: 'Quote',
result: () => exec(formatBlock, '<blockquote>')
icon: "&#8220; &#8221;",
title: "Quote",
result: () => exec(formatBlock, "<blockquote>")
},
olist: {
icon: '&#35;',
title: 'Ordered List',
result: () => exec('insertOrderedList')
icon: "&#35;",
title: "Ordered List",
result: () => exec("insertOrderedList")
},
ulist: {
icon: '&#8226;',
title: 'Unordered List',
result: () => exec('insertUnorderedList')
icon: "&#8226;",
title: "Unordered List",
result: () => exec("insertUnorderedList")
},
code: {
icon: '&lt;/&gt;',
title: 'Code',
result: () => exec(formatBlock, '<pre>')
icon: "&lt;/&gt;",
title: "Code",
result: () => exec(formatBlock, "<pre>")
},
line: {
icon: '&#8213;',
title: 'Horizontal Line',
result: () => exec('insertHorizontalRule')
icon: "&#8213;",
title: "Horizontal Line",
result: () => exec("insertHorizontalRule")
},
link: {
icon: '&#128279;',
title: 'Link',
result: () => navigator.clipboard.readText().then(url => exec('createLink', url))
icon: "&#128279;",
title: "Link",
result: () => navigator.clipboard.readText().then(url => exec("createLink", url))
},
image: {
icon: '&#128247;',
title: 'Image',
icon: "&#128247;",
title: "Image",
result: () => {
navigator.clipboard.readText().then(url => exec('insertImage', url))
exec('enableObjectResizing')
navigator.clipboard.readText().then(url => exec("insertImage", url));
exec("enableObjectResizing");
}
}
}
};
const defaultClasses = {
actionbar: 'pell-actionbar',
button: 'pell-button',
content: 'pell-content',
selected: 'pell-button-selected'
}
actionbar: "pell-actionbar",
button: "pell-button",
content: "pell-content",
selected: "pell-button-selected"
};
const init = settings => {
const actions = settings.actions
? (
settings.actions.map(action => {
if (typeof action === 'string') return defaultActions[action]
else if (defaultActions[action.name]) return { ...defaultActions[action.name], ...action }
return action
? settings.actions.map(action => {
if (typeof action === "string") return defaultActions[action];
else if (defaultActions[action.name]) return {...defaultActions[action.name], ...action};
return 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}
: 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};
})();

View file

@ -1,12 +1,9 @@
(function (global, factory) {
typeof exports === "object" && typeof module !== "undefined" ? (module.exports = factory()) : typeof define === "function" && define.amd ? define(factory) : (global.BurgsAndStates = factory());
})(this, function () {
"use strict";
"use strict";
window.BurgsAndStates = (function () {
const generate = function () {
const cells = pack.cells,
cultures = pack.cultures,
n = cells.i.length;
const {cells, cultures} = pack;
const n = cells.i.length;
cells.burg = new Uint16Array(n); // cell burg
cells.road = new Uint16Array(n); // cell road power
@ -1224,4 +1221,4 @@
};
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) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.COA = factory());
}(this, (function () {'use strict';
"use strict";
window.COA = (function () {
const tinctures = {
field: { metals: 3, colours: 4, stains: +P(.03), patterns: 1 },
division: { metals: 5, colours: 8, stains: +P(.03), patterns: 1 },
charge: { metals: 2, colours: 3, stains: +P(.05), patterns: 0 },
metals: { argent: 3, or: 2 },
colours: { gules: 5, azure: 4, sable: 3, purpure: 3, vert: 2 },
stains: { murrey: 1, sanguine: 1, tenné: 1 },
field: {metals: 3, colours: 4, stains: +P(0.03), patterns: 1},
division: {metals: 5, colours: 8, stains: +P(0.03), patterns: 1},
charge: {metals: 2, colours: 3, stains: +P(0.05), patterns: 0},
metals: {argent: 3, or: 2},
colours: {gules: 5, azure: 4, sable: 3, purpure: 3, vert: 2},
stains: {murrey: 1, sanguine: 1, tenné: 1},
patterns: {
semy: 8, ermine: 6,
vair: 4, counterVair: 1, vairInPale: 1, vairEnPointe: 2, 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 }
}
semy: 8,
ermine: 6,
vair: 4,
counterVair: 1,
vairInPale: 1,
vairEnPointe: 2,
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 = {
// 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 },
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 },
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},
semy: {conventional: 12, crosses: 3, plants: 1},
// generic categories
conventional: {
lozenge: 2, fusil: 4, mascle: 4, 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
lozenge: 2,
fusil: 4,
mascle: 4,
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: {
crossHummetty: 15, crossVoided: 1, crossPattee: 2, crossPatteeAlisee: 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
crossHummetty: 15,
crossVoided: 1,
crossPattee: 2,
crossPatteeAlisee: 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: {
lionRampant: 5, 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
lionRampant: 5,
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 },
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 },
plants: { tree: 1, oak: 1, cinquefoil: 1, rose: 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 },
bodyparts: { hand: 4, head: 1, headWreathed: 1 },
people: { cavalier: 3, monk: 1, angel: 2 },
architecture: { tower: 1, castle: 1 },
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},
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},
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},
bodyparts: {hand: 4, head: 1, headWreathed: 1},
people: {cavalier: 3, monk: 1, angel: 2},
architecture: {tower: 1, castle: 1},
miscellaneous: {
crown: 3, 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
crown: 3,
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:
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 },
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 },
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 },
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},
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},
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},
// selection based on type
City: { key: 3, bell: 2, lute: 1, tower: 1, castle: 1, mallet: 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 },
City: {key: 3, bell: 2, lute: 1, tower: 1, castle: 1, mallet: 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},
// specific cases
natural: { fountain: "azure", garb: "or", raven: "sable" }, // charges to mainly use predefined colours
sinister: [ // charges that can be sinister
"crossGamma", "lionRampant", "lionPassant", "wolfRampant", "wolfPassant", "wolfStatant", "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"
natural: {fountain: "azure", garb: "or", raven: "sable"}, // charges to mainly use predefined colours
sinister: [
// charges that can be sinister
"crossGamma",
"lionRampant",
"lionPassant",
"wolfRampant",
"wolfPassant",
"wolfStatant",
"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 = {
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 },
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},
divisions: {
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 },
perBend: { e: 5, lm: 5, bcfdgh: 1 },
perBendSinister: { e: 1, jo: 1 },
perCross: { e: 4, jlmo: 1, j: 1, jo: 2, jl: 1 },
perChevron: { e: 1, jlh: 1, dfk: 1, dfbh: 2, 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 },
perPile: { ee: 3, be: 2, abceh: 1, abcabc: 1, jleh: 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},
perBend: {e: 5, lm: 5, bcfdgh: 1},
perBendSinister: {e: 1, jo: 1},
perCross: {e: 4, jlmo: 1, j: 1, jo: 2, jl: 1},
perChevron: {e: 1, jlh: 1, dfk: 1, dfbh: 2, 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},
perPile: {ee: 3, be: 2, abceh: 1, abcabc: 1, jleh: 1}
},
ordinariesOn: {
pale: { ee: 12, beh: 10, kn: 3, bb: 1 },
fess: { ee: 1, def: 3 },
bar: { defdefdef: 1 },
fessCotissed: { ee: 1, def: 3 },
fessDoubleCotissed: { ee: 1, defdef: 3 },
bend: { ee: 2, jo: 1, joe: 1 },
bendSinister: { ee: 1, lm: 1, lem: 4 },
bendlet: { joejoejoe: 1 },
bendletSinister: { lemlemlem: 1 },
bordure: { ABCDEFGHIJKL: 1 },
chief: { abc: 5, bbb: 1 },
quarter: { jjj: 1 },
canton: { yyyy: 1 },
cross: { eeee: 1, behdfbehdf: 3, behbehbeh: 2 },
crossParted: { e: 5, ee: 1 },
saltire: { ee: 5, jlemo: 1 },
saltireParted: { e: 5, ee: 1 },
pall: { ee: 1, jleh: 5, jlhh: 3 },
pallReversed: { ee: 1, bemo: 5 },
pile: { bbb: 1 },
pileInBend: { eeee: 1, eeoo: 1 },
pileInBendSinister: { eeee: 1, eemm: 1 }
pale: {ee: 12, beh: 10, kn: 3, bb: 1},
fess: {ee: 1, def: 3},
bar: {defdefdef: 1},
fessCotissed: {ee: 1, def: 3},
fessDoubleCotissed: {ee: 1, defdef: 3},
bend: {ee: 2, jo: 1, joe: 1},
bendSinister: {ee: 1, lm: 1, lem: 4},
bendlet: {joejoejoe: 1},
bendletSinister: {lemlemlem: 1},
bordure: {ABCDEFGHIJKL: 1},
chief: {abc: 5, bbb: 1},
quarter: {jjj: 1},
canton: {yyyy: 1},
cross: {eeee: 1, behdfbehdf: 3, behbehbeh: 2},
crossParted: {e: 5, ee: 1},
saltire: {ee: 5, jlemo: 1},
saltireParted: {e: 5, ee: 1},
pall: {ee: 1, jleh: 5, jlhh: 3},
pallReversed: {ee: 1, bemo: 5},
pile: {bbb: 1},
pileInBend: {eeee: 1, eeoo: 1},
pileInBendSinister: {eeee: 1, eemm: 1}
},
ordinariesOff: {
pale: { yyy: 1 },
fess: { abc: 3, abcz: 1 },
bar: { abc: 2, abcgzi: 1, jlh: 5, bgi: 2, ach: 1 },
gemelle: { abc: 1 },
bend: { ccg: 2, ccc: 1 },
bendSinister: { aai: 2, aaa: 1 },
bendlet: { ccg: 2, ccc: 1 },
bendletSinister: { aai: 2, aaa: 1 },
bordure: { e: 4, jleh:2, kenken: 1, peqpeq: 1 },
orle: { e: 4, jleh: 1, kenken: 1, peqpeq: 1 },
chief: { emo: 2, emoz: 1, ez: 2 },
terrace: { e: 5, def: 1, bdf: 3 },
mount: { e: 5, def: 1, bdf: 3 },
point: { e: 2, def: 1, bdf: 3, acbdef: 1 },
flaunches: { e: 3, kn: 1, beh: 3 },
gyron: { bh: 1 },
quarter: { e: 1 },
canton: { e: 5, beh: 1, def: 1, bdefh: 1, kn: 1 },
cross: { acgi: 1 },
pall: { BCKFEILGJbdmfo: 1 },
pallReversed: { aczac: 1 },
chevron: { ach: 3, hhh: 1 },
chevronReversed: { bbb: 1 },
pile: { acdfgi: 1, acac: 1 },
pileInBend: { cg: 1 },
pileInBendSinister: { ai: 1 },
label: { defgzi: 2, eh: 3, defdefhmo: 1, egiegi: 1, pqn: 5 }
pale: {yyy: 1},
fess: {abc: 3, abcz: 1},
bar: {abc: 2, abcgzi: 1, jlh: 5, bgi: 2, ach: 1},
gemelle: {abc: 1},
bend: {ccg: 2, ccc: 1},
bendSinister: {aai: 2, aaa: 1},
bendlet: {ccg: 2, ccc: 1},
bendletSinister: {aai: 2, aaa: 1},
bordure: {e: 4, jleh: 2, kenken: 1, peqpeq: 1},
orle: {e: 4, jleh: 1, kenken: 1, peqpeq: 1},
chief: {emo: 2, emoz: 1, ez: 2},
terrace: {e: 5, def: 1, bdf: 3},
mount: {e: 5, def: 1, bdf: 3},
point: {e: 2, def: 1, bdf: 3, acbdef: 1},
flaunches: {e: 3, kn: 1, beh: 3},
gyron: {bh: 1},
quarter: {e: 1},
canton: {e: 5, beh: 1, def: 1, bdefh: 1, kn: 1},
cross: {acgi: 1},
pall: {BCKFEILGJbdmfo: 1},
pallReversed: {aczac: 1},
chevron: {ach: 3, hhh: 1},
chevronReversed: {bbb: 1},
pile: {acdfgi: 1, acac: 1},
pileInBend: {cg: 1},
pileInBendSinister: {ai: 1},
label: {defgzi: 2, eh: 3, defdefhmo: 1, egiegi: 1, pqn: 5}
},
// charges
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 },
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 },
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 },
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 },
boarRampant: { e: 12, beh: 1, kn: 1, jln: 2 },
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 },
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 },
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 },
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},
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},
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},
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},
boarRampant: {e: 12, beh: 1, kn: 1, jln: 2},
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},
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},
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},
armillarySphere: {e: 1},
tree: {e: 1},
lymphad: {e: 1},
@ -175,33 +369,92 @@
};
const lines = {
straight: 50, wavy: 8, engrailed: 4, 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
straight: 50,
wavy: 8,
engrailed: 4,
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 = {
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,
perFess: lines,
perBend: lines,
perBendSinister: lines,
perChevron: 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
};
const ordinaries = {
lined: {
pale: 7, 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
pale: 7,
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: {
bordure: 8, orle: 4, 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
bordure: 8,
orle: 4,
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},
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}
}
};
const generate = function(parent, kinship, dominion, type) {
const generate = function (parent, kinship, dominion, type) {
if (!parent || parent === "custom") {
parent = null;
kinship = 0;
dominion = 0;
}
let usedPattern = null, usedTinctures = [];
let usedPattern = null,
usedTinctures = [];
const t1 = P(kinship) ? parent.t1 : getTincture("field");
if (t1.includes("-")) usedPattern = t1;
const coa = {t1};
let charge = P(usedPattern ? .5 : .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 ordinary = !charge && P(.65) || P(.3) ? linedOrdinary ? linedOrdinary : rw(ordinaries.straight) : null; // 36% for ordinary
let charge = P(usedPattern ? 0.5 : 0.93) ? true : false; // 80% for charge
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(0.65)) || P(0.3) ? (linedOrdinary ? linedOrdinary : rw(ordinaries.straight)) : null; // 36% for 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 division = divisioned ? parent?.division && P(kinship - .1) ? parent.division.division : rw(divisions.variants) : null;
if (charge) charge =
parent?.charges && P(kinship - .1) ? parent.charges[0].charge :
type && type !== "Generic" && P(.2) ? rw(charges[type]) :
selectCharge();
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 - 0.1) ? parent.division.division : rw(divisions.variants)) : null;
if (charge) charge = parent?.charges && P(kinship - 0.1) ? parent.charges[0].charge : type && type !== "Generic" && P(0.2) ? rw(charges[type]) : selectCharge();
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};
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) {
coa.ordinaries = [{ordinary, t: getTincture("charge", usedTinctures, coa.t1)}];
if (linedOrdinary) coa.ordinaries[0].line = usedPattern || (division && P(.7)) ? "straight" : rw(lines);
if (division && !charge && !usedPattern && P(.5) && ordinary !== "bordure" && ordinary !== "orle") {
if (P(.8)) coa.ordinaries[0].divided = "counter"; // 40%
else if (P(.6)) coa.ordinaries[0].divided = "field"; // 6%
if (linedOrdinary) coa.ordinaries[0].line = usedPattern || (division && P(0.7)) ? "straight" : rw(lines);
if (division && !charge && !usedPattern && P(0.5) && ordinary !== "bordure" && ordinary !== "orle") {
if (P(0.8)) coa.ordinaries[0].divided = "counter";
// 40%
else if (P(0.6)) coa.ordinaries[0].divided = "field";
// 6%
else coa.ordinaries[0].divided = "division"; // 4%
}
}
if (charge) {
let p = "e", t = "gules";
let p = "e",
t = "gules";
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)
p = rw(positions.ordinariesOn[ordinary]);
while (charges.natural[charge] === ordinaryT) charge = selectCharge();
t = !usedPattern && P(.3) ? coa.t1 : getTincture("charge", [], ordinaryT);
} else if (positions.ordinariesOff[ordinary] && P(.95)) {
t = !usedPattern && P(0.3) ? coa.t1 : getTincture("charge", [], ordinaryT);
} else if (positions.ordinariesOff[ordinary] && P(0.95)) {
// place charge out of ordinary (use tincture of ordinary type)
p = rw(positions.ordinariesOff[ordinary]);
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]) {
// place charge in fields made by division
p = rw(positions.divisions[division]);
@ -289,43 +543,41 @@
if (charges.natural[charge]) t = charges.natural[charge]; // natural tincture
coa.charges = [{charge, t, p}];
if (p === "ABCDEFGHIKL" && P(.95)) {
if (p === "ABCDEFGHIKL" && P(0.95)) {
// add central charge if charge is in bordure
coa.charges[0].charge = rw(charges.conventional);
const charge = selectCharge(charges.single);
const t = getTincture("charge", usedTinctures, coa.t1);
coa.charges.push({charge, t, p: "e"});
} else if (P(.8) && charge === "inescutcheon") {
} else if (P(0.8) && charge === "inescutcheon") {
// add charge to inescutcheon
const charge = selectCharge(charges.types);
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) {
const allowCounter = !usedPattern && (!coa.line || coa.line === "straight");
// 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";
if (P(.95)) {
const p2 = p === "e" || P(.5) ? "e" : rw(positions.divisions[division]);
if (P(0.95)) {
const p2 = p === "e" || P(0.5) ? "e" : rw(positions.divisions[division]);
const charge = selectCharge(charges.single);
const t = getTincture("charge", usedTinctures, coa.division.t);
coa.charges.push({charge, t, p: p2, divided: "division"});
}
}
else if (allowCounter && P(.4)) coa.charges[0].divided = "counter"; // counterchanged, 40%
else if (["perPale", "perFess", "perBend", "perBendSinister"].includes(division) && P(.8)) { // place 2 charges in division standard positions
const [p1, p2] = division === "perPale" ? ["p", "q"] :
division === "perFess" ? ["k", "n"] :
division === "perBend" ? ["l", "m"] :
["j", "o"]; // perBendSinister
} else if (allowCounter && P(0.4)) coa.charges[0].divided = "counter";
// counterchanged, 40%
else if (["perPale", "perFess", "perBend", "perBendSinister"].includes(division) && P(0.8)) {
// place 2 charges in division standard positions
const [p1, p2] = division === "perPale" ? ["p", "q"] : division === "perFess" ? ["k", "n"] : division === "perBend" ? ["l", "m"] : ["j", "o"]; // perBendSinister
coa.charges[0].p = p1;
const charge = selectCharge(charges.single);
const t = getTincture("charge", usedTinctures, coa.division.t);
coa.charges.push({charge, t, p: p2});
}
else if (["perCross", "perSaltire"].includes(division) && P(.5)) { // place 4 charges in division standard positions
} else if (["perCross", "perSaltire"].includes(division) && P(0.5)) {
// place 4 charges in division standard positions
const [p1, p2, p3, p4] = division === "perCross" ? ["j", "l", "m", "o"] : ["b", "d", "f", "h"];
coa.charges[0].p = p1;
@ -338,8 +590,7 @@
const c4 = selectCharge(charges.single);
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});
}
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));
@ -351,8 +602,8 @@
c.p = [...new Set(c.p)].join("");
// define orientation
if (P(.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.sinister.includes(c.charge)) c.sinister = 1;
if (P(0.02) && charges.reversed.includes(c.charge)) c.reversed = 1;
}
}
@ -380,24 +631,26 @@
if (!coa.charges) coa.charges = [];
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) {
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]);
}
// select tincture: element type (field, division, charge), used field tinctures, field type to follow 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
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
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
@ -425,39 +678,60 @@
if (Object.keys(tinctures.stains).includes(tincture)) return "stains";
else return "pattern";
}
}
function definePattern(pattern, element, size = "") {
let t1 = null, t2 = null;
if (P(.1)) size = "-small";
else if (P(.1)) size = "-smaller";
else if (P(.01)) size = "-big";
else if (P(.005)) size = "-smallest";
let t1 = null,
t2 = null;
if (P(0.1)) size = "-small";
else if (P(0.1)) size = "-smaller";
else if (P(0.01)) size = "-big";
else if (P(0.005)) size = "-smallest";
// apply standard tinctures
if (P(.5) && ["vair", "vairInPale", "vairEnPointe"].includes(pattern)) {t1 = "azure"; t2 = "argent";}
else if (P(.8) && pattern === "ermine") {t1 = "argent"; t2 = "sable";}
else if (pattern === "pappellony") {
if (P(.2)) {t1 = "gules"; t2 = "or";}
else if (P(.2)) {t1 = "argent"; t2 = "sable";}
else if (P(.2)) {t1 = "azure"; t2 = "argent";}
}
else if (pattern === "masoned") {
if (P(.3)) {t1 = "gules"; t2 = "argent";}
else if (P(.3)) {t1 = "argent"; t2 = "sable";}
else if (P(.1)) {t1 = "or"; t2 = "sable";}
}
else if (pattern === "fretty") {
if (t2 === "sable" || P(.35)) {t1 = "argent"; t2 = "gules";}
else if (P(.25)) {t1 = "sable"; t2 = "or";}
else if (P(.15)) {t1 = "gules"; t2 = "argent";}
}
else if (pattern === "semy") pattern += "_of_" + selectCharge(charges.semy);
if (P(0.5) && ["vair", "vairInPale", "vairEnPointe"].includes(pattern)) {
t1 = "azure";
t2 = "argent";
} else if (P(0.8) && pattern === "ermine") {
t1 = "argent";
t2 = "sable";
} else if (pattern === "pappellony") {
if (P(0.2)) {
t1 = "gules";
t2 = "or";
} else if (P(0.2)) {
t1 = "argent";
t2 = "sable";
} else if (P(0.2)) {
t1 = "azure";
t2 = "argent";
}
} 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) {
const startWithMetal = P(.7);
const startWithMetal = P(0.7);
t1 = startWithMetal ? rw(tinctures.metals) : rw(tinctures.colours);
t2 = startWithMetal ? rw(tinctures.colours) : rw(tinctures.metals);
}
@ -474,28 +748,30 @@
function replaceTincture(t, n) {
const type = getType(t);
while (!n || n === t) {n = rw(tinctures[type]);}
while (!n || n === t) {
n = rw(tinctures[type]);
}
return n;
}
function getSize(p, o = null, d = null) {
if (p === "e" && (o === "bordure" || o === "orle")) return 1.1;
if (p === "e") return 1.5;
if (p === "jln" || p === "jlh") return .7;
if (p === "abcpqh" || p === "ez" || p === "be") return .5;
if (["a", "b", "c", "d", "f", "g", "h", "i", "bh", "df"].includes(p)) return .5;
if (["j", "l", "m", "o", "jlmo"].includes(p) && d === "perCross") return .6;
if (p.length > 10) return .18; // >10 (bordure)
if (p.length > 7) return .3; // 8, 9, 10
if (p.length > 4) return .4; // 5, 6, 7
if (p.length > 2) return .5; // 3, 4
return .7; // 1, 2
if (p === "jln" || p === "jlh") return 0.7;
if (p === "abcpqh" || p === "ez" || p === "be") return 0.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 0.6;
if (p.length > 10) return 0.18; // >10 (bordure)
if (p.length > 7) return 0.3; // 8, 9, 10
if (p.length > 4) return 0.4; // 5, 6, 7
if (p.length > 2) return 0.5; // 3, 4
return 0.7; // 1, 2
}
return coa;
}
};
const getShield = function(culture, state) {
const getShield = function (culture, state) {
const emblemShape = document.getElementById("emblemShape");
const shapeGroup = emblemShape.selectedOptions[0]?.parentNode.label || "Diversiform";
if (shapeGroup !== "Diversiform") return emblemShape.value;
@ -504,11 +780,10 @@
if (pack.cultures[culture].shield) return pack.cultures[culture].shield;
console.error("Shield shape is not defined on culture level", pack.cultures[culture]);
return "heater";
}
};
const toString = coa => JSON.stringify(coa).replaceAll("#", "%23");
const copy = coa => JSON.parse(JSON.stringify(coa));
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) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.Cultures = factory());
}(this, (function () {'use strict';
"use strict";
window.Cultures = (function () {
let cells;
const generate = function() {
TIME && console.time('generateCultures');
const generate = function () {
TIME && console.time("generateCultures");
cells = pack.cells;
cells.culture = new Uint16Array(cells.i.length); // cell cultures
let count = Math.min(+culturesInput.value, +culturesSet.selectedOptions[0].dataset.max);
@ -17,13 +14,19 @@
count = Math.floor(populated.length / 50);
if (!count) {
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 = `
The climate is harsh and people cannot live in this world.<br>
No cultures, states and burgs will be created.<br>
Please consider changing climate settings in the World Configurator`;
$("#alert").dialog({resizable: false, title: "Extreme climate warning",
buttons: {Ok: function() {$(this).dialog("close");}}
$("#alert").dialog({
resizable: false,
title: "Extreme climate warning",
buttons: {
Ok: function () {
$(this).dialog("close");
}
}
});
return;
} else {
@ -32,22 +35,28 @@
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>
Please consider changing climate settings in the World Configurator`;
$("#alert").dialog({resizable: false, title: "Extreme climate warning",
buttons: {Ok: function() {$(this).dialog("close");}}
$("#alert").dialog({
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 colors = getColors(count);
const emblemShape = document.getElementById("emblemShape").value;
const codes = [];
cultures.forEach(function(c, i) {
const cell = c.center = placeCenter(c.sort ? c.sort : (i) => cells.s[i]);
cultures.forEach(function (c, i) {
const cell = (c.center = placeCenter(c.sort ? c.sort : i => cells.s[i]));
centers.add(cells.p[cell]);
c.i = i+1;
c.i = i + 1;
delete c.odd;
delete c.sort;
c.color = colors[i];
@ -56,20 +65,24 @@
c.origin = 0;
c.code = abbreviate(c.name, codes);
codes.push(c.code);
cells.culture[cell] = i+1;
cells.culture[cell] = i + 1;
if (emblemShape === "random") c.shield = getRandomShield();
});
function placeCenter(v) {
let c, spacing = (graphWidth + graphHeight) / 2 / count;
const sorted = [...populated].sort((a, b) => v(b) - v(a)), max = Math.floor(sorted.length / 2);
do {c = sorted[biased(0, max, 5)]; spacing *= .9;}
while (centers.find(cells.p[c][0], cells.p[c][1], spacing) !== undefined);
let c,
spacing = (graphWidth + graphHeight) / 2 / count;
const sorted = [...populated].sort((a, b) => v(b) - v(a)),
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;
}
// 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
if (!nameBases.length) {
@ -77,7 +90,7 @@
nameBases = Names.getNameBases();
}
cultures.forEach(c => c.base = c.base % nameBases.length);
cultures.forEach(c => (c.base = c.base % nameBases.length));
function selectCultures(c) {
let def = getDefault(c);
@ -87,11 +100,11 @@
const count = Math.min(c, def.length);
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 {
rnd = rand(def.length-1);
rnd = rand(def.length - 1);
culture = def[rnd];
} while (!P(culture.odd))
} while (!P(culture.odd));
cultures.push(culture);
def.splice(rnd, 1);
}
@ -100,31 +113,31 @@
// set culture type based on culture center position
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
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 (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 (f.type === "lake" && f.cells > 5) return "Lake"; // low water cross penalty and high for growth not along coastline
if ((cells.harbor[i] && f.type !== "lake" && P(0.1)) || (cells.harbor[i] === 1 && P(0.6)) || (pack.features[cells.f[i]].group === "isle" && P(0.4))) return "Naval"; // low water cross penalty and high for non-along-coastline growth
if (cells.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";
}
function defineCultureExpansionism(type) {
let base = 1; // Generic
if (type === "Lake") base = .8; else
if (type === "Naval") base = 1.5; else
if (type === "River") base = .9; else
if (type === "Nomadic") base = 1.5; else
if (type === "Hunting") base = .7; else
if (type === "Highland") base = 1.2;
return rn((Math.random() * powerInput.value / 2 + 1) * base, 1);
if (type === "Lake") base = 0.8;
else if (type === "Naval") base = 1.5;
else if (type === "River") base = 0.9;
else if (type === "Nomadic") base = 1.5;
else if (type === "Hunting") base = 0.7;
else if (type === "Highland") base = 1.2;
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();
let culture, base, name;
@ -139,7 +152,10 @@
name = Names.getCulture(culture, 5, 8, "");
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 color = d3.color(d3.scaleSequential(d3.interpolateRainbow)(Math.random())).hex();
@ -148,220 +164,231 @@
const emblemShape = document.getElementById("emblemShape").value;
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
const cells = pack.cells, s = cells.s, sMax = d3.max(s), t = cells.t, 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
const cells = pack.cells,
s = cells.s,
sMax = d3.max(s),
t = cells.t,
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") {
return [
{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:"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:"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:"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:"Romian", base:8, odd:.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:"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:"Turchian", base: 16, odd:.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:"Keltan", base: 22, odd:.05, sort: i => n(i) / td(i, 11) / bd(i, [6, 8]) * t[i], shield:"oval"}
{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: "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: "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: "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: "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: "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: "Turchian", base: 16, odd: 0.05, sort: i => n(i) / td(i, 14), shield: "round"},
{name: "Euskati", base: 20, odd: 0.05, sort: i => (n(i) / td(i, 15)) * h[i], shield: "oldFrench"},
{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") {
return [
{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:"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:"Berberan", base: 17, odd:.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:"Efratic", base: 23, odd:.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:"Maui", base: 25, odd:.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:"Vietic", base: 29, odd:.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:"Ulus", base:31, odd:1, sort: i => n(i) / td(i, 5) / bd(i, [2, 4, 10], 7) * t[i], shield:"banner"}
{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: "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: "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: "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: "Maui", base: 25, odd: 0.2, sort: i => n(i) / td(i, 24) / sf(i) / t[i], shield: "vesicaPiscis"},
{name: "Carnatic", base: 26, odd: 0.5, sort: i => n(i) / td(i, 26), shield: "round"},
{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: 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"}
];
}
if (culturesSet.value === "english") {
const getName = () => Names.getBase(1, 5, 9, "", 0);
return [
{name:getName(), base:1, odd:1, shield:"heater"},
{name:getName(), base:1, odd:1, shield:"wedged"},
{name:getName(), base:1, odd:1, shield:"swiss"},
{name:getName(), base:1, odd:1, shield:"oldFrench"},
{name:getName(), base:1, odd:1, shield:"swiss"},
{name:getName(), base:1, odd:1, shield:"spanish"},
{name:getName(), base:1, odd:1, shield:"hessen"},
{name:getName(), base:1, odd:1, shield:"fantasy5"},
{name:getName(), base:1, odd:1, shield:"fantasy4"},
{name:getName(), base:1, odd:1, shield:"fantasy1"}
{name: getName(), base: 1, odd: 1, shield: "heater"},
{name: getName(), base: 1, odd: 1, shield: "wedged"},
{name: getName(), base: 1, odd: 1, shield: "swiss"},
{name: getName(), base: 1, odd: 1, shield: "oldFrench"},
{name: getName(), base: 1, odd: 1, shield: "swiss"},
{name: getName(), base: 1, odd: 1, shield: "spanish"},
{name: getName(), base: 1, odd: 1, shield: "hessen"},
{name: getName(), base: 1, odd: 1, shield: "fantasy5"},
{name: getName(), base: 1, odd: 1, shield: "fantasy4"},
{name: getName(), base: 1, odd: 1, shield: "fantasy1"}
];
}
if (culturesSet.value === "antique") {
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, 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, 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, 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:"Celtic", base:22, odd:1, sort: i => n(i) / td(i, 11) ** .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:"Persian", base:24, odd:.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:"Cantabrian", base: 20, odd:.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:"Carthaginian", base: 17, odd:.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: "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, 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: "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: "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) ** 0.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: 0.8, sort: i => (n(i) / td(i, 18)) * h[i], shield: "oval"}, // 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: 0.5, sort: i => (n(i) / td(i, 16)) * h[i], shield: "oval"}, // Basque
{name: "Estian", base: 9, odd: 0.2, sort: i => (n(i) / td(i, 5)) * t[i], shield: "pavise"}, // Finnic
{name: "Carthaginian", base: 17, odd: 0.3, sort: i => n(i) / td(i, 19) / sf(i), shield: "oval"}, // Berber
{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") {
return [
// 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:"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:"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:"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:"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:"Ugluk (Orkish)", base: 37, odd:.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:"Rake (Drakonic)", base: 39, odd:.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:"Aj'Snaga (Serpents)", base: 41, odd:.7, sort: i => n(i) / bd(i, [12], 10), shield:"fantasy1"}, // Serpents
{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: "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: 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: "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: "Uruk (Orkish)", base: 37, odd: 1, sort: i => h[i] * t[i], shield: "urukHai"}, // 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: 0.7, sort: i => td(i, -10), shield: "pavise"}, // Giant
{name: "Rake (Drakonic)", base: 39, odd: 0.7, sort: i => -s[i], shield: "fantasy2"}, // Draconic
{name: "Arago (Arachnid)", base: 40, odd: 0.7, sort: i => t[i] - s[i], shield: "horsehead2"}, // Arachnid
{name: "Aj'Snaga (Serpents)", base: 41, odd: 0.7, sort: i => n(i) / bd(i, [12], 10), shield: "fantasy1"}, // Serpents
// fantasy human
{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:"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: "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: "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"}
];
}
if (culturesSet.value === "darkFantasy") {
return [
// common real-world English
{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:"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:"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: "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: "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: "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"},
// rare real-world western
{name:"Norse", base:6, odd:.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:"Luarian", base:2, odd:.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:"Astellian", base:4, odd:.3, sort: i => n(i) / td(i, 16), shield:"spanish"},
{name: "Norse", base: 6, odd: 0.7, sort: i => n(i) / td(i, 5) / sf(i), shield: "oldFrench"},
{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: 0.3, sort: i => n(i) / td(i, 12) / bd(i, [6, 8]), shield: "oldFrench"},
{name: "Hetallian", base: 3, odd: 0.3, sort: i => n(i) / td(i, 15), shield: "oval"},
{name: "Astellian", base: 4, odd: 0.3, sort: i => n(i) / td(i, 16), shield: "spanish"},
// 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:"Yoruba", base:21, odd:.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:"Hantzu", base:11, odd:.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:"Guantzu", base:30, odd:.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:"Turan", base: 16, odd:.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:"Eurabic", base: 18, odd:.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:"Keltan", base: 22, odd:.1, sort: i => n(i) / td(i, 11) ** .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:"Romian", base:8, odd:.2, sort: i => n(i) / td(i, 14) / t[i], shield:"roman"},
{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: 0.05, sort: i => n(i) / td(i, 15) / bd(i, [5, 7]), shield: "vesicaPiscis"},
{name: "Koryo", base: 10, odd: 0.05, sort: i => n(i) / td(i, 12) / t[i], shield: "round"},
{name: "Hantzu", base: 11, odd: 0.05, sort: i => n(i) / td(i, 13), shield: "banner"},
{name: "Yamoto", base: 12, odd: 0.05, sort: i => n(i) / td(i, 15) / t[i], shield: "round"},
{name: "Guantzu", base: 30, odd: 0.05, sort: i => n(i) / td(i, 17), 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: 0.05, sort: i => n(i) / td(i, 12), 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: 0.05, sort: i => (n(i) / td(i, 26) / bd(i, [1, 2], 7)) * 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: 0.1, sort: i => n(i) / td(i, 11) ** 0.5 / bd(i, [6, 8]), shield: "vesicaPiscis"},
{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: 0.2, sort: i => n(i) / td(i, 14) / t[i], shield: "roman"},
// 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:"Trow", base: 34, odd:.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:"Kobblin", base: 36, odd:.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:"Yotunn", base: 38, odd:.8, sort: i => td(i, -10), shield:"pavise"}, // Giant
{name:"Drake", base: 39, odd:.9, sort: i => -s[i], shield:"fantasy2"}, // Draconic
{name:"Rakhnid", base: 40, odd:.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: "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: 0.8, sort: i => (n(i) / bd(i, [7, 8, 9, 12], 10)) * t[i], shield: "hessen"}, // Dark Elves
{name: "Durinn", base: 35, odd: 0.8, sort: i => n(i) + h[i], shield: "erebor"}, // Dwarven
{name: "Kobblin", base: 36, odd: 0.8, sort: i => t[i] - s[i], shield: "moriaOrc"}, // Goblin
{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: 0.8, sort: i => td(i, -10), shield: "pavise"}, // Giant
{name: "Drake", base: 39, odd: 0.9, sort: i => -s[i], shield: "fantasy2"}, // Draconic
{name: "Rakhnid", base: 40, odd: 0.9, sort: i => t[i] - s[i], shield: "horsehead2"}, // Arachnid
{name: "Aj'Snaga", base: 41, odd: 0.9, sort: i => n(i) / bd(i, [12], 10), shield: "fantasy1"} // Serpents
];
}
if (culturesSet.value === "random") {
return d3.range(count).map(function() {
const rnd = rand(nameBases.length-1);
return d3.range(count).map(function () {
const rnd = rand(nameBases.length - 1);
const name = Names.getBaseShort(rnd);
return {name, base:rnd, odd:1, shield:getRandomShield()}
return {name, base: rnd, odd: 1, shield: getRandomShield()};
});
}
// all-world
return [
{name:"Shwazen", base:0, odd:.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:"Luari", base:2, odd:.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:"Astellian", base:4, odd:.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:"Norse", base:6, odd:.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:"Romian", base:8, odd:.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:"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:"Yamoto", base:12, odd:.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:"Nawatli", base:14, odd:.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:"Turchian", base: 16, odd:.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:"Eurabic", base: 18, odd:.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:"Euskati", base: 20, odd:.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:"Keltan", base: 22, odd:.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:"Tehrani", base: 24, odd:.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:"Carnatic", base: 26, odd:.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:"Kiswaili", base: 28, odd:.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:"Guantzu", base:30, odd:.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: "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: "Luari", base: 2, odd: 0.6, sort: i => n(i) / td(i, 12) / bd(i, [6, 8]), shield: "oldFrench"},
{name: "Tallian", base: 3, odd: 0.6, sort: i => n(i) / td(i, 15), shield: "horsehead2"},
{name: "Astellian", base: 4, odd: 0.6, sort: i => n(i) / td(i, 16), shield: "spanish"},
{name: "Slovan", base: 5, odd: 0.7, sort: i => (n(i) / td(i, 6)) * t[i], shield: "round"},
{name: "Norse", base: 6, odd: 0.7, sort: i => n(i) / td(i, 5), shield: "heater"},
{name: "Elladan", base: 7, odd: 0.7, sort: i => (n(i) / td(i, 18)) * h[i], shield: "boeotian"},
{name: "Romian", base: 8, odd: 0.7, sort: i => n(i) / td(i, 15), shield: "roman"},
{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: 0.1, sort: i => n(i) / td(i, 12) / t[i], shield: "round"},
{name: "Hantzu", base: 11, odd: 0.1, sort: i => n(i) / td(i, 13), shield: "banner"},
{name: "Yamoto", base: 12, odd: 0.1, sort: i => n(i) / td(i, 15) / t[i], shield: "round"},
{name: "Portuzian", base: 13, odd: 0.4, sort: i => n(i) / td(i, 17) / sf(i), shield: "spanish"},
{name: "Nawatli", base: 14, odd: 0.1, sort: i => h[i] / td(i, 18) / bd(i, [7]), shield: "square"},
{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: 0.2, sort: i => n(i) / td(i, 13), 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: 0.2, sort: i => (n(i) / td(i, 26) / bd(i, [1, 2], 7)) * t[i], shield: "round"},
{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: 0.05, sort: i => (n(i) / td(i, 15)) * h[i], shield: "spanish"},
{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: 0.05, sort: i => (n(i) / td(i, 11) / bd(i, [6, 8])) * t[i], shield: "vesicaPiscis"},
{name: "Efratic", base: 23, odd: 0.05, sort: i => (n(i) / td(i, 22)) * t[i], shield: "diamond"},
{name: "Tehrani", base: 24, odd: 0.1, sort: i => (n(i) / td(i, 18)) * h[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: 0.05, sort: i => n(i) / td(i, 26), shield: "round"},
{name: "Inqan", base: 27, odd: 0.05, sort: i => h[i] / td(i, 13), shield: "square"},
{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: 0.1, sort: i => n(i) / td(i, 25) / bd(i, [7], 7) / t[i], shield: "banner"},
{name: "Guantzu", base: 30, odd: 0.1, sort: i => n(i) / td(i, 17), 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)
const expand = function() {
TIME && console.time('expandCultures');
const expand = function () {
TIME && console.time("expandCultures");
cells = pack.cells;
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;
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 = [];
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;
cells.c[n].forEach(function(e) {
cells.c[n].forEach(function (e) {
const biome = cells.biome[e];
const biomeCost = getBiomeCost(c, biome, type);
const biomeChangeCost = biome === cells.biome[n] ? 0 : 20; // penalty on biome change
@ -375,13 +402,13 @@
if (!cost[e] || totalCost < cost[e]) {
if (cells.s[e] > 0) cells.culture[e] = c; // assign culture to populated cell
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) {
if (cells.biome[pack.cultures[c].center] === biome) return 10; // tiny penalty for native biome
@ -391,7 +418,8 @@
}
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 === "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
@ -407,21 +435,20 @@
function getRiverCost(r, i, type) {
if (type === "River") return r ? 0 : 100; // penalty for river cultures
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) {
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 !== -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;
}
const getRandomShield = function() {
const getRandomShield = function () {
const type = rw(COA.shields.types);
return rw(COA.shields[type]);
}
};
return {generate, add, expand, getDefault, getRandomShield};
})));
})();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,19 +1,13 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.Religions = factory());
}(this, (function () {'use strict';
"use strict";
window.Religions = (function () {
// name generation approach and relative chance to be selected
const approach = {"Number":1, "Being":3, "Adjective":5, "Color + Animal":5,
"Adjective + Animal":5, "Adjective + Being":5, "Adjective + Genitive":1,
"Color + Being":3, "Color + Genitive":3, "Being + of + Genitive":2, "Being + of the + Genitive":1,
"Animal + of + Genitive":1, "Adjective + Being + of + Genitive":2, "Adjective + Animal + of + Genitive":2};
const approach = {Number: 1, Being: 3, Adjective: 5, "Color + Animal": 5, "Adjective + Animal": 5, "Adjective + Being": 5, "Adjective + Genitive": 1, "Color + Being": 3, "Color + Genitive": 3, "Being + of + Genitive": 2, "Being + of the + Genitive": 1, "Animal + of + Genitive": 1, "Adjective + Being + of + Genitive": 2, "Adjective + Animal + of + Genitive": 2};
// turn weighted array into simple array
const approaches = [];
for (const a in approach) {
for (let j=0; j < approach[a]; j++) {
for (let j = 0; j < approach[a]; j++) {
approaches.push(a);
}
}
@ -21,7 +15,7 @@
const base = {
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"],
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"],
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"],
@ -29,64 +23,70 @@
};
const forms = {
Folk:{"Shamanism":2, "Animism":2, "Ancestor worship":1, "Polytheism":2},
Organized:{"Polytheism":5, "Dualism":1, "Monotheism":4, "Non-theism":1},
Cult:{"Cult":1, "Dark Cult":1},
Heresy:{"Heresy":1}
Folk: {Shamanism: 2, Animism: 2, "Ancestor worship": 1, Polytheism: 2},
Organized: {Polytheism: 5, Dualism: 1, Monotheism: 4, "Non-theism": 1},
Cult: {Cult: 1, "Dark Cult": 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 = {
"Shamanism":{"Beliefs":3, "Shamanism":2, "Spirits":1},
"Animism":{"Spirits":1, "Beliefs":1},
"Ancestor worship":{"Beliefs":1, "Forefathers":2, "Ancestors":2},
"Polytheism":{"Deities":3, "Faith":1, "Gods":1, "Pantheon":1},
Shamanism: {Beliefs: 3, Shamanism: 2, Spirits: 1},
Animism: {Spirits: 1, Beliefs: 1},
"Ancestor worship": {Beliefs: 1, Forefathers: 2, Ancestors: 2},
Polytheism: {Deities: 3, Faith: 1, Gods: 1, Pantheon: 1},
"Dualism":{"Religion":3, "Faith":1, "Cult":1},
"Monotheism":{"Religion":1, "Church":1},
"Non-theism":{"Beliefs":3, "Spirits":1},
Dualism: {Religion: 3, Faith: 1, Cult: 1},
Monotheism: {Religion: 1, Church: 1},
"Non-theism": {Beliefs: 3, Spirits: 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},
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},
"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() {
TIME && console.time('generateReligions');
const cells = pack.cells, states = pack.states, cultures = pack.cultures;
const religions = pack.religions = [];
const generate = function () {
TIME && console.time("generateReligions");
const cells = pack.cells,
states = pack.states,
cultures = pack.cultures;
const religions = (pack.religions = []);
cells.religion = new Uint16Array(cells.culture); // cell religion; initially based on culture
// add folk religions
pack.cultures.forEach(c => {
if (!c.i) {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, .1, 0), removed:true}); return;}
if (!c.i) {
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 name = c.name + " " + rw(types[form]);
const deity = form === "Animism" ? null : getDeityName(c.i);
const color = getMixedColor(c.color, .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});
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});
});
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;
}
const burgs = pack.burgs.filter(b => b.i && !b.removed);
const sorted = burgs.length > +religionsInput.value
? burgs.sort((a, b) => b.population - a.population).map(b => b.cell)
: cells.i.filter(i => cells.s[i] > 2).sort((a, b) => cells.s[b] - cells.s[a]);
const sorted = burgs.length > +religionsInput.value ? burgs.sort((a, b) => b.population - a.population).map(b => b.cell) : cells.i.filter(i => cells.s[i] > 2).sort((a, b) => cells.s[b] - cells.s[a]);
const religionsTree = d3.quadtree();
const 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;
// generate organized religions
for (let i=0; religions.length < count && i < 1000; i++) {
let center = sorted[biased(0, sorted.length-1, 5)]; // religion center
for (let i = 0; religions.length < count && i < 1000; i++) {
let center = sorted[biased(0, sorted.length - 1, 5)]; // religion center
const form = rw(forms.Organized);
const state = cells.state[center];
const culture = cells.culture[center];
@ -96,34 +96,36 @@
if (expansion === "state" && !state) expansion = "global";
if (expansion === "culture" && !culture) expansion = "global";
if (expansion === "state" && Math.random() > .5) center = states[state].center;
if (expansion === "culture" && Math.random() > .5) center = cultures[culture].center;
if (expansion === "state" && Math.random() > 0.5) center = states[state].center;
if (expansion === "culture" && Math.random() > 0.5) center = cultures[culture].center;
if (!cells.burg[center] && cells.c[center].some(c => cells.burg[c])) center = cells.c[center].find(c => cells.burg[c]);
const x = 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
// add "Old" to name of the folk religion on this culture
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 expansionism = rand(3, 8);
const color = getMixedColor(religions[origin].color, .3, 0); // `url(#hatch${rand(0,5)})`;
religions.push({i: religions.length, name, color, culture, type:"Organized", form, deity, expansion, expansionism, center, origin});
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});
religionsTree.add([x, y]);
}
// 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);
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]);
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
const culture = cells.culture[center];
@ -131,9 +133,9 @@
const origin = folk ? folk.i : 0;
const deity = getDeityName(culture);
const name = getCultName(form, center);
const expansionism = gauss(1.1, .5, 0, 5);
const color = getMixedColor(cultures[culture].color, .5, 0); // "url(#hatch7)";
religions.push({i: religions.length, name, color, culture, type:"Cult", form, deity, expansion:"global", expansionism, center, origin});
const expansionism = gauss(1.1, 0.5, 0, 5);
const color = getMixedColor(cultures[culture].color, 0.5, 0); // "url(#hatch7)";
religions.push({i: religions.length, name, color, culture, type: "Cult", form, deity, expansion: "global", expansionism, center, origin});
religionsTree.add([x, y]);
//debug.append("circle").attr("cx", x).attr("cy", y).attr("r", 2).attr("fill", "red");
}
@ -141,72 +143,90 @@
expandReligions();
// generate heresies
religions.filter(r => r.type === "Organized").forEach(r => {
if (r.expansionism < 3) return;
const count = gauss(0, 1, 0, 3);
for (let i=0; i < count; i++) {
let center = ra(cells.i.filter(i => cells.religion[i] === r.i && cells.c[i].some(c => cells.religion[c] !== r.i)));
if (!center) continue;
if (!cells.burg[center] && cells.c[center].some(c => cells.burg[c])) center = cells.c[center].find(c => cells.burg[c]);
const x = cells.p[center][0], y = cells.p[center][1];
if (religionsTree.find(x, y, spacing / 10) !== undefined) continue; // to close to other
religions
.filter(r => r.type === "Organized")
.forEach(r => {
if (r.expansionism < 3) return;
const count = gauss(0, 1, 0, 3);
for (let i = 0; i < count; i++) {
let center = ra(cells.i.filter(i => cells.religion[i] === r.i && cells.c[i].some(c => cells.religion[c] !== r.i)));
if (!center) continue;
if (!cells.burg[center] && cells.c[center].some(c => cells.burg[c])) center = cells.c[center].find(c => cells.burg[c]);
const x = 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 name = getCultName("Heresy", center);
const expansionism = gauss(1.2, .5, 0, 5);
const color = getMixedColor(r.color, .4, .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});
religionsTree.add([x, y]);
//debug.append("circle").attr("cx", x).attr("cy", y).attr("r", 2).attr("fill", "green");
}
});
const culture = cells.culture[center];
const name = getCultName("Heresy", center);
const expansionism = gauss(1.2, 0.5, 0, 5);
const color = getMixedColor(r.color, 0.4, 0.2); // "url(#hatch6)";
religions.push({i: religions.length, name, color, culture, type: "Heresy", form: r.form, deity: r.deity, expansion: "global", expansionism, center, origin: r.i});
religionsTree.add([x, y]);
//debug.append("circle").attr("cx", x).attr("cy", y).attr("r", 2).attr("fill", "green");
}
});
expandHeresies();
checkCenters();
TIME && console.timeEnd('generateReligions');
}
TIME && console.timeEnd("generateReligions");
};
const add = function(center) {
const cells = pack.cells, religions = pack.religions;
const add = function (center) {
const cells = pack.cells,
religions = pack.religions;
const r = cells.religion[center];
const i = religions.length;
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 deity = type === "Heresy" ? religions[r].deity : form === "Non-theism" ? null : getDeityName(culture);
let name, expansion;
if (type === "Organized") [name, expansion] = getReligionName(form, deity, center)
else {name = getCultName(form, center); expansion = "global";}
if (type === "Organized") [name, expansion] = getReligionName(form, deity, center);
else {
name = getCultName(form, center);
expansion = "global";
}
const formName = type === "Heresy" ? religions[r].form : form;
const code = abbreviate(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});
const code = abbreviate(
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;
}
};
// growth algorithm to assign cells to religions
const expandReligions = function() {
const cells = pack.cells, religions = pack.religions;
const expandReligions = function () {
const cells = pack.cells,
religions = pack.religions;
const queue = new PriorityQueue({comparator: (a, b) => a.p - b.p});
const cost = [];
religions.filter(r => r.type === "Organized" || r.type === "Cult").forEach(r => {
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;
});
religions
.filter(r => r.type === "Organized" || r.type === "Cult")
.forEach(r => {
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
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;
cells.c[n].forEach(function(e) {
cells.c[n].forEach(function (e) {
if (expansion === "culture" && c !== cells.culture[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 populationCost = Math.max(rn(popCost - cells.pop[e]), 0);
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;
if (totalCost > neutral) return;
if (!cost[e] || totalCost < cost[e]) {
if (cells.h[e] >= 20 && cells.culture[e]) cells.religion[e] = r; // assign religion to cell
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
const expandHeresies = function() {
const cells = pack.cells, religions = pack.religions;
const expandHeresies = function () {
const cells = pack.cells,
religions = pack.religions;
const queue = new PriorityQueue({comparator: (a, b) => a.p - b.p});
const cost = [];
religions.filter(r => r.type === "Heresy").forEach(r => {
const b = cells.religion[r.center]; // "base" religion id
cells.religion[r.center] = r.i; // heresy id
queue.queue({e:r.center, p:0, r:r.i, b});
cost[r.center] = 1;
});
religions
.filter(r => r.type === "Heresy")
.forEach(r => {
const b = cells.religion[r.center]; // "base" religion id
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) {
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 biomeCost = cells.road[e] ? 0 : biomesData.cost[cells.biome[e]];
const heightCost = Math.max(cells.h[e], 20) - 20;
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 waterCost = cells.h[e] < 20 ? (cells.road[e] ? 50 : 1000) : 0;
const totalCost = p + (religionCost + biomeCost + heightCost + waterCost) / Math.max(religions[r].expansionism, 0.1);
if (totalCost > neutral) return;
if (!cost[e] || totalCost < cost[e]) {
if (cells.h[e] >= 20 && cells.culture[e]) cells.religion[e] = r; // assign religion to cell
cost[e] = totalCost;
queue.queue({e, p:totalCost, r});
queue.queue({e, p: totalCost, r});
}
});
}
}
};
function checkCenters() {
const cells = pack.cells, religions = pack.religions;
const cells = pack.cells,
religions = pack.religions;
const codes = religions.map(r => r.code);
religions.filter(r => r.i).forEach(r => {
r.code = abbreviate(r.name, codes);
religions
.filter(r => r.i)
.forEach(r => {
r.code = abbreviate(r.name, codes);
// move religion center if it's not within religion area after expansion
if (cells.religion[r.center] === r.i) return; // in area
const religCells = cells.i.filter(i => cells.religion[i] === r.i);
if (!religCells.length) return; // extinct religion
r.center = religCells.sort((a,b) => b.pop - a.pop)[0];
});
// move religion center if it's not within religion area after expansion
if (cells.religion[r.center] === r.i) return; // in area
const religCells = cells.i.filter(i => cells.religion[i] === r.i);
if (!religCells.length) return; // extinct religion
r.center = religCells.sort((a, b) => b.pop - a.pop)[0];
});
}
function updateCultures() {
TIME && console.time('updateCulturesForReligions');
pack.religions = pack.religions.map( (religion, index) => {
if(index === 0) {
TIME && console.time("updateCulturesForReligions");
pack.religions = pack.religions.map((religion, index) => {
if (index === 0) {
return religion;
}
return {...religion, culture: pack.cells.culture[religion.center]};
});
TIME && console.timeEnd('updateCulturesForReligions');
TIME && console.timeEnd("updateCulturesForReligions");
}
// get supreme deity name
const getDeityName = function(culture) {
if (culture === undefined) {ERROR && console.error("Please define a culture"); return;}
const getDeityName = function (culture) {
if (culture === undefined) {
ERROR && console.error("Please define a culture");
return;
}
const meaning = generateMeaning();
const cultureName = Names.getCulture(culture, null, null, "", .8);
const cultureName = Names.getCulture(culture, null, null, "", 0.8);
return cultureName + ", The " + meaning;
}
};
function generateMeaning() {
const a = ra(approaches); // select generation approach
@ -318,21 +351,29 @@
function getReligionName(form, deity, center) {
const cells = pack.cells;
const random = function() {return Names.getCulture(cells.culture[center], null, null, "", 0);}
const type = function() {return rw(types[form]);}
const supreme = function() {return deity.split(/[ ,]+/)[0];}
const place = function(adj) {
const random = function () {
return Names.getCulture(cells.culture[center], null, null, "", 0);
};
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;
let name = trimVowels(base.split(/[ ,]+/)[0]);
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);
if (m === "Random + type") return [random() + " " + type(), "global"];
if (m === "Random + ism") return [trimVowels(random()) + "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 === "Culture + ism") return [trimVowels(culture()) + "ism", "culture"];
if (m === "Place + ian + type") return [place("adj") + " " + type(), "state"];
@ -342,14 +383,19 @@
function getCultName(form, center) {
const cells = pack.cells;
const type = function() {return rw(types[form]);}
const random = function() {return trimVowels(Names.getCulture(cells.culture[center], null, null, "", 0).split(/[ ,]+/)[0]);}
const burg = function() {return trimVowels(pack.burgs[cells.burg[center]].name.split(/[ ,]+/)[0]);}
const type = function () {
return rw(types[form]);
};
const random = function () {
return trimVowels(Names.getCulture(cells.culture[center], null, null, "", 0).split(/[ ,]+/)[0]);
};
const burg = function () {
return trimVowels(pack.burgs[cells.burg[center]].name.split(/[ ,]+/)[0]);
};
if (cells.burg[center]) return burg() + "ian " + type();
if (Math.random() > .5) return random() + "ian " + type();
if (Math.random() > 0.5) return random() + "ian " + type();
return type() + " of the " + generateMeaning();
};
}
return {generate, add, getDeityName, expandReligions, updateCultures};
})));
})();

View file

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

View file

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

View file

@ -1,8 +1,6 @@
(function (global, factory) {
typeof exports === "object" && typeof module !== "undefined" ? (module.exports = factory()) : typeof define === "function" && define.amd ? define(factory) : (global.ThreeD = factory());
})(this, function () {
"use strict";
"use strict";
window.ThreeD = (function () {
// 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};
@ -581,4 +579,4 @@
}
return {create, redraw, update, stop, options, setScale, setLightness, setSun, setRotation, toggleLabels, toggleSky, setResolution, setColors, saveScreenshot, saveOBJ};
});
})();

View file

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