mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 09:41:24 +01:00
feat: multi-parental tree
This commit is contained in:
parent
1142be65c6
commit
f4ef859af8
4 changed files with 370 additions and 114 deletions
|
|
@ -60,7 +60,7 @@ window.Cultures = (function () {
|
||||||
c.color = colors[i];
|
c.color = colors[i];
|
||||||
c.type = defineCultureType(cell);
|
c.type = defineCultureType(cell);
|
||||||
c.expansionism = defineCultureExpansionism(c.type);
|
c.expansionism = defineCultureExpansionism(c.type);
|
||||||
c.origin = 0;
|
c.origins = [0];
|
||||||
c.code = abbreviate(c.name, codes);
|
c.code = abbreviate(c.name, codes);
|
||||||
codes.push(c.code);
|
codes.push(c.code);
|
||||||
cells.culture[cell] = i + 1;
|
cells.culture[cell] = i + 1;
|
||||||
|
|
@ -80,7 +80,7 @@ window.Cultures = (function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
// the first culture with id 0 is for wildlands
|
// the first culture with id 0 is for wildlands
|
||||||
cultures.unshift({name: "Wildlands", i: 0, base: 1, origin: null, shield: "round"});
|
cultures.unshift({name: "Wildlands", i: 0, base: 1, origins: [null], shield: "round"});
|
||||||
|
|
||||||
// make sure all bases exist in nameBases
|
// make sure all bases exist in nameBases
|
||||||
if (!nameBases.length) {
|
if (!nameBases.length) {
|
||||||
|
|
@ -115,7 +115,11 @@ window.Cultures = (function () {
|
||||||
if (cells.h[i] > 50) return "Highland"; // no penalty for hills and moutains, high for other elevations
|
if (cells.h[i] > 50) return "Highland"; // no penalty for hills and moutains, high for other elevations
|
||||||
const f = pack.features[cells.f[cells.haven[i]]]; // opposite feature
|
const f = pack.features[cells.f[cells.haven[i]]]; // opposite feature
|
||||||
if (f.type === "lake" && f.cells > 5) return "Lake"; // low water cross penalty and high for growth not along coastline
|
if (f.type === "lake" && f.cells > 5) return "Lake"; // low water cross penalty and high for growth not along coastline
|
||||||
if ((cells.harbor[i] && f.type !== "lake" && P(0.1)) || (cells.harbor[i] === 1 && P(0.6)) || (pack.features[cells.f[i]].group === "isle" && P(0.4)))
|
if (
|
||||||
|
(cells.harbor[i] && f.type !== "lake" && P(0.1)) ||
|
||||||
|
(cells.harbor[i] === 1 && P(0.6)) ||
|
||||||
|
(pack.features[cells.f[i]].group === "isle" && P(0.4))
|
||||||
|
)
|
||||||
return "Naval"; // low water cross penalty and high for non-along-coastline growth
|
return "Naval"; // low water cross penalty and high for non-along-coastline growth
|
||||||
if (cells.r[i] && cells.fl[i] > 100) return "River"; // no River cross penalty, penalty for non-River growth
|
if (cells.r[i] && cells.fl[i] > 100) return "River"; // no River cross penalty, penalty for non-River growth
|
||||||
if (cells.t[i] > 2 && [3, 7, 8, 9, 10, 12].includes(cells.biome[i])) return "Hunting"; // high penalty in non-native biomes
|
if (cells.t[i] > 2 && [3, 7, 8, 9, 10, 12].includes(cells.biome[i])) return "Hunting"; // high penalty in non-native biomes
|
||||||
|
|
@ -163,7 +167,22 @@ window.Cultures = (function () {
|
||||||
const emblemShape = document.getElementById("emblemShape").value;
|
const emblemShape = document.getElementById("emblemShape").value;
|
||||||
if (emblemShape === "random") shield = getRandomShield();
|
if (emblemShape === "random") shield = getRandomShield();
|
||||||
|
|
||||||
pack.cultures.push({name, color, base, center, i, expansionism: 1, type: "Generic", cells: 0, area: 0, rural: 0, urban: 0, origin: 0, code, shield});
|
pack.cultures.push({
|
||||||
|
name,
|
||||||
|
color,
|
||||||
|
base,
|
||||||
|
center,
|
||||||
|
i,
|
||||||
|
expansionism: 1,
|
||||||
|
type: "Generic",
|
||||||
|
cells: 0,
|
||||||
|
area: 0,
|
||||||
|
rural: 0,
|
||||||
|
urban: 0,
|
||||||
|
origins: [0],
|
||||||
|
code,
|
||||||
|
shield
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const getDefault = function (count) {
|
const getDefault = function (count) {
|
||||||
|
|
@ -180,7 +199,8 @@ window.Cultures = (function () {
|
||||||
return d ? d + 1 : 1;
|
return d ? d + 1 : 1;
|
||||||
}; // temperature difference fee
|
}; // temperature difference fee
|
||||||
const bd = (cell, biomes, fee = 4) => (biomes.includes(cells.biome[cell]) ? 1 : fee); // biome difference fee
|
const bd = (cell, biomes, fee = 4) => (biomes.includes(cells.biome[cell]) ? 1 : fee); // biome difference fee
|
||||||
const sf = (cell, fee = 4) => (cells.haven[cell] && pack.features[cells.f[cells.haven[cell]]].type !== "lake" ? 1 : fee); // not on sea coast fee
|
const sf = (cell, fee = 4) =>
|
||||||
|
cells.haven[cell] && pack.features[cells.f[cells.haven[cell]]].type !== "lake" ? 1 : fee; // not on sea coast fee
|
||||||
|
|
||||||
if (culturesSet.value === "european") {
|
if (culturesSet.value === "european") {
|
||||||
return [
|
return [
|
||||||
|
|
@ -208,7 +228,13 @@ window.Cultures = (function () {
|
||||||
{name: "Hantzu", base: 11, odd: 1, sort: i => n(i) / td(i, 13), shield: "banner"},
|
{name: "Hantzu", base: 11, odd: 1, sort: i => n(i) / td(i, 13), shield: "banner"},
|
||||||
{name: "Yamoto", base: 12, odd: 1, sort: i => n(i) / td(i, 15) / t[i], shield: "round"},
|
{name: "Yamoto", base: 12, odd: 1, sort: i => n(i) / td(i, 15) / t[i], shield: "round"},
|
||||||
{name: "Turchian", base: 16, odd: 1, sort: i => n(i) / td(i, 12), shield: "round"},
|
{name: "Turchian", base: 16, odd: 1, sort: i => n(i) / td(i, 12), shield: "round"},
|
||||||
{name: "Berberan", base: 17, odd: 0.2, sort: i => (n(i) / td(i, 19) / bd(i, [1, 2, 3], 7)) * t[i], shield: "oval"},
|
{
|
||||||
|
name: "Berberan",
|
||||||
|
base: 17,
|
||||||
|
odd: 0.2,
|
||||||
|
sort: i => (n(i) / td(i, 19) / bd(i, [1, 2, 3], 7)) * t[i],
|
||||||
|
shield: "oval"
|
||||||
|
},
|
||||||
{name: "Eurabic", base: 18, odd: 1, sort: i => (n(i) / td(i, 26) / bd(i, [1, 2], 7)) * t[i], shield: "oval"},
|
{name: "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: "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: "Tehrani", base: 24, odd: 1, sort: i => (n(i) / td(i, 18)) * h[i], shield: "round"},
|
||||||
|
|
@ -259,15 +285,45 @@ window.Cultures = (function () {
|
||||||
if (culturesSet.value === "highFantasy") {
|
if (culturesSet.value === "highFantasy") {
|
||||||
return [
|
return [
|
||||||
// fantasy races
|
// fantasy races
|
||||||
{name: "Quenian (Elfish)", base: 33, odd: 1, sort: i => (n(i) / bd(i, [6, 7, 8, 9], 10)) * t[i], shield: "gondor"}, // Elves
|
{
|
||||||
{name: "Eldar (Elfish)", base: 33, odd: 1, sort: i => (n(i) / bd(i, [6, 7, 8, 9], 10)) * t[i], shield: "noldor"}, // Elves
|
name: "Quenian (Elfish)",
|
||||||
{name: "Trow (Dark Elfish)", base: 34, odd: 0.9, sort: i => (n(i) / bd(i, [7, 8, 9, 12], 10)) * t[i], shield: "hessen"}, // Dark Elves
|
base: 33,
|
||||||
{name: "Lothian (Dark Elfish)", base: 34, odd: 0.3, sort: i => (n(i) / bd(i, [7, 8, 9, 12], 10)) * t[i], shield: "wedged"}, // Dark Elves
|
odd: 1,
|
||||||
|
sort: i => (n(i) / bd(i, [6, 7, 8, 9], 10)) * t[i],
|
||||||
|
shield: "gondor"
|
||||||
|
}, // Elves
|
||||||
|
{
|
||||||
|
name: "Eldar (Elfish)",
|
||||||
|
base: 33,
|
||||||
|
odd: 1,
|
||||||
|
sort: i => (n(i) / bd(i, [6, 7, 8, 9], 10)) * t[i],
|
||||||
|
shield: "noldor"
|
||||||
|
}, // Elves
|
||||||
|
{
|
||||||
|
name: "Trow (Dark Elfish)",
|
||||||
|
base: 34,
|
||||||
|
odd: 0.9,
|
||||||
|
sort: i => (n(i) / bd(i, [7, 8, 9, 12], 10)) * t[i],
|
||||||
|
shield: "hessen"
|
||||||
|
}, // Dark Elves
|
||||||
|
{
|
||||||
|
name: "Lothian (Dark Elfish)",
|
||||||
|
base: 34,
|
||||||
|
odd: 0.3,
|
||||||
|
sort: i => (n(i) / bd(i, [7, 8, 9, 12], 10)) * t[i],
|
||||||
|
shield: "wedged"
|
||||||
|
}, // Dark Elves
|
||||||
{name: "Dunirr (Dwarven)", base: 35, odd: 1, sort: i => n(i) + h[i], shield: "ironHills"}, // Dwarfs
|
{name: "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: "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: "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: "Uruk (Orkish)", base: 37, odd: 1, sort: i => h[i] * t[i], shield: "urukHai"}, // Orc
|
||||||
{name: "Ugluk (Orkish)", base: 37, odd: 0.5, sort: i => (h[i] * t[i]) / bd(i, [1, 2, 10, 11]), shield: "moriaOrc"}, // Orc
|
{
|
||||||
|
name: "Ugluk (Orkish)",
|
||||||
|
base: 37,
|
||||||
|
odd: 0.5,
|
||||||
|
sort: i => (h[i] * t[i]) / bd(i, [1, 2, 10, 11]),
|
||||||
|
shield: "moriaOrc"
|
||||||
|
}, // Orc
|
||||||
{name: "Yotunn (Giants)", base: 38, odd: 0.7, sort: i => td(i, -10), shield: "pavise"}, // Giant
|
{name: "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: "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: "Arago (Arachnid)", base: 40, odd: 0.7, sort: i => t[i] - s[i], shield: "horsehead2"}, // Arachnid
|
||||||
|
|
@ -276,7 +332,13 @@ window.Cultures = (function () {
|
||||||
{name: "Anor (Human)", base: 32, odd: 1, sort: i => n(i) / td(i, 10), shield: "fantasy5"},
|
{name: "Anor (Human)", base: 32, odd: 1, sort: i => n(i) / td(i, 10), shield: "fantasy5"},
|
||||||
{name: "Dail (Human)", base: 32, odd: 1, sort: i => n(i) / td(i, 13), shield: "roman"},
|
{name: "Dail (Human)", base: 32, odd: 1, sort: i => n(i) / td(i, 13), shield: "roman"},
|
||||||
{name: "Rohand (Human)", base: 16, odd: 1, sort: i => n(i) / td(i, 16), shield: "round"},
|
{name: "Rohand (Human)", base: 16, odd: 1, sort: i => n(i) / td(i, 16), shield: "round"},
|
||||||
{name: "Dulandir (Human)", base: 31, odd: 1, sort: i => (n(i) / td(i, 5) / bd(i, [2, 4, 10], 7)) * t[i], shield: "easterling"}
|
{
|
||||||
|
name: "Dulandir (Human)",
|
||||||
|
base: 31,
|
||||||
|
odd: 1,
|
||||||
|
sort: i => (n(i) / td(i, 5) / bd(i, [2, 4, 10], 7)) * t[i],
|
||||||
|
shield: "easterling"
|
||||||
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -296,18 +358,48 @@ window.Cultures = (function () {
|
||||||
{name: "Hetallian", base: 3, odd: 0.3, sort: i => n(i) / td(i, 15), shield: "oval"},
|
{name: "Hetallian", base: 3, odd: 0.3, sort: i => n(i) / td(i, 15), shield: "oval"},
|
||||||
{name: "Astellian", base: 4, odd: 0.3, sort: i => n(i) / td(i, 16), shield: "spanish"},
|
{name: "Astellian", base: 4, odd: 0.3, sort: i => n(i) / td(i, 16), shield: "spanish"},
|
||||||
// rare real-world exotic
|
// 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"},
|
{
|
||||||
|
name: "Kiswaili",
|
||||||
|
base: 28,
|
||||||
|
odd: 0.05,
|
||||||
|
sort: i => n(i) / td(i, 29) / bd(i, [1, 3, 5, 7]),
|
||||||
|
shield: "vesicaPiscis"
|
||||||
|
},
|
||||||
{name: "Yoruba", base: 21, odd: 0.05, sort: i => n(i) / td(i, 15) / bd(i, [5, 7]), shield: "vesicaPiscis"},
|
{name: "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: "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: "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: "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: "Guantzu", base: 30, odd: 0.05, sort: i => n(i) / td(i, 17), shield: "banner"},
|
||||||
{name: "Ulus", base: 31, odd: 0.05, sort: i => (n(i) / td(i, 5) / bd(i, [2, 4, 10], 7)) * t[i], shield: "banner"},
|
{
|
||||||
|
name: "Ulus",
|
||||||
|
base: 31,
|
||||||
|
odd: 0.05,
|
||||||
|
sort: i => (n(i) / td(i, 5) / bd(i, [2, 4, 10], 7)) * t[i],
|
||||||
|
shield: "banner"
|
||||||
|
},
|
||||||
{name: "Turan", base: 16, odd: 0.05, sort: i => n(i) / td(i, 12), shield: "round"},
|
{name: "Turan", base: 16, odd: 0.05, sort: i => n(i) / td(i, 12), shield: "round"},
|
||||||
{name: "Berberan", base: 17, odd: 0.05, sort: i => (n(i) / td(i, 19) / bd(i, [1, 2, 3], 7)) * t[i], shield: "round"},
|
{
|
||||||
{name: "Eurabic", base: 18, odd: 0.05, sort: i => (n(i) / td(i, 26) / bd(i, [1, 2], 7)) * t[i], shield: "round"},
|
name: "Berberan",
|
||||||
|
base: 17,
|
||||||
|
odd: 0.05,
|
||||||
|
sort: i => (n(i) / td(i, 19) / bd(i, [1, 2, 3], 7)) * t[i],
|
||||||
|
shield: "round"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Eurabic",
|
||||||
|
base: 18,
|
||||||
|
odd: 0.05,
|
||||||
|
sort: i => (n(i) / td(i, 26) / bd(i, [1, 2], 7)) * t[i],
|
||||||
|
shield: "round"
|
||||||
|
},
|
||||||
{name: "Slovan", base: 5, odd: 0.05, sort: i => (n(i) / td(i, 6)) * t[i], shield: "round"},
|
{name: "Slovan", base: 5, odd: 0.05, sort: i => (n(i) / td(i, 6)) * t[i], shield: "round"},
|
||||||
{name: "Keltan", base: 22, odd: 0.1, sort: i => n(i) / td(i, 11) ** 0.5 / bd(i, [6, 8]), shield: "vesicaPiscis"},
|
{
|
||||||
|
name: "Keltan",
|
||||||
|
base: 22,
|
||||||
|
odd: 0.1,
|
||||||
|
sort: i => n(i) / td(i, 11) ** 0.5 / bd(i, [6, 8]),
|
||||||
|
shield: "vesicaPiscis"
|
||||||
|
},
|
||||||
{name: "Elladan", base: 7, odd: 0.2, sort: i => (n(i) / td(i, 18) / sf(i)) * h[i], shield: "boeotian"},
|
{name: "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: "Romian", base: 8, odd: 0.2, sort: i => n(i) / td(i, 14) / t[i], shield: "roman"},
|
||||||
// fantasy races
|
// fantasy races
|
||||||
|
|
@ -350,12 +442,24 @@ window.Cultures = (function () {
|
||||||
{name: "Nawatli", base: 14, odd: 0.1, sort: i => h[i] / td(i, 18) / bd(i, [7]), shield: "square"},
|
{name: "Nawatli", base: 14, odd: 0.1, sort: i => h[i] / td(i, 18) / bd(i, [7]), shield: "square"},
|
||||||
{name: "Vengrian", base: 15, odd: 0.2, sort: i => (n(i) / td(i, 11) / bd(i, [4])) * t[i], shield: "wedged"},
|
{name: "Vengrian", base: 15, odd: 0.2, sort: i => (n(i) / td(i, 11) / bd(i, [4])) * t[i], shield: "wedged"},
|
||||||
{name: "Turchian", base: 16, odd: 0.2, sort: i => n(i) / td(i, 13), shield: "round"},
|
{name: "Turchian", base: 16, odd: 0.2, sort: i => n(i) / td(i, 13), shield: "round"},
|
||||||
{name: "Berberan", base: 17, odd: 0.1, sort: i => (n(i) / td(i, 19) / bd(i, [1, 2, 3], 7)) * t[i], shield: "round"},
|
{
|
||||||
|
name: "Berberan",
|
||||||
|
base: 17,
|
||||||
|
odd: 0.1,
|
||||||
|
sort: i => (n(i) / td(i, 19) / bd(i, [1, 2, 3], 7)) * t[i],
|
||||||
|
shield: "round"
|
||||||
|
},
|
||||||
{name: "Eurabic", base: 18, odd: 0.2, sort: i => (n(i) / td(i, 26) / bd(i, [1, 2], 7)) * t[i], shield: "round"},
|
{name: "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: "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: "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: "Yoruba", base: 21, odd: 0.05, sort: i => n(i) / td(i, 15) / bd(i, [5, 7]), shield: "vesicaPiscis"},
|
||||||
{name: "Keltan", base: 22, odd: 0.05, sort: i => (n(i) / td(i, 11) / bd(i, [6, 8])) * t[i], shield: "vesicaPiscis"},
|
{
|
||||||
|
name: "Keltan",
|
||||||
|
base: 22,
|
||||||
|
odd: 0.05,
|
||||||
|
sort: i => (n(i) / td(i, 11) / bd(i, [6, 8])) * t[i],
|
||||||
|
shield: "vesicaPiscis"
|
||||||
|
},
|
||||||
{name: "Efratic", base: 23, odd: 0.05, sort: i => (n(i) / td(i, 22)) * t[i], shield: "diamond"},
|
{name: "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: "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: "Maui", base: 25, odd: 0.05, sort: i => n(i) / td(i, 24) / sf(i) / t[i], shield: "round"},
|
||||||
|
|
@ -394,7 +498,8 @@ window.Cultures = (function () {
|
||||||
const heightCost = getHeightCost(e, cells.h[e], type);
|
const heightCost = getHeightCost(e, cells.h[e], type);
|
||||||
const riverCost = getRiverCost(cells.r[e], e, type);
|
const riverCost = getRiverCost(cells.r[e], e, type);
|
||||||
const typeCost = getTypeCost(cells.t[e], type);
|
const typeCost = getTypeCost(cells.t[e], type);
|
||||||
const totalCost = p + (biomeCost + biomeChangeCost + heightCost + riverCost + typeCost) / pack.cultures[c].expansionism;
|
const totalCost =
|
||||||
|
p + (biomeCost + biomeChangeCost + heightCost + riverCost + typeCost) / pack.cultures[c].expansionism;
|
||||||
|
|
||||||
if (totalCost > neutral) return;
|
if (totalCost > neutral) return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -30,8 +30,18 @@ export function resolveVersionConflicts(version) {
|
||||||
.attr("stroke-dasharray", null)
|
.attr("stroke-dasharray", null)
|
||||||
.attr("stroke-linecap", null)
|
.attr("stroke-linecap", null)
|
||||||
.attr("filter", null);
|
.attr("filter", null);
|
||||||
stateBorders.attr("opacity", 0.8).attr("stroke", "#56566d").attr("stroke-width", 1).attr("stroke-dasharray", "2").attr("stroke-linecap", "butt");
|
stateBorders
|
||||||
provinceBorders.attr("opacity", 0.8).attr("stroke", "#56566d").attr("stroke-width", 0.5).attr("stroke-dasharray", "1").attr("stroke-linecap", "butt");
|
.attr("opacity", 0.8)
|
||||||
|
.attr("stroke", "#56566d")
|
||||||
|
.attr("stroke-width", 1)
|
||||||
|
.attr("stroke-dasharray", "2")
|
||||||
|
.attr("stroke-linecap", "butt");
|
||||||
|
provinceBorders
|
||||||
|
.attr("opacity", 0.8)
|
||||||
|
.attr("stroke", "#56566d")
|
||||||
|
.attr("stroke-width", 0.5)
|
||||||
|
.attr("stroke-dasharray", "1")
|
||||||
|
.attr("stroke-linecap", "butt");
|
||||||
|
|
||||||
// v1.0 added state relations, provinces, forms and full names
|
// v1.0 added state relations, provinces, forms and full names
|
||||||
provs = viewbox.insert("g", "#borders").attr("id", "provs").attr("opacity", 0.6);
|
provs = viewbox.insert("g", "#borders").attr("id", "provs").attr("opacity", 0.6);
|
||||||
|
|
@ -47,7 +57,12 @@ export function resolveVersionConflicts(version) {
|
||||||
|
|
||||||
// v1.0 added zones layer
|
// v1.0 added zones layer
|
||||||
zones = viewbox.insert("g", "#borders").attr("id", "zones").attr("display", "none");
|
zones = viewbox.insert("g", "#borders").attr("id", "zones").attr("display", "none");
|
||||||
zones.attr("opacity", 0.6).attr("stroke", null).attr("stroke-width", 0).attr("stroke-dasharray", null).attr("stroke-linecap", "butt");
|
zones
|
||||||
|
.attr("opacity", 0.6)
|
||||||
|
.attr("stroke", null)
|
||||||
|
.attr("stroke-width", 0)
|
||||||
|
.attr("stroke-dasharray", null)
|
||||||
|
.attr("stroke-linecap", "butt");
|
||||||
addZones();
|
addZones();
|
||||||
if (!markers.selectAll("*").size()) {
|
if (!markers.selectAll("*").size()) {
|
||||||
Markers.generate();
|
Markers.generate();
|
||||||
|
|
@ -55,9 +70,23 @@ export function resolveVersionConflicts(version) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// v1.0 add fogging layer (state focus)
|
// v1.0 add fogging layer (state focus)
|
||||||
fogging = viewbox.insert("g", "#ruler").attr("id", "fogging-cont").attr("mask", "url(#fog)").append("g").attr("id", "fogging").style("display", "none");
|
fogging = viewbox
|
||||||
|
.insert("g", "#ruler")
|
||||||
|
.attr("id", "fogging-cont")
|
||||||
|
.attr("mask", "url(#fog)")
|
||||||
|
.append("g")
|
||||||
|
.attr("id", "fogging")
|
||||||
|
.style("display", "none");
|
||||||
fogging.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%");
|
fogging.append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%");
|
||||||
defs.append("mask").attr("id", "fog").append("rect").attr("x", 0).attr("y", 0).attr("width", "100%").attr("height", "100%").attr("fill", "white");
|
defs
|
||||||
|
.append("mask")
|
||||||
|
.attr("id", "fog")
|
||||||
|
.append("rect")
|
||||||
|
.attr("x", 0)
|
||||||
|
.attr("y", 0)
|
||||||
|
.attr("width", "100%")
|
||||||
|
.attr("height", "100%")
|
||||||
|
.attr("fill", "white");
|
||||||
|
|
||||||
// v1.0 changes states opacity bask to regions level
|
// v1.0 changes states opacity bask to regions level
|
||||||
if (statesBody.attr("opacity")) {
|
if (statesBody.attr("opacity")) {
|
||||||
|
|
@ -103,12 +132,24 @@ export function resolveVersionConflicts(version) {
|
||||||
|
|
||||||
if (!document.getElementById("freshwater")) {
|
if (!document.getElementById("freshwater")) {
|
||||||
lakes.append("g").attr("id", "freshwater");
|
lakes.append("g").attr("id", "freshwater");
|
||||||
lakes.select("#freshwater").attr("opacity", 0.5).attr("fill", "#a6c1fd").attr("stroke", "#5f799d").attr("stroke-width", 0.7).attr("filter", null);
|
lakes
|
||||||
|
.select("#freshwater")
|
||||||
|
.attr("opacity", 0.5)
|
||||||
|
.attr("fill", "#a6c1fd")
|
||||||
|
.attr("stroke", "#5f799d")
|
||||||
|
.attr("stroke-width", 0.7)
|
||||||
|
.attr("filter", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!document.getElementById("salt")) {
|
if (!document.getElementById("salt")) {
|
||||||
lakes.append("g").attr("id", "salt");
|
lakes.append("g").attr("id", "salt");
|
||||||
lakes.select("#salt").attr("opacity", 0.5).attr("fill", "#409b8a").attr("stroke", "#388985").attr("stroke-width", 0.7).attr("filter", null);
|
lakes
|
||||||
|
.select("#salt")
|
||||||
|
.attr("opacity", 0.5)
|
||||||
|
.attr("fill", "#409b8a")
|
||||||
|
.attr("stroke", "#388985")
|
||||||
|
.attr("stroke-width", 0.7)
|
||||||
|
.attr("filter", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// v1.1 added new lake and coast groups
|
// v1.1 added new lake and coast groups
|
||||||
|
|
@ -116,14 +157,42 @@ export function resolveVersionConflicts(version) {
|
||||||
lakes.append("g").attr("id", "sinkhole");
|
lakes.append("g").attr("id", "sinkhole");
|
||||||
lakes.append("g").attr("id", "frozen");
|
lakes.append("g").attr("id", "frozen");
|
||||||
lakes.append("g").attr("id", "lava");
|
lakes.append("g").attr("id", "lava");
|
||||||
lakes.select("#sinkhole").attr("opacity", 1).attr("fill", "#5bc9fd").attr("stroke", "#53a3b0").attr("stroke-width", 0.7).attr("filter", null);
|
lakes
|
||||||
lakes.select("#frozen").attr("opacity", 0.95).attr("fill", "#cdd4e7").attr("stroke", "#cfe0eb").attr("stroke-width", 0).attr("filter", null);
|
.select("#sinkhole")
|
||||||
lakes.select("#lava").attr("opacity", 0.7).attr("fill", "#90270d").attr("stroke", "#f93e0c").attr("stroke-width", 2).attr("filter", "url(#crumpled)");
|
.attr("opacity", 1)
|
||||||
|
.attr("fill", "#5bc9fd")
|
||||||
|
.attr("stroke", "#53a3b0")
|
||||||
|
.attr("stroke-width", 0.7)
|
||||||
|
.attr("filter", null);
|
||||||
|
lakes
|
||||||
|
.select("#frozen")
|
||||||
|
.attr("opacity", 0.95)
|
||||||
|
.attr("fill", "#cdd4e7")
|
||||||
|
.attr("stroke", "#cfe0eb")
|
||||||
|
.attr("stroke-width", 0)
|
||||||
|
.attr("filter", null);
|
||||||
|
lakes
|
||||||
|
.select("#lava")
|
||||||
|
.attr("opacity", 0.7)
|
||||||
|
.attr("fill", "#90270d")
|
||||||
|
.attr("stroke", "#f93e0c")
|
||||||
|
.attr("stroke-width", 2)
|
||||||
|
.attr("filter", "url(#crumpled)");
|
||||||
|
|
||||||
coastline.append("g").attr("id", "sea_island");
|
coastline.append("g").attr("id", "sea_island");
|
||||||
coastline.append("g").attr("id", "lake_island");
|
coastline.append("g").attr("id", "lake_island");
|
||||||
coastline.select("#sea_island").attr("opacity", 0.5).attr("stroke", "#1f3846").attr("stroke-width", 0.7).attr("filter", "url(#dropShadow)");
|
coastline
|
||||||
coastline.select("#lake_island").attr("opacity", 1).attr("stroke", "#7c8eaf").attr("stroke-width", 0.35).attr("filter", null);
|
.select("#sea_island")
|
||||||
|
.attr("opacity", 0.5)
|
||||||
|
.attr("stroke", "#1f3846")
|
||||||
|
.attr("stroke-width", 0.7)
|
||||||
|
.attr("filter", "url(#dropShadow)");
|
||||||
|
coastline
|
||||||
|
.select("#lake_island")
|
||||||
|
.attr("opacity", 1)
|
||||||
|
.attr("stroke", "#7c8eaf")
|
||||||
|
.attr("stroke-width", 0.35)
|
||||||
|
.attr("filter", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// v1.1 features stores more data
|
// v1.1 features stores more data
|
||||||
|
|
@ -203,7 +272,13 @@ export function resolveVersionConflicts(version) {
|
||||||
|
|
||||||
// v1.3 added militry layer
|
// v1.3 added militry layer
|
||||||
armies = viewbox.insert("g", "#icons").attr("id", "armies");
|
armies = viewbox.insert("g", "#icons").attr("id", "armies");
|
||||||
armies.attr("opacity", 1).attr("fill-opacity", 1).attr("font-size", 6).attr("box-size", 3).attr("stroke", "#000").attr("stroke-width", 0.3);
|
armies
|
||||||
|
.attr("opacity", 1)
|
||||||
|
.attr("fill-opacity", 1)
|
||||||
|
.attr("font-size", 6)
|
||||||
|
.attr("box-size", 3)
|
||||||
|
.attr("stroke", "#000")
|
||||||
|
.attr("stroke-width", 0.3);
|
||||||
turnButtonOn("toggleMilitary");
|
turnButtonOn("toggleMilitary");
|
||||||
Military.generate();
|
Military.generate();
|
||||||
}
|
}
|
||||||
|
|
@ -212,12 +287,23 @@ export function resolveVersionConflicts(version) {
|
||||||
// v1.35 added dry lakes
|
// v1.35 added dry lakes
|
||||||
if (!lakes.select("#dry").size()) {
|
if (!lakes.select("#dry").size()) {
|
||||||
lakes.append("g").attr("id", "dry");
|
lakes.append("g").attr("id", "dry");
|
||||||
lakes.select("#dry").attr("opacity", 1).attr("fill", "#c9bfa7").attr("stroke", "#8e816f").attr("stroke-width", 0.7).attr("filter", null);
|
lakes
|
||||||
|
.select("#dry")
|
||||||
|
.attr("opacity", 1)
|
||||||
|
.attr("fill", "#c9bfa7")
|
||||||
|
.attr("stroke", "#8e816f")
|
||||||
|
.attr("stroke-width", 0.7)
|
||||||
|
.attr("filter", null);
|
||||||
}
|
}
|
||||||
|
|
||||||
// v1.4 added ice layer
|
// v1.4 added ice layer
|
||||||
ice = viewbox.insert("g", "#coastline").attr("id", "ice").style("display", "none");
|
ice = viewbox.insert("g", "#coastline").attr("id", "ice").style("display", "none");
|
||||||
ice.attr("opacity", null).attr("fill", "#e8f0f6").attr("stroke", "#e8f0f6").attr("stroke-width", 1).attr("filter", "url(#dropShadow05)");
|
ice
|
||||||
|
.attr("opacity", null)
|
||||||
|
.attr("fill", "#e8f0f6")
|
||||||
|
.attr("stroke", "#e8f0f6")
|
||||||
|
.attr("stroke-width", 1)
|
||||||
|
.attr("filter", "url(#dropShadow05)");
|
||||||
drawIce();
|
drawIce();
|
||||||
|
|
||||||
// v1.4 added icon and power attributes for units
|
// v1.4 added icon and power attributes for units
|
||||||
|
|
@ -530,4 +616,19 @@ export function resolveVersionConflicts(version) {
|
||||||
// v1.84.0 moved intial screen out of maon svg
|
// v1.84.0 moved intial screen out of maon svg
|
||||||
svg.select("#initial").remove();
|
svg.select("#initial").remove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (version < 1.86) {
|
||||||
|
// v1.86.0 added support of multi-origin culture and religion hierarchy trees
|
||||||
|
for (const culture of pack.cultures) {
|
||||||
|
const origin = culture.origin;
|
||||||
|
delete culture.origin;
|
||||||
|
culture.origins = [origin];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const religion of pack.religions) {
|
||||||
|
const origin = religion.origin;
|
||||||
|
delete religion.origin;
|
||||||
|
religion.origins = [origin];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -143,7 +143,9 @@ function culturesEditorAddLines() {
|
||||||
const rural = c.rural * populationRate;
|
const rural = c.rural * populationRate;
|
||||||
const urban = c.urban * populationRate * urbanization;
|
const urban = c.urban * populationRate * urbanization;
|
||||||
const population = rn(rural + urban);
|
const population = rn(rural + urban);
|
||||||
const populationTip = `Total population: ${si(population)}; Rural population: ${si(rural)}; Urban population: ${si(urban)}. Click to edit`;
|
const populationTip = `Total population: ${si(population)}; Rural population: ${si(rural)}; Urban population: ${si(
|
||||||
|
urban
|
||||||
|
)}. Click to edit`;
|
||||||
totalArea += area;
|
totalArea += area;
|
||||||
totalPopulation += population;
|
totalPopulation += population;
|
||||||
|
|
||||||
|
|
@ -167,7 +169,9 @@ function culturesEditorAddLines() {
|
||||||
value="${c.name}" autocorrect="off" spellcheck="false" />
|
value="${c.name}" autocorrect="off" spellcheck="false" />
|
||||||
<span class="icon-cw placeholder"></span>
|
<span class="icon-cw placeholder"></span>
|
||||||
<select class="cultureType placeholder">${getTypeOptions(c.type)}</select>
|
<select class="cultureType placeholder">${getTypeOptions(c.type)}</select>
|
||||||
<select data-tip="Culture namesbase. Click to change. Click on arrows to re-generate names" class="cultureBase">${getBaseOptions(c.base)}</select>
|
<select data-tip="Culture namesbase. Click to change. Click on arrows to re-generate names" class="cultureBase">${getBaseOptions(
|
||||||
|
c.base
|
||||||
|
)}</select>
|
||||||
<span data-tip="Cells count" class="icon-check-empty hide"></span>
|
<span data-tip="Cells count" class="icon-check-empty hide"></span>
|
||||||
<div data-tip="Cells count" class="cultureCells hide" style="width: 4em">${c.cells}</div>
|
<div data-tip="Cells count" class="cultureCells hide" style="width: 4em">${c.cells}</div>
|
||||||
<span class="icon-resize-full placeholder hide"></span>
|
<span class="icon-resize-full placeholder hide"></span>
|
||||||
|
|
@ -175,7 +179,9 @@ function culturesEditorAddLines() {
|
||||||
<span data-tip="Culture area" style="padding-right: 4px" class="icon-map-o hide"></span>
|
<span data-tip="Culture area" style="padding-right: 4px" class="icon-map-o hide"></span>
|
||||||
<div data-tip="Culture area" class="cultureArea hide" style="width: 6em">${si(area)} ${unit}</div>
|
<div data-tip="Culture area" class="cultureArea hide" style="width: 6em">${si(area)} ${unit}</div>
|
||||||
<span data-tip="${populationTip}" class="icon-male hide"></span>
|
<span data-tip="${populationTip}" class="icon-male hide"></span>
|
||||||
<div data-tip="${populationTip}" class="culturePopulation hide pointer" style="width: 5em">${si(population)}</div>
|
<div data-tip="${populationTip}" class="culturePopulation hide pointer" style="width: 5em">${si(
|
||||||
|
population
|
||||||
|
)}</div>
|
||||||
<span data-tip="Click to re-generate names for burgs with this culture assigned" class="icon-arrows-cw hide"></span>
|
<span data-tip="Click to re-generate names for burgs with this culture assigned" class="icon-arrows-cw hide"></span>
|
||||||
${getShapeOptions(selectShape, c.shield)}
|
${getShapeOptions(selectShape, c.shield)}
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|
@ -199,8 +205,12 @@ function culturesEditorAddLines() {
|
||||||
<input data-tip="Culture name. Click and type to change" class="cultureName" style="width: 7em"
|
<input data-tip="Culture name. Click and type to change" class="cultureName" style="width: 7em"
|
||||||
value="${c.name}" autocorrect="off" spellcheck="false" />
|
value="${c.name}" autocorrect="off" spellcheck="false" />
|
||||||
<span data-tip="Regenerate culture name" class="icon-cw hiddenIcon" style="visibility: hidden"></span>
|
<span data-tip="Regenerate culture name" class="icon-cw hiddenIcon" style="visibility: hidden"></span>
|
||||||
<select data-tip="Culture type. Defines growth model. Click to change" class="cultureType">${getTypeOptions(c.type)}</select>
|
<select data-tip="Culture type. Defines growth model. Click to change" class="cultureType">${getTypeOptions(
|
||||||
<select data-tip="Culture namesbase. Click to change. Click on arrows to re-generate names" class="cultureBase">${getBaseOptions(c.base)}</select>
|
c.type
|
||||||
|
)}</select>
|
||||||
|
<select data-tip="Culture namesbase. Click to change. Click on arrows to re-generate names" class="cultureBase">${getBaseOptions(
|
||||||
|
c.base
|
||||||
|
)}</select>
|
||||||
<span data-tip="Cells count" class="icon-check-empty hide"></span>
|
<span data-tip="Cells count" class="icon-check-empty hide"></span>
|
||||||
<div data-tip="Cells count" class="cultureCells hide" style="width: 4em">${c.cells}</div>
|
<div data-tip="Cells count" class="cultureCells hide" style="width: 4em">${c.cells}</div>
|
||||||
<span data-tip="Culture expansionism. Defines competitive size" class="icon-resize-full hide"></span>
|
<span data-tip="Culture expansionism. Defines competitive size" class="icon-resize-full hide"></span>
|
||||||
|
|
@ -216,7 +226,9 @@ function culturesEditorAddLines() {
|
||||||
<span data-tip="Culture area" style="padding-right: 4px" class="icon-map-o hide"></span>
|
<span data-tip="Culture area" style="padding-right: 4px" class="icon-map-o hide"></span>
|
||||||
<div data-tip="Culture area" class="cultureArea hide" style="width: 6em">${si(area)} ${unit}</div>
|
<div data-tip="Culture area" class="cultureArea hide" style="width: 6em">${si(area)} ${unit}</div>
|
||||||
<span data-tip="${populationTip}" class="icon-male hide"></span>
|
<span data-tip="${populationTip}" class="icon-male hide"></span>
|
||||||
<div data-tip="${populationTip}" class="culturePopulation hide pointer" style="width: 5em">${si(population)}</div>
|
<div data-tip="${populationTip}" class="culturePopulation hide pointer" style="width: 5em">${si(
|
||||||
|
population
|
||||||
|
)}</div>
|
||||||
<span data-tip="Click to re-generate names for burgs with this culture assigned" class="icon-arrows-cw hide"></span>
|
<span data-tip="Click to re-generate names for burgs with this culture assigned" class="icon-arrows-cw hide"></span>
|
||||||
${getShapeOptions(selectShape, c.shield)}
|
${getShapeOptions(selectShape, c.shield)}
|
||||||
<span data-tip="Remove culture" class="icon-trash-empty hide"></span>
|
<span data-tip="Remove culture" class="icon-trash-empty hide"></span>
|
||||||
|
|
@ -276,12 +288,14 @@ function getShapeOptions(selectShape, selected) {
|
||||||
const shapes = Object.keys(COA.shields.types)
|
const shapes = Object.keys(COA.shields.types)
|
||||||
.map(type => Object.keys(COA.shields[type]))
|
.map(type => Object.keys(COA.shields[type]))
|
||||||
.flat();
|
.flat();
|
||||||
const options = shapes.map(shape => `<option ${shape === selected ? "selected" : ""} value="${shape}">${capitalize(shape)}</option>`);
|
const options = shapes.map(
|
||||||
|
shape => `<option ${shape === selected ? "selected" : ""} value="${shape}">${capitalize(shape)}</option>`
|
||||||
|
);
|
||||||
return `<select data-tip="Emblem shape associated with culture. Click to change" class="cultureEmblems hide">${options}</select>`;
|
return `<select data-tip="Emblem shape associated with culture. Click to change" class="cultureEmblems hide">${options}</select>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function cultureHighlightOn(event) {
|
function cultureHighlightOn(event) {
|
||||||
const culture = +event.target.dataset.id;
|
const culture = Number(event.id || event.target.dataset.id);
|
||||||
const $info = byId("cultureInfo");
|
const $info = byId("cultureInfo");
|
||||||
if ($info) {
|
if ($info) {
|
||||||
d3.select("#hierarchy")
|
d3.select("#hierarchy")
|
||||||
|
|
@ -314,7 +328,7 @@ function cultureHighlightOn(event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function cultureHighlightOff(event) {
|
function cultureHighlightOff(event) {
|
||||||
const culture = +event.target.dataset.id;
|
const culture = Number(event.id || event.target.dataset.id);
|
||||||
const $info = byId("cultureInfo");
|
const $info = byId("cultureInfo");
|
||||||
if ($info) {
|
if ($info) {
|
||||||
d3.select("#hierarchy")
|
d3.select("#hierarchy")
|
||||||
|
|
@ -412,7 +426,14 @@ function cultureChangeEmblemsShape() {
|
||||||
});
|
});
|
||||||
|
|
||||||
pack.provinces.forEach(province => {
|
pack.provinces.forEach(province => {
|
||||||
if (pack.cells.culture[province.center] !== culture || !province.i || province.removed || !province.coa || province.coa === "custom") return;
|
if (
|
||||||
|
pack.cells.culture[province.center] !== culture ||
|
||||||
|
!province.i ||
|
||||||
|
province.removed ||
|
||||||
|
!province.coa ||
|
||||||
|
province.coa === "custom"
|
||||||
|
)
|
||||||
|
return;
|
||||||
if (shape === province.coa.shield) return;
|
if (shape === province.coa.shield) return;
|
||||||
province.coa.shield = shape;
|
province.coa.shield = shape;
|
||||||
rerenderCOA("provinceCOA" + province.i, province.coa);
|
rerenderCOA("provinceCOA" + province.i, province.coa);
|
||||||
|
|
@ -438,8 +459,12 @@ function changePopulation() {
|
||||||
const burgs = pack.burgs.filter(b => !b.removed && b.culture === cultureId);
|
const burgs = pack.burgs.filter(b => !b.removed && b.culture === cultureId);
|
||||||
|
|
||||||
alertMessage.innerHTML = /* html */ `Rural: <input type="number" min="0" step="1" id="ruralPop" value=${rural} style="width:6em" /> Urban:
|
alertMessage.innerHTML = /* html */ `Rural: <input type="number" min="0" step="1" id="ruralPop" value=${rural} style="width:6em" /> Urban:
|
||||||
<input type="number" min="0" step="1" id="urbanPop" value=${urban} style="width:6em" ${burgs.length ? "" : "disabled"} />
|
<input type="number" min="0" step="1" id="urbanPop" value=${urban} style="width:6em" ${
|
||||||
<p>Total population: ${l(total)} ⇒ <span id="totalPop">${l(total)}</span> (<span id="totalPopPerc">100</span>%)</p>`;
|
burgs.length ? "" : "disabled"
|
||||||
|
} />
|
||||||
|
<p>Total population: ${l(total)} ⇒ <span id="totalPop">${l(
|
||||||
|
total
|
||||||
|
)}</span> (<span id="totalPopPerc">100</span>%)</p>`;
|
||||||
|
|
||||||
const update = function () {
|
const update = function () {
|
||||||
const totalNew = ruralPop.valueAsNumber + urbanPop.valueAsNumber;
|
const totalNew = ruralPop.valueAsNumber + urbanPop.valueAsNumber;
|
||||||
|
|
@ -522,9 +547,11 @@ function removeCulture(cultureId) {
|
||||||
});
|
});
|
||||||
cultures[cultureId].removed = true;
|
cultures[cultureId].removed = true;
|
||||||
|
|
||||||
const origin = cultures[cultureId].origin;
|
cultures
|
||||||
cultures.forEach(c => {
|
.filter(c => c.i && !c.removed)
|
||||||
if (c.origin === cultureId) c.origin = origin;
|
.forEach(c => {
|
||||||
|
c.origins = c.origins.filter(origin => origin !== cultureId);
|
||||||
|
if (!c.origins.length) c.origins = [0];
|
||||||
});
|
});
|
||||||
refreshCulturesEditor();
|
refreshCulturesEditor();
|
||||||
}
|
}
|
||||||
|
|
@ -552,7 +579,12 @@ function cultureRemove() {
|
||||||
function drawCultureCenters() {
|
function drawCultureCenters() {
|
||||||
const tooltip = "Drag to move the culture center (ancestral home)";
|
const tooltip = "Drag to move the culture center (ancestral home)";
|
||||||
debug.select("#cultureCenters").remove();
|
debug.select("#cultureCenters").remove();
|
||||||
const cultureCenters = debug.append("g").attr("id", "cultureCenters").attr("stroke-width", 2).attr("stroke", "#444444").style("cursor", "move");
|
const cultureCenters = debug
|
||||||
|
.append("g")
|
||||||
|
.attr("id", "cultureCenters")
|
||||||
|
.attr("stroke-width", 2)
|
||||||
|
.attr("stroke", "#444444")
|
||||||
|
.style("cursor", "move");
|
||||||
|
|
||||||
const data = pack.cultures.filter(c => c.i && !c.removed);
|
const data = pack.cultures.filter(c => c.i && !c.removed);
|
||||||
cultureCenters
|
cultureCenters
|
||||||
|
|
@ -623,17 +655,17 @@ function togglePercentageMode() {
|
||||||
|
|
||||||
function showHierarchy() {
|
function showHierarchy() {
|
||||||
// build hierarchy tree
|
// build hierarchy tree
|
||||||
pack.cultures[0].origin = null;
|
pack.cultures[0].origins = [null];
|
||||||
const validCultures = pack.cultures.filter(c => !c.removed);
|
const validCultures = pack.cultures.filter(c => !c.removed);
|
||||||
if (validCultures.length < 3) return tip("Not enough cultures to show hierarchy", false, "error");
|
if (validCultures.length < 3) return tip("Not enough cultures to show hierarchy", false, "error");
|
||||||
|
|
||||||
const root = d3
|
const root = d3
|
||||||
.stratify()
|
.stratify()
|
||||||
.id(d => d.i)
|
.id(d => d.i)
|
||||||
.parentId(d => d.origin)(validCultures);
|
.parentId(d => d.origins[0])(validCultures);
|
||||||
const treeWidth = root.leaves().length;
|
const treeWidth = root.leaves().length;
|
||||||
const treeHeight = root.height;
|
const treeHeight = root.height;
|
||||||
const width = treeWidth * 40;
|
const width = Math.max(treeWidth * 40, 300);
|
||||||
const height = treeHeight * 60;
|
const height = treeHeight * 60;
|
||||||
|
|
||||||
const margin = {top: 10, right: 10, bottom: -5, left: 10};
|
const margin = {top: 10, right: 10, bottom: -5, left: 10};
|
||||||
|
|
@ -649,39 +681,59 @@ function showHierarchy() {
|
||||||
.attr("id", "hierarchy")
|
.attr("id", "hierarchy")
|
||||||
.attr("width", width)
|
.attr("width", width)
|
||||||
.attr("height", height)
|
.attr("height", height)
|
||||||
.style("text-anchor", "middle");
|
.style("text-anchor", "middle")
|
||||||
|
.style("min-width", "300px");
|
||||||
const graph = svg.append("g").attr("transform", `translate(10, -45)`);
|
const graph = svg.append("g").attr("transform", `translate(10, -45)`);
|
||||||
const links = graph.append("g").attr("fill", "none").attr("stroke", "#aaaaaa");
|
const links = graph.append("g").attr("fill", "none").attr("stroke", "#aaaaaa");
|
||||||
|
const primaryLinks = links.append("g");
|
||||||
|
const secondaryLinks = links.append("g").attr("stroke-dasharray", 1);
|
||||||
const nodes = graph.append("g");
|
const nodes = graph.append("g");
|
||||||
|
|
||||||
|
// render helper functions
|
||||||
|
const getLinkPath = d => {
|
||||||
|
const {
|
||||||
|
source: {x: sx, y: sy},
|
||||||
|
target: {x: tx, y: ty}
|
||||||
|
} = d;
|
||||||
|
return `M${sx},${sy} C${sx},${(sy * 3 + ty) / 4} ${tx},${(sy * 2 + ty) / 3} ${tx},${ty}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSecondaryLinks = root => {
|
||||||
|
const nodes = root.descendants();
|
||||||
|
const links = [];
|
||||||
|
|
||||||
|
for (const node of nodes) {
|
||||||
|
const origins = node.data.origins;
|
||||||
|
if (node.depth < 2) continue;
|
||||||
|
|
||||||
|
for (let i = 1; i < origins.length; i++) {
|
||||||
|
const source = nodes.find(n => n.data.i === origins[i]);
|
||||||
|
if (source) links.push({source, target: node});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return links;
|
||||||
|
};
|
||||||
|
|
||||||
|
const nodePathMap = {
|
||||||
|
undefined: "M5,0A5,5,0,1,1,-5,0A5,5,0,1,1,5,0", // small circle
|
||||||
|
Generic: "M11.3,0A11.3,11.3,0,1,1,-11.3,0A11.3,11.3,0,1,1,11.3,0", // circle
|
||||||
|
River: "M0,-14L14,0L0,14L-14,0Z", // diamond
|
||||||
|
Lake: "M-6.5,-11.26l13,0l6.5,11.26l-6.5,11.26l-13,0l-6.5,-11.26Z", // hexagon
|
||||||
|
Naval: "M-11,-11h22v22h-22Z", // square
|
||||||
|
Highland: "M-11,-11l11,2l11,-2l-2,11l2,11l-11,-2l-11,2l2,-11Z", // concave square
|
||||||
|
Nomadic: "M-4.97,-12.01 l9.95,0 l7.04,7.04 l0,9.95 l-7.04,7.04 l-9.95,0 l-7.04,-7.04 l0,-9.95Z", // octagon
|
||||||
|
Hunting: "M0,-14l14,11l-6,14h-16l-6,-14Z" // pentagon
|
||||||
|
};
|
||||||
|
|
||||||
|
const getNodePath = d => nodePathMap[d.data.type];
|
||||||
|
|
||||||
renderTree();
|
renderTree();
|
||||||
function renderTree() {
|
function renderTree() {
|
||||||
treeLayout(root);
|
treeLayout(root);
|
||||||
links
|
|
||||||
.selectAll("path")
|
primaryLinks.selectAll("path").data(root.links()).enter().append("path").attr("d", getLinkPath);
|
||||||
.data(root.links())
|
secondaryLinks.selectAll("path").data(getSecondaryLinks(root)).enter().append("path").attr("d", getLinkPath);
|
||||||
.enter()
|
|
||||||
.append("path")
|
|
||||||
.attr("d", d => {
|
|
||||||
return (
|
|
||||||
"M" +
|
|
||||||
d.source.x +
|
|
||||||
"," +
|
|
||||||
d.source.y +
|
|
||||||
"C" +
|
|
||||||
d.source.x +
|
|
||||||
"," +
|
|
||||||
(d.source.y * 3 + d.target.y) / 4 +
|
|
||||||
" " +
|
|
||||||
d.target.x +
|
|
||||||
"," +
|
|
||||||
(d.source.y * 2 + d.target.y) / 3 +
|
|
||||||
" " +
|
|
||||||
d.target.x +
|
|
||||||
"," +
|
|
||||||
d.target.y
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
const node = nodes
|
const node = nodes
|
||||||
.selectAll("g")
|
.selectAll("g")
|
||||||
|
|
@ -691,42 +743,29 @@ function showHierarchy() {
|
||||||
.attr("data-id", d => d.data.i)
|
.attr("data-id", d => d.data.i)
|
||||||
.attr("stroke", "#333333")
|
.attr("stroke", "#333333")
|
||||||
.attr("transform", d => `translate(${d.x}, ${d.y})`)
|
.attr("transform", d => `translate(${d.x}, ${d.y})`)
|
||||||
.on("mouseenter", () => cultureHighlightOn(event))
|
.on("mouseenter", cultureHighlightOn)
|
||||||
.on("mouseleave", () => cultureHighlightOff(event))
|
.on("mouseleave", cultureHighlightOff)
|
||||||
.call(d3.drag().on("start", d => dragToReorigin(d)));
|
.call(d3.drag().on("start", dragToReorigin));
|
||||||
|
|
||||||
node
|
node
|
||||||
.append("path")
|
.append("path")
|
||||||
.attr("d", d => {
|
.attr("d", getNodePath)
|
||||||
if (!d.data.i) return "M5,0A5,5,0,1,1,-5,0A5,5,0,1,1,5,0";
|
.attr("fill", d => d.data.color || "#ffffff")
|
||||||
// small circle
|
|
||||||
else if (d.data.type === "Generic") return "M11.3,0A11.3,11.3,0,1,1,-11.3,0A11.3,11.3,0,1,1,11.3,0";
|
|
||||||
// circle
|
|
||||||
else if (d.data.type === "River") return "M0,-14L14,0L0,14L-14,0Z";
|
|
||||||
// diamond
|
|
||||||
else if (d.data.type === "Lake") return "M-6.5,-11.26l13,0l6.5,11.26l-6.5,11.26l-13,0l-6.5,-11.26Z";
|
|
||||||
// hexagon
|
|
||||||
else if (d.data.type === "Naval") return "M-11,-11h22v22h-22Z"; // square
|
|
||||||
if (d.data.type === "Highland") return "M-11,-11l11,2l11,-2l-2,11l2,11l-11,-2l-11,2l2,-11Z"; // concave square
|
|
||||||
if (d.data.type === "Nomadic") return "M-4.97,-12.01 l9.95,0 l7.04,7.04 l0,9.95 l-7.04,7.04 l-9.95,0 l-7.04,-7.04 l0,-9.95Z"; // octagon
|
|
||||||
if (d.data.type === "Hunting") return "M0,-14l14,11l-6,14h-16l-6,-14Z"; // pentagon
|
|
||||||
return "M-11,-11h22v22h-22Z"; // square
|
|
||||||
})
|
|
||||||
.attr("fill", d => (d.data.i ? d.data.color : "#ffffff"))
|
|
||||||
.attr("stroke-dasharray", d => (d.data.cells ? "null" : "1"));
|
.attr("stroke-dasharray", d => (d.data.cells ? "null" : "1"));
|
||||||
|
|
||||||
node
|
node
|
||||||
.append("text")
|
.append("text")
|
||||||
.attr("dy", ".35em")
|
.attr("dy", ".35em")
|
||||||
.text(d => (d.data.i ? d.data.code : ""));
|
.text(d => d.data.code || "");
|
||||||
}
|
}
|
||||||
|
|
||||||
$("#alert").dialog({
|
$("#alert").dialog({
|
||||||
title: "Cultures tree",
|
title: "Cultures tree",
|
||||||
width: fitContent(),
|
width: fitContent(),
|
||||||
|
minWidth: "20vw",
|
||||||
resizable: false,
|
resizable: false,
|
||||||
position: {my: "left center", at: "left+10 center", of: "svg"},
|
position: {my: "left center", at: "left+10 center", of: "svg"},
|
||||||
buttons: {},
|
buttons: null,
|
||||||
close: () => {
|
close: () => {
|
||||||
alertMessage.innerHTML = "";
|
alertMessage.innerHTML = "";
|
||||||
}
|
}
|
||||||
|
|
@ -745,14 +784,17 @@ function showHierarchy() {
|
||||||
originLine.remove();
|
originLine.remove();
|
||||||
const selected = graph.select("path.selected");
|
const selected = graph.select("path.selected");
|
||||||
if (!selected.size()) return;
|
if (!selected.size()) return;
|
||||||
const culture = d.data.i;
|
const cultureId = d.data.i;
|
||||||
const oldOrigin = d.data.origin;
|
let newOrigin = Number(selected.datum().data.i);
|
||||||
let newOrigin = selected.datum().data.i;
|
if (cultureId === newOrigin) return; // dragged to itself
|
||||||
if (newOrigin == oldOrigin) return; // already a child of the selected node
|
if (d.data.origins.includes(newOrigin)) return; // already a child of the selected node
|
||||||
if (newOrigin == culture) newOrigin = 0; // move to top
|
if (newOrigin && d.descendants().some(node => node.id === newOrigin)) return; // cannot be a child of its own child
|
||||||
if (newOrigin && d.descendants().some(node => node.id == newOrigin)) return; // cannot be a child of its own child
|
|
||||||
pack.cultures[culture].origin = d.data.origin = newOrigin; // change data
|
const culture = pack.cultures[cultureId];
|
||||||
showHierarchy(); // update hierarchy
|
if (culture.origins[0] === 0) culture.origins = [];
|
||||||
|
culture.origins.push(newOrigin);
|
||||||
|
|
||||||
|
showHierarchy();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -853,7 +895,13 @@ function changeCultureForSelection(selection) {
|
||||||
// change of append new element
|
// change of append new element
|
||||||
if (exists.size()) exists.attr("data-culture", cultureNew).attr("fill", color).attr("stroke", color);
|
if (exists.size()) exists.attr("data-culture", cultureNew).attr("fill", color).attr("stroke", color);
|
||||||
else
|
else
|
||||||
temp.append("polygon").attr("data-cell", i).attr("data-culture", cultureNew).attr("points", getPackPolygon(i)).attr("fill", color).attr("stroke", color);
|
temp
|
||||||
|
.append("polygon")
|
||||||
|
.attr("data-cell", i)
|
||||||
|
.attr("data-culture", cultureNew)
|
||||||
|
.attr("points", getPackPolygon(i))
|
||||||
|
.attr("fill", color)
|
||||||
|
.attr("stroke", color);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -921,7 +969,8 @@ function addCulture() {
|
||||||
const point = d3.mouse(this);
|
const point = d3.mouse(this);
|
||||||
const center = findCell(point[0], point[1]);
|
const center = findCell(point[0], point[1]);
|
||||||
|
|
||||||
if (pack.cells.h[center] < 20) return tip("You cannot place culture center into the water. Please click on a land cell", false, "error");
|
if (pack.cells.h[center] < 20)
|
||||||
|
return tip("You cannot place culture center into the water. Please click on a land cell", false, "error");
|
||||||
const occupied = pack.cultures.some(c => !c.removed && c.center === center);
|
const occupied = pack.cultures.some(c => !c.removed && c.center === center);
|
||||||
if (occupied) return tip("This cell is already a culture center. Please select a different cell", false, "error");
|
if (occupied) return tip("This cell is already a culture center. Please select a different cell", false, "error");
|
||||||
|
|
||||||
|
|
@ -989,7 +1038,7 @@ async function uploadCulturesData() {
|
||||||
|
|
||||||
current.color = c.color;
|
current.color = c.color;
|
||||||
current.expansionism = +c.expansionism;
|
current.expansionism = +c.expansionism;
|
||||||
current.origin = +c.origin;
|
current.origins = JSON.parse(c.origins);
|
||||||
|
|
||||||
if (cultureTypes.includes(c.type)) current.type = c.type;
|
if (cultureTypes.includes(c.type)) current.type = c.type;
|
||||||
else current.type = "Generic";
|
else current.type = "Generic";
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
// version and caching control
|
// version and caching control
|
||||||
|
|
||||||
const version = "1.85.02"; // generator version, update each time
|
const version = "1.86.00"; // generator version, update each time
|
||||||
|
|
||||||
{
|
{
|
||||||
document.title += " v" + version;
|
document.title += " v" + version;
|
||||||
|
|
@ -28,6 +28,7 @@ const version = "1.85.02"; // generator version, update each time
|
||||||
|
|
||||||
<ul>
|
<ul>
|
||||||
<strong>Latest changes:</strong>
|
<strong>Latest changes:</strong>
|
||||||
|
<li>Hierarchy tree: cultures and religions can have multiple parents</li>
|
||||||
<li>Heightmap selection screen</li>
|
<li>Heightmap selection screen</li>
|
||||||
<li>Dialogs optimization for mobile</li>
|
<li>Dialogs optimization for mobile</li>
|
||||||
<li>New heightmap template: Fractious</li>
|
<li>New heightmap template: Fractious</li>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue