- Dialogue d'apprentissage personnage : 4 nouveaux types d'achat.
- Capacité de Totem : options filtrées (mode de jeu, niveau de
groupe, totem, non-apprises), difficulté = valeur d'Apprentissage,
limites 3×N1 / 1×N2-N3 / max 5, marque system.learned de l'item
(convention type character) et learned.ability du personnage.
- Élément d'Historique : difficulté 9, handicap 1 si astérisque
(background.asterisk), marque learned.background + item.
- Test de Réputation (réduire/augmenter) : réduire = difficulté
10 − score visé (−1 pt), augmenter = difficulté = réputation
actuelle (+1 par succès), bornes 0-10, marque learned.reputation.
- Évolution : option Dé de Totem Adapté ×2 quand Adapté > Humain
(1D remplacé par un dé adapté compté ×2 via VermineUtils.roll).
- Correction règles : les Dés d'Expérience sont dépensés même en cas
d'échec du jet d'apprentissage (décompte hors #applySuccess).
- Expérience de Groupe : nouveau dialogue d'apprentissage de Groupe
(niveau, réserve, capacité de totem), réserve d'expérience et phase
dans system.experience, boutons sur la fiche Groupe, reset des
limites par phase (level/reserve/ability).
- Phase d'expérience : filtre par groupe sélectionné, associe chaque
personnage à son premier groupe, reset des limites de tous les
nouveaux types d'apprentissage.
- Corrections : l'attaque de créature n'ajoute plus la Réaction du Rôle
(regles_creatures.txt), suppression de la case XP des en-têtes PJ
(legacy et AppV2).
- Divers : .eslintrc.js (règles inexistantes/obsolètes retirées),
libellé jaune de dialogue en couleur chitine, i18n FR/EN, chips
« appris » sur l'onglet expérience du personnage.
539 lines
21 KiB
JavaScript
539 lines
21 KiB
JavaScript
import { VermineUtils } from "../roll.mjs";
|
|
import { VermineExperience } from "../experience.mjs";
|
|
|
|
const { HandlebarsApplicationMixin } = foundry.applications.api;
|
|
|
|
/**
|
|
* Dialogue d'apprentissage (phase d'Expérience).
|
|
* Permet de dépenser des Dés d'Expérience pour tenter de développer une
|
|
* Compétence, une Spécialité, une Caractéristique, une Réserve ou un
|
|
* Dé d'Évolution. La progression est appliquée automatiquement en cas de
|
|
* réussite.
|
|
*/
|
|
export default class LearningDialog extends HandlebarsApplicationMixin(foundry.applications.api.ApplicationV2) {
|
|
|
|
#actor;
|
|
|
|
static DEFAULT_OPTIONS = {
|
|
classes: ["vermine-roll"],
|
|
tag: "form",
|
|
window: {
|
|
icon: "fas fa-graduation-cap",
|
|
resizable: false
|
|
},
|
|
position: {
|
|
width: 560,
|
|
height: 640
|
|
},
|
|
actions: {
|
|
roll: LearningDialog.#onRoll,
|
|
cancel: LearningDialog.#onCancel
|
|
}
|
|
};
|
|
|
|
static PARTS = {
|
|
main: { template: "systems/vermine2047/templates/dialogs/learning-dialog.hbs", scrollable: [""] }
|
|
};
|
|
|
|
static async create({ actorId }) {
|
|
const actor = await game.actors.get(actorId);
|
|
if (!actor) {
|
|
ui.notifications.warn(game.i18n.localize("VERMINE.error_no_actor_selected"));
|
|
return null;
|
|
}
|
|
return new LearningDialog({ actor });
|
|
}
|
|
|
|
constructor(options = {}) {
|
|
super(options);
|
|
this.#actor = options.actor;
|
|
}
|
|
|
|
get title() {
|
|
return game.i18n.localize("VERMINE.open_learning");
|
|
}
|
|
|
|
async _prepareContext() {
|
|
const actor = this.#actor;
|
|
const skills = Object.entries(CONFIG.VERMINE.skills).map(([key, cfg]) => ({
|
|
key,
|
|
name: game.i18n.localize("SKILLS." + key + ".name"),
|
|
category: cfg.category,
|
|
value: actor.system?.skills?.[key]?.value || 0,
|
|
target: Math.min(5, (actor.system?.skills?.[key]?.value || 0) + 1),
|
|
rarity: actor.system?.skills?.[key]?.rarity || 0,
|
|
isDomain: VermineExperience.isPreferredDomain(actor, key)
|
|
}));
|
|
const totem = actor.system?.identity?.totem || "";
|
|
const gameMode = this.#getGameMode();
|
|
const groupLevel = this.#getGroupLevel();
|
|
const totemAbilities = (actor.itemTypes?.ability || [])
|
|
.filter(i =>
|
|
i.system?.type === "character"
|
|
&& !i.system?.learned
|
|
&& i.system?.totem === totem
|
|
&& (i.system?.level?.value || 0) <= gameMode
|
|
&& groupLevel >= (i.system?.level?.value || 0)
|
|
)
|
|
.map(a => ({
|
|
id: a.id,
|
|
name: a.name,
|
|
level: a.system.level.value,
|
|
threshold: a.system.learn.threshold,
|
|
hindrance: a.system.learn.hindrance
|
|
}));
|
|
const backgrounds = (actor.itemTypes?.background || [])
|
|
.filter(i => !i.system?.learned)
|
|
.map(b => ({ id: b.id, name: b.name, asterisk: b.system?.asterisk || false }));
|
|
const human = actor.system?.adaptation?.totems?.human?.value || 0;
|
|
const adapted = actor.system?.adaptation?.totems?.adapted?.value || 0;
|
|
return {
|
|
actor,
|
|
system: actor.system,
|
|
config: CONFIG.VERMINE,
|
|
dice: actor.system?.experience?.dice || 0,
|
|
ageType: actor.system?.identity?.ageType || 2,
|
|
abilities: Object.entries(CONFIG.VERMINE.abilities).map(([key, label]) => ({
|
|
key,
|
|
label: game.i18n.localize(label),
|
|
value: actor.system?.abilities?.[key]?.value || 0
|
|
})),
|
|
reserves: [
|
|
{ key: "self_control", label: game.i18n.localize("VERMINE.self_control"), value: actor.system?.attributes?.self_control?.value || 0, max: actor.system?.attributes?.self_control?.max || 0 },
|
|
{ key: "effort", label: game.i18n.localize("VERMINE.effort"), value: actor.system?.attributes?.effort?.value || 0, max: actor.system?.attributes?.effort?.max || 0 }
|
|
],
|
|
specialties: actor.itemTypes?.specialty || [],
|
|
skills,
|
|
specialtyCount: (actor.itemTypes?.specialty || []).length,
|
|
totem,
|
|
gameMode,
|
|
groupLevel,
|
|
totemAbilities,
|
|
backgrounds,
|
|
reputation: actor.system?.attributes?.reputation?.value ?? 0,
|
|
human,
|
|
adapted,
|
|
canUseAdaptedDie: VermineExperience.canUseAdaptedDie(human, adapted)
|
|
};
|
|
}
|
|
|
|
#getGameMode() {
|
|
return parseInt(game.settings.get("vermine2047", "game-mode") || "1", 10) || 1;
|
|
}
|
|
|
|
#getGroupLevel() {
|
|
const groups = game.actors.filter(a => a.type === "group" && (a.system?.members || []).includes(this.#actor.id));
|
|
return Math.max(0, ...groups.map(g => g.system?.level?.value || 0));
|
|
}
|
|
|
|
#canUseAdaptedDie() {
|
|
const a = this.#actor.system?.adaptation?.totems;
|
|
return VermineExperience.canUseAdaptedDie(a?.human?.value || 0, a?.adapted?.value || 0);
|
|
}
|
|
|
|
#totemCapacityCounts() {
|
|
const items = (this.#actor.itemTypes?.ability || []).filter(i => i.system?.type === "character" && i.system?.learned);
|
|
const byLevel = { 1: 0, 2: 0, 3: 0 };
|
|
for (const i of items) {
|
|
const level = i.system?.level?.value || 0;
|
|
byLevel[level] = (byLevel[level] || 0) + 1;
|
|
}
|
|
return { total: items.length, byLevel };
|
|
}
|
|
|
|
async _onRender() {
|
|
this.element.dataset.actorId = this.#actor.id;
|
|
for (const inp of this.element.querySelectorAll("[data-roll]")) {
|
|
inp.addEventListener("change", () => this.#updateUI());
|
|
}
|
|
this.element.querySelector("#learning-dice")?.addEventListener("input", () => this.#updateUI());
|
|
this.#updateUI();
|
|
}
|
|
|
|
get #el() { return this.element; }
|
|
|
|
#getType() {
|
|
return this.#el.querySelector('input[name="learning-type"]:checked')?.value ?? "skill";
|
|
}
|
|
|
|
#getTargetKey() {
|
|
const type = this.#getType();
|
|
if (type === "characteristic") return this.#el.querySelector("#learning-ability")?.value ?? "";
|
|
if (type === "reserve") return this.#el.querySelector("#learning-reserve")?.value ?? "";
|
|
if (type === "totem") return this.#el.querySelector("#learning-totem")?.value ?? "";
|
|
if (type === "background") return this.#el.querySelector("#learning-background")?.value ?? "";
|
|
return this.#el.querySelector("#learning-target")?.value ?? "";
|
|
}
|
|
|
|
#getReputationDirection() {
|
|
return this.#el.querySelector("#learning-reputation-direction")?.value ?? "reduce";
|
|
}
|
|
|
|
#isAdaptedDieChecked() {
|
|
return Boolean(this.#el.querySelector("#learning-adapted-die")?.checked);
|
|
}
|
|
|
|
#getDiceSpent() {
|
|
return Math.max(1, parseInt(this.#el.querySelector("#learning-dice")?.value, 10) || 1);
|
|
}
|
|
|
|
#getSpecialtyName() {
|
|
return this.#el.querySelector("#specialty-name")?.value?.trim() || game.i18n.localize("ITEMS.new_specialty");
|
|
}
|
|
|
|
#compute() {
|
|
const actor = this.#actor;
|
|
const type = this.#getType();
|
|
const key = this.#getTargetKey();
|
|
let label = "";
|
|
let targetLevel = null;
|
|
|
|
if (type === "skill") {
|
|
const current = key ? actor.system?.skills?.[key]?.value || 0 : 0;
|
|
targetLevel = Math.min(5, current + 1);
|
|
const rarity = key ? actor.system?.skills?.[key]?.rarity || 0 : 0;
|
|
label = game.i18n.localize(VermineExperience.skillLevelLabelKey(targetLevel));
|
|
const diff = VermineExperience.skillLearningDifficulty(targetLevel);
|
|
const domain = key ? VermineExperience.isPreferredDomain(actor, key) : false;
|
|
return {
|
|
type,
|
|
key,
|
|
targetLevel,
|
|
difficulty: domain ? Math.max(3, diff - 2) : diff,
|
|
handicap: VermineExperience.skillLearningHandicap(targetLevel, rarity),
|
|
poolModifier: 0,
|
|
label,
|
|
targetLabel: key ? game.i18n.localize("SKILLS." + key + ".name") : ""
|
|
};
|
|
}
|
|
if (type === "specialty") {
|
|
const current = key ? actor.system?.skills?.[key]?.value || 0 : 0;
|
|
const domain = key ? VermineExperience.isPreferredDomain(actor, key) : false;
|
|
label = game.i18n.localize("VERMINE.specialty");
|
|
return {
|
|
type,
|
|
key,
|
|
targetLevel: current,
|
|
difficulty: VermineExperience.specialtyLearningDifficulty(current, domain),
|
|
handicap: VermineExperience.specialtyLearningHandicap(actor.itemTypes?.specialty?.length || 0),
|
|
poolModifier: 0,
|
|
label,
|
|
targetLabel: key ? game.i18n.localize("SKILLS." + key + ".name") : ""
|
|
};
|
|
}
|
|
if (type === "characteristic") {
|
|
const current = key ? actor.system?.abilities?.[key]?.value || 0 : 0;
|
|
label = game.i18n.localize("VERMINE.characteristic");
|
|
return {
|
|
type,
|
|
key,
|
|
targetLevel: current,
|
|
difficulty: VermineExperience.characteristicLearningDifficulty(),
|
|
handicap: current,
|
|
poolModifier: 0,
|
|
label,
|
|
targetLabel: key ? game.i18n.localize(CONFIG.VERMINE.abilities[key]) : ""
|
|
};
|
|
}
|
|
if (type === "reserve") {
|
|
const current = key ? actor.system?.attributes?.[key]?.value || 0 : 0;
|
|
label = game.i18n.localize("VERMINE.reserve");
|
|
return {
|
|
type,
|
|
key,
|
|
targetLevel: current,
|
|
difficulty: VermineExperience.reserveLearningDifficulty(current),
|
|
handicap: VermineExperience.ageHandicap(actor.system?.identity?.ageType || 2),
|
|
poolModifier: 0,
|
|
adaptedDie: false,
|
|
label,
|
|
targetLabel: key ? game.i18n.localize(key === "self_control" ? "VERMINE.self_control" : "VERMINE.effort") : ""
|
|
};
|
|
}
|
|
if (type === "totem") {
|
|
const item = actor.itemTypes?.ability?.find(i => i.id === key);
|
|
const data = item?.system;
|
|
label = game.i18n.localize("VERMINE.learn_totem_ability");
|
|
return {
|
|
type,
|
|
key,
|
|
targetLevel: data?.level?.value || 0,
|
|
difficulty: VermineExperience.totemAbilityLearningDifficulty(data),
|
|
handicap: VermineExperience.totemAbilityLearningHandicap(data),
|
|
poolModifier: 0,
|
|
adaptedDie: false,
|
|
label,
|
|
targetLabel: item?.name || ""
|
|
};
|
|
}
|
|
if (type === "background") {
|
|
const item = actor.itemTypes?.background?.find(i => i.id === key);
|
|
const data = item?.system;
|
|
label = game.i18n.localize("VERMINE.learn_background");
|
|
return {
|
|
type,
|
|
key,
|
|
targetLevel: 0,
|
|
difficulty: VermineExperience.backgroundLearningDifficulty(),
|
|
handicap: VermineExperience.backgroundLearningHandicap(data?.asterisk),
|
|
poolModifier: 0,
|
|
adaptedDie: false,
|
|
label,
|
|
targetLabel: item?.name || ""
|
|
};
|
|
}
|
|
if (type === "reputation") {
|
|
const current = actor.system?.attributes?.reputation?.value ?? 0;
|
|
const direction = this.#getReputationDirection();
|
|
if (direction === "increase") {
|
|
return {
|
|
type,
|
|
key: "increase",
|
|
targetLevel: current,
|
|
difficulty: VermineExperience.reputationIncreaseDifficulty(current),
|
|
handicap: 0,
|
|
poolModifier: 0,
|
|
adaptedDie: false,
|
|
label: game.i18n.localize("VERMINE.learn_reputation"),
|
|
targetLabel: game.i18n.localize("VERMINE.reputation_increase")
|
|
};
|
|
}
|
|
return {
|
|
type,
|
|
key: "reduce",
|
|
targetLevel: Math.max(0, current - 1),
|
|
difficulty: VermineExperience.reputationReduceDifficulty(current),
|
|
handicap: 0,
|
|
poolModifier: 0,
|
|
adaptedDie: false,
|
|
label: game.i18n.localize("VERMINE.learn_reputation"),
|
|
targetLabel: game.i18n.localize("VERMINE.reputation_reduce")
|
|
};
|
|
}
|
|
// evolution
|
|
const human = actor.system?.adaptation?.totems?.human?.value || 0;
|
|
const adapted = actor.system?.adaptation?.totems?.adapted?.value || 0;
|
|
label = game.i18n.localize("VERMINE.evolution_dice");
|
|
return {
|
|
type,
|
|
key,
|
|
targetLevel: actor.system?.experience?.evolution || 0,
|
|
difficulty: VermineExperience.evolutionDifficulty(actor.system?.identity?.ageType || 2),
|
|
handicap: actor.system?.experience?.evolution || 0,
|
|
poolModifier: VermineExperience.evolutionTotemModifier(human, adapted),
|
|
adaptedDie: this.#canUseAdaptedDie() && this.#isAdaptedDieChecked(),
|
|
label,
|
|
targetLabel: ""
|
|
};
|
|
}
|
|
|
|
#updateUI() {
|
|
const type = this.#getType();
|
|
const show = (id, visible) => {
|
|
const el = this.#el.querySelector(id);
|
|
if (el) el.style.display = visible ? "" : "none";
|
|
};
|
|
show("#learning-skill-section", type === "skill" || type === "specialty");
|
|
show("#learning-specialty-section", type === "specialty");
|
|
show("#learning-ability-section", type === "characteristic");
|
|
show("#learning-reserve-section", type === "reserve");
|
|
show("#learning-evolution-section", type === "evolution");
|
|
show("#learning-totem-section", type === "totem");
|
|
show("#learning-background-section", type === "background");
|
|
show("#learning-reputation-section", type === "reputation");
|
|
show("#learning-adapted-die-row", type === "evolution" && this.#canUseAdaptedDie());
|
|
|
|
// Pour une Spécialité, seule une compétence au moins Confirmée (≥2) est valable.
|
|
const skillSelect = this.#el.querySelector("#learning-target");
|
|
if (skillSelect) {
|
|
for (const opt of skillSelect.options) {
|
|
const value = parseInt(opt.dataset.value, 10) || 0;
|
|
opt.hidden = (type === "specialty") ? value < 2 : false;
|
|
}
|
|
}
|
|
|
|
const c = this.#compute();
|
|
const set = (sel, txt) => { const el = this.#el.querySelector(sel); if (el) el.textContent = txt; };
|
|
set("#diff-value", c.difficulty);
|
|
set("#handicap-value", c.handicap);
|
|
set("#required-value", 1 + c.handicap);
|
|
const pool = Math.max(0, this.#getDiceSpent() + c.poolModifier);
|
|
let poolText = `${pool}D${c.poolModifier ? ` (${c.poolModifier >= 0 ? "+" : ""}${c.poolModifier}D)` : ""}`;
|
|
if (c.adaptedDie) poolText += " — " + game.i18n.localize("VERMINE.adapted_die_x2");
|
|
set("#pool-value", poolText);
|
|
if (type === "reputation") {
|
|
const current = this.#actor.system?.attributes?.reputation?.value ?? 0;
|
|
const direction = this.#getReputationDirection();
|
|
set("#reputation-target", direction === "increase"
|
|
? `${current} → ${Math.min(10, current + 1)}+`
|
|
: `${current} → ${Math.max(0, current - 1)}`);
|
|
}
|
|
const poolInput = this.#el.querySelector("#learning-dice");
|
|
if (poolInput) poolInput.max = this.#actor.system?.experience?.dice || 0;
|
|
}
|
|
|
|
static async #onCancel() {
|
|
this.close();
|
|
}
|
|
|
|
static async #onRoll() {
|
|
const actor = this.#actor;
|
|
const ex = actor.system?.experience;
|
|
const dice = ex?.dice || 0;
|
|
const spent = Math.min(this.#getDiceSpent(), dice);
|
|
if (spent < 1) {
|
|
ui.notifications.warn(game.i18n.localize("VERMINE.error_no_experience_dice"));
|
|
return;
|
|
}
|
|
|
|
const c = this.#compute();
|
|
|
|
// Limite : une seule compétence, spécialité, réserve, caractéristique,
|
|
// un seul Dé d'Évolution, Capacité de Totem, Historique ou jet de
|
|
// Réputation par phase.
|
|
if (ex?.learned?.phase === ex?.phase) {
|
|
const flagMap = { skill: "skill", specialty: "specialty", reserve: "reserve", characteristic: "characteristic", evolution: "evolution", totem: "ability", background: "background", reputation: "reputation" };
|
|
if (ex.learned[flagMap[c.type]]) {
|
|
ui.notifications.warn(game.i18n.localize("VERMINE.error_learning_limit"));
|
|
return;
|
|
}
|
|
}
|
|
if (c.type === "skill" && c.targetLevel !== null && (actor.system?.skills?.[c.key]?.value || 0) >= 5) {
|
|
ui.notifications.warn(game.i18n.localize("VERMINE.error_skill_max"));
|
|
return;
|
|
}
|
|
if (c.type === "characteristic" && c.targetLevel !== null && (actor.system?.abilities?.[c.key]?.value || 0) >= 3) {
|
|
ui.notifications.warn(game.i18n.localize("VERMINE.error_characteristic_max"));
|
|
return;
|
|
}
|
|
if (c.type === "specialty" && c.targetLevel !== null && c.targetLevel < 2) {
|
|
ui.notifications.warn(game.i18n.localize("VERMINE.error_specialty_requirement"));
|
|
return;
|
|
}
|
|
if (c.type === "reserve" && c.targetLevel !== null && (actor.system?.attributes?.[c.key]?.max || 0) >= 10) {
|
|
ui.notifications.warn(game.i18n.localize("VERMINE.error_reserve_max"));
|
|
return;
|
|
}
|
|
if (c.type === "totem") {
|
|
const item = actor.itemTypes?.ability?.find(i => i.id === c.key);
|
|
if (!item || item.system?.learned) {
|
|
ui.notifications.warn(game.i18n.localize("VERMINE.no_totem_ability"));
|
|
return;
|
|
}
|
|
const level = item.system?.level?.value || 0;
|
|
if (level > this.#getGameMode()) {
|
|
ui.notifications.warn(game.i18n.localize("VERMINE.error_totem_mode"));
|
|
return;
|
|
}
|
|
if (item.system?.totem !== actor.system?.identity?.totem) {
|
|
ui.notifications.warn(game.i18n.localize("VERMINE.error_totem_totem"));
|
|
return;
|
|
}
|
|
if (this.#getGroupLevel() < level) {
|
|
ui.notifications.warn(game.i18n.localize("VERMINE.error_totem_group_level"));
|
|
return;
|
|
}
|
|
const counts = this.#totemCapacityCounts();
|
|
if (level === 1 && (counts.byLevel[1] || 0) >= 3) {
|
|
ui.notifications.warn(game.i18n.localize("VERMINE.error_totem_limit"));
|
|
return;
|
|
}
|
|
if (level >= 2 && (counts.byLevel[level] || 0) >= 1) {
|
|
ui.notifications.warn(game.i18n.localize("VERMINE.error_totem_limit"));
|
|
return;
|
|
}
|
|
if (counts.total >= 5) {
|
|
ui.notifications.warn(game.i18n.localize("VERMINE.error_totem_limit"));
|
|
return;
|
|
}
|
|
}
|
|
if (c.type === "background") {
|
|
const item = actor.itemTypes?.background?.find(i => i.id === c.key);
|
|
if (!item || item.system?.learned) {
|
|
ui.notifications.warn(game.i18n.localize("VERMINE.no_background"));
|
|
return;
|
|
}
|
|
}
|
|
if (c.type === "reputation") {
|
|
const current = actor.system?.attributes?.reputation?.value ?? 0;
|
|
if (c.key === "reduce" && current <= 0) {
|
|
ui.notifications.warn(game.i18n.localize("VERMINE.error_reputation_min"));
|
|
return;
|
|
}
|
|
if (c.key === "increase" && current >= 10) {
|
|
ui.notifications.warn(game.i18n.localize("VERMINE.error_reputation_max"));
|
|
return;
|
|
}
|
|
}
|
|
if (c.type === "evolution" && c.adaptedDie && !this.#canUseAdaptedDie()) {
|
|
ui.notifications.warn(game.i18n.localize("VERMINE.error_evolution_no_adapted"));
|
|
return;
|
|
}
|
|
|
|
const pool = Math.max(0, spent + c.poolModifier);
|
|
const rollLabel = `${game.i18n.localize("VERMINE.open_learning")} — ${c.label}${c.targetLabel ? " (" + c.targetLabel + ")" : ""}`;
|
|
|
|
const roll = await VermineUtils.roll({
|
|
actor,
|
|
NoD: pool,
|
|
Reroll: 0,
|
|
difficulty: c.difficulty,
|
|
handicap: c.handicap,
|
|
totems: c.adaptedDie ? { adapted: true } : { human: false, adapted: false },
|
|
rollLabel,
|
|
poolBreakdown: { ability: 0, skill: 0, selfControl: 0, specialty: 0, help: 0, tooling: 0, group: 0, total: pool },
|
|
messageFlags: { "vermine-learning": { type: c.type, key: c.key, spent, success: false } }
|
|
});
|
|
|
|
const required = 1 + c.handicap;
|
|
const specialtyName = this.#getSpecialtyName();
|
|
if ((roll._total ?? 0) >= required) {
|
|
await this.#applySuccess(c, specialtyName, roll._total ?? 0);
|
|
}
|
|
// Les Dés d'Expérience sont perdus que le jet soit réussi ou raté.
|
|
await actor.update({ "system.experience.dice": Math.max(0, (actor.system.experience.dice || 0) - spent) });
|
|
this.close();
|
|
}
|
|
|
|
async #applySuccess(c, specialtyName, successes = 0) {
|
|
const actor = this.#actor;
|
|
const updates = {};
|
|
if (c.type === "skill") {
|
|
const current = actor.system.skills[c.key].value || 0;
|
|
updates["system.skills." + c.key + ".value"] = Math.min(5, current + 1);
|
|
updates["system.experience.learned.skill"] = true;
|
|
} else if (c.type === "specialty") {
|
|
await actor.createEmbeddedDocuments("Item", [{ name: specialtyName, type: "specialty", system: { skill: c.key } }]);
|
|
updates["system.experience.learned.specialty"] = true;
|
|
} else if (c.type === "characteristic") {
|
|
const current = actor.system.abilities[c.key].value || 0;
|
|
updates["system.abilities." + c.key + ".value"] = Math.min(3, current + 1);
|
|
updates["system.experience.learned.characteristic"] = true;
|
|
} else if (c.type === "reserve") {
|
|
const bonus = actor.system.experience.reserveBonus?.[c.key] || 0;
|
|
updates["system.experience.reserveBonus." + c.key] = bonus + 1;
|
|
updates["system.experience.learned.reserve"] = true;
|
|
} else if (c.type === "evolution") {
|
|
updates["system.experience.evolution"] = (actor.system.experience.evolution || 0) + 1;
|
|
updates["system.experience.learned.evolution"] = true;
|
|
} else if (c.type === "totem") {
|
|
updates["system.experience.learned.ability"] = true;
|
|
await actor.updateEmbeddedDocuments("Item", [{ _id: c.key, "system.learned": true }]);
|
|
} else if (c.type === "background") {
|
|
updates["system.experience.learned.background"] = true;
|
|
await actor.updateEmbeddedDocuments("Item", [{ _id: c.key, "system.learned": true }]);
|
|
} else if (c.type === "reputation") {
|
|
const current = actor.system?.attributes?.reputation?.value ?? 0;
|
|
if (c.key === "increase") {
|
|
updates["system.attributes.reputation.value"] = Math.min(10, current + successes);
|
|
} else {
|
|
updates["system.attributes.reputation.value"] = Math.max(0, current - 1);
|
|
}
|
|
updates["system.experience.learned.reputation"] = true;
|
|
}
|
|
updates["system.experience.learned.phase"] = actor.system.experience.phase;
|
|
await actor.update(updates);
|
|
ui.notifications.info(game.i18n.localize("VERMINE.learning_success"));
|
|
}
|
|
}
|