mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2025-12-17 09:41:24 +01:00
feat: burg group editor - form
This commit is contained in:
parent
32808605b3
commit
d8009f84ad
8 changed files with 156 additions and 53 deletions
16
index.css
16
index.css
|
|
@ -19,6 +19,11 @@ font {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
form input:invalid {
|
||||||
|
outline: 1px solid #ed4337;
|
||||||
|
outline-offset: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
input,
|
input,
|
||||||
select,
|
select,
|
||||||
button {
|
button {
|
||||||
|
|
@ -53,6 +58,7 @@ input:read-only {
|
||||||
input[type="radio"] {
|
input[type="radio"] {
|
||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
accent-color: var(--header);
|
||||||
}
|
}
|
||||||
|
|
||||||
textarea {
|
textarea {
|
||||||
|
|
@ -1889,7 +1895,8 @@ div.editorLine {
|
||||||
width: 5em;
|
width: 5em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#militaryOptionsTable button {
|
#militaryOptionsTable button,
|
||||||
|
#burgGroupsBody button {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1922,7 +1929,12 @@ ul.share-buttons img {
|
||||||
width: 2em;
|
width: 2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
input[type="checkbox"] {
|
input[type="checkbox"].native {
|
||||||
|
accent-color: var(--header);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"]:not(.native) {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
53
index.html
53
index.html
|
|
@ -3364,11 +3364,7 @@
|
||||||
<div data-tip="Select burg group. Groups defines burg icon, label size and style">
|
<div data-tip="Select burg group. Groups defines burg icon, label size and style">
|
||||||
<div class="label">Group:</div>
|
<div class="label">Group:</div>
|
||||||
<select id="burgGroup" style="width: 9em"></select>
|
<select id="burgGroup" style="width: 9em"></select>
|
||||||
<span
|
<span id="burgGroupConfigure" data-tip="Configure burg groups" class="icon-cog pointer"></span>
|
||||||
id="burgGroupEdit"
|
|
||||||
data-tip="Edit burg groups"
|
|
||||||
class="icon-pencil pointer"
|
|
||||||
></span>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div data-tip="Select burg type. Type slightly affects emblem generation">
|
<div data-tip="Select burg type. Type slightly affects emblem generation">
|
||||||
|
|
@ -5344,28 +5340,43 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="burgGroupsEditor" class="dialog stable" style="display: none">
|
<div id="burgGroupsEditor" class="dialog stable" style="display: none">
|
||||||
<div class="table">
|
<form id="burgGroupsForm" class="table">
|
||||||
<table>
|
<table>
|
||||||
|
<colgroup>
|
||||||
|
<col style="width: 1em" />
|
||||||
|
<col style="width: 6em" />
|
||||||
|
<col style="width: 4em" />
|
||||||
|
<col style="width: 4em" />
|
||||||
|
<col style="width: 4em" />
|
||||||
|
<col style="width: 5em" />
|
||||||
|
<col style="width: 5em" />
|
||||||
|
<col style="width: 5em" />
|
||||||
|
<col style="width: 5em" />
|
||||||
|
<col style="width: 5em" />
|
||||||
|
<col style="width: 1em" />
|
||||||
|
<col style="width: 1em" />
|
||||||
|
</colgroup>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th data-tip="Unit icon">Icon</th>
|
<th data-tip="Select group to be assigned if other groups are not passed">Default</th>
|
||||||
<th data-tip="Unit name. If name is changed for existing unit, old unit will be replaced">Unit name</th>
|
<th data-tip="Type group name">Name</th>
|
||||||
<th style="width: 5em" data-tip="Select allowed biomes">Biomes</th>
|
<th data-tip="Set min population constraint" colspan="2">Population</th>
|
||||||
<th style="width: 5em" data-tip="Select allowed states">States</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 style="width: 5em" data-tip="Select allowed cultures">Cultures</th>
|
<th data-tip="Select allowed biomes">Biomes</th>
|
||||||
<th style="width: 5em" data-tip="Select allowed religions">Religions</th>
|
<th data-tip="Select allowed states">States</th>
|
||||||
<th data-tip="Conscription percentage for rural population">Rural</th>
|
<th data-tip="Select allowed cultures">Cultures</th>
|
||||||
<th data-tip="Conscription percentage for urban population">Urban</th>
|
<th data-tip="Select allowed religions">Religions</th>
|
||||||
<th data-tip="Average number of people in crew (used for total personnel calculation)">Crew</th>
|
<th data-tip="Select allowed features">Features</th>
|
||||||
<th data-tip="Unit military power (used for battle simulation)">Power</th>
|
<th data-tip="Number of burgs in group">Count</th>
|
||||||
<th data-tip="Unit type to apply special rules on forces recalculation">Type</th>
|
<th data-tip="Activate/deactivate group">Active</th>
|
||||||
<th data-tip="Check if unit is separate and can be stacked only with units of the same type">
|
|
||||||
Separate
|
|
||||||
</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody></tbody>
|
<tbody id="burgGroupsBody"></tbody>
|
||||||
</table>
|
</table>
|
||||||
|
</form>
|
||||||
|
<div id="burgGroupsBottom" style="margin-top: .3em">
|
||||||
|
<button id="burgGroupsEditorAdd" data-tip="Create a new burgs group" class="icon-plus"></button>
|
||||||
|
<button id="burgGroupsEditStyle" data-tip="Open burgs Style setup menu" class="icon-brush"></button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -262,26 +262,26 @@ window.Burgs = (() => {
|
||||||
|
|
||||||
const getDefaultGroups = () => [
|
const getDefaultGroups = () => [
|
||||||
{name: "capitals", active: true, features: {capital: true}, preview: "watabou-city-generator"},
|
{name: "capitals", active: true, features: {capital: true}, preview: "watabou-city-generator"},
|
||||||
{name: "cities", active: true, percentile: 90, population: [5, Infinity], preview: "watabou-city-generator"},
|
{name: "cities", active: true, percentile: 90, min: 5, preview: "watabou-city-generator"},
|
||||||
{
|
{
|
||||||
name: "forts",
|
name: "forts",
|
||||||
active: true,
|
active: true,
|
||||||
features: {citadel: true, walls: false, plaza: false, port: false},
|
features: {citadel: true, walls: false, plaza: false, port: false},
|
||||||
population: [0, 1],
|
max: 1,
|
||||||
preview: null
|
preview: null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "monasteries",
|
name: "monasteries",
|
||||||
active: true,
|
active: true,
|
||||||
features: {temple: true, walls: false, plaza: false, port: false},
|
features: {temple: true, walls: false, plaza: false, port: false},
|
||||||
population: [0, 0.8],
|
max: 0.8,
|
||||||
preview: null
|
preview: null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "caravanserais",
|
name: "caravanserais",
|
||||||
active: true,
|
active: true,
|
||||||
features: {port: false, plaza: true},
|
features: {port: false, plaza: true},
|
||||||
population: [0, 0.8],
|
max: 0.8,
|
||||||
biomes: [1, 2, 3],
|
biomes: [1, 2, 3],
|
||||||
preview: null
|
preview: null
|
||||||
},
|
},
|
||||||
|
|
@ -289,14 +289,15 @@ window.Burgs = (() => {
|
||||||
name: "trading_posts",
|
name: "trading_posts",
|
||||||
active: true,
|
active: true,
|
||||||
features: {plaza: true},
|
features: {plaza: true},
|
||||||
population: [0, 0.8],
|
max: 0.8,
|
||||||
biomes: [5, 6, 7, 8, 9, 10, 11, 12],
|
biomes: [5, 6, 7, 8, 9, 10, 11, 12],
|
||||||
preview: null
|
preview: null
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "villages",
|
name: "villages",
|
||||||
active: true,
|
active: true,
|
||||||
population: [0.1, 2],
|
min: 0.1,
|
||||||
|
max: 2,
|
||||||
features: {walls: false},
|
features: {walls: false},
|
||||||
preview: "watabou-village-generator"
|
preview: "watabou-village-generator"
|
||||||
},
|
},
|
||||||
|
|
@ -304,7 +305,7 @@ window.Burgs = (() => {
|
||||||
name: "hamlets",
|
name: "hamlets",
|
||||||
active: true,
|
active: true,
|
||||||
features: {walls: false, plaza: false},
|
features: {walls: false, plaza: false},
|
||||||
population: [0, 0.1],
|
max: 0.1,
|
||||||
preview: "watabou-village-generator"
|
preview: "watabou-village-generator"
|
||||||
},
|
},
|
||||||
{name: "towns", active: true, isDefault: true, preview: "watabou-city-generator"}
|
{name: "towns", active: true, isDefault: true, preview: "watabou-city-generator"}
|
||||||
|
|
@ -314,9 +315,13 @@ window.Burgs = (() => {
|
||||||
for (const group of options.burgs.groups) {
|
for (const group of options.burgs.groups) {
|
||||||
if (!group.active) continue;
|
if (!group.active) continue;
|
||||||
|
|
||||||
if (group.population) {
|
if (group.min) {
|
||||||
const [min, max] = group.population;
|
const isFit = burg.population >= group.min;
|
||||||
const isFit = burg.population >= min && burg.population <= max;
|
if (!isFit) continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (group.max) {
|
||||||
|
const isFit = burg.population <= group.max;
|
||||||
if (!isFit) continue;
|
if (!isFit) continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ function editBurg(id) {
|
||||||
byId("burgName").on("input", changeName);
|
byId("burgName").on("input", changeName);
|
||||||
byId("burgNameReRandom").on("click", generateNameRandom);
|
byId("burgNameReRandom").on("click", generateNameRandom);
|
||||||
byId("burgGroup").on("change", changeGroup);
|
byId("burgGroup").on("change", changeGroup);
|
||||||
byId("burgGroupEdit").on("change", editBurgGroups);
|
byId("burgGroupConfigure").on("click", editBurgGroups);
|
||||||
byId("burgType").on("change", changeType);
|
byId("burgType").on("change", changeType);
|
||||||
byId("burgCulture").on("change", changeCulture);
|
byId("burgCulture").on("change", changeCulture);
|
||||||
byId("burgNameReCulture").on("click", generateNameCulture);
|
byId("burgNameReCulture").on("click", generateNameCulture);
|
||||||
|
|
|
||||||
|
|
@ -2,42 +2,57 @@
|
||||||
|
|
||||||
function editBurgGroups() {
|
function editBurgGroups() {
|
||||||
if (customization) return;
|
if (customization) return;
|
||||||
if (!layerIsOn("toggleBurgs")) toggleBurgs();
|
|
||||||
|
|
||||||
addLines();
|
addLines();
|
||||||
|
|
||||||
$("#burgGroupsEditor").dialog({
|
$("#burgGroupsEditor").dialog({
|
||||||
title: "Edit Burg groups",
|
title: "Configure Burg groups",
|
||||||
resizable: false,
|
resizable: false,
|
||||||
position: {my: "left top", at: "left+10 top+140", of: "#map"}
|
position: {my: "center", at: "center", of: "svg"},
|
||||||
|
buttons: {
|
||||||
|
Apply: () => byId("burgGroupsForm").requestSubmit(),
|
||||||
|
Cancel: function () {
|
||||||
|
$(this).dialog("close");
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (modules.editBurgGroups) return;
|
if (modules.editBurgGroups) return;
|
||||||
modules.editBurgGroups = true;
|
modules.editBurgGroups = true;
|
||||||
|
|
||||||
// add listeners
|
// add listeners
|
||||||
byId("burgGroupsEditorAdd").addEventListener("click", addGroup);
|
byId("burgGroupsForm").on("submit", submitForm);
|
||||||
byId("burgGroupsEditorBody").on("click", ev => {
|
byId("burgGroupsForm").on("change", validateForm);
|
||||||
|
byId("burgGroupsEditorAdd").on("click", addGroup);
|
||||||
|
byId("burgGroupsEditStyle").on("click", () => editStyle("burgIcons"));
|
||||||
|
byId("burgGroupsBody").on("click", ev => {
|
||||||
const group = ev.target.closest(".states")?.dataset.id;
|
const group = ev.target.closest(".states")?.dataset.id;
|
||||||
if (ev.target.classList.contains("editStyle")) editStyle("burgs", group);
|
if (ev.target.classList.contains("editStyle")) editStyle("burgs", group);
|
||||||
else if (ev.target.classList.contains("removeGroup")) removeGroup(group);
|
else if (ev.target.classList.contains("removeGroup")) removeGroup(group);
|
||||||
});
|
});
|
||||||
|
|
||||||
function addLines() {
|
function addLines() {
|
||||||
byId("burgGroupsEditorBody").innerHTML = "";
|
byId("burgGroupsBody").innerHTML = "";
|
||||||
|
|
||||||
const lines = Array.from(burgs.selectAll("g")._groups[0]).map(el => {
|
const lines = options.burgs.groups.map(group => {
|
||||||
const count = el.children.length;
|
const count = pack.burgs.filter(burg => !burg.removed && burg.group === group.name).length;
|
||||||
return /* html */ `<div data-id="${el.id}" class="states" style="display: flex; justify-content: space-between;">
|
// prettier-ignore
|
||||||
<span>${el.id} (${count})</span>
|
return /* html */ `<tr name="${group.name}">
|
||||||
<div style="width: auto; display: flex; gap: 0.4em;">
|
<td data-tip="Select group to be assigned if other groups are not passed"><input type="radio" name="isDefault" ${group.isDefault && "checked"}></td>
|
||||||
<span data-tip="Edit style" class="editStyle icon-brush pointer" style="font-size: smaller;"></span>
|
<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>
|
||||||
<span data-tip="Remove group" class="removeGroup icon-trash pointer"></span>
|
<td data-tip="Set min population constraint"><input type="number" name="min" min="0" step="any" value="${group.min || ''}" /></td>
|
||||||
</div>
|
<td data-tip="Set max population constraint"><input type="number" name="max" min="0" step="any" value="${group.max || ''}" /></td>
|
||||||
</div>`;
|
<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"><button name="biomes">${group.biomes ? "some" : "all"}</button></td>
|
||||||
|
<td data-tip="Select allowed states"><button name="states">${group.states ? "some" : "all"}</button></td>
|
||||||
|
<td data-tip="Select allowed cultures"><button name="cultures">${group.cultures ? "some" : "all"}</button></td>
|
||||||
|
<td data-tip="Select allowed religions"><button name="religions">${group.religions ? "some" : "all"}</button></td>
|
||||||
|
<td data-tip="Select allowed features" ><button name="features">${group.features ? "some" : "all"}</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>
|
||||||
|
</tr>`;
|
||||||
});
|
});
|
||||||
|
|
||||||
byId("burgGroupsEditorBody").innerHTML = lines.join("");
|
byId("burgGroupsBody").innerHTML = lines.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
const DEFAULT_GROUPS = ["roads", "trails", "seaburgs"];
|
const DEFAULT_GROUPS = ["roads", "trails", "seaburgs"];
|
||||||
|
|
@ -81,4 +96,49 @@ function editBurgGroups() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function validateForm(event) {
|
||||||
|
const form = event.target.form;
|
||||||
|
|
||||||
|
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();
|
||||||
|
});
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
function submitForm(event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
function parseInput(input) {
|
||||||
|
if (input.name === "name") return sanitizeId(input.value);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
const lines = Array.from(byId("burgGroupsBody").children);
|
||||||
|
options.burgs.groups = lines.map(line => {
|
||||||
|
const inputs = line.querySelectorAll("input");
|
||||||
|
const group = Array.from(inputs).reduce((obj, input) => {
|
||||||
|
const value = parseInput(input);
|
||||||
|
if (value !== null) obj[input.name] = value;
|
||||||
|
return obj;
|
||||||
|
}, {});
|
||||||
|
return group;
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#burgGroupsEditor").dialog("close");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -414,7 +414,7 @@ function overviewMilitary() {
|
||||||
|
|
||||||
function applyMilitaryOptions() {
|
function applyMilitaryOptions() {
|
||||||
const unitLines = Array.from(tableBody.querySelectorAll("tr"));
|
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");
|
if (new Set(names).size !== names.length) return tip("All units should have unique names", false, "error");
|
||||||
|
|
||||||
$("#militaryOptions").dialog("close");
|
$("#militaryOptions").dialog("close");
|
||||||
|
|
|
||||||
|
|
@ -56,3 +56,18 @@ JSON.isValid = str => {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ if (parseMapVersion(VERSION) !== VERSION) alert("versioning.js: Invalid format o
|
||||||
const patreon = "https://www.patreon.com/azgaar";
|
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.
|
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>
|
<ul>
|
||||||
<strong>Latest changes:</strong>
|
<strong>Latest changes:</strong>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue