mirror of
https://github.com/Azgaar/Fantasy-Map-Generator.git
synced 2026-03-23 07:37:24 +01:00
feat: add optional AI-based name generation for map entities
This commit is contained in:
parent
3f9a7702d4
commit
5b98f55bc7
20 changed files with 1393 additions and 7 deletions
|
|
@ -59,6 +59,7 @@ function insertEditorHtml() {
|
|||
</div>
|
||||
<button id="culturesEditNamesBase" data-tip="Edit a database used for names generation" class="icon-font"></button>
|
||||
<button id="culturesAdd" data-tip="Add a new culture. Hold Shift to add multiple" class="icon-plus"></button>
|
||||
<button id="culturesRegenerateNamesAi" data-tip="Regenerate culture names using AI" class="icon-robot"></button>
|
||||
<button id="culturesExport" data-tip="Download cultures-related data" class="icon-download"></button>
|
||||
<button id="culturesImport" data-tip="Upload cultures-related data" class="icon-upload"></button>
|
||||
<button id="culturesRecalculate" data-tip="Recalculate cultures based on current values of growth-related attributes" class="icon-retweet"></button>
|
||||
|
|
@ -87,6 +88,7 @@ function addListeners() {
|
|||
byId("culturesManuallyCancel").on("click", () => exitCulturesManualAssignment());
|
||||
byId("culturesEditNamesBase").on("click", editNamesbase);
|
||||
byId("culturesAdd").on("click", enterAddCulturesMode);
|
||||
byId("culturesRegenerateNamesAi").on("click", regenerateCultureNamesAi);
|
||||
byId("culturesExport").on("click", downloadCulturesCsv);
|
||||
byId("culturesImport").on("click", () => byId("culturesCSVToLoad").click());
|
||||
byId("culturesCSVToLoad").on("change", uploadCulturesData);
|
||||
|
|
@ -190,6 +192,7 @@ function culturesEditorAddLines() {
|
|||
<input data-tip="Culture name. Click and type to change" class="cultureName" style="width: 7em"
|
||||
value="${c.name}" autocorrect="off" spellcheck="false" />
|
||||
<span data-tip="Regenerate culture name" class="icon-cw hiddenIcon" style="visibility: hidden"></span>
|
||||
<span data-tip="Generate culture name with AI" class="icon-robot hiddenIcon" style="visibility: hidden"></span>
|
||||
<select data-tip="Culture type. Defines growth model. Click to change"
|
||||
class="cultureType">${getTypeOptions(c.type)}</select>
|
||||
<span data-tip="Click to re-generate names for burgs with this culture assigned" class="icon-arrows-cw hide"></span>
|
||||
|
|
@ -236,6 +239,7 @@ function culturesEditorAddLines() {
|
|||
$body.querySelectorAll("fill-box").forEach($el => $el.on("click", cultureChangeColor));
|
||||
$body.querySelectorAll("div > input.cultureName").forEach($el => $el.on("input", cultureChangeName));
|
||||
$body.querySelectorAll("div > span.icon-cw").forEach($el => $el.on("click", cultureRegenerateName));
|
||||
$body.querySelectorAll("div > span.icon-robot").forEach($el => $el.on("click", cultureRegenerateNameAi));
|
||||
$body.querySelectorAll("div > input.cultureExpan").forEach($el => $el.on("change", cultureChangeExpansionism));
|
||||
$body.querySelectorAll("div > select.cultureType").forEach($el => $el.on("change", cultureChangeType));
|
||||
$body.querySelectorAll("div > select.cultureBase").forEach($el => $el.on("change", cultureChangeBase));
|
||||
|
|
@ -354,6 +358,20 @@ function cultureRegenerateName() {
|
|||
pack.cultures[cultureId].name = name;
|
||||
}
|
||||
|
||||
async function cultureRegenerateNameAi() {
|
||||
const cultureId = +this.parentNode.dataset.id;
|
||||
const $line = this.parentNode;
|
||||
try {
|
||||
const name = await AiNames.generateName("culture", cultureId);
|
||||
$line.querySelector("input.cultureName").value = name;
|
||||
$line.dataset.name = name;
|
||||
pack.cultures[cultureId].name = name;
|
||||
pack.cultures[cultureId].code = abbreviate(name, pack.cultures.map(c => c.code));
|
||||
} catch (err) {
|
||||
if (err.message !== "No API key configured") tip("AI name generation failed: " + err.message, false, "error", 4000);
|
||||
}
|
||||
}
|
||||
|
||||
function cultureChangeExpansionism() {
|
||||
const culture = +this.parentNode.dataset.id;
|
||||
this.parentNode.dataset.expansionism = this.value;
|
||||
|
|
@ -850,6 +868,32 @@ function closeCulturesEditor() {
|
|||
exitAddCultureMode();
|
||||
}
|
||||
|
||||
async function regenerateCultureNamesAi() {
|
||||
const elements = Array.from($body.querySelectorAll(":scope > div"));
|
||||
const unlocked = elements.filter(el => {
|
||||
const id = +el.dataset.id;
|
||||
return id > 0 && !pack.cultures[id].lock;
|
||||
});
|
||||
if (!unlocked.length) return;
|
||||
|
||||
tip("Generating AI names...", false, "info");
|
||||
|
||||
try {
|
||||
for (const el of unlocked) {
|
||||
const cultureId = +el.dataset.id;
|
||||
const names = await AiNames.generateNames("culture", cultureId, 1);
|
||||
const name = names[0] || Names.getCulture(cultureId);
|
||||
el.querySelector("input.cultureName").value = name;
|
||||
el.dataset.name = name;
|
||||
pack.cultures[cultureId].name = name;
|
||||
pack.cultures[cultureId].code = abbreviate(name, pack.cultures.map(c => c.code));
|
||||
}
|
||||
tip("AI names generated successfully", true, "success", 3000);
|
||||
} catch (error) {
|
||||
tip(error.message, true, "error", 4000);
|
||||
}
|
||||
}
|
||||
|
||||
async function uploadCulturesData() {
|
||||
const file = this.files[0];
|
||||
this.value = "";
|
||||
|
|
|
|||
|
|
@ -74,6 +74,7 @@ function insertEditorHtml() {
|
|||
</div>
|
||||
<button id="religionsAdd" data-tip="Add a new religion. Hold Shift to add multiple" class="icon-plus"></button>
|
||||
<button id="religionsExport" data-tip="Download religions-related data" class="icon-download"></button>
|
||||
<button id="religionsRegenerateNamesAi" data-tip="Regenerate religion names using AI" class="icon-robot"></button>
|
||||
<button id="religionsRecalculate" data-tip="Recalculate religions based on current values of growth-related attributes" class="icon-retweet"></button>
|
||||
<span data-tip="Allow religion center, extent, and expansionism changes to take an immediate effect">
|
||||
<input id="religionsAutoChange" class="checkbox" type="checkbox" />
|
||||
|
|
@ -100,6 +101,7 @@ function addListeners() {
|
|||
byId("religionsManuallyCancel").on("click", () => exitReligionsManualAssignment());
|
||||
byId("religionsAdd").on("click", enterAddReligionMode);
|
||||
byId("religionsExport").on("click", downloadReligionsCsv);
|
||||
byId("religionsRegenerateNamesAi").on("click", regenerateReligionNamesAi);
|
||||
byId("religionsRecalculate").on("click", () => recalculateReligions(true));
|
||||
}
|
||||
|
||||
|
|
@ -196,12 +198,14 @@ function religionsEditorAddLines() {
|
|||
<fill-box fill="${r.color}"></fill-box>
|
||||
<input data-tip="Religion name. Click and type to change" class="religionName" style="width: 11em"
|
||||
value="${r.name}" autocorrect="off" spellcheck="false" />
|
||||
<span data-tip="Generate religion name with AI" class="icon-robot hiddenIcon" style="visibility: hidden"></span>
|
||||
<select data-tip="Religion type" class="religionType" style="width: 5em">
|
||||
${getTypeOptions(r.type)}
|
||||
</select>
|
||||
<input data-tip="Religion form" class="religionForm" style="width: 6em"
|
||||
value="${r.form}" autocorrect="off" spellcheck="false" />
|
||||
<span data-tip="Click to re-generate supreme deity" class="icon-arrows-cw hide"></span>
|
||||
<span data-tip="Generate deity name with AI" class="icon-robot-deity hiddenIcon hide" style="visibility: hidden"></span>
|
||||
<input data-tip="Religion supreme deity" class="religionDeity hide" style="width: 17em"
|
||||
value="${r.deity || ""}" autocorrect="off" spellcheck="false" />
|
||||
<span data-tip="Religion area" style="padding-right: 4px" class="icon-map-o hide"></span>
|
||||
|
|
@ -236,10 +240,12 @@ function religionsEditorAddLines() {
|
|||
});
|
||||
$body.querySelectorAll("fill-box").forEach(el => el.on("click", religionChangeColor));
|
||||
$body.querySelectorAll("div > input.religionName").forEach(el => el.on("input", religionChangeName));
|
||||
$body.querySelectorAll("div > span.icon-robot").forEach(el => el.on("click", religionRegenerateNameAi));
|
||||
$body.querySelectorAll("div > select.religionType").forEach(el => el.on("change", religionChangeType));
|
||||
$body.querySelectorAll("div > input.religionForm").forEach(el => el.on("input", religionChangeForm));
|
||||
$body.querySelectorAll("div > input.religionDeity").forEach(el => el.on("input", religionChangeDeity));
|
||||
$body.querySelectorAll("div > span.icon-arrows-cw").forEach(el => el.on("click", regenerateDeity));
|
||||
$body.querySelectorAll("div > span.icon-robot-deity").forEach(el => el.on("click", regenerateDeityAi));
|
||||
$body.querySelectorAll("div > div.religionPopulation").forEach(el => el.on("click", changePopulation));
|
||||
$body.querySelectorAll("div > select.religionExtent").forEach(el => el.on("change", religionChangeExtent));
|
||||
$body.querySelectorAll("div > input.religionExpantion").forEach(el => el.on("change", religionChangeExpansionism));
|
||||
|
|
@ -363,6 +369,21 @@ function religionChangeName() {
|
|||
);
|
||||
}
|
||||
|
||||
async function religionRegenerateNameAi() {
|
||||
const religionId = +this.parentNode.dataset.id;
|
||||
const $line = this.parentNode;
|
||||
const cultureId = pack.religions[religionId].culture;
|
||||
try {
|
||||
const name = await AiNames.generateName("religion", cultureId);
|
||||
$line.querySelector("input.religionName").value = name;
|
||||
$line.dataset.name = name;
|
||||
pack.religions[religionId].name = name;
|
||||
pack.religions[religionId].code = abbreviate(name, pack.religions.map(r => r.code));
|
||||
} catch (err) {
|
||||
if (err.message !== "No API key configured") tip("AI name generation failed: " + err.message, false, "error", 4000);
|
||||
}
|
||||
}
|
||||
|
||||
function religionChangeType() {
|
||||
const religionId = +this.parentNode.dataset.id;
|
||||
this.parentNode.dataset.type = this.value;
|
||||
|
|
@ -390,6 +411,23 @@ function regenerateDeity() {
|
|||
this.nextElementSibling.value = deity;
|
||||
}
|
||||
|
||||
async function regenerateDeityAi() {
|
||||
const religionId = +this.parentNode.dataset.id;
|
||||
const religion = pack.religions[religionId];
|
||||
const cultureId = religion.culture;
|
||||
try {
|
||||
const deity = await AiNames.generateName("deity", cultureId, {
|
||||
religionType: religion.type,
|
||||
religionForm: religion.form
|
||||
});
|
||||
this.parentNode.dataset.deity = deity;
|
||||
pack.religions[religionId].deity = deity;
|
||||
this.nextElementSibling.value = deity;
|
||||
} catch (err) {
|
||||
if (err.message !== "No API key configured") tip("AI deity generation failed: " + err.message, false, "error", 4000);
|
||||
}
|
||||
}
|
||||
|
||||
function changePopulation() {
|
||||
const religionId = +this.parentNode.dataset.id;
|
||||
const religion = pack.religions[religionId];
|
||||
|
|
@ -821,6 +859,42 @@ function closeReligionsEditor() {
|
|||
exitAddReligionMode();
|
||||
}
|
||||
|
||||
async function regenerateReligionNamesAi() {
|
||||
const elements = Array.from($body.querySelectorAll(":scope > div"));
|
||||
const unlocked = elements.filter(el => {
|
||||
const id = +el.dataset.id;
|
||||
return id > 0 && !pack.religions[id].lock;
|
||||
});
|
||||
if (!unlocked.length) return;
|
||||
|
||||
const byCulture = new Map();
|
||||
for (const el of unlocked) {
|
||||
const religionId = +el.dataset.id;
|
||||
const culture = pack.religions[religionId].culture;
|
||||
if (!byCulture.has(culture)) byCulture.set(culture, []);
|
||||
byCulture.get(culture).push({el, religionId});
|
||||
}
|
||||
|
||||
tip("Generating AI names...", false, "info");
|
||||
|
||||
try {
|
||||
for (const [culture, religions] of byCulture) {
|
||||
const names = await AiNames.generateNames("religion", culture, religions.length);
|
||||
for (let i = 0; i < religions.length; i++) {
|
||||
const name = names[i] || Religions.getRandomName(culture);
|
||||
const {el, religionId} = religions[i];
|
||||
el.querySelector("input.religionName").value = name;
|
||||
el.dataset.name = name;
|
||||
pack.religions[religionId].name = name;
|
||||
pack.religions[religionId].code = abbreviate(name, pack.religions.map(r => r.code));
|
||||
}
|
||||
}
|
||||
tip("AI names generated successfully", true, "success", 3000);
|
||||
} catch (error) {
|
||||
tip(error.message, true, "error", 4000);
|
||||
}
|
||||
}
|
||||
|
||||
function updateLockStatus() {
|
||||
if (customization) return;
|
||||
|
||||
|
|
|
|||
|
|
@ -78,6 +78,7 @@ function insertEditorHtml() {
|
|||
<button id="statesManuallyCancel" data-tip="Cancel assignment" class="icon-cancel"></button>
|
||||
</div>
|
||||
|
||||
<button id="statesRegenerateNamesAi" data-tip="Regenerate state names using AI" class="icon-robot"></button>
|
||||
<button id="statesAdd" data-tip="Add a new state. Hold Shift to add multiple" class="icon-plus"></button>
|
||||
<button id="statesMerge" data-tip="Merge several states into one" class="icon-layer-group"></button>
|
||||
<button id="statesExport" data-tip="Save state-related data as a text file (.csv)" class="icon-download"></button>
|
||||
|
|
@ -106,6 +107,7 @@ function addListeners() {
|
|||
byId("statesManuallyCancel").on("click", () => exitStatesManualAssignment(false));
|
||||
byId("statesAdd").on("click", enterAddStateMode);
|
||||
byId("statesMerge").on("click", openStateMergeDialog);
|
||||
byId("statesRegenerateNamesAi").on("click", regenerateStateNamesAi);
|
||||
byId("statesExport").on("click", downloadStatesCsv);
|
||||
|
||||
$body.on("click", event => {
|
||||
|
|
@ -400,9 +402,11 @@ function editStateName(state) {
|
|||
// add listeners
|
||||
byId("stateNameEditorShortCulture").on("click", regenerateShortNameCulture);
|
||||
byId("stateNameEditorShortRandom").on("click", regenerateShortNameRandom);
|
||||
byId("stateNameEditorShortAi").on("click", regenerateShortNameAi);
|
||||
byId("stateNameEditorAddForm").on("click", addCustomForm);
|
||||
byId("stateNameEditorCustomForm").on("change", addCustomForm);
|
||||
byId("stateNameEditorFullRegenerate").on("click", regenerateFullName);
|
||||
byId("stateNameEditorFullAi").on("click", regenerateFullNameAi);
|
||||
|
||||
function regenerateShortNameCulture() {
|
||||
const state = +stateNameEditor.dataset.state;
|
||||
|
|
@ -417,6 +421,16 @@ function editStateName(state) {
|
|||
byId("stateNameEditorShort").value = name;
|
||||
}
|
||||
|
||||
async function regenerateShortNameAi() {
|
||||
const state = +stateNameEditor.dataset.state;
|
||||
const culture = pack.states[state].culture;
|
||||
try {
|
||||
byId("stateNameEditorShort").value = await AiNames.generateName("state", culture);
|
||||
} catch (error) {
|
||||
tip(error.message, true, "error", 4000);
|
||||
}
|
||||
}
|
||||
|
||||
function addCustomForm() {
|
||||
const value = stateNameEditorCustomForm.value;
|
||||
const addModeActive = stateNameEditorCustomForm.style.display === "inline-block";
|
||||
|
|
@ -440,6 +454,19 @@ function editStateName(state) {
|
|||
}
|
||||
}
|
||||
|
||||
async function regenerateFullNameAi() {
|
||||
const state = +stateNameEditor.dataset.state;
|
||||
const culture = pack.states[state].culture;
|
||||
const short = byId("stateNameEditorShort").value;
|
||||
const form = byId("stateNameEditorSelectForm").value;
|
||||
try {
|
||||
const name = await AiNames.generateName("stateFullName", culture, {form: form || "State", stateName: short});
|
||||
byId("stateNameEditorFull").value = name;
|
||||
} catch (error) {
|
||||
tip(error.message, true, "error", 4000);
|
||||
}
|
||||
}
|
||||
|
||||
function applyNameChange(s) {
|
||||
const nameInput = byId("stateNameEditorShort");
|
||||
const formSelect = byId("stateNameEditorSelectForm");
|
||||
|
|
@ -1492,6 +1519,43 @@ function downloadStatesCsv() {
|
|||
downloadFile(csvData, name);
|
||||
}
|
||||
|
||||
async function regenerateStateNamesAi() {
|
||||
const elements = Array.from($body.querySelectorAll(":scope > div"));
|
||||
const unlocked = elements.filter(el => {
|
||||
const id = +el.dataset.id;
|
||||
return id > 0 && !pack.states[id].lock;
|
||||
});
|
||||
if (!unlocked.length) return;
|
||||
|
||||
const byCulture = new Map();
|
||||
for (const el of unlocked) {
|
||||
const stateId = +el.dataset.id;
|
||||
const culture = pack.states[stateId].culture;
|
||||
if (!byCulture.has(culture)) byCulture.set(culture, []);
|
||||
byCulture.get(culture).push({el, stateId});
|
||||
}
|
||||
|
||||
tip("Generating AI names...", false, "info");
|
||||
|
||||
try {
|
||||
for (const [culture, states] of byCulture) {
|
||||
const names = await AiNames.generateNames("state", culture, states.length);
|
||||
for (let i = 0; i < states.length; i++) {
|
||||
const name = names[i] || Names.getState(Names.getCultureShort(culture), culture);
|
||||
const {el, stateId} = states[i];
|
||||
const s = pack.states[stateId];
|
||||
s.name = el.dataset.name = name;
|
||||
el.querySelector("input.stateName").value = name;
|
||||
s.fullName = s.formName ? `${s.formName} of ${name}` : name;
|
||||
labels.select("[data-id='" + stateId + "']").text(s.fullName);
|
||||
}
|
||||
}
|
||||
tip("AI names generated successfully", true, "success", 3000);
|
||||
} catch (error) {
|
||||
tip(error.message, true, "error", 4000);
|
||||
}
|
||||
}
|
||||
|
||||
function closeStatesEditor() {
|
||||
if (customization === 2) exitStatesManualAssignment(true);
|
||||
if (customization === 3) exitAddStateMode();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue