This commit is contained in:
Azgaar 2025-10-31 01:55:17 +00:00 committed by GitHub
commit 1bd6685a1d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
47 changed files with 5095 additions and 1837 deletions

View file

@ -19,6 +19,11 @@ font {
pointer-events: none;
}
form input:invalid {
outline: 1px solid #ed4337;
outline-offset: 1px;
}
input,
select,
button {
@ -53,6 +58,7 @@ input:read-only {
input[type="radio"] {
vertical-align: bottom;
cursor: pointer;
accent-color: var(--header);
}
textarea {
@ -1506,12 +1512,6 @@ div.states > select {
appearance: none;
}
div.states > .burgName,
div.states > .burgState,
div.states > .burgCulture {
width: 6em;
}
div.states span.inactive {
color: #c6c2c2;
}
@ -1893,10 +1893,11 @@ div.editorLine {
}
#militaryOptionsTable input[type="number"] {
width: 4em;
width: 5em;
}
#militaryOptionsTable button {
#militaryOptionsTable button,
#burgGroupsBody button {
width: 100%;
}
@ -1929,7 +1930,12 @@ ul.share-buttons img {
width: 2em;
}
input[type="checkbox"] {
input[type="checkbox"].native {
accent-color: var(--header);
cursor: pointer;
}
input[type="checkbox"]:not(.native) {
display: none;
}

View file

@ -646,14 +646,6 @@
>
Emblems
</li>
<li
id="toggleLabels"
data-tip="Labels: click to toggle, drag to raise or lower the layer. Ctrl + click to edit layer style"
data-shortcut="L"
onclick="toggleLabels(event)"
>
<u>L</u>abels
</li>
<li
id="toggleBurgIcons"
data-tip="Burg icons: click to toggle, drag to raise or lower the layer. Ctrl + click to edit layer style"
@ -662,6 +654,14 @@
>
<u>I</u>cons
</li>
<li
id="toggleLabels"
data-tip="Labels: click to toggle, drag to raise or lower the layer. Ctrl + click to edit layer style"
data-shortcut="L"
onclick="toggleLabels(event)"
>
<u>L</u>abels
</li>
<li
id="toggleMilitary"
data-tip="Military forces: click to toggle, drag to raise or lower the layer. Ctrl + click to edit layer style"
@ -1057,6 +1057,61 @@
</tr>
</tbody>
<tbody id="styleBurgIcons">
<tr data-tip="Select group icon">
<td>Icon</td>
<td>
<select id="styleBurgIconsIcon">
<option value="#icon-circle">Circle</option>
<option value="#icon-square">Square</option>
<option value="#icon-triangle">Triangle</option>
<option value="#icon-cross">Cross</option>
<option value="#icon-star">Star</option>
<option value="#icon-circled">Circled</option>
<option value="#icon-squared">Squared</option>
<option value="#icon-star-circled">Star circled</option>
<option value="#icon-star-circled-empty">Star circled empty</option>
<option value="#icon-star-squared">Star squared</option>
<option value="#icon-watabou-capital">Watabou capital</option>
<option value="#icon-watabou-city">Watabou city</option>
<option value="#icon-watabou-town">Watabou town</option>
<option value="#icon-watabou-village">Watabou village</option>
<option value="#icon-watabou-hamlet">Watabou hamlet</option>
<option value="#icon-watabou-fort">Watabou fort</option>
<option value="#icon-watabou-monastery">Watabou monastery</option>
<option value="#icon-watabou-caravanserai">Watabou caravanserai</option>
<option value="#icon-watabou-post">Watabou trade post</option>
</select>
</td>
</tr>
<tr data-tip="Set icon size">
<td>Icon size</td>
<td>
<slider-input id="styleBurgIconsIconSize" min="0.01" max="20" step=".01"></slider-input>
</td>
</tr>
<tr data-tip="Set icon stroke linejoin"></tr>
<td>Stroke linejoin</td>
<td>
<select id="styleBurgIconsStrokeLinejoin">
<option value="inherit" selected>Inherit</option>
<option value="butt">Butt</option>
<option value="round">Round</option>
<option value="square">Square</option>
</select>
</td>
</tr>
<tr data-tip="Define transparency of fill color">
<td>Fill opacity</td>
<td>
<slider-input id="styleBurgIconsFillOpacity" min="0" max="1" step=".01"></slider-input>
</td>
</tr>
</tbody>
<tbody id="styleGrid">
<tr data-tip="Select grid overlay type">
<td>Type</td>
@ -1175,7 +1230,7 @@
<tr data-tip="Set stroke width">
<td>Stroke width</td>
<td>
<slider-input id="styleStrokeWidthInput" min="0" max="5" step=".01"></slider-input>
<slider-input id="styleStrokeWidthInput" min="0" max="10" step=".01"></slider-input>
</td>
</tr>
</tbody>
@ -1184,7 +1239,7 @@
<tr data-tip="Set letter spacing">
<td>Letter spacing</td>
<td>
<slider-input id="styleLetterSpacingInput" min="0" max="20" step=".01"></slider-input>
<slider-input id="styleLetterSpacingInput" min="-1" max="10" step=".01"></slider-input>
</td>
</tr>
</tbody>
@ -1208,7 +1263,7 @@
<tr data-tip="Set text shadow">
<td>Text shadow</td>
<td>
<input id="styleShadowInput" type="text" value="0 0 4px white" />
<input id="styleShadowInput" type="text" />
</td>
</tr>
</tbody>
@ -1234,24 +1289,12 @@
</tr>
</tbody>
<tbody id="styleRadius">
<tr data-tip="Set icon size">
<td>Radius</td>
<tbody id="styleFontShift">
<tr data-tip="Set label shift along X and Y axes">
<td>Label shift</td>
<td>
<button id="styleRadiusPlus" data-tip="Multiply radius by 1.1" class="whiteButton">+</button>
<button id="styleRadiusMinus" data-tip="Multiply radius by 1.1" class="whiteButton">-</button>
<input id="styleRadiusInput" type="number" min=".2" max="10" step=".02" value="1" />
</td>
</tr>
</tbody>
<tbody id="styleIconSize">
<tr data-tip="Set icon size">
<td>Size</td>
<td>
<button id="styleIconSizePlus" data-tip="Multiply size by 1.1" class="whiteButton">+</button>
<button id="styleIconSizeMinus" data-tip="Multiply size by 1.1" class="whiteButton">-</button>
<input id="styleIconSizeInput" type="number" min=".2" max="10" step=".02" value="1" />
<input id="styleFontShiftX" data-tip="Set label shift along Y axis" type="number" min="-5" max="5" step=".01" />
<input id="styleFontShiftY" data-tip="Set label shift along Y axis" type="number" min="-5" max="5" step=".01" />
</td>
</tr>
</tbody>
@ -1707,11 +1750,11 @@
</td>
</tr>
<tr data-tip="Define a number of towns to be placed (if enough suitable land exists)">
<tr data-tip="Define a number of non-capital settlements to be placed (if enough suitable land exists)">
<td>
<i data-locked="0" id="lock_manors" class="icon-lock-open"></i>
</td>
<td>Towns number</td>
<td>Burgs number</td>
<td>
<input id="manorsInput" data-stored="manors" type="range" min="0" max="1000" step="1" value="1000" />
</td>
@ -3373,6 +3416,12 @@
></span>
</div>
<div data-tip="Select burg group. Groups defines burg icon, label size and style">
<div class="label">Group:</div>
<select id="burgGroup" style="width: 9em"></select>
<span id="burgGroupConfigure" data-tip="Configure burg groups" class="icon-cog pointer"></span>
</div>
<div data-tip="Select burg type. Type slightly affects emblem generation">
<div class="label">Type:</div>
<select id="burgType" style="width: 9em">
@ -3477,12 +3526,6 @@
<div style="display: flex; justify-content: space-between">
<span>Burg preview:</span>
<div style="display: flex; gap: 0.5em">
<i
id="burgLinkEdit"
data-tip="Provide custom link to the burg map"
class="icon-pencil pointer"
style="margin-top: -0.1em"
></i>
<i id="burgLinkOpen" data-tip="Open burg map in a new tab" class="icon-link-ext pointer"></i>
</div>
</div>
@ -3491,20 +3534,6 @@
</div>
<div id="burgBottom">
<button id="burgGroupShow" data-tip="Show group change section" class="icon-tags"></button>
<div id="burgGroupSection" style="display: none">
<button id="burgGroupHide" data-tip="Hide group change section" class="icon-tags"></button>
<select id="burgSelectGroup" data-tip="Select a group for this burg" style="width: 10em"></select>
<input
id="burgInputGroup"
placeholder="new group name"
data-tip="Create a new Group for the Burg"
style="display: none; width: 10em"
/>
<i id="burgAddGroup" data-tip="Create a new group for the burg" class="icon-plus pointer"></i>
<i id="burgRemoveGroup" data-tip="Remove selected burg group" class="icon-trash pointer"></i>
</div>
<button id="burgStyleShow" data-tip="Show style edit section" class="icon-brush"></button>
<div id="burgStyleSection" style="display: none">
<button id="burgStyleHide" data-tip="Hide style edit section" class="icon-brush"></button>
@ -3526,7 +3555,7 @@
</div>
<button id="burgEditEmblem" data-tip="Edit emblem" class="icon-shield-alt"></button>
<button id="burgTogglePreview" data-tip="Toggle preview" class="icon-map"></button>
<button id="burgSetPreviewLink" data-tip="Set custom burg map URL" class="icon-map-o"></button>
<button id="burgLocate" data-tip="Zoom map and center view in the burg" class="icon-target"></button>
<button
id="burgRelocate"
@ -5266,7 +5295,7 @@
</slider-input>
</div>
<div data-tip="Set urbanization rate: burgs population relative to all population">
<div data-tip="Set urban population modifier. Change to increase or descrese burgs population">
<slider-input id="urbanizationInput" data-stored="urbanization" min=".01" max="5" step=".01" value="1">
<label>Urbanization rate:</label>
</slider-input>
@ -5309,7 +5338,7 @@
</div>
<div id="burgsOverview" class="dialog stable" style="display: none">
<div id="burgsHeader" class="header" style="grid-template-columns: 8em 6em 6em 6em 8em 6em">
<div id="burgsHeader" class="header" style="grid-template-columns: 9em 7em 7.5em 7.2em 6.5em 7em 6em">
<div data-tip="Click to sort by burg name" class="sortable alphabetically" data-sortby="name">Burg</div>
<div data-tip="Click to sort by province name" class="sortable alphabetically" data-sortby="province">
Province
@ -5318,6 +5347,7 @@
<div data-tip="Click to sort by culture name" class="sortable alphabetically" data-sortby="culture">
Culture
</div>
<div data-tip="Click to sort by culture group" class="sortable alphabetically" data-sortby="group">Group</div>
<div
data-tip="Click to sort by burg population"
class="sortable icon-sort-number-down"
@ -5352,6 +5382,7 @@
<div id="burgsBottom">
<button id="burgsOverviewRefresh" data-tip="Refresh the Editor" class="icon-cw"></button>
<button id="burgsGroupsEditorButton" data-tip="Edit burg groups" class="icon-cog"></button>
<button id="burgsChart" data-tip="Show burgs bubble chart" class="icon-chart-area"></button>
<button
id="regenerateBurgNames"
@ -5374,6 +5405,37 @@
</div>
</div>
<div id="burgGroupsEditor" class="dialog stable" style="display: none">
<form id="burgGroupsForm">
<table class="table">
<thead>
<tr>
<th data-tip="Rendering order: higher values are rendered on top">Order</th>
<th data-tip="Type group name">Name</th>
<th data-tip="Burg preview generator">Preview generator</th>
<th data-tip="Set min population constraint" colspan="2">Population</th>
<th
data-tip="Set population percentile: 0-100, where 90 means the burg must have a population higher than 90% of all burgs"
>
Percentile
</th>
<th data-tip="Select allowed biomes">Biomes</th>
<th data-tip="Select allowed states">States</th>
<th data-tip="Select allowed cultures">Cultures</th>
<th data-tip="Select allowed religions">Religions</th>
<th data-tip="Select allowed features">Features</th>
<th data-tip="Number of burgs in group">Count</th>
<th data-tip="Activate/deactivate group">Active</th>
<th data-tip="Select group to be assigned if burg doesn't pass the criteria for other groups">
Default
</th>
</tr>
</thead>
<tbody id="burgGroupsBody"></tbody>
</table>
</form>
</div>
<div id="routesOverview" class="dialog stable" style="display: none">
<div id="routesHeader" class="header" style="grid-template-columns: 17em 8em 8em">
<div data-tip="Click to sort by route name" class="sortable alphabetically" data-sortby="name">
@ -5931,6 +5993,11 @@
>
</div>
<div data-tip="Toggle wireframe mode" style="margin: 0.6em 0 0.3em -0.2em">
<input id="options3dMeshWireframeMode" class="checkbox" type="checkbox" />
<label for="options3dMeshWireframeMode" class="checkbox-label"><i>Show wireframe</i></label>
</div>
<div data-tip="Set sky and water color" id="options3dColorSection" style="display: none">
<span>Sky:</span
><input
@ -6230,7 +6297,7 @@
<input type="file" accept=".csv" id="culturesCSVToLoad" />
</div>
<!-- svg elements not required for map display -->
<!-- reusable svg elements -->
<svg id="defElements" width="0" height="0" style="position: absolute">
<defs>
<marker id="end-arrow" viewBox="0 -5 10 10" refX="6" markerWidth="7" markerHeight="7" orient="auto">
@ -6240,25 +6307,6 @@
<path d="M0,-5L10,0L0,5" fill="#555" />
</marker>
<symbol id="icon-store" viewBox="0 0 616 512">
<path
d="M602 118.6L537.1 15C531.3 5.7 521 0 510 0H106C95 0 84.7 5.7 78.9 15L14 118.6c-33.5 53.5-3.8 127.9 58.8 136.4 4.5.6 9.1.9 13.7.9 29.6 0 55.8-13 73.8-33.1 18 20.1 44.3 33.1 73.8 33.1 29.6 0 55.8-13 73.8-33.1 18 20.1 44.3 33.1 73.8 33.1 29.6 0 55.8-13 73.8-33.1 18.1 20.1 44.3 33.1 73.8 33.1 4.7 0 9.2-.3 13.7-.9 62.8-8.4 92.6-82.8 59-136.4zM529.5 288c-10 0-19.9-1.5-29.5-3.8V384H116v-99.8c-9.6 2.2-19.5 3.8-29.5 3.8-6 0-12.1-.4-18-1.2-5.6-.8-11.1-2.1-16.4-3.6V480c0 17.7 14.3 32 32 32h448c17.7 0 32-14.3 32-32V283.2c-5.4 1.6-10.8 2.9-16.4 3.6-6.1.8-12.1 1.2-18.2 1.2z"
/>
</symbol>
<symbol id="icon-anchor" viewBox="0 0 30 28">
<title>Port</title>
<path
d="M15 4c0-0.547-0.453-1-1-1s-1 0.453-1 1 0.453 1 1 1 1-0.453 1-1zM28 18.5v5.5c0 0.203-0.125 0.391-0.313 0.469-0.063 0.016-0.125 0.031-0.187 0.031-0.125 0-0.25-0.047-0.359-0.141l-1.453-1.453c-2.453 2.953-6.859 4.844-11.688 4.844s-9.234-1.891-11.688-4.844l-1.453 1.453c-0.094 0.094-0.234 0.141-0.359 0.141-0.063 0-0.125-0.016-0.187-0.031-0.187-0.078-0.313-0.266-0.313-0.469v-5.5c0-0.281 0.219-0.5 0.5-0.5h5.5c0.203 0 0.391 0.125 0.469 0.313s0.031 0.391-0.109 0.547l-1.563 1.563c1.406 1.891 4.109 3.266 7.203 3.687v-10.109h-3c-0.547 0-1-0.453-1-1v-2c0-0.547 0.453-1 1-1h3v-2.547c-1.188-0.688-2-1.969-2-3.453 0-2.203 1.797-4 4-4s4 1.797 4 4c0 1.484-0.812 2.766-2 3.453v2.547h3c0.547 0 1 0.453 1 1v2c0 0.547-0.453 1-1 1h-3v10.109c3.094-0.422 5.797-1.797 7.203-3.687l-1.563-1.563c-0.141-0.156-0.187-0.359-0.109-0.547s0.266-0.313 0.469-0.313h5.5c0.281 0 0.5 0.219 0.5 0.5z"
/>
</symbol>
<symbol id="icon-route" viewBox="0 0 512 512">
<path
d="M416 320h-96c-17.6 0-32-14.4-32-32s14.4-32 32-32h96s96-107 96-160-43-96-96-96-96 43-96 96c0 25.5 22.2 63.4 45.3 96H320c-52.9 0-96 43.1-96 96s43.1 96 96 96h96c17.6 0 32 14.4 32 32s-14.4 32-32 32H185.5c-16 24.8-33.8 47.7-47.3 64H416c52.9 0 96-43.1 96-96s-43.1-96-96-96zm0-256c17.7 0 32 14.3 32 32s-14.3 32-32 32-32-14.3-32-32 14.3-32 32-32zM96 256c-53 0-96 43-96 96s96 160 96 160 96-107 96-160-43-96-96-96zm0 128c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32z"
/>
</symbol>
<g id="defs-relief">
<symbol id="relief-mount-1" viewBox="0 0 100 100">
<path d="m3,69 16,-12 31,-32 15,20 30,24" fill="#fff" stroke="#5c5c70" stroke-width="1" />
@ -7800,6 +7848,214 @@
</symbol>
</g>
<g id="defs-icons">
<symbol id="icon-circle" viewBox="0 0 10 10" width="1em" height="1em" overflow="visible">
<circle cx="0" cy="0" r="5" />
</symbol>
<symbol id="icon-square" viewBox="0 0 10 10" width="1em" height="1em" overflow="visible">
<rect x="-5" y="-5" width="10" height="10" />
</symbol>
<symbol id="icon-triangle" viewBox="0 0 10 10" width="1em" height="1em" overflow="visible">
<polygon points="0,-5 5,5 -5,5" />
</symbol>
<symbol id="icon-cross" viewBox="0 0 10 10" width="1em" height="1em" overflow="visible">
<polygon points="-1.5,-5 1.5,-5 1.5,-1.5 5,-1.5 5,1.5 1.5,1.5 1.5,5 -1.5,5 -1.5,1.5 -5,1.5 -5,-1.5 -1.5,-1.5" />
</symbol>
<symbol id="icon-star" viewBox="0 0 10 10" width="1em" height="1em" overflow="visible">
<polygon points="0,-5 1.5,-1.5 5,-1.5 2,1.5 3,5 0,3 -3,5 -2,1.5 -5,-1.5 -1.5,-1.5" />
</symbol>
<symbol id="icon-circled" viewBox="0 0 10 10" width="1em" height="1em" overflow="visible">
<circle cx="0" cy="0" r="5" />
<circle cx="0" cy="0" r="1" stroke-width="4.4"></circle>
</symbol>
<symbol id="icon-squared" viewBox="0 0 10 10" width="1em" height="1em" overflow="visible">
<rect x="-5" y="-5" width="10" height="10" />
<rect x="-1" y="-1" width="2" height="2" stroke-width="4.5"></rect>
</symbol>
<symbol id="icon-star-circled" viewBox="0 0 10 10" width="1em" height="1em" overflow="visible">
<circle cx="0" cy="0" r="5" />
<polygon points="0,-5 1.5,-1.5 5,-1.5 2,1.5 3,5 0,3 -3,5 -2,1.5 -5,-1.5 -1.5,-1.5" transform="scale(0.38 0.38)" stroke-width="5"></polygon>
</symbol>
<symbol id="icon-star-circled-empty" viewBox="0 0 10 10" width="1em" height="1em" overflow="visible">
<circle cx="0" cy="0" r="5" />
<polygon points="0,-5 1.5,-1.5 5,-1.5 2,1.5 3,5 0,3 -3,5 -2,1.5 -5,-1.5 -1.5,-1.5" transform="scale(0.78 0.78)" />
</symbol>
<symbol id="icon-star-squared" viewBox="0 0 10 10" width="1em" height="1em" overflow="visible">
<rect x="-5" y="-5" width="10" height="10" />
<polygon points="0,-5 1.5,-1.5 5,-1.5 2,1.5 3,5 0,3 -3,5 -2,1.5 -5,-1.5 -1.5,-1.5" transform="scale(0.78 0.78)" />
</symbol>
<symbol id="icon-watabou-capital" viewBox="0 0 100 100" width="1em" height="1em" overflow="visible">
<g transform="translate(-60 -194) scale(2 2)">
<path fill="#EBE8DF" d="M26 90H13l1.3-37.3-1-1v-16h12.6v16l-1 1L26 90"/>
<path d="m19.5 12 1.3 6.9 1 3.8 1 3 1.5 3.6 3.1 6.4H11.6l3.1-6.4 1.5-3.6 1-3 1-3.8 1.3-7"/>
<path fill="#4D3F36" d="M19.5 10.4V5.6L21.3 4l1-.3 1 .5.9 1 1 1.7.9 1 .9.5.9-.3.9-.8.7-.5.8-.2.6.2.5.5.6.4.7.3 1 .1h1.8l-1.8.6-1 .1H32l-.6-.2L31 8h-.6l-.8.4-.7.7-1 1.1-.8.5-1-.1-.9-.8-1-1.4-.9-.7-.9-.2-1 .6-1.8 2.2"/>
<path fill="#4D3F36" d="M19.5 12V5.5"/>
<path fill="#4D3F36" d="M17.2 46h1.6V42l-.3-.6h-1l-.3.6v4"/>
<path fill="#EBE8DF" d="M41.7 90H31.2l1-24.5-.8-.9v-16h10.1v16l-.8.9 1 24.5"/>
<path d="m36.5 29.7 1 5.6.8 3 .8 2.4 1.2 2.9 2.5 5H30.2l2.5-5 1.2-3 .8-2.3.7-3 1-5.6"/>
<path fill="#4D3F36" d="M36.5 28.2v-4.8l1.5-2 .9-.4.7.4.6 1 .7 1.7.7 1.1.8.5.7-.1.8-.7 1-.4h1l1 .2 1.2.5 1 .4 1 .3h2.4l-1.5.5-1 .2h-1.9l-1.2-.3-1 .1-1 .4-1 .7-.8.9-.7.3-.8-.2-.7-1-.7-1.4-.6-.8-.7-.2-.9.7-1.5 2.4"/>
<path fill="#4D3F36" d="M36.5 29.7v-6.3"/>
<path fill="#4D3F36" d="M34 59h1.6V55l-.3-.7h-1l-.3.7V59"/>
<path fill="#4D3F36" d="M47.6 88.8h1.6v-4.1l-.4-.7h-1l-.2.7v4.1"/>
<path fill="#EBE8DF" d="M14.8 94.6H1.5L2 84.4 8.3 73l6.3 11.4.2 10.2m8.3 0h-8.3l-.2-10.2L8.3 73l2 .4h4l2-.4 6.3 11.4.5 10.2"/>
<path d="M22.6 84.4h-8L8.3 73l2 .4h4l2-.4 6.3 11.4"/>
<path fill="#4D3F36" d="M9.5 94.6H7V89l.5-1H9l.5 1v5.5"/>
<path fill="#EBE8DF" d="M50.4 95.3H33.1l.5-10.4L42 70l8.3 15 .2 10.3m8.3 0h-8.3l-.2-10.4L41.9 70l2 .5 2 .1 2-.1 2-.5 8.3 15 .5 10.3"/>
<path d="M58.2 85h-8l-8.3-15 2 .5 2 .1 2-.1 2-.5 8.3 15"/>
<path fill="#4D3F36" d="M43 95.3h-2.3v-5.5l.5-1h1.4l.5 1v5.5"/>
<path fill="#EBE8DF" d="M27.9 97.3H13.4l1-19.8L21 65.8l6.5 11.7.5 19.8m8.5 0h-8.5l-.5-19.8-6.5-11.7 2 .4 2 .1 2-.1 2-.4 6.5 11.7 1 19.8"/>
<path d="M35.4 77.5h-8l-6.5-11.7 2 .4 2 .1 2-.1 2-.4 6.5 11.7"/>
<path fill="#4D3F36" d="m21.6 77.1.1.4-.1.4-.3.3-.4.1h-.4l-.3-.4v-.8l.3-.3h.8l.3.3"/>
<path fill="#4D3F36" d="M22.1 97.3h-2.4v-5.5l.5-1h1.4l.5 1v5.5"/>
</g>
<circle fill="#EBE8DF" stroke-width="3" cx="0" cy="0" r="10" />
</symbol>
<symbol id="icon-watabou-city" viewBox="0 0 100 100" width="1em" height="1em" overflow="visible">
<g transform="translate(-60 -150) scale(2 2)">
<path fill="#EBE8DF" d="M29.5 70H18L19 48.7l-.9-.9v-16h11v16l-.8 1L29.5 70"/>
<path d="m23.7 11 1.2 6.2.8 3.3 1 2.6 1.2 3.1 2.8 5.6H16.8l2.8-5.6 1.3-3.1.9-2.6.8-3.3 1.1-6.1"/>
<path fill="#4D3F36" d="M23.7 9.6V4.8l1.4-.7h.9l.8.3 1 .7 1.1 1 1 .6.9.2.7-.4.6-.7.7-.4h.7l.7.3.8.8.8.5.9.3h1l2-.1-2 .8-1 .2h-.9l-.8-.3-.8-.5-.7-.1-.7.2-.7.6-.6 1-.7.5h-.9l-1-.2-1-.7-1-.4-1-.1-.8.3-1.4 1M23.7 11V4.9M23 42.2h1.5v-4.1l-.3-.7h-1l-.3.7v4.1"/>
<path fill="#EBE8DF" d="M19 71.2H1.5L2 63l4-15 4.2.4 4.2.1 4.2-.1 4.1-.5-4 15 .2 8.3M2 63h16.7m8.4 8.2H19l-.2-8.2 4-15 4 15 .4 8.2"/>
<path d="M18.7 63H2l4-15 4.2.4 4.2.1 4.2-.1 4.1-.5-4 15"/>
<path fill="#4D3F36" d="M9.6 69.5h1.6v-4.2l-.3-.6h-1l-.3.6v4.2"/>
<path fill="#EBE8DF" d="M49.4 71.8H32.5l.5-8.9 4-14.6 4 .5 4.1.1 4-.1 4.1-.5-4 14.6.2 8.9M33 62.9h16.2m8.5 8.9h-8.3l-.2-8.9 4-14.6 4 14.6.5 8.9"/>
<path d="M49.2 63H33l4-14.7 4 .5 4.1.1 4-.1 4.1-.5-4 14.6"/>
<path fill="#4D3F36" d="M40.3 69.8h1.6v-4.2l-.3-.6h-1l-.3.6v4.2"/>
<path fill="#EBE8DF" d="M32.6 75.3H15.9l1-19 7.6-13.6 7.6 13.6.5 19m8.5 0h-8.5l-.5-19-7.6-13.6 2 .4 2 .1 2-.1 2-.5 7.6 13.8 1 19"/>
<path d="M40.1 56.4h-8l-7.6-13.8 2 .5 2 .1 2-.1 2-.5 7.6 13.8"/>
<path fill="#4D3F36" d="m25.2 56 .1.4-.1.4-.3.2-.4.1h-.4l-.3-.4-.1-.4.1-.3.3-.3.4-.2.4.2.3.3M25.7 75.3h-2.4V70l.5-1h1.4l.5 1v5.4"/>
</g>
<circle fill="#EBE8DF" stroke-width="3" cx="0" cy="0" r="10" />
</symbol>
<symbol id="icon-watabou-town" viewBox="0 0 100 100" width="1em" height="1em" overflow="visible">
<g transform="translate(0 -10) scale(2 2)">
<path fill="#EBE8DF" d="M18.9-2H2.6l.5-9 7.8-14 7.8 14 .2 9M27-2H19l-.2-9-7.8-14 2 .4 2 .1 2-.1 2-.5 7.8 14L27-2 Z"/>
<path d="M26.7-11h-8l-7.8-14 2 .4 2 .1 2-.1 2-.5 7.8 14"/>
<path fill="#4D3F36" stroke="none" d="M12-2H9.8v-5.4l.4-1h1.5l.5 1V-2"/>
<path fill="#EBE8DF" d="M-10.3.4h-19.6l.4-8 4-17.2 4.8.5 4.7.2 4.8-.2 4.7-.5-4 17.1.2 8m-19.2-8h19m8.4 8h-8.2l-.2-8 4-17 4 17 .4 8Z"/>
<path d="M-10.5-7.7h-19l4-17 4.8.4 4.7.2 4.8-.2 4.7-.5-4 17.1"/>
<path fill="#4D3F36" stroke="none" d="M-24-1.3h1.7v-4.1l-.4-.7h-1l-.2.7v4.1"/>
<path fill="#EBE8DF" d="M-1.4 5H-17l.8-15 7.2-13.1 7.3 13 .4 15.2M7 5h-8.4L-1.8-10-9-23.1l2 .3 2 .2 2-.2 2-.3 7.3 13L7 4 Z"/>
<path d="M6.2-10h-8L-9-23.1l2 .3 2 .2 2-.2 2-.3 7.3 13"/>
<path fill="#4D3F36" stroke="none" d="M-7.9 5h-2.4V-.3l.5-1h1.5l.4 1v5.5"/>
</g>
<circle fill="#EBE8DF" stroke-width="3" cx="0" cy="0" r="10" />
</symbol>
<symbol id="icon-watabou-village" viewBox="0 0 100 100" width="1em" height="1em" overflow="visible">
<g transform="translate(0 -6) scale(2 2)">
<path d="M 9.778,-3.849 L -3.296,-3.849 L -2.723,-15.318 L 3.384,-26.311 L 9.491,-15.318 L 9.778,-3.849 M 18.065,-3.849 L 9.778,-3.849 L 9.491,-15.318 L 3.384,-26.311 L 5.384,-25.981 L 7.384,-25.871 L 9.384,-25.981 L 11.384,-26.311 L 17.491,-15.318 L 18.065,-3.849" fill="#EBE8DF" />
<path d="M 17.491,-15.318 L 9.491,-15.318 L 3.384,-26.311 L 5.384,-25.981 L 7.384,-25.871 L 9.384,-25.981 L 11.384,-26.311 L 17.491,-15.318"/>
<path d="M 4.584,-3.849 L 2.184,-3.849 L 2.184,-9.289 L 2.664,-10.249 L 4.104,-10.249 L 4.584,-9.289 L 4.584,-3.849" fill="#4D3F36"/>
<path d="M -5.555,2.988 L -20.357,2.988 L -19.872,-6.711 L -15.872,-19.379 L -12.354,-18.999 L -8.835,-18.872 L -5.316,-18.999 L -1.797,-19.379 L -5.797,-6.711 L -5.555,2.988 M -19.872,-6.711 L -5.797,-6.711 M 2.688,2.988 L -5.555,2.988 L -5.797,-6.711 L -1.797,-19.379 L 2.203,-6.711 L 2.688,2.988" fill="#EBE8DF" />
<path d=" M -5.797,-6.711 L -19.872,-6.711 L -15.872,-19.379 L -12.354,-18.999 L -8.835,-18.872 L -5.316,-18.999 L -1.797,-19.379 L -5.797,-6.711"/>
<path d="M -13.635,0.539 L -12.035,0.539 L -12.035,-3.621 L -12.355,-4.261 L -13.315,-4.261 L -13.635,-3.621 L -13.635,0.539" fill="#4D3F36"/>
</g>
<circle fill="#EBE8DF" stroke-width="3" cx="0" cy="0" r="10" />
</symbol>
<symbol id="icon-watabou-hamlet" viewBox="0 0 100 100" width="1em" height="1em" overflow="visible">
<g transform="translate(-120 -95) scale(2 2)">
<path fill="#EBE8DF" d="M63 48H48.6l.4-8.7 6.8-12.2 6.8 12.2.2 8.7m8.3 0h-8.3l-.2-8.7L56 27.1l2 .3 2 .2 2-.2 2-.3 6.8 12.2.5 8.7"/>
<path d="M70.7 39.3h-8L56 27.1l2 .3 2 .2 2-.2 2-.3 6.8 12.2"/>
<path fill="#4D3F36" d="M57.1 48h-2.4v-5.4l.5-1h1.4l.5 1V48"/>
</g>
<circle fill="#EBE8DF" stroke-width="3" cx="0" cy="0" r="10" />
</symbol>
<symbol id="icon-watabou-fort" viewBox="0 0 100 100" width="1em" height="1em" overflow="visible">
<g transform="translate(0 0) scale(2 1.6)">
<path fill="#EBE8DF" d="M 4.439,0 L -18.531,0 L -18.994,-61.849 L 4.902,-61.849 L 4.439,0 Z"/>
<path fill="#EBE8DF" d="M 19.103,0 L 1.283,0 L 0.924,-45.875 L 19.462,-45.875 L 19.103,0 Z"/>
<path d="M 10.193,-49.907 L 10.193,-54.973 L 11.638,-55.519 L 12.545,-55.52 L 13.396,-55.159 L 14.352,-54.496 L 15.413,-53.531 L 16.454,-52.885 L 17.476,-52.559 L 18.478,-52.553 L 19.462,-52.865 L 20.367,-53.044 L 21.194,-53.089 L 22.112,-52.943 L 23.623,-52.44 L 22.112,-52.373 L 21.194,-52.173 L 20.367,-51.816 L 19.462,-51.296 L 18.478,-50.612 L 17.476,-50.241 L 16.454,-50.181 L 15.413,-50.434 L 14.352,-50.999 L 13.396,-51.301 L 12.545,-51.342 L 11.638,-50.998 L 10.193,-49.907"/>
<path fill="#EBE8DF" d="M 10.193,-45.875 L 10.193,-54.973"/>
<g stroke-width="0.6" stroke-linecap="mitter" fill="#4D3F36">
<path d="M -16.365,-58.727 L -16.365,-61.849 L -14.796,-61.849 L -14.796,-58.727 L -16.365,-58.727 Z"/>
<path d="M -12.951,-58.727 L -12.951,-61.849 L -11.382,-61.849 L -11.382,-58.727 L -12.951,-58.727 Z"/>
<path d="M -9.538,-58.727 L -9.538,-61.849 L -7.968,-61.849 L -7.968,-58.727 L -9.538,-58.727 Z"/>
<path d="M -6.124,-58.727 L -6.124,-61.849 L -4.554,-61.849 L -4.554,-58.727 L -6.124,-58.727 Z"/>
<path d="M -2.71,-58.727 L -2.71,-61.849 L -1.141,-61.849 L -1.141,-58.727 L -2.71,-58.727 Z"/>
<path d="M 0.704,-58.727 L 0.704,-61.849 L 2.273,-61.849 L 2.273,-58.727 L 0.704,-58.727 Z"/>
<path d="M 3.847,-42.752 L 3.847,-45.875 L 5.416,-45.875 L 5.416,-42.752 L 3.847,-42.752 Z"/>
<path d="M 7.555,-42.752 L 7.555,-45.875 L 9.124,-45.875 L 9.124,-42.752 L 7.555,-42.752 Z"/>
<path d="M 11.263,-42.752 L 11.263,-45.875 L 12.832,-45.875 L 12.832,-42.752 L 11.263,-42.752 Z"/>
<path d="M 14.97,-42.752 L 14.97,-45.875 L 16.539,-45.875 L 16.539,-42.752 L 14.97,-42.752 Z"/>
<path d="M -8.475,-52.589 L -6.906,-52.589 L -6.906,-55.16 L -7.181,-55.711 L -8.199,-55.711 L -8.475,-55.16 L -8.475,-52.589 Z"/>
<path d="M 9.463,-36.614 L 11.032,-36.614 L 11.032,-39.185 L 10.756,-39.737 L 9.739,-39.737 L 9.463,-39.185 L 9.463,-36.614 Z"/>
<path d="M -5,0 V -5 H -8 V 0"/>
</g>
</g>
<circle fill="#EBE8DF" stroke-width="3" cx="0" cy="0" r="10" />
</symbol>
<symbol id="icon-watabou-caravanserai" viewBox="0 0 100 100" width="1em" height="1em" overflow="visible">
<g transform="translate(21 -2) scale(2 2)">
<path d="M -7.316,1.248 L -24.272,1.248 L -22.919,-10.943 L -15.456,-10.943 L -7.993,-10.943 L -7.316,1.248 M 3.531,1.248 L -7.316,1.248 L -7.993,-10.943 L -15.456,-10.943 L -12.913,-10.943 L -10.371,-10.943 L -7.828,-10.943 L -5.286,-10.943 L 2.177,-10.943 L 3.531,1.248 M -7.993,-10.943 L 2.177,-10.943" fill="#EBE8DF"/>
<path d="M -14.585,1.248 L -16.326,1.248 L -16.326,-2.59 L -16.15,-2.943 L -14.761,-2.943 L -14.585,-2.59 L -14.585,1.248" fill="#4D3F36"/>
<path d="M -10.371,-14.623 L -10.371,-14.623 L -7.542,-14.187 L -4.975,-12.922 L -2.908,-10.943 L -17.833,-10.943 L -15.766,-12.922 L -13.199,-14.187 L -10.371,-14.623"/>
</g>
<circle fill="#EBE8DF" stroke-width="3" cx="0" cy="0" r="10" />
</symbol>
<symbol id="icon-watabou-monastery" viewBox="0 0 100 100" width="1em" height="1em" overflow="visible">
<g transform="translate(20 -10) scale(2 2)">
<g transform="translate(0 6)">
<path d="M -9.764,-0.93 L -29.859,-0.93 L -29.954,-10.141 L -25.873,-31.202 L -20.814,-31.292 L -15.755,-31.322 L -10.695,-31.292 L -5.636,-31.202 L -9.716,-10.141 L -9.764,-0.93 M -29.954,-10.141 L -9.716,-10.141 M -1.651,-0.93 L -9.764,-0.93 L -9.716,-10.141 L -5.636,-31.202 L -1.556,-10.141 L -1.651,-0.93" fill="#EBE8DF" stroke="#4D3F36"/>
<path d="M -9.716,-10.141 L -29.954,-10.141 L -25.873,-31.202 L -20.814,-31.292 L -15.755,-31.322 L -10.695,-31.292 L -5.636,-31.202 L -9.716,-10.141"/>
<path stroke="none" d="M -23.484,-0.93 L -26.304,-0.93 L -26.304,-8.18 L -26.046,-8.696 L -23.742,-8.696 L -23.484,-8.18 L -23.484,-0.93" fill="#4D3F36"/>
<path stroke="none" d="M -18.425,-0.93 L -21.245,-0.93 L -21.245,-8.18 L -20.987,-8.696 L -18.683,-8.696 L -18.425,-8.18 L -18.425,-0.93" fill="#4D3F36"/>
<path stroke="none" d="M -13.366,-0.93 L -16.185,-0.93 L -16.185,-8.18 L -15.928,-8.696 L -13.623,-8.696 L -13.366,-8.18 L -13.366,-0.93" fill="#4D3F36"/>
</g>
<path d="M 0.927,5.327 L -10.543,5.327 L -10.815,-20.976 L -4.876,-33.337 L 1.063,-20.976 L 0.927,5.327 M 8.952,5.327 L 0.927,5.327 L 1.063,-20.976 L -4.876,-33.337 L -2.836,-33.39 L -0.795,-33.408 L 1.245,-33.39 L 3.285,-33.337 L 9.224,-20.976 L 8.952,5.327" fill="#EBE8DF" stroke="#4D3F36"/>
<path d="M 9.224,-20.976 L 1.063,-20.976 L -4.876,-33.337 L -2.836,-33.39 L -0.795,-33.408 L 1.245,-33.39 L 3.285,-33.337 L 9.224,-20.976"/>
<path stroke="none" d="M -5.445,5.327 L -8.265,5.327 L -8.265,-1.923 L -8.007,-2.439 L -5.703,-2.439 L -5.445,-1.923 L -5.445,5.327" fill="#4D3F36"/>
<path stroke="none" d="M -1.486,5.327 L -4.306,5.327 L -4.306,-1.923 L -4.048,-2.439 L -1.744,-2.439 L -1.486,-1.923 L -1.486,5.327" fill="#4D3F36"/>
<circle cx="-5" cy="-20" r="1.6" fill="#4D3F36"/>
</g>
<circle fill="#EBE8DF" stroke-width="3" cx="0" cy="0" r="10" />
</symbol>
<symbol id="icon-watabou-post" viewBox="0 0 100 100" width="1em" height="1em" overflow="visible">
<g transform="translate(-10 -10) scale(2 2)">
<path d="M 12.321,4.738 L -8.119,4.738 L -8.522,-8.706 L 2,-23.172 L 12.522,-8.706 L 12.321,4.738 M 22.814,4.738 L 12.321,4.738 L 12.522,-8.706 L 2,-23.172 L 4.674,-22.56 L 7.347,-22.356 L 10.021,-22.56 L 12.695,-23.172 L 23.217,-8.706 L 22.814,4.738" fill="#EBE8DF"/>
<path d="M 23.217,-8.706 L 12.522,-8.706 L 2,-23.172 L 4.674,-22.56 L 7.347,-22.356 L 10.021,-22.56 L 12.695,-23.172 L 23.217,-8.706" />
<path d="M -4,-8 v12 M 9,-8 v12"/>
<path d="M -10,-4.6 h24 l-2,-4 h-20 z"/>
<path d="M 4.5,4.5 V -1 H 0 V 4" fill="#4D3F36"/>
<circle cx="2.2" cy="-13" r="1" fill="#4D3F36"/>
</g>
<circle fill="#EBE8DF" stroke-width="3" cx="0" cy="0" r="10" />
</symbol>
<symbol id="icon-anchor" viewBox="0 0 30 30" width="1em" height="1em" overflow="visible">
<path d="m 1.003,-9.873 c 0,-0.547 -0.453,-1 -1,-1 -0.547,0 -1,0.453 -1,1 0,0.547 0.453,1 1,1 0.547,0 1,-0.453 1,-1 z m 13,14.5 v 5.5 c 0,0.203 -0.125,0.391 -0.313,0.469 -0.063,0.016 -0.125,0.031 -0.187,0.031 -0.125,0 -0.25,-0.047 -0.359,-0.141 L 11.691,9.033 c -2.453,2.953 -6.859,4.844 -11.688,4.844 -4.829,0 -9.234,-1.891 -11.688,-4.844 l -1.453,1.453 c -0.094,0.094 -0.234,0.141 -0.359,0.141 -0.063,0 -0.125,-0.016 -0.187,-0.031 -0.187,-0.078 -0.313,-0.266 -0.313,-0.469 v -5.5 c 0,-0.281 0.219,-0.5 0.5,-0.5 h 5.5 c 0.203,0 0.391,0.125 0.469,0.313 0.078,0.188 0.031,0.391 -0.109,0.547 L -9.2,6.55 c 1.406,1.891 4.109,3.266 7.203,3.687 V 0.128 h -3 c -0.547,0 -1,-0.453 -1,-1 v -2 c 0,-0.547 0.453,-1 1,-1 h 3 v -2.547 c -1.188,-0.688 -2,-1.969 -2,-3.453 0,-2.203 1.797,-4 4,-4 2.203,0 4,1.797 4,4 0,1.484 -0.812,2.766 -2,3.453 v 2.547 h 3 c 0.547,0 1,0.453 1,1 v 2 c 0,0.547 -0.453,1 -1,1 h -3 V 10.237 C 5.097,9.815 7.8,8.44 9.206,6.55 L 7.643,4.987 C 7.502,4.831 7.456,4.628 7.534,4.44 7.612,4.252 7.8,4.127 8.003,4.127 h 5.5 c 0.281,0 0.5,0.219 0.5,0.5 z"/>
</symbol>
<symbol id="icon-store" viewBox="0 0 616 512">
<path
d="M602 118.6L537.1 15C531.3 5.7 521 0 510 0H106C95 0 84.7 5.7 78.9 15L14 118.6c-33.5 53.5-3.8 127.9 58.8 136.4 4.5.6 9.1.9 13.7.9 29.6 0 55.8-13 73.8-33.1 18 20.1 44.3 33.1 73.8 33.1 29.6 0 55.8-13 73.8-33.1 18 20.1 44.3 33.1 73.8 33.1 29.6 0 55.8-13 73.8-33.1 18.1 20.1 44.3 33.1 73.8 33.1 4.7 0 9.2-.3 13.7-.9 62.8-8.4 92.6-82.8 59-136.4zM529.5 288c-10 0-19.9-1.5-29.5-3.8V384H116v-99.8c-9.6 2.2-19.5 3.8-29.5 3.8-6 0-12.1-.4-18-1.2-5.6-.8-11.1-2.1-16.4-3.6V480c0 17.7 14.3 32 32 32h448c17.7 0 32-14.3 32-32V283.2c-5.4 1.6-10.8 2.9-16.4 3.6-6.1.8-12.1 1.2-18.2 1.2z"
/>
</symbol>
<symbol id="icon-route" viewBox="0 0 512 512">
<path
d="M416 320h-96c-17.6 0-32-14.4-32-32s14.4-32 32-32h96s96-107 96-160-43-96-96-96-96 43-96 96c0 25.5 22.2 63.4 45.3 96H320c-52.9 0-96 43.1-96 96s43.1 96 96 96h96c17.6 0 32 14.4 32 32s-14.4 32-32 32H185.5c-16 24.8-33.8 47.7-47.3 64H416c52.9 0 96-43.1 96-96s-43.1-96-96-96zm0-256c17.7 0 32 14.3 32 32s-14.3 32-32 32-32-14.3-32-32 14.3-32 32-32zM96 256c-53 0-96 43-96 96s96 160 96 160 96-107 96-160-43-96-96-96zm0 128c-17.7 0-32-14.3-32-32s14.3-32 32-32 32 14.3 32 32-14.3 32-32 32z"
/>
</symbol>
</g>
<g id="defs-compass-rose" stroke-width="1.1">
<g id="rose-coord-line" stroke="#3f3f3f">
<line id="sL1" x1="0" y1="-20000" x2="0" y2="20000" />
@ -8097,9 +8353,10 @@
<script src="modules/river-generator.js?v=1.106.7"></script>
<script src="modules/lakes.js?v=1.99.00"></script>
<script src="modules/biomes.js?v=1.99.00"></script>
<script src="modules/names-generator.js?v=1.105.11"></script>
<script src="modules/names-generator.js?v=1.106.0"></script>
<script src="modules/cultures-generator.js?v=1.106.0"></script>
<script src="modules/burgs-and-states.js?v=1.106.0"></script>
<script src="modules/burgs-generator.js?v=1.107.0"></script>
<script src="modules/states-generator.js?v=1.107.0"></script>
<script src="modules/provinces-generator.js?v=1.106.0"></script>
<script src="modules/routes-generator.js?v=1.106.0"></script>
<script src="modules/religions-generator.js?v=1.106.0"></script>
@ -8140,6 +8397,7 @@
<script defer src="modules/ui/rivers-editor.js?v=1.106.0"></script>
<script defer src="modules/ui/rivers-creator.js?v=1.106.0"></script>
<script defer src="modules/ui/relief-editor.js?v=1.99.00"></script>
<script defer src="modules/ui/burg-group-editor.js?v=1.106.0"></script>
<script defer src="modules/ui/burg-editor.js?v=1.106.6"></script>
<script defer src="modules/ui/units-editor.js?v=1.108.12"></script>
<script defer src="modules/ui/notes-editor.js?v=1.107.3"></script>

105
main.js
View file

@ -77,8 +77,8 @@ let ice = viewbox.append("g").attr("id", "ice");
let prec = viewbox.append("g").attr("id", "prec").style("display", "none");
let population = viewbox.append("g").attr("id", "population");
let emblems = viewbox.append("g").attr("id", "emblems").style("display", "none");
let labels = viewbox.append("g").attr("id", "labels");
let icons = viewbox.append("g").attr("id", "icons");
let labels = viewbox.append("g").attr("id", "labels");
let burgIcons = icons.append("g").attr("id", "burgIcons");
let anchors = icons.append("g").attr("id", "anchors");
let armies = viewbox.append("g").attr("id", "armies");
@ -106,17 +106,9 @@ coastline.append("g").attr("id", "lake_island");
terrs.append("g").attr("id", "oceanHeights");
terrs.append("g").attr("id", "landHeights");
let burgLabels = labels.append("g").attr("id", "burgLabels");
labels.append("g").attr("id", "states");
labels.append("g").attr("id", "addedLabels");
burgIcons.append("g").attr("id", "cities");
burgLabels.append("g").attr("id", "cities");
anchors.append("g").attr("id", "cities");
burgIcons.append("g").attr("id", "towns");
burgLabels.append("g").attr("id", "towns");
anchors.append("g").attr("id", "towns");
let burgLabels = labels.append("g").attr("id", "burgLabels");
// population groups
population.append("g").attr("id", "rural");
@ -159,9 +151,25 @@ let notes = [];
let rulers = new Rulers();
let customization = 0;
// global options; in v2.0 to be used for all UI settings
let options = {
pinNotes: false,
winds: [225, 45, 225, 315, 135, 315],
temperatureEquator: 27,
temperatureNorthPole: -30,
temperatureSouthPole: -15,
stateLabelsMode: "auto",
showBurgPreview: true,
burgs: {
groups: JSON.safeParse(localStorage.getItem("burg-groups")) || Burgs.getDefaultGroups()
}
};
// global style object; in v2.0 to be used for all map styles and render settings
let style = {burgLabels: {}, burgIcons: {}, anchors: {}};
let biomesData = Biomes.getDefault();
let nameBases = Names.getNameBases(); // cultures-related data
let color = d3.scaleSequential(d3.interpolateSpectral); // default color scheme
const lineGen = d3.line().curve(d3.curveBasis); // d3 line generator with default curve interpolation
@ -185,18 +193,6 @@ const onZoom = debounce(function () {
}, 50);
const zoom = d3.zoom().scaleExtent([1, 20]).on("zoom", onZoom);
// default options, based on Earth data
let options = {
pinNotes: false,
winds: [225, 45, 225, 315, 135, 315],
temperatureEquator: 27,
temperatureNorthPole: -30,
temperatureSouthPole: -15,
stateLabelsMode: "auto",
showBurgPreview: true,
villageMaxPopulation: 2000
};
let mapCoordinates = {}; // map coordinates on globe
let populationRate = +byId("populationRateInput").value;
let distanceScale = +byId("distanceScaleInput").value;
@ -531,12 +527,6 @@ function invokeActiveZooming() {
});
}
// turn off ocean pattern if scale is big (improves performance)
oceanPattern
.select("rect")
.attr("fill", scale > 10 ? "#fff" : "url(#oceanic)")
.attr("opacity", scale > 10 ? 0.2 : null);
// change states halo width
if (!customization && !isOptimized) {
const desired = +statesHalo.attr("data-width");
@ -650,13 +640,18 @@ async function generate(options) {
rankCells();
Cultures.generate();
Cultures.expand();
BurgsAndStates.generate();
Burgs.generate();
States.generate();
Routes.generate();
Religions.generate();
BurgsAndStates.defineStateForms();
Burgs.specify();
States.collectStatistics();
States.defineStateForms();
Provinces.generate();
Provinces.getPoles();
BurgsAndStates.defineBurgFeatures();
Rivers.specify();
Features.specify();
@ -683,7 +678,7 @@ async function generate(options) {
title: "Generation error",
width: "32em",
buttons: {
"Clear cache": () => cleanupData(),
"Cleanup data": () => cleanupData(),
Regenerate: function () {
regenerateMap("generation error");
$(this).dialog("close");
@ -1172,36 +1167,44 @@ function rankCells() {
cells.s = new Int16Array(cells.i.length); // cell suitability array
cells.pop = new Float32Array(cells.i.length); // cell population array
const flMean = d3.median(cells.fl.filter(f => f)) || 0;
const flMax = d3.max(cells.fl) + d3.max(cells.conf); // to normalize flux
const areaMean = d3.mean(cells.area); // to adjust population by cell area
const meanFlux = d3.median(cells.fl.filter(f => f)) || 0;
const maxFlux = d3.max(cells.fl) + d3.max(cells.conf); // to normalize flux
const meanArea = d3.mean(cells.area); // to adjust population by cell area
const scoreMap = {
estuary: 15,
ocean_coast: 5,
save_harbor: 20,
freshwater: 30,
salt: 10,
frozen: 1,
dry: -5,
sinkhole: -5,
lava: -30
};
for (const i of cells.i) {
if (cells.h[i] < 20) continue; // no population in water
let s = +biomesData.habitability[cells.biome[i]]; // base suitability derived from biome habitability
if (!s) continue; // uninhabitable biomes has 0 suitability
if (flMean) s += normalize(cells.fl[i] + cells.conf[i], flMean, flMax) * 250; // big rivers and confluences are valued
s -= (cells.h[i] - 50) / 5; // low elevation is valued, high is not;
let score = biomesData.habitability[cells.biome[i]]; // base suitability derived from biome habitability
if (!score) continue; // uninhabitable biomes has 0 suitability
if (meanFlux) score += normalize(cells.fl[i] + cells.conf[i], meanFlux, maxFlux) * 250; // big rivers and confluences are valued
score -= (cells.h[i] - 50) / 5; // low elevation is valued, high is not;
if (cells.t[i] === 1) {
if (cells.r[i]) s += 15; // estuary is valued
if (cells.r[i]) score += scoreMap.estuary;
const feature = features[cells.f[cells.haven[i]]];
if (feature.type === "lake") {
if (feature.group === "freshwater") s += 30;
else if (feature.group == "salt") s += 10;
else if (feature.group == "frozen") s += 1;
else if (feature.group == "dry") s -= 5;
else if (feature.group == "sinkhole") s -= 5;
else if (feature.group == "lava") s -= 30;
score += scoreMap[feature.water] || 0;
} else {
s += 5; // ocean coast is valued
if (cells.harbor[i] === 1) s += 20; // safe sea harbor is valued
score += scoreMap.ocean_coast;
if (cells.harbor[i] === 1) score += scoreMap.save_harbor;
}
}
cells.s[i] = s / 5; // general population rate
cells.s[i] = score / 5; // general population rate
// cell rural population is suitability adjusted by cell area
cells.pop[i] = cells.s[i] > 0 ? (cells.s[i] * cells.area[i]) / areaMean : 0;
cells.pop[i] = cells.s[i] > 0 ? (cells.s[i] * cells.area[i]) / meanArea : 0;
}
TIME && console.timeEnd("rankCells");

592
modules/burgs-generator.js Normal file
View file

@ -0,0 +1,592 @@
"use strict";
window.Burgs = (() => {
const generate = () => {
TIME && console.time("generateBurgs");
const {cells} = pack;
let burgs = [0]; // burgs array
cells.burg = new Uint16Array(cells.i.length);
const populatedCells = cells.i.filter(i => cells.s[i] > 0 && cells.culture[i]);
if (!populatedCells.length) {
WARN && console.warn("There is no populated cells. Cannot generate states");
return burgs;
}
let quadtree = d3.quadtree();
generateCapitals();
generateTowns();
shiftBurgs();
pack.burgs = burgs;
TIME && console.timeEnd("generateBurgs");
function getCapitalsNumber() {
let number = +byId("statesNumber").value;
if (populatedCells.length < number * 10) {
WARN && console.warn(`Not enough populated cells. Generating only ${number} capitals/states`);
number = Math.floor(sorted.length / 10);
}
return number;
}
function getTownsNumber() {
const manorsInput = byId("manorsInput");
const isAuto = manorsInput.value === "1000"; // '1000' is considered as auto
if (isAuto) return rn(populatedCells.length / 5 / (grid.points.length / 10000) ** 0.8);
return Math.min(manorsInput.valueAsNumber, sorted.length);
}
function generateCapitals() {
const randomize = score => score * (0.5 + Math.random() * 0.5);
const score = new Int16Array(cells.s.map(randomize));
const sorted = populatedCells.sort((a, b) => score[b] - score[a]);
const capitalsNumber = getCapitalsNumber();
let spacing = (graphWidth + graphHeight) / 2 / capitalsNumber; // min distance between capitals
for (let i = 0; burgs.length <= capitalsNumber; i++) {
const cell = sorted[i];
const [x, y] = cells.p[cell];
if (quadtree.find(x, y, spacing) === undefined) {
burgs.push({cell, x, y});
quadtree.add([x, y]);
}
// reset if all cells were checked
if (i === sorted.length - 1) {
WARN && console.warn("Cannot place capitals with current spacing. Trying again with reduced spacing");
quadtree = d3.quadtree();
i = -1;
burgs = [0];
spacing /= 1.2;
}
}
burgs.forEach((burg, burgId) => {
if (!burgId) return;
burg.i = burgId;
burg.state = burgId;
burg.culture = cells.culture[burg.cell];
burg.name = Names.getCultureShort(burg.culture);
burg.feature = cells.f[burg.cell];
burg.capital = 1;
cells.burg[burg.cell] = burgId;
});
}
function generateTowns() {
const randomize = score => score * gauss(1, 3, 0, 20, 3);
const score = new Int16Array(cells.s.map(randomize));
const sorted = populatedCells.sort((a, b) => score[b] - score[a]);
const burgsNumber = getTownsNumber();
let spacing = (graphWidth + graphHeight) / 150 / (burgsNumber ** 0.7 / 66); // min distance between town
for (let added = 0; added < burgsNumber && spacing > 1; ) {
for (let i = 0; added < burgsNumber && i < sorted.length; i++) {
if (cells.burg[sorted[i]]) continue;
const cell = sorted[i];
const [x, y] = cells.p[cell];
const minSpacing = spacing * gauss(1, 0.3, 0.2, 2, 2); // randomize to make placement not uniform
if (quadtree.find(x, y, minSpacing) !== undefined) continue; // to close to existing burg
const burgId = burgs.length;
const culture = cells.culture[cell];
const name = Names.getCulture(culture);
const feature = cells.f[cell];
burgs.push({cell, x, y, i: burgId, state: 0, culture, name, feature, capital: 0});
added++;
cells.burg[cell] = burgId;
}
spacing *= 0.5;
}
}
// define port status and shift ports and burgs on rivers
function shiftBurgs() {
const {cells, features} = pack;
const temp = grid.cells.temp;
// port is a capital with any harbor OR any burg with a safe harbor
const featurePorts = {};
for (const burg of burgs) {
if (!burg.i || burg.lock) continue;
const i = burg.cell;
const haven = cells.haven[i];
const harbor = cells.harbor[i];
if (haven !== undefined && temp[cells.g[i]] > 0) {
const featureId = cells.f[haven];
const canBePort = features[featureId].cells > 1 && ((burg.capital && harbor) || harbor === 1);
if (canBePort) {
if (!featurePorts[featureId]) featurePorts[featureId] = [];
featurePorts[featureId].push(burg);
}
}
}
// shift ports to the edge of the water body. Only bodies with 2+ ports are considered
Object.entries(featurePorts).forEach(([featureId, burgs]) => {
if (burgs.length < 2) return;
burgs.forEach(burg => {
burg.port = featureId;
const haven = cells.haven[burg.cell];
const [x, y] = getCloseToEdgePoint(burg.cell, haven);
burg.x = x;
burg.y = y;
});
});
// shift non-port river burgs a bit
for (const burg of burgs) {
if (!burg.i || burg.lock || burg.port || !cells.r[burg.cell]) continue;
const cellId = burg.cell;
const shift = Math.min(cells.fl[cellId] / 150, 1);
burg.x = cellId % 2 ? rn(burg.x + shift, 2) : rn(burg.x - shift, 2);
burg.y = cells.r[cellId] % 2 ? rn(burg.y + shift, 2) : rn(burg.y - shift, 2);
}
function getCloseToEdgePoint(cell1, cell2) {
const {cells, vertices} = pack;
const [x0, y0] = cells.p[cell1];
const commonVertices = cells.v[cell1].filter(vertex => vertices.c[vertex].some(cell => cell === cell2));
const [x1, y1] = vertices.p[commonVertices[0]];
const [x2, y2] = vertices.p[commonVertices[1]];
const xEdge = (x1 + x2) / 2;
const yEdge = (y1 + y2) / 2;
const x = rn(x0 + 0.95 * (xEdge - x0), 2);
const y = rn(y0 + 0.95 * (yEdge - y0), 2);
return [x, y];
}
}
};
const specify = () => {
TIME && console.time("specifyBurgs");
pack.burgs.forEach(burg => {
if (!burg.i || burg.removed || burg.lock) return;
definePopulation(burg);
defineEmblem(burg);
defineFeatures(burg);
});
const populations = pack.burgs
.filter(b => b.i && !b.removed)
.map(b => b.population)
.sort((a, b) => a - b); // ascending
pack.burgs.forEach(burg => {
if (!burg.i || burg.removed) return;
defineGroup(burg, populations);
});
TIME && console.timeEnd("specifyBurgs");
};
const getType = (cellId, port) => {
const {cells, features} = pack;
if (port) return "Naval";
const haven = cells.haven[cellId];
if (haven !== undefined && features[cells.f[haven]].type === "lake") return "Lake";
if (cells.h[cellId] > 60) return "Highland";
if (cells.r[cellId] && cells.fl[cellId] >= 100) return "River";
const biome = cells.biome[cellId];
const population = cells.pop[cellId];
if (!cells.burg[cellId] || population <= 5) {
if (population < 5 && [1, 2, 3, 4].includes(biome)) return "Nomadic";
if (biome > 4 && biome < 10) return "Hunting";
}
return "Generic";
};
function definePopulation(burg) {
const cellId = burg.cell;
let population = pack.cells.s[cellId] / 5;
if (burg.capital) population *= 1.5;
const connectivityRate = Routes.getConnectivityRate(cellId);
if (connectivityRate) population *= connectivityRate;
population *= gauss(1, 1, 0.25, 4, 5); // randomize
population += ((burg.i % 100) - (cellId % 100)) / 1000; // unround
burg.population = rn(Math.max(population, 0.01), 3);
}
function defineEmblem(burg) {
burg.type = getType(burg.cell, burg.port);
const state = pack.states[burg.state];
const stateCOA = state.coa;
let kinship = 0.25;
if (burg.capital) kinship += 0.1;
else if (burg.port) kinship -= 0.1;
if (burg.culture !== state.culture) kinship -= 0.25;
const type = burg.capital && P(0.2) ? "Capital" : burg.type === "Generic" ? "City" : burg.type;
burg.coa = COA.generate(stateCOA, kinship, null, type);
burg.coa.shield = COA.getShield(burg.culture, burg.state);
}
function defineFeatures(burg) {
const pop = burg.population;
burg.citadel = Number(burg.capital || (pop > 50 && P(0.75)) || (pop > 15 && P(0.5)) || P(0.1));
burg.plaza = Number(
Routes.isCrossroad(burg.cell) || (Routes.hasRoad(burg.cell) && P(0.7)) || pop > 20 || (pop > 10 && P(0.8))
);
burg.walls = Number(burg.capital || pop > 30 || (pop > 20 && P(0.75)) || (pop > 10 && P(0.5)) || P(0.1));
burg.shanty = Number(pop > 60 || (pop > 40 && P(0.75)) || (pop > 20 && burg.walls && P(0.4)));
const religion = pack.cells.religion[burg.cell];
const theocracy = pack.states[burg.state].form === "Theocracy";
burg.temple = Number(
(religion && theocracy && P(0.5)) || pop > 50 || (pop > 35 && P(0.75)) || (pop > 20 && P(0.5))
);
}
const getDefaultGroups = () => [
{name: "capital", active: true, order: 9, features: {capital: true}, preview: "watabou-city"},
{name: "city", active: true, order: 8, percentile: 90, min: 5, preview: "watabou-city"},
{
name: "fort",
active: true,
features: {citadel: true, walls: false, plaza: false, port: false},
order: 6,
max: 1
},
{
name: "monastery",
active: true,
features: {temple: true, walls: false, plaza: false, port: false},
order: 5,
max: 0.8
},
{
name: "caravanserai",
active: true,
features: {port: false, plaza: true},
order: 4,
max: 0.8,
biomes: [1, 2, 3]
},
{
name: "trading_post",
active: true,
order: 3,
features: {plaza: true},
max: 0.8,
biomes: [5, 6, 7, 8, 9, 10, 11, 12]
},
{
name: "village",
active: true,
order: 2,
min: 0.1,
max: 2,
preview: "watabou-village"
},
{
name: "hamlet",
active: true,
order: 1,
features: {plaza: false},
max: 0.1,
preview: "watabou-village"
},
{name: "town", active: true, order: 7, isDefault: true, preview: "watabou-city"}
];
function defineGroup(burg, populations) {
if (burg.lock && burg.group) {
// locked burgs: don't change group if it still exists
const group = options.burgs.groups.find(g => g.name === burg.group);
if (group) return;
}
const defaultGroup = options.burgs.groups.find(g => g.isDefault);
if (!defaultGroup) {
ERROR & console.error("No default group defined");
return;
}
burg.group = defaultGroup.name;
for (const group of options.burgs.groups) {
if (!group.active) continue;
if (group.min) {
const isFit = burg.population >= group.min;
if (!isFit) continue;
}
if (group.max) {
const isFit = burg.population <= group.max;
if (!isFit) continue;
}
if (group.features) {
const isFit = Object.entries(group.features).every(([feature, value]) => Boolean(burg[feature]) === value);
if (!isFit) continue;
}
if (group.biomes) {
const isFit = group.biomes.includes(pack.cells.biome[burg.cell]);
if (!isFit) continue;
}
if (group.percentile) {
const index = populations.indexOf(burg.population);
const isFit = index >= Math.floor((populations.length * group.percentile) / 100);
if (!isFit) continue;
}
burg.group = group.name; // apply fitting group
return;
}
}
const previewGeneratorsMap = {
"watabou-city": createWatabouCityLinks,
"watabou-village": createWatabouVillageLinks,
"watabou-dwelling": createWatabouDwellingLinks
};
function getPreview(burg) {
if (burg.link) return {link: burg.link, preview: burg.link};
const group = options.burgs.groups.find(g => g.name === burg.group);
if (!group?.preview || !previewGeneratorsMap[group.preview]) return {link: null, preview: null};
return previewGeneratorsMap[group.preview](burg);
}
function createWatabouCityLinks(burg) {
const cells = pack.cells;
const {i, name, population: burgPopulation, cell} = burg;
const burgSeed = burg.MFCG || seed + String(burg.i).padStart(4, 0);
const sizeRaw = 2.13 * Math.pow((burgPopulation * populationRate) / urbanDensity, 0.385);
const size = minmax(Math.ceil(sizeRaw), 6, 100);
const population = rn(burgPopulation * populationRate * urbanization);
const river = cells.r[cell] ? 1 : 0;
const coast = Number(burg.port > 0);
const sea = (() => {
if (!coast || !cells.haven[cell]) return null;
// calculate see direction: 0 = east, 0.5 = north, 1 = west, 1.5 = south
const [x1, y1] = cells.p[cell];
const [x2, y2] = cells.p[cells.haven[cell]];
const deg = (Math.atan2(y2 - y1, x2 - x1) * 180) / Math.PI;
if (deg <= 0) return normalize(Math.abs(deg), 0, 180);
return 2 - normalize(deg, 0, 180);
})();
const arableBiomes = river ? [1, 2, 3, 4, 5, 6, 7, 8] : [5, 6, 7, 8];
const farms = +arableBiomes.includes(cells.biome[cell]);
const citadel = +burg.citadel;
const urban_castle = +(citadel && each(2)(i));
const hub = Routes.isCrossroad(cell);
const walls = +burg.walls;
const plaza = +burg.plaza;
const temple = +burg.temple;
const shantytown = +burg.shanty;
const style = "natural";
const url = new URL("https://watabou.github.io/city-generator/");
url.search = new URLSearchParams({
name,
population,
size,
seed: burgSeed,
river,
coast,
farms,
citadel,
urban_castle,
hub,
plaza,
temple,
walls,
shantytown,
gates: -1,
style
});
if (sea) url.searchParams.append("sea", sea);
const link = url.toString();
return {link, preview: link + "&preview=1"};
}
function createWatabouVillageLinks(burg) {
const {cells, features} = pack;
const {i, population, cell} = burg;
const burgSeed = seed + String(i).padStart(4, 0);
const pop = rn(population * populationRate * urbanization);
const tags = [];
if (cells.r[cell] && cells.haven[cell]) tags.push("estuary");
else if (cells.haven[cell] && features[cells.f[cell]].cells === 1) tags.push("island,district");
else if (burg.port) tags.push("coast");
else if (cells.conf[cell]) tags.push("confluence");
else if (cells.r[cell]) tags.push("river");
else if (pop < 200 && each(4)(cell)) tags.push("pond");
const connectivityRate = Routes.getConnectivityRate(cell);
tags.push(connectivityRate > 1 ? "highway" : connectivityRate === 1 ? "dead end" : "isolated");
const biome = cells.biome[cell];
const arableBiomes = cells.r[cell] ? [1, 2, 3, 4, 5, 6, 7, 8] : [5, 6, 7, 8];
if (!arableBiomes.includes(biome)) tags.push("uncultivated");
else if (each(6)(cell)) tags.push("farmland");
const temp = grid.cells.temp[cells.g[cell]];
if (temp <= 0 || temp > 28 || (temp > 25 && each(3)(cell))) tags.push("no orchards");
if (!burg.plaza) tags.push("no square");
if (burg.walls) tags.push("palisade");
if (pop < 100) tags.push("sparse");
else if (pop > 300) tags.push("dense");
const width = (() => {
if (pop > 1500) return 1600;
if (pop > 1000) return 1400;
if (pop > 500) return 1000;
if (pop > 200) return 800;
if (pop > 100) return 600;
return 400;
})();
const height = rn(width / 2.05);
const style = (() => {
if ([1, 2].includes(biome)) return "sand";
if (temp <= 5 || [9, 10, 11].includes(biome)) return "snow";
return "default";
})();
const url = new URL("https://watabou.github.io/village-generator/");
url.search = new URLSearchParams({pop, name: burg.name, seed: burgSeed, width, height, style, tags});
const link = url.toString();
return {link, preview: link + "&preview=1"};
}
function createWatabouDwellingLinks(burg) {
const burgSeed = seed + String(burg.i).padStart(4, 0);
const pop = rn(burg.population * populationRate * urbanization);
const tags = (() => {
if (pop > 200) return ["large", "tall"];
if (pop > 100) return ["large"];
if (pop > 50) return ["tall"];
if (pop > 20) return ["low"];
return ["small"];
})();
const url = new URL("https://watabou.github.io/dwellings/");
url.search = new URLSearchParams({pop, name: "", seed: burgSeed, tags});
const link = url.toString();
return {link, preview: link + "&preview=1"};
}
function add([x, y]) {
const {cells} = pack;
const burgId = pack.burgs.length;
const cellId = findCell(x, y);
const culture = cells.culture[cellId];
const name = Names.getCulture(culture);
const state = cells.state[cellId];
const feature = cells.f[cellId];
const burg = {
cell: cellId,
x,
y,
i: burgId,
state,
culture,
name,
feature,
capital: 0,
port: 0
};
definePopulation(burg);
defineEmblem(burg);
defineFeatures(burg);
const populations = pack.burgs
.filter(b => b.i && !b.removed)
.map(b => b.population)
.sort((a, b) => a - b); // ascending
defineGroup(burg, populations);
pack.burgs.push(burg);
cells.burg[cellId] = burgId;
const newRoute = Routes.connect(cellId);
if (newRoute && layerIsOn("toggleRoutes")) drawRoute(newRoute);
drawBurgIcon(burg);
drawBurgLabel(burg);
return burgId;
}
function changeGroup(burg, group) {
if (group) {
burg.group = group;
} else {
const validBurgs = pack.burgs.filter(b => b.i && !b.removed);
const populations = validBurgs.map(b => b.population).sort((a, b) => a - b);
defineGroup(burg, populations);
}
drawBurgIcon(burg);
drawBurgLabel(burg);
}
function remove(burgId) {
const burg = pack.burgs[burgId];
if (!burg) return tip(`Burg ${burgId} not found`, false, "error");
pack.cells.burg[burg.cell] = 0;
burg.removed = true;
const noteId = notes.findIndex(note => note.id === `burg${burgId}`);
if (noteId !== -1) notes.splice(noteId, 1);
if (burg.coa) {
byId("burgCOA" + burgId)?.remove();
emblems.select(`#burgEmblems > use[data-i='${burgId}']`).remove();
delete burg.coa;
}
removeBurgIcon(burg.i);
removeBurgLabel(burg.i);
}
return {generate, getDefaultGroups, specify, defineGroup, getPreview, getType, add, changeGroup, remove};
})();

View file

@ -47,10 +47,10 @@ export function resolveVersionConflicts(mapVersion) {
// v1.0 added state relations, provinces, forms and full names
provs = viewbox.insert("g", "#borders").attr("id", "provs").attr("opacity", 0.6);
BurgsAndStates.collectStatistics();
BurgsAndStates.generateCampaigns();
BurgsAndStates.generateDiplomacy();
BurgsAndStates.defineStateForms();
States.collectStatistics();
States.generateCampaigns();
States.generateDiplomacy();
States.defineStateForms();
Provinces.generate();
Provinces.getPoles();
if (!layerIsOn("toggleBorders")) $("#borders").fadeOut();
@ -260,7 +260,7 @@ export function resolveVersionConflicts(mapVersion) {
if (isOlderThan("1.22.0")) {
// v1.22 changed state neighbors from Set object to array
BurgsAndStates.collectStatistics();
States.collectStatistics();
}
if (isOlderThan("1.3.0")) {
@ -273,7 +273,7 @@ export function resolveVersionConflicts(mapVersion) {
options = {winds, year, era, eraShort, military};
// v1.3 added campaings data for all states
BurgsAndStates.generateCampaigns();
States.generateCampaigns();
// v1.3 added militry layer
armies = viewbox.insert("g", "#icons").attr("id", "armies");
@ -348,7 +348,7 @@ export function resolveVersionConflicts(mapVersion) {
// v1.5 added burg type value
pack.burgs.forEach(burg => {
if (!burg.i || burg.removed) return;
burg.type = BurgsAndStates.getType(burg.cell, burg.port);
burg.type = Burgs.getType(burg.cell, burg.port);
});
// v1.5 added emblems
@ -467,12 +467,6 @@ export function resolveVersionConflicts(mapVersion) {
if (oceanPattern) oceanPattern.removeAttribute("opacity");
const oceanicPattern = document.getElementById("oceanicPattern");
if (!oceanicPattern.getAttribute("opacity")) oceanicPattern.setAttribute("opacity", 0.2);
// v 1.63 moved label text-shadow from css to editable inline style
burgLabels.select("#cities").style("text-shadow", "white 0 0 4px");
burgLabels.select("#towns").style("text-shadow", "white 0 0 4px");
labels.select("#states").style("text-shadow", "white 0 0 4px");
labels.select("#addedLabels").style("text-shadow", "white 0 0 4px");
}
if (isOlderThan("1.64.0")) {
@ -836,22 +830,6 @@ export function resolveVersionConflicts(mapVersion) {
});
}
if (isOlderThan("1.97.0")) {
// v1.97.00 changed MFCG link to an arbitrary preview URL
options.villageMaxPopulation = 2000;
options.showBurgPreview = options.showMFCGMap;
delete options.showMFCGMap;
pack.burgs.forEach(burg => {
if (!burg.i || burg.removed) return;
if (burg.MFCG) {
burg.link = getBurgLink(burg);
delete burg.MFCG;
}
});
}
if (isOlderThan("1.98.0")) {
// v1.98.00 changed compass layer and rose element id
const rose = compass.select("use");
@ -944,7 +922,7 @@ export function resolveVersionConflicts(mapVersion) {
if (isOlderThan("1.104.0")) {
// v1.104.00 separated pole of inaccessibility detection from layer rendering
BurgsAndStates.getPoles();
States.getPoles();
Provinces.getPoles();
}
@ -994,4 +972,71 @@ export function resolveVersionConflicts(mapVersion) {
// some old maps has incorrect "heights" groups
viewbox.selectAll("#heights").remove();
}
if (isOlderThan("1.109.0")) {
// v1.109.0 added customizable burg groups and icons
options.burgs = {groups: []};
// default groups were 'cities' and 'towns'
const iconGroups = burgIcons.selectAll("g");
const citiesGroup = burgIcons.select("#cities");
const townsGroup = burgIcons.select("#towns");
if (!iconGroups.size() || (iconGroups.size() === 2 && citiesGroup.size() && townsGroup.size())) {
// it looks the loaded map has old default groups
options.burgs.groups = Burgs.getDefaultGroups();
} else {
burgIcons.selectAll("circle, use").each(function () {
const group = this.parentNode.id;
const id = this.id.replace(/^burg/, "");
const burg = pack.burgs[id];
if (group && burg) burg.group = group;
});
burgIcons.selectAll("g").each(function (_el, index) {
const name = this.id;
const isDefault = name === "towns";
options.burgs.groups.push({name, active: true, order: index + 1, isDefault, preview: "watabou-city"});
const size = Number(this.getAttribute("size") || 2) * 2;
this.removeAttribute("size");
this.setAttribute("font-size", size);
this.setAttribute("stroke-width", 1);
});
if (options.burgs.groups.filter(g => g.isDefault).length === 0) {
options.burgs.groups[0].isDefault = true;
}
anchors.selectAll("g").each(function () {
const size = Number(this.getAttribute("size") || 1);
this.removeAttribute("size");
this.setAttribute("font-size", size);
});
}
const iconSymbol = byId("icon-anchor");
if (iconSymbol) {
iconSymbol.outerHTML = /* html */ `<symbol id="icon-anchor" viewBox="0 0 30 30" width="1em" height="1em" overflow="visible">
<path d="m 1.003,-9.873 c 0,-0.547 -0.453,-1 -1,-1 -0.547,0 -1,0.453 -1,1 0,0.547 0.453,1 1,1 0.547,0 1,-0.453 1,-1 z m 13,14.5 v 5.5 c 0,0.203 -0.125,0.391 -0.313,0.469 -0.063,0.016 -0.125,0.031 -0.187,0.031 -0.125,0 -0.25,-0.047 -0.359,-0.141 L 11.691,9.033 c -2.453,2.953 -6.859,4.844 -11.688,4.844 -4.829,0 -9.234,-1.891 -11.688,-4.844 l -1.453,1.453 c -0.094,0.094 -0.234,0.141 -0.359,0.141 -0.063,0 -0.125,-0.016 -0.187,-0.031 -0.187,-0.078 -0.313,-0.266 -0.313,-0.469 v -5.5 c 0,-0.281 0.219,-0.5 0.5,-0.5 h 5.5 c 0.203,0 0.391,0.125 0.469,0.313 0.078,0.188 0.031,0.391 -0.109,0.547 L -9.2,6.55 c 1.406,1.891 4.109,3.266 7.203,3.687 V 0.128 h -3 c -0.547,0 -1,-0.453 -1,-1 v -2 c 0,-0.547 0.453,-1 1,-1 h 3 v -2.547 c -1.188,-0.688 -2,-1.969 -2,-3.453 0,-2.203 1.797,-4 4,-4 2.203,0 4,1.797 4,4 0,1.484 -0.812,2.766 -2,3.453 v 2.547 h 3 c 0.547,0 1,0.453 1,1 v 2 c 0,0.547 -0.453,1 -1,1 h -3 V 10.237 C 5.097,9.815 7.8,8.44 9.206,6.55 L 7.643,4.987 C 7.502,4.831 7.456,4.628 7.534,4.44 7.612,4.252 7.8,4.127 8.003,4.127 h 5.5 c 0.281,0 0.5,0.219 0.5,0.5 z"/>
</symbol>`;
}
const validBurgs = pack.burgs.filter(b => b.i && !b.removed);
const populations = validBurgs.map(b => b.population).sort((a, b) => a - b);
validBurgs.forEach(burg => {
if (!burg.group) Burgs.defineGroup(burg, populations);
if (burg.MFCG) {
burg.link = getBurgLink(burg);
delete burg.MFCG;
}
});
layerIsOn("toggleBurgIcons") && drawBurgIcons();
delete options.showBurgPreview;
delete options.showMFCGMap;
delete options.villageMaxPopulation;
}
}

View file

@ -144,7 +144,7 @@ function addListeners() {
}
function refreshStatesEditor() {
BurgsAndStates.collectStatistics();
States.collectStatistics();
statesEditorAddLines();
}
@ -603,7 +603,7 @@ function stateRemove(stateId) {
burg.state = 0;
if (burg.capital) {
burg.capital = 0;
moveBurgToGroup(burg.i, "towns");
Burgs.changeGroup(burg);
}
}
});
@ -840,10 +840,10 @@ function openRegenerationMenu() {
function recalculateStates(must) {
if (!must && !statesAutoChange.checked) return;
BurgsAndStates.expandStates();
States.expandStates();
Provinces.generate();
Provinces.getPoles();
BurgsAndStates.getPoles();
States.getPoles();
if (layerIsOn("toggleStates")) drawStates();
if (layerIsOn("toggleBorders")) drawBorders();
@ -985,7 +985,7 @@ function applyStatesManualAssignent() {
if (affectedStates.length) {
refreshStatesEditor();
BurgsAndStates.getPoles();
States.getPoles();
layerIsOn("toggleStates") ? drawStates() : toggleStates();
if (adjustLabels.checked) drawStateLabels([...new Set(affectedStates)]);
adjustProvinces([...new Set(affectedProvinces)]);
@ -1103,7 +1103,7 @@ function adjustProvinces(affectedProvinces) {
const color = getMixedColor(states[stateId].color);
const kinship = nameByBurg ? 0.8 : 0.4;
const type = BurgsAndStates.getType(center, burg?.port);
const type = Burgs.getType(center, burg?.port);
const coa = COA.generate(burg?.coa || states[stateId].coa, kinship, burg ? null : 0.9, type);
coa.shield = COA.getShield(culture, stateId);
@ -1181,30 +1181,30 @@ function addState() {
if (cells.h[center] < 20)
return tip("You cannot place state into the water. Please click on a land cell", false, "error");
let burg = cells.burg[center];
if (burg && burgs[burg].capital)
let burgId = cells.burg[center];
if (burgId && burgs[burgId].capital)
return tip("Existing capital cannot be selected as a new state capital! Select other cell", false, "error");
if (!burg) burg = addBurg(point); // add new burg
if (!burgId) burgId = Burgs.add(point);
const oldState = cells.state[center];
const newState = states.length;
// turn burg into a capital
burgs[burg].capital = 1;
burgs[burg].state = newState;
moveBurgToGroup(burg, "cities");
burgs[burgId].capital = 1;
burgs[burgId].state = newState;
Burgs.changeGroup(burgs[burgId]);
if (d3.event.shiftKey === false) exitAddStateMode();
const culture = cells.culture[center];
const basename = center % 5 === 0 ? burgs[burg].name : Names.getCulture(culture);
const basename = center % 5 === 0 ? burgs[burgId].name : Names.getCulture(culture);
const name = Names.getState(basename, culture);
const color = getRandomColor();
// generate emblem
const cultureType = pack.cultures[culture].type;
const coa = COA.generate(burgs[burg].coa, 0.4, null, cultureType);
const coa = COA.generate(burgs[burgId].coa, 0.4, null, cultureType);
coa.shield = COA.getShield(culture, null);
// update diplomacy and reverse relations
@ -1244,7 +1244,7 @@ function addState() {
provinces: [],
color,
expansionism: 0.5,
capital: burg,
capital: burgId,
type: "Generic",
center,
culture,
@ -1253,9 +1253,10 @@ function addState() {
coa
});
BurgsAndStates.getPoles();
BurgsAndStates.collectStatistics();
BurgsAndStates.defineStateForms([newState]);
States.getPoles();
States.findNeighbors();
States.collectStatistics();
States.defineStateForms([newState]);
adjustProvinces([cells.province[center]]);
drawStateLabels([newState]);
@ -1379,19 +1380,19 @@ function openStateMergeDialog() {
});
// reassing burgs
pack.burgs.forEach(b => {
if (statesToMerge.includes(b.state)) {
if (b.capital) {
moveBurgToGroup(b.i, "towns");
b.capital = 0;
pack.burgs.forEach(burg => {
if (statesToMerge.includes(burg.state)) {
if (burg.capital) {
burg.capital = 0;
Burgs.changeGroup(burg);
}
b.state = rulingStateId;
burg.state = rulingStateId;
}
});
// reassign provinces
pack.provinces.forEach((p, i) => {
if (statesToMerge.includes(p.state)) p.state = rulingStateId;
pack.provinces.forEach(province => {
if (statesToMerge.includes(province.state)) province.state = rulingStateId;
});
// reassing cells
@ -1402,7 +1403,7 @@ function openStateMergeDialog() {
unfog();
debug.selectAll(".highlight").remove();
BurgsAndStates.getPoles();
States.getPoles();
layerIsOn("toggleStates") ? drawStates() : toggleStates();
layerIsOn("toggleBorders") ? drawBorders() : toggleBorders();
layerIsOn("toggleProvinces") && drawProvinces();

View file

@ -168,8 +168,9 @@ async function exportToPngTiles() {
}
// parse map svg to object url
async function getMapURL(type, options) {
const {
async function getMapURL(
type,
{
debug = false,
noLabels = false,
noWater = false,
@ -177,8 +178,8 @@ async function getMapURL(type, options) {
noIce = false,
noVignette = false,
fullMap = false
} = options || {};
} = {}
) {
const cloneEl = byId("map").cloneNode(true); // clone svg
cloneEl.id = "fantasyMap";
document.body.appendChild(cloneEl);
@ -307,6 +308,15 @@ async function getMapURL(type, options) {
if (rose) cloneDefs.appendChild(rose.cloneNode(true));
}
// add burs icons
if (cloneEl.getElementById("burgIcons")) {
const groups = cloneEl.getElementById("burgIcons").querySelectorAll("g");
for (const group of Array.from(groups)) {
const icon = svgDefs.querySelector(group.dataset.icon);
if (icon) cloneDefs.appendChild(icon.cloneNode(true));
}
}
// add port icon
if (cloneEl.getElementById("anchors")) {
const anchor = svgDefs.getElementById("icon-anchor");

View file

@ -618,14 +618,12 @@ async function parseLoadedData(data, mapVersion) {
if (!state.i && capitalBurgs.length) {
ERROR &&
console.error(
`[Data integrity] Neutral burgs (${capitalBurgs
.map(b => b.i)
.join(", ")}) marked as capitals. Moving them to towns`
`[Data integrity] Neutral burgs (${capitalBurgs.map(b => b.i).join(", ")}) marked as capitals`
);
capitalBurgs.forEach(burg => {
burg.capital = 0;
moveBurgToGroup(burg.i, "towns");
Burgs.changeGroup(burg);
});
return;
@ -634,23 +632,23 @@ async function parseLoadedData(data, mapVersion) {
if (capitalBurgs.length > 1) {
const message = `[Data integrity] State ${state.i} has multiple capitals (${capitalBurgs
.map(b => b.i)
.join(", ")}) assigned. Keeping the first as capital and moving others to towns`;
.join(", ")}) assigned. Keeping the first as capital and moving others`;
ERROR && console.error(message);
capitalBurgs.forEach((burg, i) => {
if (!i) return;
burg.capital = 0;
moveBurgToGroup(burg.i, "towns");
Burgs.changeGroup(burg);
});
return;
}
if (state.i && stateBurgs.length && !capitalBurgs.length) {
ERROR &&
console.error(`[Data integrity] State ${state.i} has no capital. Assigning the first burg as capital`);
stateBurgs[0].capital = 1;
moveBurgToGroup(stateBurgs[0].i, "cities");
ERROR && console.error(`[Data integrity] State ${state.i} has no capital. Making the first burg capital`);
const capital = stateBurgs[0];
capital.capital = 1;
Burgs.changeGroup(capital);
}
});

View file

@ -592,7 +592,7 @@ window.Markers = (function () {
const {cells, states} = pack;
const state = states[cells.state[cell]];
if (!state.campaigns) state.campaigns = BurgsAndStates.generateCampaign(state);
if (!state.campaigns) state.campaigns = States.generateCampaign(state);
const campaign = ra(state.campaigns);
const date = generateDate(campaign.start, campaign.end);
const name = Names.getCulture(cells.culture[cell]) + " Battlefield";
@ -1203,7 +1203,7 @@ window.Markers = (function () {
const burgName = burgs[cells.burg[cell]].name;
const name = `${burgName} Portal`;
const legend = `An element of the magic portal system connecting major cities. The portals were installed centuries ago, but still work fine.`;
const legend = `An element of the magic portal system connecting major city. The portals were installed centuries ago, but still work fine.`;
notes.push({id, name, legend});
}

View file

@ -67,7 +67,7 @@ window.Provinces = (function () {
const fullName = name + " " + formName;
const color = getMixedColor(s.color);
const kinship = nameByBurg ? 0.8 : 0.4;
const type = BurgsAndStates.getType(center, burg.port);
const type = Burgs.getType(center, burg.port);
const coa = COA.generate(stateBurgs[i].coa, kinship, null, type);
coa.shield = COA.getShield(c, s.i);
@ -206,7 +206,7 @@ window.Provinces = (function () {
const dominion = colony ? P(0.95) : singleIsle || isleGroup ? P(0.7) : P(0.3);
const kinship = dominion ? 0 : 0.4;
const type = BurgsAndStates.getType(center, burgs[burg]?.port);
const type = Burgs.getType(center, burgs[burg]?.port);
const coa = COA.generate(s.coa, kinship, dominion, type);
coa.shield = COA.getShield(c, s.i);

View file

@ -851,13 +851,6 @@ window.Religions = (function () {
cells.religion[center] = i;
};
function updateCultures() {
pack.religions = pack.religions.map((religion, index) => {
if (index === 0) return religion;
return {...religion, culture: pack.cells.culture[religion.center]};
});
}
// get supreme deity name
const getDeityName = function (culture) {
if (culture === undefined) {
@ -924,5 +917,5 @@ window.Religions = (function () {
return [trimVowels(random()) + "ism", "global"]; // else
}
return {generate, add, getDeityName, updateCultures, recalculate};
return {generate, add, getDeityName, recalculate};
})();

View file

@ -2,68 +2,105 @@
function drawBurgIcons() {
TIME && console.time("drawBurgIcons");
createIconGroups();
icons.selectAll("circle, use").remove(); // cleanup
for (const {name} of options.burgs.groups) {
const burgsInGroup = pack.burgs.filter(b => b.group === name && !b.removed);
if (!burgsInGroup.length) continue;
// capitals
const capitals = pack.burgs.filter(b => b.capital && !b.removed);
const capitalIcons = burgIcons.select("#cities");
const capitalSize = capitalIcons.attr("size") || 1;
const capitalAnchors = anchors.selectAll("#cities");
const capitalAnchorsSize = capitalAnchors.attr("size") || 2;
const iconsGroup = document.querySelector("#burgIcons > g#" + name);
if (!iconsGroup) continue;
capitalIcons
.selectAll("circle")
.data(capitals)
.enter()
.append("circle")
.attr("id", d => "burg" + d.i)
.attr("data-id", d => d.i)
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", capitalSize);
const icon = iconsGroup.dataset.icon || "#icon-circle";
iconsGroup.innerHTML = burgsInGroup
.map(b => `<use id="burg${b.i}" data-id="${b.i}" href="${icon}" x="${b.x}" y="${b.y}"></use>`)
.join("");
capitalAnchors
.selectAll("use")
.data(capitals.filter(c => c.port))
.enter()
.append("use")
.attr("xlink:href", "#icon-anchor")
.attr("data-id", d => d.i)
.attr("x", d => rn(d.x - capitalAnchorsSize * 0.47, 2))
.attr("y", d => rn(d.y - capitalAnchorsSize * 0.47, 2))
.attr("width", capitalAnchorsSize)
.attr("height", capitalAnchorsSize);
const portsInGroup = burgsInGroup.filter(b => b.port);
if (!portsInGroup.length) continue;
// towns
const towns = pack.burgs.filter(b => b.i && !b.capital && !b.removed);
const townIcons = burgIcons.select("#towns");
const townSize = townIcons.attr("size") || 0.5;
const townsAnchors = anchors.selectAll("#towns");
const townsAnchorsSize = townsAnchors.attr("size") || 1;
const portGroup = document.querySelector("#anchors > g#" + name);
if (!portGroup) continue;
townIcons
.selectAll("circle")
.data(towns)
.enter()
.append("circle")
.attr("id", d => "burg" + d.i)
.attr("data-id", d => d.i)
.attr("cx", d => d.x)
.attr("cy", d => d.y)
.attr("r", townSize);
townsAnchors
.selectAll("use")
.data(towns.filter(c => c.port))
.enter()
.append("use")
.attr("xlink:href", "#icon-anchor")
.attr("data-id", d => d.i)
.attr("x", d => rn(d.x - townsAnchorsSize * 0.47, 2))
.attr("y", d => rn(d.y - townsAnchorsSize * 0.47, 2))
.attr("width", townsAnchorsSize)
.attr("height", townsAnchorsSize);
portGroup.innerHTML = portsInGroup
.map(b => `<use id="anchor${b.i}" data-id="${b.i}" href="#icon-anchor" x="${b.x}" y="${b.y}"></use>`)
.join("");
}
TIME && console.timeEnd("drawBurgIcons");
}
function drawBurgIcon(burg) {
removeBurgIcon(burg.i);
const iconGroup = burgIcons.select("#" + burg.group);
if (iconGroup.empty()) return;
const icon = iconGroup.attr("data-icon") || "#icon-circle";
burgIcons
.select("#" + burg.group)
.append("use")
.attr("href", icon)
.attr("id", "burg" + burg.i)
.attr("data-id", burg.i)
.attr("x", burg.x)
.attr("y", burg.y);
if (burg.port) {
anchors
.select("#" + burg.group)
.append("use")
.attr("href", "#icon-anchor")
.attr("id", "anchor" + burg.i)
.attr("data-id", burg.i)
.attr("x", burg.x)
.attr("y", burg.y);
}
}
function removeBurgIcon(burgId) {
const existingIcon = document.getElementById("burg" + burgId);
if (existingIcon) existingIcon.remove();
const existingAnchor = document.getElementById("anchor" + burgId);
if (existingAnchor) existingAnchor.remove();
}
function createIconGroups() {
// save existing styles and remove all groups
document.querySelectorAll("g#burgIcons > g").forEach(group => {
style.burgIcons[group.id] = Array.from(group.attributes).reduce((acc, attribute) => {
acc[attribute.name] = attribute.value;
return acc;
}, {});
group.remove();
});
document.querySelectorAll("g#anchors > g").forEach(group => {
style.anchors[group.id] = Array.from(group.attributes).reduce((acc, attribute) => {
acc[attribute.name] = attribute.value;
return acc;
}, {});
group.remove();
});
// create groups for each burg group and apply stored or default style
const defaultIconStyle = style.burgIcons.town || Object.values(style.burgIcons)[0];
const defaultAnchorStyle = style.anchors.town || Object.values(style.anchors)[0];
const sortedGroups = [...options.burgs.groups].sort((a, b) => a.order - b.order);
for (const {name} of sortedGroups) {
const burgGroup = burgIcons.append("g");
const iconStyles = style.burgIcons[name] || defaultIconStyle;
Object.entries(iconStyles).forEach(([key, value]) => {
burgGroup.attr(key, value);
});
burgGroup.attr("id", name);
const anchorGroup = anchors.append("g");
const anchorStyles = style.anchors[name] || defaultAnchorStyle;
Object.entries(anchorStyles).forEach(([key, value]) => {
anchorGroup.attr(key, value);
});
anchorGroup.attr("id", name);
}
}

View file

@ -2,40 +2,80 @@
function drawBurgLabels() {
TIME && console.time("drawBurgLabels");
createLabelGroups();
burgLabels.selectAll("text").remove(); // cleanup
for (const {name} of options.burgs.groups) {
const burgsInGroup = pack.burgs.filter(b => b.group === name && !b.removed);
if (!burgsInGroup.length) continue;
const capitals = pack.burgs.filter(b => b.capital && !b.removed);
const capitalSize = burgIcons.select("#cities").attr("size") || 1;
burgLabels
.select("#cities")
.selectAll("text")
.data(capitals)
.enter()
.append("text")
.attr("text-rendering", "optimizeSpeed")
.attr("id", d => "burgLabel" + d.i)
.attr("data-id", d => d.i)
.attr("x", d => d.x)
.attr("y", d => d.y)
.attr("dy", `${capitalSize * -1.5}px`)
.text(d => d.name);
const labelGroup = burgLabels.select("#" + name);
if (labelGroup.empty()) continue;
const towns = pack.burgs.filter(b => b.i && !b.capital && !b.removed);
const townSize = burgIcons.select("#towns").attr("size") || 0.5;
burgLabels
.select("#towns")
.selectAll("text")
.data(towns)
.enter()
.append("text")
.attr("text-rendering", "optimizeSpeed")
.attr("id", d => "burgLabel" + d.i)
.attr("data-id", d => d.i)
.attr("x", d => d.x)
.attr("y", d => d.y)
.attr("dy", `${townSize * -2}px`)
.text(d => d.name);
const dx = labelGroup.attr("data-dx") || 0;
const dy = labelGroup.attr("data-dy") || 0;
labelGroup
.selectAll("text")
.data(burgsInGroup)
.enter()
.append("text")
.attr("text-rendering", "optimizeSpeed")
.attr("id", d => "burgLabel" + d.i)
.attr("data-id", d => d.i)
.attr("x", d => d.x)
.attr("y", d => d.y)
.attr("dx", dx + "em")
.attr("dy", dy + "em")
.text(d => d.name);
}
TIME && console.timeEnd("drawBurgLabels");
}
function drawBurgLabel(burg) {
removeBurgLabel(burg.i);
const labelGroup = burgLabels.select("#" + burg.group);
if (labelGroup.empty()) return;
const dx = labelGroup.attr("data-dx") || 0;
const dy = labelGroup.attr("data-dy") || 0;
labelGroup
.append("text")
.attr("text-rendering", "optimizeSpeed")
.attr("id", "burgLabel" + burg.i)
.attr("data-id", burg.i)
.attr("x", burg.x)
.attr("y", burg.y)
.attr("dx", dx + "em")
.attr("dy", dy + "em")
.text(burg.name);
}
function removeBurgLabel(burgId) {
const existingLabel = document.getElementById("burgLabel" + burgId);
if (existingLabel) existingLabel.remove();
}
function createLabelGroups() {
// save existing styles and remove all groups
document.querySelectorAll("g#burgLabels > g").forEach(group => {
style.burgLabels[group.id] = Array.from(group.attributes).reduce((acc, attribute) => {
acc[attribute.name] = attribute.value;
return acc;
}, {});
group.remove();
});
// create groups for each burg group and apply stored or default style
const defaultStyle = style.burgLabels.town || Object.values(style.burgLabels)[0];
const sortedGroups = [...options.burgs.groups].sort((a, b) => a.order - b.order);
for (const {name} of sortedGroups) {
const group = burgLabels.append("g");
const styles = style.burgLabels[name] || defaultStyle;
Object.entries(styles).forEach(([key, value]) => {
group.attr(key, value);
});
group.attr("id", name);
}
}

View file

@ -360,7 +360,8 @@ window.Routes = (function () {
// connect cell with routes system by land
function connect(cellId) {
const getCost = createCostEvaluator({isWater: false, connections: new Map()});
const pathCells = findPath(cellId, isConnected, getCost);
const isExit = cellId => isLand(cellId) && isConnected(cellId);
const pathCells = findPath(cellId, isExit, getCost);
if (!pathCells) return;
const pointsArray = preparePointsArray();
@ -432,6 +433,26 @@ window.Routes = (function () {
return roadConnections.length > 2;
}
const connectivityRateMap = {
roads: 0.2,
trails: 0.1,
searoutes: 0.2,
default: 0.1
};
function getConnectivityRate(cellId) {
const connections = pack.cells.routes[cellId];
if (!connections) return 0;
const connectivity = Object.values(connections).reduce((acc, routeId) => {
const route = pack.routes.find(route => route.i === routeId);
const rate = connectivityRateMap[route.group] || connectivityRateMap.default;
return acc + rate;
}, 0.8);
return connectivity;
}
// name generator data
const models = {
roads: {burg_suffix: 3, prefix_suffix: 6, the_descriptor_prefix_suffix: 2, the_descriptor_burg_suffix: 1},
@ -645,6 +666,7 @@ window.Routes = (function () {
getRoute,
hasRoad,
isCrossroad,
getConnectivityRate,
generateName,
getPath,
getLength,

View file

@ -1,283 +1,47 @@
"use strict";
window.BurgsAndStates = (() => {
window.States = (() => {
const generate = () => {
const {cells, cultures} = pack;
const n = cells.i.length;
cells.burg = new Uint16Array(n); // cell burg
const burgs = (pack.burgs = placeCapitals());
TIME && console.time("generateStates");
pack.states = createStates();
placeTowns();
expandStates();
normalizeStates();
normalize();
getPoles();
specifyBurgs();
collectStatistics();
findNeighbors();
assignColors();
generateCampaigns();
generateDiplomacy();
function placeCapitals() {
TIME && console.time("placeCapitals");
let count = +byId("statesNumber").value;
let burgs = [0];
TIME && console.timeEnd("generateStates");
const rand = () => 0.5 + Math.random() * 0.5;
const score = new Int16Array(cells.s.map(s => s * rand())); // cell score for capitals placement
const sorted = cells.i.filter(i => score[i] > 0 && cells.culture[i]).sort((a, b) => score[b] - score[a]); // filtered and sorted array of indexes
if (sorted.length < count * 10) {
count = Math.floor(sorted.length / 10);
if (!count) {
WARN && console.warn("There is no populated cells. Cannot generate states");
return burgs;
} else {
WARN && console.warn(`Not enough populated cells (${sorted.length}). Will generate only ${count} states`);
}
}
let burgsTree = d3.quadtree();
let spacing = (graphWidth + graphHeight) / 2 / count; // min distance between capitals
for (let i = 0; burgs.length <= count; i++) {
const cell = sorted[i];
const [x, y] = cells.p[cell];
if (burgsTree.find(x, y, spacing) === undefined) {
burgs.push({cell, x, y});
burgsTree.add([x, y]);
}
if (i === sorted.length - 1) {
WARN && console.warn("Cannot place capitals with current spacing. Trying again with reduced spacing");
burgsTree = d3.quadtree();
i = -1;
burgs = [0];
spacing /= 1.2;
}
}
burgs[0] = burgsTree;
TIME && console.timeEnd("placeCapitals");
return burgs;
}
// For each capital create a state
// for each capital create a state
function createStates() {
TIME && console.time("createStates");
const states = [{i: 0, name: "Neutrals"}];
const colors = getColors(burgs.length - 1);
const each5th = each(5);
burgs.forEach((b, i) => {
if (!i) return; // skip first element
pack.burgs.forEach(burg => {
if (!burg.i || !burg.capital) return;
// burgs data
b.i = b.state = i;
b.culture = cells.culture[b.cell];
b.name = Names.getCultureShort(b.culture);
b.feature = cells.f[b.cell];
b.capital = 1;
// states data
const expansionism = rn(Math.random() * byId("sizeVariety").value + 1, 1);
const basename = b.name.length < 9 && each5th(b.cell) ? b.name : Names.getCultureShort(b.culture);
const name = Names.getState(basename, b.culture);
const type = cultures[b.culture].type;
const basename = burg.name.length < 9 && each5th(burg.cell) ? burg.name : Names.getCultureShort(burg.culture);
const name = Names.getState(basename, burg.culture);
const type = pack.cultures[burg.culture].type;
const coa = COA.generate(null, null, null, type);
coa.shield = COA.getShield(b.culture, null);
coa.shield = COA.getShield(burg.culture, null);
states.push({
i,
color: colors[i - 1],
i: burg.i,
name,
expansionism,
capital: i,
capital: burg.i,
type,
center: b.cell,
culture: b.culture,
center: burg.cell,
culture: burg.culture,
coa
});
cells.burg[b.cell] = i;
});
TIME && console.timeEnd("createStates");
return states;
}
// place secondary settlements based on geo and economical evaluation
function placeTowns() {
TIME && console.time("placeTowns");
const score = new Int16Array(cells.s.map(s => s * gauss(1, 3, 0, 20, 3))); // a bit randomized cell score for towns placement
const sorted = cells.i
.filter(i => !cells.burg[i] && score[i] > 0 && cells.culture[i])
.sort((a, b) => score[b] - score[a]); // filtered and sorted array of indexes
const desiredNumber =
manorsInput.value == 1000
? rn(sorted.length / 5 / (grid.points.length / 10000) ** 0.8)
: manorsInput.valueAsNumber;
const burgsNumber = Math.min(desiredNumber, sorted.length); // towns to generate
let burgsAdded = 0;
const burgsTree = burgs[0];
let spacing = (graphWidth + graphHeight) / 150 / (burgsNumber ** 0.7 / 66); // min distance between towns
while (burgsAdded < burgsNumber && spacing > 1) {
for (let i = 0; burgsAdded < burgsNumber && i < sorted.length; i++) {
if (cells.burg[sorted[i]]) continue;
const cell = sorted[i];
const [x, y] = cells.p[cell];
const s = spacing * gauss(1, 0.3, 0.2, 2, 2); // randomize to make placement not uniform
if (burgsTree.find(x, y, s) !== undefined) continue; // to close to existing burg
const burg = burgs.length;
const culture = cells.culture[cell];
const name = Names.getCulture(culture);
burgs.push({cell, x, y, state: 0, i: burg, culture, name, capital: 0, feature: cells.f[cell]});
burgsTree.add([x, y]);
cells.burg[cell] = burg;
burgsAdded++;
}
spacing *= 0.5;
}
if (manorsInput.value != 1000 && burgsAdded < desiredNumber) {
ERROR && console.error(`Cannot place all burgs. Requested ${desiredNumber}, placed ${burgsAdded}`);
}
burgs[0] = {name: undefined}; // do not store burgsTree anymore
TIME && console.timeEnd("placeTowns");
}
};
// define burg coordinates, coa, port status and define details
const specifyBurgs = () => {
TIME && console.time("specifyBurgs");
const {cells, features} = pack;
const temp = grid.cells.temp;
for (const b of pack.burgs) {
if (!b.i || b.lock) continue;
const i = b.cell;
// asign port status to some coastline burgs with temp > 0 °C
const haven = cells.haven[i];
if (haven && temp[cells.g[i]] > 0) {
const f = cells.f[haven]; // water body id
// port is a capital with any harbor OR town with good harbor
const port = features[f].cells > 1 && ((b.capital && cells.harbor[i]) || cells.harbor[i] === 1);
b.port = port ? f : 0; // port is defined by water body id it lays on
} else b.port = 0;
// define burg population (keep urbanization at about 10% rate)
b.population = rn(Math.max(cells.s[i] / 8 + b.i / 1000 + (i % 100) / 1000, 0.1), 3);
if (b.capital) b.population = rn(b.population * 1.3, 3); // increase capital population
if (b.port) {
b.population = b.population * 1.3; // increase port population
const [x, y] = getCloseToEdgePoint(i, haven);
b.x = x;
b.y = y;
}
// add random factor
b.population = rn(b.population * gauss(2, 3, 0.6, 20, 3), 3);
// shift burgs on rivers semi-randomly and just a bit
if (!b.port && cells.r[i]) {
const shift = Math.min(cells.fl[i] / 150, 1);
if (i % 2) b.x = rn(b.x + shift, 2);
else b.x = rn(b.x - shift, 2);
if (cells.r[i] % 2) b.y = rn(b.y + shift, 2);
else b.y = rn(b.y - shift, 2);
}
// define emblem
const state = pack.states[b.state];
const stateCOA = state.coa;
let kinship = 0.25;
if (b.capital) kinship += 0.1;
else if (b.port) kinship -= 0.1;
if (b.culture !== state.culture) kinship -= 0.25;
b.type = getType(i, b.port);
const type = b.capital && P(0.2) ? "Capital" : b.type === "Generic" ? "City" : b.type;
b.coa = COA.generate(stateCOA, kinship, null, type);
b.coa.shield = COA.getShield(b.culture, b.state);
}
// de-assign port status if it's the only one on feature
const ports = pack.burgs.filter(b => !b.removed && b.port > 0);
for (const f of features) {
if (!f.i || f.land || f.border) continue;
const featurePorts = ports.filter(b => b.port === f.i);
if (featurePorts.length === 1) featurePorts[0].port = 0;
}
TIME && console.timeEnd("specifyBurgs");
};
function getCloseToEdgePoint(cell1, cell2) {
const {cells, vertices} = pack;
const [x0, y0] = cells.p[cell1];
const commonVertices = cells.v[cell1].filter(vertex => vertices.c[vertex].some(cell => cell === cell2));
const [x1, y1] = vertices.p[commonVertices[0]];
const [x2, y2] = vertices.p[commonVertices[1]];
const xEdge = (x1 + x2) / 2;
const yEdge = (y1 + y2) / 2;
const x = rn(x0 + 0.95 * (xEdge - x0), 2);
const y = rn(y0 + 0.95 * (yEdge - y0), 2);
return [x, y];
}
const getType = (cellId, port) => {
const {cells, features, burgs} = pack;
if (port) return "Naval";
const haven = cells.haven[cellId];
if (haven !== undefined && features[cells.f[haven]].type === "lake") return "Lake";
if (cells.h[cellId] > 60) return "Highland";
if (cells.r[cellId] && cells.fl[cellId] >= 100) return "River";
const biome = cells.biome[cellId];
const population = cells.pop[cellId];
if (!cells.burg[cellId] || population <= 5) {
if (population < 5 && [1, 2, 3, 4].includes(biome)) return "Nomadic";
if (biome > 4 && biome < 10) return "Hunting";
}
return "Generic";
};
const defineBurgFeatures = burg => {
const {cells} = pack;
pack.burgs
.filter(b => (burg ? b.i == burg.i : b.i && !b.removed && !b.lock))
.forEach(b => {
const pop = b.population;
b.citadel = Number(b.capital || (pop > 50 && P(0.75)) || (pop > 15 && P(0.5)) || P(0.1));
b.plaza = Number(pop > 20 || (pop > 10 && P(0.8)) || (pop > 4 && P(0.7)) || P(0.6));
b.walls = Number(b.capital || pop > 30 || (pop > 20 && P(0.75)) || (pop > 10 && P(0.5)) || P(0.1));
b.shanty = Number(pop > 60 || (pop > 40 && P(0.75)) || (pop > 20 && b.walls && P(0.4)));
const religion = cells.religion[b.cell];
const theocracy = pack.states[b.state].form === "Theocracy";
b.temple = Number(
(religion && theocracy && P(0.5)) || pop > 50 || (pop > 35 && P(0.75)) || (pop > 20 && P(0.5))
);
});
};
// expand cultures across the map (Dijkstra-like algorithm)
@ -379,7 +143,7 @@ window.BurgsAndStates = (() => {
TIME && console.timeEnd("expandStates");
};
const normalizeStates = () => {
const normalize = () => {
TIME && console.time("normalizeStates");
const {cells, burgs} = pack;
@ -409,23 +173,53 @@ window.BurgsAndStates = (() => {
});
};
// Resets the cultures of all burgs and states to their cell or center cell's (respectively) culture
const updateCultures = () => {
TIME && console.time("updateCulturesForBurgsAndStates");
const findNeighbors = () => {
const {cells, states} = pack;
// Assign the culture associated with the burgs cell
pack.burgs = pack.burgs.map((burg, index) => {
if (index === 0) return burg;
return {...burg, culture: pack.cells.culture[burg.cell]};
states.forEach(s => {
if (s.removed) return;
s.neighbors = new Set();
});
// Assign the culture associated with the states' center cell
pack.states = pack.states.map((state, index) => {
if (index === 0) return state;
return {...state, culture: pack.cells.culture[state.center]};
for (const i of cells.i) {
if (cells.h[i] < 20) continue;
const s = cells.state[i];
cells.c[i]
.filter(c => cells.h[c] >= 20 && cells.state[c] !== s)
.forEach(c => states[s].neighbors.add(cells.state[c]));
}
// convert neighbors Set object into array
states.forEach(s => {
if (!s.neighbors || s.removed) return;
s.neighbors = Array.from(s.neighbors);
});
};
const assignColors = () => {
TIME && console.time("assignColors");
const colors = ["#66c2a5", "#fc8d62", "#8da0cb", "#e78ac3", "#a6d854", "#ffd92f"]; // d3.schemeSet2;
const states = pack.states;
// assign basic color using greedy coloring algorithm
states.forEach(state => {
if (!state.i || state.removed || state.lock) return;
state.color = colors.find(color => state.neighbors.every(neibStateId => states[neibStateId].color !== color));
if (!state.color) state.color = getRandomColor();
colors.push(colors.shift());
});
TIME && console.timeEnd("updateCulturesForBurgsAndStates");
// randomize each already used color a bit
colors.forEach(c => {
const sameColored = states.filter(state => state.color === c && state.i && !state.lock);
sameColored.forEach((state, index) => {
if (!index) return;
state.color = getMixedColor(state.color);
});
});
TIME && console.timeEnd("assignColors");
};
// calculate states data like area, population etc.
@ -436,18 +230,12 @@ window.BurgsAndStates = (() => {
states.forEach(s => {
if (s.removed) return;
s.cells = s.area = s.burgs = s.rural = s.urban = 0;
s.neighbors = new Set();
});
for (const i of cells.i) {
if (cells.h[i] < 20) continue;
const s = cells.state[i];
// check for neighboring states
cells.c[i]
.filter(c => cells.h[c] >= 20 && cells.state[c] !== s)
.forEach(c => states[s].neighbors.add(cells.state[c]));
// collect stats
states[s].cells += 1;
states[s].area += cells.area[i];
@ -458,40 +246,9 @@ window.BurgsAndStates = (() => {
}
}
// convert neighbors Set object into array
states.forEach(s => {
if (!s.neighbors) return;
s.neighbors = Array.from(s.neighbors);
});
TIME && console.timeEnd("collectStatistics");
};
const assignColors = () => {
TIME && console.time("assignColors");
const colors = ["#66c2a5", "#fc8d62", "#8da0cb", "#e78ac3", "#a6d854", "#ffd92f"]; // d3.schemeSet2;
// assign basic color using greedy coloring algorithm
pack.states.forEach(s => {
if (!s.i || s.removed || s.lock) return;
const neibs = s.neighbors;
s.color = colors.find(c => neibs.every(n => pack.states[n].color !== c));
if (!s.color) s.color = getRandomColor();
colors.push(colors.shift());
});
// randomize each already used color a bit
colors.forEach(c => {
const sameColored = pack.states.filter(s => s.color === c && !s.lock);
sameColored.forEach((s, d) => {
if (!d) return;
s.color = getMixedColor(s.color);
});
});
TIME && console.timeEnd("assignColors");
};
const wars = {
War: 6,
Conflict: 2,
@ -868,19 +625,15 @@ window.BurgsAndStates = (() => {
return {
generate,
expandStates,
normalizeStates,
normalize,
getPoles,
findNeighbors,
assignColors,
specifyBurgs,
defineBurgFeatures,
getType,
collectStatistics,
generateCampaign,
generateCampaigns,
generateDiplomacy,
defineStateForms,
getFullName,
updateCultures,
getCloseToEdgePoint
getFullName
};
})();

View file

@ -237,7 +237,7 @@ window.Submap = (function () {
? pack.burgs[s.capital].cell // capital is the best bet
: pack.cells.state.findIndex(x => x === i); // otherwise use the first valid cell
});
BurgsAndStates.getPoles();
States.getPoles();
// transfer provinces, mark provinces without land as removed.
stage("Porting provinces");

View file

@ -39,6 +39,7 @@ window.ThreeD = (function () {
let labels = [];
let icons = [];
let lines = [];
let gridToPackCellMap = null; // Map from grid cell index to pack cell index
const context2d = document.createElement("canvas").getContext("2d");
@ -298,10 +299,6 @@ window.ThreeD = (function () {
raycaster.set(new THREE.Vector3(0, 1000, 0), new THREE.Vector3(0, -1, 0));
const states = viewbox.select("#labels #states");
const cities = burgLabels.select("#cities");
const towns = burgLabels.select("#towns");
const city_icons = burgIcons.select("#cities");
const town_icons = burgIcons.select("#towns");
const stateOptions = {
font: states.attr("font-family"),
@ -311,62 +308,81 @@ window.ThreeD = (function () {
quality: 20
};
const cityOptions = {
font: cities.attr("font-family"),
size: +cities.attr("data-size"),
color: cities.attr("fill"),
elevation: 10,
quality: 20,
iconSize: 1,
iconColor: "#666",
line: 10 - cities.attr("data-size") / 2
};
// Cache icon materials and geometries by group to avoid recreating them
const iconMaterials = {};
const iconGeometries = {};
const lineMaterials = {};
const townOptions = {
font: towns.attr("font-family"),
size: +towns.attr("data-size"),
color: towns.attr("fill"),
elevation: 5,
quality: 30,
iconSize: 0.5,
iconColor: "#666",
line: 5 - towns.attr("data-size") / 2
};
// Helper function to get burg label options from its group
function getBurgLabelOptions(burg) {
if (!burg.group) return null;
const city_icon_material = new THREE.MeshPhongMaterial({color: cityOptions.iconColor});
city_icon_material.wireframe = options.wireframe;
const town_icon_material = new THREE.MeshPhongMaterial({color: townOptions.iconColor});
town_icon_material.wireframe = options.wireframe;
const city_icon_geometry = new THREE.CylinderGeometry(
cityOptions.iconSize * 2,
cityOptions.iconSize * 2,
cityOptions.iconSize,
16,
1
);
const town_icon_geometry = new THREE.CylinderGeometry(
townOptions.iconSize * 2,
townOptions.iconSize * 2,
townOptions.iconSize,
16,
1
);
const line_material = new THREE.LineBasicMaterial({color: cityOptions.iconColor});
const labelGroup = burgLabels.select("#" + burg.group);
if (labelGroup.empty()) return null;
const font = labelGroup.attr("font-family") || "Arial";
const size = +labelGroup.attr("data-size") || 10;
const color = labelGroup.attr("fill") || "#000";
// Calculate elevation, icon size, and line height based on label size
// Larger labels get higher elevation and larger icons
const elevation = Math.max(5, size * 0.5);
const iconSize = Math.max(0.3, size * 0.08);
const iconColor = "#666";
return {
font,
size,
color,
elevation,
quality: 20,
iconSize,
iconColor
};
}
// Helper function to get or create icon material for a group
function getIconMaterial(groupName, iconColor) {
if (!iconMaterials[groupName]) {
const material = new THREE.MeshPhongMaterial({color: iconColor});
material.wireframe = options.wireframe;
iconMaterials[groupName] = material;
}
return iconMaterials[groupName];
}
// Helper function to get or create icon geometry for a group
function getIconGeometry(groupName, iconSize) {
const key = `${groupName}_${iconSize.toFixed(2)}`;
if (!iconGeometries[key]) {
iconGeometries[key] = new THREE.CylinderGeometry(iconSize * 2, iconSize * 2, iconSize, 16, 1);
}
return iconGeometries[key];
}
// Helper function to get or create line material for a group
function getLineMaterial(groupName, iconColor) {
if (!lineMaterials[groupName]) {
lineMaterials[groupName] = new THREE.LineBasicMaterial({color: iconColor});
}
return lineMaterials[groupName];
}
// burg labels
for (let i = 1; i < pack.burgs.length; i++) {
const burg = pack.burgs[i];
if (burg.removed) continue;
const isCity = burg.capital;
const burgOptions = getBurgLabelOptions(burg);
if (!burgOptions) continue;
const [x, y, z] = get3dCoords(burg.x, burg.y);
const options = isCity ? cityOptions : townOptions;
if (layerIsOn("toggleLabels")) {
const burgSprite = await createTextLabel({text: burg.name, ...options});
const burgSprite = await createTextLabel({text: burg.name, ...burgOptions});
burgSprite.position.set(x, y + options.elevation, z);
burgSprite.size = options.size;
burgSprite.position.set(x, y + burgOptions.elevation, z);
burgSprite.size = burgOptions.size;
labels.push(burgSprite);
scene.add(burgSprite);
@ -374,15 +390,19 @@ window.ThreeD = (function () {
// icons
if (layerIsOn("toggleBurgIcons")) {
const geometry = isCity ? city_icon_geometry : town_icon_geometry;
const material = isCity ? city_icon_material : town_icon_material;
const geometry = getIconGeometry(burg.group, burgOptions.iconSize);
const material = getIconMaterial(burg.group, burgOptions.iconColor);
const iconMesh = new THREE.Mesh(geometry, material);
iconMesh.position.set(x, y, z);
icons.push(iconMesh);
scene.add(iconMesh);
const points = [new THREE.Vector3(x, y, z), new THREE.Vector3(x, y + options.line, z)];
const line_material = getLineMaterial(burg.group, burgOptions.iconColor);
// Line starts from top of icon (iconSize/2 above ground) and goes to just below label
const lineStart = y + burgOptions.iconSize / 2;
const lineEnd = y + burgOptions.elevation - burgOptions.size * 0.5;
const points = [new THREE.Vector3(x, lineStart, z), new THREE.Vector3(x, lineEnd, z)];
const line_geometry = new THREE.BufferGeometry().setFromPoints(points);
const line = new THREE.Line(line_geometry, line_material);
@ -467,8 +487,20 @@ window.ThreeD = (function () {
};
});
}
// create a mesh from pixel data
async function createMesh(width, height, segmentsX, segmentsY) {
// Build lookup map from grid cell index to pack cell index
gridToPackCellMap = new Map();
if (pack.cells?.g && pack.cells?.i) {
for (const packCellIndex of pack.cells.i) {
const gridCellIndex = pack.cells.g[packCellIndex];
if (!gridToPackCellMap.has(gridCellIndex)) {
gridToPackCellMap.set(gridCellIndex, packCellIndex);
}
}
}
if (texture) texture.dispose();
if (!options.wireframe) {
texture = new THREE.TextureLoader().load(await createMeshTextureUrl(), render);
@ -522,9 +554,32 @@ window.ThreeD = (function () {
}
}
const LOWER_BY_WATER = 18;
const DIVIDER = 100 - LOWER_BY_WATER;
function getMeshHeight(i) {
const h = grid.cells.h[i];
return h < 20 ? 0 : ((h - 18) / 82) * options.scale;
const height = grid.cells.h[i];
let waterCellId = null;
if (height < 20) {
waterCellId = i;
} else if (grid.cells.c[i]) {
waterCellId = grid.cells.c[i].find(c => grid.cells.h[c] < 20) ?? null;
}
// If water vertex, get uniform elevation
if (waterCellId !== null) {
const packCellIndex = gridToPackCellMap.get(waterCellId);
const featureId = pack.cells.f[packCellIndex];
if (featureId === undefined) return 0;
const feature = pack.features[featureId];
const waterHeight = feature.type === "lake" && feature.height ? feature.height : 20;
return ((waterHeight - LOWER_BY_WATER) / DIVIDER) * options.scale;
}
// Land vertex
return ((height - LOWER_BY_WATER) / DIVIDER) * options.scale;
}
function extendWater(width, height) {
@ -577,7 +632,7 @@ window.ThreeD = (function () {
// controls
controls = await OrbitControls(camera, Renderer.domElement);
controls.zoomSpeed = 0.25;
controls.minDistance = 1.8;
controls.minDistance = 1.5;
controls.maxDistance = 10;
controls.autoRotate = Boolean(options.rotateGlobe);
controls.autoRotateSpeed = options.rotateGlobe;
@ -680,6 +735,7 @@ window.ThreeD = (function () {
script.onerror = () => resolve(false);
});
}
function OrbitControls(camera, domElement) {
if (THREE.OrbitControls) return new THREE.OrbitControls(camera, domElement);

View file

@ -8,6 +8,7 @@ function editBurg(id) {
const burg = id || d3.event.target.dataset.id;
elSelected = burgLabels.select("[data-id='" + burg + "']");
burgLabels.selectAll("text").call(d3.drag().on("start", dragBurgLabel)).classed("draggable", true);
updateGroupsList();
updateBurgValues();
$("#burgEditor").dialog({
@ -21,38 +22,39 @@ function editBurg(id) {
modules.editBurg = true;
// add listeners
byId("burgGroupShow").addEventListener("click", showGroupSection);
byId("burgGroupHide").addEventListener("click", hideGroupSection);
byId("burgSelectGroup").addEventListener("change", changeGroup);
byId("burgInputGroup").addEventListener("change", createNewGroup);
byId("burgAddGroup").addEventListener("click", toggleNewGroupInput);
byId("burgRemoveGroup").addEventListener("click", removeBurgsGroup);
byId("burgName").on("input", changeName);
byId("burgNameReRandom").on("click", generateNameRandom);
byId("burgGroup").on("change", changeGroup);
byId("burgGroupConfigure").on("click", editBurgGroups);
byId("burgType").on("change", changeType);
byId("burgCulture").on("change", changeCulture);
byId("burgNameReCulture").on("click", generateNameCulture);
byId("burgPopulation").on("change", changePopulation);
burgBody.querySelectorAll(".burgFeature").forEach(el => el.on("click", toggleFeature));
byId("burgLinkOpen").on("click", openBurgLink);
byId("burgName").addEventListener("input", changeName);
byId("burgNameReRandom").addEventListener("click", generateNameRandom);
byId("burgType").addEventListener("input", changeType);
byId("burgCulture").addEventListener("input", changeCulture);
byId("burgNameReCulture").addEventListener("click", generateNameCulture);
byId("burgPopulation").addEventListener("change", changePopulation);
burgBody.querySelectorAll(".burgFeature").forEach(el => el.addEventListener("click", toggleFeature));
byId("burgLinkOpen").addEventListener("click", openBurgLink);
byId("burgLinkEdit").addEventListener("click", changeBurgLink);
byId("burgStyleShow").on("click", showStyleSection);
byId("burgStyleHide").on("click", hideStyleSection);
byId("burgEditLabelStyle").on("click", editGroupLabelStyle);
byId("burgEditIconStyle").on("click", editGroupIconStyle);
byId("burgEditAnchorStyle").on("click", editGroupAnchorStyle);
byId("burgStyleShow").addEventListener("click", showStyleSection);
byId("burgStyleHide").addEventListener("click", hideStyleSection);
byId("burgEditLabelStyle").addEventListener("click", editGroupLabelStyle);
byId("burgEditIconStyle").addEventListener("click", editGroupIconStyle);
byId("burgEditAnchorStyle").addEventListener("click", editGroupAnchorStyle);
byId("burgEmblem").on("click", openEmblemEdit);
byId("burgSetPreviewLink").on("click", setCustomPreview);
byId("burgEditEmblem").on("click", openEmblemEdit);
byId("burgLocate").on("click", zoomIntoBurg);
byId("burgRelocate").on("click", toggleRelocateBurg);
byId("burglLegend").on("click", editBurgLegend);
byId("burgLock").on("click", toggleBurgLockButton);
byId("burgRemove").on("click", removeSelectedBurg);
byId("burgTemperatureGraph").on("click", showTemperatureGraph);
byId("burgEmblem").addEventListener("click", openEmblemEdit);
byId("burgTogglePreview").addEventListener("click", toggleBurgPreview);
byId("burgEditEmblem").addEventListener("click", openEmblemEdit);
byId("burgLocate").addEventListener("click", zoomIntoBurg);
byId("burgRelocate").addEventListener("click", toggleRelocateBurg);
byId("burglLegend").addEventListener("click", editBurgLegend);
byId("burgLock").addEventListener("click", toggleBurgLockButton);
byId("burgRemove").addEventListener("click", removeSelectedBurg);
byId("burgTemperatureGraph").addEventListener("click", showTemperatureGraph);
function updateGroupsList() {
byId("burgGroup").options.length = 0; // remove all options
for (const {name} of options.burgs.groups) {
byId("burgGroup").options.add(new Option(name, name));
}
}
function updateBurgValues() {
const id = +elSelected.attr("data-id");
@ -63,6 +65,7 @@ function editBurg(id) {
byId("burgProvinceAndState").innerHTML = provinceName + stateName;
byId("burgName").value = b.name;
byId("burgGroup").value = b.group;
byId("burgType").value = b.type || "Generic";
byId("burgPopulation").value = rn(b.population * populationRate * urbanization);
byId("burgEditAnchorStyle").style.display = +b.port ? "inline-block" : "none";
@ -80,44 +83,22 @@ function editBurg(id) {
byId("burgElevation").innerHTML = getHeight(pack.cells.h[b.cell]);
// toggle features
if (b.capital) byId("burgCapital").classList.remove("inactive");
else byId("burgCapital").classList.add("inactive");
if (b.port) byId("burgPort").classList.remove("inactive");
else byId("burgPort").classList.add("inactive");
if (b.citadel) byId("burgCitadel").classList.remove("inactive");
else byId("burgCitadel").classList.add("inactive");
if (b.walls) byId("burgWalls").classList.remove("inactive");
else byId("burgWalls").classList.add("inactive");
if (b.plaza) byId("burgPlaza").classList.remove("inactive");
else byId("burgPlaza").classList.add("inactive");
if (b.temple) byId("burgTemple").classList.remove("inactive");
else byId("burgTemple").classList.add("inactive");
if (b.shanty) byId("burgShanty").classList.remove("inactive");
else byId("burgShanty").classList.add("inactive");
byId("burgCapital").classList.toggle("inactive", !b.capital);
byId("burgPort").classList.toggle("inactive", !b.port);
byId("burgCitadel").classList.toggle("inactive", !b.citadel);
byId("burgWalls").classList.toggle("inactive", !b.walls);
byId("burgPlaza").classList.toggle("inactive", !b.plaza);
byId("burgTemple").classList.toggle("inactive", !b.temple);
byId("burgShanty").classList.toggle("inactive", !b.shanty);
//toggle lock
updateBurgLockIcon();
// select group
const group = elSelected.node().parentNode.id;
const select = byId("burgSelectGroup");
select.options.length = 0; // remove all options
burgLabels.selectAll("g").each(function () {
select.options.add(new Option(this.id, this.id, false, this.id === group));
});
// set emlem image
const coaID = "burgCOA" + id;
COArenderer.trigger(coaID, b.coa);
byId("burgEmblem").setAttribute("href", "#" + coaID);
if (options.showBurgPreview) {
byId("burgPreviewSection").style.display = "block";
updateBurgPreview(b);
} else {
byId("burgPreviewSection").style.display = "none";
}
updateBurgPreview(b);
}
function dragBurgLabel() {
@ -133,128 +114,6 @@ function editBurg(id) {
});
}
function showGroupSection() {
document.querySelectorAll("#burgBottom > button").forEach(el => (el.style.display = "none"));
byId("burgGroupSection").style.display = "inline-block";
}
function hideGroupSection() {
document.querySelectorAll("#burgBottom > button").forEach(el => (el.style.display = "inline-block"));
byId("burgGroupSection").style.display = "none";
byId("burgInputGroup").style.display = "none";
byId("burgInputGroup").value = "";
byId("burgSelectGroup").style.display = "inline-block";
}
function changeGroup() {
const id = +elSelected.attr("data-id");
moveBurgToGroup(id, this.value);
}
function toggleNewGroupInput() {
if (burgInputGroup.style.display === "none") {
burgInputGroup.style.display = "inline-block";
burgInputGroup.focus();
burgSelectGroup.style.display = "none";
} else {
burgInputGroup.style.display = "none";
burgSelectGroup.style.display = "inline-block";
}
}
function createNewGroup() {
if (!this.value) {
tip("Please provide a valid group name", false, "error");
return;
}
const group = this.value
.toLowerCase()
.replace(/ /g, "_")
.replace(/[^\w\s]/gi, "");
if (byId(group)) {
tip("Element with this id already exists. Please provide a unique name", false, "error");
return;
}
if (Number.isFinite(+group.charAt(0))) {
tip("Group name should start with a letter", false, "error");
return;
}
const id = +elSelected.attr("data-id");
const oldGroup = elSelected.node().parentNode.id;
const label = document.querySelector("#burgLabels [data-id='" + id + "']");
const icon = document.querySelector("#burgIcons [data-id='" + id + "']");
const anchor = document.querySelector("#anchors [data-id='" + id + "']");
if (!label || !icon) {
ERROR && console.error("Cannot find label or icon elements");
return;
}
const labelG = document.querySelector("#burgLabels > #" + oldGroup);
const iconG = document.querySelector("#burgIcons > #" + oldGroup);
const anchorG = document.querySelector("#anchors > #" + oldGroup);
// just rename if only 1 element left
const count = elSelected.node().parentNode.childElementCount;
if (oldGroup !== "cities" && oldGroup !== "towns" && count === 1) {
byId("burgSelectGroup").selectedOptions[0].remove();
byId("burgSelectGroup").options.add(new Option(group, group, false, true));
toggleNewGroupInput();
byId("burgInputGroup").value = "";
labelG.id = group;
iconG.id = group;
if (anchor) anchorG.id = group;
return;
}
// create new groups
byId("burgSelectGroup").options.add(new Option(group, group, false, true));
toggleNewGroupInput();
byId("burgInputGroup").value = "";
addBurgsGroup(group);
moveBurgToGroup(id, group);
}
function removeBurgsGroup() {
const group = elSelected.node().parentNode;
const basic = group.id === "cities" || group.id === "towns";
const burgsInGroup = [];
for (let i = 0; i < group.children.length; i++) {
burgsInGroup.push(+group.children[i].dataset.id);
}
const burgsToRemove = burgsInGroup.filter(b => !(pack.burgs[b].capital || pack.burgs[b].lock));
const capital = burgsToRemove.length < burgsInGroup.length;
confirmationDialog({
title: "Remove burg group",
message: `Are you sure you want to remove ${
basic || capital ? "all unlocked elements in the burg group" : "the entire burg group"
}?<br />Please note that capital or locked burgs will not be deleted. <br /><br />Burgs to be removed: ${
burgsToRemove.length
}. This action cannot be reverted`,
confirm: "Remove",
onConfirm: () => {
$("#burgEditor").dialog("close");
hideGroupSection();
burgsToRemove.forEach(b => removeBurg(b));
if (!basic && !capital) {
const labelG = document.querySelector("#burgLabels > #" + group.id);
const iconG = document.querySelector("#burgIcons > #" + group.id);
const anchorG = document.querySelector("#anchors > #" + group.id);
if (labelG) labelG.remove();
if (iconG) iconG.remove();
if (anchorG) anchorG.remove();
}
}
});
}
function changeName() {
const id = +elSelected.attr("data-id");
pack.burgs[id].name = burgName.value;
@ -267,6 +126,12 @@ function editBurg(id) {
changeName();
}
function changeGroup() {
const id = +elSelected.attr("data-id");
const burg = pack.burgs[id];
Burgs.changeGroup(burg, this.value);
}
function changeType() {
const id = +elSelected.attr("data-id");
pack.burgs[id].type = this.value;
@ -293,21 +158,68 @@ function editBurg(id) {
}
function toggleFeature() {
const id = +elSelected.attr("data-id");
const burg = pack.burgs[id];
const feature = this.dataset.feature;
const turnOn = this.classList.contains("inactive");
if (feature === "port") togglePort(id);
else if (feature === "capital") toggleCapital(id);
else burg[feature] = +turnOn;
if (burg[feature]) this.classList.remove("inactive");
else if (!burg[feature]) this.classList.add("inactive");
const burgId = +elSelected.attr("data-id");
const burg = pack.burgs[burgId];
if (burg.port) byId("burgEditAnchorStyle").style.display = "inline-block";
else byId("burgEditAnchorStyle").style.display = "none";
const feature = this.dataset.feature;
const value = Number(this.classList.contains("inactive"));
if (feature === "port") togglePort(burgId);
else if (feature === "capital") toggleCapital(burgId);
else burg[feature] = value;
this.classList.toggle("inactive", !burg[feature]);
byId("burgEditAnchorStyle").style.display = burg.port ? "inline-block" : "none";
updateBurgPreview(burg);
}
function togglePort(burgId) {
const burg = pack.burgs[burgId];
if (burg.port) {
burg.port = 0;
const anchor = document.querySelector("#anchors [data-id='" + burgId + "']");
if (anchor) anchor.remove();
} else {
const haven = pack.cells.haven[burg.cell];
if (!haven) tip("Port haven is not found, system won't be able to make a searoute", false, "warn");
const portFeature = haven ? pack.cells.f[haven] : -1;
burg.port = portFeature;
anchors
.select("#" + burg.group)
.append("use")
.attr("href", "#icon-anchor")
.attr("id", "anchor" + burg.i)
.attr("data-id", burg.i)
.attr("x", burg.x)
.attr("y", burg.y);
}
}
function toggleCapital(burgId) {
const {burgs, states} = pack;
if (burgs[burgId].capital)
return tip("To change capital please assign a capital status to another burg of this state", false, "error");
const stateId = burgs[burgId].state;
if (!stateId) return tip("Neutral lands cannot have a capital", false, "error");
const oldCapitalId = states[stateId].capital;
states[stateId].capital = burgId;
states[stateId].center = burgs[burgId].cell;
const capital = burgs[burgId];
capital.capital = 1;
Burgs.changeGroup(capital);
const oldCapital = burgs[oldCapitalId];
oldCapital.capital = 0;
Burgs.changeGroup(oldCapital);
}
function toggleBurgLockButton() {
const id = +elSelected.attr("data-id");
const burg = pack.burgs[id];
@ -340,45 +252,54 @@ function editBurg(id) {
function editGroupLabelStyle() {
const g = elSelected.node().parentNode.id;
closeDialogs(".stable");
editStyle("labels", g);
}
function editGroupIconStyle() {
const g = elSelected.node().parentNode.id;
closeDialogs(".stable");
editStyle("burgIcons", g);
}
function editGroupAnchorStyle() {
const g = elSelected.node().parentNode.id;
closeDialogs(".stable");
editStyle("anchors", g);
}
function updateBurgPreview(burg) {
const src = getBurgLink(burg) + "&preview=1";
const preview = Burgs.getPreview(burg).preview;
if (!preview) {
byId("burgPreviewSection").style.display = "none";
return;
}
byId("burgPreviewSection").style.display = "block";
// recreate object to force reload (Chrome bug)
const container = byId("burgPreviewObject");
container.innerHTML = "";
const object = document.createElement("object");
object.style.width = "100%";
object.data = src;
object.data = preview;
container.insertBefore(object, null);
}
function openBurgLink() {
const id = +elSelected.attr("data-id");
const burg = pack.burgs[id];
openURL(getBurgLink(burg));
const link = Burgs.getPreview(burg).link;
if (link) openURL(link);
}
function changeBurgLink() {
function setCustomPreview() {
const id = +elSelected.attr("data-id");
const burg = pack.burgs[id];
prompt(
"Provide custom link to the burg map. It can be a link to Medieval Fantasy City Generator, a different tool, or just an image. Leave empty to use the default map",
{default: getBurgLink(burg), required: false},
"Provide custom URL to the burg map. It can be a link to a generator or just an image. Leave empty to use the default map preview",
{default: Burgs.getPreview(burg).link, required: false},
link => {
if (link) burg.link = link;
else delete burg.link;
@ -388,17 +309,11 @@ function editBurg(id) {
}
function openEmblemEdit() {
const id = +elSelected.attr("data-id"),
burg = pack.burgs[id];
const id = +elSelected.attr("data-id");
const burg = pack.burgs[id];
editEmblem("burg", "burgCOA" + id, burg);
}
function toggleBurgPreview() {
options.showBurgPreview = !options.showBurgPreview;
byId("burgPreviewSection").style.display = options.showBurgPreview ? "block" : "none";
byId("burgTogglePreview").className = options.showBurgPreview ? "icon-map" : "icon-map-o";
}
function zoomIntoBurg() {
const id = +elSelected.attr("data-id");
const burg = pack.burgs[id];
@ -430,41 +345,26 @@ function editBurg(id) {
function relocateBurgOnClick() {
const cells = pack.cells;
const point = d3.mouse(this);
const cell = findCell(point[0], point[1]);
const cellId = findCell(...point);
const id = +elSelected.attr("data-id");
const burg = pack.burgs[id];
if (cells.h[cell] < 20) {
tip("Cannot place burg into the water! Select a land cell", false, "error");
return;
}
if (cells.h[cellId] < 20) return tip("Cannot place burg into the water! Select a land cell", false, "error");
if (cells.burg[cellId] && cells.burg[cellId] !== id)
return tip("There is already a burg in this cell. Please select a free cell", false, "error");
if (cells.burg[cell] && cells.burg[cell] !== id) {
tip("There is already a burg in this cell. Please select a free cell", false, "error");
return;
}
const newState = cells.state[cell];
const newState = cells.state[cellId];
const oldState = burg.state;
if (newState !== oldState && burg.capital) {
tip("Capital cannot be relocated into another state!", false, "error");
return;
}
if (newState !== oldState && burg.capital)
return tip("Capital cannot be relocated into another state!", false, "error");
// change UI
const x = rn(point[0], 2),
y = rn(point[1], 2);
burgIcons
.select("[data-id='" + id + "']")
.attr("transform", null)
.attr("cx", x)
.attr("cy", y);
burgLabels
.select("text[data-id='" + id + "']")
.attr("transform", null)
.attr("x", x)
.attr("y", y);
const x = rn(point[0], 2);
const y = rn(point[1], 2);
burgIcons.select(`#burg${id}`).attr("x", x).attr("y", y);
burgLabels.select(`#burgLabel${id}`).attr("transform", null).attr("x", x).attr("y", y);
const anchor = anchors.select("use[data-id='" + id + "']");
if (anchor.size()) {
const size = anchor.attr("width");
@ -475,8 +375,8 @@ function editBurg(id) {
// change data
cells.burg[burg.cell] = 0;
cells.burg[cell] = id;
burg.cell = cell;
cells.burg[cellId] = id;
burg.cell = cellId;
burg.state = newState;
burg.x = x;
burg.y = y;
@ -497,10 +397,11 @@ function editBurg(id) {
}
function removeSelectedBurg() {
const id = +elSelected.attr("data-id");
if (pack.burgs[id].capital) {
alertMessage.innerHTML = /* html */ `You cannot remove the burg as it is a state capital.<br /><br />
You can change the capital using Burgs Editor (shift + T)`;
const burgId = +elSelected.attr("data-id");
const burg = pack.burgs[burgId];
if (burg.capital) {
alertMessage.innerHTML = /* html */ `You cannot remove the capital. You must change the state capital first`;
$("#alert").dialog({
resizable: false,
title: "Remove burg",
@ -516,7 +417,7 @@ function editBurg(id) {
message: "Are you sure you want to remove the burg? <br>This action cannot be reverted",
confirm: "Remove",
onConfirm: () => {
removeBurg(id); // see Editors module
Burgs.remove(burgId);
$("#burgEditor").dialog("close");
}
});
@ -530,7 +431,7 @@ function editBurg(id) {
}
}
// in °C, array from -1 °C; source: https://en.wikipedia.org/wiki/List_of_cities_by_average_temperature
// in °C, array from -1 °C; source: https://en.wikipedia.org/wiki/List_of_city_by_average_temperature
const meanTempCityMap = {
"-5": "Snag (Yukon)",
"-4": "Yellowknife (Canada)",

View file

@ -0,0 +1,329 @@
"use strict";
function editBurgGroups() {
if (customization) return;
addLines();
$("#burgGroupsEditor").dialog({
title: "Configure Burg groups",
resizable: false,
position: {my: "center", at: "center", of: "svg"},
buttons: {
Apply: () => {
byId("burgGroupsForm").requestSubmit();
},
Add: () => {
byId("burgGroupsBody").innerHTML += createLine({name: "", active: true, preview: null});
},
Restore: () => {
options.burgs.groups = Burgs.getDefaultGroups();
addLines();
},
Cancel: function () {
$(this).dialog("close");
}
}
});
if (modules.editBurgGroups) return;
modules.editBurgGroups = true;
// add listeners
byId("burgGroupsForm").on("change", validateForm).on("submit", submitForm);
byId("burgGroupsBody").on("click", ev => {
const el = ev.target;
const line = el.closest("tr");
if (!line) return;
if (el.name === "biomes") {
const biomes = Array(biomesData.i.length)
.fill(null)
.map((_, i) => ({i, name: biomesData.name[i], color: biomesData.color[i]}));
return selectLimitation(el, biomes);
}
if (el.name === "states") return selectLimitation(el, pack.states);
if (el.name === "cultures") return selectLimitation(el, pack.cultures);
if (el.name === "religions") return selectLimitation(el, pack.religions);
if (el.name === "features") return selectFeaturesLimitation(el);
if (el.name === "up") return line.parentNode.insertBefore(line, line.previousElementSibling);
if (el.name === "down") return line.parentNode.insertBefore(line.nextElementSibling, line);
if (el.name === "remove") return removeLine(line);
});
function addLines() {
const lines = options.burgs.groups.map(createLine);
byId("burgGroupsBody").innerHTML = lines.join("");
}
function createLine(group) {
const count = pack.burgs.filter(burg => !burg.removed && burg.group === group.name).length;
// prettier-ignore
return /* html */ `<tr name="${group.name}">
<td data-tip="Rendering order: higher values are rendered on top"><input type="number" name="order" min="1" max="999" step="1" required value="${group.order || ''}" /></td>
<td data-tip="Type group name. It can contain only text, digits and underscore"><input type="text" name="name" value="${group.name}" required pattern="\\w+" /></td>
<td data-tip="Burg preview generator">
<select name="preview">
<option value="" ${!group.preview ? "selected" : ""}>no</option>
<option value="watabou-city" ${group.preview === "watabou-city" ? "selected" : ""}>Watabou City</option>
<option value="watabou-village" ${group.preview === "watabou-village" ? "selected" : ""}>Watabou Village</option>
<option value="watabou-dwelling" ${group.preview === "watabou-dwellings" ? "selected" : ""}>Watabou Dwelling</option>
</select>
</td>
<td data-tip="Set min population constraint"><input type="number" name="min" min="0" step="any" value="${group.min || ''}" /></td>
<td data-tip="Set max population constraint"><input type="number" name="max" min="0" step="any" value="${group.max || ''}" /></td>
<td data-tip="Set population percentile"><input type="number" name="percentile" min="0" max="100" step="any" value="${group.percentile || ''}" /></td>
<td data-tip="Select allowed biomes">
<input type="hidden" name="biomes" value="${group.biomes || ""}">
<button type="button" name="biomes">${group.biomes ? "some" : "all"}</button>
</td>
<td data-tip="Select allowed states">
<input type="hidden" name="states" value="${group.states || ""}">
<button type="button" name="states">${group.states ? "some" : "all"}</button>
</td>
<td data-tip="Select allowed cultures">
<input type="hidden" name="cultures" value="${group.cultures || ""}">
<button type="button" name="cultures">${group.cultures ? "some" : "all"}</button>
</td>
<td data-tip="Select allowed religions">
<input type="hidden" name="religions" value="${group.religions || ""}">
<button type="button" name="religions">${group.religions ? "some" : "all"}</button>
</td>
<td data-tip="Select allowed features" >
<input type="hidden" name="features" value='${JSON.stringify(group.features || {})}'>
<button type="button" name="features">${Object.keys(group.features || {}).length ? "some" : "any"}</button>
</td>
<td data-tip="Number of burgs in group">${count}</td>
<td data-tip="Activate/deactivate group"><input type="checkbox" name="active" class="native" ${group.active && "checked"} /></td>
<td data-tip="Select group to be assigned if other groups are not passed"><input type="radio" name="isDefault" ${group.isDefault && "checked"}></td>
<td data-tip="Assignment order: move group up"><button type="button" name="up" class="icon-up-big"></button></td>
<td data-tip="Assignment order: move group down"><button type="button" name="down" class="icon-down-big"></button></td>
<td data-tip="Remove group"><button type="button" name="remove" class="icon-trash"></button></td>
</tr>`;
}
function selectLimitation(el, data) {
const value = el.previousElementSibling.value;
const initial = value ? value.split(",").map(v => +v) : [];
const filtered = data.filter(datum => datum.i && !datum.removed);
const lines = filtered.map(
({i, name, fullName, color}) => /* html */ `
<tr data-tip="${name}">
<td>
<span style="color:${color}"></span>
</td>
<td>
<input data-i="${i}" id="el${i}" type="checkbox" class="checkbox" ${
!initial.length || initial.includes(i) ? "checked" : ""
} >
<label for="el${i}" class="checkbox-label">${fullName || name}</label>
</td>
</tr>`
);
alertMessage.innerHTML = /* html */ `<b>Limit group by ${el.name}:</b>
<table style="margin-top:.3em">
<tbody>
${lines.join("")}
</tbody>
</table>`;
$("#alert").dialog({
width: fitContent(),
title: "Limit group",
buttons: {
Invert: function () {
alertMessage.querySelectorAll("input").forEach(el => (el.checked = !el.checked));
},
Apply: function () {
const inputs = Array.from(alertMessage.querySelectorAll("input"));
const selected = inputs.reduce((acc, input) => {
if (input.checked) acc.push(input.dataset.i);
return acc;
}, []);
if (!selected.length) return tip("Select at least one element", false, "error");
const allAreSelected = selected.length === inputs.length;
el.previousElementSibling.value = allAreSelected ? "" : selected.join(",");
el.innerHTML = allAreSelected ? "all" : "some";
$(this).dialog("close");
},
Cancel: function () {
$(this).dialog("close");
}
}
});
}
function selectFeaturesLimitation(el) {
const value = el.previousElementSibling.value;
const initial = value ? JSON.parse(value) : {};
const features = [
{name: "capital", icon: "icon-star"},
{name: "port", icon: "icon-anchor"},
{name: "citadel", icon: "icon-chess-rook"},
{name: "walls", icon: "icon-fort-awesome"},
{name: "plaza", icon: "icon-store"},
{name: "temple", icon: "icon-chess-bishop"},
{name: "shanty", icon: "icon-campground"}
];
const lines = features.map(
// prettier-ignore
({name, icon}) => /* html */ `
<tr data-tip="Select limitation for burg feature: ${name}">
<td>
<span class="${icon}"></span>
<span style="margin-left:.2em">${name}</span>
</td>
<td>
<input type="radio" name="${name}" value="true" ${initial[name] === true ? "checked" : ""} style="margin:0" >
</td>
<td>
<input type="radio" name="${name}" value="false" ${initial[name] === false ? "checked" : ""} style="margin:0">
</td>
<td>
<input type="radio" name="${name}" value="undefined" ${initial[name] === undefined ? "checked" : ""} style="margin:0">
</td>
</tr>`
);
alertMessage.innerHTML = /* html */ `
<form id="featuresLimitationForm">
<table>
<thead style="font-weight:bold">
<td style="width:6em">Features</td>
<td style="width:3em">True</td>
<td style="width:3em">False</td>
<td style="width:3em">Any</td>
</thead>
<tbody>
${lines.join("")}
</tbody>
</table>
</form>`;
$("#alert").dialog({
width: fitContent(),
title: "Limit group by features",
buttons: {
Apply: function () {
const form = byId("featuresLimitationForm");
const values = features.reduce((acc, {name}) => {
const value = form[name].value;
if (value !== "undefined") acc[name] = value === "true";
return acc;
}, {});
el.previousElementSibling.value = JSON.stringify(values);
el.innerHTML = Object.keys(values).length ? "some" : "any";
$(this).dialog("close");
},
Cancel: function () {
$(this).dialog("close");
}
}
});
}
function removeLine(line) {
const lines = byId("burgGroupsBody").children;
if (lines.length < 2) return tip("At least one group should be defined", false, "error");
confirmationDialog({
title: "Remove group",
message:
"Are you sure you want to remove the group? <br>This WON'T change the burgs unless the changes are applied",
confirm: "Remove",
onConfirm: () => {
line.remove();
validateForm();
}
});
}
function validateForm() {
const form = byId("burgGroupsForm");
if (form.name.length) {
const names = Array.from(form.name).map(input => input.value);
form.name.forEach(nameInput => {
const value = nameInput.value;
const isUnique = names.filter(n => n === value).length === 1;
nameInput.setCustomValidity(isUnique ? "" : "Group name should be unique");
nameInput.reportValidity();
});
}
if (form.active.length) {
const active = Array.from(form.active).map(input => input.checked);
form.active[0].setCustomValidity(active.includes(true) ? "" : "At least one group should be active");
form.active[0].reportValidity();
} else {
const active = form.active.checked;
form.active.setCustomValidity(active ? "" : "At least one group should be active");
form.active.reportValidity();
}
if (form.isDefault.length) {
const checked = Array.from(form.isDefault).map(input => input.checked);
form.isDefault[0].setCustomValidity(checked.includes(true) ? "" : "At least one group should be default");
form.isDefault[0].reportValidity();
} else {
const checked = form.isDefault.checked;
form.isDefault.setCustomValidity(checked ? "" : "At least one group should be default");
form.isDefault.reportValidity();
}
}
function submitForm(event) {
event.preventDefault();
const lines = Array.from(byId("burgGroupsBody").children);
if (!lines.length) return tip("At least one group should be defined", false, "error");
function parseInput(input) {
if (input.name === "name") return sanitizeId(input.value);
if (input.name === "features") {
const isValid = JSON.isValid(input.value);
const parsed = isValid ? JSON.parse(input.value) : {};
if (Object.keys(parsed).length) return parsed;
return null;
}
if (input.type === "hidden") return input.value || null;
if (input.type === "radio") return input.checked;
if (input.type === "checkbox") return input.checked;
if (input.type === "number") {
const value = input.valueAsNumber;
if (value === 0 || isNaN(value)) return null;
return value;
}
return input.value || null;
}
options.burgs.groups = lines.map(line => {
const inputs = line.querySelectorAll("input, select");
const group = Array.from(inputs).reduce((obj, input) => {
const value = parseInput(input);
if (value !== null) obj[input.name] = value;
return obj;
}, {});
return group;
});
localStorage.setItem("burg-groups", JSON.stringify(options.burgs.groups));
// put burgs to new groups
const validBurgs = pack.burgs.filter(b => b.i && !b.removed);
const populations = validBurgs.map(b => b.population).sort((a, b) => a - b);
validBurgs.forEach(burg => Burgs.defineGroup(burg, populations));
if (layerIsOn("toggleBurgIcons")) drawBurgIcons();
if (layerIsOn("toggleLabels")) drawBurgLabels();
if (byId("burgsOverviewRefresh")?.offsetParent) burgsOverviewRefresh.click();
$("#burgGroupsEditor").dialog("close");
}
}

View file

@ -24,6 +24,7 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) {
// add listeners
byId("burgsOverviewRefresh").addEventListener("click", refreshBurgsEditor);
byId("burgsGroupsEditorButton").addEventListener("click", editBurgGroups);
byId("burgsChart").addEventListener("click", showBurgsChart);
byId("burgsFilterState").addEventListener("change", burgsOverviewAddLines);
byId("burgsFilterCulture").addEventListener("change", burgsOverviewAddLines);
@ -88,29 +89,24 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) {
data-state="${state}"
data-province="${province}"
data-culture="${culture}"
data-group="${b.group}"
data-population=${population}
data-features="${features}"
>
<span data-tip="Click to zoom into view" class="icon-dot-circled pointer"></span>
<input data-tip="Burg name. Click and type to change" class="burgName" value="${
b.name
}" autocorrect="off" spellcheck="false" />
<input data-tip="Burg province" class="burgState" value="${province}" disabled />
<input data-tip="Burg state" class="burgState" value="${state}" disabled />
<select data-tip="Dominant culture. Click to change burg culture (to change cell culture use Cultures Editor)" class="stateCulture">
${getCultureOptions(b.culture)}
</select>
<input data-tip="Burg name" class="burgName" value="${b.name}" disabled />
<input data-tip="Burg province" value="${province}" disabled />
<input data-tip="Burg state" value="${state}" disabled />
<input data-tip="Dominant culture" value="${culture}" disabled />
<input data-tip="Burg group" value="${b.group}" disabled />
<span data-tip="Burg population" class="icon-male"></span>
<input data-tip="Burg population. Type to change" value=${si(
population
)} class="burgPopulation" style="width: 5em" />
<input data-tip="Burg population" value=${si(population)} style="width: 5em" disabled />
<div style="width: 3em">
<span
data-tip="${b.capital ? " This burg is a state capital" : "Click to assign a capital status"}"
class="icon-star-empty${b.capital ? "" : " inactive pointer"}" style="padding: 0 1px;"></span>
<span data-tip="Click to toggle port status" class="icon-anchor pointer${
b.port ? "" : " inactive"
}" style="font-size: .9em; padding: 0 1px;"></span>
data-tip="${b.capital ? " This burg is a state capital" : "This burg is a NOT state capital"}"
class="icon-star-empty${b.capital ? "" : " inactive"}" style="padding: 0 1px;"></span>
<span data-tip="${b.port ? " This burg is a port" : "This burg is NOT a port"}"
class="icon-anchor${b.port ? "" : " inactive"}" style="font-size: .9em; padding: 0 1px;"></span>
</div>
<span data-tip="Edit burg" class="icon-pencil"></span>
<span class="locks pointer ${
@ -129,16 +125,7 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) {
// add listeners
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseenter", ev => burgHighlightOn(ev)));
body.querySelectorAll("div.states").forEach(el => el.addEventListener("mouseleave", ev => burgHighlightOff(ev)));
body.querySelectorAll("div > input.burgName").forEach(el => el.addEventListener("input", changeBurgName));
body.querySelectorAll("div > span.icon-dot-circled").forEach(el => el.addEventListener("click", zoomIntoBurg));
body.querySelectorAll("div > select.stateCulture").forEach(el => el.addEventListener("change", changeBurgCulture));
body
.querySelectorAll("div > input.burgPopulation")
.forEach(el => el.addEventListener("change", changeBurgPopulation));
body
.querySelectorAll("div > span.icon-star-empty")
.forEach(el => el.addEventListener("click", toggleCapitalStatus));
body.querySelectorAll("div > span.icon-anchor").forEach(el => el.addEventListener("click", togglePortStatus));
body.querySelectorAll("div > span.locks").forEach(el => el.addEventListener("click", toggleBurgLockStatus));
body.querySelectorAll("div > span.icon-pencil").forEach(el => el.addEventListener("click", openBurgEditor));
body.querySelectorAll("div > span.icon-trash-empty").forEach(el => el.addEventListener("click", triggerBurgRemove));
@ -164,15 +151,6 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) {
burgLabels.selectAll("text.drag").classed("drag", false);
}
function changeBurgName() {
if (this.value == "") tip("Please provide a name", false, "error");
const burg = +this.parentNode.dataset.id;
pack.burgs[burg].name = this.value;
this.parentNode.dataset.name = this.value;
const label = document.querySelector("#burgLabels [data-id='" + burg + "']");
if (label) label.innerHTML = this.value;
}
function zoomIntoBurg() {
const burg = +this.parentNode.dataset.id;
const label = document.querySelector("#burgLabels [data-id='" + burg + "']");
@ -181,42 +159,6 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) {
zoomTo(x, y, 8, 2000);
}
function changeBurgCulture() {
const burg = +this.parentNode.dataset.id;
const v = +this.value;
pack.burgs[burg].culture = v;
this.parentNode.dataset.culture = pack.cultures[v].name;
}
function changeBurgPopulation() {
const burg = +this.parentNode.dataset.id;
if (this.value == "" || isNaN(+this.value)) {
tip("Please provide an integer number (like 10000, not 10K)", false, "error");
this.value = si(pack.burgs[burg].population * populationRate * urbanization);
return;
}
pack.burgs[burg].population = this.value / populationRate / urbanization;
this.parentNode.dataset.population = this.value;
this.value = si(this.value);
const population = [];
body.querySelectorAll(":scope > div").forEach(el => population.push(+getInteger(el.dataset.population)));
burgsFooterPopulation.innerHTML = si(d3.mean(population));
}
function toggleCapitalStatus() {
const burg = +this.parentNode.parentNode.dataset.id;
toggleCapital(burg);
burgsOverviewAddLines();
}
function togglePortStatus() {
const burg = +this.parentNode.parentNode.dataset.id;
togglePort(burg);
if (this.classList.contains("inactive")) this.classList.remove("inactive");
else this.classList.add("inactive");
}
function toggleBurgLockStatus() {
const burgId = +this.parentNode.dataset.id;
@ -240,16 +182,16 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) {
}
function triggerBurgRemove() {
const burg = +this.parentNode.dataset.id;
if (pack.burgs[burg].capital)
return tip("You cannot remove the capital. Please change the capital first", false, "error");
const burgId = +this.parentNode.dataset.id;
if (pack.burgs[burgId].capital)
return tip("You cannot remove the capital. Please change the state capital first", false, "error");
confirmationDialog({
title: "Remove burg",
message: "Are you sure you want to remove the burg? <br>This action cannot be reverted",
confirm: "Remove",
onConfirm: () => {
removeBurg(burg);
Burgs.remove(burgId);
burgsOverviewAddLines();
}
});
@ -286,7 +228,7 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) {
if (pack.cells.burg[cell])
return tip("There is already a burg in this cell. Please select a free cell", false, "error");
addBurg(point); // add new burg
Burgs.add(point); // add new burg
if (d3.event.shiftKey === false) {
exitAddBurgMode();
@ -481,7 +423,7 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) {
}
function downloadBurgsData() {
let data = `Id,Burg,Province,Province Full Name,State,State Full Name,Culture,Religion,Population,X,Y,Latitude,Longitude,Elevation (${heightUnit.value}),Temperature,Temperature likeness,Capital,Port,Citadel,Walls,Plaza,Temple,Shanty Town,Emblem,City Generator Link\n`; // headers
let data = `Id,Burg,Province,Province Full Name,State,State Full Name,Culture,Religion,Group,Population,X,Y,Latitude,Longitude,Elevation (${heightUnit.value}),Temperature,Temperature likeness,Capital,Port,Citadel,Walls,Plaza,Temple,Shanty Town,Emblem,Preview link\n`; // headers
const valid = pack.burgs.filter(b => b.i && !b.removed); // all valid burgs
valid.forEach(b => {
@ -494,6 +436,7 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) {
data += pack.states[b.state].fullName + ",";
data += pack.cultures[b.culture].name + ",";
data += pack.religions[pack.cells.religion[b.cell]].name + ",";
data += b.group + ",";
data += rn(b.population * populationRate * urbanization) + ",";
// add geography data
@ -515,7 +458,7 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) {
data += b.temple ? "temple," : ",";
data += b.shanty ? "shanty town," : ",";
data += b.coa ? JSON.stringify(b.coa).replace(/"/g, "").replace(/,/g, ";") + "," : ",";
data += getBurgLink(b);
data += Burgs.getPreview(b).link;
data += "\n";
});
@ -593,17 +536,15 @@ function overviewBurgs(settings = {stateId: null, cultureId: null}) {
title: `Remove ${number} burgs`,
message: `
Are you sure you want to remove all <i>unlocked</i> burgs except for capitals?
<br><i>To remove a capital you have to remove a state first</i>`,
<br><i>To remove a capital you have to remove its state first</i>`,
confirm: "Remove",
onConfirm: removeAllBurgs
onConfirm: () => {
pack.burgs.filter(b => b.i && !(b.capital || b.lock)).forEach(b => Burgs.remove(b.i));
burgsOverviewAddLines();
}
});
}
function removeAllBurgs() {
pack.burgs.filter(b => b.i && !(b.capital || b.lock)).forEach(b => removeBurg(b.i));
burgsOverviewAddLines();
}
function toggleLockAll() {
const activeBurgs = pack.burgs.filter(b => b.i && !b.removed);
const allLocked = activeBurgs.every(burg => burg.lock);

View file

@ -343,7 +343,7 @@ function editDiplomacy() {
}
function regenerateRelations() {
BurgsAndStates.generateDiplomacy();
States.generateDiplomacy();
refreshDiplomacyEditor();
}

View file

@ -128,308 +128,6 @@ function applySorting(headers) {
.forEach(line => list.appendChild(line));
}
function addBurg(point) {
const {cells, states} = pack;
const x = rn(point[0], 2);
const y = rn(point[1], 2);
const cellId = findCell(x, y);
const i = pack.burgs.length;
const culture = cells.culture[cellId];
const name = Names.getCulture(culture);
const state = cells.state[cellId];
const feature = cells.f[cellId];
const population = Math.max(cells.s[cellId] / 3 + i / 1000 + (cellId % 100) / 1000, 0.1);
const type = BurgsAndStates.getType(cellId, false);
// generate emblem
const coa = COA.generate(states[state].coa, 0.25, null, type);
coa.shield = COA.getShield(culture, state);
COArenderer.add("burg", i, coa, x, y);
const burg = {
name,
cell: cellId,
x,
y,
state,
i,
culture,
feature,
capital: 0,
port: 0,
temple: 0,
population,
coa,
type
};
pack.burgs.push(burg);
cells.burg[cellId] = i;
const townSize = burgIcons.select("#towns").attr("size") || 0.5;
burgIcons
.select("#towns")
.append("circle")
.attr("id", "burg" + i)
.attr("data-id", i)
.attr("cx", x)
.attr("cy", y)
.attr("r", townSize);
burgLabels
.select("#towns")
.append("text")
.attr("text-rendering", "optimizeSpeed")
.attr("id", "burgLabel" + i)
.attr("data-id", i)
.attr("x", x)
.attr("y", y)
.attr("dy", `${townSize * -1.5}px`)
.text(name);
BurgsAndStates.defineBurgFeatures(burg);
const newRoute = Routes.connect(cellId);
if (newRoute && layerIsOn("toggleRoutes")) {
routes
.select("#" + newRoute.group)
.append("path")
.attr("d", Routes.getPath(newRoute))
.attr("id", "route" + newRoute.i);
}
return i;
}
function moveBurgToGroup(id, g) {
const label = document.querySelector("#burgLabels [data-id='" + id + "']");
const icon = document.querySelector("#burgIcons [data-id='" + id + "']");
const anchor = document.querySelector("#anchors [data-id='" + id + "']");
if (!label || !icon) {
ERROR && console.error(`Cannot find label or icon elements for id ${id}`);
return;
}
document.querySelector("#burgLabels > #" + g).appendChild(label);
document.querySelector("#burgIcons > #" + g).appendChild(icon);
const iconSize = icon.parentNode.getAttribute("size");
icon.setAttribute("r", iconSize);
label.setAttribute("dy", `${iconSize * -1.5}px`);
if (anchor) {
document.querySelector("#anchors > #" + g).appendChild(anchor);
const anchorSize = +anchor.parentNode.getAttribute("size");
anchor.setAttribute("width", anchorSize);
anchor.setAttribute("height", anchorSize);
anchor.setAttribute("x", rn(pack.burgs[id].x - anchorSize * 0.47, 2));
anchor.setAttribute("y", rn(pack.burgs[id].y - anchorSize * 0.47, 2));
}
}
function moveAllBurgsToGroup(fromGroup, toGroup) {
const groupToMove = document.querySelector(`#burgIcons #${fromGroup}`);
const burgsToMove = Array.from(groupToMove.children).map(x => x.dataset.id);
addBurgsGroup(toGroup);
burgsToMove.forEach(x => moveBurgToGroup(x, toGroup));
}
function addBurgsGroup(group) {
if (document.querySelector(`#burgLabels > #${group}`)) return;
const labelCopy = document.querySelector("#burgLabels > #towns").cloneNode(false);
const iconCopy = document.querySelector("#burgIcons > #towns").cloneNode(false);
const anchorCopy = document.querySelector("#anchors > #towns").cloneNode(false);
// FIXME: using the same id is against the spec!
document.querySelector("#burgLabels").appendChild(labelCopy).id = group;
document.querySelector("#burgIcons").appendChild(iconCopy).id = group;
document.querySelector("#anchors").appendChild(anchorCopy).id = group;
}
function removeBurg(id) {
document.querySelector("#burgLabels [data-id='" + id + "']")?.remove();
document.querySelector("#burgIcons [data-id='" + id + "']")?.remove();
document.querySelector("#anchors [data-id='" + id + "']")?.remove();
const cells = pack.cells;
const burg = pack.burgs[id];
burg.removed = true;
cells.burg[burg.cell] = 0;
const noteId = notes.findIndex(note => note.id === `burg${id}`);
if (noteId !== -1) notes.splice(noteId, 1);
if (burg.coa) {
const coaId = "burgCOA" + id;
if (byId(coaId)) byId(coaId).remove();
emblems.select(`#burgEmblems > use[data-i='${id}']`).remove();
delete burg.coa; // remove to save data
}
}
function toggleCapital(burgId) {
const {burgs, states} = pack;
if (burgs[burgId].capital)
return tip("To change capital please assign a capital status to another burg of this state", false, "error");
const stateId = burgs[burgId].state;
if (!stateId) return tip("Neutral lands cannot have a capital", false, "error");
const prevCapitalId = states[stateId].capital;
states[stateId].capital = burgId;
states[stateId].center = burgs[burgId].cell;
burgs[burgId].capital = 1;
burgs[prevCapitalId].capital = 0;
moveBurgToGroup(burgId, "cities");
moveBurgToGroup(prevCapitalId, "towns");
}
function togglePort(burg) {
const anchor = document.querySelector("#anchors [data-id='" + burg + "']");
if (anchor) anchor.remove();
const b = pack.burgs[burg];
if (b.port) {
b.port = 0;
return;
} // not a port anymore
const haven = pack.cells.haven[b.cell];
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");
b.port = port;
const g = b.capital ? "cities" : "towns";
const group = anchors.select("g#" + g);
const size = +group.attr("size");
group
.append("use")
.attr("xlink:href", "#icon-anchor")
.attr("data-id", burg)
.attr("x", rn(b.x - size * 0.47, 2))
.attr("y", rn(b.y - size * 0.47, 2))
.attr("width", size)
.attr("height", size);
}
function getBurgLink(burg) {
if (burg.link) return burg.link;
const population = burg.population * populationRate * urbanization;
if (population >= options.villageMaxPopulation || burg.citadel || burg.walls || burg.temple || burg.shanty)
return createMfcgLink(burg);
return createVillageGeneratorLink(burg);
}
function createMfcgLink(burg) {
const {cells} = pack;
const {i, name, population: burgPopulation, cell} = burg;
const burgSeed = burg.MFCG || seed + String(burg.i).padStart(4, 0);
const sizeRaw = 2.13 * Math.pow((burgPopulation * populationRate) / urbanDensity, 0.385);
const size = minmax(Math.ceil(sizeRaw), 6, 100);
const population = rn(burgPopulation * populationRate * urbanization);
const river = cells.r[cell] ? 1 : 0;
const coast = Number(burg.port > 0);
const sea = (() => {
if (!coast || !cells.haven[cell]) return null;
// calculate see direction: 0 = south, 0.5 = west, 1 = north, 1.5 = east
const p1 = cells.p[cell];
const p2 = cells.p[cells.haven[cell]];
let deg = (Math.atan2(p2[1] - p1[1], p2[0] - p1[0]) * 180) / Math.PI - 90;
if (deg < 0) deg += 360;
return rn(normalize(deg, 0, 360) * 2, 2);
})();
const arableBiomes = river ? [1, 2, 3, 4, 5, 6, 7, 8] : [5, 6, 7, 8];
const farms = +arableBiomes.includes(cells.biome[cell]);
const citadel = +burg.citadel;
const urban_castle = +(citadel && each(2)(i));
const hub = Routes.isCrossroad(cell);
const walls = +burg.walls;
const plaza = +burg.plaza;
const temple = +burg.temple;
const shantytown = +burg.shanty;
const url = new URL("https://watabou.github.io/city-generator/");
url.search = new URLSearchParams({
name,
population,
size,
seed: burgSeed,
river,
coast,
farms,
citadel,
urban_castle,
hub,
plaza,
temple,
walls,
shantytown,
gates: -1
});
if (sea) url.searchParams.append("sea", sea);
return url.toString();
}
function createVillageGeneratorLink(burg) {
const {cells, features} = pack;
const {i, population, cell} = burg;
const pop = rn(population * populationRate * urbanization);
const burgSeed = seed + String(i).padStart(4, 0);
const tags = [];
if (cells.r[cell] && cells.haven[cell]) tags.push("estuary");
else if (cells.haven[cell] && features[cells.f[cell]].cells === 1) tags.push("island,district");
else if (burg.port) tags.push("coast");
else if (cells.conf[cell]) tags.push("confluence");
else if (cells.r[cell]) tags.push("river");
else if (pop < 200 && each(4)(cell)) tags.push("pond");
const roadsNumber = Object.values(pack.cells.routes[cell] || {}).filter(routeId => {
const route = pack.routes.find(route => route.i === routeId);
if (!route) return false;
return route.group === "roads" || route.group === "trails";
}).length;
tags.push(roadsNumber > 1 ? "highway" : roadsNumber === 1 ? "dead end" : "isolated");
const biome = cells.biome[cell];
const arableBiomes = cells.r[cell] ? [1, 2, 3, 4, 5, 6, 7, 8] : [5, 6, 7, 8];
if (!arableBiomes.includes(biome)) tags.push("uncultivated");
else if (each(6)(cell)) tags.push("farmland");
const temp = grid.cells.temp[cells.g[cell]];
if (temp <= 0 || temp > 28 || (temp > 25 && each(3)(cell))) tags.push("no orchards");
if (!burg.plaza) tags.push("no square");
if (pop < 100) tags.push("sparse");
else if (pop > 300) tags.push("dense");
const width = (() => {
if (pop > 1500) return 1600;
if (pop > 1000) return 1400;
if (pop > 500) return 1000;
if (pop > 200) return 800;
if (pop > 100) return 600;
return 400;
})();
const height = rn(width / 2.2);
const url = new URL("https://watabou.github.io/village-generator/");
url.search = new URLSearchParams({pop, name: "", seed: burgSeed, width, height, tags});
return url.toString();
}
// draw legend box
function drawLegend(name, data) {
legend.selectAll("*").remove(); // fully redraw every time

View file

@ -167,7 +167,7 @@ function showMapTooltip(point, e, i, g) {
if (burgId) {
const burg = pack.burgs[burgId];
const population = si(burg.population * populationRate * urbanization);
tip(`${burg.name}. Population: ${population}. Click to edit`);
tip(`${burg.name} ${burg.group}. Population: ${population}. Click to edit`);
if (burgsOverview?.offsetParent) highlightEditorLine(burgsOverview, burgId, 5000);
return;
}

View file

@ -238,18 +238,22 @@ function editHeightmap(options) {
}
Biomes.define();
rankCells();
rankCells();
Cultures.generate();
Cultures.expand();
BurgsAndStates.generate();
Burgs.generate();
States.generate();
Routes.generate();
Religions.generate();
BurgsAndStates.defineStateForms();
Burgs.specify();
States.collectStatistics();
States.defineStateForms();
Provinces.generate();
Provinces.getPoles();
BurgsAndStates.defineBurgFeatures();
Rivers.specify();
Features.specify();
@ -405,7 +409,7 @@ function editHeightmap(options) {
b.feature = pack.cells.f[b.cell];
pack.cells.burg[b.cell] = b.i;
if (!b.capital && pack.cells.h[b.cell] < 20) removeBurg(b.i);
if (!b.capital && pack.cells.h[b.cell] < 20) Burgs.remove(b.i);
if (b.capital) pack.states[b.state].center = b.cell;
}

View file

@ -845,6 +845,14 @@ function drawRoutes() {
TIME && console.timeEnd("drawRoutes");
}
function drawRoute(route) {
routes
.select("#" + route.group)
.append("path")
.attr("d", Routes.getPath(route))
.attr("id", "route" + route.i);
}
function toggleMilitary(event) {
if (!layerIsOn("toggleMilitary")) {
turnButtonOn("toggleMilitary");

View file

@ -381,14 +381,17 @@ function overviewMilitary() {
const filtered = data.filter(datum => datum.i && !datum.removed);
const lines = filtered.map(
({i, name, fullName, color}) =>
`<tr data-tip="${name}"><td><span style="color:${color}">⬤</span></td>
<td><input data-i="${i}" id="el${i}" type="checkbox" class="checkbox" ${
!initial.length || initial.includes(i) ? "checked" : ""
} >
<label for="el${i}" class="checkbox-label">${fullName || name}</label>
</td></tr>`
({i, name, fullName, color}) => /* html */ `
<tr data-tip="${name}">
<td><span style="color:${color}"></span></td>
<td>
<input data-i="${i}" id="el${i}" type="checkbox" class="checkbox"
${!initial.length || initial.includes(i) ? "checked" : ""} >
<label for="el${i}" class="checkbox-label">${fullName || name}</label>
</td>
</tr>`
);
alertMessage.innerHTML = /* html */ `<b>Limit unit by ${type}:</b>
<table style="margin-top:.3em">
<tbody>
@ -398,7 +401,7 @@ function overviewMilitary() {
$("#alert").dialog({
width: fitContent(),
title: `Limit unit`,
title: "Limit unit",
buttons: {
Invert: function () {
alertMessage.querySelectorAll("input").forEach(el => (el.checked = !el.checked));
@ -427,7 +430,7 @@ function overviewMilitary() {
function applyMilitaryOptions() {
const unitLines = Array.from(tableBody.querySelectorAll("tr"));
const names = unitLines.map(r => r.querySelector("input").value.replace(/[&\/\\#, +()$~%.'":*?<>{}]/g, "_"));
const names = unitLines.map(r => sanitizeId(r.querySelector("input").value));
if (new Set(names).size !== names.length) return tip("All units should have unique names", false, "error");
$("#militaryOptions").dialog("close");

View file

@ -392,7 +392,7 @@ function changeEmblemShape(emblemShape) {
function changeStatesNumber(value) {
byId("statesNumber").style.color = +value ? null : "#b12117";
burgLabels.select("#capitals").attr("data-size", Math.max(rn(6 - value / 20), 3));
burgLabels.select("#capital").attr("data-size", Math.max(rn(6 - value / 20), 3));
labels.select("#countries").attr("data-size", Math.max(rn(18 - value / 6), 4));
}
@ -1051,7 +1051,7 @@ function toggle3dOptions() {
byId("options3dMeshSky").addEventListener("input", changeColors);
byId("options3dMeshWater").addEventListener("input", changeColors);
byId("options3dGlobeResolution").addEventListener("change", changeResolution);
// byId("options3dMeshWireframeMode").addEventListener("change",toggleWireframe3d);
byId("options3dMeshWireframeMode").addEventListener("change", toggleWireframe3d);
byId("options3dSunColor").addEventListener("input", changeSunColor);
byId("options3dSubdivide").addEventListener("change", toggle3dSubdivision);
@ -1116,9 +1116,9 @@ function toggle3dOptions() {
ThreeD.toggle3dSubdivision();
}
// function toggleWireframe3d() {
// ThreeD.toggleWireframe();
// }
function toggleWireframe3d() {
ThreeD.toggleWireframe();
}
function toggleSkyMode() {
const hide = ThreeD.options.extendedWater;

View file

@ -296,8 +296,9 @@ function editProvinces() {
const newStateId = states.length;
// turn province burg into a capital
burgs[burgId].capital = 1;
moveBurgToGroup(burgId, "cities");
const capital = burgs[burgId];
capital.capital = 1;
Burgs.changeGroup(capital);
// move all burgs to a new state
province.burgs.forEach(b => (burgs[b].state = newStateId));
@ -367,9 +368,14 @@ function editProvinces() {
function updateStatesPostRelease(oldStates, newStates) {
const allStates = unique([...oldStates, ...newStates]);
BurgsAndStates.getPoles();
BurgsAndStates.collectStatistics();
BurgsAndStates.defineStateForms(newStates);
layerIsOn("toggleProvinces") && toggleProvinces();
layerIsOn("toggleStates") ? drawStates() : toggleStates();
layerIsOn("toggleBorders") ? drawBorders() : toggleBorders();
States.getPoles();
States.findNeighbors();
States.collectStatistics();
States.defineStateForms(newStates);
drawStateLabels(allStates);
// redraw emblems
@ -1032,7 +1038,7 @@ function editProvinces() {
// generate emblem
const kinship = burg ? 0.8 : 0.4;
const parent = burg ? pack.burgs[burg].coa : pack.states[state].coa;
const type = BurgsAndStates.getType(center, parent.port);
const type = Burgs.getType(center, parent.port);
const coa = COA.generate(parent, kinship, P(0.1), type);
coa.shield = COA.getShield(c, state);
COArenderer.add("province", province, coa, point[0], point[1]);

View file

@ -71,24 +71,35 @@ async function fetchSystemPreset(preset) {
}
}
function applyStyle(style) {
for (const selector in style) {
function applyStyle(styleJSON) {
for (const selector in styleJSON) {
if (selector.startsWith("#burgLabels")) {
const group = selector.split("#").pop();
style.burgLabels[group] = styleJSON[selector];
}
if (selector.startsWith("#burgIcons")) {
const group = selector.split("#").pop();
style.burgIcons[group] = styleJSON[selector];
}
if (selector.startsWith("#anchors")) {
const group = selector.split("#").pop();
style.anchors[group] = styleJSON[selector];
}
const el = document.querySelector(selector);
if (!el) continue;
for (const attribute in style[selector]) {
const value = style[selector][attribute];
for (const attribute in styleJSON[selector]) {
const value = styleJSON[selector][attribute];
if (value === "null" || value === null) {
el.removeAttribute(attribute);
continue;
}
if (attribute === "text-shadow") {
el.style[attribute] = value;
} else {
el.setAttribute(attribute, value);
}
el.setAttribute(attribute, value);
if (selector === "#texture") {
const image = document.querySelector("#texture > image");
@ -130,6 +141,11 @@ async function changeStyle(desiredPreset) {
const [presetName, style] = styleData;
localStorage.setItem("presetStyle", presetName);
applyStyleWithUiRefresh(style);
if (layerIsOn("toggleBurgIcons")) drawBurgIcons();
if (layerIsOn("toggleLabels")) {
drawBurgLabels();
drawStateLabels();
}
}
function applyStyleWithUiRefresh(style) {
@ -271,52 +287,12 @@ function addStylePreset() {
"data-columns"
],
"#legendBox": ["fill", "fill-opacity"],
"#burgLabels > #cities": [
"opacity",
"fill",
"text-shadow",
"letter-spacing",
"data-size",
"font-size",
"font-family"
],
"#burgIcons > #cities": [
"opacity",
"fill",
"fill-opacity",
"size",
"stroke",
"stroke-width",
"stroke-dasharray",
"stroke-linecap"
],
"#anchors > #cities": ["opacity", "fill", "size", "stroke", "stroke-width"],
"#burgLabels > #towns": [
"opacity",
"fill",
"text-shadow",
"letter-spacing",
"data-size",
"font-size",
"font-family"
],
"#burgIcons > #towns": [
"opacity",
"fill",
"fill-opacity",
"size",
"stroke",
"stroke-width",
"stroke-dasharray",
"stroke-linecap"
],
"#anchors > #towns": ["opacity", "fill", "size", "stroke", "stroke-width"],
"#labels > #states": [
"opacity",
"fill",
"stroke",
"stroke-width",
"text-shadow",
"style",
"letter-spacing",
"data-size",
"font-size",
@ -328,7 +304,7 @@ function addStylePreset() {
"fill",
"stroke",
"stroke-width",
"text-shadow",
"style",
"letter-spacing",
"data-size",
"font-size",
@ -352,6 +328,39 @@ function addStylePreset() {
]
};
const burgLabelsAttributes = [
"opacity",
"fill",
"stroke",
"stroke-width",
"style",
"letter-spacing",
"data-size",
"font-size",
"font-family",
"data-dx",
"data-dy"
];
const burgIconsAttributes = [
"opacity",
"data-icon",
"font-size",
"fill",
"fill-opacity",
"stroke",
"stroke-width",
"stroke-dasharray",
"stroke-linecap",
"stroke-linejoin",
"filter"
];
const anchorsAttributes = ["opacity", "fill", "font-size", "stroke", "stroke-width", "filter"];
options.burgs.groups.forEach(({name}) => {
attributes[`#burgLabels > g#${name}`] = burgLabelsAttributes;
attributes[`#burgIcons > g#${name}`] = burgIconsAttributes;
attributes[`#anchors > g#${name}`] = anchorsAttributes;
});
for (const selector in attributes) {
const el = document.querySelector(selector);
if (!el) continue;

View file

@ -248,6 +248,16 @@ function selectStyleElement() {
styleStatesHaloBlur.value = parseFloat(statesHalo.attr("filter")?.match(/blur\(([^)]+)\)/)?.[1]) || 0;
}
if (styleElement === "provs") {
styleFill.style.display = "block";
styleSize.style.display = "block";
styleFillInput.value = styleFillOutput.value = el.attr("fill") || "#111111";
styleFont.style.display = "block";
styleSelectFont.value = el.attr("font-family");
styleFontSize.value = el.attr("font-size");
}
if (styleElement === "labels") {
styleFill.style.display = "block";
styleStroke.style.display = "block";
@ -261,46 +271,46 @@ function selectStyleElement() {
styleStrokeInput.value = styleStrokeOutput.value = el.attr("stroke") || "#3a3a3a";
styleStrokeWidthInput.value = el.attr("stroke-width") || 0;
styleLetterSpacingInput.value = el.attr("letter-spacing") || 0;
styleShadowInput.value = el.style("text-shadow") || "white 0 0 4px";
styleShadowInput.value = el.style("text-shadow") || "";
styleFont.style.display = "block";
styleSelectFont.value = el.attr("font-family");
styleFontSize.value = el.attr("data-size");
if (el.node().parentNode.id === "burgLabels") {
styleFontShift.style.display = "block";
styleFontShiftX.value = el.attr("data-dx") || 0;
styleFontShiftY.value = el.attr("data-dy") || 0;
}
}
if (styleElement === "provs") {
styleFill.style.display = "block";
styleSize.style.display = "block";
styleFillInput.value = styleFillOutput.value = el.attr("fill") || "#111111";
if (styleElement === "burgIcons") {
styleBurgIcons.style.display = "block";
styleBurgIconsIcon.value = el.attr("data-icon");
styleBurgIconsIconSize.value = el.attr("font-size");
styleBurgIconsStrokeLinejoin.value = el.attr("stroke-linejoin");
styleBurgIconsFillOpacity.value = el.attr("fill-opacity");
styleFont.style.display = "block";
styleSelectFont.value = el.attr("font-family");
styleFontSize.value = el.attr("font-size");
}
if (styleElement == "burgIcons") {
styleFill.style.display = "block";
styleStroke.style.display = "block";
styleStrokeWidth.style.display = "block";
styleStrokeDash.style.display = "block";
styleRadius.style.display = "block";
styleFillInput.value = styleFillOutput.value = el.attr("fill") || "#ffffff";
styleStrokeInput.value = styleStrokeOutput.value = el.attr("stroke") || "#3e3e4b";
styleStrokeWidthInput.value = el.attr("stroke-width") || 0.24;
styleStrokeDasharrayInput.value = el.attr("stroke-dasharray") || "";
styleStrokeLinecapInput.value = el.attr("stroke-linecap") || "inherit";
styleRadiusInput.value = el.attr("size") || 1;
}
if (styleElement == "anchors") {
if (styleElement === "anchors") {
styleFill.style.display = "block";
styleStroke.style.display = "block";
styleStrokeWidth.style.display = "block";
styleIconSize.style.display = "block";
styleSize.style.display = "block";
styleFillInput.value = styleFillOutput.value = el.attr("fill") || "#ffffff";
styleStrokeInput.value = styleStrokeOutput.value = el.attr("stroke") || "#3e3e4b";
styleStrokeWidthInput.value = el.attr("stroke-width") || 0.24;
styleIconSizeInput.value = el.attr("size") || 2;
styleFontSize.value = el.attr("font-size") || 1;
}
if (styleElement === "legend") {
@ -758,6 +768,22 @@ stylePopulationUrbanStrokeInput.on("input", e => {
stylePopulationUrbanStrokeOutput.value = e.target.value;
});
styleBurgIconsIcon.on("change", e => {
getEl().attr("data-icon", e.target.value).selectAll("use").attr("href", e.target.value);
});
styleBurgIconsIconSize.on("input", e => {
getEl().attr("font-size", e.target.value);
});
styleBurgIconsStrokeLinejoin.on("change", e => {
getEl().attr("stroke-linejoin", e.target.value);
});
styleBurgIconsFillOpacity.on("input", e => {
getEl().attr("fill-opacity", e.target.value);
});
styleCompassSizeInput.on("input", shiftCompass);
styleCompassShiftX.on("input", shiftCompass);
styleCompassShiftY.on("input", shiftCompass);
@ -840,12 +866,12 @@ styleFontSize.on("change", function () {
styleFontPlus.on("click", function () {
const current = +styleFontSize.value || 12;
changeFontSize(getEl(), Math.min(current + 1, 999));
changeFontSize(getEl(), Math.min(rn(current + 0.1, 1), 999));
});
styleFontMinus.on("click", function () {
const current = +styleFontSize.value || 12;
changeFontSize(getEl(), Math.max(current - 1, 1));
changeFontSize(getEl(), Math.max(rn(current - 0.1, 1), 0.1));
});
function changeFontSize(el, size) {
@ -866,71 +892,20 @@ function changeFontSize(el, size) {
if (styleElementSelect.value === "legend") redrawLegend();
}
styleRadiusInput.on("change", function () {
changeRadius(+this.value);
});
styleRadiusPlus.on("click", function () {
const size = Math.max(rn(getEl().attr("size") * 1.1, 2), 0.2);
changeRadius(size);
});
styleRadiusMinus.on("click", function () {
const size = Math.max(rn(getEl().attr("size") * 0.9, 2), 0.2);
changeRadius(size);
});
function changeRadius(size, group) {
const el = group ? burgIcons.select("#" + group) : getEl();
const g = el.attr("id");
el.attr("size", size);
el.selectAll("circle").each(function () {
this.setAttribute("r", size);
});
styleRadiusInput.value = size;
burgLabels
.select("g#" + g)
styleFontShiftX.on("input", e => {
getEl()
.attr("data-dx", e.target.value)
.selectAll("text")
.each(function () {
this.setAttribute("dy", `${size * -1.5}px`);
});
changeIconSize(size * 2, g); // change also anchor icons
}
styleIconSizeInput.on("change", function () {
changeIconSize(+this.value);
.attr("dx", e.target.value + "em");
});
styleIconSizePlus.on("click", function () {
const size = Math.max(rn(getEl().attr("size") * 1.1, 2), 0.2);
changeIconSize(size);
styleFontShiftY.on("input", e => {
getEl()
.attr("data-dy", e.target.value)
.selectAll("text")
.attr("dy", e.target.value + "em");
});
styleIconSizeMinus.on("click", function () {
const size = Math.max(rn(getEl().attr("size") * 0.9, 2), 0.2);
changeIconSize(size);
});
function changeIconSize(size, group) {
const el = group ? anchors.select("#" + group) : getEl();
if (!el.size()) {
console.warn(`Group ${group} not found. Can not set icon size!`);
return;
}
const oldSize = +el.attr("size");
const shift = (size - oldSize) / 2;
el.attr("size", size);
el.selectAll("use").each(function () {
const x = +this.getAttribute("x");
const y = +this.getAttribute("y");
this.setAttribute("x", x - shift);
this.setAttribute("y", y - shift);
this.setAttribute("width", size);
this.setAttribute("height", size);
});
styleIconSizeInput.value = size;
}
styleStatesBodyOpacity.on("input", e => {
statesBody.attr("opacity", e.target.value);
});
@ -1133,39 +1108,6 @@ styleScaleBar.on("input", function (event) {
});
function updateElements() {
// burgIcons to desired size
burgIcons.selectAll("g").each(function () {
const size = +this.getAttribute("size");
d3.select(this)
.selectAll("circle")
.each(function () {
this.setAttribute("r", size);
});
burgLabels
.select("g#" + this.id)
.selectAll("text")
.each(function () {
this.setAttribute("dy", `${size * -1.5}px`);
});
});
// anchor icons to desired size
anchors.selectAll("g").each(function (d) {
const size = +this.getAttribute("size");
d3.select(this)
.selectAll("use")
.each(function () {
const id = +this.dataset.id;
const x = pack.burgs[id].x,
y = pack.burgs[id].y;
this.setAttribute("x", rn(x - size * 0.47, 2));
this.setAttribute("y", rn(y - size * 0.47, 2));
this.setAttribute("width", size);
this.setAttribute("height", size);
});
});
// redraw elements
if (layerIsOn("toggleHeight")) drawHeightmap();
if (legend.selectAll("*").size() && window.redrawLegend) redrawLegend();
oceanLayers.selectAll("path").remove();

View file

@ -82,10 +82,11 @@ function openSubmapTool() {
function rescaleBurgStyles(scale) {
const burgIcons = [...byId("burgIcons").querySelectorAll("g")];
for (const group of burgIcons) {
const newRadius = rn(minmax(group.getAttribute("size") * scale, 0.2, 10), 2);
changeRadius(newRadius, group.id);
const strokeWidth = group.attributes["stroke-width"];
strokeWidth.value = strokeWidth.value * scale;
const newSize = rn(minmax(group.getAttribute("size") * scale, 0.2, 10), 2);
group.setAttribute("font-size", newSize);
const newStroke = rn(group.getAttribute("stroke-width") * scale, 2);
group.setAttribute("stroke-width", newStroke);
}
const burgLabels = [...byId("burgLabels").querySelectorAll("g")];

View file

@ -154,14 +154,16 @@ function regenerateStates() {
if (!newStates) return;
pack.states = newStates;
BurgsAndStates.expandStates();
BurgsAndStates.normalizeStates();
BurgsAndStates.getPoles();
BurgsAndStates.collectStatistics();
BurgsAndStates.assignColors();
BurgsAndStates.generateCampaigns();
BurgsAndStates.generateDiplomacy();
BurgsAndStates.defineStateForms();
States.expandStates();
States.normalize();
States.getPoles();
States.findNeighbors();
States.collectStatistics();
States.assignColors();
States.generateCampaigns();
States.generateDiplomacy();
States.defineStateForms();
Provinces.generate(true);
Provinces.getPoles();
@ -209,13 +211,13 @@ function recreateStates() {
return null;
}
// turn all old capitals into towns, except for the capitals of locked states
// turn all old capitals into town, except for the capitals of locked states
for (const burg of validBurgs) {
if (!burg.capital) continue;
if (lockedStatesCapitals.includes(burg.i)) continue;
moveBurgToGroup(burg.i, "towns");
burg.capital = 0;
if (burg.capital) {
if (lockedStatesCapitals.includes(burg.i)) continue;
burg.capital = 0;
Burgs.changeGroup(burg);
}
}
// remove labels and emblems for non-locked states
@ -302,7 +304,7 @@ function recreateStates() {
burg.capital = 1;
capital = burg;
capitalsTree.add([x, y]);
moveBurgToGroup(burg.i, "cities");
Burgs.changeGroup(capital);
break;
}
@ -400,7 +402,7 @@ function regenerateBurgs() {
const burgsCount =
(manorsInput.value === "1000" ? rn(sorted.length / 5 / (grid.points.length / 10000) ** 0.8) : +manorsInput.value) +
existingStatesCount;
const spacing = (graphWidth + graphHeight) / 150 / (burgsCount ** 0.7 / 66); // base min distance between towns
const spacing = (graphWidth + graphHeight) / 150 / (burgsCount ** 0.7 / 66); // base min distance between town
for (let i = 0; i < sorted.length && newBurgs.length < burgsCount; i++) {
const id = newBurgs.length;
@ -431,20 +433,21 @@ function regenerateBurgs() {
.filter(s => s.i && !s.removed && !s.capital)
.forEach(s => {
const [x, y] = cells.p[s.center];
const burgId = addBurg([x, y]);
const burgId = Burgs.add([x, y]);
s.capital = burgId;
s.center = pack.burgs[burgId].cell;
pack.burgs[burgId].capital = 1;
pack.burgs[burgId].state = s.i;
moveBurgToGroup(burgId, "cities");
const burg = pack.burgs[burgId];
burg.state = s.i;
burg.capital = 1;
Burgs.changeGroup(burg);
});
features.forEach(f => {
if (f.port) f.port = 0; // reset features ports counter
});
BurgsAndStates.specifyBurgs();
BurgsAndStates.defineBurgFeatures();
Burgs.specify();
regenerateRoutes();
drawBurgIcons();
@ -503,7 +506,7 @@ function regenerateEmblems() {
const nameByBurg = province.burg && province.name.slice(0, 3) === parent.name.slice(0, 3);
const kinship = dominion ? 0 : nameByBurg ? 0.8 : 0.4;
const culture = pack.cells.culture[province.center];
const type = BurgsAndStates.getType(province.center, parent.port);
const type = Burgs.getType(province.center, parent.port);
province.coa = COA.generate(parent.coa, kinship, dominion, type);
province.coa.shield = COA.getShield(culture, province.state);
});
@ -521,8 +524,24 @@ function regenerateReligions() {
function regenerateCultures() {
Cultures.generate();
Cultures.expand();
BurgsAndStates.updateCultures();
Religions.updateCultures();
// update culture for states
pack.states = pack.states.map(state => {
if (!state.i || state.removed) return state;
return {...state, culture: pack.cells.culture[state.center]};
});
// update culture for burgs
pack.burgs = pack.burgs.map(burg => {
if (!burg.i || burg.removed) return burg;
return {...burg, culture: pack.cells.culture[burg.cell]};
});
// update culture for religions
pack.religions = pack.religions.map(religion => {
if (!religion.i || religion.removed) return religion;
return {...religion, culture: pack.cells.culture[religion.center]};
});
layerIsOn("toggleCultures") ? drawCultures() : toggleCultures();
refreshAllEditors();

View file

@ -162,11 +162,11 @@
"filter": null
},
"#sea_island": {
"opacity": 0.5,
"stroke": "#1f3846",
"stroke-width": 0.7,
"filter": "url(#dropShadow)",
"auto-filter": 1
"opacity": 1,
"stroke": "#c1a884",
"stroke-width": 0.3,
"filter": "none",
"auto-filter": 0
},
"#lake_island": {
"opacity": 1,
@ -194,7 +194,7 @@
"#roads": {
"opacity": 0.7,
"stroke": "#8d502a",
"stroke-width": 1,
"stroke-width": 0.8,
"stroke-dasharray": 3,
"stroke-linecap": "inherit",
"filter": "",
@ -202,7 +202,7 @@
},
"#trails": {
"opacity": 0.7,
"stroke": "#924217",
"stroke": "#8d502a",
"stroke-width": 0.5,
"stroke-dasharray": "1 2",
"stroke-linecap": "butt",
@ -211,16 +211,14 @@
},
"#searoutes": {
"opacity": 0.8,
"stroke": "#b16925",
"stroke-width": 0.8,
"stroke-dasharray": "1 2",
"stroke-linecap": "round",
"filter": null,
"mask": null
"stroke": "#aa7658",
"stroke-width": 0.6,
"stroke-dasharray": "2 2",
"stroke-linecap": "butt"
},
"#statesBody": {
"opacity": 0.2,
"filter": "url(#filter-sepia)"
"filter": null
},
"#statesHalo": {
"opacity": 0.4,
@ -328,64 +326,282 @@
"data-y": 93,
"data-columns": 8
},
"#burgLabels > #cities": {
"opacity": 1,
"#burgLabels > g#capital": {
"opacity": 0.9,
"fill": "#3e3e4b",
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 12,
"font-size": 12,
"font-family": "Great Vibes"
"data-size": 6,
"font-size": 6,
"font-family": "UnifrakturMaguntia",
"data-dy": 0.85
},
"#burgIcons > #cities": {
"#burgIcons > g#capital": {
"data-icon": "#icon-watabou-capital",
"opacity": 1,
"fill": "#fdfab9",
"fill-opacity": 0.7,
"size": 1,
"stroke": "#6f4e1f",
"stroke-width": 0.3,
"stroke-dasharray": ".3 .4",
"stroke-linecap": "butt"
"fill": "#E59189",
"fill-opacity": 1,
"font-size": 2,
"stroke": "#4D3F36",
"stroke-width": 1.4,
"stroke-dasharray": null,
"stroke-linecap": "round",
"stroke-linejoin": "round",
"filter": null
},
"#anchors > #cities": {
"#anchors > g#capital": {
"opacity": 1,
"fill": "#ffffff",
"size": 2,
"stroke": "#3e3e4b",
"stroke-width": 1.2
"fill": "#EBE8DF",
"font-size": 1.5,
"stroke": "#4D3F36",
"stroke-width": 1
},
"#burgLabels > #towns": {
"opacity": 1,
"#burgLabels > g#city": {
"opacity": 0.9,
"fill": "#3e3e4b",
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 5,
"font-size": 5,
"font-family": "Great Vibes"
"font-family": "UnifrakturMaguntia",
"data-dy": 0.85
},
"#burgIcons > #towns": {
"#burgIcons > g#city": {
"data-icon": "#icon-watabou-city",
"opacity": 1,
"fill": "#fef4d8",
"fill-opacity": 0.7,
"size": 0.5,
"stroke": "#72472c",
"stroke-width": 0.12,
"stroke-dasharray": "",
"stroke-linecap": "butt"
"fill": "#E59189",
"fill-opacity": 1,
"font-size": 2,
"stroke": "#4D3F36",
"stroke-width": 1.4,
"stroke-dasharray": null,
"stroke-linecap": "round",
"stroke-linejoin": "round",
"filter": null
},
"#anchors > #towns": {
"#anchors > g#city": {
"opacity": 1,
"fill": "#ffffff",
"size": 1,
"stroke": "#3e3e4b",
"stroke-width": 1.2
"fill": "#EBE8DF",
"font-size": 1.5,
"stroke": "#4D3F36",
"stroke-width": 1
},
"#burgLabels > g#fort": {
"opacity": 0.9,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 3,
"font-size": 3,
"font-family": "UnifrakturMaguntia",
"data-dy": 0.85
},
"#burgIcons > g#fort": {
"data-icon": "#icon-watabou-fort",
"opacity": 1,
"fill": "#E59189",
"fill-opacity": 1,
"font-size": 2,
"stroke": "#4D3F36",
"stroke-width": 1.8,
"stroke-dasharray": null,
"stroke-linecap": "round",
"stroke-linejoin": "round",
"filter": null
},
"#anchors > g#fort": {
"opacity": 1,
"fill": "#EBE8DF",
"font-size": 1,
"stroke": "#4D3F36",
"stroke-width": 1
},
"#burgLabels > g#monastery": {
"opacity": 0.9,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "UnifrakturMaguntia",
"data-dy": 0.85
},
"#burgIcons > g#monastery": {
"data-icon": "#icon-watabou-monastery",
"opacity": 1,
"fill": "#E59189",
"fill-opacity": 1,
"font-size": 2,
"stroke": "#4D3F36",
"stroke-width": 1.4,
"stroke-dasharray": null,
"stroke-linecap": "round",
"stroke-linejoin": "round",
"filter": null
},
"#anchors > g#monastery": {
"opacity": 1,
"fill": "#EBE8DF",
"font-size": 1,
"stroke": "#4D3F36",
"stroke-width": 1
},
"#burgLabels > g#caravanserai": {
"opacity": 0.9,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "UnifrakturMaguntia",
"data-dy": 0.85
},
"#burgIcons > g#caravanserai": {
"data-icon": "#icon-watabou-caravanserai",
"opacity": 1,
"fill": "#E59189",
"fill-opacity": 1,
"font-size": 2,
"stroke": "#4D3F36",
"stroke-width": 1.4,
"stroke-dasharray": null,
"stroke-linecap": "round",
"stroke-linejoin": "round",
"filter": null
},
"#anchors > g#caravanserai": {
"opacity": 1,
"fill": "#EBE8DF",
"font-size": 1,
"stroke": "#4D3F36",
"stroke-width": 1
},
"#burgLabels > g#trading_post": {
"opacity": 0.9,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "UnifrakturMaguntia",
"data-dy": 0.85
},
"#burgIcons > g#trading_post": {
"data-icon": "#icon-watabou-post",
"opacity": 1,
"fill": "#E59189",
"fill-opacity": 1,
"font-size": 2,
"stroke": "#4D3F36",
"stroke-width": 1.4,
"stroke-dasharray": null,
"stroke-linecap": "round",
"stroke-linejoin": "round",
"filter": null
},
"#anchors > g#trading_post": {
"opacity": 1,
"fill": "#EBE8DF",
"font-size": 1,
"stroke": "#4D3F36",
"stroke-width": 1
},
"#burgLabels > g#village": {
"opacity": 0.9,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 3,
"font-size": 3,
"font-family": "UnifrakturMaguntia",
"data-dy": 0.85
},
"#burgIcons > g#village": {
"data-icon": "#icon-watabou-village",
"opacity": 1,
"fill": "#E59189",
"fill-opacity": 1,
"font-size": 2,
"stroke": "#4D3F36",
"stroke-width": 1.4,
"stroke-dasharray": null,
"stroke-linecap": "round",
"stroke-linejoin": "round",
"filter": null
},
"#anchors > g#village": {
"opacity": 1,
"fill": "#EBE8DF",
"font-size": 1,
"stroke": "#4D3F36",
"stroke-width": 1
},
"#burgLabels > g#hamlet": {
"opacity": 0.9,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "UnifrakturMaguntia",
"data-dy": 0.85
},
"#burgIcons > g#hamlet": {
"data-icon": "#icon-watabou-hamlet",
"opacity": 0.9,
"fill": "#E59189",
"fill-opacity": 1,
"font-size": 2,
"stroke": "#4D3F36",
"stroke-width": 1.4,
"stroke-dasharray": null,
"stroke-linecap": "round",
"stroke-linejoin": "round",
"filter": null
},
"#anchors > g#hamlet": {
"opacity": 1,
"fill": "#EBE8DF",
"font-size": 1,
"stroke": "#4D3F36",
"stroke-width": 1
},
"#burgLabels > g#town": {
"opacity": 0.9,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 3,
"font-size": 3,
"font-family": "UnifrakturMaguntia",
"data-dy": 0.85
},
"#burgIcons > g#town": {
"data-icon": "#icon-watabou-town",
"opacity": 1,
"fill": "#E59189",
"fill-opacity": 1,
"font-size": 2,
"stroke": "#4D3F36",
"stroke-width": 1.4,
"stroke-dasharray": null,
"stroke-linecap": "round",
"stroke-linejoin": "round",
"filter": null
},
"#anchors > g#town": {
"opacity": 1,
"fill": "#EBE8DF",
"font-size": 1,
"stroke": "#4D3F36",
"stroke-width": 1
},
"#labels > #states": {
"opacity": 1,
"fill": "#3e3e4b",
"stroke": "#3a3a3a",
"stroke-width": 0,
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 22,
"font-size": 22,
@ -397,7 +613,7 @@
"fill": "#3e3e4b",
"stroke": "#3a3a3a",
"stroke-width": 0,
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 18,
"font-size": 18,

View file

@ -328,55 +328,255 @@
"data-y": 93,
"data-columns": 8
},
"#burgLabels > #cities": {
"#burgLabels > g#city": {
"opacity": 1,
"fill": "#000000",
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 5,
"font-size": 5,
"font-family": "Amarante"
"data-size": 6,
"font-size": 6,
"font-family": "Amarante",
"data-dy": -0.4
},
"#burgIcons > #cities": {
"#burgIcons > g#city": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#000000",
"fill-opacity": 0.7,
"size": 1,
"font-size": 2,
"stroke": "#000000",
"stroke-width": 0.24,
"stroke-width": 1,
"stroke-dasharray": "",
"stroke-linecap": "round"
},
"#anchors > #cities": {
"#anchors > g#city": {
"opacity": 1,
"fill": "#ffffff",
"size": 2,
"font-size": 2,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > #towns": {
"#burgLabels > g#town": {
"opacity": 1,
"fill": "#000000",
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 4,
"font-size": 4,
"font-family": "Amarante"
"font-family": "Amarante",
"data-dy": -0.4
},
"#burgIcons > #towns": {
"#burgIcons > g#town": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#000000",
"fill-opacity": 0.7,
"size": 0.5,
"font-size": 1.2,
"stroke": "#000000",
"stroke-width": 0.12,
"stroke-width": 1,
"stroke-dasharray": "",
"stroke-linecap": "round"
},
"#anchors > #towns": {
"#anchors > g#town": {
"opacity": 1,
"fill": "#ffffff",
"size": 1,
"font-size": 1.2,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#capital": {
"opacity": 1,
"fill": "#000000",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 7,
"font-size": 7,
"font-family": "Amarante",
"data-dy": -0.4
},
"#burgIcons > g#capital": {
"data-icon": "#icon-square",
"opacity": 1,
"fill": "#000000",
"fill-opacity": 0.7,
"font-size": 2,
"stroke": "#000000",
"stroke-width": 1,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > g#capital": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 1.9,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#fort": {
"opacity": 1,
"fill": "#000000",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Amarante",
"data-dy": -0.4
},
"#burgIcons > g#fort": {
"data-icon": "#icon-triangle",
"opacity": 1,
"fill": "#000000",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#000000",
"stroke-width": 1,
"stroke-dasharray": "",
"stroke-linecap": "round"
},
"#anchors > g#fort": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#monastery": {
"opacity": 1,
"fill": "#000000",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Amarante",
"data-dy": -0.4
},
"#burgIcons > g#monastery": {
"data-icon": "#icon-cross",
"opacity": 1,
"fill": "#000000",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#000000",
"stroke-width": 1,
"stroke-dasharray": "",
"stroke-linecap": "round"
},
"#anchors > g#monastery": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#caravanserai": {
"opacity": 1,
"fill": "#000000",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Amarante",
"data-dy": -0.4
},
"#burgIcons > g#caravanserai": {
"data-icon": "#icon-triangle",
"opacity": 1,
"fill": "#000000",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#000000",
"stroke-width": 1,
"stroke-dasharray": "",
"stroke-linecap": "round"
},
"#anchors > g#caravanserai": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#trading_post": {
"opacity": 1,
"fill": "#000000",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Amarante",
"data-dy": -0.4
},
"#burgIcons > g#trading_post": {
"data-icon": "#icon-triangle",
"opacity": 1,
"fill": "#000000",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#000000",
"stroke-width": 1,
"stroke-dasharray": "",
"stroke-linecap": "round"
},
"#anchors > g#trading_post": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#village": {
"opacity": 1,
"fill": "#000000",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 3,
"font-size": 3,
"font-family": "Amarante",
"data-dy": -0.4
},
"#burgIcons > g#village": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#000000",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#000000",
"stroke-width": 1,
"stroke-dasharray": "",
"stroke-linecap": "round"
},
"#anchors > g#village": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#hamlet": {
"opacity": 1,
"fill": "#000000",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Amarante",
"data-dy": -0.4
},
"#burgIcons > g#hamlet": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#000000",
"fill-opacity": 0.7,
"font-size": 0.5,
"stroke": "#000000",
"stroke-width": 1,
"stroke-dasharray": "",
"stroke-linecap": "round"
},
"#anchors > g#hamlet": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.5,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
@ -385,7 +585,7 @@
"fill": "#000000",
"stroke": "#000000",
"stroke-width": 0,
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 21,
"font-size": 21,
@ -397,7 +597,7 @@
"fill": "#000000",
"stroke": "#000000",
"stroke-width": 0,
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 18,
"font-size": 18,

View file

@ -330,64 +330,271 @@
"data-y": 93,
"data-columns": 8
},
"#burgLabels > #cities": {
"#burgLabels > g#city": {
"opacity": 1,
"fill": "#414141",
"text-shadow": "white 0 0 4px",
"style": "text-shadow: white 0 0 4px",
"letter-spacing": 0,
"data-size": 7,
"font-size": 7,
"font-family": "Arial"
"font-family": "Arial",
"data-dy": -0.4
},
"#burgIcons > #cities": {
"#burgIcons > g#city": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 1,
"font-size": 1.5,
"stroke": "#3e3e4b",
"stroke-width": 0.24,
"stroke-width": 1,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #cities": {
"#anchors > g#city": {
"opacity": 1,
"fill": "#ffffff",
"size": 2,
"font-size": 1.5,
"stroke": "#303030",
"stroke-width": 1.7
},
"#burgLabels > #towns": {
"#burgLabels > g#town": {
"opacity": 1,
"fill": "#414141",
"text-shadow": "none",
"style": "text-shadow: none",
"letter-spacing": 0,
"data-size": 3,
"font-size": 3,
"font-family": "Arial"
"font-family": "Arial",
"data-dy": -0.4
},
"#burgIcons > #towns": {
"#burgIcons > g#town": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 0.5,
"font-size": 1,
"stroke": "#3e3e4b",
"stroke-width": 0.12,
"stroke-width": 1,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #towns": {
"#anchors > g#town": {
"opacity": 1,
"fill": "#ffffff",
"size": 1,
"font-size": 1,
"stroke": "#3e3e4b",
"stroke-width": 1.06
"stroke-width": 1
},
"#burgLabels > g#capital": {
"opacity": 1,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 7,
"font-size": 7,
"font-family": "Arial",
"data-dy": -0.4
},
"#burgIcons > g#capital": {
"data-icon": "#icon-square",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 2,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#capital": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 1.9,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#fort": {
"opacity": 1,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Arial",
"data-dy": -0.4
},
"#burgIcons > g#fort": {
"data-icon": "#icon-triangle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#fort": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#monastery": {
"opacity": 1,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Arial",
"data-dy": -0.4
},
"#burgIcons > g#monastery": {
"data-icon": "#icon-cross",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#monastery": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#caravanserai": {
"opacity": 1,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Arial",
"data-dy": -0.4
},
"#burgIcons > g#caravanserai": {
"data-icon": "#icon-triangle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#caravanserai": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#trading_post": {
"opacity": 1,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Arial",
"data-dy": -0.4
},
"#burgIcons > g#trading_post": {
"data-icon": "#icon-triangle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#trading_post": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#village": {
"opacity": 1,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 3,
"font-size": 3,
"font-family": "Arial",
"data-dy": -0.4
},
"#burgIcons > g#village": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#village": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#hamlet": {
"opacity": 1,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Arial",
"data-dy": -0.4
},
"#burgIcons > g#hamlet": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.5,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#hamlet": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.5,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#labels > #states": {
"opacity": 1,
"fill": "#292929",
"stroke": "#303030",
"stroke-width": 0,
"text-shadow": "white 0 0 2px",
"style": "text-shadow: white 0 0 2px",
"letter-spacing": 0,
"data-size": 10,
"font-size": 10,
@ -399,7 +606,7 @@
"fill": "#414141",
"stroke": "#3a3a3a",
"stroke-width": 0,
"text-shadow": "white 0 0 4px",
"style": "text-shadow: white 0 0 4px",
"letter-spacing": 0,
"data-size": 18,
"font-size": 18,

View file

@ -328,64 +328,271 @@
"data-y": 93,
"data-columns": 8
},
"#burgLabels > #cities": {
"#burgLabels > g#city": {
"opacity": 1,
"fill": "#ffffff",
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 8,
"font-size": 8,
"font-family": "Orbitron"
"font-family": "Orbitron",
"data-dy": -0.4
},
"#burgIcons > #cities": {
"#burgIcons > g#city": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 2,
"font-size": 2,
"stroke": "#444444",
"stroke-width": 0.25,
"stroke-width": 1.2,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #cities": {
"#anchors > g#city": {
"opacity": 1,
"fill": "#ffffff",
"size": 4,
"font-size": 2,
"stroke": "#3e3e4b",
"stroke-width": 1
},
"#burgLabels > #towns": {
"#burgLabels > g#town": {
"opacity": 1,
"fill": "#ffffff",
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 4,
"font-size": 4,
"font-family": "Orbitron",
"data-dy": -0.4
},
"#burgIcons > g#town": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 1,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > g#town": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 1,
"stroke": "#3e3e4b",
"stroke-width": 1
},
"#burgLabels > g#capital": {
"opacity": 1,
"fill": "#ffffff",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 9,
"font-size": 9,
"font-family": "Orbitron",
"data-dy": -0.4
},
"#burgIcons > g#capital": {
"data-icon": "#icon-square",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 2,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#capital": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 1.9,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#fort": {
"opacity": 1,
"fill": "#ffffff",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 3,
"font-size": 3,
"font-family": "Orbitron"
"font-family": "Orbitron",
"data-dy": -0.4
},
"#burgIcons > #towns": {
"opacity": 0.95,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 0.8,
"stroke": "#3e3e4b",
"stroke-width": 0.2,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #towns": {
"#burgIcons > g#fort": {
"data-icon": "#icon-triangle",
"opacity": 1,
"fill": "#ffffff",
"size": 1.6,
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#fort": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#monastery": {
"opacity": 1,
"fill": "#ffffff",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 3,
"font-size": 3,
"font-family": "Orbitron",
"data-dy": -0.4
},
"#burgIcons > g#monastery": {
"data-icon": "#icon-cross",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#monastery": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#caravanserai": {
"opacity": 1,
"fill": "#ffffff",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 3,
"font-size": 3,
"font-family": "Orbitron",
"data-dy": -0.4
},
"#burgIcons > g#caravanserai": {
"data-icon": "#icon-triangle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#caravanserai": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#trading_post": {
"opacity": 1,
"fill": "#ffffff",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 3,
"font-size": 3,
"font-family": "Orbitron",
"data-dy": -0.4
},
"#burgIcons > g#trading_post": {
"data-icon": "#icon-triangle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#trading_post": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#village": {
"opacity": 1,
"fill": "#ffffff",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 3,
"font-size": 3,
"font-family": "Orbitron",
"data-dy": -0.4
},
"#burgIcons > g#village": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#village": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#hamlet": {
"opacity": 1,
"fill": "#ffffff",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 3,
"font-size": 3,
"font-family": "Orbitron",
"data-dy": -0.4
},
"#burgIcons > g#hamlet": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.5,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#hamlet": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.5,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#labels > #states": {
"opacity": 1,
"fill": "#ffffff",
"stroke": "#000000",
"stroke-width": 0,
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 18,
"font-size": 18,
@ -397,7 +604,7 @@
"fill": "#ffffff",
"stroke": "#000000",
"stroke-width": 0,
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 18,
"font-size": 18,

View file

@ -183,7 +183,7 @@
"#roads": {
"opacity": 1,
"stroke": "#ff6000",
"stroke-width": 1.75,
"stroke-width": 1.25,
"stroke-dasharray": 0,
"stroke-linecap": "butt",
"filter": null,
@ -192,7 +192,7 @@
"#trails": {
"opacity": 1,
"stroke": "#ff6000",
"stroke-width": 1,
"stroke-width": 0.75,
"stroke-dasharray": 0,
"stroke-linecap": "butt",
"filter": null,
@ -317,53 +317,246 @@
"data-y": 93,
"data-columns": 8
},
"#burgLabels > #cities": {
"#burgLabels > g#city": {
"opacity": 1,
"fill": "#000000",
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 7,
"font-size": 7,
"font-family": "Lugrasimo"
"font-family": "Lugrasimo",
"data-dy": -0.4
},
"#burgIcons > #cities": {
"#burgIcons > g#city": {
"data-icon": "#icon-circle",
"opacity": 0.7,
"fill": "#000000",
"size": 1.75,
"font-size": 2,
"stroke": "#000000",
"stroke-width": 0,
"stroke-dasharray": 0,
"stroke-linecap": "butt"
},
"#anchors > #cities": {
"#anchors > g#city": {
"opacity": 1,
"fill": "#ffffff",
"size": 3.5,
"font-size": 2,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > #towns": {
"#burgLabels > g#town": {
"opacity": 1,
"fill": "#000000",
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 5,
"font-size": 5,
"font-family": "Lugrasimo"
"font-family": "Lugrasimo",
"data-dy": -0.4
},
"#burgIcons > #towns": {
"#burgIcons > g#town": {
"data-icon": "#icon-circle",
"opacity": 0.7,
"fill": "#000000",
"size": 1.25,
"font-size": 1.25,
"stroke": "#000000",
"stroke-width": 0,
"stroke-dasharray": 0,
"stroke-linecap": "butt"
},
"#anchors > #towns": {
"#anchors > g#town": {
"opacity": 1,
"fill": "#ffffff",
"size": 2,
"font-size": 1.25,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#capital": {
"opacity": 1,
"fill": "#000000",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 7,
"font-size": 7,
"font-family": "Lugrasimo",
"data-dy": -0.4
},
"#burgIcons > g#capital": {
"data-icon": "#icon-square",
"opacity": 0.7,
"fill": "#000000",
"font-size": 2,
"stroke": "#000000",
"stroke-width": 0,
"stroke-dasharray": 0,
"stroke-linecap": "butt"
},
"#anchors > g#capital": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 1.9,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#fort": {
"opacity": 1,
"fill": "#000000",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Lugrasimo",
"data-dy": -0.4
},
"#burgIcons > g#fort": {
"data-icon": "#icon-triangle",
"opacity": 0.7,
"fill": "#000000",
"font-size": 0.7,
"stroke": "#000000",
"stroke-width": 0,
"stroke-dasharray": 0,
"stroke-linecap": "butt"
},
"#anchors > g#fort": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#monastery": {
"opacity": 1,
"fill": "#000000",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Lugrasimo",
"data-dy": -0.4
},
"#burgIcons > g#monastery": {
"data-icon": "#icon-cross",
"opacity": 0.7,
"fill": "#000000",
"font-size": 0.7,
"stroke": "#000000",
"stroke-width": 0,
"stroke-dasharray": 0,
"stroke-linecap": "butt"
},
"#anchors > g#monastery": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#caravanserai": {
"opacity": 1,
"fill": "#000000",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Lugrasimo",
"data-dy": -0.4
},
"#burgIcons > g#caravanserai": {
"data-icon": "#icon-triangle",
"opacity": 0.7,
"fill": "#000000",
"font-size": 0.7,
"stroke": "#000000",
"stroke-width": 0,
"stroke-dasharray": 0,
"stroke-linecap": "butt"
},
"#anchors > g#caravanserai": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#trading_post": {
"opacity": 1,
"fill": "#000000",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Lugrasimo",
"data-dy": -0.4
},
"#burgIcons > g#trading_post": {
"data-icon": "#icon-triangle",
"opacity": 0.7,
"fill": "#000000",
"font-size": 0.7,
"stroke": "#000000",
"stroke-width": 0,
"stroke-dasharray": 0,
"stroke-linecap": "butt"
},
"#anchors > g#trading_post": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#village": {
"opacity": 1,
"fill": "#000000",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 3,
"font-size": 3,
"font-family": "Lugrasimo",
"data-dy": -0.4
},
"#burgIcons > g#village": {
"data-icon": "#icon-circle",
"opacity": 0.7,
"fill": "#000000",
"font-size": 0.7,
"stroke": "#000000",
"stroke-width": 0,
"stroke-dasharray": 0,
"stroke-linecap": "butt"
},
"#anchors > g#village": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#hamlet": {
"opacity": 1,
"fill": "#000000",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Lugrasimo",
"data-dy": -0.4
},
"#burgIcons > g#hamlet": {
"data-icon": "#icon-circle",
"opacity": 0.7,
"fill": "#000000",
"font-size": 0.5,
"stroke": "#000000",
"stroke-width": 0,
"stroke-dasharray": 0,
"stroke-linecap": "butt"
},
"#anchors > g#hamlet": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.5,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
@ -372,7 +565,7 @@
"fill": "#000000",
"stroke": "#000000",
"stroke-width": 0,
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 21,
"font-size": 21,
@ -384,7 +577,7 @@
"fill": "#000000",
"stroke": "#000000",
"stroke-width": 0,
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 18,
"font-size": 18,

View file

@ -328,55 +328,264 @@
"data-y": 93,
"data-columns": 8
},
"#burgLabels > #cities": {
"#burgLabels > g#capital": {
"opacity": 1,
"fill": "#3e3e4b",
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 7,
"font-size": 7,
"font-family": "Almendra SC"
"data-size": 6,
"font-size": 6,
"font-family": "Almendra SC",
"data-dy": -0.5
},
"#burgIcons > #cities": {
"#burgIcons > g#capital": {
"data-icon": "#icon-square",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 1,
"font-size": 2,
"stroke": "#3e3e4b",
"stroke-width": 0.24,
"stroke-dasharray": "",
"stroke-linecap": "butt"
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > #cities": {
"#anchors > g#capital": {
"opacity": 1,
"fill": "#ffffff",
"size": 2,
"font-size": 1.9,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > #towns": {
"#burgLabels > g#city": {
"opacity": 1,
"fill": "#3e3e4b",
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 4,
"font-size": 4,
"font-family": "Almendra SC"
"data-size": 5,
"font-size": 5,
"font-family": "Almendra SC",
"data-dy": -0.4
},
"#burgIcons > #towns": {
"#burgIcons > g#city": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 0.5,
"font-size": 1.5,
"stroke": "#3e3e4b",
"stroke-width": 0.12,
"stroke-dasharray": "",
"stroke-linecap": "butt"
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > #towns": {
"#anchors > g#city": {
"opacity": 1,
"fill": "#ffffff",
"size": 1,
"font-size": 1.5,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#fort": {
"opacity": 1,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Almendra SC",
"data-dy": -0.5
},
"#burgIcons > g#fort": {
"data-icon": "#icon-square",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#fort": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#monastery": {
"opacity": 1,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Almendra SC",
"data-dy": -0.5
},
"#burgIcons > g#monastery": {
"data-icon": "#icon-cross",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#monastery": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#caravanserai": {
"opacity": 1,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Almendra SC",
"data-dy": -0.5
},
"#burgIcons > g#caravanserai": {
"data-icon": "#icon-triangle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#caravanserai": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#trading_post": {
"opacity": 1,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Almendra SC",
"data-dy": -0.5
},
"#burgIcons > g#trading_post": {
"data-icon": "#icon-triangle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#trading_post": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#village": {
"opacity": 1,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 3,
"font-size": 3,
"font-family": "Almendra SC",
"data-dy": -0.4
},
"#burgIcons > g#village": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#village": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#hamlet": {
"opacity": 1,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Almendra SC",
"data-dy": -0.4
},
"#burgIcons > g#hamlet": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.5,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#hamlet": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.5,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#town": {
"opacity": 1,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 4,
"font-size": 4,
"font-family": "Almendra SC",
"data-dy": -0.4
},
"#burgIcons > g#town": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 1,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#town": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 1,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
@ -385,7 +594,7 @@
"fill": "#3e3e4b",
"stroke": "#3a3a3a",
"stroke-width": 0,
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 22,
"font-size": 22,
@ -397,7 +606,7 @@
"fill": "#3e3e4b",
"stroke": "#3a3a3a",
"stroke-width": 0,
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 18,
"font-size": 18,

View file

@ -226,7 +226,7 @@
"#statesHalo": {
"opacity": 0.5,
"data-width": 12,
"stroke-width": 12,
"stroke-width": 10,
"filter": "blur(10px)"
},
"#provs": {
@ -331,64 +331,318 @@
"data-columns": 8
},
"#legendBox": {},
"#burgLabels > #cities": {
"#burgLabels > g#capital": {
"opacity": 1,
"fill": "#3e3e4b",
"text-shadow": "white 0 0 2px",
"letter-spacing": 0,
"data-size": 8,
"font-size": 8,
"font-family": "Underdog"
"stroke": null,
"stroke-width": null,
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": null,
"data-size": 7,
"font-size": 7,
"font-family": "Underdog",
"data-dx": null,
"data-dy": -0.4
},
"#burgIcons > #cities": {
"#burgIcons > g#capital": {
"data-icon": "#icon-watabou-capital",
"opacity": 1,
"font-size": 2,
"fill": "#8c8c8c",
"fill-opacity": 1,
"stroke": "#4D3F36",
"stroke-width": 1.4,
"stroke-dasharray": null,
"stroke-linecap": "round",
"stroke-linejoin": "round",
"filter": null
},
"#anchors > g#capital": {
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 2,
"stroke": "#444444",
"stroke-width": 0.25,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #cities": {
"opacity": 0.8,
"fill": "#ffffff",
"size": 4,
"font-size": 1.9,
"stroke": "#3e3e4b",
"stroke-width": 1
"stroke-width": 1.2,
"filter": null
},
"#burgLabels > #towns": {
"#burgLabels > g#city": {
"opacity": 1,
"fill": "#3e3e4b",
"text-shadow": "none",
"letter-spacing": 0,
"stroke": null,
"stroke-width": null,
"style": "text-shadow: white 0px 0px 2px",
"letter-spacing": null,
"data-size": 6,
"font-size": 6,
"font-family": "Underdog",
"data-dx": null,
"data-dy": -0.7
},
"#burgIcons > g#city": {
"data-icon": "#icon-watabou-city",
"opacity": 1,
"font-size": 2,
"fill": "#8c8c8c",
"fill-opacity": 1,
"stroke": "#4D3F36",
"stroke-width": 1.4,
"stroke-dasharray": null,
"stroke-linecap": "round",
"stroke-linejoin": "round",
"filter": null
},
"#anchors > g#city": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 1.2,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"filter": null
},
"#burgLabels > g#fort": {
"opacity": 1,
"fill": "#3e3e4b",
"stroke": null,
"stroke-width": null,
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": null,
"data-size": 2,
"font-size": 2,
"font-family": "Underdog",
"data-dx": null,
"data-dy": -2
},
"#burgIcons > g#fort": {
"data-icon": "#icon-watabou-fort",
"opacity": 1,
"fill": "#8c8c8c",
"fill-opacity": 1,
"font-size": 2,
"stroke": "#4D3F36",
"stroke-width": 1.8,
"stroke-dasharray": null,
"stroke-linecap": "round",
"stroke-linejoin": "round",
"filter": null
},
"#anchors > g#fort": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"filter": null
},
"#burgLabels > g#monastery": {
"opacity": 1,
"fill": "#3e3e4b",
"stroke": null,
"stroke-width": null,
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": null,
"data-size": 2,
"font-size": 2,
"font-family": "Underdog",
"data-dx": null,
"data-dy": -1.8
},
"#burgIcons > g#monastery": {
"opacity": 1,
"data-icon": "#icon-watabou-monastery",
"font-size": 2.3,
"fill": "#8c8c8c",
"fill-opacity": 1,
"stroke": "#4D3F36",
"stroke-width": 1.4,
"stroke-dasharray": null,
"stroke-linecap": "round",
"stroke-linejoin": "round",
"filter": null
},
"#anchors > g#monastery": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"filter": null
},
"#burgLabels > g#caravanserai": {
"opacity": 1,
"fill": "#3e3e4b",
"stroke": null,
"stroke-width": null,
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": null,
"data-size": 2,
"font-size": 2,
"font-family": "Underdog",
"data-dx": null,
"data-dy": -1
},
"#burgIcons > g#caravanserai": {
"opacity": 1,
"data-icon": "#icon-watabou-caravanserai",
"font-size": 2.75,
"fill": "#8c8c8c",
"fill-opacity": 1,
"stroke": "#4D3F36",
"stroke-width": 1.4,
"stroke-dasharray": null,
"stroke-linecap": "round",
"stroke-linejoin": "round",
"filter": null
},
"#anchors > g#caravanserai": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"filter": null
},
"#burgLabels > g#trading_post": {
"opacity": 1,
"fill": "#3e3e4b",
"stroke": null,
"stroke-width": null,
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": null,
"data-size": 2,
"font-size": 2,
"font-family": "Underdog",
"data-dx": null,
"data-dy": -1.2
},
"#burgIcons > g#trading_post": {
"opacity": 1,
"data-icon": "#icon-watabou-post",
"font-size": 2,
"fill": "#8c8c8c",
"fill-opacity": 1,
"stroke": "#4D3F36",
"stroke-width": 1.4,
"stroke-dasharray": null,
"stroke-linecap": "round",
"stroke-linejoin": "round",
"filter": null
},
"#anchors > g#trading_post": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"filter": null
},
"#burgLabels > g#village": {
"opacity": 1,
"fill": "#3e3e4b",
"stroke": null,
"stroke-width": null,
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": null,
"data-size": 3,
"font-size": 3,
"font-family": "Underdog",
"data-dx": null,
"data-dy": -0.6
},
"#burgIcons > g#village": {
"opacity": 1,
"data-icon": "#icon-watabou-village",
"font-size": 1.7,
"fill": "#8c8c8c",
"fill-opacity": 1,
"stroke": "#4D3F36",
"stroke-width": 1.4,
"stroke-dasharray": null,
"stroke-linecap": "round",
"stroke-linejoin": "round",
"filter": null
},
"#anchors > g#village": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"filter": null
},
"#burgLabels > g#hamlet": {
"opacity": 1,
"fill": "#3e3e4b",
"stroke": null,
"stroke-width": null,
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": null,
"data-size": 2,
"font-size": 2,
"font-family": "Underdog",
"data-dx": 0,
"data-dy": -1
},
"#burgIcons > g#hamlet": {
"opacity": 1,
"data-icon": "#icon-watabou-hamlet",
"font-size": 1.8,
"fill": "#8c8c8c",
"fill-opacity": 1,
"stroke": "#4D3F36",
"stroke-width": 1.4,
"stroke-dasharray": null,
"stroke-linecap": "round",
"stroke-linejoin": "round",
"filter": null
},
"#anchors > g#hamlet": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.5,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"filter": null
},
"#burgLabels > g#town": {
"opacity": 1,
"fill": "#3e3e4b",
"stroke": null,
"stroke-width": null,
"style": "text-shadow: white 0px 0px 2px",
"letter-spacing": null,
"data-size": 4,
"font-size": 4,
"font-family": "Underdog"
"font-family": "Underdog",
"data-dx": null,
"data-dy": -0.6
},
"#burgIcons > #towns": {
"opacity": 0.95,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 0.8,
"stroke": "#3e3e4b",
"stroke-width": 0.2,
"stroke-dasharray": "",
"stroke-linecap": "butt"
"#burgIcons > g#town": {
"opacity": 1,
"data-icon": "#icon-watabou-town",
"font-size": 1.8,
"fill": "#8c8c8c",
"fill-opacity": 1,
"stroke": "#4D3F36",
"stroke-width": 1.4,
"stroke-dasharray": null,
"stroke-linecap": "round",
"stroke-linejoin": "round",
"filter": null
},
"#anchors > #towns": {
"#anchors > g#town": {
"opacity": 1,
"fill": "#ffffff",
"size": 1.6,
"font-size": 1,
"stroke": "#3e3e4b",
"stroke-width": 1.2
"stroke-width": 1.2,
"filter": null
},
"#labels > #states": {
"opacity": 1,
"fill": "#4e4e4e",
"stroke": "#b5b5b5",
"stroke-width": 0,
"text-shadow": "white 0 0 2px",
"style": "text-shadow: white 0 0 2px",
"letter-spacing": 0,
"data-size": 20,
"font-size": 20,
@ -400,7 +654,7 @@
"fill": "#3e3e4b",
"stroke": "#3a3a3a",
"stroke-width": 0,
"text-shadow": "white 0 0 4px",
"style": "text-shadow: white 0 0 4px",
"letter-spacing": 0,
"data-size": 18,
"font-size": 18,

View file

@ -328,64 +328,318 @@
"data-y": 62.98,
"data-columns": 8
},
"#burgLabels > #cities": {
"opacity": 1,
"fill": "#3a3a3a",
"text-shadow": "white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 8,
"font-size": 8,
"font-family": "IM Fell English"
},
"#burgIcons > #cities": {
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 3,
"stroke": "#3e3e4b",
"stroke-width": 0.4,
"stroke-dasharray": "0.5 0.25",
"stroke-linecap": "butt"
},
"#anchors > #cities": {
"opacity": 1,
"fill": "#ffffff",
"size": 5.5,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > #towns": {
"#burgLabels > g#capital": {
"opacity": 1,
"fill": "#3e3e4b",
"text-shadow": "white 0px 0px 4px",
"stroke": null,
"stroke-width": null,
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 7,
"font-size": 7,
"font-family": "IM Fell English",
"data-dx": null,
"data-dy": -0.4
},
"#burgIcons > g#capital": {
"data-icon": "#icon-star-circled",
"opacity": 1,
"font-size": 2,
"fill": "#ffffff",
"fill-opacity": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": null,
"filter": null
},
"#anchors > g#capital": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.6,
"stroke": "#3e3e4b",
"stroke-width": 0.5,
"filter": null
},
"#burgLabels > g#city": {
"opacity": 1,
"fill": "#3a3a3a",
"stroke": null,
"stroke-width": null,
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 6,
"font-size": 6,
"font-family": "IM Fell English",
"data-dx": null,
"data-dy": -0.4
},
"#burgIcons > g#city": {
"data-icon": "#icon-circled",
"opacity": 1,
"font-size": 1.8,
"fill": "#ffffff",
"fill-opacity": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": null,
"filter": null
},
"#anchors > g#city": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.6,
"stroke": "#3e3e4b",
"stroke-width": 0.5,
"filter": null
},
"#burgLabels > g#fort": {
"opacity": 1,
"fill": "#3e3e4b",
"stroke": null,
"stroke-width": null,
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "IM Fell English",
"data-dx": null,
"data-dy": -0.5
},
"#burgIcons > g#fort": {
"data-icon": "#icon-square",
"opacity": 1,
"font-size": 0.7,
"fill": "#ffffff",
"fill-opacity": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": null,
"filter": null
},
"#anchors > g#fort": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"filter": null
},
"#burgLabels > g#monastery": {
"opacity": 1,
"fill": "#3e3e4b",
"stroke": null,
"stroke-width": null,
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "IM Fell English",
"data-dx": null,
"data-dy": -0.5
},
"#burgIcons > g#monastery": {
"data-icon": "#icon-cross",
"opacity": 1,
"font-size": 0.8,
"fill": "#ffffff",
"fill-opacity": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": null,
"filter": null
},
"#anchors > g#monastery": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.5,
"stroke": "#3e3e4b",
"stroke-width": 2,
"filter": null
},
"#burgLabels > g#caravanserai": {
"opacity": 1,
"fill": "#3e3e4b",
"stroke": null,
"stroke-width": null,
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "IM Fell English",
"data-dx": null,
"data-dy": -0.5
},
"#burgIcons > g#caravanserai": {
"data-icon": "#icon-triangle",
"opacity": 1,
"font-size": 0.7,
"fill": "#ffffff",
"fill-opacity": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": null,
"filter": null
},
"#anchors > g#caravanserai": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"filter": null
},
"#burgLabels > g#trading_post": {
"opacity": 1,
"fill": "#3e3e4b",
"stroke": null,
"stroke-width": null,
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "IM Fell English",
"data-dx": null,
"data-dy": -0.5
},
"#burgIcons > g#trading_post": {
"data-icon": "#icon-triangle",
"opacity": 1,
"font-size": 0.7,
"fill": "#ffffff",
"fill-opacity": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": null,
"filter": null
},
"#anchors > g#trading_post": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"filter": null
},
"#burgLabels > g#village": {
"opacity": 1,
"fill": "#3e3e4b",
"stroke": null,
"stroke-width": null,
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 3,
"font-size": 3,
"font-family": "IM Fell English",
"data-dx": null,
"data-dy": -0.4
},
"#burgIcons > g#village": {
"data-icon": "#icon-circle",
"opacity": 1,
"font-size": 0.8,
"fill": "#ffffff",
"fill-opacity": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": null,
"filter": null
},
"#anchors > g#village": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.5,
"stroke": "#3e3e4b",
"stroke-width": 1.8,
"filter": null
},
"#burgLabels > g#hamlet": {
"opacity": 1,
"fill": "#3e3e4b",
"stroke": null,
"stroke-width": null,
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "IM Fell English",
"data-dx": null,
"data-dy": -0.4
},
"#burgIcons > g#hamlet": {
"data-icon": "#icon-circle",
"opacity": 1,
"font-size": 0.5,
"fill": "#ffffff",
"fill-opacity": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": null,
"filter": null
},
"#anchors > g#hamlet": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.4,
"stroke": "#3e3e4b",
"stroke-width": 1.6,
"filter": null
},
"#burgLabels > g#town": {
"opacity": 1,
"fill": "#3e3e4b",
"stroke": null,
"stroke-width": null,
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 4,
"font-size": 4,
"font-family": "IM Fell English"
"font-family": "IM Fell English",
"data-dx": null,
"data-dy": -0.4
},
"#burgIcons > #towns": {
"#burgIcons > g#town": {
"data-icon": "#icon-circle",
"opacity": 1,
"font-size": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 1.2,
"stroke": "#3e3e4b",
"stroke-width": 0.2,
"stroke-dasharray": "",
"stroke-linecap": "butt"
"stroke-width": 1.2,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": null,
"filter": null
},
"#anchors > #towns": {
"#anchors > g#town": {
"opacity": 1,
"fill": "#ffffff",
"size": 2.2,
"font-size": 0.6,
"stroke": "#3e3e4b",
"stroke-width": 1.2
"stroke-width": 2,
"filter": null
},
"#labels > #states": {
"opacity": 1,
"fill": "#3e3e3e",
"stroke": "#000000",
"stroke-width": 0.3,
"text-shadow": "white 0px 0px 6px",
"style": "text-shadow: white 0px 0px 6px",
"letter-spacing": 0,
"data-size": 14,
"font-size": 14,
@ -397,7 +651,7 @@
"fill": "#f24706",
"stroke": "#701b05",
"stroke-width": 0.1,
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 6,
"font-size": 6,

View file

@ -324,55 +324,260 @@
"data-columns": 8
},
"#legendBox": {},
"#burgLabels > #cities": {
"#burgLabels > g#city": {
"opacity": 1,
"fill": "#000000",
"text-shadow": "white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 7,
"font-size": 7,
"font-family": "Courier New"
"font-family": "Courier New",
"data-dy": -0.4
},
"#burgIcons > #cities": {
"#burgIcons > g#city": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 1,
"font-size": 1.5,
"stroke": "#3e3e4b",
"stroke-width": 0.24,
"stroke-width": 1,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #cities": {
"#anchors > g#city": {
"opacity": 1,
"fill": "#ffffff",
"size": 2,
"font-size": 1.5,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > #towns": {
"#burgLabels > g#town": {
"opacity": 1,
"fill": "#000000",
"text-shadow": "white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 4,
"font-size": 4,
"font-family": "Courier New"
"font-family": "Courier New",
"data-dy": -0.4
},
"#burgIcons > #towns": {
"#burgIcons > g#town": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 0.5,
"font-size": 1,
"stroke": "#3e3e4b",
"stroke-width": 0.12,
"stroke-width": 1.2,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #towns": {
"#anchors > g#town": {
"opacity": 1,
"fill": "#ffffff",
"size": 1,
"font-size": 1,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#capital": {
"opacity": 1,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 7,
"font-size": 7,
"font-family": "Courier New",
"data-dy": -0.4
},
"#burgIcons > g#capital": {
"data-icon": "#icon-square",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 2,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#capital": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 1.9,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#fort": {
"opacity": 1,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Courier New",
"data-dy": -0.4
},
"#burgIcons > g#fort": {
"data-icon": "#icon-triangle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#fort": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#monastery": {
"opacity": 1,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Courier New",
"data-dy": -0.4
},
"#burgIcons > g#monastery": {
"data-icon": "#icon-cross",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#monastery": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#caravanserai": {
"opacity": 1,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Courier New",
"data-dy": -0.4
},
"#burgIcons > g#caravanserai": {
"data-icon": "#icon-triangle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#caravanserai": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#trading_post": {
"opacity": 1,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Courier New",
"data-dy": -0.4
},
"#burgIcons > g#trading_post": {
"data-icon": "#icon-triangle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#trading_post": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#village": {
"opacity": 1,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 3,
"font-size": 3,
"font-family": "Courier New",
"data-dy": -0.4
},
"#burgIcons > g#village": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#village": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#hamlet": {
"opacity": 1,
"fill": "#3e3e4b",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Courier New",
"data-dy": -0.4
},
"#burgIcons > g#hamlet": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.5,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#hamlet": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.5,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
@ -381,7 +586,7 @@
"fill": "#000000",
"stroke": "#3a3a3a",
"stroke-width": 0,
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 18,
"font-size": 18,
@ -393,7 +598,7 @@
"fill": "#3e3e4b",
"stroke": "#3a3a3a",
"stroke-width": 0,
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 18,
"font-size": 18,

View file

@ -328,64 +328,271 @@
"data-y": 99.37,
"data-columns": null
},
"#burgLabels > #cities": {
"#burgLabels > g#city": {
"opacity": 1,
"fill": "#dbdbe1",
"text-shadow": "black 0px 0px 4px",
"style": "text-shadow: black 0px 0px 4px",
"letter-spacing": 0,
"data-size": 8,
"font-size": 8,
"font-family": "Courier New"
"data-size": 7,
"font-size": 7,
"font-family": "Courier New",
"data-dy": -0.4
},
"#burgIcons > #cities": {
"#burgIcons > g#city": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 2.1,
"font-size": 2,
"stroke": "#000000",
"stroke-width": 0.2,
"stroke-width": 1,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #cities": {
"#anchors > g#city": {
"opacity": 1,
"fill": "#ffffff",
"size": 4.2,
"font-size": 2,
"stroke": "#000000",
"stroke-width": 1.46
},
"#burgLabels > #towns": {
"#burgLabels > g#town": {
"opacity": 1,
"fill": "#ffffff",
"text-shadow": "black 0px 0px 4px",
"style": "text-shadow: black 0px 0px 4px",
"letter-spacing": 0,
"data-size": 4.28,
"font-size": 4.28,
"font-family": "Courier New"
"data-size": 4,
"font-size": 4,
"font-family": "Courier New",
"data-dy": -0.4
},
"#burgIcons > #towns": {
"#burgIcons > g#town": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 0.8,
"font-size": 1,
"stroke": "#000000",
"stroke-width": 0.1,
"stroke-width": 1,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #towns": {
"#anchors > g#town": {
"opacity": 1,
"fill": "#ffffff",
"size": 1.6,
"font-size": 1,
"stroke": "#000000",
"stroke-width": 1.53
},
"#burgLabels > g#capital": {
"opacity": 1,
"fill": "#ffffff",
"style": "text-shadow: black 0px 0px 4px",
"letter-spacing": 0,
"data-size": 8,
"font-size": 8,
"font-family": "Courier New",
"data-dy": -0.4
},
"#burgIcons > g#capital": {
"data-icon": "#icon-square",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 2,
"stroke": "#000000",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#capital": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 1.9,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#fort": {
"opacity": 1,
"fill": "#ffffff",
"style": "text-shadow: black 0px 0px 4px",
"letter-spacing": 0,
"data-size": 3,
"font-size": 3,
"font-family": "Courier New",
"data-dy": -0.4
},
"#burgIcons > g#fort": {
"data-icon": "#icon-triangle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#000000",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#fort": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#monastery": {
"opacity": 1,
"fill": "#ffffff",
"style": "text-shadow: black 0px 0px 4px",
"letter-spacing": 0,
"data-size": 3,
"font-size": 3,
"font-family": "Courier New",
"data-dy": -0.4
},
"#burgIcons > g#monastery": {
"data-icon": "#icon-cross",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#000000",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#monastery": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#caravanserai": {
"opacity": 1,
"fill": "#ffffff",
"style": "text-shadow: black 0px 0px 4px",
"letter-spacing": 0,
"data-size": 3,
"font-size": 3,
"font-family": "Courier New",
"data-dy": -0.4
},
"#burgIcons > g#caravanserai": {
"data-icon": "#icon-triangle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#000000",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#caravanserai": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#trading_post": {
"opacity": 1,
"fill": "#ffffff",
"style": "text-shadow: black 0px 0px 4px",
"letter-spacing": 0,
"data-size": 3,
"font-size": 3,
"font-family": "Courier New",
"data-dy": -0.4
},
"#burgIcons > g#trading_post": {
"data-icon": "#icon-triangle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#000000",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#trading_post": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#village": {
"opacity": 1,
"fill": "#ffffff",
"style": "text-shadow: black 0px 0px 4px",
"letter-spacing": 0,
"data-size": 3,
"font-size": 3,
"font-family": "Courier New",
"data-dy": -0.4
},
"#burgIcons > g#village": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#000000",
"stroke-width": 1.2,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#village": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#hamlet": {
"opacity": 1,
"fill": "#ffffff",
"style": "text-shadow: black 0px 0px 4px",
"letter-spacing": 0,
"data-size": 3,
"font-size": 3,
"font-family": "Courier New",
"data-dy": -0.4
},
"#burgIcons > g#hamlet": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.5,
"stroke": "#000000",
"stroke-width": 1.2,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#hamlet": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.5,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#labels > #states": {
"opacity": 1,
"fill": "#5d77a2",
"stroke": "#7a83ae",
"stroke-width": 0.3,
"text-shadow": "black 0px 0px 0.1px",
"style": "text-shadow: black 0px 0px 0.1px",
"letter-spacing": 0,
"data-size": 14,
"font-size": 14,
@ -397,7 +604,7 @@
"fill": "#3e3e4b",
"stroke": "#3a3a3a",
"stroke-width": 0,
"text-shadow": "black 0px 0px 4px",
"style": "text-shadow: black 0px 0px 4px",
"letter-spacing": 0,
"data-size": 18,
"font-size": 18,

View file

@ -328,64 +328,271 @@
"data-y": 62.98,
"data-columns": 8
},
"#burgLabels > #cities": {
"#burgLabels > g#city": {
"opacity": 0.8,
"fill": "#3a3a3a",
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 7,
"font-size": 7,
"font-family": "Arima Madurai"
"font-family": "Arima Madurai",
"data-dy": -0.4
},
"#burgIcons > #cities": {
"#burgIcons > g#city": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 1.5,
"font-size": 1.5,
"stroke": "#4f4f4f",
"stroke-width": 0.2,
"stroke-width": 1,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #cities": {
"#anchors > g#city": {
"opacity": 1,
"fill": "#ffffff",
"size": 3,
"font-size": 1.5,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > #towns": {
"#burgLabels > g#town": {
"opacity": 0.8,
"fill": "#3e3e4b",
"text-shadow": "white 0px 0px 4px",
"fill": "#3a3a3a",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 4,
"font-size": 4,
"font-family": "Arima Madurai"
"font-family": "Arima Madurai",
"data-dy": -0.4
},
"#burgIcons > #towns": {
"#burgIcons > g#town": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 0.6,
"font-size": 0.6,
"stroke": "#4f4f4f",
"stroke-width": 0.12,
"stroke-width": 1,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #towns": {
"#anchors > g#town": {
"opacity": 1,
"fill": "#ffffff",
"size": 1.2,
"font-size": 0.6,
"stroke": "#3e3e4b",
"stroke-width": 1
},
"#burgLabels > g#capital": {
"opacity": 0.8,
"fill": "#3a3a3a",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 7,
"font-size": 7,
"font-family": "Arima Madurai",
"data-dy": -0.4
},
"#burgIcons > g#capital": {
"data-icon": "#icon-square",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 2,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#capital": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 1.9,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#fort": {
"opacity": 0.8,
"fill": "#3a3a3a",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Arima Madurai",
"data-dy": -0.4
},
"#burgIcons > g#fort": {
"data-icon": "#icon-triangle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#fort": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#monastery": {
"opacity": 0.8,
"fill": "#3a3a3a",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Arima Madurai",
"data-dy": -0.4
},
"#burgIcons > g#monastery": {
"data-icon": "#icon-cross",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#monastery": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#caravanserai": {
"opacity": 0.8,
"fill": "#3a3a3a",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Arima Madurai",
"data-dy": -0.4
},
"#burgIcons > g#caravanserai": {
"data-icon": "#icon-triangle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#caravanserai": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#trading_post": {
"opacity": 0.8,
"fill": "#3a3a3a",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Arima Madurai",
"data-dy": -0.4
},
"#burgIcons > g#trading_post": {
"data-icon": "#icon-triangle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#trading_post": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#village": {
"opacity": 0.8,
"fill": "#3a3a3a",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 3,
"font-size": 3,
"font-family": "Arima Madurai",
"data-dy": -0.4
},
"#burgIcons > g#village": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#village": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#hamlet": {
"opacity": 0.8,
"fill": "#3a3a3a",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Arima Madurai",
"data-dy": -0.4
},
"#burgIcons > g#hamlet": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.5,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#hamlet": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.5,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#labels > #states": {
"opacity": 0.8,
"fill": "#3e3e3e",
"fill": "#3a3a3a",
"stroke": "#000000",
"stroke-width": 0,
"text-shadow": "white 0px 0px 6px",
"style": "text-shadow: white 0px 0px 6px",
"letter-spacing": 0,
"data-size": 14,
"font-size": 14,
@ -397,7 +604,7 @@
"fill": "#f24706",
"stroke": "#701b05",
"stroke-width": 0.1,
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 6,
"font-size": 6,

View file

@ -328,55 +328,262 @@
"data-y": 93,
"data-columns": 8
},
"#burgLabels > #cities": {
"#burgLabels > g#city": {
"opacity": 1,
"fill": "#043449",
"text-shadow": "white 0px 0px 2px",
"style": "text-shadow: white 0px 0px 2px",
"letter-spacing": 0,
"data-size": 5,
"font-size": 5,
"font-family": "Comfortaa"
"font-family": "Comfortaa",
"data-dy": -0.4
},
"#burgIcons > #cities": {
"#burgIcons > g#city": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 1,
"font-size": 1.5,
"stroke": "#3e3e4b",
"stroke-width": 0.24,
"stroke-width": 1.2,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #cities": {
"#anchors > g#city": {
"opacity": 1,
"fill": "#ffffff",
"size": 2,
"font-size": 1.5,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > #towns": {
"#burgLabels > g#town": {
"opacity": 1,
"fill": "#3e3e4b",
"text-shadow": "white 0px 0px 4px",
"fill": "#043449",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 3,
"font-size": 3,
"font-family": "Comfortaa"
"font-family": "Comfortaa",
"data-dy": -0.4
},
"#burgIcons > #towns": {
"#burgIcons > g#town": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"size": 0.5,
"font-size": 1,
"stroke": "#3e3e4b",
"stroke-width": 0.12,
"stroke-width": 1.2,
"stroke-dasharray": "",
"stroke-linecap": "butt"
},
"#anchors > #towns": {
"#anchors > g#town": {
"opacity": 1,
"fill": "#ffffff",
"size": 1,
"font-size": 1,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#capital": {
"opacity": 1,
"fill": "#043449",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 7,
"font-size": 7,
"font-family": "Comfortaa",
"data-dy": -0.4
},
"#burgIcons > g#capital": {
"data-icon": "#icon-square",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 2,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#capital": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 1.9,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#fort": {
"opacity": 1,
"fill": "#043449",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Comfortaa",
"data-dy": -0.4
},
"#burgIcons > g#fort": {
"data-icon": "#icon-triangle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#fort": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#monastery": {
"opacity": 1,
"fill": "#043449",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Comfortaa",
"data-dy": -0.4
},
"#burgIcons > g#monastery": {
"data-icon": "#icon-cross",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#monastery": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#caravanserai": {
"opacity": 1,
"fill": "#043449",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Comfortaa",
"data-dy": -0.4
},
"#burgIcons > g#caravanserai": {
"data-icon": "#icon-triangle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#caravanserai": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#trading_post": {
"opacity": 1,
"fill": "#043449",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Comfortaa",
"data-dy": -0.4
},
"#burgIcons > g#trading_post": {
"data-icon": "#icon-triangle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#trading_post": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#village": {
"opacity": 1,
"fill": "#043449",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 3,
"font-size": 3,
"font-family": "Comfortaa",
"data-dy": -0.4
},
"#burgIcons > g#village": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#village": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.7,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
"#burgLabels > g#hamlet": {
"opacity": 1,
"fill": "#043449",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 2,
"font-size": 2,
"font-family": "Comfortaa",
"data-dy": -0.4
},
"#burgIcons > g#hamlet": {
"data-icon": "#icon-circle",
"opacity": 1,
"fill": "#ffffff",
"fill-opacity": 0.7,
"font-size": 0.5,
"stroke": "#3e3e4b",
"stroke-width": 1.2,
"stroke-dasharray": null,
"stroke-linecap": "butt",
"stroke-linejoin": "round"
},
"#anchors > g#hamlet": {
"opacity": 1,
"fill": "#ffffff",
"font-size": 0.5,
"stroke": "#3e3e4b",
"stroke-width": 1.2
},
@ -385,7 +592,7 @@
"fill": "#ffffff",
"stroke": "#000000",
"stroke-width": 0.15,
"text-shadow": "black 1px 1px 3px",
"style": "text-shadow: black 1px 1px 3px",
"letter-spacing": 0,
"data-size": 18,
"font-size": 18,
@ -397,7 +604,7 @@
"fill": "#3e3e4b",
"stroke": "#3a3a3a",
"stroke-width": 0,
"text-shadow": "white 0px 0px 4px",
"style": "text-shadow: white 0px 0px 4px",
"letter-spacing": 0,
"data-size": 16,
"font-size": 16,

View file

@ -169,6 +169,7 @@ void (function () {
input.required = options.required === false ? false : true;
input.placeholder = "type a " + type;
input.value = options.default;
input.style.width = promptText.length > 10 ? "100%" : "auto";
prompt.style.display = "block";
form.addEventListener(

View file

@ -51,10 +51,10 @@ function parseTransform(string) {
JSON.isValid = str => {
try {
JSON.parse(str);
return true;
} catch (e) {
return false;
}
return true;
};
JSON.safeParse = str => {
@ -64,3 +64,18 @@ JSON.safeParse = str => {
return null;
}
};
function sanitizeId(string) {
if (!string) throw new Error("No string provided");
let sanitized = string
.toLowerCase()
.trim()
.replace(/[^a-z0-9-_]/g, "") // no invalid characters
.replace(/\s+/g, "-"); // replace spaces with hyphens
// remove leading numbers
if (sanitized.match(/^\d/)) sanitized = "_" + sanitized;
return sanitized;
}

View file

@ -13,7 +13,7 @@
* Example: 1.102.2 -> Major version 1, Minor version 102, Patch version 2
*/
const VERSION = "1.108.12";
const VERSION = "1.109.0";
if (parseMapVersion(VERSION) !== VERSION) alert("versioning.js: Invalid format or parsing function");
{
@ -33,10 +33,11 @@ if (parseMapVersion(VERSION) !== VERSION) alert("versioning.js: Invalid format o
const patreon = "https://www.patreon.com/azgaar";
alertMessage.innerHTML = /* html */ `The Fantasy Map Generator is updated up to version <strong>${VERSION}</strong>. This version is compatible with <a href="${changelog}" target="_blank">previous versions</a>, loaded save files will be auto-updated.
${storedVersion ? "<span>Click on OK and then reload the page to fetch fresh code.</span>" : ""}
${storedVersion ? "<span>In case of errors reload the page to update the code.</span>" : ""}
<ul>
<strong>Latest changes:</strong>
<li>Custom burg grouping and icon selection</li>
<li>Ability to set custom image as Marker or Regiment icon</li>
<li>Submap and Transform tools rework</li>
<li>Azgaar Bot to answer questions and provide help</li>