emblem-controls v1: Adds settings to culture that provide some control over the creation of emblems for that culture. This implementation includes variation in the default cultures but it could be configured to not change anything by default. I also cleaned up the culture editor grid a bit. The next step will likely involve seeing about adding an iconic charge (or two) and iconic tincture to religions and states to make that charge and color likely to appear in emblems for that religion or culture. Note that the version was updated to 1.94.00 because it adds not data to the culture structure.

This commit is contained in:
John Morrow 2023-11-01 00:22:01 -04:00
parent bed7486218
commit c278829f11
14 changed files with 902 additions and 228 deletions

View file

@ -7950,7 +7950,7 @@
<script src="libs/jquery-3.1.1.min.js"></script> <script src="libs/jquery-3.1.1.min.js"></script>
<script src="libs/jquery-ui.min.js"></script> <script src="libs/jquery-ui.min.js"></script>
<script src="versioning.js"></script> <script src="versioning.js?v=1.94.00"></script>
<script src="libs/d3.min.js"></script> <script src="libs/d3.min.js"></script>
<script src="libs/priority-queue.min.js"></script> <script src="libs/priority-queue.min.js"></script>
<script src="libs/delaunator.min.js"></script> <script src="libs/delaunator.min.js"></script>
@ -7964,7 +7964,7 @@
<script src="utils/nodeUtils.js"></script> <script src="utils/nodeUtils.js"></script>
<script src="utils/numberUtils.js?v=1.89.08"></script> <script src="utils/numberUtils.js?v=1.89.08"></script>
<script src="utils/polyfills.js?v=1.93.00"></script> <script src="utils/polyfills.js?v=1.93.00"></script>
<script src="utils/probabilityUtils.js?v=1.88.00"></script> <script src="utils/probabilityUtils.js?v=1.94.00"></script>
<script src="utils/stringUtils.js"></script> <script src="utils/stringUtils.js"></script>
<script src="utils/languageUtils.js"></script> <script src="utils/languageUtils.js"></script>
<script src="utils/unitUtils.js?v=1.87.00"></script> <script src="utils/unitUtils.js?v=1.87.00"></script>
@ -7978,14 +7978,14 @@
<script src="modules/lakes.js"></script> <script src="modules/lakes.js"></script>
<script src="modules/biomes.js"></script> <script src="modules/biomes.js"></script>
<script src="modules/names-generator.js?v=1.87.14"></script> <script src="modules/names-generator.js?v=1.87.14"></script>
<script src="modules/cultures-generator.js?v=1.89.10"></script> <script src="modules/cultures-generator.js?v=1.94.00"></script>
<script src="modules/renderers/state-labels.js"></script> <script src="modules/renderers/state-labels.js"></script>
<script src="modules/burgs-and-states.js?v=1.92.00"></script> <script src="modules/burgs-and-states.js?v=1.94.00"></script>
<script src="modules/routes-generator.js"></script> <script src="modules/routes-generator.js"></script>
<script src="modules/religions-generator.js?v=1.89.36"></script> <script src="modules/religions-generator.js?v=1.89.36"></script>
<script src="modules/military-generator.js"></script> <script src="modules/military-generator.js"></script>
<script src="modules/markers-generator.js?v=1.93.04"></script> <script src="modules/markers-generator.js?v=1.93.04"></script>
<script src="modules/coa-generator.js?v=1.91.05"></script> <script src="modules/coa-generator.js?v=1.94.00"></script>
<script src="modules/submap.js?v=1.91.05"></script> <script src="modules/submap.js?v=1.91.05"></script>
<script src="libs/polylabel.min.js"></script> <script src="libs/polylabel.min.js"></script>
<script src="libs/lineclip.min.js"></script> <script src="libs/lineclip.min.js"></script>
@ -8001,11 +8001,11 @@
<script defer src="modules/relief-icons.js"></script> <script defer src="modules/relief-icons.js"></script>
<script defer src="modules/ui/style.js"></script> <script defer src="modules/ui/style.js"></script>
<script defer src="modules/ui/editors.js?v=1.92.00"></script> <script defer src="modules/ui/editors.js?v=1.94.00"></script>
<script defer src="modules/ui/tools.js?v=1.92.00"></script> <script defer src="modules/ui/tools.js?v=1.94.00"></script>
<script defer src="modules/ui/world-configurator.js?v=1.91.05"></script> <script defer src="modules/ui/world-configurator.js?v=1.91.05"></script>
<script defer src="modules/ui/heightmap-editor.js?v=1.93.00"></script> <script defer src="modules/ui/heightmap-editor.js?v=1.93.00"></script>
<script defer src="modules/ui/provinces-editor.js?v=1.92.00"></script> <script defer src="modules/ui/provinces-editor.js?v=1.94.00"></script>
<script defer src="modules/ui/biomes-editor.js?v=1.91.05"></script> <script defer src="modules/ui/biomes-editor.js?v=1.91.05"></script>
<script defer src="modules/ui/namesbase-editor.js?v=1.89.26"></script> <script defer src="modules/ui/namesbase-editor.js?v=1.89.26"></script>
<script defer src="modules/ui/elevation-profile.js"></script> <script defer src="modules/ui/elevation-profile.js"></script>
@ -8030,7 +8030,7 @@
<script defer src="modules/ui/markers-overview.js?v=1.89.38"></script> <script defer src="modules/ui/markers-overview.js?v=1.89.38"></script>
<script defer src="modules/ui/regiment-editor.js"></script> <script defer src="modules/ui/regiment-editor.js"></script>
<script defer src="modules/ui/battle-screen.js"></script> <script defer src="modules/ui/battle-screen.js"></script>
<script defer src="modules/ui/emblems-editor.js?v=1.91.00"></script> <script defer src="modules/ui/emblems-editor.js?v=1.94.00"></script>
<script defer src="modules/ui/markers-editor.js"></script> <script defer src="modules/ui/markers-editor.js"></script>
<script defer src="modules/ui/3d.js?v=1.89.36"></script> <script defer src="modules/ui/3d.js?v=1.89.36"></script>
<script defer src="modules/ui/submap.js?v=1.92.00"></script> <script defer src="modules/ui/submap.js?v=1.92.00"></script>

View file

@ -97,7 +97,7 @@ window.BurgsAndStates = (function () {
const name = Names.getState(basename, b.culture); const name = Names.getState(basename, b.culture);
const type = cultures[b.culture].type; const type = cultures[b.culture].type;
const coa = COA.generate(null, null, null, type); const coa = COA.generate(null, null, null, type, cultures, b.culture);
coa.shield = COA.getShield(b.culture, null); coa.shield = COA.getShield(b.culture, null);
states.push({ states.push({
i, i,
@ -216,7 +216,7 @@ window.BurgsAndStates = (function () {
if (b.culture !== state.culture) kinship -= 0.25; if (b.culture !== state.culture) kinship -= 0.25;
b.type = getType(i, b.port); b.type = getType(i, b.port);
const type = b.capital && P(0.2) ? "Capital" : b.type === "Generic" ? "City" : b.type; const type = b.capital && P(0.2) ? "Capital" : b.type === "Generic" ? "City" : b.type;
b.coa = COA.generate(stateCOA, kinship, null, type); b.coa = COA.generate(stateCOA, kinship, null, type, pack.cultures, b.culture);
b.coa.shield = COA.getShield(b.culture, b.state); b.coa.shield = COA.getShield(b.culture, b.state);
} }
@ -1005,7 +1005,7 @@ window.BurgsAndStates = (function () {
const color = getMixedColor(s.color); const color = getMixedColor(s.color);
const kinship = nameByBurg ? 0.8 : 0.4; const kinship = nameByBurg ? 0.8 : 0.4;
const type = getType(center, burg.port); const type = getType(center, burg.port);
const coa = COA.generate(stateBurgs[i].coa, kinship, null, type); const coa = COA.generate(stateBurgs[i].coa, kinship, null, type, pack.cultures, c);
coa.shield = COA.getShield(c, s.i); coa.shield = COA.getShield(c, s.i);
s.provinces.push(provinceId); s.provinces.push(provinceId);
@ -1144,7 +1144,7 @@ window.BurgsAndStates = (function () {
const dominion = colony ? P(0.95) : singleIsle || isleGroup ? P(0.7) : P(0.3); const dominion = colony ? P(0.95) : singleIsle || isleGroup ? P(0.7) : P(0.3);
const kinship = dominion ? 0 : 0.4; const kinship = dominion ? 0 : 0.4;
const type = getType(center, burgs[burg]?.port); const type = getType(center, burgs[burg]?.port);
const coa = COA.generate(s.coa, kinship, dominion, type); const coa = COA.generate(s.coa, kinship, dominion, type, pack.cultures, c);
coa.shield = COA.getShield(c, s.i); coa.shield = COA.getShield(c, s.i);
provinces.push({i: provinceId, state: s.i, center, burg, name, formName, fullName, color, coa}); provinces.push({i: provinceId, state: s.i, center, burg, name, formName, fullName, color, coa});

View file

@ -933,6 +933,70 @@ window.COA = (function () {
ornaments: 0, // 9 charges ornaments: 0, // 9 charges
uploaded: 0 uploaded: 0
}, },
typesNature: {
simpleShapes: 1,
beasts: 7,
beastHeads: 3,
birds: 3,
reptiles: 2,
bugs: 2,
fishes: 1,
molluscs: 1,
plants: 3,
fantastic: 5,
inescutcheon: 1
},
typesNatureAndThings: {
simpleShapes: 2,
beasts: 7,
beastHeads: 3,
birds: 3,
reptiles: 2,
bugs: 2,
fishes: 1,
molluscs: 1,
plants: 3,
fantastic: 5,
agriculture: 2,
arms: 5,
architecture: 3,
seafaring: 3,
tools: 3,
miscellaneous: 5,
inescutcheon: 1
},
typesThings: {
simpleShapes: 3,
agriculture: 2,
arms: 8,
architecture: 3,
seafaring: 3,
tools: 5,
miscellaneous: 5,
inescutcheon: 1
},
typesLimited: {
conventional: 33,
beasts: 7,
beastHeads: 3,
birds: 3,
reptiles: 2,
bugs: 2,
fishes: 1,
molluscs: 1,
plants: 3,
fantastic: 5,
agriculture: 2,
arms: 5,
architecture: 3,
seafaring: 3,
tools: 3,
miscellaneous: 5,
inescutcheon: 3
},
typesShapes: {
simpleShapes: 1
},
single: { single: {
conventional: 10, conventional: 10,
crosses: 8, crosses: 8,
@ -959,14 +1023,103 @@ window.COA = (function () {
conventional: 4, conventional: 4,
crosses: 1 crosses: 1
}, },
singleNature: {
simpleShapes: 1,
beasts: 7,
beastHeads: 3,
birds: 3,
reptiles: 2,
bugs: 2,
fishes: 1,
molluscs: 1,
plants: 3,
fantastic: 5,
inescutcheon: 1
},
singleNatureAndThings: {
simpleShapes: 2,
beasts: 7,
beastHeads: 3,
birds: 3,
reptiles: 2,
bugs: 2,
fishes: 1,
molluscs: 1,
plants: 3,
fantastic: 5,
agriculture: 2,
arms: 5,
architecture: 3,
seafaring: 3,
tools: 3,
miscellaneous: 5,
inescutcheon: 1
},
singleThings: {
simpleShapes: 3,
agriculture: 2,
arms: 5,
architecture: 3,
seafaring: 3,
tools: 3,
miscellaneous: 5,
inescutcheon: 1
},
singleLimited: {
conventional: 12,
beasts: 7,
beastHeads: 3,
birds: 3,
reptiles: 2,
bugs: 2,
fishes: 1,
molluscs: 1,
plants: 3,
fantastic: 5,
agriculture: 2,
arms: 5,
architecture: 3,
seafaring: 3,
tools: 3,
miscellaneous: 5,
inescutcheon: 1
},
singleShapes: {
simpleShapes: 1
},
semy: {conventional: 4, crosses: 1},
semyLimited: {conventional: 1},
semyShapes: {simpleShapes: 1},
simpleShapes: {
lozenge: 2,
fusil: 8,
mascle: 5,
rustre: 3,
lozengePloye: 1,
roundel: 10,
annulet: 7,
mullet: 4,
mulletPierced: 1,
mullet4: 5,
mullet6: 4,
mullet6Pierced: 1,
mullet7: 1,
mullet8: 5,
billet: 1,
delf: 8,
triangle: 8,
trianglePierced: 4,
carreau: 1,
spiral: 1
},
conventional: { conventional: {
annulet: 4, annulet: 4,
billet: 5, billet: 3,
carreau: 1, carreau: 1,
comet: 1, comet: 1,
compassRose: 1, compassRose: 1,
crescent: 5, crescent: 5,
delf: 0, delf: 3,
estoile: 1, estoile: 1,
fleurDeLis: 6, fleurDeLis: 6,
fountain: 1, fountain: 1,
@ -1381,7 +1534,267 @@ window.COA = (function () {
ribbon7: 1, ribbon7: 1,
ribbon8: 1 ribbon8: 1
}, },
data: chargeData data: chargeData,
excludeNoCharges: [
],
excludeUnusualCharges: [
"roundel2",
"mulletFaceted",
"mullet6Faceted",
"compassRose",
"heart",
"trefle",
"pique",
"fleurDeLis",
"sunInSplendour",
"sunInSplendour2",
"moonInCrescent",
"fountain",
"agnusDei",
"crown2",
"mitre",
"orb",
"fasces",
"cannon",
"centaur",
"sagittarius"
],
excludeThingCharges: [
"garb",
"millstone",
"plough",
"ploughshare",
"rake",
"scythe",
"scythe2",
"sickle",
"arbalest",
"arbalest2",
"arrow",
"arrowsSheaf",
"axe",
"bow",
"bowWithArrow",
"bowWithThreeArrows",
"cannon",
"falchion",
"flamberge",
"flangedMace",
"gauntlet",
"grenade",
"hatchet",
"helmet",
"helmetCorinthian",
"helmetGreat",
"helmetZischagge",
"lanceHead",
"lanceWithBanner",
"lochaberAxe",
"mace",
"maces",
"mallet",
"rapier",
"sabre",
"sabre2",
"sabresCrossed",
"shield",
"spear",
"sword",
"bridge",
"bridge2",
"castle",
"castle2",
"column",
"lighthouse",
"palace",
"pillar",
"portcullis",
"tower",
"windmill",
"anchor",
"armillarySphere",
"boat",
"boat2",
"caravel",
"drakkar",
"lymphad",
"raft",
"shipWheel",
"anvil",
"drawingCompass",
"fan",
"hook",
"ladder",
"ladder2",
"pincers",
"saw",
"scale",
"scaleImbalanced",
"scalesHanging",
"scissors",
"scissors2",
"shears",
"trowel",
"attire",
"banner",
"bell",
"bookClosed",
"bookClosed2",
"bookOpen",
"bucket",
"buckle",
"bugleHorn",
"bugleHorn2",
"chain",
"chalice",
"cowHorns",
"crosier",
"crown",
"crown2",
"drum",
"fasces",
"feather",
"harp",
"horseshoe",
"hourglass",
"key",
"laurelWreath",
"laurelWreath2",
"log",
"lute",
"lyre",
"mitre",
"orb",
"pot",
"ramsHorn",
"sceptre",
"scrollClosed",
"snowflake",
"stagsAttires",
"stirrup",
"wheel",
"wing",
"wingSword"
],
excludeNatureCharges: [
"agnusDei",
"badgerStatant",
"bearPassant",
"bearRampant",
"boarRampant",
"bullPassant",
"camel",
"catPassantGuardant",
"cowStatant",
"dolphin",
"elephant",
"goat",
"greyhoundCourant",
"greyhoundRampant",
"greyhoundSejant",
"hedgehog",
"hindStatant",
"horsePassant",
"horseRampant",
"horseSalient",
"lamb",
"lambPassantReguardant",
"lionPassant",
"lionPassantGuardant",
"lionRampant",
"lionSejant",
"martenCourant",
"mastiffStatant",
"porcupine",
"rabbitSejant",
"ramPassant",
"ratRampant",
"rhinoceros",
"squirrel",
"stagLodgedRegardant",
"stagPassant",
"talbotPassant",
"talbotSejant",
"wolfPassant",
"wolfRampant",
"wolfStatant",
"boarHeadErased",
"bullHeadCaboshed",
"deerHeadCaboshed",
"donkeyHeadCaboshed",
"elephantHeadErased",
"horseHeadCouped",
"lionHeadCaboshed",
"lionHeadErased",
"ramHeadErased",
"wolfHeadErased",
"cock",
"dove",
"doveDisplayed",
"duck",
"eagle",
"falcon",
"heron",
"owl",
"owlDisplayed",
"parrot",
"peacock",
"peacockInPride",
"raven",
"swallow",
"swan",
"swanErased",
"crocodile",
"frog",
"lizard",
"ouroboros",
"snake",
"bee",
"butterfly",
"cancer",
"dragonfly",
"fly",
"ladybird",
"scorpion",
"wasp",
"pike",
"plaice",
"salmon",
"escallop",
"snail",
"apple",
"cinquefoil",
"earOfWheat",
"grapeBunch",
"grapeBunch2",
"mapleLeaf",
"oak",
"palmTree",
"pear",
"pineCone",
"pineTree",
"quatrefoil",
"rose",
"sextifoil",
"thistle",
"tree",
"trefoil",
"wheatStalk",
"angel",
"basilisk",
"centaur",
"dragonPassant",
"dragonRampant",
"eagleTwoHeads",
"griffinPassant",
"griffinRampant",
"pegasus",
"sagittarius",
"serpent",
"unicornRampant",
"wyvern",
"wyvernWithWingsDisplayed",
]
}; };
// charges specific to culture or burg type (FMG-only config, not coming from Armoria) // charges specific to culture or burg type (FMG-only config, not coming from Armoria)
@ -1640,6 +2053,66 @@ window.COA = (function () {
firTrees: 1 firTrees: 1
}; };
const excludeComplexLines = [
"engrailed",
"invecked",
"raguly",
"urdy",
"dentilly",
"bevilled",
"flechy",
"barby",
"enclavy",
"escartely",
"nowy",
"nowyReversed",
"embattledGhibellin",
"embattledNotched",
"embattledGrady",
"dovetailedIndented",
"dovetailed",
"potenty",
"potentyDexter",
"potentySinister",
"nebuly",
"firTrees"
];
const excludeNonStraightLines = [
"wavy",
"engrailed",
"invecked",
"rayonne",
"embattled",
"raguly",
"urdy",
"dancetty",
"indented",
"dentilly",
"bevilled",
"angled",
"flechy",
"barby",
"enclavy",
"escartely",
"arched",
"archedReversed",
"nowy",
"nowyReversed",
"embattledGhibellin",
"embattledNotched",
"embattledGrady",
"dovetailedIndented",
"dovetailed",
"potenty",
"potentyDexter",
"potentySinister",
"nebuly",
"seaWaves",
"dragonTeeth",
"firTrees"
];
const divisions = { const divisions = {
variants: { variants: {
perPale: 5, perPale: 5,
@ -1854,21 +2327,112 @@ window.COA = (function () {
middleEarth: {noldor: 1, gondor: 1, easterling: 1, erebor: 1, ironHills: 1, urukHai: 1, moriaOrc: 1} middleEarth: {noldor: 1, gondor: 1, easterling: 1, erebor: 1, ironHills: 1, urukHai: 1, moriaOrc: 1}
}; };
const generate = function (parent, kinship, dominion, type) { const getCharges = function (cultures, culture) {
if (cultures[culture].charges) return cultures[culture].charges;
ERROR && console.error("Shield charges option is not defined on culture level", cultures[culture]);
return "Limited";
};
const getLines = function (cultures, culture) {
if (cultures[culture].lines) return cultures[culture].lines;
ERROR && console.error("Shield lines option is not defined on culture level", cultures[culture]);
return "Straight";
};
const getTinctures = function (cultures, culture) {
if (cultures[culture].tinctures) return cultures[culture].tinctures;
ERROR && console.error("Shield tinctures option is not defined on culture level", cultures[culture]);
return "TinctureOnly";
};
const generate = function (parent, kinship, dominion, type, cultures, culture) {
if (!parent || parent.custom) { if (!parent || parent.custom) {
parent = null; parent = null;
kinship = 0; kinship = 0;
dominion = 0; dominion = 0;
} }
const chargesSetting = getCharges(cultures, culture);
const linesSetting = getLines(cultures, culture);
const tincturesSetting = getTinctures(cultures, culture);
let chargesTypesSet = charges.types;
let chargesSingleSet = charges.single;
let chargesSemySet = charges.semy;
let excludeChargesSet = charges.excludeNoCharges;
let excludeCultureChargesSet = charges.excludeNoCharges;
let excludeLinesSet = [];
let excludeTincturesSet = [];
let allowCharges = true;
let allowOrdinaries = true;
let allowTypeMapping = true;
if (chargesSetting === "Shapes") {
chargesTypesSet = charges.typesShapes;
chargesSingleSet = charges.singleShapes;
chargesSemySet = charges.semyShapes;
excludeChargesSet = charges.excludeUnusualCharges;
allowTypeMapping = false;
} else if (chargesSetting === "Limited") {
chargesTypesSet = charges.typesLimited;
chargesSingleSet = charges.singleLimited;
chargesSemySet = charges.semyLimited;
excludeChargesSet = charges.excludeUnusualCharges;
excludeCultureChargesSet = charges.excludeUnusualCharges;
} else if (chargesSetting === "Nature") {
chargesTypesSet = charges.typesNature;
chargesSingleSet = charges.singleNature;
chargesSemySet = null;
excludeChargesSet = charges.excludeUnusualCharges;
excludeCultureChargesSet = charges.excludeThingCharges;
} else if (chargesSetting === "Nature&Things") {
chargesTypesSet = charges.typesLimited;
chargesSingleSet = charges.singleLimited;
chargesSemySet = charges.semyShapes;
excludeChargesSet = charges.excludeUnusualCharges;
} else if (chargesSetting === "Things") {
chargesTypesSet = charges.typesThings;
chargesSingleSet = charges.singleThings;
chargesSemySet = charges.semyShapes;
excludeChargesSet = charges.excludeUnusualCharges;
excludeCultureChargesSet = charges.excludeNatureCharges;
} else if (chargesSetting === "None") {
let allowCharges = false;
chargesTypesSet = charges.typesShapes;
chargesSingleSet = charges.singleShapes;
chargesSemySet = charges.semyShapes;
excludeChargesSet = charges.excludeUnusualCharges;
allowTypeMapping = false;
}
if (linesSetting === "Limited") {
excludeLinesSet = excludeComplexLines;
} else if (linesSetting === "Straight") {
excludeLinesSet = excludeNonStraightLines;
} else if (linesSetting === "None") {
// This option disables oridinaries
allowOrdinaries = false;
excludeLinesSet = excludeNonStraightLines;
}
if (tincturesSetting === "NoPatterns") {
excludeTincturesSet = ['patterns']
} else if (tincturesSetting === "NoStains") {
excludeTincturesSet = ['patterns', 'stains']
}
let usedPattern = null; let usedPattern = null;
let usedTinctures = []; let usedTinctures = [];
const t1 = P(kinship) ? parent.t1 : getTincture("field"); const t1 = P(kinship) ? parent.t1 : getTincture("field", excludeTincturesSet);
if (t1.includes("-")) usedPattern = t1; if (t1.includes("-")) usedPattern = t1;
const coa = {t1}; const coa = {t1};
const addCharge = P(usedPattern ? 0.5 : 0.93); // 80% for charge let addCharge = allowCharges && P(usedPattern ? 0.5 : 0.93) ? true : false;
// 80% for charge if not chargesSetting == "None"
const linedOrdinary = const linedOrdinary =
(addCharge && P(0.3)) || P(0.5) (addCharge && P(0.3)) || P(0.5)
? parent?.ordinaries && P(kinship) ? parent?.ordinaries && P(kinship)
@ -1876,8 +2440,9 @@ window.COA = (function () {
: rw(ordinaries.lined) : rw(ordinaries.lined)
: null; : null;
const ordinary = const ordinary =
(!addCharge && P(0.65)) || P(0.3) ? (linedOrdinary ? linedOrdinary : rw(ordinaries.straight)) : null; // 36% for ordinary allowOrdinaries && ((!addCharge && P(0.65)) || P(0.3)) ? (linedOrdinary ? linedOrdinary : rw(ordinaries.straight)) : null; // 36% for ordinary if allowOrdinaries is true
const rareDivided = ["chief", "terrace", "chevron", "quarter", "flaunches"].includes(ordinary); const rareDivided = ["chief", "terrace", "chevron", "quarter", "flaunches"].includes(ordinary);
@ -1898,15 +2463,16 @@ window.COA = (function () {
})(); })();
if (division) { if (division) {
const t = getTincture("division", usedTinctures, P(0.98) ? coa.t1 : null); const t = getTincture("division", excludeTincturesSet, usedTinctures, P(0.98) ? coa.t1 : null);
coa.division = {division, t}; coa.division = {division, t};
if (divisions[division]) if (divisions[division])
coa.division.line = usedPattern || (ordinary && P(0.7)) ? "straight" : rw(divisions[division]); coa.division.line = usedPattern || (ordinary && P(0.7))
? "straight" : rwx(divisions[division], excludeLinesSet);
} }
if (ordinary) { if (ordinary) {
coa.ordinaries = [{ordinary, t: getTincture("charge", usedTinctures, coa.t1)}]; coa.ordinaries = [{ordinary, t: getTincture("charge", excludeTincturesSet, usedTinctures, coa.t1)}];
if (linedOrdinary) coa.ordinaries[0].line = usedPattern || (division && P(0.7)) ? "straight" : rw(lines); if (linedOrdinary) coa.ordinaries[0].line = usedPattern || (division && P(0.7)) ? "straight" : rw(lines, excludeLinesSet);
if (division && !addCharge && !usedPattern && P(0.5) && ordinary !== "bordure" && ordinary !== "orle") { if (division && !addCharge && !usedPattern && P(0.5) && ordinary !== "bordure" && ordinary !== "orle") {
if (P(0.8)) coa.ordinaries[0].divided = "counter"; if (P(0.8)) coa.ordinaries[0].divided = "counter";
// 40% // 40%
@ -1919,8 +2485,9 @@ window.COA = (function () {
if (addCharge) { if (addCharge) {
const charge = (() => { const charge = (() => {
if (parent?.charges && P(kinship - 0.1)) return parent.charges[0].charge; if (parent?.charges && P(kinship - 0.1)) return parent.charges[0].charge;
if (type && type !== "Generic" && P(0.3)) return rw(typeMapping[type]); if (type && allowTypeMapping && type !== "Generic" && P(0.3))
return selectCharge(ordinary || divisioned ? charges.types : charges.single); return rwx(typeMapping[type], excludeCultureChargesSet);
return selectCharge(ordinary || divisioned ? chargesTypesSet : chargesSingleSet, chargesTypesSet, chargesSingleSet, excludeCultureChargesSet);
})(); })();
const chargeData = charges.data[charge] || {}; const chargeData = charges.data[charge] || {};
@ -1933,54 +2500,56 @@ window.COA = (function () {
if (ordinaryData?.positionsOn && P(0.8)) { if (ordinaryData?.positionsOn && P(0.8)) {
// place charge over ordinary (use tincture of field type) // place charge over ordinary (use tincture of field type)
p = rw(ordinaryData.positionsOn); p = rw(ordinaryData.positionsOn);
t = !usedPattern && P(0.3) ? coa.t1 : getTincture("charge", [], tOrdinary); t = !usedPattern && P(0.3) ? coa.t1 : getTincture("charge", excludeTincturesSet, [], tOrdinary);
} else if (ordinaryData?.positionsOff && P(0.95)) { } else if (ordinaryData?.positionsOff && P(0.95)) {
// place charge out of ordinary (use tincture of ordinary type) // place charge out of ordinary (use tincture of ordinary type)
p = rw(ordinaryData.positionsOff); p = rw(ordinaryData.positionsOff);
t = !usedPattern && P(0.3) ? tOrdinary : getTincture("charge", usedTinctures, coa.t1); t = !usedPattern && P(0.3) ? tOrdinary : getTincture("charge", excludeTincturesSet, usedTinctures, coa.t1);
} else if (positions.divisions[division]) { } else if (positions.divisions[division]) {
// place charge in fields made by division // place charge in fields made by division
p = rw(positions.divisions[division]); p = rw(positions.divisions[division]);
t = getTincture("charge", tOrdinary ? usedTinctures.concat(tOrdinary) : usedTinctures, coa.t1); t = getTincture("charge", excludeTincturesSet, tOrdinary ? usedTinctures.concat(tOrdinary) : usedTinctures, coa.t1);
} else if (chargeData.positions) { } else if (chargeData.positions) {
// place charge-suitable position // place charge-suitable position
p = rw(chargeData.positions); p = rw(chargeData.positions);
t = getTincture("charge", usedTinctures, coa.t1); t = getTincture("charge", excludeTincturesSet, usedTinctures, coa.t1);
} else { } else {
// place in standard position (use new tincture) // place in standard position (use new tincture)
p = usedPattern ? "e" : charges.conventional[charge] ? rw(positions.conventional) : rw(positions.complex); p = usedPattern ? "e" : charges.conventional[charge] ? rw(positions.conventional) : rw(positions.complex);
t = getTincture("charge", usedTinctures.concat(tOrdinary), coa.t1); t = getTincture("charge", excludeTincturesSet, usedTinctures.concat(tOrdinary), coa.t1);
} }
if (chargeData.natural && chargeData.natural !== t && chargeData.natural !== tOrdinary) t = chargeData.natural; if (chargeData.natural && chargeData.natural !== t && chargeData.natural !== tOrdinary) t = chargeData.natural;
const item = {charge: charge, t, p}; const item = {charge: charge, t, p};
const colors = chargeData.colors || 1; const colors = chargeData.colors || 1;
if (colors > 1) item.t2 = P(0.25) ? getTincture("charge", usedTinctures, coa.t1) : t; if (colors > 1) item.t2 = P(0.25) ? getTincture("charge", excludeTincturesSet, usedTinctures, coa.t1) : t;
if (colors > 2 && item.t2) item.t3 = P(0.5) ? getTincture("charge", usedTinctures, coa.t1) : t; if (colors > 2 && item.t2) item.t3 = P(0.5) ? getTincture("charge", excludeTincturesSet, usedTinctures, coa.t1) : t;
coa.charges = [item]; coa.charges = [item];
if (p === "ABCDEFGHIKL" && P(0.95)) { if (p === "ABCDEFGHIKL" && P(0.95)) {
// add central charge if charge is in bordure // add central charge if charge is in bordure
coa.charges[0].charge = rw(charges.conventional); coa.charges[0].charge = rwx(chargesSingleSet, excludeChargesSet);
const charge = selectCharge(charges.single); const charge = selectCharge(chargesSingleSet, chargesTypesSet, chargesSingleSet, excludeChargesSet)
const t = getTincture("charge", usedTinctures, coa.t1); ;
const t = getTincture("charge", excludeTincturesSet, usedTinctures, coa.t1);
coa.charges.push({charge, t, p: "e"}); coa.charges.push({charge, t, p: "e"});
} else if (P(0.8) && charge === "inescutcheon") { } else if (P(0.8) && charge === "inescutcheon") {
// add charge to inescutcheon // add charge to inescutcheon
const charge = selectCharge(charges.types); const charge = selectCharge(chargesTypesSet, chargesTypesSet, chargesSingleSet, excludeChargesSet)
const t2 = getTincture("charge", [], t); ;
const t2 = getTincture("charge", excludeTincturesSet, [], t);
coa.charges.push({charge, t: t2, p, size: 0.5}); coa.charges.push({charge, t: t2, p, size: 0.5});
} else if (division && !ordinary) { } else if (division && !ordinary) {
const allowCounter = !usedPattern && (!coa.line || coa.line === "straight"); const allowCounter = !usedPattern && (!coa.line || coa.line === "straight");
// dimidiation: second charge at division basic positons // dimidiation: second charge at division basic positons
if (P(0.3) && ["perPale", "perFess"].includes(division) && coa.line === "straight") { if (P(0.3) && ["perPale", "perFess"].includes(division) && coa.line === "straight") {
coa.charges[0].divided = "field"; coa.charges[0].divided = "field";
if (P(0.95)) { if (P(0.95)) {
const p2 = p === "e" || P(0.5) ? "e" : rw(positions.divisions[division]); const p2 = p === "e" || P(0.5) ? "e" : rw(positions.divisions[division]);
const charge = selectCharge(charges.single); const charge = selectCharge(chargesSingleSet, chargesTypesSet, chargesSingleSet, excludeChargesSet)
const t = getTincture("charge", usedTinctures, coa.division.t); ;
const t = getTincture("charge", excludeTincturesSet, usedTinctures, coa.division.t);
coa.charges.push({charge, t, p: p2, divided: "division"}); coa.charges.push({charge, t, p: p2, divided: "division"});
} }
} else if (allowCounter && P(0.4)) coa.charges[0].divided = "counter"; } else if (allowCounter && P(0.4)) coa.charges[0].divided = "counter";
@ -1997,22 +2566,26 @@ window.COA = (function () {
: ["j", "o"]; // perBendSinister : ["j", "o"]; // perBendSinister
coa.charges[0].p = p1; coa.charges[0].p = p1;
const charge = selectCharge(charges.single); const charge = selectCharge(chargesSingleSet, chargesTypesSet, chargesSingleSet, excludeChargesSet)
const t = getTincture("charge", usedTinctures, coa.division.t); ;
const t = getTincture("charge", excludeTincturesSet, usedTinctures, coa.division.t);
coa.charges.push({charge, t, p: p2}); coa.charges.push({charge, t, p: p2});
} else if (["perCross", "perSaltire"].includes(division) && P(0.5)) { } else if (["perCross", "perSaltire"].includes(division) && P(0.5)) {
// place 4 charges in division standard positions // place 4 charges in division standard positions
const [p1, p2, p3, p4] = division === "perCross" ? ["j", "l", "m", "o"] : ["b", "d", "f", "h"]; const [p1, p2, p3, p4] = division === "perCross" ? ["j", "l", "m", "o"] : ["b", "d", "f", "h"];
coa.charges[0].p = p1; coa.charges[0].p = p1;
const c2 = selectCharge(charges.single); const c2 = selectCharge(chargesSingleSet, chargesTypesSet, chargesSingleSet, excludeChargesSet)
const t2 = getTincture("charge", [], coa.division.t); ;
const t2 = getTincture("charge", excludeTincturesSet, [], coa.division.t);
const c3 = selectCharge(charges.single); const c3 = selectCharge(chargesSingleSet, chargesTypesSet, chargesSingleSet, excludeChargesSet)
const t3 = getTincture("charge", [], coa.division.t); ;
const t3 = getTincture("charge", excludeTincturesSet, [], coa.division.t);
const c4 = selectCharge(charges.single); const c4 = selectCharge(chargesSingleSet, chargesTypesSet, chargesSingleSet, excludeChargesSet)
const t4 = getTincture("charge", [], coa.t1); ;
const t4 = getTincture("charge", excludeTincturesSet, [], coa.t1);
coa.charges.push({charge: c2, t: t2, p: p2}, {charge: c3, t: t3, p: p3}, {charge: c4, t: t4, p: p4}); coa.charges.push({charge: c2, t: t2, p: p2}, {charge: c3, t: t3, p: p3}, {charge: c4, t: t4, p: p4});
} else if (allowCounter && p.length > 1) coa.charges[0].divided = "counter"; // counterchanged, 40% } else if (allowCounter && p.length > 1) coa.charges[0].divided = "counter"; // counterchanged, 40%
} }
@ -2023,7 +2596,7 @@ window.COA = (function () {
// dominions have canton with parent coa // dominions have canton with parent coa
if (P(dominion) && parent.charges) { if (P(dominion) && parent.charges) {
const invert = isSameType(parent.t1, coa.t1); const invert = isSameType(parent.t1, coa.t1);
const t = invert ? getTincture("division", usedTinctures, coa.t1) : parent.t1; const t = invert ? getTincture("division", excludeTincturesSet, usedTinctures, coa.t1) : parent.t1;
const canton = {ordinary: "canton", t}; const canton = {ordinary: "canton", t};
coa.charges?.forEach((charge, i) => { coa.charges?.forEach((charge, i) => {
@ -2036,7 +2609,7 @@ window.COA = (function () {
if (charge === "inescutcheon" && parent.charges[1]) charge = parent.charges[1].charge; if (charge === "inescutcheon" && parent.charges[1]) charge = parent.charges[1].charge;
let t2 = invert ? parent.t1 : parent.charges[0].t; let t2 = invert ? parent.t1 : parent.charges[0].t;
if (isSameType(t, t2)) t2 = getTincture("charge", usedTinctures, t); if (isSameType(t, t2)) t2 = getTincture("charge", excludeTincturesSet, usedTinctures, t);
if (!coa.charges) coa.charges = []; if (!coa.charges) coa.charges = [];
coa.charges.push({charge, t: t2, p: "y", size: 0.5}); coa.charges.push({charge, t: t2, p: "y", size: 0.5});
@ -2044,19 +2617,19 @@ window.COA = (function () {
coa.ordinaries ? coa.ordinaries.push(canton) : (coa.ordinaries = [canton]); coa.ordinaries ? coa.ordinaries.push(canton) : (coa.ordinaries = [canton]);
} }
function selectCharge(set) { function selectCharge(set, chargesTypesSet, chargesSingleSet, exclude) {
const type = set ? rw(set) : ordinary || divisioned ? rw(charges.types) : rw(charges.single); const type = set ? rw(set) : ordinary || divisioned ? rw(chargesTypesSet) : rw(chargesSingleSet);
return type === "inescutcheon" ? "inescutcheon" : rw(charges[type]); return type === "inescutcheon" ? "inescutcheon" : rwx(charges[type], exclude);
} }
// select tincture: element type (field, division, charge), used field tinctures, field type to follow RoT // select tincture: element type (field, division, charge), used field tinctures, field type to follow RoT
function getTincture(element, fields = [], RoT) { function getTincture(element, exclude, fields = [], RoT) {
const base = RoT ? (RoT.includes("-") ? RoT.split("-")[1] : RoT) : null; const base = RoT ? (RoT.includes("-") ? RoT.split("-")[1] : RoT) : null;
let type = rw(tinctures[element]); // metals, colours, stains, patterns let type = rwx(tinctures[element], exclude); // metals, colours, stains, patterns
if (RoT && type !== "patterns") type = getType(base) === "metals" ? "colours" : "metals"; // follow RoT if (RoT && type !== "patterns") type = getType(base) === "metals" ? "colours" : "metals"; // follow RoT
if (type === "metals" && fields.includes("or") && fields.includes("argent")) type = "colours"; // exclude metals overuse if (type === "metals" && fields.includes("or") && fields.includes("argent")) type = "colours"; // exclude metals overuse
let tincture = rw(tinctures[type]); let tincture = rwx(tinctures[type], exclude);
while (tincture === base || fields.includes(tincture)) { while (tincture === base || fields.includes(tincture)) {
tincture = rw(tinctures[type]); tincture = rw(tinctures[type]);
@ -2150,7 +2723,7 @@ window.COA = (function () {
t1 = "gules"; t1 = "gules";
t2 = "argent"; t2 = "argent";
} }
} else if (pattern === "semy") pattern += "_of_" + selectCharge(charges.semy); } else if ((pattern === "semy") && (chargesSemySet !== null)) pattern += "_of_" + selectCharge(chargesSemySet, chargesTypesSet, chargesSingleSet, excludeChargesSet);
if (!t1 || !t2) { if (!t1 || !t2) {
const startWithMetal = P(0.7); const startWithMetal = P(0.7);

View file

@ -15,7 +15,7 @@ window.Cultures = (function () {
count = Math.floor(populated.length / 50); count = Math.floor(populated.length / 50);
if (!count) { if (!count) {
WARN && console.warn(`There are no populated cells. Cannot generate cultures`); WARN && console.warn(`There are no populated cells. Cannot generate cultures`);
pack.cultures = [{name: "Wildlands", i: 0, base: 1, shield: "round"}]; pack.cultures = [{name: "Wildlands", i: 0, base: 1, shield: "round", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"}];
cells.culture = cultureIds; cells.culture = cultureIds;
alertMessage.innerHTML = /* html */ `The climate is harsh and people cannot live in this world.<br /> alertMessage.innerHTML = /* html */ `The climate is harsh and people cannot live in this world.<br />
@ -106,7 +106,7 @@ window.Cultures = (function () {
} }
// the first culture with id 0 is for wildlands // the first culture with id 0 is for wildlands
cultures.unshift({name: "Wildlands", i: 0, base: 1, origins: [null], shield: "round"}); cultures.unshift({name: "Wildlands", i: 0, base: 1, origins: [null], shield: "round", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"});
// make sure all bases exist in nameBases // make sure all bases exist in nameBases
if (!nameBases.length) { if (!nameBases.length) {
@ -182,7 +182,7 @@ window.Cultures = (function () {
base = defaultCultures[culture].base; base = defaultCultures[culture].base;
name = defaultCultures[culture].name; name = defaultCultures[culture].name;
} else { } else {
// add random culture besed on one of the current ones // add random culture based on one of the current ones
culture = rand(pack.cultures.length - 1); culture = rand(pack.cultures.length - 1);
name = Names.getCulture(culture, 5, 8, ""); name = Names.getCulture(culture, 5, 8, "");
base = pack.cultures[culture].base; base = pack.cultures[culture].base;
@ -196,6 +196,8 @@ window.Cultures = (function () {
// define emblem shape // define emblem shape
let shield = culture.shield; let shield = culture.shield;
let charges = culture.charges;
let lines = culture.lines;
const emblemShape = document.getElementById("emblemShape").value; const emblemShape = document.getElementById("emblemShape").value;
if (emblemShape === "random") shield = getRandomShield(); if (emblemShape === "random") shield = getRandomShield();
@ -213,7 +215,10 @@ window.Cultures = (function () {
urban: 0, urban: 0,
origins: [0], origins: [0],
code, code,
shield shield,
charges,
lines,
tinctures
}); });
}; };
@ -236,82 +241,82 @@ window.Cultures = (function () {
if (culturesSet.value === "european") { if (culturesSet.value === "european") {
return [ return [
{name: "Shwazen", base: 0, odd: 1, sort: i => n(i) / td(i, 10) / bd(i, [6, 8]), shield: "swiss"}, {name: "Shwazen", base: 0, odd: 1, sort: i => n(i) / td(i, 10) / bd(i, [6, 8]), shield: "swiss", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Angshire", base: 1, odd: 1, sort: i => n(i) / td(i, 10) / sf(i), shield: "wedged"}, {name: "Angshire", base: 1, odd: 1, sort: i => n(i) / td(i, 10) / sf(i), shield: "wedged", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Luari", base: 2, odd: 1, sort: i => n(i) / td(i, 12) / bd(i, [6, 8]), shield: "french"}, {name: "Luari", base: 2, odd: 1, sort: i => n(i) / td(i, 12) / bd(i, [6, 8]), shield: "french", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Tallian", base: 3, odd: 1, sort: i => n(i) / td(i, 15), shield: "horsehead"}, {name: "Tallian", base: 3, odd: 1, sort: i => n(i) / td(i, 15), shield: "horsehead", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Astellian", base: 4, odd: 1, sort: i => n(i) / td(i, 16), shield: "spanish"}, {name: "Astellian", base: 4, odd: 1, sort: i => n(i) / td(i, 16), shield: "spanish", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Slovan", base: 5, odd: 1, sort: i => (n(i) / td(i, 6)) * t[i], shield: "polish"}, {name: "Slovan", base: 5, odd: 1, sort: i => (n(i) / td(i, 6)) * t[i], shield: "polish", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"},
{name: "Norse", base: 6, odd: 1, sort: i => n(i) / td(i, 5), shield: "heater"}, {name: "Norse", base: 6, odd: 1, sort: i => n(i) / td(i, 5), shield: "heater", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"},
{name: "Elladan", base: 7, odd: 1, sort: i => (n(i) / td(i, 18)) * h[i], shield: "boeotian"}, {name: "Elladan", base: 7, odd: 1, sort: i => (n(i) / td(i, 18)) * h[i], shield: "boeotian", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"},
{name: "Romian", base: 8, odd: 0.2, sort: i => n(i) / td(i, 15) / t[i], shield: "roman"}, {name: "Romian", base: 8, odd: 0.2, sort: i => n(i) / td(i, 15) / t[i], shield: "roman", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"},
{name: "Soumi", base: 9, odd: 1, sort: i => (n(i) / td(i, 5) / bd(i, [9])) * t[i], shield: "pavise"}, {name: "Soumi", base: 9, odd: 1, sort: i => (n(i) / td(i, 5) / bd(i, [9])) * t[i], shield: "pavise", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Portuzian", base: 13, odd: 1, sort: i => n(i) / td(i, 17) / sf(i), shield: "renaissance"}, {name: "Portuzian", base: 13, odd: 1, sort: i => n(i) / td(i, 17) / sf(i), shield: "renaissance", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Vengrian", base: 15, odd: 1, sort: i => (n(i) / td(i, 11) / bd(i, [4])) * t[i], shield: "horsehead2"}, {name: "Vengrian", base: 15, odd: 1, sort: i => (n(i) / td(i, 11) / bd(i, [4])) * t[i], shield: "horsehead2", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Turchian", base: 16, odd: 0.05, sort: i => n(i) / td(i, 14), shield: "round"}, {name: "Turchian", base: 16, odd: 0.05, sort: i => n(i) / td(i, 14), shield: "round", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Euskati", base: 20, odd: 0.05, sort: i => (n(i) / td(i, 15)) * h[i], shield: "oldFrench"}, {name: "Euskati", base: 20, odd: 0.05, sort: i => (n(i) / td(i, 15)) * h[i], shield: "oldFrench", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Keltan", base: 22, odd: 0.05, sort: i => (n(i) / td(i, 11) / bd(i, [6, 8])) * t[i], shield: "oval"} {name: "Keltan", base: 22, odd: 0.05, sort: i => (n(i) / td(i, 11) / bd(i, [6, 8])) * t[i], shield: "oval", charges: "Limited", lines: "All", tinctures: "All"}
]; ];
} }
if (culturesSet.value === "oriental") { if (culturesSet.value === "oriental") {
return [ return [
{name: "Koryo", base: 10, odd: 1, sort: i => n(i) / td(i, 12) / t[i], shield: "round"}, {name: "Koryo", base: 10, odd: 1, sort: i => n(i) / td(i, 12) / t[i], shield: "round", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{name: "Hantzu", base: 11, odd: 1, sort: i => n(i) / td(i, 13), shield: "banner"}, {name: "Hantzu", base: 11, odd: 1, sort: i => n(i) / td(i, 13), shield: "banner", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{name: "Yamoto", base: 12, odd: 1, sort: i => n(i) / td(i, 15) / t[i], shield: "round"}, {name: "Yamoto", base: 12, odd: 1, sort: i => n(i) / td(i, 15) / t[i], shield: "round", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{name: "Turchian", base: 16, odd: 1, sort: i => n(i) / td(i, 12), shield: "round"}, {name: "Turchian", base: 16, odd: 1, sort: i => n(i) / td(i, 12), shield: "round", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{ {
name: "Berberan", name: "Berberan",
base: 17, base: 17,
odd: 0.2, odd: 0.2,
sort: i => (n(i) / td(i, 19) / bd(i, [1, 2, 3], 7)) * t[i], sort: i => (n(i) / td(i, 19) / bd(i, [1, 2, 3], 7)) * t[i],
shield: "oval" shield: "oval", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"
}, },
{name: "Eurabic", base: 18, odd: 1, sort: i => (n(i) / td(i, 26) / bd(i, [1, 2], 7)) * t[i], shield: "oval"}, {name: "Eurabic", base: 18, odd: 1, sort: i => (n(i) / td(i, 26) / bd(i, [1, 2], 7)) * t[i], shield: "oval", charges: "Shapes", lines: "All", tinctures: "All"},
{name: "Efratic", base: 23, odd: 0.1, sort: i => (n(i) / td(i, 22)) * t[i], shield: "round"}, {name: "Efratic", base: 23, odd: 0.1, sort: i => (n(i) / td(i, 22)) * t[i], shield: "round", charges: "Shapes", lines: "All", tinctures: "All"},
{name: "Tehrani", base: 24, odd: 1, sort: i => (n(i) / td(i, 18)) * h[i], shield: "round"}, {name: "Tehrani", base: 24, odd: 1, sort: i => (n(i) / td(i, 18)) * h[i], shield: "round", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{name: "Maui", base: 25, odd: 0.2, sort: i => n(i) / td(i, 24) / sf(i) / t[i], shield: "vesicaPiscis"}, {name: "Maui", base: 25, odd: 0.2, sort: i => n(i) / td(i, 24) / sf(i) / t[i], shield: "vesicaPiscis", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{name: "Carnatic", base: 26, odd: 0.5, sort: i => n(i) / td(i, 26), shield: "round"}, {name: "Carnatic", base: 26, odd: 0.5, sort: i => n(i) / td(i, 26), shield: "round", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{name: "Vietic", base: 29, odd: 0.8, sort: i => n(i) / td(i, 25) / bd(i, [7], 7) / t[i], shield: "banner"}, {name: "Vietic", base: 29, odd: 0.8, sort: i => n(i) / td(i, 25) / bd(i, [7], 7) / t[i], shield: "banner", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{name: "Guantzu", base: 30, odd: 0.5, sort: i => n(i) / td(i, 17), shield: "banner"}, {name: "Guantzu", base: 30, odd: 0.5, sort: i => n(i) / td(i, 17), shield: "banner", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{name: "Ulus", base: 31, odd: 1, sort: i => (n(i) / td(i, 5) / bd(i, [2, 4, 10], 7)) * t[i], shield: "banner"} {name: "Ulus", base: 31, odd: 1, sort: i => (n(i) / td(i, 5) / bd(i, [2, 4, 10], 7)) * t[i], shield: "banner", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"}
]; ];
} }
if (culturesSet.value === "english") { if (culturesSet.value === "english") {
const getName = () => Names.getBase(1, 5, 9, "", 0); const getName = () => Names.getBase(1, 5, 9, "", 0);
return [ return [
{name: getName(), base: 1, odd: 1, shield: "heater"}, {name: getName(), base: 1, odd: 1, shield: "heater", charges: "Limited", lines: "All", tinctures: "All"},
{name: getName(), base: 1, odd: 1, shield: "wedged"}, {name: getName(), base: 1, odd: 1, shield: "wedged", charges: "Limited", lines: "All", tinctures: "All"},
{name: getName(), base: 1, odd: 1, shield: "swiss"}, {name: getName(), base: 1, odd: 1, shield: "swiss", charges: "Limited", lines: "All", tinctures: "All"},
{name: getName(), base: 1, odd: 1, shield: "oldFrench"}, {name: getName(), base: 1, odd: 1, shield: "oldFrench", charges: "Limited", lines: "All", tinctures: "All"},
{name: getName(), base: 1, odd: 1, shield: "swiss"}, {name: getName(), base: 1, odd: 1, shield: "swiss", charges: "Limited", lines: "All", tinctures: "All"},
{name: getName(), base: 1, odd: 1, shield: "spanish"}, {name: getName(), base: 1, odd: 1, shield: "spanish", charges: "Limited", lines: "All", tinctures: "All"},
{name: getName(), base: 1, odd: 1, shield: "hessen"}, {name: getName(), base: 1, odd: 1, shield: "hessen", charges: "Limited", lines: "All", tinctures: "All"},
{name: getName(), base: 1, odd: 1, shield: "fantasy5"}, {name: getName(), base: 1, odd: 1, shield: "fantasy5", charges: "Limited", lines: "All", tinctures: "All"},
{name: getName(), base: 1, odd: 1, shield: "fantasy4"}, {name: getName(), base: 1, odd: 1, shield: "fantasy4", charges: "Limited", lines: "All", tinctures: "All"},
{name: getName(), base: 1, odd: 1, shield: "fantasy1"} {name: getName(), base: 1, odd: 1, shield: "fantasy1", charges: "Limited", lines: "All", tinctures: "All"}
]; ];
} }
if (culturesSet.value === "antique") { if (culturesSet.value === "antique") {
return [ return [
{name: "Roman", base: 8, odd: 1, sort: i => n(i) / td(i, 14) / t[i], shield: "roman"}, // Roman {name: "Roman", base: 8, odd: 1, sort: i => n(i) / td(i, 14) / t[i], shield: "roman", charges: "Limited", lines: "All", tinctures: "All"}, // Roman
{name: "Roman", base: 8, odd: 1, sort: i => n(i) / td(i, 15) / sf(i), shield: "roman"}, // Roman {name: "Roman", base: 8, odd: 1, sort: i => n(i) / td(i, 15) / sf(i), shield: "roman", charges: "Limited", lines: "All", tinctures: "All"}, // Roman
{name: "Roman", base: 8, odd: 1, sort: i => n(i) / td(i, 16) / sf(i), shield: "roman"}, // Roman {name: "Roman", base: 8, odd: 1, sort: i => n(i) / td(i, 16) / sf(i), shield: "roman", charges: "Limited", lines: "All", tinctures: "All"}, // Roman
{name: "Roman", base: 8, odd: 1, sort: i => n(i) / td(i, 17) / t[i], shield: "roman"}, // Roman {name: "Roman", base: 8, odd: 1, sort: i => n(i) / td(i, 17) / t[i], shield: "roman", charges: "Limited", lines: "All", tinctures: "All"}, // Roman
{name: "Hellenic", base: 7, odd: 1, sort: i => (n(i) / td(i, 18) / sf(i)) * h[i], shield: "boeotian"}, // Greek {name: "Hellenic", base: 7, odd: 1, sort: i => (n(i) / td(i, 18) / sf(i)) * h[i], shield: "boeotian", charges: "Limited", lines: "All", tinctures: "All"}, // Greek
{name: "Hellenic", base: 7, odd: 1, sort: i => (n(i) / td(i, 19) / sf(i)) * h[i], shield: "boeotian"}, // Greek {name: "Hellenic", base: 7, odd: 1, sort: i => (n(i) / td(i, 19) / sf(i)) * h[i], shield: "boeotian", charges: "Limited", lines: "All", tinctures: "All"}, // Greek
{name: "Macedonian", base: 7, odd: 0.5, sort: i => (n(i) / td(i, 12)) * h[i], shield: "round"}, // Greek {name: "Macedonian", base: 7, odd: 0.5, sort: i => (n(i) / td(i, 12)) * h[i], shield: "round", charges: "Limited", lines: "All", tinctures: "All"}, // Greek
{name: "Celtic", base: 22, odd: 1, sort: i => n(i) / td(i, 11) ** 0.5 / bd(i, [6, 8]), shield: "round"}, {name: "Celtic", base: 22, odd: 1, sort: i => n(i) / td(i, 11) ** 0.5 / bd(i, [6, 8]), shield: "round", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Germanic", base: 0, odd: 1, sort: i => n(i) / td(i, 10) ** 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", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Persian", base: 24, odd: 0.8, sort: i => (n(i) / td(i, 18)) * h[i], shield: "oval"}, // Iranian {name: "Persian", base: 24, odd: 0.8, sort: i => (n(i) / td(i, 18)) * h[i], shield: "oval", charges: "Limited", lines: "All", tinctures: "All"}, // Iranian
{name: "Scythian", base: 24, odd: 0.5, sort: i => n(i) / td(i, 11) ** 0.5 / bd(i, [4]), shield: "round"}, // Iranian {name: "Scythian", base: 24, odd: 0.5, sort: i => n(i) / td(i, 11) ** 0.5 / bd(i, [4]), shield: "round", charges: "Limited", lines: "All", tinctures: "All"}, // Iranian
{name: "Cantabrian", base: 20, odd: 0.5, sort: i => (n(i) / td(i, 16)) * h[i], shield: "oval"}, // Basque {name: "Cantabrian", base: 20, odd: 0.5, sort: i => (n(i) / td(i, 16)) * h[i], shield: "oval", charges: "Limited", lines: "All", tinctures: "All"}, // Basque
{name: "Estian", base: 9, odd: 0.2, sort: i => (n(i) / td(i, 5)) * t[i], shield: "pavise"}, // Finnic {name: "Estian", base: 9, odd: 0.2, sort: i => (n(i) / td(i, 5)) * t[i], shield: "pavise", charges: "Limited", lines: "All", tinctures: "All"}, // Finnic
{name: "Carthaginian", base: 42, odd: 0.3, sort: i => n(i) / td(i, 20) / sf(i), shield: "oval"}, // Levantine {name: "Carthaginian", base: 42, odd: 0.3, sort: i => n(i) / td(i, 20) / sf(i), shield: "oval", charges: "Limited", lines: "All", tinctures: "All"}, // Levantine
{name: "Hebrew", base: 42, odd: 0.2, sort: i => (n(i) / td(i, 19)) * sf(i), shield: "oval"}, // Levantine {name: "Hebrew", base: 42, odd: 0.2, sort: i => (n(i) / td(i, 19)) * sf(i), shield: "oval", charges: "Shapes", lines: "All", tinctures: "All"}, // Levantine
{name: "Mesopotamian", base: 23, odd: 0.2, sort: i => n(i) / td(i, 22) / bd(i, [1, 2, 3]), shield: "oval"} // Mesopotamian {name: "Mesopotamian", base: 23, odd: 0.2, sort: i => n(i) / td(i, 22) / bd(i, [1, 2, 3]), shield: "oval", charges: "Limited", lines: "All", tinctures: "All"} // Mesopotamian
]; ];
} }
@ -323,54 +328,54 @@ window.Cultures = (function () {
base: 33, base: 33,
odd: 1, odd: 1,
sort: i => (n(i) / bd(i, [6, 7, 8, 9], 10)) * t[i], sort: i => (n(i) / bd(i, [6, 7, 8, 9], 10)) * t[i],
shield: "gondor" shield: "gondor", charges: "Nature", lines: "All", tinctures: "All"
}, // Elves }, // Elves
{ {
name: "Eldar (Elfish)", name: "Eldar (Elfish)",
base: 33, base: 33,
odd: 1, odd: 1,
sort: i => (n(i) / bd(i, [6, 7, 8, 9], 10)) * t[i], sort: i => (n(i) / bd(i, [6, 7, 8, 9], 10)) * t[i],
shield: "noldor" shield: "noldor", charges: "Nature", lines: "All", tinctures: "All"
}, // Elves }, // Elves
{ {
name: "Trow (Dark Elfish)", name: "Trow (Dark Elfish)",
base: 34, base: 34,
odd: 0.9, odd: 0.9,
sort: i => (n(i) / bd(i, [7, 8, 9, 12], 10)) * t[i], sort: i => (n(i) / bd(i, [7, 8, 9, 12], 10)) * t[i],
shield: "hessen" shield: "hessen", charges: "Limited", lines: "All", tinctures: "All"
}, // Dark Elves }, // Dark Elves
{ {
name: "Lothian (Dark Elfish)", name: "Lothian (Dark Elfish)",
base: 34, base: 34,
odd: 0.3, odd: 0.3,
sort: i => (n(i) / bd(i, [7, 8, 9, 12], 10)) * t[i], sort: i => (n(i) / bd(i, [7, 8, 9, 12], 10)) * t[i],
shield: "wedged" shield: "wedged", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"
}, // Dark Elves }, // Dark Elves
{name: "Dunirr (Dwarven)", base: 35, odd: 1, sort: i => n(i) + h[i], shield: "ironHills"}, // Dwarfs {name: "Dunirr (Dwarven)", base: 35, odd: 1, sort: i => n(i) + h[i], shield: "ironHills", charges: "Shapes", lines: "Things", tinctures: "All"}, // Dwarfs
{name: "Khazadur (Dwarven)", base: 35, odd: 1, sort: i => n(i) + h[i], shield: "erebor"}, // Dwarfs {name: "Khazadur (Dwarven)", base: 35, odd: 1, sort: i => n(i) + h[i], shield: "erebor", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"}, // Dwarfs
{name: "Kobold (Goblin)", base: 36, odd: 1, sort: i => t[i] - s[i], shield: "moriaOrc"}, // Goblin {name: "Kobold (Goblin)", base: 36, odd: 1, sort: i => t[i] - s[i], shield: "moriaOrc", charges: "None", lines: "None", tinctures: "NoStains"}, // Goblin
{name: "Uruk (Orkish)", base: 37, odd: 1, sort: i => h[i] * t[i], shield: "urukHai"}, // Orc {name: "Uruk (Orkish)", base: 37, odd: 1, sort: i => h[i] * t[i], shield: "urukHai", charges: "Limited", lines: "Straight", tinctures: "NoPatterns"}, // Orc
{ {
name: "Ugluk (Orkish)", name: "Ugluk (Orkish)",
base: 37, base: 37,
odd: 0.5, odd: 0.5,
sort: i => (h[i] * t[i]) / bd(i, [1, 2, 10, 11]), sort: i => (h[i] * t[i]) / bd(i, [1, 2, 10, 11]),
shield: "moriaOrc" shield: "moriaOrc", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"
}, // Orc }, // Orc
{name: "Yotunn (Giants)", base: 38, odd: 0.7, sort: i => td(i, -10), shield: "pavise"}, // Giant {name: "Yotunn (Giants)", base: 38, odd: 0.7, sort: i => td(i, -10), shield: "pavise", charges: "Limited", lines: "Straight", tinctures: "NoPatterns"}, // Giant
{name: "Rake (Drakonic)", base: 39, odd: 0.7, sort: i => -s[i], shield: "fantasy2"}, // Draconic {name: "Rake (Drakonic)", base: 39, odd: 0.7, sort: i => -s[i], shield: "fantasy2", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"}, // Draconic
{name: "Arago (Arachnid)", base: 40, odd: 0.7, sort: i => t[i] - s[i], shield: "horsehead2"}, // Arachnid {name: "Arago (Arachnid)", base: 40, odd: 0.7, sort: i => t[i] - s[i], shield: "horsehead2", charges: "Shapes", lines: "None", tinctures: "NoPatterns"}, // Arachnid
{name: "Aj'Snaga (Serpents)", base: 41, odd: 0.7, sort: i => n(i) / bd(i, [12], 10), shield: "fantasy1"}, // Serpents {name: "Aj'Snaga (Serpents)", base: 41, odd: 0.7, sort: i => n(i) / bd(i, [12], 10), shield: "fantasy1", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"}, // Serpents
// fantasy human // fantasy human
{name: "Anor (Human)", base: 32, odd: 1, sort: i => n(i) / td(i, 10), shield: "fantasy5"}, {name: "Anor (Human)", base: 32, odd: 1, sort: i => n(i) / td(i, 10), shield: "fantasy5", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Dail (Human)", base: 32, odd: 1, sort: i => n(i) / td(i, 13), shield: "roman"}, {name: "Dail (Human)", base: 32, odd: 1, sort: i => n(i) / td(i, 13), shield: "roman", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Rohand (Human)", base: 16, odd: 1, sort: i => n(i) / td(i, 16), shield: "round"}, {name: "Rohand (Human)", base: 16, odd: 1, sort: i => n(i) / td(i, 16), shield: "round", charges: "Limited", lines: "All", tinctures: "All"},
{ {
name: "Dulandir (Human)", name: "Dulandir (Human)",
base: 31, base: 31,
odd: 1, odd: 1,
sort: i => (n(i) / td(i, 5) / bd(i, [2, 4, 10], 7)) * t[i], sort: i => (n(i) / td(i, 5) / bd(i, [2, 4, 10], 7)) * t[i],
shield: "easterling" shield: "easterling", charges: "Limited", lines: "All", tinctures: "All"
} }
]; ];
} }
@ -378,73 +383,73 @@ window.Cultures = (function () {
if (culturesSet.value === "darkFantasy") { if (culturesSet.value === "darkFantasy") {
return [ return [
// common real-world English // common real-world English
{name: "Angshire", base: 1, odd: 1, sort: i => n(i) / td(i, 10) / sf(i), shield: "heater"}, {name: "Angshire", base: 1, odd: 1, sort: i => n(i) / td(i, 10) / sf(i), shield: "heater", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Enlandic", base: 1, odd: 1, sort: i => n(i) / td(i, 12), shield: "heater"}, {name: "Enlandic", base: 1, odd: 1, sort: i => n(i) / td(i, 12), shield: "heater", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Westen", base: 1, odd: 1, sort: i => n(i) / td(i, 10), shield: "heater"}, {name: "Westen", base: 1, odd: 1, sort: i => n(i) / td(i, 10), shield: "heater", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Nortumbic", base: 1, odd: 1, sort: i => n(i) / td(i, 7), shield: "heater"}, {name: "Nortumbic", base: 1, odd: 1, sort: i => n(i) / td(i, 7), shield: "heater", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Mercian", base: 1, odd: 1, sort: i => n(i) / td(i, 9), shield: "heater"}, {name: "Mercian", base: 1, odd: 1, sort: i => n(i) / td(i, 9), shield: "heater", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Kentian", base: 1, odd: 1, sort: i => n(i) / td(i, 12), shield: "heater"}, {name: "Kentian", base: 1, odd: 1, sort: i => n(i) / td(i, 12), shield: "heater", charges: "Limited", lines: "All", tinctures: "All"},
// rare real-world western // rare real-world western
{name: "Norse", base: 6, odd: 0.7, sort: i => n(i) / td(i, 5) / sf(i), shield: "oldFrench"}, {name: "Norse", base: 6, odd: 0.7, sort: i => n(i) / td(i, 5) / sf(i), shield: "oldFrench", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"},
{name: "Schwarzen", base: 0, odd: 0.3, sort: i => n(i) / td(i, 10) / bd(i, [6, 8]), shield: "gonfalon"}, {name: "Schwarzen", base: 0, odd: 0.3, sort: i => n(i) / td(i, 10) / bd(i, [6, 8]), shield: "gonfalon", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"},
{name: "Luarian", base: 2, odd: 0.3, sort: i => n(i) / td(i, 12) / bd(i, [6, 8]), shield: "oldFrench"}, {name: "Luarian", base: 2, odd: 0.3, sort: i => n(i) / td(i, 12) / bd(i, [6, 8]), shield: "oldFrench", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"},
{name: "Hetallian", base: 3, odd: 0.3, sort: i => n(i) / td(i, 15), shield: "oval"}, {name: "Hetallian", base: 3, odd: 0.3, sort: i => n(i) / td(i, 15), shield: "oval", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"},
{name: "Astellian", base: 4, odd: 0.3, sort: i => n(i) / td(i, 16), shield: "spanish"}, {name: "Astellian", base: 4, odd: 0.3, sort: i => n(i) / td(i, 16), shield: "spanish", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"},
// rare real-world exotic // rare real-world exotic
{ {
name: "Kiswaili", name: "Kiswaili",
base: 28, base: 28,
odd: 0.05, odd: 0.05,
sort: i => n(i) / td(i, 29) / bd(i, [1, 3, 5, 7]), sort: i => n(i) / td(i, 29) / bd(i, [1, 3, 5, 7]),
shield: "vesicaPiscis" shield: "vesicaPiscis", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"
}, },
{name: "Yoruba", base: 21, odd: 0.05, sort: i => n(i) / td(i, 15) / bd(i, [5, 7]), shield: "vesicaPiscis"}, {name: "Yoruba", base: 21, odd: 0.05, sort: i => n(i) / td(i, 15) / bd(i, [5, 7]), shield: "vesicaPiscis", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{name: "Koryo", base: 10, odd: 0.05, sort: i => n(i) / td(i, 12) / t[i], shield: "round"}, {name: "Koryo", base: 10, odd: 0.05, sort: i => n(i) / td(i, 12) / t[i], shield: "round", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{name: "Hantzu", base: 11, odd: 0.05, sort: i => n(i) / td(i, 13), shield: "banner"}, {name: "Hantzu", base: 11, odd: 0.05, sort: i => n(i) / td(i, 13), shield: "banner", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{name: "Yamoto", base: 12, odd: 0.05, sort: i => n(i) / td(i, 15) / t[i], shield: "round"}, {name: "Yamoto", base: 12, odd: 0.05, sort: i => n(i) / td(i, 15) / t[i], shield: "round", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{name: "Guantzu", base: 30, odd: 0.05, sort: i => n(i) / td(i, 17), shield: "banner"}, {name: "Guantzu", base: 30, odd: 0.05, sort: i => n(i) / td(i, 17), shield: "banner", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{ {
name: "Ulus", name: "Ulus",
base: 31, base: 31,
odd: 0.05, odd: 0.05,
sort: i => (n(i) / td(i, 5) / bd(i, [2, 4, 10], 7)) * t[i], sort: i => (n(i) / td(i, 5) / bd(i, [2, 4, 10], 7)) * t[i],
shield: "banner" shield: "banner", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"
}, },
{name: "Turan", base: 16, odd: 0.05, sort: i => n(i) / td(i, 12), shield: "round"}, {name: "Turan", base: 16, odd: 0.05, sort: i => n(i) / td(i, 12), shield: "round", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{ {
name: "Berberan", name: "Berberan",
base: 17, base: 17,
odd: 0.05, odd: 0.05,
sort: i => (n(i) / td(i, 19) / bd(i, [1, 2, 3], 7)) * t[i], sort: i => (n(i) / td(i, 19) / bd(i, [1, 2, 3], 7)) * t[i],
shield: "round" shield: "round", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"
}, },
{ {
name: "Eurabic", name: "Eurabic",
base: 18, base: 18,
odd: 0.05, odd: 0.05,
sort: i => (n(i) / td(i, 26) / bd(i, [1, 2], 7)) * t[i], sort: i => (n(i) / td(i, 26) / bd(i, [1, 2], 7)) * t[i],
shield: "round" shield: "round", charges: "Shapes", lines: "All", tinctures: "All"
}, },
{name: "Slovan", base: 5, odd: 0.05, sort: i => (n(i) / td(i, 6)) * t[i], shield: "round"}, {name: "Slovan", base: 5, odd: 0.05, sort: i => (n(i) / td(i, 6)) * t[i], shield: "round", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"},
{ {
name: "Keltan", name: "Keltan",
base: 22, base: 22,
odd: 0.1, odd: 0.1,
sort: i => n(i) / td(i, 11) ** 0.5 / bd(i, [6, 8]), sort: i => n(i) / td(i, 11) ** 0.5 / bd(i, [6, 8]),
shield: "vesicaPiscis" shield: "vesicaPiscis", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"
}, },
{name: "Elladan", base: 7, odd: 0.2, sort: i => (n(i) / td(i, 18) / sf(i)) * h[i], shield: "boeotian"}, {name: "Elladan", base: 7, odd: 0.2, sort: i => (n(i) / td(i, 18) / sf(i)) * h[i], shield: "boeotian", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"},
{name: "Romian", base: 8, odd: 0.2, sort: i => n(i) / td(i, 14) / t[i], shield: "roman"}, {name: "Romian", base: 8, odd: 0.2, sort: i => n(i) / td(i, 14) / t[i], shield: "roman", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"},
// fantasy races // fantasy races
{name: "Eldar", base: 33, odd: 0.5, sort: i => (n(i) / bd(i, [6, 7, 8, 9], 10)) * t[i], shield: "fantasy5"}, // Elves {name: "Eldar", base: 33, odd: 0.5, sort: i => (n(i) / bd(i, [6, 7, 8, 9], 10)) * t[i], shield: "fantasy5", charges: "Nature", lines: "All", tinctures: "All"}, // 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: "Trow", base: 34, odd: 0.8, sort: i => (n(i) / bd(i, [7, 8, 9, 12], 10)) * t[i], shield: "hessen", charges: "Shapes", lines: "All", tinctures: "All"}, // Dark Elves
{name: "Durinn", base: 35, odd: 0.8, sort: i => n(i) + h[i], shield: "erebor"}, // Dwarven {name: "Durinn", base: 35, odd: 0.8, sort: i => n(i) + h[i], shield: "erebor", charges: "Shapes", lines: "All", tinctures: "All"}, // Dwarven
{name: "Kobblin", base: 36, odd: 0.8, sort: i => t[i] - s[i], shield: "moriaOrc"}, // Goblin {name: "Kobblin", base: 36, odd: 0.8, sort: i => t[i] - s[i], shield: "moriaOrc", charges: "Shapes", lines: "None", tinctures: "NoStains"}, // Goblin
{name: "Uruk", base: 37, odd: 0.8, sort: i => (h[i] * t[i]) / bd(i, [1, 2, 10, 11]), shield: "urukHai"}, // Orc {name: "Uruk", base: 37, odd: 0.8, sort: i => (h[i] * t[i]) / bd(i, [1, 2, 10, 11]), shield: "urukHai", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"}, // Orc
{name: "Yotunn", base: 38, odd: 0.8, sort: i => td(i, -10), shield: "pavise"}, // Giant {name: "Yotunn", base: 38, odd: 0.8, sort: i => td(i, -10), shield: "pavise", charges: "Limited", lines: "Straight", tinctures: "NoPatterns"}, // Giant
{name: "Drake", base: 39, odd: 0.9, sort: i => -s[i], shield: "fantasy2"}, // Draconic {name: "Drake", base: 39, odd: 0.9, sort: i => -s[i], shield: "fantasy2", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"}, // Draconic
{name: "Rakhnid", base: 40, odd: 0.9, sort: i => t[i] - s[i], shield: "horsehead2"}, // Arachnid {name: "Rakhnid", base: 40, odd: 0.9, sort: i => t[i] - s[i], shield: "horsehead2", charges: "Shapes", lines: "None", tinctures: "NoPatterns"}, // Arachnid
{name: "Aj'Snaga", base: 41, odd: 0.9, sort: i => n(i) / bd(i, [12], 10), shield: "fantasy1"} // Serpents {name: "Aj'Snaga", base: 41, odd: 0.9, sort: i => n(i) / bd(i, [12], 10), shield: "fantasy1", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"} // Serpents
]; ];
} }
@ -458,51 +463,51 @@ window.Cultures = (function () {
// all-world // all-world
return [ return [
{name: "Shwazen", base: 0, odd: 0.7, sort: i => n(i) / td(i, 10) / bd(i, [6, 8]), shield: "hessen"}, {name: "Shwazen", base: 0, odd: 0.7, sort: i => n(i) / td(i, 10) / bd(i, [6, 8]), shield: "hessen", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"},
{name: "Angshire", base: 1, odd: 1, sort: i => n(i) / td(i, 10) / sf(i), shield: "heater"}, {name: "Angshire", base: 1, odd: 1, sort: i => n(i) / td(i, 10) / sf(i), shield: "heater", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Luari", base: 2, odd: 0.6, sort: i => n(i) / td(i, 12) / bd(i, [6, 8]), shield: "oldFrench"}, {name: "Luari", base: 2, odd: 0.6, sort: i => n(i) / td(i, 12) / bd(i, [6, 8]), shield: "oldFrench", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Tallian", base: 3, odd: 0.6, sort: i => n(i) / td(i, 15), shield: "horsehead2"}, {name: "Tallian", base: 3, odd: 0.6, sort: i => n(i) / td(i, 15), shield: "horsehead2", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Astellian", base: 4, odd: 0.6, sort: i => n(i) / td(i, 16), shield: "spanish"}, {name: "Astellian", base: 4, odd: 0.6, sort: i => n(i) / td(i, 16), shield: "spanish", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Slovan", base: 5, odd: 0.7, sort: i => (n(i) / td(i, 6)) * t[i], shield: "round"}, {name: "Slovan", base: 5, odd: 0.7, sort: i => (n(i) / td(i, 6)) * t[i], shield: "round", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"},
{name: "Norse", base: 6, odd: 0.7, sort: i => n(i) / td(i, 5), shield: "heater"}, {name: "Norse", base: 6, odd: 0.7, sort: i => n(i) / td(i, 5), shield: "heater", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"},
{name: "Elladan", base: 7, odd: 0.7, sort: i => (n(i) / td(i, 18)) * h[i], shield: "boeotian"}, {name: "Elladan", base: 7, odd: 0.7, sort: i => (n(i) / td(i, 18)) * h[i], shield: "boeotian", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"},
{name: "Romian", base: 8, odd: 0.7, sort: i => n(i) / td(i, 15), shield: "roman"}, {name: "Romian", base: 8, odd: 0.7, sort: i => n(i) / td(i, 15), shield: "roman", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"},
{name: "Soumi", base: 9, odd: 0.3, sort: i => (n(i) / td(i, 5) / bd(i, [9])) * t[i], shield: "pavise"}, {name: "Soumi", base: 9, odd: 0.3, sort: i => (n(i) / td(i, 5) / bd(i, [9])) * t[i], shield: "pavise", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Koryo", base: 10, odd: 0.1, sort: i => n(i) / td(i, 12) / t[i], shield: "round"}, {name: "Koryo", base: 10, odd: 0.1, sort: i => n(i) / td(i, 12) / t[i], shield: "round", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Hantzu", base: 11, odd: 0.1, sort: i => n(i) / td(i, 13), shield: "banner"}, {name: "Hantzu", base: 11, odd: 0.1, sort: i => n(i) / td(i, 13), shield: "banner", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{name: "Yamoto", base: 12, odd: 0.1, sort: i => n(i) / td(i, 15) / t[i], shield: "round"}, {name: "Yamoto", base: 12, odd: 0.1, sort: i => n(i) / td(i, 15) / t[i], shield: "round", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{name: "Portuzian", base: 13, odd: 0.4, sort: i => n(i) / td(i, 17) / sf(i), shield: "spanish"}, {name: "Portuzian", base: 13, odd: 0.4, sort: i => n(i) / td(i, 17) / sf(i), shield: "spanish", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Nawatli", base: 14, odd: 0.1, sort: i => h[i] / td(i, 18) / bd(i, [7]), shield: "square"}, {name: "Nawatli", base: 14, odd: 0.1, sort: i => h[i] / td(i, 18) / bd(i, [7]), shield: "square", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{name: "Vengrian", base: 15, odd: 0.2, sort: i => (n(i) / td(i, 11) / bd(i, [4])) * t[i], shield: "wedged"}, {name: "Vengrian", base: 15, odd: 0.2, sort: i => (n(i) / td(i, 11) / bd(i, [4])) * t[i], shield: "wedged", charges: "Limited", lines: "All", tinctures: "All"},
{name: "Turchian", base: 16, odd: 0.2, sort: i => n(i) / td(i, 13), shield: "round"}, {name: "Turchian", base: 16, odd: 0.2, sort: i => n(i) / td(i, 13), shield: "round", charges: "Limited", lines: "All", tinctures: "NoPatterns"},
{ {
name: "Berberan", name: "Berberan",
base: 17, base: 17,
odd: 0.1, odd: 0.1,
sort: i => (n(i) / td(i, 19) / bd(i, [1, 2, 3], 7)) * t[i], sort: i => (n(i) / td(i, 19) / bd(i, [1, 2, 3], 7)) * t[i],
shield: "round" shield: "round", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"
}, },
{name: "Eurabic", base: 18, odd: 0.2, sort: i => (n(i) / td(i, 26) / bd(i, [1, 2], 7)) * t[i], shield: "round"}, {name: "Eurabic", base: 18, odd: 0.2, sort: i => (n(i) / td(i, 26) / bd(i, [1, 2], 7)) * t[i], shield: "round", charges: "Shapes", lines: "All", tinctures: "All"},
{name: "Inuk", base: 19, odd: 0.05, sort: i => td(i, -1) / bd(i, [10, 11]) / sf(i), shield: "square"}, {name: "Inuk", base: 19, odd: 0.05, sort: i => td(i, -1) / bd(i, [10, 11]) / sf(i), shield: "square", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{name: "Euskati", base: 20, odd: 0.05, sort: i => (n(i) / td(i, 15)) * h[i], shield: "spanish"}, {name: "Euskati", base: 20, odd: 0.05, sort: i => (n(i) / td(i, 15)) * h[i], shield: "spanish", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{name: "Yoruba", base: 21, odd: 0.05, sort: i => n(i) / td(i, 15) / bd(i, [5, 7]), shield: "vesicaPiscis"}, {name: "Yoruba", base: 21, odd: 0.05, sort: i => n(i) / td(i, 15) / bd(i, [5, 7]), shield: "vesicaPiscis", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{ {
name: "Keltan", name: "Keltan",
base: 22, base: 22,
odd: 0.05, odd: 0.05,
sort: i => (n(i) / td(i, 11) / bd(i, [6, 8])) * t[i], sort: i => (n(i) / td(i, 11) / bd(i, [6, 8])) * t[i],
shield: "vesicaPiscis" shield: "vesicaPiscis", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"
}, },
{name: "Efratic", base: 23, odd: 0.05, sort: i => (n(i) / td(i, 22)) * t[i], shield: "diamond"}, {name: "Efratic", base: 23, odd: 0.05, sort: i => (n(i) / td(i, 22)) * t[i], shield: "diamond", charges: "Shapes", lines: "All", tinctures: "NoPatterns"},
{name: "Tehrani", base: 24, odd: 0.1, sort: i => (n(i) / td(i, 18)) * h[i], shield: "round"}, {name: "Tehrani", base: 24, odd: 0.1, sort: i => (n(i) / td(i, 18)) * h[i], shield: "round", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{name: "Maui", base: 25, odd: 0.05, sort: i => n(i) / td(i, 24) / sf(i) / t[i], shield: "round"}, {name: "Maui", base: 25, odd: 0.05, sort: i => n(i) / td(i, 24) / sf(i) / t[i], shield: "round", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"},
{name: "Carnatic", base: 26, odd: 0.05, sort: i => n(i) / td(i, 26), shield: "round"}, {name: "Carnatic", base: 26, odd: 0.05, sort: i => n(i) / td(i, 26), shield: "round", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"},
{name: "Inqan", base: 27, odd: 0.05, sort: i => h[i] / td(i, 13), shield: "square"}, {name: "Inqan", base: 27, odd: 0.05, sort: i => h[i] / td(i, 13), shield: "square", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{name: "Kiswaili", base: 28, odd: 0.1, sort: i => n(i) / td(i, 29) / bd(i, [1, 3, 5, 7]), shield: "vesicaPiscis"}, {name: "Kiswaili", base: 28, odd: 0.1, sort: i => n(i) / td(i, 29) / bd(i, [1, 3, 5, 7]), shield: "vesicaPiscis", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{name: "Vietic", base: 29, odd: 0.1, sort: i => n(i) / td(i, 25) / bd(i, [7], 7) / t[i], shield: "banner"}, {name: "Vietic", base: 29, odd: 0.1, sort: i => n(i) / td(i, 25) / bd(i, [7], 7) / t[i], shield: "banner", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{name: "Guantzu", base: 30, odd: 0.1, sort: i => n(i) / td(i, 17), shield: "banner"}, {name: "Guantzu", base: 30, odd: 0.1, sort: i => n(i) / td(i, 17), shield: "banner", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{name: "Ulus", base: 31, odd: 0.1, sort: i => (n(i) / td(i, 5) / bd(i, [2, 4, 10], 7)) * t[i], shield: "banner"}, {name: "Ulus", base: 31, odd: 0.1, sort: i => (n(i) / td(i, 5) / bd(i, [2, 4, 10], 7)) * t[i], shield: "banner", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"},
{name: "Hebrew", base: 42, odd: 0.2, sort: i => (n(i) / td(i, 18)) * sf(i), shield: "oval"} // Levantine {name: "Hebrew", base: 42, odd: 0.2, sort: i => (n(i) / td(i, 18)) * sf(i), shield: "oval", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"} // Levantine
]; ];
}; };

View file

@ -719,4 +719,15 @@ export function resolveVersionConflicts(version) {
} }
} }
} }
if (version < 1.94) {
// v1.94 adds several emblem adjustment attributes to cultures
console.log('##### 1.94 culture update #####');
pack.cultures.forEach(culture => {
console.log(culture);
if (culture.removed) return;
culture.charges = "All";
culture.lines = "All";
culture.tinctures = "All";
});
}
} }

View file

@ -1,7 +1,10 @@
const $body = insertEditorHtml(); const $body = insertEditorHtml();
addListeners(); addListeners();
const chargesTypes = ["All", "Limited", "Nature", "Things", "Nature&Things", "Shapes", "None"];
const cultureTypes = ["Generic", "River", "Lake", "Naval", "Nomadic", "Hunting", "Highland"]; const cultureTypes = ["Generic", "River", "Lake", "Naval", "Nomadic", "Hunting", "Highland"];
const linesTypes = ["All", "Limited", "Straight", "None"];
const tinctureTypes = ["All", "NoPatterns", "NoStains"];
export function open() { export function open() {
closeDialogs("#culturesEditor, .stable"); closeDialogs("#culturesEditor, .stable");
@ -21,10 +24,10 @@ export function open() {
}); });
$body.focus(); $body.focus();
} }
function insertEditorHtml() { function insertEditorHtml() {
const editorHtml = /* html */ `<div id="culturesEditor" class="dialog stable"> const editorHtml = /* html */ `<div id="culturesEditor" class="dialog stable">
<div id="culturesHeader" class="header" style="grid-template-columns: 10em 7em 9em 4em 8em 5em 7em 8em"> <div id="culturesHeader" class="header" style="grid-template-columns: 10em 7em 9em 3em 8em 5em 7em 7em 6em 6em 6em
1em 1em 1em">
<div data-tip="Click to sort by culture name" class="sortable alphabetically" data-sortby="name">Culture&nbsp;</div> <div data-tip="Click to sort by culture name" class="sortable alphabetically" data-sortby="name">Culture&nbsp;</div>
<div data-tip="Click to sort by type" class="sortable alphabetically" data-sortby="type">Type&nbsp;</div> <div data-tip="Click to sort by type" class="sortable alphabetically" data-sortby="type">Type&nbsp;</div>
<div data-tip="Click to sort by culture namesbase" class="sortable" data-sortby="base">Namesbase&nbsp;</div> <div data-tip="Click to sort by culture namesbase" class="sortable" data-sortby="base">Namesbase&nbsp;</div>
@ -33,6 +36,11 @@ function insertEditorHtml() {
<div data-tip="Click to sort by culture area" class="sortable hide" data-sortby="area">Area&nbsp;</div> <div data-tip="Click to sort by culture area" class="sortable hide" data-sortby="area">Area&nbsp;</div>
<div data-tip="Click to sort by culture population" class="sortable hide icon-sort-number-down" data-sortby="population">Population&nbsp;</div> <div data-tip="Click to sort by culture population" class="sortable hide icon-sort-number-down" data-sortby="population">Population&nbsp;</div>
<div data-tip="Click to sort by culture emblems shape" class="sortable alphabetically hide" data-sortby="emblems">Emblems&nbsp;</div> <div data-tip="Click to sort by culture emblems shape" class="sortable alphabetically hide" data-sortby="emblems">Emblems&nbsp;</div>
<div data-tip="Click to sort by charge options" class="sortable alphabetically hide" data-sortby="charges">Charges&nbsp;</div>
<div data-tip="Click to sort by line options" class="sortable alphabetically hide" data-sortby="lines">Lines&nbsp;</div>
<div data-tip="Click to sort by tincture options" class="sortable alphabetically hide" data-sortby="tinctures">Tinctures&nbsp;</div>
<div data-tip="" class="hide">&nbsp;</div>
<div data-tip="" class="hide">&nbsp;</div>
</div> </div>
<div id="culturesBody" class="table" data-type="absolute"></div> <div id="culturesBody" class="table" data-type="absolute"></div>
@ -42,7 +50,6 @@ function insertEditorHtml() {
<div data-tip="Total land area" style="margin-left: 12px">Land Area:&nbsp;<span id="culturesFooterArea">0</span></div> <div data-tip="Total land area" style="margin-left: 12px">Land Area:&nbsp;<span id="culturesFooterArea">0</span></div>
<div data-tip="Total population" style="margin-left: 12px">Population:&nbsp;<span id="culturesFooterPopulation">0</span></div> <div data-tip="Total population" style="margin-left: 12px">Population:&nbsp;<span id="culturesFooterPopulation">0</span></div>
</div> </div>
<div id="culturesBottom"> <div id="culturesBottom">
<button id="culturesEditorRefresh" data-tip="Refresh the Editor" class="icon-cw"></button> <button id="culturesEditorRefresh" data-tip="Refresh the Editor" class="icon-cw"></button>
<button id="culturesEditStyle" data-tip="Edit cultures style in Style Editor" class="icon-adjust"></button> <button id="culturesEditStyle" data-tip="Edit cultures style in Style Editor" class="icon-adjust"></button>
@ -165,8 +172,13 @@ function culturesEditorAddLines() {
data-type="" data-type=""
data-expansionism="" data-expansionism=""
data-emblems="${c.shield}" data-emblems="${c.shield}"
data-charges="${c.charges}"
data-lines="${c.lines}"
data-tinctures="${c.tinctures}"
data-lock=""
data-delete=""
> >
<svg width="11" height="11" class="placeholder"></svg> <fill-box fill="#ffffff" width="11" height="11" style="visibility: hidden"></fill-box>
<input data-tip="Neutral culture name. Click and type to change" class="cultureName italic" style="width: 7em" <input data-tip="Neutral culture name. Click and type to change" class="cultureName italic" style="width: 7em"
value="${c.name}" autocorrect="off" spellcheck="false" /> value="${c.name}" autocorrect="off" spellcheck="false" />
<span class="icon-cw placeholder"></span> <span class="icon-cw placeholder"></span>
@ -175,15 +187,20 @@ function culturesEditorAddLines() {
<select data-tip="Culture namesbase. Click to change. Click on arrows to re-generate names" <select data-tip="Culture namesbase. Click to change. Click on arrows to re-generate names"
class="cultureBase">${getBaseOptions(c.base)}</select> class="cultureBase">${getBaseOptions(c.base)}</select>
<span data-tip="Cells count" class="icon-check-empty hide"></span> <span data-tip="Cells count" class="icon-check-empty hide"></span>
<div data-tip="Cells count" class="cultureCells hide" style="width: 4em">${c.cells}</div> <div data-tip="Cells count" class="cultureCells hide" style="width: 3em">${c.cells}</div>
<span class="icon-resize-full placeholder hide"></span> <span class="icon-resize-full placeholder hide"></span>
<input class="cultureExpan placeholder hide" type="number" /> <input class="cultureExpan placeholder hide" type="number" style="width: 5em;"/>
<span data-tip="Culture area" style="padding-right: 4px" class="icon-map-o hide"></span> <span data-tip="Culture area" style="padding-right: 4px" class="icon-map-o hide"></span>
<div data-tip="Culture area" class="cultureArea hide" style="width: 6em">${si(area)} ${unit}</div> <div data-tip="Culture area" class="cultureArea hide" style="width: 6em">${si(area)} ${unit}</div>
<span data-tip="${populationTip}" class="icon-male hide"></span> <span data-tip="${populationTip}" class="icon-male hide"></span>
<div data-tip="${populationTip}" class="culturePopulation hide pointer" <div data-tip="${populationTip}" class="culturePopulation hide pointer"
style="width: 4em">${si(population)}</div> style="width: 4em">${si(population)}</div>
${getShapeOptions(selectShape, c.shield)} <select data-tip="Emblem shape associated with culture. Click to change" class="cultureEmblems hide" style="width: 7em">${getShapeOptions(selectShape, c.shield)}</select>
<select class="chargesType placeholder" style="width: 6em">${getChargesOptions(c.charges)}</select>
<select class="linesType placeholder" style="width: 6em">${getLinesOptions(c.lines)}</select>
<select class="tincturesType placeholder" style="width: 6em">${getTincturesOptions(c.tinctures)}</select>
<span class="cultureLock placeholder">>&nbsp;</span>
<span class="cultureDelete placeholder">>&nbsp;</span>
</div>`; </div>`;
continue; continue;
} }
@ -200,8 +217,13 @@ function culturesEditorAddLines() {
data-type="${c.type}" data-type="${c.type}"
data-expansionism="${c.expansionism}" data-expansionism="${c.expansionism}"
data-emblems="${c.shield}" data-emblems="${c.shield}"
data-charges="${c.charges}"
data-lines="${c.lines}"
data-tinctures="${c.tinctures}"
data-lock=""
data-delete=""
> >
<fill-box fill="${c.color}"></fill-box> <fill-box fill="${c.color}" width="11" height="11"></fill-box>
<input data-tip="Culture name. Click and type to change" class="cultureName" style="width: 7em" <input data-tip="Culture name. Click and type to change" class="cultureName" style="width: 7em"
value="${c.name}" autocorrect="off" spellcheck="false" /> value="${c.name}" autocorrect="off" spellcheck="false" />
<span data-tip="Regenerate culture name" class="icon-cw hiddenIcon" style="visibility: hidden"></span> <span data-tip="Regenerate culture name" class="icon-cw hiddenIcon" style="visibility: hidden"></span>
@ -212,7 +234,6 @@ function culturesEditorAddLines() {
class="cultureBase">${getBaseOptions(c.base)}</select> class="cultureBase">${getBaseOptions(c.base)}</select>
<span data-tip="Cells count" class="icon-check-empty hide"></span> <span data-tip="Cells count" class="icon-check-empty hide"></span>
<div data-tip="Cells count" class="cultureCells hide" style="width: 4em">${c.cells}</div> <div data-tip="Cells count" class="cultureCells hide" style="width: 4em">${c.cells}</div>
<span data-tip="Culture expansionism. Defines competitive size" class="icon-resize-full hide"></span>
<input <input
data-tip="Culture expansionism. Defines competitive size. Click to change, then click Recalculate to apply change" data-tip="Culture expansionism. Defines competitive size. Click to change, then click Recalculate to apply change"
class="cultureExpan hide" class="cultureExpan hide"
@ -220,14 +241,23 @@ function culturesEditorAddLines() {
min="0" min="0"
max="99" max="99"
step=".1" step=".1"
value=${c.expansionism} value="${c.expansionism}"
style="width: 5em;"
/> />
<span data-tip="Culture area" style="padding-right: 4px" class="icon-map-o hide"></span> <span data-tip="Culture area" style="padding-right: 4px" class="icon-map-o hide"></span>
<div data-tip="Culture area" class="cultureArea hide" style="width: 6em">${si(area)} ${unit}</div> <div data-tip="Culture area" class="cultureArea hide" style="width: 6em">${si(area)} ${unit}</div>
<span data-tip="${populationTip}" class="icon-male hide"></span> <span data-tip="${populationTip}" class="icon-male hide"></span>
<div data-tip="${populationTip}" class="culturePopulation hide pointer" <div data-tip="${populationTip}" class="culturePopulation hide pointer"
style="width: 4em">${si(population)}</div> style="width: 4em">${si(population)}</div>
${getShapeOptions(selectShape, c.shield)} <select data-tip="Emblem shape associated with culture. Click to change" class="cultureEmblems" style="width: 6em">${getShapeOptions(selectShape, c.shield)}</select>
<select data-tip="Emblem charge options. Determines which charges can appear on an emblem. Click to change"
class="chargesType" style="width: 6em">${getChargesOptions(c.charges)}</select>
<select
data-tip="Emblem line options. Determines which types of lines can appear on an emblem. Click to change"
class="linesType" style="width: 6em">${getLinesOptions(c.lines)}</select>
<select
data-tip="Emblem tincture options. Determines which types of tinctures can appear on an emblem. Click to change"
class="tincturesType" style="width: 6em">${getTincturesOptions(c.tinctures)}</select>
<span data-tip="Lock culture" class="icon-lock${c.lock ? "" : "-open"} hide"></span> <span data-tip="Lock culture" class="icon-lock${c.lock ? "" : "-open"} hide"></span>
<span data-tip="Remove culture" class="icon-trash-empty hide"></span> <span data-tip="Remove culture" class="icon-trash-empty hide"></span>
</div>`; </div>`;
@ -255,6 +285,9 @@ function culturesEditorAddLines() {
$body.querySelectorAll("div > select.cultureType").forEach($el => $el.on("change", cultureChangeType)); $body.querySelectorAll("div > select.cultureType").forEach($el => $el.on("change", cultureChangeType));
$body.querySelectorAll("div > select.cultureBase").forEach($el => $el.on("change", cultureChangeBase)); $body.querySelectorAll("div > select.cultureBase").forEach($el => $el.on("change", cultureChangeBase));
$body.querySelectorAll("div > select.cultureEmblems").forEach($el => $el.on("change", cultureChangeEmblemsShape)); $body.querySelectorAll("div > select.cultureEmblems").forEach($el => $el.on("change", cultureChangeEmblemsShape));
$body.querySelectorAll("div > select.chargesType").forEach($el => $el.on("change", cultureChangeEmblemsCharges));
$body.querySelectorAll("div > select.linesType").forEach($el => $el.on("change", cultureChangeEmblemsLines));
$body.querySelectorAll("div > select.tincturesType").forEach($el => $el.on("change", cultureChangeEmblemsTinctures));
$body.querySelectorAll("div > div.culturePopulation").forEach($el => $el.on("click", changePopulation)); $body.querySelectorAll("div > div.culturePopulation").forEach($el => $el.on("click", changePopulation));
$body.querySelectorAll("div > span.icon-arrows-cw").forEach($el => $el.on("click", cultureRegenerateBurgs)); $body.querySelectorAll("div > span.icon-arrows-cw").forEach($el => $el.on("click", cultureRegenerateBurgs));
$body.querySelectorAll("div > span.icon-trash-empty").forEach($el => $el.on("click", cultureRemovePrompt)); $body.querySelectorAll("div > span.icon-trash-empty").forEach($el => $el.on("click", cultureRemovePrompt));
@ -263,6 +296,9 @@ function culturesEditorAddLines() {
const $culturesHeader = byId("culturesHeader"); const $culturesHeader = byId("culturesHeader");
$culturesHeader.querySelector("div[data-sortby='emblems']").style.display = selectShape ? "inline-block" : "none"; $culturesHeader.querySelector("div[data-sortby='emblems']").style.display = selectShape ? "inline-block" : "none";
$culturesHeader.querySelector("div[data-sortby='lines']").style.display = "inline-block";
$culturesHeader.querySelector("div[data-sortby='charges']").style.display = "inline-block";
$culturesHeader.querySelector("div[data-sortby='tinctures']").style.display = "inline-block";
if ($body.dataset.type === "percentage") { if ($body.dataset.type === "percentage") {
$body.dataset.type = "absolute"; $body.dataset.type = "absolute";
@ -272,9 +308,27 @@ function culturesEditorAddLines() {
$("#culturesEditor").dialog({width: fitContent()}); $("#culturesEditor").dialog({width: fitContent()});
} }
function getTypeOptions(type) { function getChargesOptions(charges) {
let options = ""; let options = "";
cultureTypes.forEach(t => (options += `<option ${type === t ? "selected" : ""} value="${t}">${t}</option>`)); chargesTypes.forEach(c => (options += `<option ${charges === c ? "selected" : ""} value="${c}">${c}</option>`));
return options;
}
function getLinesOptions(lines) {
let options = "";
linesTypes.forEach(l => (options += `<option ${lines === l ? "selected" : ""} value="${l}">${l}</option>`));
return options;
}
function getTincturesOptions(tinctures) {
let options = "";
tinctureTypes.forEach(t => (options += `<option ${tinctures === t ? "selected" : ""} value="${t}">${t}</option>`));
return options;
}
function getTypeOptions(types) {
let options = "";
cultureTypes.forEach(t => (options += `<option ${types === t ? "selected" : ""} value="${t}">${t}</option>`));
return options; return options;
} }
@ -293,7 +347,7 @@ function getShapeOptions(selectShape, selected) {
const options = shapes.map( const options = shapes.map(
shape => `<option ${shape === selected ? "selected" : ""} value="${shape}">${capitalize(shape)}</option>` shape => `<option ${shape === selected ? "selected" : ""} value="${shape}">${capitalize(shape)}</option>`
); );
return `<select data-tip="Emblem shape associated with culture. Click to change" class="cultureEmblems hide">${options}</select>`; return options;
} }
const cultureHighlightOn = debounce(event => { const cultureHighlightOn = debounce(event => {
@ -429,6 +483,24 @@ function cultureChangeEmblemsShape() {
}); });
} }
function cultureChangeEmblemsCharges() {
const culture = +this.parentNode.dataset.id;
const charges = this.value;
this.parentNode.dataset.charges = pack.cultures[culture].charges = charges;
}
function cultureChangeEmblemsLines() {
const culture = +this.parentNode.dataset.id;
const lines = this.value;
this.parentNode.dataset.lines = pack.cultures[culture].lines = lines;
}
function cultureChangeEmblemsTinctures() {
const culture = +this.parentNode.dataset.id;
const tinctures = this.value;
this.parentNode.dataset.tinctures = pack.cultures[culture].tinctures = tinctures;
}
function changePopulation() { function changePopulation() {
const cultureId = +this.parentNode.dataset.id; const cultureId = +this.parentNode.dataset.id;
const culture = pack.cultures[cultureId]; const culture = pack.cultures[cultureId];

View file

@ -1139,7 +1139,7 @@ function adjustProvinces(affectedProvinces) {
const kinship = nameByBurg ? 0.8 : 0.4; const kinship = nameByBurg ? 0.8 : 0.4;
const type = BurgsAndStates.getType(center, burg?.port); const type = BurgsAndStates.getType(center, burg?.port);
const coa = COA.generate(burg?.coa || states[stateId].coa, kinship, burg ? null : 0.9, type); const coa = COA.generate(burg?.coa || states[stateId].coa, kinship, burg ? null : 0.9, type, pack.cultures, culture);
coa.shield = COA.getShield(culture, stateId); coa.shield = COA.getShield(culture, stateId);
provinces.push({ provinces.push({
@ -1240,7 +1240,7 @@ function addState() {
// generate emblem // generate emblem
const cultureType = pack.cultures[culture].type; const cultureType = pack.cultures[culture].type;
const coa = COA.generate(burgs[burg].coa, 0.4, null, cultureType); const coa = COA.generate(burgs[burg].coa, 0.4, null, cultureType, pack.cultures, culture);
coa.shield = COA.getShield(culture, null); coa.shield = COA.getShield(culture, null);
// update diplomacy and reverse relations // update diplomacy and reverse relations

View file

@ -109,7 +109,6 @@ function uploadMap(file, callback) {
uploadMap.timeStart = performance.now(); uploadMap.timeStart = performance.now();
const OLDEST_SUPPORTED_VERSION = 0.7; const OLDEST_SUPPORTED_VERSION = 0.7;
const currentVersion = parseFloat(version); const currentVersion = parseFloat(version);
const fileReader = new FileReader(); const fileReader = new FileReader();
fileReader.onloadend = async function (fileLoadedEvent) { fileReader.onloadend = async function (fileLoadedEvent) {
if (callback) callback(); if (callback) callback();
@ -456,7 +455,7 @@ async function parseLoadedData(data) {
{ {
// dynamically import and run auto-udpdate script // dynamically import and run auto-udpdate script
const versionNumber = parseFloat(params[0]); const versionNumber = parseFloat(params[0]);
const {resolveVersionConflicts} = await import("../dynamic/auto-update.js?v=1.93.00"); const {resolveVersionConflicts} = await import("../dynamic/auto-update.js?v=1.94.00");
resolveVersionConflicts(versionNumber); resolveVersionConflicts(versionNumber);
} }

View file

@ -147,7 +147,7 @@ function addBurg(point) {
const type = BurgsAndStates.getType(cell, false); const type = BurgsAndStates.getType(cell, false);
// generate emblem // generate emblem
const coa = COA.generate(pack.states[state].coa, 0.25, null, type); const coa = COA.generate(pack.states[state].coa, 0.25, null, type, pack.cultures, culture);
coa.shield = COA.getShield(culture, state); coa.shield = COA.getShield(culture, state);
COArenderer.add("burg", i, coa, x, y); COArenderer.add("burg", i, coa, x, y);
@ -1176,13 +1176,13 @@ function refreshAllEditors() {
// dynamically loaded editors // dynamically loaded editors
async function editStates() { async function editStates() {
if (customization) return; if (customization) return;
const Editor = await import("../dynamic/editors/states-editor.js?v=1.92.00"); const Editor = await import("../dynamic/editors/states-editor.js?v=1.94.00");
Editor.open(); Editor.open();
} }
async function editCultures() { async function editCultures() {
if (customization) return; if (customization) return;
const Editor = await import("../dynamic/editors/cultures-editor.js?v=1.91.00"); const Editor = await import("../dynamic/editors/cultures-editor.js?v=1.94.00");
Editor.open(); Editor.open();
} }

View file

@ -212,7 +212,7 @@ function editEmblem(type, id, el) {
} }
const shield = el.coa.shield || COA.getShield(el.culture || parent?.culture || 0, el.state); const shield = el.coa.shield || COA.getShield(el.culture || parent?.culture || 0, el.state);
el.coa = COA.generate(parent ? parent.coa : null, 0.3, 0.1, null); el.coa = COA.generate(parent ? parent.coa : null, 0.3, 0.1, null, pack.cultures, (el.culture || parent?.culture || 0));
el.coa.shield = shield; el.coa.shield = shield;
emblemShapeSelector.disabled = false; emblemShapeSelector.disabled = false;
emblemShapeSelector.value = el.coa.shield; emblemShapeSelector.value = el.coa.shield;

View file

@ -1017,7 +1017,7 @@ function editProvinces() {
const kinship = burg ? 0.8 : 0.4; const kinship = burg ? 0.8 : 0.4;
const parent = burg ? pack.burgs[burg].coa : pack.states[state].coa; const parent = burg ? pack.burgs[burg].coa : pack.states[state].coa;
const type = BurgsAndStates.getType(center, parent.port); const type = BurgsAndStates.getType(center, parent.port);
const coa = COA.generate(parent, kinship, P(0.1), type); const coa = COA.generate(parent, kinship, P(0.1), type, pack.cultures, c);
coa.shield = COA.getShield(c, state); coa.shield = COA.getShield(c, state);
COArenderer.add("province", province, coa, point[0], point[1]); COArenderer.add("province", province, coa, point[0], point[1]);

View file

@ -308,7 +308,7 @@ function recreateStates() {
const expansionism = rn(Math.random() * powerInput.value + 1, 1); const expansionism = rn(Math.random() * powerInput.value + 1, 1);
const cultureType = pack.cultures[culture].type; const cultureType = pack.cultures[culture].type;
const coa = COA.generate(capital.coa, 0.3, null, cultureType); const coa = COA.generate(capital.coa, 0.3, null, cultureType, pack.cultures, culture);
coa.shield = capital.coa.shield; coa.shield = capital.coa.shield;
newStates.push({i, name, type, capital: capital.i, center: capital.cell, culture, expansionism, coa}); newStates.push({i, name, type, capital: capital.i, center: capital.cell, culture, expansionism, coa});
@ -430,7 +430,7 @@ function regenerateEmblems() {
pack.states.forEach(state => { pack.states.forEach(state => {
if (!state.i || state.removed) return; if (!state.i || state.removed) return;
const cultureType = pack.cultures[state.culture].type; const cultureType = pack.cultures[state.culture].type;
state.coa = COA.generate(null, null, null, cultureType); state.coa = COA.generate(null, null, null, cultureType, pack.cultures, state.culture);
state.coa.shield = COA.getShield(state.culture, null); state.coa.shield = COA.getShield(state.culture, null);
}); });
@ -442,7 +442,7 @@ function regenerateEmblems() {
if (burg.capital) kinship += 0.1; if (burg.capital) kinship += 0.1;
else if (burg.port) kinship -= 0.1; else if (burg.port) kinship -= 0.1;
if (state && burg.culture !== state.culture) kinship -= 0.25; if (state && burg.culture !== state.culture) kinship -= 0.25;
burg.coa = COA.generate(state ? state.coa : null, kinship, null, burg.type); burg.coa = COA.generate(state ? state.coa : null, kinship, null, burg.type, pack.cultures, burg.culture);
burg.coa.shield = COA.getShield(burg.culture, state ? burg.state : 0); burg.coa.shield = COA.getShield(burg.culture, state ? burg.state : 0);
}); });
@ -464,7 +464,7 @@ function regenerateEmblems() {
const kinship = dominion ? 0 : nameByBurg ? 0.8 : 0.4; const kinship = dominion ? 0 : nameByBurg ? 0.8 : 0.4;
const culture = pack.cells.culture[province.center]; const culture = pack.cells.culture[province.center];
const type = BurgsAndStates.getType(province.center, parent.port); const type = BurgsAndStates.getType(province.center, parent.port);
province.coa = COA.generate(parent.coa, kinship, dominion, type); province.coa = COA.generate(parent.coa, kinship, dominion, type, pack.cultures, culture);
province.coa.shield = COA.getShield(culture, province.state); province.coa.shield = COA.getShield(culture, province.state);
}); });

View file

@ -48,6 +48,19 @@ function rw(object) {
return array[Math.floor(Math.random() * array.length)]; return array[Math.floor(Math.random() * array.length)];
} }
// return random value from weighted array {"key1":weight1, "key2":weight2}, removing excluded keys
function rwx(object, exclude) {
const array = [];
for (const key in object) {
if (!exclude.includes(key)) {
for (let i = 0; i < object[key]; i++) {
array.push(key);
}
}
}
return array[Math.floor(Math.random() * array.length)];
}
// return a random integer from min to max biased towards one end based on exponent distribution (the bigger ex the higher bias towards min) // return a random integer from min to max biased towards one end based on exponent distribution (the bigger ex the higher bias towards min)
function biased(min, max, ex) { function biased(min, max, ex) {
return Math.round(min + (max - min) * Math.pow(Math.random(), ex)); return Math.round(min + (max - min) * Math.pow(Math.random(), ex));

View file

@ -1,7 +1,7 @@
"use strict"; "use strict";
// version and caching control // version and caching control
const version = "1.93.05"; // generator version, update each time const version = "1.94.00"; // generator version, update each time
{ {
document.title += " v" + version; document.title += " v" + version;
@ -39,6 +39,7 @@ const version = "1.93.05"; // generator version, update each time
<li>Google translation support (in Options)</li> <li>Google translation support (in Options)</li>
<li>Religions can be edited and redrawn like cultures</li> <li>Religions can be edited and redrawn like cultures</li>
<li>Lock states, provinces, cultures, and religions from regeneration</li> <li>Lock states, provinces, cultures, and religions from regeneration</li>
<li>Adjust apsects of the emblems created for a culture in the culture editor</li>
</ul> </ul>
<p>Join our <a href="${discord}" target="_blank">Discord server</a> and <a href="${reddit}" target="_blank">Reddit community</a> to ask questions, share maps, discuss the Generator and Worlbuilding, report bugs and propose new features.</p> <p>Join our <a href="${discord}" target="_blank">Discord server</a> and <a href="${reddit}" target="_blank">Reddit community</a> to ask questions, share maps, discuss the Generator and Worlbuilding, report bugs and propose new features.</p>