From c278829f11a0e7453f84b9034e09181c69ce417d Mon Sep 17 00:00:00 2001 From: John Morrow Date: Wed, 1 Nov 2023 00:22:01 -0400 Subject: [PATCH] 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. --- index.html | 18 +- modules/burgs-and-states.js | 8 +- modules/coa-generator.js | 663 +++++++++++++++++++-- modules/cultures-generator.js | 289 ++++----- modules/dynamic/auto-update.js | 11 + modules/dynamic/editors/cultures-editor.js | 100 +++- modules/dynamic/editors/states-editor.js | 4 +- modules/io/load.js | 3 +- modules/ui/editors.js | 6 +- modules/ui/emblems-editor.js | 2 +- modules/ui/provinces-editor.js | 2 +- modules/ui/tools.js | 8 +- utils/probabilityUtils.js | 13 + versioning.js | 3 +- 14 files changed, 902 insertions(+), 228 deletions(-) diff --git a/index.html b/index.html index 57767b30..0326f3f8 100644 --- a/index.html +++ b/index.html @@ -7950,7 +7950,7 @@ - + @@ -7964,7 +7964,7 @@ - + @@ -7978,14 +7978,14 @@ - + - + - + @@ -8001,11 +8001,11 @@ - - + + - + @@ -8030,7 +8030,7 @@ - + diff --git a/modules/burgs-and-states.js b/modules/burgs-and-states.js index ff433f46..302e152b 100644 --- a/modules/burgs-and-states.js +++ b/modules/burgs-and-states.js @@ -97,7 +97,7 @@ window.BurgsAndStates = (function () { const name = Names.getState(basename, b.culture); 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); states.push({ i, @@ -216,7 +216,7 @@ window.BurgsAndStates = (function () { if (b.culture !== state.culture) kinship -= 0.25; b.type = getType(i, b.port); 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); } @@ -1005,7 +1005,7 @@ window.BurgsAndStates = (function () { const color = getMixedColor(s.color); const kinship = nameByBurg ? 0.8 : 0.4; 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); 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 kinship = dominion ? 0 : 0.4; 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); provinces.push({i: provinceId, state: s.i, center, burg, name, formName, fullName, color, coa}); diff --git a/modules/coa-generator.js b/modules/coa-generator.js index 188707c3..ae0a1121 100644 --- a/modules/coa-generator.js +++ b/modules/coa-generator.js @@ -933,6 +933,70 @@ window.COA = (function () { ornaments: 0, // 9 charges 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: { conventional: 10, crosses: 8, @@ -959,14 +1023,103 @@ window.COA = (function () { conventional: 4, 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: { annulet: 4, - billet: 5, + billet: 3, carreau: 1, comet: 1, compassRose: 1, crescent: 5, - delf: 0, + delf: 3, estoile: 1, fleurDeLis: 6, fountain: 1, @@ -1381,7 +1534,267 @@ window.COA = (function () { ribbon7: 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) @@ -1640,6 +2053,66 @@ window.COA = (function () { 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 = { variants: { perPale: 5, @@ -1854,21 +2327,112 @@ window.COA = (function () { 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) { parent = null; kinship = 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 usedTinctures = []; - const t1 = P(kinship) ? parent.t1 : getTincture("field"); + const t1 = P(kinship) ? parent.t1 : getTincture("field", excludeTincturesSet); if (t1.includes("-")) usedPattern = 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 = (addCharge && P(0.3)) || P(0.5) ? parent?.ordinaries && P(kinship) @@ -1876,8 +2440,9 @@ window.COA = (function () { : rw(ordinaries.lined) : null; + 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); @@ -1898,15 +2463,16 @@ window.COA = (function () { })(); 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}; 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) { - coa.ordinaries = [{ordinary, t: getTincture("charge", usedTinctures, coa.t1)}]; - if (linedOrdinary) coa.ordinaries[0].line = usedPattern || (division && P(0.7)) ? "straight" : rw(lines); + coa.ordinaries = [{ordinary, t: getTincture("charge", excludeTincturesSet, usedTinctures, coa.t1)}]; + 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 (P(0.8)) coa.ordinaries[0].divided = "counter"; // 40% @@ -1919,8 +2485,9 @@ window.COA = (function () { if (addCharge) { const charge = (() => { if (parent?.charges && P(kinship - 0.1)) return parent.charges[0].charge; - if (type && type !== "Generic" && P(0.3)) return rw(typeMapping[type]); - return selectCharge(ordinary || divisioned ? charges.types : charges.single); + if (type && allowTypeMapping && type !== "Generic" && P(0.3)) + return rwx(typeMapping[type], excludeCultureChargesSet); + return selectCharge(ordinary || divisioned ? chargesTypesSet : chargesSingleSet, chargesTypesSet, chargesSingleSet, excludeCultureChargesSet); })(); const chargeData = charges.data[charge] || {}; @@ -1933,54 +2500,56 @@ window.COA = (function () { if (ordinaryData?.positionsOn && P(0.8)) { // place charge over ordinary (use tincture of field type) 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)) { // place charge out of ordinary (use tincture of ordinary type) 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]) { // place charge in fields made by 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) { // place charge-suitable position p = rw(chargeData.positions); - t = getTincture("charge", usedTinctures, coa.t1); + t = getTincture("charge", excludeTincturesSet, usedTinctures, coa.t1); } else { // place in standard position (use new tincture) 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; const item = {charge: charge, t, p}; const colors = chargeData.colors || 1; - if (colors > 1) item.t2 = P(0.25) ? getTincture("charge", usedTinctures, coa.t1) : t; - if (colors > 2 && item.t2) item.t3 = P(0.5) ? 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", excludeTincturesSet, usedTinctures, coa.t1) : t; coa.charges = [item]; if (p === "ABCDEFGHIKL" && P(0.95)) { // add central charge if charge is in bordure - coa.charges[0].charge = rw(charges.conventional); - const charge = selectCharge(charges.single); - const t = getTincture("charge", usedTinctures, coa.t1); + coa.charges[0].charge = rwx(chargesSingleSet, excludeChargesSet); + const charge = selectCharge(chargesSingleSet, chargesTypesSet, chargesSingleSet, excludeChargesSet) +; + const t = getTincture("charge", excludeTincturesSet, usedTinctures, coa.t1); coa.charges.push({charge, t, p: "e"}); } else if (P(0.8) && charge === "inescutcheon") { // add charge to inescutcheon - const charge = selectCharge(charges.types); - const t2 = getTincture("charge", [], t); + const charge = selectCharge(chargesTypesSet, chargesTypesSet, chargesSingleSet, excludeChargesSet) +; + const t2 = getTincture("charge", excludeTincturesSet, [], t); coa.charges.push({charge, t: t2, p, size: 0.5}); } else if (division && !ordinary) { const allowCounter = !usedPattern && (!coa.line || coa.line === "straight"); - // dimidiation: second charge at division basic positons if (P(0.3) && ["perPale", "perFess"].includes(division) && coa.line === "straight") { coa.charges[0].divided = "field"; if (P(0.95)) { const p2 = p === "e" || P(0.5) ? "e" : rw(positions.divisions[division]); - const charge = selectCharge(charges.single); - const t = getTincture("charge", usedTinctures, coa.division.t); + const charge = selectCharge(chargesSingleSet, chargesTypesSet, chargesSingleSet, excludeChargesSet) +; + const t = getTincture("charge", excludeTincturesSet, usedTinctures, coa.division.t); coa.charges.push({charge, t, p: p2, divided: "division"}); } } else if (allowCounter && P(0.4)) coa.charges[0].divided = "counter"; @@ -1997,22 +2566,26 @@ window.COA = (function () { : ["j", "o"]; // perBendSinister coa.charges[0].p = p1; - const charge = selectCharge(charges.single); - const t = getTincture("charge", usedTinctures, coa.division.t); + const charge = selectCharge(chargesSingleSet, chargesTypesSet, chargesSingleSet, excludeChargesSet) +; + const t = getTincture("charge", excludeTincturesSet, usedTinctures, coa.division.t); coa.charges.push({charge, t, p: p2}); } else if (["perCross", "perSaltire"].includes(division) && P(0.5)) { // place 4 charges in division standard positions const [p1, p2, p3, p4] = division === "perCross" ? ["j", "l", "m", "o"] : ["b", "d", "f", "h"]; coa.charges[0].p = p1; - const c2 = selectCharge(charges.single); - const t2 = getTincture("charge", [], coa.division.t); + const c2 = selectCharge(chargesSingleSet, chargesTypesSet, chargesSingleSet, excludeChargesSet) +; + const t2 = getTincture("charge", excludeTincturesSet, [], coa.division.t); - const c3 = selectCharge(charges.single); - const t3 = getTincture("charge", [], coa.division.t); + const c3 = selectCharge(chargesSingleSet, chargesTypesSet, chargesSingleSet, excludeChargesSet) +; + const t3 = getTincture("charge", excludeTincturesSet, [], coa.division.t); - const c4 = selectCharge(charges.single); - const t4 = getTincture("charge", [], coa.t1); + const c4 = selectCharge(chargesSingleSet, chargesTypesSet, chargesSingleSet, excludeChargesSet) +; + 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}); } 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 if (P(dominion) && parent.charges) { 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}; coa.charges?.forEach((charge, i) => { @@ -2036,7 +2609,7 @@ window.COA = (function () { if (charge === "inescutcheon" && parent.charges[1]) charge = parent.charges[1].charge; 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 = []; 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]); } - function selectCharge(set) { - const type = set ? rw(set) : ordinary || divisioned ? rw(charges.types) : rw(charges.single); - return type === "inescutcheon" ? "inescutcheon" : rw(charges[type]); + function selectCharge(set, chargesTypesSet, chargesSingleSet, exclude) { + const type = set ? rw(set) : ordinary || divisioned ? rw(chargesTypesSet) : rw(chargesSingleSet); + return type === "inescutcheon" ? "inescutcheon" : rwx(charges[type], exclude); } // 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; - 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 (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)) { tincture = rw(tinctures[type]); @@ -2150,7 +2723,7 @@ window.COA = (function () { t1 = "gules"; 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) { const startWithMetal = P(0.7); diff --git a/modules/cultures-generator.js b/modules/cultures-generator.js index cb4f6d05..1d7019a7 100644 --- a/modules/cultures-generator.js +++ b/modules/cultures-generator.js @@ -15,7 +15,7 @@ window.Cultures = (function () { count = Math.floor(populated.length / 50); if (!count) { WARN && console.warn(`There are no populated cells. Cannot generate cultures`); - pack.cultures = [{name: "Wildlands", i: 0, base: 1, shield: "round"}]; + pack.cultures = [{name: "Wildlands", i: 0, base: 1, shield: "round", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"}]; cells.culture = cultureIds; alertMessage.innerHTML = /* html */ `The climate is harsh and people cannot live in this world.
@@ -106,7 +106,7 @@ window.Cultures = (function () { } // 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 if (!nameBases.length) { @@ -182,7 +182,7 @@ window.Cultures = (function () { base = defaultCultures[culture].base; name = defaultCultures[culture].name; } 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); name = Names.getCulture(culture, 5, 8, ""); base = pack.cultures[culture].base; @@ -196,6 +196,8 @@ window.Cultures = (function () { // define emblem shape let shield = culture.shield; + let charges = culture.charges; + let lines = culture.lines; const emblemShape = document.getElementById("emblemShape").value; if (emblemShape === "random") shield = getRandomShield(); @@ -213,7 +215,10 @@ window.Cultures = (function () { urban: 0, origins: [0], code, - shield + shield, + charges, + lines, + tinctures }); }; @@ -236,82 +241,82 @@ window.Cultures = (function () { if (culturesSet.value === "european") { return [ - {name: "Shwazen", base: 0, odd: 1, sort: i => n(i) / td(i, 10) / bd(i, [6, 8]), shield: "swiss"}, - {name: "Angshire", base: 1, odd: 1, sort: i => n(i) / td(i, 10) / sf(i), shield: "wedged"}, - {name: "Luari", base: 2, odd: 1, sort: i => n(i) / td(i, 12) / bd(i, [6, 8]), shield: "french"}, - {name: "Tallian", base: 3, odd: 1, sort: i => n(i) / td(i, 15), shield: "horsehead"}, - {name: "Astellian", base: 4, odd: 1, sort: i => n(i) / td(i, 16), shield: "spanish"}, - {name: "Slovan", base: 5, odd: 1, sort: i => (n(i) / td(i, 6)) * t[i], shield: "polish"}, - {name: "Norse", base: 6, odd: 1, sort: i => n(i) / td(i, 5), shield: "heater"}, - {name: "Elladan", base: 7, odd: 1, sort: i => (n(i) / td(i, 18)) * h[i], shield: "boeotian"}, - {name: "Romian", base: 8, odd: 0.2, sort: i => n(i) / td(i, 15) / t[i], shield: "roman"}, - {name: "Soumi", base: 9, odd: 1, sort: i => (n(i) / td(i, 5) / bd(i, [9])) * t[i], shield: "pavise"}, - {name: "Portuzian", base: 13, odd: 1, sort: i => n(i) / td(i, 17) / sf(i), shield: "renaissance"}, - {name: "Vengrian", base: 15, odd: 1, sort: i => (n(i) / td(i, 11) / bd(i, [4])) * t[i], shield: "horsehead2"}, - {name: "Turchian", base: 16, odd: 0.05, sort: i => n(i) / td(i, 14), shield: "round"}, - {name: "Euskati", base: 20, odd: 0.05, sort: i => (n(i) / td(i, 15)) * h[i], shield: "oldFrench"}, - {name: "Keltan", base: 22, odd: 0.05, sort: i => (n(i) / td(i, 11) / bd(i, [6, 8])) * t[i], shield: "oval"} + {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", 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", charges: "Limited", lines: "All", tinctures: "All"}, + {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", charges: "Limited", lines: "All", tinctures: "All"}, + {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", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"}, + {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", 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", charges: "Limited", lines: "All", tinctures: "All"}, + {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", charges: "Limited", lines: "All", tinctures: "All"}, + {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", 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", charges: "Limited", lines: "All", tinctures: "All"} ]; } if (culturesSet.value === "oriental") { return [ - {name: "Koryo", base: 10, odd: 1, sort: i => n(i) / td(i, 12) / t[i], shield: "round"}, - {name: "Hantzu", base: 11, odd: 1, sort: i => n(i) / td(i, 13), shield: "banner"}, - {name: "Yamoto", base: 12, odd: 1, sort: i => n(i) / td(i, 15) / t[i], shield: "round"}, - {name: "Turchian", base: 16, odd: 1, sort: i => n(i) / td(i, 12), shield: "round"}, + {name: "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", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"}, + {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", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"}, { name: "Berberan", base: 17, odd: 0.2, 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: "Efratic", base: 23, odd: 0.1, sort: i => (n(i) / td(i, 22)) * t[i], shield: "round"}, - {name: "Tehrani", base: 24, odd: 1, sort: i => (n(i) / td(i, 18)) * h[i], shield: "round"}, - {name: "Maui", base: 25, odd: 0.2, sort: i => n(i) / td(i, 24) / sf(i) / t[i], shield: "vesicaPiscis"}, - {name: "Carnatic", base: 26, odd: 0.5, sort: i => n(i) / td(i, 26), shield: "round"}, - {name: "Vietic", base: 29, odd: 0.8, sort: i => n(i) / td(i, 25) / bd(i, [7], 7) / t[i], shield: "banner"}, - {name: "Guantzu", base: 30, odd: 0.5, sort: i => n(i) / td(i, 17), shield: "banner"}, - {name: "Ulus", base: 31, odd: 1, sort: i => (n(i) / td(i, 5) / bd(i, [2, 4, 10], 7)) * t[i], shield: "banner"} + {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", charges: "Shapes", lines: "All", tinctures: "All"}, + {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", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"}, + {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", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"}, + {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", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"} ]; } if (culturesSet.value === "english") { const getName = () => Names.getBase(1, 5, 9, "", 0); return [ - {name: getName(), base: 1, odd: 1, shield: "heater"}, - {name: getName(), base: 1, odd: 1, shield: "wedged"}, - {name: getName(), base: 1, odd: 1, shield: "swiss"}, - {name: getName(), base: 1, odd: 1, shield: "oldFrench"}, - {name: getName(), base: 1, odd: 1, shield: "swiss"}, - {name: getName(), base: 1, odd: 1, shield: "spanish"}, - {name: getName(), base: 1, odd: 1, shield: "hessen"}, - {name: getName(), base: 1, odd: 1, shield: "fantasy5"}, - {name: getName(), base: 1, odd: 1, shield: "fantasy4"}, - {name: getName(), base: 1, odd: 1, shield: "fantasy1"} + {name: getName(), base: 1, odd: 1, shield: "heater", charges: "Limited", lines: "All", tinctures: "All"}, + {name: getName(), base: 1, odd: 1, shield: "wedged", charges: "Limited", lines: "All", tinctures: "All"}, + {name: getName(), base: 1, odd: 1, shield: "swiss", charges: "Limited", lines: "All", tinctures: "All"}, + {name: getName(), base: 1, odd: 1, shield: "oldFrench", charges: "Limited", lines: "All", tinctures: "All"}, + {name: getName(), base: 1, odd: 1, shield: "swiss", charges: "Limited", lines: "All", tinctures: "All"}, + {name: getName(), base: 1, odd: 1, shield: "spanish", charges: "Limited", lines: "All", tinctures: "All"}, + {name: getName(), base: 1, odd: 1, shield: "hessen", charges: "Limited", lines: "All", tinctures: "All"}, + {name: getName(), base: 1, odd: 1, shield: "fantasy5", charges: "Limited", lines: "All", tinctures: "All"}, + {name: getName(), base: 1, odd: 1, shield: "fantasy4", charges: "Limited", lines: "All", tinctures: "All"}, + {name: getName(), base: 1, odd: 1, shield: "fantasy1", charges: "Limited", lines: "All", tinctures: "All"} ]; } if (culturesSet.value === "antique") { return [ - {name: "Roman", base: 8, odd: 1, sort: i => n(i) / td(i, 14) / t[i], shield: "roman"}, // Roman - {name: "Roman", base: 8, odd: 1, sort: i => n(i) / td(i, 15) / sf(i), shield: "roman"}, // Roman - {name: "Roman", base: 8, odd: 1, sort: i => n(i) / td(i, 16) / sf(i), shield: "roman"}, // Roman - {name: "Roman", base: 8, odd: 1, sort: i => n(i) / td(i, 17) / t[i], shield: "roman"}, // Roman - {name: "Hellenic", base: 7, odd: 1, sort: i => (n(i) / td(i, 18) / sf(i)) * h[i], shield: "boeotian"}, // Greek - {name: "Hellenic", base: 7, odd: 1, sort: i => (n(i) / td(i, 19) / sf(i)) * h[i], shield: "boeotian"}, // Greek - {name: "Macedonian", base: 7, odd: 0.5, sort: i => (n(i) / td(i, 12)) * h[i], shield: "round"}, // Greek - {name: "Celtic", base: 22, odd: 1, sort: i => n(i) / td(i, 11) ** 0.5 / bd(i, [6, 8]), shield: "round"}, - {name: "Germanic", base: 0, odd: 1, sort: i => n(i) / td(i, 10) ** 0.5 / bd(i, [6, 8]), shield: "round"}, - {name: "Persian", base: 24, odd: 0.8, sort: i => (n(i) / td(i, 18)) * h[i], shield: "oval"}, // Iranian - {name: "Scythian", base: 24, odd: 0.5, sort: i => n(i) / td(i, 11) ** 0.5 / bd(i, [4]), shield: "round"}, // Iranian - {name: "Cantabrian", base: 20, odd: 0.5, sort: i => (n(i) / td(i, 16)) * h[i], shield: "oval"}, // Basque - {name: "Estian", base: 9, odd: 0.2, sort: i => (n(i) / td(i, 5)) * t[i], shield: "pavise"}, // Finnic - {name: "Carthaginian", base: 42, odd: 0.3, sort: i => n(i) / td(i, 20) / sf(i), shield: "oval"}, // Levantine - {name: "Hebrew", base: 42, odd: 0.2, sort: i => (n(i) / td(i, 19)) * sf(i), shield: "oval"}, // Levantine - {name: "Mesopotamian", base: 23, odd: 0.2, sort: i => n(i) / td(i, 22) / bd(i, [1, 2, 3]), shield: "oval"} // Mesopotamian + {name: "Roman", base: 8, odd: 1, sort: i => n(i) / td(i, 14) / t[i], shield: "roman", charges: "Limited", lines: "All", tinctures: "All"}, // 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", charges: "Limited", lines: "All", tinctures: "All"}, // 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", 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", 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", 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", 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", charges: "Limited", lines: "All", tinctures: "All"}, + {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", 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", 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", 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", 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", 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", charges: "Limited", lines: "All", tinctures: "All"} // Mesopotamian ]; } @@ -323,54 +328,54 @@ window.Cultures = (function () { base: 33, odd: 1, sort: i => (n(i) / bd(i, [6, 7, 8, 9], 10)) * t[i], - shield: "gondor" + shield: "gondor", charges: "Nature", lines: "All", tinctures: "All" }, // Elves { name: "Eldar (Elfish)", base: 33, odd: 1, sort: i => (n(i) / bd(i, [6, 7, 8, 9], 10)) * t[i], - shield: "noldor" + shield: "noldor", charges: "Nature", lines: "All", tinctures: "All" }, // Elves { name: "Trow (Dark Elfish)", base: 34, odd: 0.9, sort: i => (n(i) / bd(i, [7, 8, 9, 12], 10)) * t[i], - shield: "hessen" + shield: "hessen", charges: "Limited", lines: "All", tinctures: "All" }, // Dark Elves { name: "Lothian (Dark Elfish)", base: 34, odd: 0.3, sort: i => (n(i) / bd(i, [7, 8, 9, 12], 10)) * t[i], - shield: "wedged" + shield: "wedged", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns" }, // Dark Elves - {name: "Dunirr (Dwarven)", base: 35, odd: 1, sort: i => n(i) + h[i], shield: "ironHills"}, // Dwarfs - {name: "Khazadur (Dwarven)", base: 35, odd: 1, sort: i => n(i) + h[i], shield: "erebor"}, // Dwarfs - {name: "Kobold (Goblin)", base: 36, odd: 1, sort: i => t[i] - s[i], shield: "moriaOrc"}, // Goblin - {name: "Uruk (Orkish)", base: 37, odd: 1, sort: i => h[i] * t[i], shield: "urukHai"}, // Orc + {name: "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", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"}, // Dwarfs + {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", charges: "Limited", lines: "Straight", tinctures: "NoPatterns"}, // Orc { name: "Ugluk (Orkish)", base: 37, odd: 0.5, sort: i => (h[i] * t[i]) / bd(i, [1, 2, 10, 11]), - shield: "moriaOrc" + shield: "moriaOrc", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns" }, // Orc - {name: "Yotunn (Giants)", base: 38, odd: 0.7, sort: i => td(i, -10), shield: "pavise"}, // Giant - {name: "Rake (Drakonic)", base: 39, odd: 0.7, sort: i => -s[i], shield: "fantasy2"}, // Draconic - {name: "Arago (Arachnid)", base: 40, odd: 0.7, sort: i => t[i] - s[i], shield: "horsehead2"}, // Arachnid - {name: "Aj'Snaga (Serpents)", base: 41, odd: 0.7, sort: i => n(i) / bd(i, [12], 10), shield: "fantasy1"}, // Serpents + {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", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"}, // Draconic + {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", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"}, // Serpents // fantasy human - {name: "Anor (Human)", base: 32, odd: 1, sort: i => n(i) / td(i, 10), shield: "fantasy5"}, - {name: "Dail (Human)", base: 32, odd: 1, sort: i => n(i) / td(i, 13), shield: "roman"}, - {name: "Rohand (Human)", base: 16, odd: 1, sort: i => n(i) / td(i, 16), shield: "round"}, + {name: "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", charges: "Limited", lines: "All", tinctures: "All"}, + {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)", base: 31, odd: 1, 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") { return [ // common real-world English - {name: "Angshire", base: 1, odd: 1, sort: i => n(i) / td(i, 10) / sf(i), shield: "heater"}, - {name: "Enlandic", base: 1, odd: 1, sort: i => n(i) / td(i, 12), shield: "heater"}, - {name: "Westen", base: 1, odd: 1, sort: i => n(i) / td(i, 10), shield: "heater"}, - {name: "Nortumbic", base: 1, odd: 1, sort: i => n(i) / td(i, 7), shield: "heater"}, - {name: "Mercian", base: 1, odd: 1, sort: i => n(i) / td(i, 9), shield: "heater"}, - {name: "Kentian", base: 1, odd: 1, sort: i => n(i) / td(i, 12), shield: "heater"}, + {name: "Angshire", base: 1, odd: 1, sort: i => n(i) / td(i, 10) / sf(i), shield: "heater", charges: "Limited", lines: "All", tinctures: "All"}, + {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", charges: "Limited", lines: "All", tinctures: "All"}, + {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", charges: "Limited", lines: "All", tinctures: "All"}, + {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 - {name: "Norse", base: 6, odd: 0.7, sort: i => n(i) / td(i, 5) / sf(i), shield: "oldFrench"}, - {name: "Schwarzen", base: 0, odd: 0.3, sort: i => n(i) / td(i, 10) / bd(i, [6, 8]), shield: "gonfalon"}, - {name: "Luarian", base: 2, odd: 0.3, sort: i => n(i) / td(i, 12) / bd(i, [6, 8]), shield: "oldFrench"}, - {name: "Hetallian", base: 3, odd: 0.3, sort: i => n(i) / td(i, 15), shield: "oval"}, - {name: "Astellian", base: 4, odd: 0.3, sort: i => n(i) / td(i, 16), shield: "spanish"}, + {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", 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", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"}, + {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", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"}, // rare real-world exotic { name: "Kiswaili", base: 28, odd: 0.05, 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: "Koryo", base: 10, odd: 0.05, sort: i => n(i) / td(i, 12) / t[i], shield: "round"}, - {name: "Hantzu", base: 11, odd: 0.05, sort: i => n(i) / td(i, 13), shield: "banner"}, - {name: "Yamoto", base: 12, odd: 0.05, sort: i => n(i) / td(i, 15) / t[i], shield: "round"}, - {name: "Guantzu", base: 30, odd: 0.05, sort: i => n(i) / td(i, 17), shield: "banner"}, + {name: "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", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"}, + {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", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"}, + {name: "Guantzu", base: 30, odd: 0.05, sort: i => n(i) / td(i, 17), shield: "banner", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"}, { name: "Ulus", base: 31, odd: 0.05, 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", base: 17, odd: 0.05, 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.05, 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", base: 22, odd: 0.1, 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: "Romian", base: 8, odd: 0.2, sort: i => n(i) / td(i, 14) / t[i], shield: "roman"}, + {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", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"}, // 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: "Trow", base: 34, odd: 0.8, sort: i => (n(i) / bd(i, [7, 8, 9, 12], 10)) * t[i], shield: "hessen"}, // Dark Elves - {name: "Durinn", base: 35, odd: 0.8, sort: i => n(i) + h[i], shield: "erebor"}, // Dwarven - {name: "Kobblin", base: 36, odd: 0.8, sort: i => t[i] - s[i], shield: "moriaOrc"}, // Goblin - {name: "Uruk", base: 37, odd: 0.8, sort: i => (h[i] * t[i]) / bd(i, [1, 2, 10, 11]), shield: "urukHai"}, // Orc - {name: "Yotunn", base: 38, odd: 0.8, sort: i => td(i, -10), shield: "pavise"}, // Giant - {name: "Drake", base: 39, odd: 0.9, sort: i => -s[i], shield: "fantasy2"}, // Draconic - {name: "Rakhnid", base: 40, odd: 0.9, sort: i => t[i] - s[i], shield: "horsehead2"}, // Arachnid - {name: "Aj'Snaga", base: 41, odd: 0.9, sort: i => n(i) / bd(i, [12], 10), shield: "fantasy1"} // Serpents + {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", charges: "Shapes", lines: "All", tinctures: "All"}, // Dark Elves + {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", 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", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"}, // Orc + {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", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"}, // Draconic + {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", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"} // Serpents ]; } @@ -458,51 +463,51 @@ window.Cultures = (function () { // all-world return [ - {name: "Shwazen", base: 0, odd: 0.7, sort: i => n(i) / td(i, 10) / bd(i, [6, 8]), shield: "hessen"}, - {name: "Angshire", base: 1, odd: 1, sort: i => n(i) / td(i, 10) / sf(i), shield: "heater"}, - {name: "Luari", base: 2, odd: 0.6, sort: i => n(i) / td(i, 12) / bd(i, [6, 8]), shield: "oldFrench"}, - {name: "Tallian", base: 3, odd: 0.6, sort: i => n(i) / td(i, 15), shield: "horsehead2"}, - {name: "Astellian", base: 4, odd: 0.6, sort: i => n(i) / td(i, 16), shield: "spanish"}, - {name: "Slovan", base: 5, odd: 0.7, sort: i => (n(i) / td(i, 6)) * t[i], shield: "round"}, - {name: "Norse", base: 6, odd: 0.7, sort: i => n(i) / td(i, 5), shield: "heater"}, - {name: "Elladan", base: 7, odd: 0.7, sort: i => (n(i) / td(i, 18)) * h[i], shield: "boeotian"}, - {name: "Romian", base: 8, odd: 0.7, sort: i => n(i) / td(i, 15), shield: "roman"}, - {name: "Soumi", base: 9, odd: 0.3, sort: i => (n(i) / td(i, 5) / bd(i, [9])) * t[i], shield: "pavise"}, - {name: "Koryo", base: 10, odd: 0.1, sort: i => n(i) / td(i, 12) / t[i], shield: "round"}, - {name: "Hantzu", base: 11, odd: 0.1, sort: i => n(i) / td(i, 13), shield: "banner"}, - {name: "Yamoto", base: 12, odd: 0.1, sort: i => n(i) / td(i, 15) / t[i], shield: "round"}, - {name: "Portuzian", base: 13, odd: 0.4, sort: i => n(i) / td(i, 17) / sf(i), shield: "spanish"}, - {name: "Nawatli", base: 14, odd: 0.1, sort: i => h[i] / td(i, 18) / bd(i, [7]), shield: "square"}, - {name: "Vengrian", base: 15, odd: 0.2, sort: i => (n(i) / td(i, 11) / bd(i, [4])) * t[i], shield: "wedged"}, - {name: "Turchian", base: 16, odd: 0.2, sort: i => n(i) / td(i, 13), shield: "round"}, + {name: "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", 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", charges: "Limited", lines: "All", tinctures: "All"}, + {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", charges: "Limited", lines: "All", tinctures: "All"}, + {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", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"}, + {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", 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", charges: "Limited", lines: "All", tinctures: "All"}, + {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", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"}, + {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", 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", 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", charges: "Limited", lines: "All", tinctures: "All"}, + {name: "Turchian", base: 16, odd: 0.2, sort: i => n(i) / td(i, 13), shield: "round", charges: "Limited", lines: "All", tinctures: "NoPatterns"}, { name: "Berberan", base: 17, odd: 0.1, 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: "Inuk", base: 19, odd: 0.05, sort: i => td(i, -1) / bd(i, [10, 11]) / sf(i), shield: "square"}, - {name: "Euskati", base: 20, odd: 0.05, sort: i => (n(i) / td(i, 15)) * h[i], shield: "spanish"}, - {name: "Yoruba", base: 21, odd: 0.05, sort: i => n(i) / td(i, 15) / bd(i, [5, 7]), shield: "vesicaPiscis"}, + {name: "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", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"}, + {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", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"}, { name: "Keltan", base: 22, odd: 0.05, 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: "Tehrani", base: 24, odd: 0.1, sort: i => (n(i) / td(i, 18)) * h[i], shield: "round"}, - {name: "Maui", base: 25, odd: 0.05, sort: i => n(i) / td(i, 24) / sf(i) / t[i], shield: "round"}, - {name: "Carnatic", base: 26, odd: 0.05, sort: i => n(i) / td(i, 26), shield: "round"}, - {name: "Inqan", base: 27, odd: 0.05, sort: i => h[i] / td(i, 13), shield: "square"}, - {name: "Kiswaili", base: 28, odd: 0.1, sort: i => n(i) / td(i, 29) / bd(i, [1, 3, 5, 7]), shield: "vesicaPiscis"}, - {name: "Vietic", base: 29, odd: 0.1, sort: i => n(i) / td(i, 25) / bd(i, [7], 7) / t[i], shield: "banner"}, - {name: "Guantzu", base: 30, odd: 0.1, sort: i => n(i) / td(i, 17), shield: "banner"}, - {name: "Ulus", base: 31, odd: 0.1, sort: i => (n(i) / td(i, 5) / bd(i, [2, 4, 10], 7)) * t[i], shield: "banner"}, - {name: "Hebrew", base: 42, odd: 0.2, sort: i => (n(i) / td(i, 18)) * sf(i), shield: "oval"} // Levantine + {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", 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", charges: "Limited", lines: "Limited", tinctures: "NoPatterns"}, + {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", 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", 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", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"}, + {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", charges: "Shapes", lines: "Limited", tinctures: "NoPatterns"}, + {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 ]; }; diff --git a/modules/dynamic/auto-update.js b/modules/dynamic/auto-update.js index b88e7d43..31030d01 100644 --- a/modules/dynamic/auto-update.js +++ b/modules/dynamic/auto-update.js @@ -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"; + }); + } } diff --git a/modules/dynamic/editors/cultures-editor.js b/modules/dynamic/editors/cultures-editor.js index 053a177e..c0bc2d21 100644 --- a/modules/dynamic/editors/cultures-editor.js +++ b/modules/dynamic/editors/cultures-editor.js @@ -1,7 +1,10 @@ const $body = insertEditorHtml(); addListeners(); +const chargesTypes = ["All", "Limited", "Nature", "Things", "Nature&Things", "Shapes", "None"]; const cultureTypes = ["Generic", "River", "Lake", "Naval", "Nomadic", "Hunting", "Highland"]; +const linesTypes = ["All", "Limited", "Straight", "None"]; +const tinctureTypes = ["All", "NoPatterns", "NoStains"]; export function open() { closeDialogs("#culturesEditor, .stable"); @@ -21,10 +24,10 @@ export function open() { }); $body.focus(); } - function insertEditorHtml() { const editorHtml = /* html */ `
-
+
Culture 
Type 
Namesbase 
@@ -33,6 +36,11 @@ function insertEditorHtml() {
Area 
Population 
Emblems 
+
Charges 
+
Lines 
+
Tinctures 
+
 
+
 
@@ -42,7 +50,6 @@ function insertEditorHtml() {
Land Area: 0
Population: 0
-
@@ -165,8 +172,13 @@ function culturesEditorAddLines() { data-type="" data-expansionism="" data-emblems="${c.shield}" + data-charges="${c.charges}" + data-lines="${c.lines}" + data-tinctures="${c.tinctures}" + data-lock="" + data-delete="" > - + @@ -175,15 +187,20 @@ function culturesEditorAddLines() { -
${c.cells}
+
${c.cells}
- +
${si(area)} ${unit}
${si(population)}
- ${getShapeOptions(selectShape, c.shield)} + + + + + +
`; continue; } @@ -200,8 +217,13 @@ function culturesEditorAddLines() { data-type="${c.type}" data-expansionism="${c.expansionism}" data-emblems="${c.shield}" + data-charges="${c.charges}" + data-lines="${c.lines}" + data-tinctures="${c.tinctures}" + data-lock="" + data-delete="" > - + @@ -212,7 +234,6 @@ function culturesEditorAddLines() { class="cultureBase">${getBaseOptions(c.base)}
${c.cells}
-
${si(area)} ${unit}
${si(population)}
- ${getShapeOptions(selectShape, c.shield)} + + + +
`; @@ -255,6 +285,9 @@ function culturesEditorAddLines() { $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.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 > span.icon-arrows-cw").forEach($el => $el.on("click", cultureRegenerateBurgs)); $body.querySelectorAll("div > span.icon-trash-empty").forEach($el => $el.on("click", cultureRemovePrompt)); @@ -263,6 +296,9 @@ function culturesEditorAddLines() { const $culturesHeader = byId("culturesHeader"); $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") { $body.dataset.type = "absolute"; @@ -272,9 +308,27 @@ function culturesEditorAddLines() { $("#culturesEditor").dialog({width: fitContent()}); } -function getTypeOptions(type) { +function getChargesOptions(charges) { let options = ""; - cultureTypes.forEach(t => (options += ``)); + chargesTypes.forEach(c => (options += ``)); + return options; +} + +function getLinesOptions(lines) { + let options = ""; + linesTypes.forEach(l => (options += ``)); + return options; +} + +function getTincturesOptions(tinctures) { + let options = ""; + tinctureTypes.forEach(t => (options += ``)); + return options; +} + +function getTypeOptions(types) { + let options = ""; + cultureTypes.forEach(t => (options += ``)); return options; } @@ -293,7 +347,7 @@ function getShapeOptions(selectShape, selected) { const options = shapes.map( shape => `` ); - return ``; + return options; } 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() { const cultureId = +this.parentNode.dataset.id; const culture = pack.cultures[cultureId]; diff --git a/modules/dynamic/editors/states-editor.js b/modules/dynamic/editors/states-editor.js index a6128c57..607f3636 100644 --- a/modules/dynamic/editors/states-editor.js +++ b/modules/dynamic/editors/states-editor.js @@ -1139,7 +1139,7 @@ function adjustProvinces(affectedProvinces) { const kinship = nameByBurg ? 0.8 : 0.4; 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); provinces.push({ @@ -1240,7 +1240,7 @@ function addState() { // generate emblem 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); // update diplomacy and reverse relations diff --git a/modules/io/load.js b/modules/io/load.js index 6208b99b..047208a4 100644 --- a/modules/io/load.js +++ b/modules/io/load.js @@ -109,7 +109,6 @@ function uploadMap(file, callback) { uploadMap.timeStart = performance.now(); const OLDEST_SUPPORTED_VERSION = 0.7; const currentVersion = parseFloat(version); - const fileReader = new FileReader(); fileReader.onloadend = async function (fileLoadedEvent) { if (callback) callback(); @@ -456,7 +455,7 @@ async function parseLoadedData(data) { { // dynamically import and run auto-udpdate script 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); } diff --git a/modules/ui/editors.js b/modules/ui/editors.js index d28abe30..9e67adba 100644 --- a/modules/ui/editors.js +++ b/modules/ui/editors.js @@ -147,7 +147,7 @@ function addBurg(point) { const type = BurgsAndStates.getType(cell, false); // 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); COArenderer.add("burg", i, coa, x, y); @@ -1176,13 +1176,13 @@ function refreshAllEditors() { // dynamically loaded editors async function editStates() { 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(); } async function editCultures() { 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(); } diff --git a/modules/ui/emblems-editor.js b/modules/ui/emblems-editor.js index 2f36454c..2b9a6ba9 100644 --- a/modules/ui/emblems-editor.js +++ b/modules/ui/emblems-editor.js @@ -212,7 +212,7 @@ function editEmblem(type, id, el) { } 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; emblemShapeSelector.disabled = false; emblemShapeSelector.value = el.coa.shield; diff --git a/modules/ui/provinces-editor.js b/modules/ui/provinces-editor.js index 07ee522d..a685c2bd 100644 --- a/modules/ui/provinces-editor.js +++ b/modules/ui/provinces-editor.js @@ -1017,7 +1017,7 @@ function editProvinces() { const kinship = burg ? 0.8 : 0.4; const parent = burg ? pack.burgs[burg].coa : pack.states[state].coa; 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); COArenderer.add("province", province, coa, point[0], point[1]); diff --git a/modules/ui/tools.js b/modules/ui/tools.js index e1d554da..cf68fe70 100644 --- a/modules/ui/tools.js +++ b/modules/ui/tools.js @@ -308,7 +308,7 @@ function recreateStates() { const expansionism = rn(Math.random() * powerInput.value + 1, 1); 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; newStates.push({i, name, type, capital: capital.i, center: capital.cell, culture, expansionism, coa}); @@ -430,7 +430,7 @@ function regenerateEmblems() { pack.states.forEach(state => { if (!state.i || state.removed) return; 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); }); @@ -442,7 +442,7 @@ function regenerateEmblems() { if (burg.capital) kinship += 0.1; else if (burg.port) kinship -= 0.1; 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); }); @@ -464,7 +464,7 @@ function regenerateEmblems() { const kinship = dominion ? 0 : nameByBurg ? 0.8 : 0.4; const culture = pack.cells.culture[province.center]; 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); }); diff --git a/utils/probabilityUtils.js b/utils/probabilityUtils.js index b0234251..a4398933 100644 --- a/utils/probabilityUtils.js +++ b/utils/probabilityUtils.js @@ -48,6 +48,19 @@ function rw(object) { 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) function biased(min, max, ex) { return Math.round(min + (max - min) * Math.pow(Math.random(), ex)); diff --git a/versioning.js b/versioning.js index 92fdf0e1..fc17359a 100644 --- a/versioning.js +++ b/versioning.js @@ -1,7 +1,7 @@ "use strict"; // 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; @@ -39,6 +39,7 @@ const version = "1.93.05"; // generator version, update each time
  • Google translation support (in Options)
  • Religions can be edited and redrawn like cultures
  • Lock states, provinces, cultures, and religions from regeneration
  • +
  • Adjust apsects of the emblems created for a culture in the culture editor
  • Join our Discord server and Reddit community to ask questions, share maps, discuss the Generator and Worlbuilding, report bugs and propose new features.