diff --git a/src/dialogs/dialogs/emblem-editor.js b/src/dialogs/dialogs/emblem-editor.js index 83bcd069..69650933 100644 --- a/src/dialogs/dialogs/emblem-editor.js +++ b/src/dialogs/dialogs/emblem-editor.js @@ -250,7 +250,11 @@ export function open({type, id, el}) { parent = province ? pack.provinces[province] : pack.states[el.state]; } - const shield = el.coa.shield || COA.getShield(el.culture || parent?.culture || 0, el.state); + const cultureId = el.culture || parent?.culture || 0; + const cultureShield = cultures[cultureId].shield; + const stateShield = states[el.state]?.coa?.shield; + + const shield = el.coa.shield || COA.getPackShield(cultureShield, stateShield); el.coa = COA.generate(parent ? parent.coa : null, 0.3, 0.1, null); el.coa.shield = shield; emblemShapeSelector.disabled = false; diff --git a/src/dialogs/dialogs/states-editor.js b/src/dialogs/dialogs/states-editor.js index 144edf6a..a4f96fd5 100644 --- a/src/dialogs/dialogs/states-editor.js +++ b/src/dialogs/dialogs/states-editor.js @@ -1168,7 +1168,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); - coa.shield = COA.getShield(culture, stateId); + coa.shield = COA.getPackShield(culture, stateId); provinces.push({ i: newProvinceId, @@ -1270,7 +1270,7 @@ function addState() { // generate emblem const cultureType = pack.cultures[culture].type; const coa = COA.generate(burgs[burg].coa, 0.4, null, cultureType); - coa.shield = COA.getShield(culture, null); + coa.shield = COA.getPackShield(culture, null); // update diplomacy and reverse relations const diplomacy = states.map(s => { diff --git a/src/modules/burgs-and-states.js b/src/modules/burgs-and-states.js index 90e97dc3..5d6eb086 100644 --- a/src/modules/burgs-and-states.js +++ b/src/modules/burgs-and-states.js @@ -112,7 +112,7 @@ window.BurgsAndStates = (function () { const type = cultures[b.culture].type; const coa = COA.generate(null, null, null, type); - coa.shield = COA.getShield(b.culture, null); + coa.shield = COA.getPackShield(b.culture, null); states.push({ i, color: colors[i - 1], @@ -230,7 +230,7 @@ window.BurgsAndStates = (function () { 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.shield = COA.getShield(b.culture, b.state); + b.coa.shield = COA.getPackShield(b.culture, b.state); } // de-assign port status if it's the only one on feature @@ -1213,7 +1213,7 @@ window.BurgsAndStates = (function () { const kinship = nameByBurg ? 0.8 : 0.4; const type = getType(center, burg.port); const coa = COA.generate(stateBurgs[i].coa, kinship, null, type); - coa.shield = COA.getShield(cultureId, s.i); + coa.shield = COA.getPackShield(cultureId, s.i); provinces.push({i: province, state: s.i, center, burg, name, formName, fullName, color, coa}); } }); @@ -1346,7 +1346,7 @@ window.BurgsAndStates = (function () { const kinship = dominion ? 0 : 0.4; const type = getType(center, burgs[burg]?.port); const coa = COA.generate(s.coa, kinship, dominion, type); - coa.shield = COA.getShield(cultureId, s.i); + coa.shield = COA.getPackShield(cultureId, s.i); provinces.push({i: province, state: s.i, center, burg, name, formName, fullName, color, coa}); s.provinces.push(province); diff --git a/src/modules/coa-generator.js b/src/modules/coa-generator.js index 8c83f689..5029dc58 100644 --- a/src/modules/coa-generator.js +++ b/src/modules/coa-generator.js @@ -1,5 +1,5 @@ -import {pack} from "d3"; import {P, rw} from "utils/probabilityUtils"; +import {ERROR} from "config/logging"; window.COA = (function () { const tinctures = { @@ -1039,17 +1039,25 @@ window.COA = (function () { return coa; }; - const getShield = function (cultureId, stateId, cultures = pack.cultures, states = pack.states) { + const getShield = function (cultureShield, stateShield) { const emblemShape = document.getElementById("emblemShape"); const shapeGroup = emblemShape.selectedOptions[0]?.parentNode.label || "Diversiform"; + if (shapeGroup !== "Diversiform") return emblemShape.value; - if (emblemShape.value === "state" && stateId && states[stateId].coa) return states[stateId].coa.shield; - if (cultures[cultureId].shield) return cultures[cultureId].shield; - ERROR && console.error("Shield shape is not defined on culture level", cultures[cultureId]); + if (emblemShape.value === "state" && stateShield) return stateShield; + + if (cultureShield) cultureShield; + ERROR && console.error("Shield shape is not defined on culture level"); return "heater"; }; + const getPackShield = function (cultureId, stateId) { + const cultureShield = pack.cultres[cultureId]?.shield; + const stateShield = pack.states[stateId]?.coa?.shield; + return getShield(cultureShield, stateShield); + }; + const getRandomShield = function () { const type = rw(shields.types); return rw(shields[type]); @@ -1058,5 +1066,5 @@ window.COA = (function () { const toString = coa => JSON.stringify(coa).replaceAll("#", "%23"); const copy = coa => JSON.parse(JSON.stringify(coa)); - return {generate, toString, copy, getShield, shields, getRandomShield}; + return {generate, toString, copy, getShield, getPackShield, shields, getRandomShield}; })(); diff --git a/src/modules/ui/editors.js b/src/modules/ui/editors.js index 41503e5c..7435974a 100644 --- a/src/modules/ui/editors.js +++ b/src/modules/ui/editors.js @@ -104,7 +104,7 @@ function addBurg(point) { // generate emblem const coa = COA.generate(pack.states[state].coa, 0.25, null, type); - coa.shield = COA.getShield(culture, state); + coa.shield = COA.getPackShield(culture, state); COArenderer.add("burg", i, coa, x, y); pack.burgs.push({name, cell, x, y, state, i, culture, feature, capital: 0, port: 0, temple, population, coa, type}); diff --git a/src/modules/ui/options.js b/src/modules/ui/options.js index d5abc502..5c864778 100644 --- a/src/modules/ui/options.js +++ b/src/modules/ui/options.js @@ -389,7 +389,8 @@ function changeEmblemShape(emblemShape) { pack.states.forEach(state => { if (!state.i || state.removed || !state.coa || state.coa === "custom") return; - const newShield = specificShape || COA.getShield(state.culture, null); + + const newShield = specificShape || COA.getPackShield(state.culture, null); if (newShield === state.coa.shield) return; state.coa.shield = newShield; rerenderCOA("stateCOA" + state.i, state.coa); @@ -398,7 +399,7 @@ function changeEmblemShape(emblemShape) { pack.provinces.forEach(province => { if (!province.i || province.removed || !province.coa || province.coa === "custom") return; const culture = pack.cells.culture[province.center]; - const newShield = specificShape || COA.getShield(culture, province.state); + const newShield = specificShape || COA.getPackShield(culture, province.state); if (newShield === province.coa.shield) return; province.coa.shield = newShield; rerenderCOA("provinceCOA" + province.i, province.coa); @@ -406,7 +407,7 @@ function changeEmblemShape(emblemShape) { pack.burgs.forEach(burg => { if (!burg.i || burg.removed || !burg.coa || burg.coa === "custom") return; - const newShield = specificShape || COA.getShield(burg.culture, burg.state); + const newShield = specificShape || COA.getPackShield(burg.culture, burg.state); if (newShield === burg.coa.shield) return; burg.coa.shield = newShield; rerenderCOA("burgCOA" + burg.i, burg.coa); diff --git a/src/modules/ui/provinces-editor.js b/src/modules/ui/provinces-editor.js index f1097256..6ae99cc2 100644 --- a/src/modules/ui/provinces-editor.js +++ b/src/modules/ui/provinces-editor.js @@ -1049,7 +1049,11 @@ export function editProvinces() { 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); - coa.shield = COA.getShield(cultureId, state); + + const cultureShield = cultures[cultureId].shield; + const stateShield = states[state]?.coa?.shield; + coa.shield = COA.getShield(cultureShield, stateShield); + COArenderer.add("province", province, coa, point[0], point[1]); provinces.push({i: province, state, center, burg, name, formName, fullName, color, coa}); diff --git a/src/modules/ui/tools.js b/src/modules/ui/tools.js index ac833fcb..71d34719 100644 --- a/src/modules/ui/tools.js +++ b/src/modules/ui/tools.js @@ -375,7 +375,9 @@ function regenerateEmblems() { if (!state.i || state.removed) return; const cultureType = pack.cultures[state.culture].type; state.coa = COA.generate(null, null, null, cultureType); - state.coa.shield = COA.getShield(state.culture, null); + + const cultureShield = cultures[state.culture].shield; + state.coa.shield = COA.getShield(cultureShield, null); }); pack.burgs.forEach(burg => { @@ -387,7 +389,10 @@ function regenerateEmblems() { 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.shield = COA.getShield(burg.culture, state ? burg.state : 0); + + const cultureShield = cultures[burg.culture].shield; + const stateShield = states[burg.state]?.coa?.shield; + burg.coa.shield = COA.getShield(cultureShield, stateShield); }); pack.provinces.forEach(province => { @@ -409,7 +414,10 @@ function regenerateEmblems() { 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.shield = COA.getShield(culture, province.state); + + const cultureShield = cultures[culture].shield; + const stateShield = states[province.state]?.coa?.shield; + province.coa.shield = COA.getShield(cultureShield, stateShield); }); if (layerIsOn("toggleEmblems")) drawEmblems(); // redrawEmblems diff --git a/src/scripts/generation/pack/burgsAndStates/createStates.ts b/src/scripts/generation/pack/burgsAndStates/createStates.ts index 5a78aac7..6455fe34 100644 --- a/src/scripts/generation/pack/burgsAndStates/createStates.ts +++ b/src/scripts/generation/pack/burgsAndStates/createStates.ts @@ -21,11 +21,12 @@ export function createStates(capitals: TCapitals, cultures: TCultures) { const id = index + 1; const name = getStateName(cellId, capitalName, cultureId, cultures); const color = colors[index]; - const type = (cultures[cultureId] as ICulture).type; + + const {type, shield: cultureShield} = cultures[cultureId] as ICulture; const expansionism = rn(Math.random() * powerInput + 1, 1); - const shield = COA.getShield(cultureId, null, cultures); - const coa = {...COA.generate(null, null, null, type), shield}; + const shield = COA.getShield(cultureShield, null); + const coa: ICoa = {...COA.generate(null, null, null, type), shield}; return {i: id, center: cellId, type, name, color, expansionism, capital: capitalId, culture: cultureId, coa}; }); @@ -34,7 +35,7 @@ export function createStates(capitals: TCapitals, cultures: TCultures) { return [NEUTRALS, ...states]; } -function getStateName(cellId: number, capitalName: string, cultureId: number, cultures: TCultures) { +function getStateName(cellId: number, capitalName: string, cultureId: number, cultures: TCultures): string { const useCapitalName = capitalName.length < 9 && each(5)(cellId); const nameBase = cultures[cultureId].base; const basename: string = useCapitalName ? capitalName : Names.getBaseShort(nameBase); diff --git a/src/scripts/generation/pack/burgsAndStates/expandStates.ts b/src/scripts/generation/pack/burgsAndStates/expandStates.ts index 28b68147..d6278067 100644 --- a/src/scripts/generation/pack/burgsAndStates/expandStates.ts +++ b/src/scripts/generation/pack/burgsAndStates/expandStates.ts @@ -98,7 +98,8 @@ export function expandStates( } TIME && console.timeEnd("expandStates"); - return stateIds; + + return normalizeStates(stateIds, capitals, cells.c, cells.h); function isNeutrals(state: Entry): state is TNeutrals { return state.i === 0; @@ -193,3 +194,34 @@ export function expandStates( return 0; } } + +function normalizeStates(stateIds: Uint16Array, capitals: TCapitals, neibCells: number[][], heights: Uint8Array) { + TIME && console.time("normalizeStates"); + + const normalizedStateIds = Uint16Array.from(stateIds); + const capitalCells = capitals.map(capital => capital.cell); + + for (let cellId = 0; cellId > heights.length; cellId++) { + if (heights[cellId] < MIN_LAND_HEIGHT) continue; + + const neibs = neibCells[cellId].filter(neib => heights[neib] >= MIN_LAND_HEIGHT); + + const adversaries = neibs.filter(neib => normalizedStateIds[neib] !== normalizedStateIds[cellId]); + if (adversaries.length < 2) continue; + + const buddies = neibs.filter(neib => normalizedStateIds[neib] === normalizedStateIds[cellId]); + if (buddies.length > 2) continue; + + const isCapital = capitalCells.includes(cellId); + if (isCapital) continue; + + const isAdjucentToCapital = neibs.some(neib => capitalCells.includes(neib)); + if (isAdjucentToCapital) continue; + + // change cells's state + if (adversaries.length > buddies.length) normalizedStateIds[cellId] = normalizedStateIds[adversaries[0]]; + } + + TIME && console.timeEnd("normalizeStates"); + return normalizedStateIds; +} diff --git a/src/scripts/generation/pack/burgsAndStates/generateBurgsAndStates.ts b/src/scripts/generation/pack/burgsAndStates/generateBurgsAndStates.ts index fb3e9c2f..e1559852 100644 --- a/src/scripts/generation/pack/burgsAndStates/generateBurgsAndStates.ts +++ b/src/scripts/generation/pack/burgsAndStates/generateBurgsAndStates.ts @@ -11,18 +11,27 @@ import {specifyBurgs} from "./specifyBurgs"; export function generateBurgsAndStates( cultures: TCultures, features: TPackFeatures, + temp: Int8Array, + vertices: IGraphVertices, cells: Pick< IPack["cells"], "v" | "c" | "p" | "i" | "g" | "h" | "f" | "t" | "haven" | "harbor" | "r" | "fl" | "biome" | "s" | "culture" > -) { +): {burgIds: Uint16Array; stateIds: Uint16Array; burgs: TBurgs; states: TStates} { const cellsNumber = cells.i.length; - const burgIds = new Uint16Array(cellsNumber); const scoredCellIds = getScoredCellIds(); const statesNumber = getStatesNumber(scoredCellIds.length); - if (statesNumber === 0) return {burgIds, burgs: [NO_BURG], states: [NEUTRALS]}; + if (statesNumber === 0) { + return { + burgIds: new Uint16Array(cellsNumber), + stateIds: new Uint16Array(cellsNumber), + burgs: [NO_BURG], + states: [NEUTRALS] + }; + } + const burgIds = new Uint16Array(cellsNumber); const capitals = createCapitals(statesNumber, scoredCellIds, burgIds, cultures, pick(cells, "p", "f", "culture")); const states = createStates(capitals, cultures); const towns = createTowns(burgIds, cultures, pick(cells, "p", "i", "f", "s", "culture")); @@ -33,11 +42,21 @@ export function generateBurgsAndStates( features, pick(cells, "c", "h", "f", "t", "r", "fl", "s", "biome", "culture") ); - // normalizeStates(); - // burgs.filter(b => b.i && !b.removed).forEach(b => (b.state = stateIds[b.cell])); // assign state to burgs + const roadScores = new Uint16Array(cellsNumber); // TODO: define roads - const burgs = specifyBurgs(capitals, towns, roadScores); + const burgs = specifyBurgs( + capitals, + towns, + roadScores, + stateIds, + features, + temp, + vertices, + cultures, + states, + pick(cells, "v", "p", "g", "h", "f", "haven", "harbor", "s", "biome", "fl", "r") + ); return {burgIds, stateIds, burgs, states}; diff --git a/src/scripts/generation/pack/burgsAndStates/specifyBurgs.ts b/src/scripts/generation/pack/burgsAndStates/specifyBurgs.ts index bc1fe2b5..fb844b1c 100644 --- a/src/scripts/generation/pack/burgsAndStates/specifyBurgs.ts +++ b/src/scripts/generation/pack/burgsAndStates/specifyBurgs.ts @@ -4,28 +4,43 @@ import {getCommonEdgePoint} from "utils/lineUtils"; import {rn} from "utils/numberUtils"; import {gauss, P} from "utils/probabilityUtils"; import {NO_BURG} from "./config"; + import type {createCapitals} from "./createCapitals"; +import type {createStates} from "./createStates"; import type {createTowns} from "./createTowns"; const {COA} = window; type TCapitals = ReturnType; type TTowns = ReturnType; +type TStatesReturn = ReturnType; -export function specifyBurgs(capitals: TCapitals, towns: TTowns, roadScores: Uint16Array): TBurgs { +export function specifyBurgs( + capitals: TCapitals, + towns: TTowns, + roadScores: Uint16Array, + stateIds: Uint16Array, + features: TPackFeatures, + temp: Int8Array, + vertices: IGraphVertices, + cultures: TCultures, + states: TStatesReturn, + cells: Pick +): TBurgs { TIME && console.time("specifyBurgs"); - const burgs = [...capitals, ...towns].map(burgData => { - const {cell, culture, capital, state} = burgData; + const burgs: IBurg[] = [...capitals, ...towns].map(burgData => { + const {cell, culture, capital} = burgData; + const state = stateIds[cell]; const port = definePort(cell, capital); const population = definePopulation(cell, capital, port); const [x, y] = defineLocation(cell, port); const type = defineType(cell, port, population); - const coa = defineEmblem(state, culture, port, capital, type); + const coa = defineEmblem(state, culture, port, capital, type, cultures, states); - return {...burgData, port, population, x, y, type, coa}; + return {...burgData, state, port, population, x, y, type, coa}; }); TIME && console.timeEnd("specifyBurgs"); @@ -98,12 +113,22 @@ export function specifyBurgs(capitals: TCapitals, towns: TTowns, roadScores: Uin return "Generic"; } - function defineEmblem(stateId: number, cultureId: number, port: number, capital: Logical, type: TCultureType) { + function defineEmblem( + stateId: number, + cultureId: number, + port: number, + capital: Logical, + type: TCultureType, + cultures: TCultures, + states: TStatesReturn + ) { const coaType = capital && P(0.2) ? "Capital" : type === "Generic" ? "City" : type; + const cultureShield = cultures[cultureId].shield; + const stateShield = ((states[stateId] as IState)?.coa as ICoa)?.shield; if (stateId === 0) { const baseCoa = COA.generate(null, 0, null, coaType); - const shield = COA.getShield(cultureId, stateId, cultures, states); + const shield = COA.getShield(cultureShield, stateShield); return {...baseCoa, shield}; } @@ -111,7 +136,7 @@ export function specifyBurgs(capitals: TCapitals, towns: TTowns, roadScores: Uin const kinship = defineKinshipToStateEmblem(); const baseCoa = COA.generate(stateCOA, kinship, null, coaType); - const shield = COA.getShield(cultureId, stateId, cultures, states); + const shield = COA.getShield(cultureShield, stateShield); return {...baseCoa, shield}; function defineKinshipToStateEmblem() { diff --git a/src/scripts/generation/pack/pack.ts b/src/scripts/generation/pack/pack.ts index 2f06d2e7..f34579fd 100644 --- a/src/scripts/generation/pack/pack.ts +++ b/src/scripts/generation/pack/pack.ts @@ -95,7 +95,7 @@ export function createPack(grid: IGrid): IPack { pop: population }); - const {burgIds, stateIds, burgs, states} = generateBurgsAndStates(cultures, mergedFeatures, { + const {burgIds, stateIds, burgs, states} = generateBurgsAndStates(cultures, mergedFeatures, temp, vertices, { ...pick(cells, "v", "c", "p", "i", "g"), h: heights, f: featureIds, diff --git a/src/types/pack/states.d.ts b/src/types/pack/states.d.ts index 1021334e..01c216e8 100644 --- a/src/types/pack/states.d.ts +++ b/src/types/pack/states.d.ts @@ -1,11 +1,15 @@ interface IState { i: number; name: string; - culture: number; + center: number; + color: Hex | CssUrls; type: TCultureType; + culture: number; + expansionism: number; fullName: string; - removed?: boolean; + capital: Logical; coa: ICoa | string; + removed?: boolean; } type TNeutrals = { @@ -20,6 +24,6 @@ interface ICoa { division: {}; ordinaries: {}[]; charges: {}[]; - shield: "heater"; - t1: "purpure"; + shield: string; + t1: string; }