dnd5-scripts/generate_npc/generate_npc.js
2026-06-17 22:32:48 +03:00

249 lines
8.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Dataviewjs не поддерживает импорты, кроме таких. Для обхода ограничения внутренние объекты прописываем в поля `dv`
await dv.view("scripts/henchmen/languages");
await dv.view("scripts/henchmen/norminv");
await dv.view("scripts/henchmen/races");
await dv.view("scripts/henchmen/abilities");
await dv.view("scripts/henchmen/skills");
await dv.view("scripts/henchmen/classes");
await dv.view("scripts/henchmen/backgrounds");
await dv.view("scripts/henchmen/ages");
await dv.view("scripts/henchmen/psychoTypes");
// Константы
const maxCount = 1000;
const degree = 0.7;
// Параметры фита нормальным распределением
const I_a = 201.7348602;
const II_a = 52.38963682;
const III_a = 17.94924525;
const IV_a = 7.481035692;
const V_a = 3.492919247;
const VI_a = 1.497346392;
let muAs = [I_a, II_a, III_a, IV_a, V_a, VI_a];
const I_b = -1.445350459;
const II_b = -1.411086938;
const III_b = -1.350400144;
const IV_b = -1.148846596;
const V_b = -1.215297703;
const VI_b = -1.421497636;
let muBs = [I_b, II_b, III_b, IV_b, V_b, VI_b];
const I_σ_a = 57.62483553;
const II_σ_a = 12.84292177;
const III_σ_a = 4.541993632;
const IV_σ_a = 1.906368905;
const V_σ_a = 1.667679638;
const VI_σ_a = 0.5420709982;
let muSigmaAs = [I_σ_a, II_σ_a, III_σ_a, IV_σ_a, V_σ_a, VI_σ_a];
const I_σ_b = -1.514703422;
const II_σ_b = -1.166486302;
const III_σ_b = -0.9321346977;
const IV_σ_b = -0.9380410427;
const V_σ_b = -0.6880944825;
const VI_σ_b = -0.3555551311;
let muSigmaBs = [I_σ_b, II_σ_b, III_σ_b, IV_σ_b, V_σ_b, VI_σ_b];
// Constans
let alignments = ["LG", "LN", "LE", "NG", "TN", "NE", "CG", "CN", "CE"];
function getRandomGender() {
return Math.random() < 0.68 ? "M" : "F";
}
function formatLanguages(languages) {
return languages
.map((lang) => {
let result = `${lang.name} [${lang.alphabet}`;
if (lang.speakers) {
result += `; ${lang.speakers}`;
}
result += "]";
return result;
})
.join(", ");
}
function recalc_generateHenchmen(maxCount, muA, muB, sigmaA, sigmaB, degree) {
// Функция для броска кубиков 3d6
// Переменная для хранения итогового текста
var resultText = "";
// Проход по уровням от 3 до 10
for (var level = 1; level < 8; level++) {
let mu = muA * Math.exp(muB * level ** degree);
let sigma = sigmaA * Math.exp(sigmaB * level ** degree);
var hench_count = recalc_countFromNormalDistribution(mu, sigma);
if (hench_count > 0) {
if (level !== "" && hench_count !== "") {
// Минимум между BN и maxCount
var minHenchCount = Math.min(hench_count, maxCount);
// Текст для текущего уровня
var levelText = "### Level " + level + " (" + minHenchCount + "):\n";
// Генерируем строки для персонажей (персонаж1, персонаж2, и т.д.)
for (var j = 1; j <= minHenchCount; j++) {
var alignmentIndex = Math.floor(Math.random() * alignments.length);
var randomAlignment = alignments[alignmentIndex];
let race = dv.races.selectRandomRaceAndSubrace(dv.races.races);
let raceString = "**";
// Проверка на совпадение расы и подрасы
if (race.race === race.subrace || !race.subrace) {
raceString += race.race; // Если совпадает, возвращаем только расу
} else {
raceString += `${race.subrace} ${race.race}`; // Если разные, возвращаем в формате "раса подраса"
}
raceString += "**";
let gender = getRandomGender();
let fullNameObject = dv.races.getName(race, gender);
let fullName = fullNameObject.name;
let knownLanguagesFromRace = dv.races.getLanguages(
race,
fullNameObject
);
raceString += " (" + formatLanguages(knownLanguagesFromRace) + ")";
var randomClass = dv.classes.getRandomClass();
let abilityScores = dv.abilities.getAbilityScores(randomClass);
const [ageCategory, ageExplanation, agedAbilityScores] = dv.ages.getAgeAndModifyAbilities(abilityScores);
let ageString = "**" + ageCategory + "** (" + ageExplanation + ")";
let background = dv.backgrounds.getBackground(randomClass);
let backgroundString =
background.name +
" (" +
background.skills.join(", ") +
", " +
background.tools.join(", ") +
", " +
formatLanguages(background.languages) +
")";
let chosenClassSkills = dv.backgrounds.getClassSkillsWithBackground(
randomClass,
background
);
let chosenClassSkillsString = chosenClassSkills.join(", ");
// Формируем текст с атрибутами
var attributes =
"Str " +
agedAbilityScores["Strength"] +
", Dex " +
agedAbilityScores["Dexterity"] +
", Con " +
agedAbilityScores["Constitution"] +
", Int " +
agedAbilityScores["Intelligence"] +
", Wis " +
agedAbilityScores["Wisdom"] +
", Cha " +
agedAbilityScores["Charisma"];
let psychoType = dv.psychoTypes.getPsychosocialTypeAndArchetype();
let psychoTypeString = "temperament: " + psychoType.temperament.name + ", " +
"psychosocial type: " + psychoType.psychosocialType.name + ", " +
"archetype by C.Jung: " + psychoType.archetype.name;
// Добавляем строку для персонажа с атрибутами
levelText +=
"#" +
j +
" - **" +
randomClass +
"** lvl " +
level +
"; " +
gender +
"; " +
fullName +
"; race: " +
raceString +
"; age: " +
ageString +
"; " +
attributes +
"; al " +
randomAlignment +
"; class skills: " +
chosenClassSkillsString +
"; background: " +
backgroundString +
"; " +
psychoTypeString +
".\n";
}
// Добавляем результат для текущего уровня в итоговый текст
resultText += levelText + "\n";
}
resultText += "---\n";
}
}
// Возвращаем итоговый текст
return resultText;
}
function recalc_generateNormalRandom(mu, sigma) {
return dv.norminv.generateNormalRandom(mu, sigma);
}
// =IF(μ >= 1, FLOOR(μ + σ * NORMINV(RAND(), 0, 1)), IF(RAND() <= μ, 1, 0))
function recalc_countFromNormalDistribution(mu, sigma) {
const rand = Math.random();
if (mu >= 1) {
return Math.floor(dv.norminv.generateNormalRandom(mu, sigma));
// return Math.floor(mu + sigma * norminv(rand));
} else {
return rand <= mu ? 1 : 0;
}
}
// Функция для отображения таблицы
function show_table(dv, marketLevel, basePath) {
/*// Отображаем таблицу с найденными локациями
dv.table(["Image", "Name", "Main Info"], getDescendantLocations(dv, targetLocations, basePath)); // Изменен порядок колонок*/
let curMarketMuA = muAs[marketLevel - 1];
let curMarketMuB = muBs[marketLevel - 1];
let curMarketSigmaA = muSigmaAs[marketLevel - 1];
let curMarketSigmaB = muSigmaBs[marketLevel - 1];
// Вызов функции с подстановкой значений
const result = recalc_generateHenchmen(
maxCount,
curMarketMuA,
curMarketMuB,
curMarketSigmaA,
curMarketSigmaB,
degree
);
dv.span(result);
}
// Функция для запуска отображения таблицы
function run_show_table(...args) {
let dv = args[0].arg1;
//let targetLocations = args[0].arg2;
let marketLevel = args[0].arg2;
let basePath = this.app.vault.adapter.basePath; // Получаем базовый путь к хранилищу
show_table(dv, marketLevel, basePath);
}
run_show_table(input);