latest version

This commit is contained in:
howlingsails 2022-08-16 18:26:34 -07:00
parent 41432ac944
commit c477c8dfcd
36 changed files with 3979 additions and 832 deletions

BIN
_maps/.DS_Store vendored Normal file

Binary file not shown.

BIN
_maps/working/.DS_Store vendored Normal file

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
[{"name":"Wildlands","i":0,"base":1,"origin":null,"shield":"round"},{"name":"Dhommeam (Dwarf)","base":35,"shield":"erebor","center":2793,"i":1,"color":"#ffed6f","type":"Highland","expansionism":0.5,"origin":0,"code":"DD"},{"name":"Dunirr (Dwarf)","base":35,"shield":"ironHills","center":5754,"i":2,"color":"#dababf","type":"Generic","expansionism":0.1,"origin":0,"code":"DU"},{"name":"Mudtoe (Dwarf)","base":35,"shield":"erebor","center":183,"i":3,"color":"#ee4395","type":"Highland","expansionism":0.4,"origin":0,"code":"MD"},{"name":"Khazadur (Dwarf)","base":35,"shield":"erebor","center":3542,"i":4,"color":"#eb8de7","type":"Highland","expansionism":0.5,"origin":0,"code":"KD"},{"name":"Yotunn (Giant)","base":38,"shield":"pavise","center":702,"i":5,"color":"#80b1d3","type":"Hunting","expansionism":0.6,"origin":0,"code":"YG"},{"name":"Shazgob (Orc)","base":37,"shield":"moriaOrc","center":5639,"i":6,"color":"#fccde5","type":"Hunting","expansionism":0.7,"origin":0,"code":"SO"},{"name":"Gul (Orc)","base":37,"shield":"moriaOrc","center":6040,"i":7,"color":"#2f96e0","type":"Highland","expansionism":0.5,"origin":0,"code":"GO"},{"name":"Gongrem (Dwarf)","base":35,"shield":"erebor","center":2190,"i":8,"color":"#b3de69","type":"Highland","expansionism":0.3,"origin":0,"code":"GD"},{"name":"Gralcek (Human)","base":16,"shield":"round","center":3704,"i":9,"color":"#bc80bd","type":"Generic","expansionism":0.1,"origin":0,"code":"GH"},{"name":"Fruthos (Human)","base":32,"shield":"fantasy5","center":6517,"i":10,"color":"#ccebc5","type":"River","expansionism":0.4,"origin":0,"code":"FH"},{"name":"Rulor (Human)","base":32,"shield":"roman","center":2763,"i":11,"color":"#ff8c38","type":"Lake","expansionism":0.4,"origin":0,"code":"RH"},{"name":"Mazoga (Orc)","base":37,"shield":"moriaOrc","center":5278,"i":12,"color":"#6e40aa","type":"Highland","expansionism":0.5,"origin":0,"code":"MO"},{"name":"Heenzurm (Goblin)","base":36,"shield":"moriaOrc","center":3211,"i":13,"color":"#28ea8d","type":"Hunting","expansionism":0.9,"origin":0,"code":"HG"},{"name":"Zildud (Orc)","base":37,"shield":"moriaOrc","center":249,"i":14,"color":"#aff05b","type":"Highland","expansionism":0.5,"origin":0,"code":"ZO"},{"name":"Ginikirr (Dwarf)","base":35,"shield":"erebor","center":2794,"i":15,"color":"#8dd3c7","type":"Highland","expansionism":0.4,"origin":0,"code":"GI"},{"name":"Lothian (Elf)","base":33,"shield":"wedged","center":3477,"i":16,"color":"#c6b9c1","type":"River","expansionism":0.4,"origin":0,"code":"LE"},{"name":"Zakaos (Giant)","base":38,"shield":"pavise","center":439,"i":17,"color":"#fdb462","type":"Generic","expansionism":0.1,"origin":0,"code":"ZG"},{"name":"Lagakh (Orc)","base":37,"shield":"urukHai","center":2558,"i":18,"color":"#fb8072","type":"Highland","expansionism":0.5,"origin":0,"code":"LO"}]

View file

@ -0,0 +1 @@
[{"name":"Wildlands","i":0,"base":1,"origin":null,"shield":"round"},{"name":"Dhommeam (Dwarf)","base":35,"shield":"erebor","center":2793,"i":1,"color":"#ffed6f","type":"Highland","expansionism":0.5,"origin":0,"code":"DD"},{"name":"Dunirr (Dwarf)","base":35,"shield":"ironHills","center":5759,"i":2,"color":"#dababf","type":"Generic","expansionism":0.1,"origin":0,"code":"DU"},{"name":"Mudtoe (Dwarf)","base":35,"shield":"erebor","center":603,"i":3,"color":"#ee4395","type":"Highland","expansionism":0.4,"origin":0,"code":"MD"},{"name":"Khazadur (Dwarf)","base":35,"shield":"erebor","center":2608,"i":4,"color":"#eb8de7","type":"Nomadic","expansionism":1.4,"origin":0,"code":"KD"},{"name":"Yotunn (Giant)","base":38,"shield":"pavise","center":701,"i":5,"color":"#80b1d3","type":"River","expansionism":0.3,"origin":0,"code":"YG"},{"name":"Shazgob (Orc)","base":37,"shield":"moriaOrc","center":5644,"i":6,"color":"#fccde5","type":"Hunting","expansionism":0.7,"origin":0,"code":"SO"},{"name":"Gul (Orc)","base":37,"shield":"moriaOrc","center":6045,"i":7,"color":"#2f96e0","type":"Highland","expansionism":0.5,"origin":0,"code":"GO"},{"name":"Gongrem (Dwarf)","base":35,"shield":"erebor","center":2190,"i":8,"color":"#b3de69","type":"Highland","expansionism":0.3,"origin":0,"code":"GD"},{"name":"Gralcek (Human)","base":16,"shield":"round","center":3922,"i":9,"color":"#bc80bd","type":"Hunting","expansionism":0.8,"origin":0,"code":"GH"},{"name":"Fruthos (Human)","base":32,"shield":"fantasy5","center":6399,"i":10,"color":"#ccebc5","type":"Naval","expansionism":0.6,"origin":0,"code":"FH"},{"name":"Rulor (Human)","base":32,"shield":"roman","center":2632,"i":11,"color":"#ff8c38","type":"Nomadic","expansionism":0.9,"origin":0,"code":"RH"},{"name":"Mazoga (Orc)","base":37,"shield":"moriaOrc","center":4222,"i":12,"color":"#6e40aa","type":"Hunting","expansionism":0.9,"origin":0,"code":"MO"},{"name":"Heenzurm (Goblin)","base":36,"shield":"moriaOrc","center":3211,"i":13,"color":"#28ea8d","type":"Hunting","expansionism":0.9,"origin":0,"code":"HG"},{"name":"Zildud (Orc)","base":37,"shield":"moriaOrc","center":5769,"i":14,"color":"#aff05b","type":"Highland","expansionism":0.5,"origin":0,"code":"ZO"},{"name":"Ginikirr (Dwarf)","base":35,"shield":"erebor","center":2794,"i":15,"color":"#8dd3c7","type":"Highland","expansionism":0.4,"origin":0,"code":"GI"},{"name":"Lothian (Elf)","base":33,"shield":"wedged","center":3480,"i":16,"color":"#c6b9c1","type":"River","expansionism":0.4,"origin":0,"code":"LE"},{"name":"Zakaos (Giant)","base":38,"shield":"pavise","center":417,"i":17,"color":"#fdb462","type":"Nomadic","expansionism":1.2,"origin":0,"code":"ZG"},{"name":"Lagakh (Orc)","base":37,"shield":"urukHai","center":2558,"i":18,"color":"#fb8072","type":"Highland","expansionism":0.5,"origin":0,"code":"LO"}]

View file

@ -0,0 +1 @@
[{"name":"Wildlands","i":0,"base":1,"origin":null,"shield":"round"},{"name":"Dhommeam (Dwarf)","base":35,"shield":"erebor","center":2793,"i":1,"color":"#ffed6f","type":"Highland","expansionism":0.5,"origin":0,"code":"DD"},{"name":"Dunirr (Dwarf)","base":35,"shield":"ironHills","center":5754,"i":2,"color":"#dababf","type":"Generic","expansionism":0.1,"origin":0,"code":"DU"},{"name":"Mudtoe (Dwarf)","base":35,"shield":"erebor","center":183,"i":3,"color":"#ee4395","type":"Highland","expansionism":0.4,"origin":0,"code":"MD"},{"name":"Khazadur (Dwarf)","base":35,"shield":"erebor","center":3542,"i":4,"color":"#eb8de7","type":"Highland","expansionism":0.5,"origin":0,"code":"KD"},{"name":"Yotunn (Giant)","base":38,"shield":"pavise","center":702,"i":5,"color":"#80b1d3","type":"Hunting","expansionism":0.6,"origin":0,"code":"YG"},{"name":"Shazgob (Orc)","base":37,"shield":"moriaOrc","center":5639,"i":6,"color":"#fccde5","type":"Hunting","expansionism":0.7,"origin":0,"code":"SO"},{"name":"Gul (Orc)","base":37,"shield":"moriaOrc","center":6040,"i":7,"color":"#2f96e0","type":"Highland","expansionism":0.5,"origin":0,"code":"GO"},{"name":"Gongrem (Dwarf)","base":35,"shield":"erebor","center":2190,"i":8,"color":"#b3de69","type":"Highland","expansionism":0.3,"origin":0,"code":"GD"},{"name":"Gralcek (Human)","base":16,"shield":"round","center":3704,"i":9,"color":"#bc80bd","type":"Generic","expansionism":0.1,"origin":0,"code":"GH"},{"name":"Fruthos (Human)","base":32,"shield":"fantasy5","center":6517,"i":10,"color":"#ccebc5","type":"River","expansionism":0.4,"origin":0,"code":"FH"},{"name":"Rulor (Human)","base":32,"shield":"roman","center":2763,"i":11,"color":"#ff8c38","type":"Lake","expansionism":0.4,"origin":0,"code":"RH"},{"name":"Mazoga (Orc)","base":37,"shield":"moriaOrc","center":5278,"i":12,"color":"#6e40aa","type":"Highland","expansionism":0.5,"origin":0,"code":"MO"},{"name":"Heenzurm (Goblin)","base":36,"shield":"moriaOrc","center":3211,"i":13,"color":"#28ea8d","type":"Hunting","expansionism":0.9,"origin":0,"code":"HG"},{"name":"Zildud (Orc)","base":37,"shield":"moriaOrc","center":249,"i":14,"color":"#aff05b","type":"Highland","expansionism":0.5,"origin":0,"code":"ZO"},{"name":"Ginikirr (Dwarf)","base":35,"shield":"erebor","center":2794,"i":15,"color":"#8dd3c7","type":"Highland","expansionism":0.4,"origin":0,"code":"GI"},{"name":"Lothian (Elf)","base":33,"shield":"wedged","center":3477,"i":16,"color":"#c6b9c1","type":"River","expansionism":0.4,"origin":0,"code":"LE"},{"name":"Zakaos (Giant)","base":38,"shield":"pavise","center":439,"i":17,"color":"#fdb462","type":"Generic","expansionism":0.1,"origin":0,"code":"ZG"},{"name":"Lagakh (Orc)","base":37,"shield":"urukHai","center":2558,"i":18,"color":"#fb8072","type":"Highland","expansionism":0.5,"origin":0,"code":"LO"}]

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1 @@
undefined

View file

@ -0,0 +1 @@
undefined

View file

@ -0,0 +1 @@
undefined

View file

@ -0,0 +1 @@
530

View file

@ -0,0 +1 @@
530

View file

@ -0,0 +1 @@
530

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -85,7 +85,7 @@ a {
} }
#prec text { #prec text {
font-size: 32px; font-size: 24px;
stroke: none; stroke: none;
text-shadow: 1px 1px 1px #9daac9; text-shadow: 1px 1px 1px #9daac9;
user-select: none; user-select: none;
@ -297,12 +297,17 @@ i.icon-lock {
} }
} }
#provinceLabels,
#burgLabels { #burgLabels {
dominant-baseline: alphabetic; dominant-baseline: alphabetic;
text-anchor: middle; text-anchor: middle;
font-size: 0.1em;
}
#provinceLabels {
dominant-baseline: alphabetic;
text-anchor: leftOver;
font-size: 0.2em;
text-shadow: 1px 1px #e7ffa1;
} }
#routeLength, #routeLength,
#coastlineArea { #coastlineArea {
background-color: #eeeeee; background-color: #eeeeee;

View file

@ -1046,7 +1046,7 @@
<option value="english" data-max="10">English</option> <option value="english" data-max="10">English</option>
<option value="antique" data-max="10">Antique</option> <option value="antique" data-max="10">Antique</option>
<option value="highFantasy" data-max="17">High Fantasy</option> <option value="highFantasy" data-max="17">High Fantasy</option>
<option value="darkFantasy" data-max="18">Dark Fantasy</option> <option value="darkFantasy" data-max="32">Dark Fantasy</option>
<option value="random" data-max="100">Random</option> <option value="random" data-max="100">Random</option>
</select> </select>
</td> </td>

File diff suppressed because it is too large Load diff

View file

@ -11,34 +11,65 @@ window.Markers = (function () {
return [ return [
{ type: "volcanoes", icon: "🌋", multiplier: 10, fn: addVolcanoes }, { type: "volcanoes", icon: "🌋", multiplier: 10, fn: addVolcanoes },
{ type: "hot-springs", icon: "♨️", multiplier: 20, fn: addHotSprings }, { type: "hot-springs", icon: "♨️", multiplier: 20, fn: addHotSprings },
{type: "mines", icon: "⛏️", multiplier: 5, fn: addMines}, { type: "mines", icon: "⛏️", multiplier: 50, fn: addMines },
{type: "bridges", icon: "🌉", multiplier: 10, fn: addBridges}, { type: "gem-mines", icon: "💎", multiplier: 3, fn: addGemMines },
{ type: "bridges", icon: "🌉", multiplier: 40, fn: addBridges },
{ type: "inns", icon: "🍻", multiplier: 20, fn: addInns }, { type: "inns", icon: "🍻", multiplier: 20, fn: addInns },
{ type: "lighthouses", icon: "🚨", multiplier: 1, fn: addLighthouses }, { type: "lighthouses", icon: "🚨", multiplier: 1, fn: addLighthouses },
{type: "waterfalls", icon: "⟱", multiplier: 3, fn: addWaterfalls}, { type: "waterfalls", icon: "💦", multiplier: 1, fn: addWaterfalls },
{type: "battlefields", icon: "⚔️", multiplier: 1, fn: addBattlefields}, { type: "battlefields", icon: "⚔️", multiplier: 3, fn: addBattlefields },
{type: "dungeons", icon: "🗝️", multiplier: 20, fn: addDungeons}, { type: "dungeons", icon: "🗝️", multiplier: 5, fn: addDungeons },
{type: "lake-monsters", icon: "🐉", multiplier: 2, fn: addLakeMonsters}, { type: "lake-monsters", icon: "🐉", multiplier: 8, fn: addLakeMonsters },
{type: "sea-monsters", icon: "🦑", multiplier: 5, fn: addSeaMonsters}, { type: "sea-monsters", icon: "🦑", multiplier: 150, fn: addSeaMonsters },
{type: "hill-monsters", icon: "👹", multiplier: 20, fn: addHillMonsters}, {
{type: "sacred-mountains", icon: "🗻", multiplier: 5, fn: addSacredMountains}, type: "hill-monsters",
{type: "sacred-forests", icon: "🌳", multiplier: 10, fn: addSacredForests}, icon: "👹",
{type: "sacred-pineries", icon: "🌲", multiplier: 20, fn: addSacredPineries}, multiplier: 20,
{type: "sacred-palm-groves", icon: "🌴", multiplier: 1, fn: addSacredPalmGroves}, fn: addHillMonsters,
},
{
type: "sacred-mountains",
icon: "🗻",
multiplier: 5,
fn: addSacredMountains,
},
{
type: "sacred-forests",
icon: "🌳",
multiplier: 10,
fn: addSacredForests,
},
{
type: "sacred-pineries",
icon: "🌲",
multiplier: 10,
fn: addSacredPineries,
},
{
type: "sacred-palm-groves",
icon: "🌴",
multiplier: 5,
fn: addSacredPalmGroves,
},
{ type: "brigands", icon: "💰", multiplier: 5, fn: addBrigands }, { type: "brigands", icon: "💰", multiplier: 5, fn: addBrigands },
{type: "pirates", icon: "🏴‍☠️", multiplier: 3, fn: addPirates}, { type: "pirates", icon: "🏴‍☠️", multiplier: 15, fn: addPirates },
{ type: "statues", icon: "🗿", multiplier: 5, fn: addStatues }, { type: "statues", icon: "🗿", multiplier: 5, fn: addStatues },
{ type: "ruines", icon: "🏺", multiplier: 10, fn: addRuines }, { type: "ruines", icon: "🏺", multiplier: 10, fn: addRuines },
{type: "spiders", icon: "🕷️", multiplier: 25, fn: addHillMonsters}, { type: "spiders", icon: "🕷️", multiplier: 45, fn: addSpiders },
{type: "giant goat heard", icon: "🐐", multiplier: 25, fn: addHillMonsters}, {
{type: "citadel", icon: "🏯", multiplier: 25, fn: addSacredPineries}, type: "giant goat heard",
{type: "portals", icon: "🌀", multiplier: 4, fn: addPortals} icon: "🐐",
multiplier: 25,
fn: addGoatHeard,
},
{ type: "citadel", icon: "🏯", multiplier: 10, fn: addSacredCitidel },
{ type: "portals", icon: "🌀", multiplier: 3, fn: addPortals },
]; ];
} }
const getConfig = () => config; const getConfig = () => config;
const setConfig = newConfig => { const setConfig = (newConfig) => {
config = newConfig; config = newConfig;
}; };
@ -56,7 +87,7 @@ window.Markers = (function () {
} }
const id = `marker${i}`; const id = `marker${i}`;
document.getElementById(id)?.remove(); document.getElementById(id)?.remove();
const index = notes.findIndex(note => note.id === id); const index = notes.findIndex((note) => note.id === id);
if (index != -1) notes.splice(index, 1); if (index != -1) notes.splice(index, 1);
return false; return false;
}); });
@ -114,7 +145,11 @@ window.Markers = (function () {
function addVolcanoes(type, icon, multiplier) { function addVolcanoes(type, icon, multiplier) {
const { cells } = pack; const { cells } = pack;
let mountains = Array.from(cells.i.filter(i => !occupied[i] && cells.h[i] >= 70).sort((a, b) => cells.h[b] - cells.h[a])); let mountains = Array.from(
cells.i
.filter((i) => !occupied[i] && cells.h[i] >= 70)
.sort((a, b) => cells.h[b] - cells.h[a])
);
let quantity = getQuantity(mountains, 10, 500, multiplier); let quantity = getQuantity(mountains, 10, 500, multiplier);
if (!quantity) return; if (!quantity) return;
@ -122,8 +157,16 @@ window.Markers = (function () {
const [cell] = extractAnyElement(mountains); const [cell] = extractAnyElement(mountains);
const id = addMarker({ cell, icon, type, dx: 52, px: 13 }); const id = addMarker({ cell, icon, type, dx: 52, px: 13 });
const proper = Names.getCulture(cells.culture[cell]); const proper = Names.getCulture(cells.culture[cell]);
const name = P(0.3) ? "Mount " + proper : Math.random() > 0.3 ? proper + " Volcano" : proper; const name = P(0.3)
notes.push({id, name, legend: `Active volcano. Height: ${getFriendlyHeight(cells.p[cell])}`}); ? "Mount " + proper
: Math.random() > 0.3
? proper + " Volcano"
: proper;
notes.push({
id,
name,
legend: `Active volcano. Height: ${getFriendlyHeight(cells.p[cell])}`,
});
quantity--; quantity--;
} }
} }
@ -131,7 +174,11 @@ window.Markers = (function () {
function addHotSprings(type, icon, multiplier) { function addHotSprings(type, icon, multiplier) {
const { cells } = pack; const { cells } = pack;
let springs = Array.from(cells.i.filter(i => !occupied[i] && cells.h[i] > 50).sort((a, b) => cells.h[b] - cells.h[a])); let springs = Array.from(
cells.i
.filter((i) => !occupied[i] && cells.h[i] > 50)
.sort((a, b) => cells.h[b] - cells.h[a])
);
let quantity = getQuantity(springs, 30, 1200, multiplier); let quantity = getQuantity(springs, 30, 1200, multiplier);
if (!quantity) return; if (!quantity) return;
@ -140,7 +187,11 @@ window.Markers = (function () {
const id = addMarker({ cell, icon, type, dy: 52 }); const id = addMarker({ cell, icon, type, dy: 52 });
const proper = Names.getCulture(cells.culture[cell]); const proper = Names.getCulture(cells.culture[cell]);
const temp = convertTemperature(gauss(35, 15, 20, 100)); const temp = convertTemperature(gauss(35, 15, 20, 100));
notes.push({id, name: proper + " Hot Springs", legend: `A hot springs area. Average temperature: ${temp}`}); notes.push({
id,
name: proper + " Hot Springs",
legend: `A hot springs area. Average temperature: ${temp}`,
});
quantity--; quantity--;
} }
} }
@ -148,11 +199,22 @@ window.Markers = (function () {
function addMines(type, icon, multiplier) { function addMines(type, icon, multiplier) {
const { cells } = pack; const { cells } = pack;
let hillyBurgs = Array.from(cells.i.filter(i => !occupied[i] && cells.h[i] > 47 && cells.burg[i])); let hillyBurgs = Array.from(
cells.i.filter((i) => !occupied[i] && cells.h[i] > 47 && cells.burg[i])
);
let quantity = getQuantity(hillyBurgs, 1, 15, multiplier); let quantity = getQuantity(hillyBurgs, 1, 15, multiplier);
if (!quantity) return; if (!quantity) return;
const resources = {salt: 5, gold: 2, silver: 4, copper: 2, iron: 3, lead: 1, tin: 1}; const resources = {
salt: 5,
coal: 7,
gold: 2,
silver: 4,
copper: 2,
iron: 3,
lead: 1,
tin: 1,
};
while (quantity && hillyBurgs.length) { while (quantity && hillyBurgs.length) {
const [cell] = extractAnyElement(hillyBurgs); const [cell] = extractAnyElement(hillyBurgs);
@ -167,12 +229,205 @@ window.Markers = (function () {
} }
} }
function addGemMines(type, icon, multiplier) {
const { cells } = pack;
let hillyBurgs = Array.from(
cells.i.filter((i) => !occupied[i] && cells.h[i] > 67)
);
let quantity = getQuantity(hillyBurgs, 1, 15, multiplier);
if (!quantity) return;
const resources = {
Agni_mani: 1,
Alamandine: 1,
Alestone: 1,
Alexandrite: 1,
Algae: 1,
Amaratha: 1,
Amber: 1,
Amethyst: 1,
Andar: 1,
Aquamarine: 1,
Aradite: 1,
Augelite: 1,
Aventurine: 1,
Azurite: 1,
Banded_agate: 1,
Beljuril: 1,
Beryl: 1,
Black_opal: 1,
Black_pearl: 1,
Black_sapphire: 1,
Bloodstone: 1,
Blue_quartz: 1,
Blue_sapphire: 1,
Blue_spinel: 1,
Bluestone: 1,
Boakhar: 1,
Brandeen: 1,
Carbuncle: 1,
Carnelian: 1,
Chalcedony: 1,
Chrysoberyl: 1,
Chrysocolla: 1,
Chrysolite: 1,
Chrysoprase: 1,
Citrine: 1,
Cleiophane: 1,
Coral: 1,
Corstal: 1,
Corundum: 1,
Crown_of_silver: 1,
Cymophane: 1,
Datcha: 1,
Demontoid: 1,
Diamond: 1,
Diopside: 1,
Dioptase: 1,
Disthene: 1,
Emerald: 1,
Epidote: 1,
Essonite: 1,
Euclase: 1,
Eye_agate: 1,
Fire_agate: 1,
Fire_opal: 1,
Flamedance: 1,
Fluorite: 1,
Frost_agate: 1,
Garnet: 1,
Goldline: 1,
Greenstone: 1,
Hambergyle: 1,
Hematite: 1,
Hyacinth: 1,
Hyaline: 1,
Hyalite: 1,
Hydrophane: 1,
Hypersthene: 1,
Idicolite: 1,
Iolite: 1,
Irtios: 1,
Jacinth: 1,
Jade: 1,
Jargoon: 1,
Jasmal: 1,
Jasper: 1,
Jet: 1,
Kings_tears: 1,
Kornerupine: 1,
Kunzite: 1,
Laerals_Tears: 1,
Lapis_lazuli: 1,
Luriyl: 1,
Lynx_eye: 1,
Malachite: 1,
Malacon: 1,
Mellochrysos: 1,
Microcline: 1,
Moonbar: 1,
Moonstone: 1,
Morganite: 1,
Moss_agate: 1,
Mykaro: 1,
Mynteer: 1,
Nelvine: 1,
Nephrite: 1,
Nune: 1,
Obsidian: 1,
Octel: 1,
Olivine: 1,
Ooline: 1,
Onyx: 1,
Ophealine: 1,
Orbaline: 1,
Orblen: 1,
Orl: 1,
Orprase: 1,
Pearl: 1,
Peridot: 1,
Pyrope: 1,
Quartz: 1,
Raindrop: 1,
Red_Tears: 1,
Rhodochrosite: 1,
Rhodolite: 1,
Rhodonite: 1,
Rosaline: 1,
Rubellite: 1,
Ruby: 1,
Rusteen: 1,
Saganite: 1,
Samarskite: 1,
Sanidine: 1,
Sarbossa: 1,
Sardonyx: 1,
Satin_spar: 1,
Scapra: 1,
Serpentine: 1,
Shandon: 1,
Sharpstone: 1,
Silkstone: 1,
Sinhalite: 1,
Skydrop: 1,
Spessartite: 1,
Sphene: 1,
Spinel: 1,
Star_rose_quarts: 1,
Star_ruby: 1,
Star_sapphire: 1,
Sunstone: 1,
Tabasheer: 1,
Tanzanite: 1,
Tchazar: 1,
Thupartial: 1,
Tiger_eye: 1,
Topaz: 1,
Tourmaline: 1,
Tremair: 1,
Turquoise: 1,
Ulvaen: 1,
Variscite: 1,
Water_opal: 1,
Waterstar: 1,
White_opal: 1,
Witherite: 1,
Wonderstone: 1,
Woodtine: 1,
Yellow_sapphire: 1,
Zarbrina: 1,
Zendalure: 1,
Ziose: 1,
Zircon: 1,
};
while (quantity && hillyBurgs.length) {
const [cell] = extractAnyElement(hillyBurgs);
const id = addMarker({ cell, icon, type, dx: 48, px: 13 });
const resource = rw(resources);
const burg = pack.burgs[cells.burg[cell]];
const name = `${burg.name}${resource} mining town`;
const population = rn(burg.population * populationRate * urbanization);
const legend = `${resource} mine`;
notes.push({ id, name, legend });
quantity--;
}
}
function addBridges(type, icon, multiplier) { function addBridges(type, icon, multiplier) {
const { cells, burgs } = pack; const { cells, burgs } = pack;
const meanFlux = d3.mean(cells.fl.filter(fl => fl)); const meanFlux = d3.mean(cells.fl.filter((fl) => fl));
let bridges = Array.from( let bridges = Array.from(
cells.i.filter(i => !occupied[i] && cells.burg[i] && cells.t[i] !== 1 && burgs[cells.burg[i]].population > 20 && cells.r[i] && cells.fl[i] > meanFlux) cells.i.filter(
(i) =>
!occupied[i] &&
cells.burg[i] &&
cells.t[i] !== 1 &&
burgs[cells.burg[i]].population > 20 &&
cells.r[i] &&
cells.fl[i] > meanFlux
)
); );
let quantity = getQuantity(bridges, 1, 5, multiplier); let quantity = getQuantity(bridges, 1, 5, multiplier);
if (!quantity) return; if (!quantity) return;
@ -181,7 +436,7 @@ window.Markers = (function () {
const [cell] = extractAnyElement(bridges); const [cell] = extractAnyElement(bridges);
const id = addMarker({ cell, icon, type, px: 14 }); const id = addMarker({ cell, icon, type, px: 14 });
const burg = pack.burgs[cells.burg[cell]]; const burg = pack.burgs[cells.burg[cell]];
const river = pack.rivers.find(r => r.i === pack.cells.r[cell]); const river = pack.rivers.find((r) => r.i === pack.cells.r[cell]);
const riverName = river ? `${river.name} ${river.type}` : "river"; const riverName = river ? `${river.name} ${river.type}` : "river";
const name = river && P(0.2) ? river.name : burg.name; const name = river && P(0.2) ? river.name : burg.name;
const weightedAdjectives = { const weightedAdjectives = {
@ -191,9 +446,15 @@ window.Markers = (function () {
formidable: 2, formidable: 2,
rickety: 1, rickety: 1,
beaten: 1, beaten: 1,
weathered: 1 weathered: 1,
}; };
notes.push({id, name: `${name} Bridge`, legend: `A ${rw(weightedAdjectives)} bridge spans over the ${riverName} near ${burg.name}`}); notes.push({
id,
name: `${name} Bridge`,
legend: `A ${rw(
weightedAdjectives
)} bridge spans over the ${riverName} near ${burg.name}`,
});
quantity--; quantity--;
} }
} }
@ -201,7 +462,15 @@ window.Markers = (function () {
function addInns(type, icon, multiplier) { function addInns(type, icon, multiplier) {
const { cells } = pack; const { cells } = pack;
let taverns = Array.from(cells.i.filter(i => !occupied[i] && cells.h[i] >= 20 && cells.road[i] > 4 && cells.pop[i] > 10)); let taverns = Array.from(
cells.i.filter(
(i) =>
!occupied[i] &&
cells.h[i] >= 20 &&
cells.road[i] > 4 &&
cells.pop[i] > 10
)
);
let quantity = getQuantity(taverns, 1, 100, multiplier); let quantity = getQuantity(taverns, 1, 100, multiplier);
if (!quantity) return; if (!quantity) return;
@ -221,7 +490,7 @@ window.Markers = (function () {
"Amber", "Amber",
"Orange", "Orange",
"Brown", "Brown",
"Grey" "Grey",
]; ];
const animals = [ const animals = [
"Antelope", "Antelope",
@ -277,7 +546,7 @@ window.Markers = (function () {
"Camel", "Camel",
"Falcon", "Falcon",
"Hound", "Hound",
"Ox" "Ox",
]; ];
const adjectives = [ const adjectives = [
"New", "New",
@ -313,7 +582,7 @@ window.Markers = (function () {
"Favorable", "Favorable",
"Brave", "Brave",
"Sunny", "Sunny",
"Flying" "Flying",
]; ];
const methods = [ const methods = [
"Boiled", "Boiled",
@ -340,7 +609,7 @@ window.Markers = (function () {
"Steamed", "Steamed",
"Cured", "Cured",
"Syrupped", "Syrupped",
"Flame-Broiled" "Flame-Broiled",
]; ];
const courses = [ const courses = [
"beef", "beef",
@ -406,9 +675,23 @@ window.Markers = (function () {
"cheese", "cheese",
"corn", "corn",
"rat tails", "rat tails",
"pig ears" "pig ears",
];
const types = [
"hot",
"cold",
"fire",
"ice",
"smoky",
"misty",
"shiny",
"sweet",
"bitter",
"salty",
"sour",
"sparkling",
"smelly",
]; ];
const types = ["hot", "cold", "fire", "ice", "smoky", "misty", "shiny", "sweet", "bitter", "salty", "sour", "sparkling", "smelly"];
const drinks = [ const drinks = [
"wine", "wine",
"brandy", "brandy",
@ -430,7 +713,7 @@ window.Markers = (function () {
"tea", "tea",
"water", "water",
"juice", "juice",
"sap" "sap",
]; ];
while (quantity) { while (quantity) {
@ -439,10 +722,16 @@ window.Markers = (function () {
const typeName = P(0.3) ? "inn" : "tavern"; const typeName = P(0.3) ? "inn" : "tavern";
const isAnimalThemed = P(0.85); const isAnimalThemed = P(0.85);
const animal = ra(animals); const animal = ra(animals);
const name = isAnimalThemed ? (P(0.6) ? ra(colors) + " " + animal : ra(adjectives) + " " + animal) : ra(adjectives) + " " + capitalize(type); const name = isAnimalThemed
? P(0.6)
? ra(colors) + " " + animal
: ra(adjectives) + " " + animal
: ra(adjectives) + " " + capitalize(type);
const meal = isAnimalThemed && P(0.3) ? animal : ra(courses); const meal = isAnimalThemed && P(0.3) ? animal : ra(courses);
const course = `${ra(methods)} ${meal}`.toLowerCase(); const course = `${ra(methods)} ${meal}`.toLowerCase();
const drink = `${P(0.5) ? ra(types) : ra(colors)} ${ra(drinks)}`.toLowerCase(); const drink = `${P(0.5) ? ra(types) : ra(colors)} ${ra(
drinks
)}`.toLowerCase();
const legend = `A big and famous roadside ${typeName}. Delicious ${course} with ${drink} is served here`; const legend = `A big and famous roadside ${typeName}. Delicious ${course} with ${drink} is served here`;
notes.push({ id, name: "The " + name, legend }); notes.push({ id, name: "The " + name, legend });
quantity--; quantity--;
@ -452,15 +741,28 @@ window.Markers = (function () {
function addLighthouses(type, icon, multiplier) { function addLighthouses(type, icon, multiplier) {
const { cells } = pack; const { cells } = pack;
const lighthouses = Array.from(cells.i.filter(i => !occupied[i] && cells.harbor[i] > 6 && cells.c[i].some(c => cells.h[c] < 20 && cells.road[c]))); const lighthouses = Array.from(
cells.i.filter(
(i) =>
!occupied[i] &&
cells.harbor[i] > 6 &&
cells.c[i].some((c) => cells.h[c] < 20 && cells.road[c])
)
);
let quantity = getQuantity(lighthouses, 1, 2, multiplier); let quantity = getQuantity(lighthouses, 1, 2, multiplier);
if (!quantity) return; if (!quantity) return;
while (quantity) { while (quantity) {
const [cell] = extractAnyElement(lighthouses); const [cell] = extractAnyElement(lighthouses);
const id = addMarker({ cell, icon, type, px: 14 }); const id = addMarker({ cell, icon, type, px: 14 });
const proper = cells.burg[cell] ? pack.burgs[cells.burg[cell]].name : Names.getCulture(cells.culture[cell]); const proper = cells.burg[cell]
notes.push({id, name: getAdjective(proper) + " Lighthouse" + name, legend: `A lighthouse to serve as a beacon for ships in the open sea`}); ? pack.burgs[cells.burg[cell]].name
: Names.getCulture(cells.culture[cell]);
notes.push({
id,
name: getAdjective(proper) + " Lighthouse" + name,
legend: `A lighthouse to serve as a beacon for ships in the open sea`,
});
quantity--; quantity--;
} }
} }
@ -468,7 +770,15 @@ window.Markers = (function () {
function addWaterfalls(type, icon, multiplier) { function addWaterfalls(type, icon, multiplier) {
const { cells } = pack; const { cells } = pack;
const waterfalls = Array.from(cells.i.filter(i => cells.r[i] && !occupied[i] && cells.h[i] >= 50 && cells.c[i].some(c => cells.h[c] < 40 && cells.r[c]))); const waterfalls = Array.from(
cells.i.filter(
(i) =>
cells.r[i] &&
!occupied[i] &&
cells.h[i] >= 50 &&
cells.c[i].some((c) => cells.h[c] < 40 && cells.r[c])
)
);
const quantity = getQuantity(waterfalls, 1, 5, multiplier); const quantity = getQuantity(waterfalls, 1, 5, multiplier);
if (!quantity) return; if (!quantity) return;
@ -478,20 +788,35 @@ window.Markers = (function () {
"An impressive waterfall has cut through the land", "An impressive waterfall has cut through the land",
"The cascades of a stunning waterfall", "The cascades of a stunning waterfall",
"A river drops down from a great height forming a wonderous waterfall", "A river drops down from a great height forming a wonderous waterfall",
"A breathtaking waterfall cuts through the landscape" "A breathtaking waterfall cuts through the landscape",
]; ];
for (let i = 0; i < waterfalls.length && i < quantity; i++) { for (let i = 0; i < waterfalls.length && i < quantity; i++) {
const cell = waterfalls[i]; const cell = waterfalls[i];
const id = addMarker({ cell, icon, type, dy: 54, px: 16 }); const id = addMarker({ cell, icon, type, dy: 54, px: 16 });
const proper = cells.burg[cell] ? pack.burgs[cells.burg[cell]].name : Names.getCulture(cells.culture[cell]); const proper = cells.burg[cell]
notes.push({id, name: getAdjective(proper) + " Waterfall" + name, legend: `${ra(descriptions)}`}); ? pack.burgs[cells.burg[cell]].name
: Names.getCulture(cells.culture[cell]);
notes.push({
id,
name: getAdjective(proper) + " Waterfall" + name,
legend: `${ra(descriptions)}`,
});
} }
} }
function addBattlefields(type, icon, multiplier) { function addBattlefields(type, icon, multiplier) {
const { cells, states } = pack; const { cells, states } = pack;
let battlefields = Array.from(cells.i.filter(i => !occupied[i] && cells.state[i] && cells.pop[i] > 2 && cells.h[i] < 50 && cells.h[i] > 25)); let battlefields = Array.from(
cells.i.filter(
(i) =>
!occupied[i] &&
cells.state[i] &&
cells.pop[i] > 2 &&
cells.h[i] < 50 &&
cells.h[i] > 25
)
);
let quantity = getQuantity(battlefields, 50, 700, multiplier); let quantity = getQuantity(battlefields, 50, 700, multiplier);
if (!quantity) return; if (!quantity) return;
@ -499,7 +824,8 @@ window.Markers = (function () {
const [cell] = extractAnyElement(battlefields); const [cell] = extractAnyElement(battlefields);
const id = addMarker({ cell, icon, type, dy: 52 }); const id = addMarker({ cell, icon, type, dy: 52 });
const state = states[cells.state[cell]]; const state = states[cells.state[cell]];
if (!state.campaigns) state.campaigns = BurgsAndStates.generateCampaign(state); if (!state.campaigns)
state.campaigns = BurgsAndStates.generateCampaign(state);
const campaign = ra(state.campaigns); const campaign = ra(state.campaigns);
const date = generateDate(campaign.start, campaign.end); const date = generateDate(campaign.start, campaign.end);
const name = Names.getCulture(cells.culture[cell]) + " Battlefield"; const name = Names.getCulture(cells.culture[cell]) + " Battlefield";
@ -512,7 +838,9 @@ window.Markers = (function () {
function addDungeons(type, icon, multiplier) { function addDungeons(type, icon, multiplier) {
const { cells } = pack; const { cells } = pack;
let dungeons = Array.from(cells.i.filter(i => !occupied[i] && cells.pop[i] && cells.pop[i] < 3)); let dungeons = Array.from(
cells.i.filter((i) => !occupied[i] && cells.pop[i] && cells.pop[i] < 3)
);
let quantity = getQuantity(dungeons, 30, 200, multiplier); let quantity = getQuantity(dungeons, 30, 200, multiplier);
if (!quantity) return; if (!quantity) return;
@ -531,7 +859,12 @@ window.Markers = (function () {
function addLakeMonsters(type, icon, multiplier) { function addLakeMonsters(type, icon, multiplier) {
const { features } = pack; const { features } = pack;
const lakes = features.filter(feature => feature.type === "lake" && feature.group === "freshwater" && !occupied[feature.firstCell]); const lakes = features.filter(
(feature) =>
feature.type === "lake" &&
feature.group === "freshwater" &&
!occupied[feature.firstCell]
);
let quantity = getQuantity(lakes, 2, 10, multiplier); let quantity = getQuantity(lakes, 2, 10, multiplier);
if (!quantity) return; if (!quantity) return;
@ -550,7 +883,14 @@ window.Markers = (function () {
function addSeaMonsters(type, icon, multiplier) { function addSeaMonsters(type, icon, multiplier) {
const { cells, features } = pack; const { cells, features } = pack;
const sea = Array.from(cells.i.filter(i => !occupied[i] && cells.h[i] < 20 && cells.road[i] && features[cells.f[i]].type === "ocean")); const sea = Array.from(
cells.i.filter(
(i) =>
!occupied[i] &&
cells.h[i] < 20 &&
features[cells.f[i]].type === "ocean"
)
);
let quantity = getQuantity(sea, 50, 700, multiplier); let quantity = getQuantity(sea, 50, 700, multiplier);
if (!quantity) return; if (!quantity) return;
@ -568,7 +908,9 @@ window.Markers = (function () {
function addHillMonsters(type, icon, multiplier) { function addHillMonsters(type, icon, multiplier) {
const { cells } = pack; const { cells } = pack;
const hills = Array.from(cells.i.filter(i => !occupied[i] && cells.h[i] >= 50 && cells.pop[i])); const hills = Array.from(
cells.i.filter((i) => !occupied[i] && cells.h[i] >= 50)
);
let quantity = getQuantity(hills, 30, 600, multiplier); let quantity = getQuantity(hills, 30, 600, multiplier);
if (!quantity) return; if (!quantity) return;
@ -587,23 +929,33 @@ window.Markers = (function () {
"superior", "superior",
"terrifying", "terrifying",
"horrifying", "horrifying",
"feared" "feared",
];
const subjects = [
"Locals",
"Elders",
"Inscriptions",
"Tipplers",
"Legends",
"Whispers",
"Rumors",
"Journeying folk",
"Tales",
]; ];
const subjects = ["Locals", "Elders", "Inscriptions", "Tipplers", "Legends", "Whispers", "Rumors", "Journeying folk", "Tales"];
const species = [ const species = [
"Ogre", "Ankheg",
"Troll", "Troll",
"Cyclops", "Cyclops",
"Giant", "Griffon",
"Monster",
"Beast",
"Dragon",
"Hag", "Hag",
"Banshee",
"Bearded Devil",
"Roc", "Roc",
"Hydra", "Warg",
"Warg" "Giant Owl",
"Owlbear",
"Dire bear",
"Dire tiger",
"Giant Lizard",
"Hyenas",
]; ];
const modusOperandi = [ const modusOperandi = [
"steals cattle at night", "steals cattle at night",
@ -616,7 +968,7 @@ window.Markers = (function () {
"harasses travelers in the area", "harasses travelers in the area",
"snatches people from homes", "snatches people from homes",
"attacks anyone who dares to approach its lair", "attacks anyone who dares to approach its lair",
"attacks unsuspecting victims" "attacks unsuspecting victims",
]; ];
while (quantity) { while (quantity) {
@ -625,7 +977,91 @@ window.Markers = (function () {
const monster = ra(species); const monster = ra(species);
const toponym = Names.getCulture(cells.culture[cell]); const toponym = Names.getCulture(cells.culture[cell]);
const name = `${toponym} ${monster}`; const name = `${toponym} ${monster}`;
const legend = `${ra(subjects)} speak of a ${ra(adjectives)} ${monster} who inhabits ${toponym} hills and ${ra(modusOperandi)}`; const legend = `${ra(subjects)} speak of a ${ra(
adjectives
)} ${monster} who inhabits ${toponym} hills and ${ra(modusOperandi)}`;
notes.push({ id, name, legend });
quantity--;
}
}
function addGoatHeard(type, icon, multiplier) {
const { cells } = pack;
const hills = Array.from(
cells.i.filter((i) => !occupied[i] && cells.h[i] >= 20)
);
let quantity = getQuantity(hills, 30, 600, multiplier);
if (!quantity) return;
while (quantity) {
const [cell] = extractAnyElement(hills);
const id = addMarker({ cell, icon, type, dy: 54, px: 13 });
const name = `Heard of Giant Goats`;
const legend = `Giant heard of goats`;
notes.push({ id, name, legend });
quantity--;
}
}
function addSpiders(type, icon, multiplier) {
const { cells } = pack;
const hills = Array.from(
cells.i.filter((i) => !occupied[i] && cells.h[i] >= 50)
);
let quantity = getQuantity(hills, 30, 600, multiplier);
if (!quantity) return;
const adjectives = [
"prime",
"proud",
"fat",
"hungry",
"superior",
"terrifying",
"horrifying",
"feared",
];
const subjects = [
"Locals",
"Elders",
"Inscriptions",
"Tipplers",
"Legends",
"Whispers",
"Rumors",
"Journeying folk",
"Tales",
];
const species = [
"Giant Spider",
"Giant Wolf",
"Mammoth Spider",
"Phase Spider",
"Sword Spider",
"Steeder",
];
const modusOperandi = [
"steals goats at night",
"prefers eating children",
"keeps the region at bay",
"abducts young women",
"terrorizes the region",
"harasses travelers in the area",
"attacks anyone who dares to approach its lair",
"attacks unsuspecting victims",
];
while (quantity) {
const [cell] = extractAnyElement(hills);
const id = addMarker({ cell, icon, type, dy: 54, px: 13 });
const monster = ra(species);
const toponym = Names.getCulture(cells.culture[cell]);
const name = `${toponym} ${monster}`;
const legend = `${ra(subjects)} speak of a ${ra(
adjectives
)} ${monster} who inhabits ${toponym} hills and ${ra(modusOperandi)}`;
notes.push({ id, name, legend }); notes.push({ id, name, legend });
quantity--; quantity--;
} }
@ -635,7 +1071,13 @@ window.Markers = (function () {
const { cells, cultures } = pack; const { cells, cultures } = pack;
let lonelyMountains = Array.from( let lonelyMountains = Array.from(
cells.i.filter(i => !occupied[i] && cells.h[i] >= 70 && cells.c[i].some(c => cells.culture[c]) && cells.c[i].every(c => cells.h[c] < 60)) cells.i.filter(
(i) =>
!occupied[i] &&
cells.h[i] >= 70 &&
cells.c[i].some((c) => cells.culture[c]) &&
cells.c[i].every((c) => cells.h[c] < 60)
)
); );
let quantity = getQuantity(lonelyMountains, 1, 5, multiplier); let quantity = getQuantity(lonelyMountains, 1, 5, multiplier);
if (!quantity) return; if (!quantity) return;
@ -643,7 +1085,7 @@ window.Markers = (function () {
while (quantity) { while (quantity) {
const [cell] = extractAnyElement(lonelyMountains); const [cell] = extractAnyElement(lonelyMountains);
const id = addMarker({ cell, icon, type, dy: 48 }); const id = addMarker({ cell, icon, type, dy: 48 });
const culture = cells.c[cell].map(c => cells.culture[c]).find(c => c); const culture = cells.c[cell].map((c) => cells.culture[c]).find((c) => c);
const name = `${Names.getCulture(culture)} Mountain`; const name = `${Names.getCulture(culture)} Mountain`;
const height = getFriendlyHeight(cells.p[cell]); const height = getFriendlyHeight(cells.p[cell]);
const legend = `A sacred mountain of ${cultures[culture].name} culture. Height: ${height}`; const legend = `A sacred mountain of ${cultures[culture].name} culture. Height: ${height}`;
@ -655,7 +1097,12 @@ window.Markers = (function () {
function addSacredForests(type, icon, multiplier) { function addSacredForests(type, icon, multiplier) {
const { cells, cultures } = pack; const { cells, cultures } = pack;
let temperateForests = Array.from(cells.i.filter(i => !occupied[i] && cells.culture[i] && [6, 8].includes(cells.biome[i]))); let temperateForests = Array.from(
cells.i.filter(
(i) =>
!occupied[i] && cells.culture[i] && [6, 8].includes(cells.biome[i])
)
);
let quantity = getQuantity(temperateForests, 30, 1000, multiplier); let quantity = getQuantity(temperateForests, 30, 1000, multiplier);
if (!quantity) return; if (!quantity) return;
@ -673,7 +1120,11 @@ window.Markers = (function () {
function addSacredPineries(type, icon, multiplier) { function addSacredPineries(type, icon, multiplier) {
const { cells, cultures } = pack; const { cells, cultures } = pack;
let borealForests = Array.from(cells.i.filter(i => !occupied[i] && cells.culture[i] && cells.biome[i] === 9)); let borealForests = Array.from(
cells.i.filter(
(i) => !occupied[i] && cells.culture[i] && cells.biome[i] === 9
)
);
let quantity = getQuantity(borealForests, 30, 800, multiplier); let quantity = getQuantity(borealForests, 30, 800, multiplier);
if (!quantity) return; if (!quantity) return;
@ -691,7 +1142,31 @@ window.Markers = (function () {
function addSacredPalmGroves(type, icon, multiplier) { function addSacredPalmGroves(type, icon, multiplier) {
const { cells, cultures } = pack; const { cells, cultures } = pack;
let oasises = Array.from(cells.i.filter(i => !occupied[i] && cells.culture[i] && cells.biome[i] === 1 && cells.pop[i] > 1 && cells.road[i])); let borealForests = Array.from(
cells.i.filter(
(i) => !occupied[i] && cells.culture[i] && cells.biome[i] === 5
)
);
let quantity = getQuantity(borealForests, 30, 800, multiplier);
if (!quantity) return;
while (quantity) {
const [cell] = extractAnyElement(borealForests);
const id = addMarker({ cell, icon, type, px: 13 });
const culture = cells.culture[cell];
const name = `${Names.getCulture(culture)} Pinery`;
const legend = `A sacred pinery of ${cultures[culture].name} culture`;
notes.push({ id, name, legend });
quantity--;
}
}
function addSacredCitidel(type, icon, multiplier) {
const { cells, cultures } = pack;
let oasises = Array.from(
cells.i.filter((i) => !occupied[i] && cells.road[i] && cells.h[i] > 40)
);
let quantity = getQuantity(oasises, 1, 100, multiplier); let quantity = getQuantity(oasises, 1, 100, multiplier);
if (!quantity) return; if (!quantity) return;
@ -700,7 +1175,7 @@ window.Markers = (function () {
const id = addMarker({ cell, icon, type, px: 13 }); const id = addMarker({ cell, icon, type, px: 13 });
const culture = cells.culture[cell]; const culture = cells.culture[cell];
const name = `${Names.getCulture(culture)} Palm Grove`; const name = `${Names.getCulture(culture)} Palm Grove`;
const legend = `A sacred palm grove of ${cultures[culture].name} culture`; const legend = `A Sacred Citidel of ${cultures[culture].name} culture`;
notes.push({ id, name, legend }); notes.push({ id, name, legend });
quantity--; quantity--;
} }
@ -709,7 +1184,11 @@ window.Markers = (function () {
function addBrigands(type, icon, multiplier) { function addBrigands(type, icon, multiplier) {
const { cells } = pack; const { cells } = pack;
let roads = Array.from(cells.i.filter(i => !occupied[i] && cells.culture[i] && cells.road[i] > 4)); let roads = Array.from(
cells.i.filter(
(i) => !occupied[i] && cells.culture[i] && cells.road[i] > 4
)
);
let quantity = getQuantity(roads, 50, 100, multiplier); let quantity = getQuantity(roads, 50, 100, multiplier);
if (!quantity) return; if (!quantity) return;
@ -743,7 +1222,7 @@ window.Markers = (function () {
"Tigers", "Tigers",
"Wolfs", "Wolfs",
"Wolverines", "Wolverines",
"Falcons" "Falcons",
]; ];
const types = { brigands: 4, bandits: 3, robbers: 1, highwaymen: 1 }; const types = { brigands: 4, bandits: 3, robbers: 1, highwaymen: 1 };
@ -775,7 +1254,9 @@ window.Markers = (function () {
function addPirates(type, icon, multiplier) { function addPirates(type, icon, multiplier) {
const { cells } = pack; const { cells } = pack;
let searoutes = Array.from(cells.i.filter(i => !occupied[i] && cells.h[i] < 20 && cells.road[i])); let searoutes = Array.from(
cells.i.filter((i) => !occupied[i] && cells.h[i] < 20 && cells.road[i])
);
let quantity = getQuantity(searoutes, 40, 300, multiplier); let quantity = getQuantity(searoutes, 40, 300, multiplier);
if (!quantity) return; if (!quantity) return;
@ -791,17 +1272,33 @@ window.Markers = (function () {
function addStatues(type, icon, multiplier) { function addStatues(type, icon, multiplier) {
const { cells } = pack; const { cells } = pack;
let statues = Array.from(cells.i.filter(i => !occupied[i] && cells.h[i] >= 20 && cells.h[i] < 40)); let statues = Array.from(
cells.i.filter((i) => !occupied[i] && cells.h[i] >= 20 && cells.h[i] < 40)
);
let quantity = getQuantity(statues, 80, 1200, multiplier); let quantity = getQuantity(statues, 80, 1200, multiplier);
if (!quantity) return; if (!quantity) return;
const variants = ["Statue", "Obelisk", "Monument", "Column", "Monolith", "Pillar", "Megalith", "Stele", "Runestone", "Sculpture", "Effigy", "Idol"]; const variants = [
"Statue",
"Obelisk",
"Monument",
"Column",
"Monolith",
"Pillar",
"Megalith",
"Stele",
"Runestone",
"Sculpture",
"Effigy",
"Idol",
];
const scripts = { const scripts = {
cypriot: "𐠁𐠂𐠃𐠄𐠅𐠈𐠊𐠋𐠌𐠍𐠎𐠏𐠐𐠑𐠒𐠓𐠔𐠕𐠖𐠗𐠘𐠙𐠚𐠛𐠜𐠝𐠞𐠟𐠠𐠡𐠢𐠣𐠤𐠥𐠦𐠧𐠨𐠩𐠪𐠫𐠬𐠭𐠮𐠯𐠰𐠱𐠲𐠳𐠴𐠵𐠷𐠸𐠼𐠿 ", cypriot: "𐠁𐠂𐠃𐠄𐠅𐠈𐠊𐠋𐠌𐠍𐠎𐠏𐠐𐠑𐠒𐠓𐠔𐠕𐠖𐠗𐠘𐠙𐠚𐠛𐠜𐠝𐠞𐠟𐠠𐠡𐠢𐠣𐠤𐠥𐠦𐠧𐠨𐠩𐠪𐠫𐠬𐠭𐠮𐠯𐠰𐠱𐠲𐠳𐠴𐠵𐠷𐠸𐠼𐠿 ",
geez: "ሀለሐመሠረሰቀበተኀነአከወዐዘየደገጠጰጸፀፈፐ ", geez: "ሀለሐመሠረሰቀበተኀነአከወዐዘየደገጠጰጸፀፈፐ ",
coptic: "ⲲⲴⲶⲸⲺⲼⲾⳀⳁⳂⳃⳄⳆⳈⳊⳌⳎⳐⳒⳔⳖⳘⳚⳜⳞⳠⳢⳤ⳥⳧⳩⳪ⳫⳬⳭⳲ⳹⳾ ", coptic: "ⲲⲴⲶⲸⲺⲼⲾⳀⳁⳂⳃⳄⳆⳈⳊⳌⳎⳐⳒⳔⳖⳘⳚⳜⳞⳠⳢⳤ⳥⳧⳩⳪ⳫⳬⳭⳲ⳹⳾ ",
tibetan: "ༀ༁༂༃༄༅༆༇༈༉༊་༌༐༑༒༓༔༕༖༗༘༙༚༛༜༠༡༢༣༤༥༦༧༨༩༪༫༬༭༮༯༰༱༲༳༴༵༶༷༸༹༺༻༼༽༾༿", tibetan: "ༀ༁༂༃༄༅༆༇༈༉༊་༌༐༑༒༓༔༕༖༗༘༙༚༛༜༠༡༢༣༤༥༦༧༨༩༪༫༬༭༮༯༰༱༲༳༴༵༶༷༸༹༺༻༼༽༾༿",
mongolian: "᠀᠐᠑᠒ᠠᠡᠦᠧᠨᠩᠪᠭᠮᠯᠰᠱᠲᠳᠵᠻᠼᠽᠾᠿᡀᡁᡆᡍᡎᡏᡐᡑᡒᡓᡔᡕᡖᡗᡙᡜᡝᡞᡟᡠᡡᡭᡮᡯᡰᡱᡲᡳᡴᢀᢁᢂᢋᢏᢐᢑᢒᢓᢛᢜᢞᢟᢠᢡᢢᢤᢥᢦ" mongolian:
"᠀᠐᠑᠒ᠠᠡᠦᠧᠨᠩᠪᠭᠮᠯᠰᠱᠲᠳᠵᠻᠼᠽᠾᠿᡀᡁᡆᡍᡎᡏᡐᡑᡒᡓᡔᡕᡖᡗᡙᡜᡝᡞᡟᡠᡡᡭᡮᡯᡰᡱᡲᡳᡴᢀᢁᢂᢋᢏᢐᢑᢒᢓᢛᢜᢞᢟᢠᢡᢢᢤᢥᢦ",
}; };
while (quantity) { while (quantity) {
@ -825,7 +1322,15 @@ window.Markers = (function () {
function addRuines(type, icon, multiplier) { function addRuines(type, icon, multiplier) {
const { cells } = pack; const { cells } = pack;
let ruins = Array.from(cells.i.filter(i => !occupied[i] && cells.culture[i] && cells.h[i] >= 20 && cells.h[i] < 60)); let ruins = Array.from(
cells.i.filter(
(i) =>
!occupied[i] &&
cells.culture[i] &&
cells.h[i] >= 20 &&
cells.h[i] < 60
)
);
let quantity = getQuantity(ruins, 80, 1200, multiplier); let quantity = getQuantity(ruins, 80, 1200, multiplier);
if (!quantity) return; if (!quantity) return;
@ -842,7 +1347,7 @@ window.Markers = (function () {
"Outpost", "Outpost",
"Fortification", "Fortification",
"Fortress", "Fortress",
"Castle" "Castle",
]; ];
while (quantity) { while (quantity) {
@ -857,13 +1362,12 @@ window.Markers = (function () {
} }
} }
function addPortals(type, icon, multiplier) { function addPortals(type, icon, multiplier) {
const { burgs } = pack; const { burgs } = pack;
let portals = burgs let portals = burgs
.slice(1, Math.ceil(burgs.length / 10) + 1) .slice(1, Math.ceil(burgs.length / 10) + 1)
.filter(({ cell }) => !occupied[cell]) .filter(({ cell }) => !occupied[cell])
.map(burg => [burg.name, burg.cell]); .map((burg) => [burg.name, burg.cell]);
let quantity = getQuantity(portals, 16, 8, multiplier); let quantity = getQuantity(portals, 16, 8, multiplier);
if (!quantity) return; if (!quantity) return;

View file

@ -6,9 +6,19 @@ function getMapData() {
TIME && console.time("createMapData"); TIME && console.time("createMapData");
const date = new Date(); const date = new Date();
const dateString = date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate(); const dateString =
const license = "File can be loaded in azgaar.github.io/Fantasy-Map-Generator"; date.getFullYear() + "-" + (date.getMonth() + 1) + "-" + date.getDate();
const params = [version, license, dateString, seed, graphWidth, graphHeight, mapId].join("|"); const license =
"File can be loaded in azgaar.github.io/Fantasy-Map-Generator";
const params = [
version,
license,
dateString,
seed,
graphWidth,
graphHeight,
mapId,
].join("|");
const settings = [ const settings = [
distanceUnitInput.value, distanceUnitInput.value,
distanceScaleInput.value, distanceScaleInput.value,
@ -34,10 +44,14 @@ function getMapData() {
+hideLabels.checked, +hideLabels.checked,
stylePreset.value, stylePreset.value,
+rescaleLabels.checked, +rescaleLabels.checked,
urbanDensity urbanDensity,
].join("|"); ].join("|");
const coords = JSON.stringify(mapCoordinates); const coords = JSON.stringify(mapCoordinates);
const biomes = [biomesData.color, biomesData.habitability, biomesData.name].join("|"); const biomes = [
biomesData.color,
biomesData.habitability,
biomesData.name,
].join("|");
const notesData = JSON.stringify(notes); const notesData = JSON.stringify(notes);
const rulersString = rulers.toString(); const rulersString = rulers.toString();
const fonts = JSON.stringify(getUsedFonts(svg.node())); const fonts = JSON.stringify(getUsedFonts(svg.node()));
@ -55,7 +69,14 @@ function getMapData() {
const serializedSVG = new XMLSerializer().serializeToString(cloneEl); const serializedSVG = new XMLSerializer().serializeToString(cloneEl);
const { spacing, cellsX, cellsY, boundary, points, features } = grid; const { spacing, cellsX, cellsY, boundary, points, features } = grid;
const gridGeneral = JSON.stringify({spacing, cellsX, cellsY, boundary, points, features}); const gridGeneral = JSON.stringify({
spacing,
cellsX,
cellsY,
boundary,
points,
features,
});
const packFeatures = JSON.stringify(pack.features); const packFeatures = JSON.stringify(pack.features);
const cultures = JSON.stringify(pack.cultures); const cultures = JSON.stringify(pack.cultures);
const states = JSON.stringify(pack.states); const states = JSON.stringify(pack.states);
@ -75,7 +96,7 @@ function getMapData() {
.join("/"); .join("/");
// round population to save space // round population to save space
const pop = Array.from(pack.cells.pop).map(p => rn(p, 4)); const pop = Array.from(pack.cells.pop).map((p) => rn(p, 4));
// data format as below // data format as below
const mapData = [ const mapData = [
@ -114,7 +135,7 @@ function getMapData() {
rivers, rivers,
rulersString, rulersString,
fonts, fonts,
markers markers,
].join("\r\n"); ].join("\r\n");
TIME && console.timeEnd("createMapData"); TIME && console.timeEnd("createMapData");
@ -122,10 +143,7 @@ function getMapData() {
} }
// Download .map file // Download .map file
function dowloadMap() { function downloadMapOrig() {
if (customization) return tip("Map cannot be saved when edit mode is active, please exit the mode and retry", false, "error");
closeDialogs("#alert");
const mapData = getMapData(); const mapData = getMapData();
const blob = new Blob([mapData], { type: "text/plain" }); const blob = new Blob([mapData], { type: "text/plain" });
const URL = window.URL.createObjectURL(blob); const URL = window.URL.createObjectURL(blob);
@ -133,12 +151,104 @@ function dowloadMap() {
link.download = getFileName() + ".map"; link.download = getFileName() + ".map";
link.href = URL; link.href = URL;
link.click(); link.click();
tip(`${link.download} is saved. Open "Downloads" screen (CTRL + J) to check`, true, "success", 7000); }
function downloadMarkers() {
const mapData = JSON.stringify(pack.markers);
const blob = new Blob([mapData], { type: "text/plain" });
const URL = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.download = getFileName() + "-markers.json";
link.href = URL;
link.click();
}
function downloadBurgs() {
const mapData = JSON.stringify(pack.burgs);
const blob = new Blob([mapData], { type: "text/plain" });
const URL = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.download = getFileName() + "-burgs.json";
link.href = URL;
link.click();
}
function downloadReligions() {
const mapData = JSON.stringify(pack.religions);
const blob = new Blob([mapData], { type: "text/plain" });
const URL = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.download = getFileName() + "-religions.json";
link.href = URL;
link.click();
}
function downloadCultures() {
const mapData = JSON.stringify(pack.cultures);
const blob = new Blob([mapData], { type: "text/plain" });
const URL = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.download = getFileName() + "-cultures.json";
link.href = URL;
link.click();
}
function downloadStates() {
const mapData = JSON.stringify(pack.states);
const blob = new Blob([mapData], { type: "text/plain" });
const URL = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.download = getFileName() + "-states.json";
link.href = URL;
link.click();
}
function downloadNotes() {
const mapData = JSON.stringify(pack.notes);
const blob = new Blob([mapData], { type: "text/plain" });
const URL = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.download = getFileName() + "-notes.json";
link.href = URL;
link.click();
}
function downloadPopRates() {
const mapData = populationRate * urbanization;
const blob = new Blob([mapData], { type: "text/plain" });
const URL = window.URL.createObjectURL(blob);
const link = document.createElement("a");
link.download = getFileName() + "-poprate.txt";
link.href = URL;
link.click();
}
function dowloadMap() {
if (customization)
return tip(
"Map cannot be saved when edit mode is active, please exit the mode and retry",
false,
"error"
);
closeDialogs("#alert");
downloadMapOrig();
downloadMarkers();
downloadBurgs();
downloadCultures();
downloadNotes();
downloadReligions();
downloadPopRates();
downloadStates();
window.URL.revokeObjectURL(URL); window.URL.revokeObjectURL(URL);
} }
async function saveToDropbox() { async function saveToDropbox() {
if (customization) return tip("Map cannot be saved when edit mode is active, please exit the mode and retry", false, "error"); if (customization)
return tip(
"Map cannot be saved when edit mode is active, please exit the mode and retry",
false,
"error"
);
closeDialogs("#alert"); closeDialogs("#alert");
const mapData = getMapData(); const mapData = getMapData();
const filename = getFileName() + ".map"; const filename = getFileName() + ".map";
@ -152,12 +262,22 @@ async function saveToDropbox() {
} }
function quickSave() { function quickSave() {
if (customization) return tip("Map cannot be saved when edit mode is active, please exit the mode and retry", false, "error"); if (customization)
return tip(
"Map cannot be saved when edit mode is active, please exit the mode and retry",
false,
"error"
);
const mapData = getMapData(); const mapData = getMapData();
const blob = new Blob([mapData], { type: "text/plain" }); const blob = new Blob([mapData], { type: "text/plain" });
if (blob) ldb.set("lastMap", blob); // auto-save map if (blob) ldb.set("lastMap", blob); // auto-save map
tip("Map is saved to browser memory. Please also save as .map file to secure progress", true, "success", 2000); tip(
"Map is saved to browser memory. Please also save as .map file to secure progress",
true,
"success",
2000
);
} }
const saveReminder = function () { const saveReminder = function () {
@ -170,7 +290,7 @@ const saveReminder = function () {
"Don't forget to save your map on a regular basis!", "Don't forget to save your map on a regular basis!",
"Just a gentle reminder for you to save the map", "Just a gentle reminder for you to save the map",
"Please don't forget to save your progress (saving as .map is the best option)", "Please don't forget to save your progress (saving as .map is the best option)",
"Don't want to be reminded about need to save? Press CTRL+Q" "Don't want to be reminded about need to save? Press CTRL+Q",
]; ];
const interval = 15 * 60 * 1000; // remind every 15 minutes const interval = 15 * 60 * 1000; // remind every 15 minutes
@ -184,12 +304,22 @@ saveReminder();
function toggleSaveReminder() { function toggleSaveReminder() {
if (saveReminder.status) { if (saveReminder.status) {
tip("Save reminder is turned off. Press CTRL+Q again to re-initiate", true, "warn", 2000); tip(
"Save reminder is turned off. Press CTRL+Q again to re-initiate",
true,
"warn",
2000
);
clearInterval(saveReminder.reminder); clearInterval(saveReminder.reminder);
localStorage.setItem("noReminder", true); localStorage.setItem("noReminder", true);
saveReminder.status = 0; saveReminder.status = 0;
} else { } else {
tip("Save reminder is turned on. Press CTRL+Q to turn off", true, "warn", 2000); tip(
"Save reminder is turned on. Press CTRL+Q to turn off",
true,
"warn",
2000
);
localStorage.removeItem("noReminder"); localStorage.removeItem("noReminder");
saveReminder(); saveReminder();
} }

View file

@ -7,89 +7,170 @@ function editBurg(id) {
const burg = id || d3.event.target.dataset.id; const burg = id || d3.event.target.dataset.id;
elSelected = burgLabels.select("[data-id='" + burg + "']"); elSelected = burgLabels.select("[data-id='" + burg + "']");
burgLabels.selectAll("text").call(d3.drag().on("start", dragBurgLabel)).classed("draggable", true); burgLabels
.selectAll("text")
.call(d3.drag().on("start", dragBurgLabel))
.classed("draggable", true);
updateBurgValues(); updateBurgValues();
$("#burgEditor").dialog({ $("#burgEditor").dialog({
title: "Edit Burg", title: "Edit Burg",
resizable: false, resizable: false,
close: closeBurgEditor, close: closeBurgEditor,
position: {my: "left top", at: "left+10 top+10", of: "svg", collision: "fit"} position: {
my: "left top",
at: "left+10 top+10",
of: "svg",
collision: "fit",
},
}); });
if (modules.editBurg) return; if (modules.editBurg) return;
modules.editBurg = true; modules.editBurg = true;
// add listeners // add listeners
document.getElementById("burgGroupShow").addEventListener("click", showGroupSection); document
document.getElementById("burgGroupHide").addEventListener("click", hideGroupSection); .getElementById("burgGroupShow")
document.getElementById("burgSelectGroup").addEventListener("change", changeGroup); .addEventListener("click", showGroupSection);
document.getElementById("burgInputGroup").addEventListener("change", createNewGroup); document
document.getElementById("burgAddGroup").addEventListener("click", toggleNewGroupInput); .getElementById("burgGroupHide")
document.getElementById("burgRemoveGroup").addEventListener("click", removeBurgsGroup); .addEventListener("click", hideGroupSection);
document
.getElementById("burgSelectGroup")
.addEventListener("change", changeGroup);
document
.getElementById("burgInputGroup")
.addEventListener("change", createNewGroup);
document
.getElementById("burgAddGroup")
.addEventListener("click", toggleNewGroupInput);
document
.getElementById("burgRemoveGroup")
.addEventListener("click", removeBurgsGroup);
document.getElementById("burgName").addEventListener("input", changeName); document.getElementById("burgName").addEventListener("input", changeName);
document.getElementById("burgNameReRandom").addEventListener("click", generateNameRandom); document
.getElementById("burgNameReRandom")
.addEventListener("click", generateNameRandom);
document.getElementById("burgType").addEventListener("input", changeType); document.getElementById("burgType").addEventListener("input", changeType);
document.getElementById("burgCulture").addEventListener("input", changeCulture); document
document.getElementById("burgNameReCulture").addEventListener("click", generateNameCulture); .getElementById("burgCulture")
document.getElementById("burgPopulation").addEventListener("change", changePopulation); .addEventListener("input", changeCulture);
burgBody.querySelectorAll(".burgFeature").forEach(el => el.addEventListener("click", toggleFeature)); document
document.getElementById("mfcgBurgSeed").addEventListener("change", changeSeed); .getElementById("burgNameReCulture")
document.getElementById("regenerateMFCGBurgSeed").addEventListener("click", randomizeSeed); .addEventListener("click", generateNameCulture);
document
.getElementById("burgPopulation")
.addEventListener("change", changePopulation);
burgBody
.querySelectorAll(".burgFeature")
.forEach((el) => el.addEventListener("click", toggleFeature));
document
.getElementById("mfcgBurgSeed")
.addEventListener("change", changeSeed);
document
.getElementById("regenerateMFCGBurgSeed")
.addEventListener("click", randomizeSeed);
document.getElementById("burgStyleShow").addEventListener("click", showStyleSection); document
document.getElementById("burgStyleHide").addEventListener("click", hideStyleSection); .getElementById("burgStyleShow")
document.getElementById("burgEditLabelStyle").addEventListener("click", editGroupLabelStyle); .addEventListener("click", showStyleSection);
document.getElementById("burgEditIconStyle").addEventListener("click", editGroupIconStyle); document
document.getElementById("burgEditAnchorStyle").addEventListener("click", editGroupAnchorStyle); .getElementById("burgStyleHide")
.addEventListener("click", hideStyleSection);
document
.getElementById("burgEditLabelStyle")
.addEventListener("click", editGroupLabelStyle);
document
.getElementById("burgEditIconStyle")
.addEventListener("click", editGroupIconStyle);
document
.getElementById("burgEditAnchorStyle")
.addEventListener("click", editGroupAnchorStyle);
document.getElementById("burgEmblem").addEventListener("click", openEmblemEdit); document
document.getElementById("burgToggleMFCGMap").addEventListener("click", toggleMFCGMap); .getElementById("burgEmblem")
document.getElementById("burgEditEmblem").addEventListener("click", openEmblemEdit); .addEventListener("click", openEmblemEdit);
document.getElementById("burgRelocate").addEventListener("click", toggleRelocateBurg); document
document.getElementById("burglLegend").addEventListener("click", editBurgLegend); .getElementById("burgToggleMFCGMap")
document.getElementById("burgLock").addEventListener("click", toggleBurgLockButton); .addEventListener("click", toggleMFCGMap);
document.getElementById("burgRemove").addEventListener("click", removeSelectedBurg); document
.getElementById("burgEditEmblem")
.addEventListener("click", openEmblemEdit);
document
.getElementById("burgRelocate")
.addEventListener("click", toggleRelocateBurg);
document
.getElementById("burglLegend")
.addEventListener("click", editBurgLegend);
document
.getElementById("burgLock")
.addEventListener("click", toggleBurgLockButton);
document
.getElementById("burgRemove")
.addEventListener("click", removeSelectedBurg);
function updateBurgValues() { function updateBurgValues() {
const id = +elSelected.attr("data-id"); const id = +elSelected.attr("data-id");
const b = pack.burgs[id]; const b = pack.burgs[id];
const province = pack.cells.province[b.cell]; const province = pack.cells.province[b.cell];
const provinceName = province ? pack.provinces[province].fullName + ", " : ""; const provinceName = province
const stateName = pack.states[b.state].fullName || pack.states[b.state].name; ? pack.provinces[province].fullName + ", "
document.getElementById("burgProvinceAndState").innerHTML = provinceName + stateName; : "";
const stateName =
pack.states[b.state].fullName || pack.states[b.state].name;
document.getElementById("burgProvinceAndState").innerHTML =
provinceName + stateName;
document.getElementById("burgName").value = b.name; document.getElementById("burgName").value = b.name;
document.getElementById("burgType").value = b.type || "Generic"; document.getElementById("burgType").value = b.type || "Generic";
document.getElementById("burgPopulation").value = rn(b.population * populationRate * urbanization); document.getElementById("burgPopulation").value = rn(
document.getElementById("burgEditAnchorStyle").style.display = +b.port ? "inline-block" : "none"; b.population * populationRate * urbanization
);
document.getElementById("burgEditAnchorStyle").style.display = +b.port
? "inline-block"
: "none";
// update list and select culture // update list and select culture
const cultureSelect = document.getElementById("burgCulture"); const cultureSelect = document.getElementById("burgCulture");
cultureSelect.options.length = 0; cultureSelect.options.length = 0;
const cultures = pack.cultures.filter(c => !c.removed); const cultures = pack.cultures.filter((c) => !c.removed);
cultures.forEach(c => cultureSelect.options.add(new Option(c.name, c.i, false, c.i === b.culture))); cultures.forEach((c) =>
cultureSelect.options.add(
new Option(c.name, c.i, false, c.i === b.culture)
)
);
const temperature = grid.cells.temp[pack.cells.g[b.cell]]; const temperature = grid.cells.temp[pack.cells.g[b.cell]];
document.getElementById("burgTemperature").innerHTML = convertTemperature(temperature); document.getElementById("burgTemperature").innerHTML =
document.getElementById("burgTemperatureLikeIn").innerHTML = getTemperatureLikeness(temperature); convertTemperature(temperature);
document.getElementById("burgElevation").innerHTML = getHeight(pack.cells.h[b.cell]); document.getElementById("burgTemperatureLikeIn").innerHTML =
getTemperatureLikeness(temperature);
document.getElementById("burgElevation").innerHTML = getHeight(
pack.cells.h[b.cell]
);
// toggle features // toggle features
if (b.capital) document.getElementById("burgCapital").classList.remove("inactive"); if (b.capital)
document.getElementById("burgCapital").classList.remove("inactive");
else document.getElementById("burgCapital").classList.add("inactive"); else document.getElementById("burgCapital").classList.add("inactive");
if (b.port) document.getElementById("burgPort").classList.remove("inactive"); if (b.port)
document.getElementById("burgPort").classList.remove("inactive");
else document.getElementById("burgPort").classList.add("inactive"); else document.getElementById("burgPort").classList.add("inactive");
if (b.citadel) document.getElementById("burgCitadel").classList.remove("inactive"); if (b.citadel)
document.getElementById("burgCitadel").classList.remove("inactive");
else document.getElementById("burgCitadel").classList.add("inactive"); else document.getElementById("burgCitadel").classList.add("inactive");
if (b.walls) document.getElementById("burgWalls").classList.remove("inactive"); if (b.walls)
document.getElementById("burgWalls").classList.remove("inactive");
else document.getElementById("burgWalls").classList.add("inactive"); else document.getElementById("burgWalls").classList.add("inactive");
if (b.plaza) document.getElementById("burgPlaza").classList.remove("inactive"); if (b.plaza)
document.getElementById("burgPlaza").classList.remove("inactive");
else document.getElementById("burgPlaza").classList.add("inactive"); else document.getElementById("burgPlaza").classList.add("inactive");
if (b.temple) document.getElementById("burgTemple").classList.remove("inactive"); if (b.temple)
document.getElementById("burgTemple").classList.remove("inactive");
else document.getElementById("burgTemple").classList.add("inactive"); else document.getElementById("burgTemple").classList.add("inactive");
if (b.shanty) document.getElementById("burgShanty").classList.remove("inactive"); if (b.shanty)
document.getElementById("burgShanty").classList.remove("inactive");
else document.getElementById("burgShanty").classList.add("inactive"); else document.getElementById("burgShanty").classList.add("inactive");
//toggle lock //toggle lock
@ -101,7 +182,9 @@ function editBurg(id) {
select.options.length = 0; // remove all options select.options.length = 0; // remove all options
burgLabels.selectAll("g").each(function () { burgLabels.selectAll("g").each(function () {
select.options.add(new Option(this.id, this.id, false, this.id === group)); select.options.add(
new Option(this.id, this.id, false, this.id === group)
);
}); });
// set emlem image // set emlem image
@ -157,7 +240,7 @@ function editBurg(id) {
"Mogadishu", "Mogadishu",
"Bangkok", "Bangkok",
"Aden", "Aden",
"Khartoum" "Khartoum",
]; // 21 - 30 ]; // 21 - 30
if (temperature > 30) return "Mecca"; if (temperature > 30) return "Mecca";
return cities[temperature + 5] || null; return cities[temperature + 5] || null;
@ -172,17 +255,25 @@ function editBurg(id) {
const x = d3.event.x, const x = d3.event.x,
y = d3.event.y; y = d3.event.y;
this.setAttribute("transform", `translate(${dx + x},${dy + y})`); this.setAttribute("transform", `translate(${dx + x},${dy + y})`);
tip('Use dragging for fine-tuning only, to actually move burg use "Relocate" button', false, "warning"); tip(
'Use dragging for fine-tuning only, to actually move burg use "Relocate" button',
false,
"warning"
);
}); });
} }
function showGroupSection() { function showGroupSection() {
document.querySelectorAll("#burgBottom > button").forEach(el => (el.style.display = "none")); document
.querySelectorAll("#burgBottom > button")
.forEach((el) => (el.style.display = "none"));
document.getElementById("burgGroupSection").style.display = "inline-block"; document.getElementById("burgGroupSection").style.display = "inline-block";
} }
function hideGroupSection() { function hideGroupSection() {
document.querySelectorAll("#burgBottom > button").forEach(el => (el.style.display = "inline-block")); document
.querySelectorAll("#burgBottom > button")
.forEach((el) => (el.style.display = "inline-block"));
document.getElementById("burgGroupSection").style.display = "none"; document.getElementById("burgGroupSection").style.display = "none";
document.getElementById("burgInputGroup").style.display = "none"; document.getElementById("burgInputGroup").style.display = "none";
document.getElementById("burgInputGroup").value = ""; document.getElementById("burgInputGroup").value = "";
@ -216,7 +307,11 @@ function editBurg(id) {
.replace(/[^\w\s]/gi, ""); .replace(/[^\w\s]/gi, "");
if (document.getElementById(group)) { if (document.getElementById(group)) {
tip("Element with this id already exists. Please provide a unique name", false, "error"); tip(
"Element with this id already exists. Please provide a unique name",
false,
"error"
);
return; return;
} }
@ -244,7 +339,9 @@ function editBurg(id) {
const count = elSelected.node().parentNode.childElementCount; const count = elSelected.node().parentNode.childElementCount;
if (oldGroup !== "cities" && oldGroup !== "towns" && count === 1) { if (oldGroup !== "cities" && oldGroup !== "towns" && count === 1) {
document.getElementById("burgSelectGroup").selectedOptions[0].remove(); document.getElementById("burgSelectGroup").selectedOptions[0].remove();
document.getElementById("burgSelectGroup").options.add(new Option(group, group, false, true)); document
.getElementById("burgSelectGroup")
.options.add(new Option(group, group, false, true));
toggleNewGroupInput(); toggleNewGroupInput();
document.getElementById("burgInputGroup").value = ""; document.getElementById("burgInputGroup").value = "";
labelG.id = group; labelG.id = group;
@ -254,16 +351,24 @@ function editBurg(id) {
} }
// create new groups // create new groups
document.getElementById("burgSelectGroup").options.add(new Option(group, group, false, true)); document
.getElementById("burgSelectGroup")
.options.add(new Option(group, group, false, true));
toggleNewGroupInput(); toggleNewGroupInput();
document.getElementById("burgInputGroup").value = ""; document.getElementById("burgInputGroup").value = "";
const newLabelG = document.querySelector("#burgLabels").appendChild(labelG.cloneNode(false)); const newLabelG = document
.querySelector("#burgLabels")
.appendChild(labelG.cloneNode(false));
newLabelG.id = group; newLabelG.id = group;
const newIconG = document.querySelector("#burgIcons").appendChild(iconG.cloneNode(false)); const newIconG = document
.querySelector("#burgIcons")
.appendChild(iconG.cloneNode(false));
newIconG.id = group; newIconG.id = group;
if (anchor) { if (anchor) {
const newAnchorG = document.querySelector("#anchors").appendChild(anchorG.cloneNode(false)); const newAnchorG = document
.querySelector("#anchors")
.appendChild(anchorG.cloneNode(false));
newAnchorG.id = group; newAnchorG.id = group;
} }
moveBurgToGroup(id, group); moveBurgToGroup(id, group);
@ -277,11 +382,17 @@ function editBurg(id) {
for (let i = 0; i < group.children.length; i++) { for (let i = 0; i < group.children.length; i++) {
burgsInGroup.push(+group.children[i].dataset.id); burgsInGroup.push(+group.children[i].dataset.id);
} }
const burgsToRemove = burgsInGroup.filter(b => !(pack.burgs[b].capital || pack.burgs[b].lock)); const burgsToRemove = burgsInGroup.filter(
(b) => !(pack.burgs[b].capital || pack.burgs[b].lock)
);
const capital = burgsToRemove.length < burgsInGroup.length; const capital = burgsToRemove.length < burgsInGroup.length;
alertMessage.innerHTML = `Are you sure you want to remove alertMessage.innerHTML = `Are you sure you want to remove
${basic || capital ? "all unlocked elements in the group" : "the entire burg group"}? ${
basic || capital
? "all unlocked elements in the group"
: "the entire burg group"
}?
<br>Please note that capital or locked burgs will not be deleted. <br>Please note that capital or locked burgs will not be deleted.
<br><br>Burgs to be removed: ${burgsToRemove.length}`; <br><br>Burgs to be removed: ${burgsToRemove.length}`;
$("#alert").dialog({ $("#alert").dialog({
@ -292,7 +403,7 @@ function editBurg(id) {
$(this).dialog("close"); $(this).dialog("close");
$("#burgEditor").dialog("close"); $("#burgEditor").dialog("close");
hideGroupSection(); hideGroupSection();
burgsToRemove.forEach(b => removeBurg(b)); burgsToRemove.forEach((b) => removeBurg(b));
if (!basic && !capital) { if (!basic && !capital) {
// entirely remove group // entirely remove group
@ -306,8 +417,8 @@ function editBurg(id) {
}, },
Cancel: function () { Cancel: function () {
$(this).dialog("close"); $(this).dialog("close");
} },
} },
}); });
} }
@ -342,7 +453,10 @@ function editBurg(id) {
function changePopulation() { function changePopulation() {
const id = +elSelected.attr("data-id"); const id = +elSelected.attr("data-id");
pack.burgs[id].population = rn(burgPopulation.value / populationRate / urbanization, 4); pack.burgs[id].population = rn(
burgPopulation.value / populationRate / urbanization,
4
);
} }
function toggleFeature() { function toggleFeature() {
@ -356,7 +470,9 @@ function editBurg(id) {
if (b[feature]) this.classList.remove("inactive"); if (b[feature]) this.classList.remove("inactive");
else if (!b[feature]) this.classList.add("inactive"); else if (!b[feature]) this.classList.add("inactive");
if (b.port) document.getElementById("burgEditAnchorStyle").style.display = "inline-block"; if (b.port)
document.getElementById("burgEditAnchorStyle").style.display =
"inline-block";
else document.getElementById("burgEditAnchorStyle").style.display = "none"; else document.getElementById("burgEditAnchorStyle").style.display = "none";
} }
@ -379,12 +495,16 @@ function editBurg(id) {
} }
function showStyleSection() { function showStyleSection() {
document.querySelectorAll("#burgBottom > button").forEach(el => (el.style.display = "none")); document
.querySelectorAll("#burgBottom > button")
.forEach((el) => (el.style.display = "none"));
document.getElementById("burgStyleSection").style.display = "inline-block"; document.getElementById("burgStyleSection").style.display = "inline-block";
} }
function hideStyleSection() { function hideStyleSection() {
document.querySelectorAll("#burgBottom > button").forEach(el => (el.style.display = "inline-block")); document
.querySelectorAll("#burgBottom > button")
.forEach((el) => (el.style.display = "inline-block"));
document.getElementById("burgStyleSection").style.display = "none"; document.getElementById("burgStyleSection").style.display = "none";
} }
@ -408,8 +528,10 @@ function editBurg(id) {
const burgGeneratorURL = getBurgLink(burg); const burgGeneratorURL = getBurgLink(burg);
document.getElementById("mfcgPreview").setAttribute("src", mfcgURL); document.getElementById("mfcgPreview").setAttribute("src", mfcgURL);
document.getElementById("mfcgLink").setAttribute("href", mfcgURL); document.getElementById("mfcgLink").setAttribute("href", mfcgURL);
document.getElementById("burgGenerator").setAttribute("href", burgGeneratorURL); document
debug .getElementById("burgGenerator")
.setAttribute("href", burgGeneratorURL);
debug;
} }
function getBurgSeed(burg) { function getBurgSeed(burg) {
return burg.MFCG || Number(`${seed}${String(burg.i).padStart(4, 0)}`); return burg.MFCG || Number(`${seed}${String(burg.i).padStart(4, 0)}`);
@ -417,12 +539,14 @@ function editBurg(id) {
function getBurgLink(burg) { function getBurgLink(burg) {
const { cells } = pack; const { cells } = pack;
let burgCulture = pack.cultures[burg.culture].name.split('(')[1].split(')')[0]; let burgCulture = pack.cultures[burg.culture].name
.split("(")[1]
.split(")")[0];
const { name, population, cell } = burg; const { name, population, cell } = burg;
const burgSeed = getBurgSeed(burg); const burgSeed = getBurgSeed(burg);
const sizeRaw = 0.43 * Math.pow((population * populationRate) / urbanDensity, 0.385); const sizeRaw =
0.43 * Math.pow((population * populationRate) / urbanDensity, 0.385);
const size = minmax(Math.ceil(sizeRaw), 2, 40); const size = minmax(Math.ceil(sizeRaw), 2, 40);
const people = rn(population * populationRate * urbanization); const people = rn(population * populationRate * urbanization);
const hub = +cells.road[cell] > 50; const hub = +cells.road[cell] > 50;
@ -435,6 +559,7 @@ function editBurg(id) {
const temple = +burg.temple; const temple = +burg.temple;
const shanty = +burg.shanty; const shanty = +burg.shanty;
const worldName = mapName.value;
const sea = coast && cells.haven[cell] ? getSeaDirections(cell) : ""; const sea = coast && cells.haven[cell] ? getSeaDirections(cell) : "";
function getSeaDirections(i) { function getSeaDirections(i) {
const p1 = cells.p[i]; const p1 = cells.p[i];
@ -445,23 +570,22 @@ function editBurg(id) {
return "&sea=" + norm; return "&sea=" + norm;
} }
let townSize = "tiny"; let townSize = "tiny";
if (people > 200) townSize = 'small'; if (people > 200) townSize = "small";
if (people > 900) townSize = 'medium'; if (people > 900) townSize = "medium";
if (people > 2_000) townSize = 'average'; if (people > 2_000) townSize = "average";
if (people > 5_500) townSize = 'big'; if (people > 5_500) townSize = "big";
const baseURL = "http://localhost:9090/town/"; const baseURL = "http://localhost:9090/town/";
const url = `${baseURL}${name}-${burgCulture}/${townSize}`; const url = `${baseURL}${worldName}_${name}-${burgCulture}/${townSize}`;
return url; return url;
} }
function getMFCGlink(burg) { function getMFCGlink(burg) {
const { cells } = pack; const { cells } = pack;
const { name, population, cell } = burg; const { name, population, cell } = burg;
const burgSeed = getBurgSeed(burg); const burgSeed = getBurgSeed(burg);
const sizeRaw = 0.43 * Math.pow((population * populationRate) / urbanDensity, 0.385); const sizeRaw =
0.43 * Math.pow((population * populationRate) / urbanDensity, 0.385);
const size = minmax(Math.ceil(sizeRaw), 2, 40) * 3; const size = minmax(Math.ceil(sizeRaw), 2, 40) * 3;
const people = rn(population * populationRate * urbanization); const people = rn(population * populationRate * urbanization);
@ -485,7 +609,8 @@ function editBurg(id) {
return "&sea=" + norm; return "&sea=" + norm;
} }
const baseURL = "https://watabou.github.io/city-generator/?random=0&continuous=0"; const baseURL =
"https://watabou.github.io/city-generator/?random=0&continuous=0";
const url = `${baseURL}&name=${name}&population=${people}&size=${size}&seed=${burgSeed}&hub=${hub}&river=${river}&coast=${coast}&citadel=${citadel}&plaza=${plaza}&temple=${temple}&walls=${walls}&shantytown=${shanty}${sea}`; const url = `${baseURL}&name=${name}&population=${people}&size=${size}&seed=${burgSeed}&hub=${hub}&river=${river}&coast=${coast}&citadel=${citadel}&plaza=${plaza}&temple=${temple}&walls=${walls}&shantytown=${shanty}${sea}`;
return url; return url;
} }
@ -515,8 +640,11 @@ function editBurg(id) {
function toggleMFCGMap() { function toggleMFCGMap() {
options.showMFCGMap = !options.showMFCGMap; options.showMFCGMap = !options.showMFCGMap;
document.getElementById("mfcgPreviewSection").style.display = options.showMFCGMap ? "block" : "none"; document.getElementById("mfcgPreviewSection").style.display =
document.getElementById("burgToggleMFCGMap").className = options.showMFCGMap ? "icon-map" : "icon-map-o"; options.showMFCGMap ? "block" : "none";
document.getElementById("burgToggleMFCGMap").className = options.showMFCGMap
? "icon-map"
: "icon-map-o";
} }
function toggleRelocateBurg() { function toggleRelocateBurg() {
@ -524,7 +652,10 @@ function editBurg(id) {
document.getElementById("burgRelocate").classList.toggle("pressed"); document.getElementById("burgRelocate").classList.toggle("pressed");
if (document.getElementById("burgRelocate").classList.contains("pressed")) { if (document.getElementById("burgRelocate").classList.contains("pressed")) {
viewbox.style("cursor", "crosshair").on("click", relocateBurgOnClick); viewbox.style("cursor", "crosshair").on("click", relocateBurgOnClick);
tip("Click on map to relocate burg. Hold Shift for continuous move", true); tip(
"Click on map to relocate burg. Hold Shift for continuous move",
true
);
if (!layerIsOn("toggleCells")) { if (!layerIsOn("toggleCells")) {
toggleCells(); toggleCells();
toggler.dataset.forced = true; toggler.dataset.forced = true;
@ -547,12 +678,20 @@ function editBurg(id) {
const burg = pack.burgs[id]; const burg = pack.burgs[id];
if (cells.h[cell] < 20) { if (cells.h[cell] < 20) {
tip("Cannot place burg into the water! Select a land cell", false, "error"); tip(
"Cannot place burg into the water! Select a land cell",
false,
"error"
);
return; return;
} }
if (cells.burg[cell] && cells.burg[cell] !== id) { if (cells.burg[cell] && cells.burg[cell] !== id) {
tip("There is already a burg in this cell. Please select a free cell", false, "error"); tip(
"There is already a burg in this cell. Please select a free cell",
false,
"error"
);
return; return;
} }
@ -614,8 +753,8 @@ function editBurg(id) {
buttons: { buttons: {
Ok: function () { Ok: function () {
$(this).dialog("close"); $(this).dialog("close");
} },
} },
}); });
} else { } else {
alertMessage.innerHTML = "Are you sure you want to remove the burg?"; alertMessage.innerHTML = "Are you sure you want to remove the burg?";
@ -630,15 +769,18 @@ function editBurg(id) {
}, },
Cancel: function () { Cancel: function () {
$(this).dialog("close"); $(this).dialog("close");
} },
} },
}); });
} }
} }
function closeBurgEditor() { function closeBurgEditor() {
document.getElementById("burgRelocate").classList.remove("pressed"); document.getElementById("burgRelocate").classList.remove("pressed");
burgLabels.selectAll("text").call(d3.drag().on("drag", null)).classed("draggable", false); burgLabels
.selectAll("text")
.call(d3.drag().on("drag", null))
.classed("draggable", false);
unselect(); unselect();
} }
} }

View file

@ -7,7 +7,11 @@ restoreDefaultEvents(); // apply default viewbox events on load
// restore default viewbox events // restore default viewbox events
function restoreDefaultEvents() { function restoreDefaultEvents() {
svg.call(zoom); svg.call(zoom);
viewbox.style("cursor", "default").on(".drag", null).on("click", clicked).on("touchmove mousemove", moved); viewbox
.style("cursor", "default")
.on(".drag", null)
.on("click", clicked)
.on("touchmove mousemove", moved);
legend.call(d3.drag().on("start", dragLegendBox)); legend.call(d3.drag().on("start", dragLegendBox));
} }
@ -24,7 +28,11 @@ function clicked() {
if (grand.id === "emblems") editEmblem(); if (grand.id === "emblems") editEmblem();
else if (parent.id === "rivers") editRiver(el.id); else if (parent.id === "rivers") editRiver(el.id);
else if (grand.id === "routes") editRoute(); else if (grand.id === "routes") editRoute();
else if (el.tagName === "tspan" && grand.parentNode.parentNode.id === "labels") editLabel(); else if (
el.tagName === "tspan" &&
grand.parentNode.parentNode.id === "labels"
)
editLabel();
else if (grand.id === "burgLabels") editBurg(); else if (grand.id === "burgLabels") editBurg();
else if (grand.id === "burgIcons") editBurg(); else if (grand.id === "burgIcons") editBurg();
else if (parent.id === "ice") editIce(); else if (parent.id === "ice") editIce();
@ -71,7 +79,8 @@ function moveCircle(x, y, r = 20) {
} }
function removeCircle() { function removeCircle() {
if (document.getElementById("brushCircle")) document.getElementById("brushCircle").remove(); if (document.getElementById("brushCircle"))
document.getElementById("brushCircle").remove();
} }
// get browser-defined fit-content // get browser-defined fit-content
@ -92,8 +101,8 @@ function sortLines(header) {
if (!header.className.includes("icon-sort") && type === "name") order = "-up"; if (!header.className.includes("icon-sort") && type === "name") order = "-up";
const headers = header.parentNode; const headers = header.parentNode;
headers.querySelectorAll("div.sortable").forEach(e => { headers.querySelectorAll("div.sortable").forEach((e) => {
e.classList.forEach(c => { e.classList.forEach((c) => {
if (c.includes("icon-sort")) e.classList.remove(c); if (c.includes("icon-sort")) e.classList.remove(c);
}); });
}); });
@ -116,7 +125,7 @@ function applySorting(headers) {
const bn = name ? b.dataset[sortby] : +b.dataset[sortby]; const bn = name ? b.dataset[sortby] : +b.dataset[sortby];
return (an > bn ? 1 : an < bn ? -1 : 0) * desc; return (an > bn ? 1 : an < bn ? -1 : 0) * desc;
}) })
.forEach(line => list.appendChild(line)); .forEach((line) => list.appendChild(line));
} }
function addBurg(point) { function addBurg(point) {
@ -131,7 +140,10 @@ function addBurg(point) {
const feature = cells.f[cell]; const feature = cells.f[cell];
const temple = pack.states[state].form === "Theocracy"; const temple = pack.states[state].form === "Theocracy";
const population = Math.max((cells.s[cell] + cells.road[cell]) / 3 + i / 1000 + (cell % 100) / 1000, 0.1); const population = Math.max(
(cells.s[cell] + cells.road[cell]) / 3 + i / 1000 + (cell % 100) / 1000,
0.1
);
const type = BurgsAndStates.getType(cell, false); const type = BurgsAndStates.getType(cell, false);
// generate emblem // generate emblem
@ -139,7 +151,22 @@ function addBurg(point) {
coa.shield = COA.getShield(culture, state); coa.shield = COA.getShield(culture, state);
COArenderer.add("burg", i, coa, x, y); COArenderer.add("burg", i, coa, x, y);
pack.burgs.push({name, cell, x, y, state, i, culture, feature, capital: 0, port: 0, temple, population, coa, type}); pack.burgs.push({
name,
cell,
x,
y,
state,
i,
culture,
feature,
capital: 0,
port: 0,
temple,
population,
coa,
type,
});
cells.burg[cell] = i; cells.burg[cell] = i;
const townSize = burgIcons.select("#towns").attr("size") || 0.5; const townSize = burgIcons.select("#towns").attr("size") || 0.5;
@ -219,7 +246,11 @@ function toggleCapital(burg) {
return; return;
} }
if (pack.burgs[burg].capital) { if (pack.burgs[burg].capital) {
tip("To change capital please assign a capital status to another burg of this state", false, "error"); tip(
"To change capital please assign a capital status to another burg of this state",
false,
"error"
);
return; return;
} }
const old = pack.states[state].capital; const old = pack.states[state].capital;
@ -244,7 +275,12 @@ function togglePort(burg) {
const haven = pack.cells.haven[b.cell]; const haven = pack.cells.haven[b.cell];
const port = haven ? pack.cells.f[haven] : -1; const port = haven ? pack.cells.f[haven] : -1;
if (!haven) tip("Port haven is not found, system won't be able to make a searoute", false, "warn"); if (!haven)
tip(
"Port haven is not found, system won't be able to make a searoute",
false,
"warn"
);
b.port = port; b.port = port;
const g = b.capital ? "cities" : "towns"; const g = b.capital ? "cities" : "towns";
@ -281,13 +317,22 @@ function drawLegend(name, data) {
const vOffset = fontSize / 2; const vOffset = fontSize / 2;
// append items // append items
const boxes = legend.append("g").attr("stroke-width", 0.5).attr("stroke", "#111111").attr("stroke-dasharray", "none"); const boxes = legend
const labels = legend.append("g").attr("fill", "#000000").attr("stroke", "none"); .append("g")
.attr("stroke-width", 0.5)
.attr("stroke", "#111111")
.attr("stroke-dasharray", "none");
const labels = legend
.append("g")
.attr("fill", "#000000")
.attr("stroke", "none");
const columns = Math.ceil(data.length / itemsInCol); const columns = Math.ceil(data.length / itemsInCol);
for (let column = 0, i = 0; column < columns; column++) { for (let column = 0, i = 0; column < columns; column++) {
const linesInColumn = Math.ceil(data.length / columns); const linesInColumn = Math.ceil(data.length / columns);
const offset = column ? colOffset * 2 + legend.node().getBBox().width : colOffset; const offset = column
? colOffset * 2 + legend.node().getBBox().width
: colOffset;
for (let l = 0; l < linesInColumn && data[i]; l++, i++) { for (let l = 0; l < linesInColumn && data[i]; l++, i++) {
boxes boxes
@ -354,7 +399,7 @@ function redrawLegend() {
const data = legend const data = legend
.attr("data") .attr("data")
.split("|") .split("|")
.map(l => l.split(",")); .map((l) => l.split(","));
drawLegend(name, data); drawLegend(name, data);
} }
@ -383,7 +428,12 @@ function createPicker() {
const cl = () => tip("Click to close the picker"); const cl = () => tip("Click to close the picker");
const closePicker = () => contaiter.style("display", "none"); const closePicker = () => contaiter.style("display", "none");
const contaiter = d3.select("body").append("svg").attr("id", "pickerContainer").attr("width", "100%").attr("height", "100%"); const contaiter = d3
.select("body")
.append("svg")
.attr("id", "pickerContainer")
.attr("width", "100%")
.attr("height", "100%");
contaiter contaiter
.append("rect") .append("rect")
.attr("x", 0) .attr("x", 0)
@ -407,19 +457,39 @@ function createPicker() {
const h = controls.append("g"); const h = controls.append("g");
h.append("text").attr("x", 4).attr("y", 14).text("H:"); h.append("text").attr("x", 4).attr("y", 14).text("H:");
h.append("line").attr("x1", 18).attr("y1", 10).attr("x2", 107).attr("y2", 10); h.append("line").attr("x1", 18).attr("y1", 10).attr("x2", 107).attr("y2", 10);
h.append("circle").attr("cx", 75).attr("cy", 10).attr("r", 5).attr("id", "pickerH"); h.append("circle")
.attr("cx", 75)
.attr("cy", 10)
.attr("r", 5)
.attr("id", "pickerH");
h.on("mousemove", () => tip("Set palette hue")); h.on("mousemove", () => tip("Set palette hue"));
const s = controls.append("g"); const s = controls.append("g");
s.append("text").attr("x", 113).attr("y", 14).text("S:"); s.append("text").attr("x", 113).attr("y", 14).text("S:");
s.append("line").attr("x1", 124).attr("y1", 10).attr("x2", 206).attr("y2", 10); s.append("line")
s.append("circle").attr("cx", 181.4).attr("cy", 10).attr("r", 5).attr("id", "pickerS"); .attr("x1", 124)
.attr("y1", 10)
.attr("x2", 206)
.attr("y2", 10);
s.append("circle")
.attr("cx", 181.4)
.attr("cy", 10)
.attr("r", 5)
.attr("id", "pickerS");
s.on("mousemove", () => tip("Set palette saturation")); s.on("mousemove", () => tip("Set palette saturation"));
const l = controls.append("g"); const l = controls.append("g");
l.append("text").attr("x", 213).attr("y", 14).text("L:"); l.append("text").attr("x", 213).attr("y", 14).text("L:");
l.append("line").attr("x1", 226).attr("y1", 10).attr("x2", 306).attr("y2", 10); l.append("line")
l.append("circle").attr("cx", 282).attr("cy", 10).attr("r", 5).attr("id", "pickerL"); .attr("x1", 226)
.attr("y1", 10)
.attr("x2", 306)
.attr("y2", 10);
l.append("circle")
.attr("cx", 282)
.attr("cy", 10)
.attr("r", 5)
.attr("id", "pickerL");
l.on("mousemove", () => tip("Set palette lightness")); l.on("mousemove", () => tip("Set palette lightness"));
controls.selectAll("line").on("click", clickPickerControl); controls.selectAll("line").on("click", clickPickerControl);
@ -432,7 +502,9 @@ function createPicker() {
.attr("y", 20) .attr("y", 20)
.attr("width", 303) .attr("width", 303)
.attr("height", 20) .attr("height", 20)
.on("mousemove", () => tip("Color value in different color spaces. Edit to change")); .on("mousemove", () =>
tip("Color value in different color spaces. Edit to change")
);
const html = ` const html = `
<label style="margin-right: 6px">HSL: <label style="margin-right: 6px">HSL:
<input type="number" id="pickerHSL_H" data-space="hsl" min=0 max=360 value="231">, <input type="number" id="pickerHSL_H" data-space="hsl" min=0 max=360 value="231">,
@ -448,12 +520,20 @@ function createPicker() {
spaces.node().insertAdjacentHTML("beforeend", html); spaces.node().insertAdjacentHTML("beforeend", html);
spaces.selectAll("input").on("change", changePickerSpace); spaces.selectAll("input").on("change", changePickerSpace);
const colors = picker.append("g").attr("id", "pickerColors").attr("stroke", "#333333"); const colors = picker
const hatches = picker.append("g").attr("id", "pickerHatches").attr("stroke", "#333333"); .append("g")
.attr("id", "pickerColors")
.attr("stroke", "#333333");
const hatches = picker
.append("g")
.attr("id", "pickerHatches")
.attr("stroke", "#333333");
const hatching = d3.selectAll("g#hatching > pattern"); const hatching = d3.selectAll("g#hatching > pattern");
const number = hatching.size(); const number = hatching.size();
const clr = d3.range(number).map(i => d3.hsl((i / number) * 360, 0.7, 0.7).hex()); const clr = d3
.range(number)
.map((i) => d3.hsl((i / number) * 360, 0.7, 0.7).hex());
clr.forEach(function (d, i) { clr.forEach(function (d, i) {
colors colors
.append("rect") .append("rect")
@ -500,7 +580,12 @@ function createPicker() {
.attr("fill", "#ffffff") .attr("fill", "#ffffff")
.attr("stroke", "#5d4651") .attr("stroke", "#5d4651")
.on("mousemove", pos); .on("mousemove", pos);
picker.insert("text", ":first-child").attr("x", 291).attr("y", -10).attr("id", "pickerCloseText").text("✕"); picker
.insert("text", ":first-child")
.attr("x", 291)
.attr("y", -10)
.attr("id", "pickerCloseText")
.text("✕");
picker picker
.insert("rect", ":first-child") .insert("rect", ":first-child")
.attr("x", 288) .attr("x", 288)
@ -510,13 +595,32 @@ function createPicker() {
.attr("height", 14) .attr("height", 14)
.on("mousemove", cl) .on("mousemove", cl)
.on("click", closePicker); .on("click", closePicker);
picker.insert("text", ":first-child").attr("x", 12).attr("y", -10).attr("id", "pickerLabel").text("Color Picker").on("mousemove", pos); picker
picker.insert("rect", ":first-child").attr("x", 0).attr("y", -30).attr("width", width).attr("height", 30).attr("id", "pickerHeader").on("mousemove", pos); .insert("text", ":first-child")
picker.attr("transform", `translate(${(svgWidth - width) / 2},${(svgHeight - height) / 2})`); .attr("x", 12)
.attr("y", -10)
.attr("id", "pickerLabel")
.text("Color Picker")
.on("mousemove", pos);
picker
.insert("rect", ":first-child")
.attr("x", 0)
.attr("y", -30)
.attr("width", width)
.attr("height", 30)
.attr("id", "pickerHeader")
.on("mousemove", pos);
picker.attr(
"transform",
`translate(${(svgWidth - width) / 2},${(svgHeight - height) / 2})`
);
} }
function updateSelectedRect(fill) { function updateSelectedRect(fill) {
document.getElementById("picker").querySelector("rect.selected").classList.remove("selected"); document
.getElementById("picker")
.querySelector("rect.selected")
.classList.remove("selected");
document document
.getElementById("picker") .getElementById("picker")
.querySelector("rect[fill='" + fill.toLowerCase() + "']") .querySelector("rect[fill='" + fill.toLowerCase() + "']")
@ -574,7 +678,9 @@ function openPicker(fill, callback) {
updateSelectedRect(fill); updateSelectedRect(fill);
openPicker.updateFill = function () { openPicker.updateFill = function () {
const selected = document.getElementById("picker").querySelector("rect.selected"); const selected = document
.getElementById("picker")
.querySelector("rect.selected");
if (!selected) return; if (!selected) return;
callback(selected.getAttribute("fill")); callback(selected.getAttribute("fill"));
}; };
@ -649,8 +755,15 @@ function changePickerSpace() {
} }
const space = this.dataset.space; const space = this.dataset.space;
const i = Array.from(this.parentNode.querySelectorAll("input")).map(input => input.value); // inputs const i = Array.from(this.parentNode.querySelectorAll("input")).map(
const fill = space === "hex" ? d3.rgb(this.value) : space === "rgb" ? d3.rgb(i[0], i[1], i[2]) : d3.hsl(i[0], i[1] / 100, i[2] / 100); (input) => input.value
); // inputs
const fill =
space === "hex"
? d3.rgb(this.value)
: space === "rgb"
? d3.rgb(i[0], i[1], i[2])
: d3.hsl(i[0], i[1] / 100, i[2] / 100);
const hsl = d3.hsl(fill); const hsl = d3.hsl(fill);
if (isNaN(hsl.l)) { if (isNaN(hsl.l)) {
@ -671,11 +784,27 @@ function fog(id, path) {
if (defs.select("#fog #" + id).size()) return; if (defs.select("#fog #" + id).size()) return;
const fadeIn = d3.transition().duration(2000).ease(d3.easeSinInOut); const fadeIn = d3.transition().duration(2000).ease(d3.easeSinInOut);
if (defs.select("#fog path").size()) { if (defs.select("#fog path").size()) {
defs.select("#fog").append("path").attr("d", path).attr("id", id).attr("opacity", 0).transition(fadeIn).attr("opacity", 1); defs
.select("#fog")
.append("path")
.attr("d", path)
.attr("id", id)
.attr("opacity", 0)
.transition(fadeIn)
.attr("opacity", 1);
} else { } else {
defs.select("#fog").append("path").attr("d", path).attr("id", id).attr("opacity", 1); defs
.select("#fog")
.append("path")
.attr("d", path)
.attr("id", id)
.attr("opacity", 1);
const opacity = fogging.attr("opacity"); const opacity = fogging.attr("opacity");
fogging.style("display", "block").attr("opacity", 0).transition(fadeIn).attr("opacity", opacity); fogging
.style("display", "block")
.attr("opacity", 0)
.transition(fadeIn)
.attr("opacity", opacity);
} }
} }
@ -689,7 +818,7 @@ function unfog(id) {
} }
function getFileName(dataType) { function getFileName(dataType) {
const formatTime = time => (time < 10 ? "0" + time : time); const formatTime = (time) => (time < 10 ? "0" + time : time);
const name = mapName.value; const name = mapName.value;
const type = dataType ? dataType + " " : ""; const type = dataType ? dataType + " " : "";
const date = new Date(); const date = new Date();
@ -699,7 +828,9 @@ function getFileName(dataType) {
const hour = formatTime(date.getHours()); const hour = formatTime(date.getHours());
const minutes = formatTime(date.getMinutes()); const minutes = formatTime(date.getMinutes());
const dateString = [year, month, day, hour, minutes].join("-"); const dateString = [year, month, day, hour, minutes].join("-");
return name + " " + type + dateString; //return name + " " + type + dateString;
return name;
} }
function downloadFile(data, name, type = "text/plain") { function downloadFile(data, name, type = "text/plain") {
@ -716,7 +847,7 @@ function uploadFile(el, callback) {
const fileReader = new FileReader(); const fileReader = new FileReader();
fileReader.readAsText(el.files[0], "UTF-8"); fileReader.readAsText(el.files[0], "UTF-8");
el.value = ""; el.value = "";
fileReader.onload = loaded => callback(loaded.target.result); fileReader.onload = (loaded) => callback(loaded.target.result);
} }
function getBBox(element) { function getBBox(element) {
@ -734,9 +865,20 @@ function highlightElement(element, zoom) {
const enter = d3.transition().duration(1000).ease(d3.easeBounceOut); const enter = d3.transition().duration(1000).ease(d3.easeBounceOut);
const exit = d3.transition().duration(500).ease(d3.easeLinear); const exit = d3.transition().duration(500).ease(d3.easeLinear);
const highlight = debug.append("rect").attr("x", box.x).attr("y", box.y).attr("width", box.width).attr("height", box.height); const highlight = debug
.append("rect")
.attr("x", box.x)
.attr("y", box.y)
.attr("width", box.width)
.attr("height", box.height);
highlight.classed("highlighted", 1).attr("transform", transform); highlight.classed("highlighted", 1).attr("transform", transform);
highlight.transition(enter).style("outline-offset", "0px").transition(exit).style("outline-color", "transparent").delay(1000).remove(); highlight
.transition(enter)
.style("outline-offset", "0px")
.transition(exit)
.style("outline-color", "transparent")
.delay(1000)
.remove();
if (zoom) { if (zoom) {
const tr = parseTransform(transform); const tr = parseTransform(transform);
@ -944,7 +1086,7 @@ function selectIcon(initial, callback) {
"🍻", "🍻",
"🍺", "🍺",
"🍲", "🍲",
"🍷" "🍷",
]; ];
let row = ""; let row = "";
@ -955,15 +1097,16 @@ function selectIcon(initial, callback) {
} }
} }
input.oninput = e => callback(input.value); input.oninput = (e) => callback(input.value);
table.onclick = e => { table.onclick = (e) => {
if (e.target.tagName === "TD") { if (e.target.tagName === "TD") {
input.value = e.target.innerHTML; input.value = e.target.innerHTML;
callback(input.value); callback(input.value);
} }
}; };
table.onmouseover = e => { table.onmouseover = (e) => {
if (e.target.tagName === "TD") tip(`Click to select ${e.target.innerHTML} icon`); if (e.target.tagName === "TD")
tip(`Click to select ${e.target.innerHTML} icon`);
}; };
$("#iconSelector").dialog({ $("#iconSelector").dialog({
@ -977,8 +1120,8 @@ function selectIcon(initial, callback) {
Close: function () { Close: function () {
callback(initial); callback(initial);
$(this).dialog("close"); $(this).dialog("close");
} },
} },
}); });
} }
@ -989,7 +1132,7 @@ function confirmationDialog(options) {
cancel = "Cancel", cancel = "Cancel",
confirm = "Continue", confirm = "Continue",
onCancel, onCancel,
onConfirm onConfirm,
} = options; } = options;
const buttons = { const buttons = {
@ -1000,7 +1143,7 @@ function confirmationDialog(options) {
[cancel]: function () { [cancel]: function () {
if (onCancel) onCancel(); if (onCancel) onCancel();
$(this).dialog("close"); $(this).dialog("close");
} },
}; };
document.getElementById("alertMessage").innerHTML = message; document.getElementById("alertMessage").innerHTML = message;
@ -1016,12 +1159,19 @@ function listen(element, event, handler) {
// Calls the refresh functionality on all editors currently open. // Calls the refresh functionality on all editors currently open.
function refreshAllEditors() { function refreshAllEditors() {
TIME && console.time("refreshAllEditors"); TIME && console.time("refreshAllEditors");
if (document.getElementById("culturesEditorRefresh").offsetParent) culturesEditorRefresh.click(); if (document.getElementById("culturesEditorRefresh").offsetParent)
if (document.getElementById("biomesEditorRefresh").offsetParent) biomesEditorRefresh.click(); culturesEditorRefresh.click();
if (document.getElementById("diplomacyEditorRefresh").offsetParent) diplomacyEditorRefresh.click(); if (document.getElementById("biomesEditorRefresh").offsetParent)
if (document.getElementById("provincesEditorRefresh").offsetParent) provincesEditorRefresh.click(); biomesEditorRefresh.click();
if (document.getElementById("religionsEditorRefresh").offsetParent) religionsEditorRefresh.click(); if (document.getElementById("diplomacyEditorRefresh").offsetParent)
if (document.getElementById("statesEditorRefresh").offsetParent) statesEditorRefresh.click(); diplomacyEditorRefresh.click();
if (document.getElementById("zonesEditorRefresh").offsetParent) zonesEditorRefresh.click(); if (document.getElementById("provincesEditorRefresh").offsetParent)
provincesEditorRefresh.click();
if (document.getElementById("religionsEditorRefresh").offsetParent)
religionsEditorRefresh.click();
if (document.getElementById("statesEditorRefresh").offsetParent)
statesEditorRefresh.click();
if (document.getElementById("zonesEditorRefresh").offsetParent)
zonesEditorRefresh.click();
TIME && console.timeEnd("refreshAllEditors"); TIME && console.timeEnd("refreshAllEditors");
} }

View file

@ -3,13 +3,18 @@
// fit full-screen map if window is resized // fit full-screen map if window is resized
window.addEventListener("resize", function (e) { window.addEventListener("resize", function (e) {
if (localStorage.getItem("mapWidth") && localStorage.getItem("mapHeight")) return; if (localStorage.getItem("mapWidth") && localStorage.getItem("mapHeight"))
return;
mapWidthInput.value = window.innerWidth; mapWidthInput.value = window.innerWidth;
mapHeightInput.value = window.innerHeight; mapHeightInput.value = window.innerHeight;
changeMapSize(); changeMapSize();
}); });
if (location.hostname && location.hostname !== "localhost" && location.hostname !== "127.0.0.1") { if (
location.hostname &&
location.hostname !== "localhost" &&
location.hostname !== "127.0.0.1"
) {
window.onbeforeunload = () => "Are you sure you want to navigate away?"; window.onbeforeunload = () => "Are you sure you want to navigate away?";
} }
@ -18,15 +23,26 @@ const tooltip = document.getElementById("tooltip");
// show tip for non-svg elemets with data-tip // show tip for non-svg elemets with data-tip
document.getElementById("dialogs").addEventListener("mousemove", showDataTip); document.getElementById("dialogs").addEventListener("mousemove", showDataTip);
document.getElementById("optionsContainer").addEventListener("mousemove", showDataTip); document
document.getElementById("exitCustomization").addEventListener("mousemove", showDataTip); .getElementById("optionsContainer")
.addEventListener("mousemove", showDataTip);
document
.getElementById("exitCustomization")
.addEventListener("mousemove", showDataTip);
function tip(tip = "Tip is undefined", main, type, time) { function tip(tip = "Tip is undefined", main, type, time) {
tooltip.innerHTML = tip; tooltip.innerHTML = tip;
tooltip.style.background = "linear-gradient(0.1turn, #ffffff00, #5e5c5c80, #ffffff00)"; tooltip.style.background =
if (type === "error") tooltip.style.background = "linear-gradient(0.1turn, #ffffff00, #e11d1dcc, #ffffff00)"; "linear-gradient(0.1turn, #ffffff00, #5e5c5c80, #ffffff00)";
else if (type === "warn") tooltip.style.background = "linear-gradient(0.1turn, #ffffff00, #be5d08cc, #ffffff00)"; if (type === "error")
else if (type === "success") tooltip.style.background = "linear-gradient(0.1turn, #ffffff00, #127912cc, #ffffff00)"; tooltip.style.background =
"linear-gradient(0.1turn, #ffffff00, #e11d1dcc, #ffffff00)";
else if (type === "warn")
tooltip.style.background =
"linear-gradient(0.1turn, #ffffff00, #be5d08cc, #ffffff00)";
else if (type === "success")
tooltip.style.background =
"linear-gradient(0.1turn, #ffffff00, #127912cc, #ffffff00)";
if (main) { if (main) {
tooltip.dataset.main = tip; tooltip.dataset.main = tip;
@ -50,7 +66,8 @@ function clearMainTip() {
function showDataTip(e) { function showDataTip(e) {
if (!e.target) return; if (!e.target) return;
let dataTip = e.target.dataset.tip; let dataTip = e.target.dataset.tip;
if (!dataTip && e.target.parentNode.dataset.tip) dataTip = e.target.parentNode.dataset.tip; if (!dataTip && e.target.parentNode.dataset.tip)
dataTip = e.target.parentNode.dataset.tip;
if (!dataTip) return; if (!dataTip) return;
//const tooltip = lang === "en" ? dataTip : translate(e.target.dataset.t || e.target.parentNode.dataset.t, dataTip); //const tooltip = lang === "en" ? dataTip : translate(e.target.dataset.t || e.target.parentNode.dataset.t, dataTip);
tip(dataTip); tip(dataTip);
@ -59,9 +76,13 @@ function showDataTip(e) {
function showElementLockTip(event) { function showElementLockTip(event) {
const locked = event?.target?.classList?.contains("icon-lock"); const locked = event?.target?.classList?.contains("icon-lock");
if (locked) { if (locked) {
tip("Click to unlock the element and allow it to be changed by regeneration tools"); tip(
"Click to unlock the element and allow it to be changed by regeneration tools"
);
} else { } else {
tip("Click to lock the element and prevent changes to it by regeneration tools"); tip(
"Click to lock the element and prevent changes to it by regeneration tools"
);
} }
} }
@ -80,11 +101,14 @@ function mouseMove() {
// show note box on hover (if any) // show note box on hover (if any)
function showNotes(e, i) { function showNotes(e, i) {
if (notesEditor.offsetParent) return; if (notesEditor.offsetParent) return;
let id = e.target.id || e.target.parentNode.id || e.target.parentNode.parentNode.id; let id =
if (e.target.parentNode.parentNode.id === "burgLabels") id = "burg" + e.target.dataset.id; e.target.id || e.target.parentNode.id || e.target.parentNode.parentNode.id;
else if (e.target.parentNode.parentNode.id === "burgIcons") id = "burg" + e.target.dataset.id; if (e.target.parentNode.parentNode.id === "burgLabels")
id = "burg" + e.target.dataset.id;
else if (e.target.parentNode.parentNode.id === "burgIcons")
id = "burg" + e.target.dataset.id;
const note = notes.find(note => note.id === id); const note = notes.find((note) => note.id === id);
if (note !== undefined && note.legend !== "") { if (note !== undefined && note.legend !== "") {
document.getElementById("notes").style.display = "block"; document.getElementById("notes").style.display = "block";
document.getElementById("notesHeader").innerHTML = note.name; document.getElementById("notesHeader").innerHTML = note.name;
@ -96,6 +120,34 @@ function showNotes(e, i) {
} }
} }
function createMetaTip(point, e, i, g) {
const biome = pack.cells.biome[i];
const religion = pack.cells.religion[i];
const r = pack.religions[religion];
const religionType =
r.type === "Cult" || r.type == "Heresy" ? r.type : r.type + " religion";
const state = pack.cells.state[i];
const stateCulture = pack.cells.state[i].culture;
const stateName = pack.states[state].fullName;
const province = pack.cells.province[i];
const prov = province ? pack.provinces[province].fullName + ", " : "";
const culture = pack.cells.culture[i];
tip("Culture: " + pack.cultures[culture].name);
let tipText =
pack.cultures[culture].name +
" | " +
religionType +
": " +
r.name +
" | " +
stateName +
" - " +
prov +
" | Biome: " +
biomesData.name[biome];
tip(tipText);
}
// show viewbox tooltip if main tooltip is blank // show viewbox tooltip if main tooltip is blank
function showMapTooltip(point, e, i, g) { function showMapTooltip(point, e, i, g) {
tip(""); // clear tip tip(""); // clear tip
@ -106,12 +158,17 @@ function showMapTooltip(point, e, i, g) {
const land = pack.cells.h[i] >= 20; const land = pack.cells.h[i] >= 20;
// specific elements // specific elements
if (group === "armies") return tip(e.target.parentNode.dataset.name + ". Click to edit"); if (group === "armies")
return tip(e.target.parentNode.dataset.name + ". Click to edit");
if (group === "emblems" && e.target.tagName === "use") { if (group === "emblems" && e.target.tagName === "use") {
const parent = e.target.parentNode; const parent = e.target.parentNode;
const [g, type] = const [g, type] =
parent.id === "burgEmblems" ? [pack.burgs, "burg"] : parent.id === "provinceEmblems" ? [pack.provinces, "province"] : [pack.states, "state"]; parent.id === "burgEmblems"
? [pack.burgs, "burg"]
: parent.id === "provinceEmblems"
? [pack.provinces, "province"]
: [pack.states, "state"];
const i = +e.target.dataset.i; const i = +e.target.dataset.i;
if (event.shiftKey) highlightEmblemElement(type, g[i]); if (event.shiftKey) highlightEmblemElement(type, g[i]);
@ -119,16 +176,19 @@ function showMapTooltip(point, e, i, g) {
d3.select(parent).raise(); d3.select(parent).raise();
const name = g[i].fullName || g[i].name; const name = g[i].fullName || g[i].name;
tip(`${name} ${type} emblem. Click to edit. Hold Shift to show associated area or place`); tip(
`${name} ${type} emblem. Click to edit. Hold Shift to show associated area or place`
);
return; return;
} }
if (group === "rivers") { if (group === "rivers") {
const river = +e.target.id.slice(5); const river = +e.target.id.slice(5);
const r = pack.rivers.find(r => r.i === river); const r = pack.rivers.find((r) => r.i === river);
const name = r ? r.name + " " + r.type : ""; const name = r ? r.name + " " + r.type : "";
tip(name + ". Click to edit"); tip(name + ". Click to edit");
if (riversOverview.offsetParent) highlightEditorLine(riversOverview, river, 5000); if (riversOverview.offsetParent)
highlightEditorLine(riversOverview, river, 5000);
return; return;
} }
@ -141,22 +201,31 @@ function showMapTooltip(point, e, i, g) {
const b = pack.burgs[burg]; const b = pack.burgs[burg];
const population = si(b.population * populationRate * urbanization); const population = si(b.population * populationRate * urbanization);
tip(`${b.name}. Population: ${population}. Click to edit`); tip(`${b.name}. Population: ${population}. Click to edit`);
if (burgsOverview.offsetParent) highlightEditorLine(burgsOverview, burg, 5000); if (burgsOverview.offsetParent)
highlightEditorLine(burgsOverview, burg, 5000);
return; return;
} }
if (group === "labels") return tip("Click to edit the Label"); if (group === "labels") return tip("Click to edit the Label");
if (group === "markers") return tip("Click to edit the Marker and pin the marker note"); if (group === "markers")
return tip("Click to edit the Marker and pin the marker note");
if (group === "ruler") { if (group === "ruler") {
const tag = e.target.tagName; const tag = e.target.tagName;
const className = e.target.getAttribute("class"); const className = e.target.getAttribute("class");
if (tag === "circle" && className === "edge") return tip("Drag to adjust. Hold Ctrl and drag to add a point. Click to remove the point"); if (tag === "circle" && className === "edge")
if (tag === "circle" && className === "control") return tip("Drag to adjust. Hold Shift and drag to keep axial direction. Click to remove the point"); return tip(
"Drag to adjust. Hold Ctrl and drag to add a point. Click to remove the point"
);
if (tag === "circle" && className === "control")
return tip(
"Drag to adjust. Hold Shift and drag to keep axial direction. Click to remove the point"
);
if (tag === "circle") return tip("Drag to adjust the measurer"); if (tag === "circle") return tip("Drag to adjust the measurer");
if (tag === "polyline") return tip("Click on drag to add a control point"); if (tag === "polyline") return tip("Click on drag to add a control point");
if (tag === "path") return tip("Drag to move the measurer"); if (tag === "path") return tip("Drag to move the measurer");
if (tag === "text") return tip("Drag to move, click to remove the measurer"); if (tag === "text")
return tip("Drag to move, click to remove the measurer");
} }
if (subgroup === "burgIcons") return tip("Click to edit the Burg"); if (subgroup === "burgIcons") return tip("Click to edit the Burg");
@ -175,46 +244,55 @@ function showMapTooltip(point, e, i, g) {
if (group === "zones") { if (group === "zones") {
const zone = path[path.length - 8]; const zone = path[path.length - 8];
tip(zone.dataset.description); tip(zone.dataset.description);
if (zonesEditor.offsetParent) highlightEditorLine(zonesEditor, zone.id, 5000); if (zonesEditor.offsetParent)
highlightEditorLine(zonesEditor, zone.id, 5000);
return; return;
} }
if (group === "ice") return tip("Click to edit the Ice"); if (group === "ice") return tip("Click to edit the Ice");
// covering elements // covering elements
if (layerIsOn("togglePrec") && land) tip("Annual Precipitation: " + getFriendlyPrecipitation(i)); if (layerIsOn("togglePrec") && land)
tip("Annual Precipitation: " + getFriendlyPrecipitation(i));
else if (layerIsOn("togglePopulation")) tip(getPopulationTip(i)); else if (layerIsOn("togglePopulation")) tip(getPopulationTip(i));
else if (layerIsOn("toggleTemp")) tip("Temperature: " + convertTemperature(grid.cells.temp[g])); else if (layerIsOn("toggleTemp"))
tip("Temperature: " + convertTemperature(grid.cells.temp[g]));
else if (layerIsOn("toggleBiomes") && pack.cells.biome[i]) { else if (layerIsOn("toggleBiomes") && pack.cells.biome[i]) {
const biome = pack.cells.biome[i]; createMetaTip(point, e, i, g);
tip("Biome: " + biomesData.name[biome]);
if (biomesEditor.offsetParent) highlightEditorLine(biomesEditor, biome); if (biomesEditor.offsetParent) highlightEditorLine(biomesEditor, biome);
} else if (layerIsOn("toggleReligions") && pack.cells.religion[i]) { } else if (layerIsOn("toggleReligions") && pack.cells.religion[i]) {
const religion = pack.cells.religion[i]; createMetaTip(point, e, i, g);
const r = pack.religions[religion]; if (religionsEditor.offsetParent)
const type = r.type === "Cult" || r.type == "Heresy" ? r.type : r.type + " religion"; highlightEditorLine(religionsEditor, religion);
tip(type + ": " + r.name); } else if (
if (religionsEditor.offsetParent) highlightEditorLine(religionsEditor, religion); pack.cells.state[i] &&
} else if (pack.cells.state[i] && (layerIsOn("toggleProvinces") || layerIsOn("toggleStates"))) { (layerIsOn("toggleProvinces") || layerIsOn("toggleStates"))
const state = pack.cells.state[i]; ) {
const stateName = pack.states[state].fullName; createMetaTip(point, e, i, g);
const province = pack.cells.province[i];
const prov = province ? pack.provinces[province].fullName + ", " : "";
tip(prov + stateName);
if (statesEditor.offsetParent) highlightEditorLine(statesEditor, state); if (statesEditor.offsetParent) highlightEditorLine(statesEditor, state);
if (diplomacyEditor.offsetParent) highlightEditorLine(diplomacyEditor, state); if (diplomacyEditor.offsetParent)
if (militaryOverview.offsetParent) highlightEditorLine(militaryOverview, state); highlightEditorLine(diplomacyEditor, state);
if (provincesEditor.offsetParent) highlightEditorLine(provincesEditor, province); if (militaryOverview.offsetParent)
highlightEditorLine(militaryOverview, state);
if (provincesEditor.offsetParent)
highlightEditorLine(provincesEditor, province);
} else if (layerIsOn("toggleCultures") && pack.cells.culture[i]) { } else if (layerIsOn("toggleCultures") && pack.cells.culture[i]) {
const culture = pack.cells.culture[i]; createMetaTip(point, e, i, g);
tip("Culture: " + pack.cultures[culture].name); if (culturesEditor.offsetParent) {
if (culturesEditor.offsetParent) highlightEditorLine(culturesEditor, culture); highlightEditorLine(culturesEditor, culture);
} else if (layerIsOn("toggleHeight")) tip("Height: " + getFriendlyHeight(point)); }
} else if (layerIsOn("toggleHeight"))
tip("Height: " + getFriendlyHeight(point));
} }
function highlightEditorLine(editor, id, timeout = 15000) { function highlightEditorLine(editor, id, timeout = 15000) {
Array.from(editor.getElementsByClassName("states hovered")).forEach(el => el.classList.remove("hovered")); // clear all hovered Array.from(editor.getElementsByClassName("states hovered")).forEach((el) =>
const hovered = Array.from(editor.querySelectorAll("div")).find(el => el.dataset.id == id); el.classList.remove("hovered")
); // clear all hovered
const hovered = Array.from(editor.querySelectorAll("div")).find(
(el) => el.dataset.id == id
);
if (hovered) hovered.classList.add("hovered"); // add hovered class if (hovered) hovered.classList.add("hovered"); // add hovered class
if (timeout) if (timeout)
setTimeout(() => { setTimeout(() => {
@ -228,23 +306,48 @@ function updateCellInfo(point, i, g) {
const x = (infoX.innerHTML = rn(point[0])); const x = (infoX.innerHTML = rn(point[0]));
const y = (infoY.innerHTML = rn(point[1])); const y = (infoY.innerHTML = rn(point[1]));
const f = cells.f[i]; const f = cells.f[i];
infoLat.innerHTML = toDMS(mapCoordinates.latN - (y / graphHeight) * mapCoordinates.latT, "lat"); infoLat.innerHTML = toDMS(
infoLon.innerHTML = toDMS(mapCoordinates.lonW + (x / graphWidth) * mapCoordinates.lonT, "lon"); mapCoordinates.latN - (y / graphHeight) * mapCoordinates.latT,
"lat"
);
infoLon.innerHTML = toDMS(
mapCoordinates.lonW + (x / graphWidth) * mapCoordinates.lonT,
"lon"
);
infoCell.innerHTML = i; infoCell.innerHTML = i;
const unit = areaUnit.value === "square" ? " " + distanceUnitInput.value + "²" : " " + areaUnit.value; const unit =
infoArea.innerHTML = cells.area[i] ? si(cells.area[i] * distanceScaleInput.value ** 2) + unit : "n/a"; areaUnit.value === "square"
? " " + distanceUnitInput.value + "²"
: " " + areaUnit.value;
infoArea.innerHTML = cells.area[i]
? si(cells.area[i] * distanceScaleInput.value ** 2) + unit
: "n/a";
infoEvelation.innerHTML = getElevation(pack.features[f], pack.cells.h[i]); infoEvelation.innerHTML = getElevation(pack.features[f], pack.cells.h[i]);
infoDepth.innerHTML = getDepth(pack.features[f], pack.cells.h[i], point); infoDepth.innerHTML = getDepth(pack.features[f], pack.cells.h[i], point);
infoTemp.innerHTML = convertTemperature(grid.cells.temp[g]); infoTemp.innerHTML = convertTemperature(grid.cells.temp[g]);
infoPrec.innerHTML = cells.h[i] >= 20 ? getFriendlyPrecipitation(i) : "n/a"; infoPrec.innerHTML = cells.h[i] >= 20 ? getFriendlyPrecipitation(i) : "n/a";
infoRiver.innerHTML = cells.h[i] >= 20 && cells.r[i] ? getRiverInfo(cells.r[i]) : "no"; infoRiver.innerHTML =
infoState.innerHTML = cells.h[i] >= 20 ? (cells.state[i] ? `${pack.states[cells.state[i]].fullName} (${cells.state[i]})` : "neutral lands (0)") : "no"; cells.h[i] >= 20 && cells.r[i] ? getRiverInfo(cells.r[i]) : "no";
infoProvince.innerHTML = cells.province[i] ? `${pack.provinces[cells.province[i]].fullName} (${cells.province[i]})` : "no"; infoState.innerHTML =
infoCulture.innerHTML = cells.culture[i] ? `${pack.cultures[cells.culture[i]].name} (${cells.culture[i]})` : "no"; cells.h[i] >= 20
infoReligion.innerHTML = cells.religion[i] ? `${pack.religions[cells.religion[i]].name} (${cells.religion[i]})` : "no"; ? cells.state[i]
? `${pack.states[cells.state[i]].fullName} (${cells.state[i]})`
: "neutral lands (0)"
: "no";
infoProvince.innerHTML = cells.province[i]
? `${pack.provinces[cells.province[i]].fullName} (${cells.province[i]})`
: "no";
infoCulture.innerHTML = cells.culture[i]
? `${pack.cultures[cells.culture[i]].name} (${cells.culture[i]})`
: "no";
infoReligion.innerHTML = cells.religion[i]
? `${pack.religions[cells.religion[i]].name} (${cells.religion[i]})`
: "no";
infoPopulation.innerHTML = getFriendlyPopulation(i); infoPopulation.innerHTML = getFriendlyPopulation(i);
infoBurg.innerHTML = cells.burg[i] ? pack.burgs[cells.burg[i]].name + " (" + cells.burg[i] + ")" : "no"; infoBurg.innerHTML = cells.burg[i]
? pack.burgs[cells.burg[i]].name + " (" + cells.burg[i] + ")"
: "no";
infoFeature.innerHTML = f ? pack.features[f].group + " (" + f + ")" : "n/a"; infoFeature.innerHTML = f ? pack.features[f].group + " (" + f + ")" : "n/a";
infoBiome.innerHTML = biomesData.name[cells.biome[i]]; infoBiome.innerHTML = biomesData.name[cells.biome[i]];
} }
@ -255,7 +358,8 @@ function toDMS(coord, c) {
const minutesNotTruncated = (Math.abs(coord) - degrees) * 60; const minutesNotTruncated = (Math.abs(coord) - degrees) * 60;
const minutes = Math.floor(minutesNotTruncated); const minutes = Math.floor(minutesNotTruncated);
const seconds = Math.floor((minutesNotTruncated - minutes) * 60); const seconds = Math.floor((minutesNotTruncated - minutes) * 60);
const cardinal = c === "lat" ? (coord >= 0 ? "N" : "S") : coord >= 0 ? "E" : "W"; const cardinal =
c === "lat" ? (coord >= 0 ? "N" : "S") : coord >= 0 ? "E" : "W";
return degrees + "° " + minutes + " " + seconds + "″ " + cardinal; return degrees + "° " + minutes + " " + seconds + "″ " + cardinal;
} }
@ -310,13 +414,15 @@ function getFriendlyPrecipitation(i) {
} }
function getRiverInfo(id) { function getRiverInfo(id) {
const r = pack.rivers.find(r => r.i == id); const r = pack.rivers.find((r) => r.i == id);
return r ? `${r.name} ${r.type} (${id})` : "n/a"; return r ? `${r.name} ${r.type} (${id})` : "n/a";
} }
function getCellPopulation(i) { function getCellPopulation(i) {
const rural = pack.cells.pop[i] * populationRate; const rural = pack.cells.pop[i] * populationRate;
const urban = pack.cells.burg[i] ? pack.burgs[pack.cells.burg[i]].population * populationRate * urbanization : 0; const urban = pack.cells.burg[i]
? pack.burgs[pack.cells.burg[i]].population * populationRate * urbanization
: 0;
return [rural, urban]; return [rural, urban];
} }
@ -328,7 +434,9 @@ function getFriendlyPopulation(i) {
function getPopulationTip(i) { function getPopulationTip(i) {
const [rural, urban] = getCellPopulation(i); const [rural, urban] = getCellPopulation(i);
return `Cell population: ${si(rural + urban)}; Rural: ${si(rural)}; Urban: ${si(urban)}`; return `Cell population: ${si(rural + urban)}; Rural: ${si(
rural
)}; Urban: ${si(urban)}`;
} }
function highlightEmblemElement(type, el) { function highlightEmblemElement(type, el) {
@ -357,11 +465,13 @@ function highlightEmblemElement(type, el) {
const [x, y] = el.pole || pack.cells.p[el.center]; const [x, y] = el.pole || pack.cells.p[el.center];
const obj = type === "state" ? cells.state : cells.province; const obj = type === "state" ? cells.state : cells.province;
const borderCells = cells.i.filter(id => obj[id] === i && cells.c[id].some(n => obj[n] !== i)); const borderCells = cells.i.filter(
(id) => obj[id] === i && cells.c[id].some((n) => obj[n] !== i)
);
const data = Array.from(borderCells) const data = Array.from(borderCells)
.filter((c, i) => !(i % 2)) .filter((c, i) => !(i % 2))
.map(i => cells.p[i]) .map((i) => cells.p[i])
.map(i => [i[0], i[1], Math.hypot(i[0] - x, i[1] - y)]); .map((i) => [i[0], i[1], Math.hypot(i[0] - x, i[1] - y)]);
debug debug
.selectAll("line") .selectAll("line")
@ -370,19 +480,19 @@ function highlightEmblemElement(type, el) {
.append("line") .append("line")
.attr("x1", x) .attr("x1", x)
.attr("y1", y) .attr("y1", y)
.attr("x2", d => d[0]) .attr("x2", (d) => d[0])
.attr("y2", d => d[1]) .attr("y2", (d) => d[1])
.attr("stroke", "#d0240f") .attr("stroke", "#d0240f")
.attr("stroke-width", 0.5) .attr("stroke-width", 0.5)
.attr("opacity", 0.2) .attr("opacity", 0.2)
.attr("stroke-dashoffset", d => d[2]) .attr("stroke-dashoffset", (d) => d[2])
.attr("stroke-dasharray", d => d[2]) .attr("stroke-dasharray", (d) => d[2])
.transition(animation) .transition(animation)
.attr("stroke-dashoffset", 0) .attr("stroke-dashoffset", 0)
.attr("opacity", 1) .attr("opacity", 1)
.transition(animation) .transition(animation)
.delay(1000) .delay(1000)
.attr("stroke-dashoffset", d => d[2]) .attr("stroke-dashoffset", (d) => d[2])
.attr("opacity", 0) .attr("opacity", 0)
.remove(); .remove();
} }
@ -390,8 +500,14 @@ function highlightEmblemElement(type, el) {
// assign lock behavior // assign lock behavior
document.querySelectorAll("[data-locked]").forEach(function (e) { document.querySelectorAll("[data-locked]").forEach(function (e) {
e.addEventListener("mouseover", function (event) { e.addEventListener("mouseover", function (event) {
if (this.className === "icon-lock") tip("Click to unlock the option and allow it to be randomized on new map generation"); if (this.className === "icon-lock")
else tip("Click to lock the option and always use the current value on new map generation"); tip(
"Click to unlock the option and allow it to be randomized on new map generation"
);
else
tip(
"Click to lock the option and always use the current value on new map generation"
);
event.stopPropagation(); event.stopPropagation();
}); });
@ -433,7 +549,7 @@ function stored(option) {
} }
// assign skeaker behaviour // assign skeaker behaviour
Array.from(document.getElementsByClassName("speaker")).forEach(el => { Array.from(document.getElementsByClassName("speaker")).forEach((el) => {
const input = el.previousElementSibling; const input = el.previousElementSibling;
el.addEventListener("click", () => speak(input.value)); el.addEventListener("click", () => speak(input.value));
}); });
@ -450,7 +566,7 @@ function speak(text) {
// apply drop-down menu option. If the value is not in options, add it // apply drop-down menu option. If the value is not in options, add it
function applyOption(select, id, name = id) { function applyOption(select, id, name = id) {
const custom = !Array.from(select.options).some(o => o.value == id); const custom = !Array.from(select.options).some((o) => o.value == id);
if (custom) select.options.add(new Option(name, id)); if (custom) select.options.add(new Option(name, id));
select.value = id; select.value = id;
} }
@ -460,11 +576,20 @@ function showInfo() {
const Discord = link("https://discordapp.com/invite/X7E84HU", "Discord"); const Discord = link("https://discordapp.com/invite/X7E84HU", "Discord");
const Reddit = link("https://www.reddit.com/r/FantasyMapGenerator", "Reddit"); const Reddit = link("https://www.reddit.com/r/FantasyMapGenerator", "Reddit");
const Patreon = link("https://www.patreon.com/azgaar", "Patreon"); const Patreon = link("https://www.patreon.com/azgaar", "Patreon");
const Trello = link("https://trello.com/b/7x832DG4/fantasy-map-generator", "Trello"); const Trello = link(
"https://trello.com/b/7x832DG4/fantasy-map-generator",
"Trello"
);
const Armoria = link("https://azgaar.github.io/Armoria", "Armoria"); const Armoria = link("https://azgaar.github.io/Armoria", "Armoria");
const QuickStart = link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Quick-Start-Tutorial", "Quick start tutorial"); const QuickStart = link(
const QAA = link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Q&A", "Q&A page"); "https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Quick-Start-Tutorial",
"Quick start tutorial"
);
const QAA = link(
"https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Q&A",
"Q&A page"
);
alertMessage.innerHTML = ` alertMessage.innerHTML = `
<b>Fantasy Map Generator</b> (FMG) is an open-source application, it means the code is published an anyone can use it. <b>Fantasy Map Generator</b> (FMG) is an open-source application, it means the code is published an anyone can use it.
@ -482,10 +607,22 @@ function showInfo() {
<b>Links:</b> <b>Links:</b>
<ul style="columns:2"> <ul style="columns:2">
<li>${link("https://github.com/Azgaar/Fantasy-Map-Generator", "GitHub repository")}</li> <li>${link(
<li>${link("https://github.com/Azgaar/Fantasy-Map-Generator/blob/master/LICENSE", "License")}</li> "https://github.com/Azgaar/Fantasy-Map-Generator",
<li>${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog", "Changelog")}</li> "GitHub repository"
<li>${link("https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Hotkeys", "Hotkeys")}</li> )}</li>
<li>${link(
"https://github.com/Azgaar/Fantasy-Map-Generator/blob/master/LICENSE",
"License"
)}</li>
<li>${link(
"https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Changelog",
"Changelog"
)}</li>
<li>${link(
"https://github.com/Azgaar/Fantasy-Map-Generator/wiki/Hotkeys",
"Hotkeys"
)}</li>
</ul>`; </ul>`;
$("#alert").dialog({ $("#alert").dialog({
@ -495,8 +632,8 @@ function showInfo() {
buttons: { buttons: {
OK: function () { OK: function () {
$(this).dialog("close"); $(this).dialog("close");
}
}, },
position: {my: "center", at: "center", of: "svg"} },
position: { my: "center", at: "center", of: "svg" },
}); });
} }

File diff suppressed because it is too large Load diff