v1.5.01 - COA gen integration, first try, embed

This commit is contained in:
Azgaar 2021-01-26 13:38:37 +03:00
parent e1bc6d9eba
commit b86d985607
9 changed files with 516 additions and 60 deletions

View file

@ -1391,6 +1391,11 @@ div.states>input.riverType {
width: 5em;
}
div.states>embed.emblemIcon {
margin: -.4em 0;
cursor: pointer;
}
#diplomacyBodySection > div {
cursor: pointer;
}

View file

@ -2383,7 +2383,7 @@
</div>
<button id="burgSeeInMFCG" data-tip="Open burg in the Medieval Fantasy City Generator by Watabou. Ctrl + click to change the seed" class="icon-map-o"></button>
<button id="burgOpenCOA" data-tip="Open burg's COA in the Iron Arachne Heraldry Generator. Ctrl + click to change the seed" class="icon-shield-alt"></button>
<button id="burgOpenCOA" data-tip="Open burg's COA. Ctrl + click to change the seed" class="icon-shield-alt"></button>
<button id="burgRelocate" data-tip="Relocate burg" class="icon-target"></button>
<button id="burglLegend" data-tip="Edit free text notes (legend) for this burg" class="icon-edit"></button>
<button id="burgRemove" data-tip="Remove non-capital burg. Shortcut: Delete" class="icon-trash fastDelete"></button>
@ -3258,6 +3258,13 @@
</div>
</div>
<div id="emblemsEditor" class="dialog" style="display: none">
<div id="emblemsBody">
<embed id="emblemsEmbed" type="image/svg+xml" width="200" height="200">
</div>
<input type="text" value=""/>
</div>
<div id="unitsEditor" class="dialog stable" style="display: none">
<div id="unitsBody" style="margin-left:1.1em">
<div class="unitsHeader" style="margin-top:.4em">
@ -3709,6 +3716,7 @@
<script src="modules/routes-generator.js"></script>
<script src="modules/religions-generator.js"></script>
<script src="modules/military-generator.js"></script>
<script src="modules/coa-generator.js"></script>
<script src="libs/polylabel.min.js"></script>
<script src="libs/lineclip.js"></script>
<script src="libs/jquery-ui.min.js"></script>

View file

@ -212,7 +212,7 @@ const sourceDataForReference = {
burgEditIconStyle: "Edit icon style for burg group in Style Editor",
burgEditAnchorStyle: "Edit port icon (anchor) style for burg group in Style Editor",
burgSeeInMFCG: "Open burg in the Medieval Fantasy City Generator by Watabou. Ctrl + click to change the seed",
burgOpenCOA: "Open burg's COA in the Iron Arachne Heraldry Generator. Ctrl + click to change the seed",
burgOpenCOA: "Open burg's COA. Ctrl + click to change the seed",
burgRelocate: "Relocate burg",
burglLegend: "Edit free text notes (legend) for this burg",
burgRemove: "Remove non-capital burg. Shortcut: Delete",

View file

@ -90,7 +90,9 @@
const name = Names.getState(basename, b.culture);
const nomadic = [1, 2, 3, 4].includes(cells.biome[b.cell]);
const type = nomadic ? "Nomadic" : cultures[b.culture].type === "Nomadic" ? "Generic" : cultures[b.culture].type;
states.push({i, color: colors[i-1], name, expansionism, capital: i, type, center: b.cell, culture: b.culture});
const coa = COA.generate(null);
coa.shield = getShield(b.culture, null);
states.push({i, color: colors[i-1], name, expansionism, capital: i, type, center: b.cell, culture: b.culture, coa});
cells.burg[b.cell] = i;
});
@ -137,6 +139,12 @@
}
}
function getShield(culture, state) {
if (pack.cultures[culture].shield) return pack.cultures[culture].shield;
if (state) return pack.states[state].coa.shield;
return "heater";
}
// define burg coordinates, port status and define details
const specifyBurgs = function() {
TIME && console.time("specifyBurgs");
@ -189,15 +197,20 @@
}
const defineBurgFeatures = function(newburg) {
const cells = pack.cells;
pack.burgs.filter(b => newburg ? b.i == newburg.i : (b.i && !b.removed)).forEach(b => {
const pop = b.population;
b.citadel = b.capital || pop > 50 && P(.75) || P(.5) ? 1 : 0;
b.plaza = pop > 50 || pop > 30 && P(.75) || pop > 10 && P(.5) || P(.25) ? 1 : 0;
b.walls = b.capital || pop > 30 || pop > 20 && P(.75) || pop > 10 && P(.5) || P(.2) ? 1 : 0;
b.shanty = pop > 30 || pop > 20 && P(.75) || b.walls && P(.75) ? 1 : 0;
const religion = pack.cells.religion[b.cell];
const religion = cells.religion[b.cell];
const theocracy = pack.states[b.state].form === "Theocracy";
b.temple = religion && theocracy || pop > 50 || pop > 35 && P(.75) || pop > 20 && P(.5) ? 1 : 0;
const province = cells.province[b.cell];
const parentCOA = province ? pack.provinces[province].coa : pack.states[b.state].coa;
b.coa = COA.generate(parentCOA);
b.coa.shield = getShield(b.culture, b.state);
});
}
@ -933,7 +946,9 @@
form[formName] += 5;
const fullName = name + " " + formName;
const color = getMixedColor(s.color);
provinces.push({i:province, state:s.i, center, burg, name, formName, fullName, color});
const coa = COA.generate(s.coa);
coa.shield = getShield(s.culture, s.i);
provinces.push({i:province, state:s.i, center, burg, name, formName, fullName, color, coa});
}
});
@ -1026,7 +1041,8 @@
const formName = singleIsle ? "Island" : isleGroup ? "Islands" : colony ? "Colony" : rw(forms["Wild"]);
const fullName = name + " " + formName;
const color = getMixedColor(s.color);
provinces.push({i:province, state:s.i, center, burg, name, formName, fullName, color});
const coa = COA.generate(s.coa);
provinces.push({i:province, state:s.i, center, burg, name, formName, fullName, color, coa});
s.provinces.push(province);
// check if there is a land way within the same state between two cells

440
modules/coa-generator.js Normal file
View file

@ -0,0 +1,440 @@
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define(factory) :
(global.COA = factory());
}(this, (function () {'use strict';
const tinctures = {
field: { metals: 30, colours: 42, stains: 1, patterns: 12 },
division: { metals: 30, colours: 42, stains: 1, patterns: 6 },
charge: { metals: 16, colours: 24, stains: 1, patterns: 0 },
metals: { argent: 3, or: 2 },
colours: { gules: 5, azure: 4, sable: 3, purpure: 3, vert: 2 },
stains: { murrey: 1, sanguine: 1, tenné: 1 },
patterns: { semy: 1, vair: 2, vairInPale: 1, vairEnPointe: 2, ermine: 2, chequy: 5, lozengy: 2, fusily: 1, pally: 4, barry: 4, gemelles: 1, bendy: 3, bendySinister: 2, palyBendy: 1, pappellony: 2, masoned: 3, fretty: 2 }
}
const charges = {
types: { conventional: 30, crosses: 8, animals: 2, animalHeads: 1, birds: 2, aquatic: 1, seafaring: 1, fantastic: 3, plants: 1, agriculture: 1, arms: 3, bodyparts: 1, people: 1, architecture: 1, miscellaneous: 3, inescutcheon: 3, uploaded: 0 },
single: { conventional: 12, crosses: 8, plants: 2, animals: 10, animalHeads: 2, birds: 4, aquatic: 2, seafaring: 2, fantastic: 7, agriculture: 1, arms: 6, bodyparts: 1, people: 1, architecture: 1, miscellaneous: 9, inescutcheon: 5, uploaded: 0 },
semy: { conventional: 12, crosses: 3, plants: 1 },
conventional: {
lozenge: 2, fusil: 4, mascle: 4, rustre: 2, lozengeFaceted: 3, lozengePloye: 1, roundel: 4, roundel2: 3, annulet: 4,
mullet: 5, mulletPierced: 1, mulletFaceted: 1, mullet4: 3, mullet6: 4, mullet6Pierced: 1, mullet6Faceted: 1, mullet7: 1, mullet8: 1, mullet10: 1,
estoile: 1, compassRose: 1, billet: 5, delf: 0, triangle: 3, trianglePierced: 1, goutte: 4, heart: 4, pique: 2, сarreau: 1, trefle: 2,
fleurDeLis: 6, sun: 3, sunInSplendour: 1, crescent: 5, fountain: 1
},
inescutcheon: {
inescutcheonHeater: 1, inescutcheonSpanish: 1, inescutcheonFrench: 1,
inescutcheonHorsehead: 1, inescutcheonHorsehead2: 1, inescutcheonPolish: 1, inescutcheonHessen: 1, inescutcheonSwiss: 1,
inescutcheonBoeotian: 1, inescutcheonRoman: 1, inescutcheonKite: 1, inescutcheonOldFrench: 1, inescutcheonRenaissance: 1, inescutcheonBaroque: 1,
inescutcheonTarge: 1, inescutcheonTarge2: 1, inescutcheonPavise: 1, inescutcheonWedged: 1,
inescutcheonFlag: 1, inescutcheonPennon: 1, inescutcheonGuidon: 1, inescutcheonBanner: 1, inescutcheonDovetail: 1, inescutcheonGonfalon: 1, inescutcheonPennant: 1,
inescutcheonRound: 1, inescutcheonOval: 1, inescutcheonVesicaPiscis: 1, inescutcheonSquare: 1, inescutcheonDiamond: 1, inescutcheonNo: 1,
inescutcheonFantasy1: 1, inescutcheonFantasy2: 1, inescutcheonFantasy3: 1, inescutcheonFantasy4: 1, inescutcheonFantasy5: 1,
inescutcheonNoldor: 1, inescutcheonGondor: 1, inescutcheonEasterling: 1, inescutcheonErebor: 1, inescutcheonIronHills: 1, inescutcheonUrukHai: 1, inescutcheonMoriaOrc: 1
},
crosses: {
crossHummetty: 15, crossVoided: 1, crossPattee: 3, crossPotent: 2, crossClechy: 3, crosslet: 1, crossBottony: 1, crossFleury: 3,
crossPatonce: 1, crossPommy: 1, crossGamma: 1, crossArrowed: 1, crossFitchy: 1, crossCercelee: 1, crossMoline: 2, crossFourchy: 1,
crossAvellane: 1, crossErminee: 1, crossMaltese: 3, crossCeltic: 1, crossOccitan: 1, crossSaltire: 3, crossTau: 1
},
animals: {
lionRampant: 5, lionPassant: 2, wolfRampant: 1, wolfPassant: 1, wolfStatant: 1, greyhoundCourant: 1, boarRampant: 1,
horseRampant: 2, horseSalient: 1, bearRampant: 2, bearPassant: 1, bullPassant: 1, goat: 1, lamb: 1, elephant: 1
},
animalHeads: {
wolfHeadErased: 2, bullHeadCaboshed: 1, deerHeadCaboshed: 1
},
fantastic: { dragonPassant: 2, dragonRampant: 2, wyvern: 1, wyvernWithWingsDisplayed: 1, griffinPassant: 1, griffinRampant: 1, eagleTwoHeards: 2, unicornRampant: 1, pegasus: 1, serpent: 1 },
birds: { eagle: 9, raven: 2, cock: 3, parrot: 1, swan: 2, swanErased: 1, heron: 1 },
plants: { tree: 1, cinquefoil: 1, rose: 1 },
aquatic: { escallop: 5, pike: 1, cancer: 1, dolphin: 1 },
seafaring: { anchor: 6, boat: 2, lymphad: 2, armillarySphere: 1 },
agriculture: { garb: 2, rake: 1 },
arms: { sword: 5, sabre: 1, sabresCrossed: 1, hatchet: 2, lochaberAxe: 1, mallet: 1, bowWithArrow: 2, bow: 1, arrow: 1, arrowsSheaf: 1 },
bodyparts: { hand: 4, head: 1, headWreathed: 1 },
people: { cavalier: 1 },
architecture: { tower: 1, castle: 1 },
miscellaneous: {
crown: 3, orb: 1, chalice: 1, key: 1, buckle: 1, bugleHorn: 1, bell: 2, pot: 1, horseshoe: 3, stagsAttires: 1, cowHorns: 2, wing: 1, wingSword: 1,
lute: 1, harp: 1, wheel: 2, crosier: 1, log: 1},
uploaded: {},
natural: { fountain: "azure", garb: "or", raven: "sable" }, // charges to mainly use predefined colours
sinister: ["crossGamma", "lionRampant", "lionPassant", "wolfRampant", "wolfPassant", "wolfStatant", "wolfHeadErased", "greyhoundСourant", "boarRampant", "horseRampant", "horseSalient", "bullPassant",
"bearRampant", "bearPassant", "goat", "lamb", "elephant",
"eagle", "raven", "cock", "parrot", "swan", "swanErased", "heron", "pike", "dragonPassant", "dragonRampant", "wyvern", "wyvernWithWingsDisplayed", "griffinPassant", "griffinRampant", "unicornRampant",
"pegasus", "serpent", "hatchet", "lochaberAxe", "hand", "wing", "wingSword", "lute", "harp", "bow", "head", "headWreathed", "knight", "lymphad", "log",
"crosier", "dolphin", "sabre"], // charges that can be sinister
reversed: ["goutte", "mullet", "mullet7", "crescent", "crossTau", "cancer", "sword", "sabresCrossed", "hand", "horseshoe", "bowWithArrow", "arrow", "arrowsSheaf", "rake"] // charges that can be reversed
}
const positions = {
conventional: { e: 20, abcdefgzi: 3, beh: 3, behdf: 2, acegi: 1, kn: 3, bhdf: 1, jeo: 1, abc: 3, jln: 6, jlh: 3, kmo: 2, jleh: 1, def: 3, abcpqh: 4, ABCDEFGHIJKL: 1 },
complex: { e: 40, beh: 1, kn: 1, jeo: 1, abc: 2, jln: 7, jlh: 2, def: 1, abcpqh: 1 },
divisions: {
perPale: { e: 15, pq: 5, jo: 2, jl: 2, ABCDEFGHIJKL: 1 },
perFess: { e: 12, kn: 4, jkl: 2, gizgiz: 1, jlh: 3, kmo: 1, ABCDEFGHIJKL: 1 },
perBend: { e: 5, lm: 5, bcfdgh: 1 },
perBendSinister: { e: 1, jo: 1 },
perCross: { e: 4, jlmo: 1, j: 1, jo: 2, jl: 1 },
perChevron: { e: 1, jlh: 1, dfk: 1, dfbh: 2, bdefh: 1 },
perChevronReversed: { e: 1, mok: 2, dfh: 2, dfbh: 1, bdefh: 1 },
perSaltire: { bhdf: 8, e: 3, abcdefgzi: 1, bh: 1, df: 1, ABCDEFGHIJKL: 1 },
perPile: { ee: 3, be: 2, abceh: 1, abcabc: 1, jleh: 1 }
},
ordinariesOn: {
pale: { ee: 12, beh: 10, kn: 3, bb: 1 },
fess: { ee: 1, def: 3 },
bar: { defdefdef: 1 },
fessCotissed: { ee: 1, def: 3 },
fessDoubleCotissed: { ee: 1, defdef: 3 },
bend: { ee: 2, jo: 1, joe: 1 },
bendSinister: { ee: 1, lm: 1, lem: 4 },
bendlet: { joejoejoe: 1 },
bendletSinister: { lemlemlem: 1 },
bordure: { ABCDEFGHIJKL: 1 },
chief: { abc: 5, bbb: 1 },
quarter: { jjj: 1 },
canton: { yyyy: 1 },
cross: { eeee: 1, behdfbehdf: 3, behbehbeh: 2 },
crossParted: { e: 5, ee: 1 },
saltire: { ee: 5, jlemo: 1 },
saltireParted: { e: 5, ee: 1 },
pall: { ee: 1, acez: 5, jlhh: 3 },
pallReversed: { ee: 1, bemo: 5 },
pile: { bbb: 1 },
pileInBend: { eeee: 1, eeoo: 1 },
pileInBendSinister: { eeee: 1, eemm: 1 }
},
ordinariesOff: {
pale: { yyy: 1 },
fess: { abc: 3, abcz: 1 },
bar: { abc: 2, abcgzi: 1, jlh: 5, bgi: 2, ach: 1 },
gemelle: { abc: 1 },
bend: { ccg: 2, ccc: 1 },
bendSinister: { aai: 2, aaa: 1 },
bendlet: { ccg: 2, ccc: 1 },
bendletSinister: { aai: 2, aaa: 1 },
bordure: { e: 4, jleh:2, kenken: 1, peqpeq: 1 },
orle: { e: 4, jleh: 1, kenken: 1, peqpeq: 1 },
chief: { emo: 2, emoz: 1, ez: 2 },
terrace: { e: 5, def: 1, bdf: 3 },
mount: { e: 5, def: 1, bdf: 3 },
point: { e: 2, def: 1, bdf: 3, acbdef: 1 },
flaunches: { e: 3, kn: 1, beh: 3 },
gyron: { bh: 1 },
quarter: { e: 1 },
canton: { e: 5, beh: 1, def: 1, bdefh: 1, kn: 1 },
cross: { acgi: 1 },
pall: { BCKFEILGbdmfo: 1 },
pallReversed: { aczac: 1 },
chevron: { ach: 3, hhh: 1 },
chevronReversed: { bbb: 1 },
pile: { acdfgi: 1, acac: 1 },
pileInBend: { cg: 1 },
pileInBendSinister: { ai: 1 },
label: { defgzi: 2, eh: 3, defdefhmo: 1, egiegi: 1, pqn: 5 }
},
// charges
inescutcheon: { e: 4, jln: 1 },
mascle: { e: 15, abcdefgzi: 3, beh: 3, bdefh: 4, acegi: 1, kn: 3, joe: 2, abc: 3, jlh: 8, jleh: 1, df: 3, abcpqh: 4, pqe: 3, eknpq: 3 },
lionRampant: { e: 10, def: 2, abc: 2, bdefh: 1, kn: 1, jlh: 2, abcpqh: 1 },
lionPassant: { e: 10, def: 1, abc: 1, bdefh: 1, jlh: 1, abcpqh: 1 },
wolfPassant: { e: 10, def: 1, abc: 1, bdefh: 1, jlh: 1, abcpqh: 1 },
greyhoundСourant: { e: 10, def: 1, abc: 1, bdefh: 1, jlh: 1, abcpqh: 1 },
griffinRampant: { e: 10, def: 2, abc: 2, bdefh: 1, kn: 1, jlh: 2, abcpqh: 1 },
griffinPassant: { e: 10, def: 1, abc: 1, bdefh: 1, jlh: 1, abcpqh: 1 },
boarRampant: { e: 12, beh: 1, kn: 1, jln: 2 },
eagle: { e: 15, beh: 1, kn: 1, abc: 1, jlh: 2, def: 2, pq: 1 },
raven: { e: 15, beh: 1, kn: 1, jeo: 1, abc: 3, jln: 3, def: 1 },
wyvern: { e: 10, jln: 1 },
garb: { e: 1, def: 3, abc: 2, beh: 1, kn: 1, jln: 3, jleh: 1, abcpqh: 1, joe: 1, lme: 1 },
crown: { e: 10, abcdefgzi: 1, beh: 3, behdf: 2, acegi: 1, kn: 1, pq: 2, abc: 1, jln: 4, jleh: 1, def: 2, abcpqh: 3 },
hand: { e: 10, jln: 2, kn: 1, jeo: 1, abc: 2, pqe: 1 },
armillarySphere: {e: 1},
tree: {e: 1},
lymphad: {e: 1},
head: {e: 1},
headWreathed: {e: 1}
};
const lines = {
straight: 50, wavy: 8, engrailed: 4, invecked: 3, rayonne: 3, embattled: 1, raguly: 1, urdy: 1, dancetty: 1, indented: 2,
dentilly: 1, bevilled: 1, angled: 1, flechy: 1, barby: 1, enclavy: 1, escartely: 1, arched: 2, archedReversed: 1, nowy: 1, nowyReversed: 1,
embattledGhibellin: 1, embattledNotched: 1, embattledGrady: 1, dovetailedIndented: 1, dovetailed: 1,
potenty: 1, potentyDexter: 1, potentySinister: 1, nebuly: 2, seaWaves: 1, dragonTeeth: 1, firTrees: 1
};
const divisions = {
variants: { perPale: 5, perFess: 5, perBend: 2, perBendSinister: 1, perChevron: 1, perChevronReversed: 1, perCross: 5, perPile: 1, perSaltire: 1, gyronny: 1, chevronny: 1 },
perPale: lines,
perFess: lines,
perBend: lines,
perBendSinister: lines,
perChevron: lines,
perChevronReversed: lines,
perCross: { straight: 20, wavy: 5, engrailed: 4, invecked: 3, rayonne: 1, embattled: 1, raguly: 1, urdy: 1, indented: 2, dentilly: 1, bevilled: 1, angled: 1, embattledGhibellin: 1, embattledGrady: 1, dovetailedIndented: 1, dovetailed: 1, potenty: 1, potentyDexter: 1, potentySinister: 1, nebuly: 1 },
perPile: lines
};
const ordinaries = {
lined: {
pale: 7, fess: 5, bend: 3, bendSinister: 2, chief: 5, bar: 2, gemelle: 1, fessCotissed: 1, fessDoubleCotissed: 1,
bendlet: 2, bendletSinister: 1, terrace: 3, cross: 6, crossParted: 1, saltire: 2, saltireParted: 1
},
straight: {
bordure: 8, orle: 4, mount: 1, point: 2, flaunches: 1, gore: 1,
gyron: 1, quarter: 1, canton: 2, pall: 3, pallReversed: 2, chevron: 4, chevronReversed: 3,
pile: 2, pileInBend: 2, pileInBendSinister: 1, piles: 1, pilesInPoint: 2, label: 1
}
};
const generate = function(parent) {
TIME && console.time("generateCOA");
let usedPattern = null, usedTinctures = [];
const t1 = parent && P(.25) ? parent.t1 : getTincture("field");
const coa = {t1};
let charge = P(usedPattern ? .5 : .93) ? true : false; // 80% for charge
const linedOrdinary = charge && P(.3) || P(.5) ? parent?.ordinaries && P(.2) ? parent.ordinaries[0].ordinary : rw(ordinaries.lined) : null;
const ordinary = !charge && P(.65) || P(.3) ? linedOrdinary ? linedOrdinary : rw(ordinaries.straight) : null; // 36% for ordinary
const rareDivided = ["chief", "terrace", "chevron", "quarter", "flaunches"].includes(ordinary);
const divisioned = rareDivided ? P(.03) : charge && ordinary ? P(.03) : charge ? P(.3) : ordinary ? P(.7) : P(.995); // 33% for division
const division = divisioned ? parent?.division && P(.2) ? parent.division.division : rw(divisions.variants) : null;
if (charge) charge = parent?.charges && P(.3) ? parent.charges[0].charge : selectCharge();
if (division) {
const t = getTincture("division", usedTinctures, P(.98) ? coa.t1 : null);
coa.division = {division, t};
if (divisions[division]) coa.division.line = usedPattern || (ordinary && P(.7)) ? "straight" : rw(divisions[division]);
}
if (ordinary) {
coa.ordinaries = [{ordinary, t: getTincture("charge", usedTinctures, coa.t1)}];
if (linedOrdinary) coa.ordinaries[0].line = usedPattern || (division && P(.7)) ? "straight" : rw(lines);
if (division && !charge && !usedPattern && P(.5) && ordinary !== "bordure" && ordinary !== "orle") {
if (P(.8)) coa.ordinaries[0].divided = "counter"; // 40%
else if (P(.6)) coa.ordinaries[0].divided = "field"; // 6%
else coa.ordinaries[0].divided = "division"; // 4%
}
}
if (charge) {
let p = "e", t = "gules";
const ordinaryT = coa.ordinaries ? coa.ordinaries[0].t : null;
if (positions.ordinariesOn[ordinary] && P(.8)) {
// place charge over ordinary (use tincture of field type)
p = rw(positions.ordinariesOn[ordinary]);
while (charges.natural[charge] === ordinaryT) charge = selectCharge();
t = !usedPattern && P(.3) ? coa.t1 : getTincture("charge", [], ordinaryT);
} else if (positions.ordinariesOff[ordinary] && P(.95)) {
// place charge out of ordinary (use tincture of ordinary type)
p = rw(positions.ordinariesOff[ordinary]);
while (charges.natural[charge] === coa.t1) charge = selectCharge();
t = !usedPattern && P(.3) ? ordinaryT : getTincture("charge", usedTinctures, coa.t1);
} else if (positions.divisions[division]) {
// place charge in fields made by division
p = rw(positions.divisions[division]);
while (charges.natural[charge] === coa.t1) charge = selectCharge();
t = getTincture("charge", ordinaryT ? usedTinctures.concat(ordinaryT) : usedTinctures, coa.t1);
} else if (positions[charge]) {
// place charge-suitable position
p = rw(positions[charge]);
while (charges.natural[charge] === coa.t1) charge = selectCharge();
t = getTincture("charge", usedTinctures, coa.t1);
} else {
// place in standard position (use new tincture)
p = usedPattern ? "e" : charges.conventional[charge] ? rw(positions.conventional) : rw(positions.complex);
while (charges.natural[charge] === coa.t1) charge = selectCharge();
t = getTincture("charge", usedTinctures.concat(ordinaryT), coa.t1);
}
if (charges.natural[charge]) t = charges.natural[charge]; // natural tincture
coa.charges = [{charge, t, p}];
if (p === "ABCDEFGHIKL" && P(.95)) {
// add central charge if charge is in bordure
coa.charges[0].charge = rw(charges.conventional);
const charge = selectCharge(charges.single);
const t = getTincture("charge", usedTinctures, coa.t1);
coa.charges.push({charge, t, p: "e"});
} else if (P(.8) && charge === "inescutcheon") {
// add charge to inescutcheon
const charge = selectCharge(charges.types);
const t2 = getTincture("charge", [], t);
coa.charges.push({charge, t: t2, p, size:.5});
} else if (division && !ordinary) {
const allowCounter = !usedPattern && (!coa.line || coa.line === "straight");
// dimidiation: second charge at division basic positons
if (P(.3) && ["perPale", "perFess"].includes(division) && coa.line === "straight") {
coa.charges[0].divided = "field";
if (P(.95)) {
const p2 = p === "e" || P(.5) ? "e" : rw(positions.divisions[division]);
const charge = selectCharge(charges.single);
const t = getTincture("charge", usedTinctures, coa.division.t);
coa.charges.push({charge, t, p: p2, divided: "division"});
}
}
else if (allowCounter && P(.4)) coa.charges[0].divided = "counter"; // counterchanged, 40%
else if (["perPale", "perFess", "perBend", "perBendSinister"].includes(division) && P(.8)) { // place 2 charges in division standard positions
const [p1, p2] = division === "perPale" ? ["p", "q"] :
division === "perFess" ? ["k", "n"] :
division === "perBend" ? ["l", "m"] :
["j", "o"]; // perBendSinister
coa.charges[0].p = p1;
const charge = selectCharge(charges.single);
const t = getTincture("charge", usedTinctures, coa.division.t);
coa.charges.push({charge, t, p: p2});
}
else if (["perCross", "perSaltire"].includes(division) && P(.5)) { // place 4 charges in division standard positions
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 c3 = selectCharge(charges.single);
const t3 = getTincture("charge", [], coa.division.t);
const c4 = selectCharge(charges.single);
const t4 = getTincture("charge", [], coa.t1);
coa.charges.push({charge: c2, t: t2, p: p2}, {charge: c3, t: t3, p: p3}, {charge: c4, t: t4, p: p4});
}
else if (allowCounter && p.length > 1) coa.charges[0].divided = "counter"; // counterchanged, 40%
}
coa.charges.forEach(c => defineChargeAttributes(c));
function defineChargeAttributes(c) {
// define size
c.size = (c.size || 1) * getSize(c.p, ordinary, division);
// clean-up position
c.p = [...new Set(c.p)].join("");
// define orientation
if (P(.02) && charges.sinister.includes(c.charge)) c.sinister = 1;
if (P(.02) && charges.reversed.includes(c.charge)) c.reversed = 1;
}
}
function selectCharge(set) {
const type = set ? rw(set) : ordinary || divisioned ? rw(charges.types): rw(charges.single);
return type === "inescutcheon" ? "inescutcheon" : rw(charges[type]);
}
// select tincture: element type (field, division, charge), used field tinctures, field type to follow RoT
function getTincture(element, fields = [], RoT) {
const base = RoT ? RoT.includes("-") ? RoT.split("-")[1] : RoT : null;
let type = rw(tinctures[element]); // metals, colours, stains, patterns
if (RoT && type !== "patterns") type = getType(base) === "metals" ? "colours" : "metals"; // follow RoT
if (type === "metals" && fields.includes("or") && fields.includes("argent")) type = "colours"; // exclude metals overuse
let tincture = rw(tinctures[type]);
while (tincture === base || fields.includes(tincture)) {tincture = rw(tinctures[type]);} // follow RoT
if (type !== "patterns" && element !== "charge") usedTinctures.push(tincture); // add field tincture
if (type === "patterns") {
usedPattern = tincture;
tincture = definePattern(tincture, element);
}
return tincture;
}
function getType(t) {
const tincture = t.includes("-") ? t.split("-")[1] : t;
if (Object.keys(tinctures.metals).includes(tincture)) return "metals";
if (Object.keys(tinctures.colours).includes(tincture)) return "colours";
if (Object.keys(tinctures.stains).includes(tincture)) return "stains";
debugger; // exception
}
function definePattern(pattern, element, size = "") {
let t1 = null, t2 = null;
if (P(.15)) size = "-small";
else if (P(.05)) size = "-smaller";
else if (P(.035)) size = "-big";
else if (P(.001)) size = "-smallest";
// apply standard tinctures
if (P(.5) && ["vair", "vairInPale", "vairEnPointe"].includes(pattern)) {t1 = "azure"; t2 = "argent";}
else if (P(.8) && pattern === "ermine") {t1 = "argent"; t2 = "sable";}
else if (pattern === "pappellony") {
if (P(.2)) {t1 = "gules"; t2 = "or";}
else if (P(.2)) {t1 = "argent"; t2 = "sable";}
else if (P(.2)) {t1 = "azure"; t2 = "argent";}
}
else if (pattern === "masoned") {
if (P(.3)) {t1 = "gules"; t2 = "argent";}
else if (P(.3)) {t1 = "argent"; t2 = "sable";}
else if (P(.1)) {t1 = "or"; t2 = "sable";}
}
else if (pattern === "fretty") {
if (t2 === "sable" || P(.35)) {t1 = "argent"; t2 = "gules";}
else if (P(.25)) {t1 = "sable"; t2 = "or";}
else if (P(.15)) {t1 = "gules"; t2 = "argent";}
}
else if (pattern === "semy") pattern += "_of_" + selectCharge(charges.semy);
if (!t1 || !t2) {
const startWithMetal = P(.7);
t1 = startWithMetal ? rw(tinctures.metals) : rw(tinctures.colours);
t2 = startWithMetal ? rw(tinctures.colours) : rw(tinctures.metals);
}
// division should not be the same tincture as base field
if (element === "division") {
if (usedTinctures.includes(t1)) t1 = replaceTincture(t1);
if (usedTinctures.includes(t2)) t2 = replaceTincture(t2);
}
usedTinctures.push(t1, t2);
return `${pattern}-${t1}-${t2}${size}`;
}
function replaceTincture(t, n) {
const type = getType(t);
while (!n || n === t) {n = rw(tinctures[type]);}
return n;
}
function getSize(p, o = null, d = null) {
if (p === "e" && (o === "bordure" || o === "orle")) return 1.1;
if (p === "e") return 1.5;
if (p === "jln" || p === "jlh") return .7;
if (p === "abcpqh" || p === "ez" || p === "be") return .5;
if (["a", "b", "c", "d", "f", "g", "h", "i", "bh", "df"].includes(p)) return .5;
if (["j", "l", "m", "o", "jlmo"].includes(p) && d === "perCross") return .6;
if (p.length > 10) return .18; // >10 (bordure)
if (p.length > 7) return .3; // 8, 9, 10
if (p.length > 4) return .4; // 5, 6, 7
if (p.length > 2) return .5; // 3, 4
return .7; // 1, 2
}
TIME && console.timeEnd("generateCOA");
return coa;
}
const generateAll = function() {
const states = pack.states, burgs = pack.burgs, provinces = pack.provinces;
states.forEach(state => {
if (!state.i || state.removed) return;
const coa = generate();
s.coa = coa;
});
}
const toString = coa => JSON.stringify(coa).replaceAll("#", "%23");
return {generate, generateAll, toString};
})));

View file

@ -17,7 +17,7 @@
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}];
pack.cultures = [{name:"Wildlands", i:0, base:1, shield:"moriaOrc"}];
alertMessage.innerHTML = `
The climate is harsh and people cannot live in this world.<br>
No cultures, states and burgs will be created.<br>
@ -152,21 +152,21 @@
if (culturesSet.value === "european") {
return [
{name:"Shwazen", base:0, odd: 1, sort: i => n(i) / td(i, 10) / bd(i, [6, 8])},
{name:"Angshire", base:1, odd: 1, sort: i => n(i) / td(i, 10) / sf(i)},
{name:"Luari", base:2, odd: 1, sort: i => n(i) / td(i, 12) / bd(i, [6, 8])},
{name:"Tallian", base:3, odd: 1, sort: i => n(i) / td(i, 15)},
{name:"Astellian", base:4, odd: 1, sort: i => n(i) / td(i, 16)},
{name:"Slovan", base:5, odd: 1, sort: i => n(i) / td(i, 6) * t[i]},
{name:"Norse", base:6, odd: 1, sort: i => n(i) / td(i, 5)},
{name:"Elladan", base:7, odd: 1, sort: i => n(i) / td(i, 18) * h[i]},
{name:"Romian", base:8, odd: .2, sort: i => n(i) / td(i, 15) / t[i]},
{name:"Soumi", base:9, odd: 1, sort: i => n(i) / td(i, 5) / bd(i, [9]) * t[i]},
{name:"Portuzian", base:13, odd: 1, sort: i => n(i) / td(i, 17) / sf(i)},
{name:"Vengrian", base: 15, odd: 1, sort: i => n(i) / td(i, 11) / bd(i, [4]) * t[i]},
{name:"Turchian", base: 16, odd: .05, sort: i => n(i) / td(i, 14)},
{name:"Euskati", base: 20, odd: .05, sort: i => n(i) / td(i, 15) * h[i]},
{name:"Keltan", base: 22, odd: .05, sort: i => n(i) / td(i, 11) / bd(i, [6, 8]) * t[i]}
{name:"Shwazen", base:0, odd: 1, sort: i => n(i) / td(i, 10) / bd(i, [6, 8]), shield:ra(["hessen", "swiss"])},
{name:"Angshire", base:1, odd: 1, sort: i => n(i) / td(i, 10) / sf(i), shield:ra(["heater", "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:ra(["horsehead", "horsehead2", "oval"])},
{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:"kite"},
{name:"Elladan", base:7, odd: 1, sort: i => n(i) / td(i, 18) * h[i], shield:"boeotian"},
{name:"Romian", base:8, odd: .2, sort: i => n(i) / td(i, 15) / t[i], shield:"roman"},
{name:"Soumi", base:9, odd: 1, sort: i => n(i) / td(i, 5) / bd(i, [9]) * t[i], shield:"pavise"},
{name:"Portuzian", base:13, odd: 1, sort: i => n(i) / td(i, 17) / sf(i), shield:"renaissance"},
{name:"Vengrian", base: 15, odd: 1, sort: i => n(i) / td(i, 11) / bd(i, [4]) * t[i], shield:"targe"},
{name:"Turchian", base: 16, odd: .05, sort: i => n(i) / td(i, 14), shield:"round"},
{name:"Euskati", base: 20, odd: .05, sort: i => n(i) / td(i, 15) * h[i], shield:"oldFrench"},
{name:"Keltan", base: 22, odd: .05, sort: i => n(i) / td(i, 11) / bd(i, [6, 8]) * t[i], shield:"vesicaPiscis"}
];
}

View file

@ -43,7 +43,7 @@ function editBurg(id) {
document.getElementById("burgEditAnchorStyle").addEventListener("click", editGroupAnchorStyle);
document.getElementById("burgSeeInMFCG").addEventListener("click", openInMFCG);
document.getElementById("burgOpenCOA").addEventListener("click", openInIAHG);
document.getElementById("burgOpenCOA").addEventListener("click", editCOA);
document.getElementById("burgRelocate").addEventListener("click", toggleRelocateBurg);
document.getElementById("burglLegend").addEventListener("click", editBurgLegend);
document.getElementById("burgRemove").addEventListener("click", removeSelectedBurg);
@ -326,17 +326,10 @@ function editBurg(id) {
}
}
function openInIAHG(event) {
const id = elSelected.attr("data-id"), burg = pack.burgs[id], defSeed = `${seed}-b${id}`;
const openIAHG = () => openURL("https://ironarachne.com/#/heraldry/" + (burg.IAHG || defSeed));
if (isCtrlClick(event)) {
prompt(`Please provide an Iron Arachne Heraldry Generator seed. <br>Default seed is a combination of FMG map seed and burg id (${defSeed})`,
{default:burg.IAHG || defSeed}, v => {
if (v && v != defSeed) burg.IAHG = v;
openIAHG();
});
} else openIAHG();
function editCOA() {
const id = elSelected.attr("data-id"), burg = pack.burgs[id];
const coa = COA.toString(burg.coa);
openURL("http://azgaar.github.io/Armoria/?coa=" + coa);
}
function toggleRelocateBurg() {

View file

@ -38,7 +38,7 @@ function editProvinces() {
const el = ev.target, cl = el.classList, line = el.parentNode, p = +line.dataset.id;
if (cl.contains("fillRect")) changeFill(el); else
if (cl.contains("name")) editProvinceName(p); else
if (cl.contains("icon-coa")) provinceOpenCOA(ev, p); else
if (cl.contains("icon-coa")) editCOA(p); else
if (cl.contains("icon-star-empty")) capitalZoomIn(p); else
if (cl.contains("icon-flag-empty")) triggerIndependencePromps(p); else
if (cl.contains("culturePopulation")) changePopulation(p); else
@ -117,7 +117,7 @@ function editProvinces() {
lines += `<div class="states" data-id=${p.i} data-name="${p.name}" data-form="${p.formName}" data-color="${p.color}" data-capital="${capital}" data-state="${stateName}" data-area=${area} data-population=${population}>
<svg data-tip="Province fill style. Click to change" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${p.color}" class="fillRect pointer"></svg>
<input data-tip="Province name. Click to change" class="name pointer" value="${p.name}" readonly>
<span data-tip="Click to open province COA in the Iron Arachne Heraldry Generator. Ctrl + click to change the seed" class="icon-coa pointer hide"></span>
<span data-tip="Click to edit province COA" class="icon-coa pointer hide"></span>
<input data-tip="Province form name. Click to change" class="name pointer hide" value="${p.formName}" readonly>
<span data-tip="Province capital. Click to zoom into view" class="icon-star-empty pointer hide ${p.burg?'':'placeholder'}"></span>
<select data-tip="Province capital. Click to select from burgs within the state. No capital means the province is governed from the state capital" class="cultureBase hide ${p.burgs.length?'':'placeholder'}">${p.burgs.length ? getCapitalOptions(p.burgs, p.burg) : ''}</select>
@ -192,17 +192,9 @@ function editProvinces() {
openPicker(currentFill, callback);
}
function provinceOpenCOA(event, p) {
const defSeed = `${seed}-p${p}`;
const openIAHG = () => openURL("https://ironarachne.com/#/heraldry/" + (pack.provinces[p].IAHG || defSeed));
if (isCtrlClick(event)) {
prompt(`Please provide an Iron Arachne Heraldry Generator seed. <br>Default seed is a combination of FMG map seed and province id (${defSeed})`,
{default:pack.provinces[p].IAHG || defSeed}, v => {
if (v && v != defSeed) pack.provinces[p].IAHG = v;
openIAHG();
});
} else openIAHG();
function editCOA(p) {
const coa = COA.toString(pack.provinces[p].coa);
openURL("http://azgaar.github.io/Armoria/?coa=" + coa);
}
function capitalZoomIn(p) {

View file

@ -42,7 +42,7 @@ function editStates() {
const el = ev.target, cl = el.classList, line = el.parentNode, state = +line.dataset.id;
if (cl.contains("fillRect")) stateChangeFill(el); else
if (cl.contains("name")) editStateName(state); else
if (cl.contains("icon-coa")) stateOpenCOA(ev, state); else
if (cl.contains("emblemIcon")) editCOA(state); else
if (cl.contains("icon-star-empty")) stateCapitalZoomIn(state); else
if (cl.contains("culturePopulation")) changePopulation(state); else
if (cl.contains("icon-pin")) toggleFog(state, cl); else
@ -83,6 +83,7 @@ function editStates() {
totalPopulation += population;
totalBurgs += s.burgs;
const focused = defs.select("#fog #focusState"+s.i).size();
const COAsize = Math.round(15 * +uiSizeInput.value);
if (!s.i) {
// Neutral line
@ -90,7 +91,7 @@ function editStates() {
data-population=${population} data-burgs=${s.burgs} data-color="" data-form="" data-capital="" data-culture="" data-type="" data-expansionism="">
<svg width="9" height="9" class="placeholder"></svg>
<input data-tip="Neutral lands name. Click to change" class="stateName name pointer italic" value="${s.name}" readonly>
<span class="icon-coa placeholder hide"></span>
<embed class="emblemIcon placeholder hide" type="image/svg+xml" width=${COAsize} height=${COAsize} >
<input class="stateForm placeholder" value="none">
<span class="icon-star-empty placeholder hide"></span>
<input class="stateCapital placeholder hide">
@ -110,11 +111,12 @@ function editStates() {
continue;
}
const capital = pack.burgs[s.capital].name;
const coaURL = `http://azgaar.github.io/Armoria/?view=1&size=${COAsize}&noedit&coa=${COA.toString(s.coa)}`;
lines += `<div class="states" data-id=${s.i} data-name="${s.name}" data-form="${s.formName}" data-capital="${capital}" data-color="${s.color}" data-cells=${s.cells}
data-area=${area} data-population=${population} data-burgs=${s.burgs} data-culture=${pack.cultures[s.culture].name} data-type=${s.type} data-expansionism=${s.expansionism}>
<svg data-tip="State fill style. Click to change" width=".9em" height=".9em" style="margin-bottom:-1px"><rect x="0" y="0" width="100%" height="100%" fill="${s.color}" class="fillRect pointer"></svg>
<input data-tip="State name. Click to change" class="stateName name pointer" value="${s.name}" readonly>
<span data-tip="Click to open state COA in the Iron Arachne Heraldry Generator. Ctrl + click to change the seed" class="icon-coa pointer hide"></span>
<embed data-tip="Click to show state COA" class="emblemIcon hide" type="image/svg+xml" width=${COAsize} height=${COAsize} src='${coaURL}'>
<input data-tip="State form name. Click to change" class="stateForm name pointer" value="${s.formName}" readonly>
<span data-tip="State capital. Click to zoom into view" class="icon-star-empty pointer hide"></span>
<input data-tip="Capital name. Click and type to rename" class="stateCapital hide" value="${capital}" autocorrect="off" spellcheck="false"/>
@ -313,17 +315,17 @@ function editStates() {
document.querySelector("#burgLabel"+capital).textContent = value;
}
function stateOpenCOA(event, state) {
const defSeed = `${seed}-s${state}`;
const openIAHG = () => openURL("https://ironarachne.com/#/heraldry/" + (pack.states[state].IAHG || defSeed));
function editCOA(state) {
const coa = COA.toString(pack.states[state].coa);
const url = "http://azgaar.github.io/Armoria/?view=1&size=200&noedit&coa=" + coa;
if (isCtrlClick(event)) {
prompt(`Please provide an Iron Arachne Heraldry Generator seed. <br>Default seed is a combination of FMG map seed and state id (${defSeed})`,
{default:pack.states[state].IAHG || defSeed}, v => {
if (v && v != defSeed) pack.states[state].IAHG = v;
openIAHG();
});
} else openIAHG();
$("#emblemsEditor").dialog({
title: "Emblems Editor", resizable: false, width: fitContent(), close: closeStatesEditor,
position: {my: "left top", at: "left+10 top+10", of: "svg", collision: "fit"}
});
const embed = document.getElementById("emblemsEmbed");
embed.setAttribute("src", url);
}
function changePopulation(state) {