Fix apv2, WIP

This commit is contained in:
2026-06-06 10:21:24 +02:00
parent 6cec1da910
commit 9b77a0c552
130 changed files with 12850 additions and 2830 deletions
+1 -1
View File
@@ -251,7 +251,7 @@ VERMINE.skillCategories = {
}
}
VERMINE.sexes = { "male": "VERMINE.sexes.male", "female": "VERMINE.sexes.female" };
VERMINE.sexes = { "male": "SEXES.male", "female": "SEXES.female" };
VERMINE.totems = {
"human": "TOTEMS.human.name",
+327 -596
View File
@@ -1,596 +1,327 @@
import { VermineUtils } from "../roll.mjs";
/**
* Dialog for rolling dice in Vermine2047.
* Handles dice pool calculation, modifiers, and roll execution.
*/
export default class RollDialog extends Dialog {
/**
* Creates a new RollDialog instance.
* @param {Object} data - The data for the dialog
* @param {HTMLElement} html - The HTML content of the dialog
* @param {Object} options - The options for the dialog
* @param {Function} [close] - The callback function for closing the dialog
*/
constructor(data, html, options, close = undefined) {
const conf = {
title: "jet de dés",
content: html,
buttons: {
roll: {
icon: '<i class="fas fa-check"></i>',
label: "Lancer !",
callback: () => this._onRoll()
},
cancel: {
icon: '<i class="fas fa-times"></i>',
label: "Annuler",
callback: () => this.close()
}
},
close: close
};
super({ ...conf, ...data }, options);
// Store reference to close callback
this._closeCallback = close;
}
/**
* Creates a new RollDialog instance.
* @param {Object} [data] - The data for the dialog
* @param {string} [data.label] - Roll label
* @param {string} [data.rolltype] - Roll type
* @param {number} [data.NoD=1] - Number of dice
* @param {boolean} [data.Reroll=false] - Allow rerolls
* @param {string} [data.actorId] - Actor ID for the roll
* @returns {Promise<RollDialog|null>} The RollDialog instance or null if creation failed
*/
static async create(data = {
label: null,
rolltype: null,
NoD: 1,
Reroll: false,
actorId: game.user.character?.id ?? canvas.tokens.controlled[0]?.actor?.id
}) {
// Validate actorId
const actorId = data.actorId;
if (!actorId || typeof actorId !== 'string') {
ui.notifications.warn(game.i18n.localize('VERMINE.error_no_actor_selected'));
return null;
}
// Retrieve the actor data based on the actorId
data.actor = await game.actors.get(actorId);
if (!data.actor) {
ui.notifications.warn(game.i18n.localize('VERMINE.error_no_actor_selected'));
return null;
}
data.availableSpecialties = data.actor.items.filter(item => item.type === "specialty");
data.availableItems = data.actor.items.filter(item => item.type === "item");
data.config = CONFIG.VERMINE;
// Define options for the dialog
const options = {
classes: ["vermineDialog"],
width: "fit-content",
height: 'fit-content',
zIndex: 99999
};
// Render the HTML template for the dialog
const html = await renderTemplate('systems/vermine2047/templates/dialogs/roll-dialog.hbs', data);
// Return a new RollDialog instance with the provided data, HTML, and options
return new RollDialog(data, html, options);
}
/**
* Retrieves the default options for the RollDialog.
*/
static get defaultOptions() {
return foundry.utils.mergeObject(super.defaultOptions, {
focus: true,
classes: ["dialog vermine-roll"],
});
}
/**
* Retrieves the data for the dialog.
* @returns {Object} The context data for the dialog
*/
getData() {
// Get the context data from the superclass
const context = super.getData();
context.data = this.data;
context.config = CONFIG.VERMINE;
return context;
}
/**
* Prepares items for display.
* @returns {Array} Filtered list of items
*/
prepareItems() {
return this.data.actor.items.filter(it => it.type === "item");
}
/**
* Prepares specialties for display.
* @returns {Array} Filtered list of specialties
*/
prepareSpecialties() {
return this.data.actor.items.filter(it => it.type === "specialty");
}
/**
* Activates event listeners for the dialog.
* @param {HTMLElement} html - The HTML element of the dialog.
*/
async activateListeners(html) {
// Activate event listeners from the superclass
super.activateListeners(html);
// Initialize UI elements
this._html = html;
// Retrieve roll data and set up event listeners
await this.getRollData();
// Set up event listeners for all roll-related inputs
const rollInputs = html.find('[data-roll]');
for (const inp of rollInputs) {
inp.addEventListener('change', this._onRollInputChange.bind(this));
}
this.displaySpecialties();
const selectAbil = html.find('#ability')[0];
// Set the maximum value for self control based on ability value
html.find("#self_control")[0].max = selectAbil.value;
selectAbil.addEventListener('change', this._onChangeAbility.bind(this));
const selfControl = html.find('#self_control')[0];
// Add event listener for self control changes
selfControl.addEventListener('change', this._onChangeSelfControl.bind(this));
// Set up difficulty change listener
html.find('#difficulty')[0].addEventListener('change', this._onDifficultyChange.bind(this));
// Set up handicap change listener
html.find('#handicap')[0].addEventListener('change', this._onHandicapChange.bind(this));
// Set up totem checkbox listeners
html.find('#human-totem')[0]?.addEventListener('change', this._onTotemChange.bind(this));
html.find('#adapted-totem')[0]?.addEventListener('change', this._onTotemChange.bind(this));
// Initial update of all UI elements
this._updateUI();
};
/**
* Retrieves the roll data for the dialog.
* @param {Event} _ev - The event triggering the roll data retrieval (unused).
*/
getRollData(_ev) {
// Calculate and store the roll data
this.rollData = {
actor: this.data.actor,
NoD: this.getDicePool(),
Reroll: this.getReroll(),
difficulty: this.getDifficulty(),
handicap: this.getHandicap(),
rollType: this.getRollType(),
rollLabel: this.getLabel(),
totems: this.getTotems(),
self_control: this.getSelfControl(),
max_effort: this.getMaxEffort(),
keepTotem: this.getKeepTotem(),
skillCategory: this.getSkillCategory()
};
this.displaySpecialties();
this._updateUI();
};
/**
* Gets the selected skill category
* @returns {string|null} - The skill category
*/
getSkillCategory() {
const html = this.element[0];
const skillSelect = html.querySelector('#skill');
if (skillSelect && skillSelect.selectedIndex > 0) {
const selectedOption = skillSelect.options[skillSelect.selectedIndex];
return selectedOption.dataset.category || null;
}
return null;
}
/**
* Gets the selected skill level
* @returns {number|null} - The skill level
*/
getSkillLevel() {
const html = this.element[0];
const skillSelect = html.querySelector('#skill');
if (skillSelect && skillSelect.selectedIndex > 0) {
const selectedOption = skillSelect.options[skillSelect.selectedIndex];
return parseInt(selectedOption.value) || null;
}
return null;
}
/**
* Checks if a specialty is selected
* @returns {boolean} - True if a specialty is selected
*/
hasSpecialtySelected() {
const html = this.element[0];
const specialtyRadio = html.querySelector('input[name="usingSpecialization"]:checked');
return specialtyRadio && specialtyRadio.value !== 'aucune';
}
/**
* Handles changes to roll inputs and updates UI.
* @param {Event} ev - The change event.
*/
_onRollInputChange(ev) {
this.getRollData(ev);
}
/**
* Updates all UI elements based on current roll data
*/
_updateUI() {
if (!this._html) return;
const html = this._html[0];
// Update total dice pool display
const totalDice = this.getDicePool();
const totalEl = html.querySelector('#dice-pool-total');
if (totalEl) {
totalEl.textContent = `${totalDice}D`;
}
// Update bonus count
const bonusCount = this._calculateBonusCount();
const bonusEl = html.querySelector('#total-bonus');
if (bonusEl) {
bonusEl.textContent = bonusCount;
}
// Update difficulty display
const difficultyEl = html.querySelector('#current-difficulty');
const difficultySelect = html.querySelector('#difficulty');
if (difficultyEl && difficultySelect) {
const selectedIndex = difficultySelect.selectedIndex;
const diffValue = parseInt(difficultySelect.options[selectedIndex].value);
const diffLabel = difficultySelect.options[selectedIndex].text.split(' ')[0];
difficultyEl.textContent = `${diffLabel} (${diffValue})`;
}
// Update handicap display
const handicapEl = html.querySelector('#current-handicap');
const handicapSelect = html.querySelector('#handicap');
if (handicapEl && handicapSelect) {
const selectedIndex = handicapSelect.selectedIndex;
handicapEl.textContent = handicapSelect.options[selectedIndex].text;
}
// Update ability score display
const abilSelect = html.querySelector('#ability');
const abilScoreEl = html.querySelector('#abilityScoreValue');
if (abilSelect && abilScoreEl) {
const selectedIndex = abilSelect.selectedIndex;
if (selectedIndex > 0) {
abilScoreEl.textContent = abilSelect.options[selectedIndex].value;
} else {
abilScoreEl.textContent = '0';
}
}
// Update specialty display
const specialtyRadios = html.querySelectorAll('input[name="usingSpecialization"]:checked');
const currentSpecEl = html.querySelector('.current-specialty');
if (currentSpecEl && specialtyRadios.length > 0) {
const checkedRadio = specialtyRadios[0];
currentSpecEl.textContent = checkedRadio.value === 'aucune' ? game.i18n.localize('VERMINE.none') : checkedRadio.value;
}
}
/**
* Calculates the bonus count for display.
* @returns {number} Total bonus dice.
*/
_calculateBonusCount() {
let bonus = 0;
// Help bonus
if (this._html?.find('#helped')[0]?.checked) {
bonus += 1;
}
// Group bonus
const groupValue = parseInt(this._html?.find('#group')[0]?.value, 10) || 0;
bonus += groupValue;
// Self control bonus
const selfControlValue = parseInt(this._html?.find('#self_control')[0]?.value, 10) || 0;
bonus += selfControlValue;
// Tools bonus
const toolsChecked = this._html?.find('input[name="usingTools"]:checked')[0]?.value !== '0';
if (toolsChecked) {
bonus += 1;
}
// Totems bonus
if (this._html?.find('#human-totem')[0]?.checked) {
bonus += parseInt(this.data.actor?.system?.adaptation?.totems?.human?.value, 10) || 0;
}
if (this._html?.find('#adapted-totem')[0]?.checked) {
bonus += parseInt(this.data.actor?.system?.adaptation?.totems?.adapted?.value, 10) || 0;
}
// Specialty bonus
const specialtyChecked = this._html?.find('input[name="usingSpecialization"]:checked')[0]?.value !== 'aucune';
if (specialtyChecked) {
bonus += 1;
}
return bonus;
}
/**
* Handles difficulty change
* @param {Event} ev - The change event
*/
_onDifficultyChange(ev) {
this._updateUI();
}
/**
* Handles handicap change
* @param {Event} ev - The change event
*/
_onHandicapChange(ev) {
this._updateUI();
}
/**
* Handles totem checkbox change
* @param {Event} ev - The change event
*/
_onTotemChange(ev) {
this._updateUI();
}
/**
* Gets the selected totem to keep (for dual totem rolls)
* @returns {string|null} - The totem to keep ('human', 'adapted', or null)
*/
getKeepTotem() {
const keepTotemSelect = this._html?.find('#keep-totem-select')[0];
if (keepTotemSelect) {
return keepTotemSelect.value;
}
// Default to null (both totems used)
return null;
}
/**
* Handles the change in self control value.
* @param {Event} ev - The event triggering the change in self control value.
*/
_onChangeSelfControl(ev) {
const html = this.element[0];
const selfControlValueElement = html.querySelector('#self_control_value');
if (selfControlValueElement) {
selfControlValueElement.innerText = ev.currentTarget.value;
}
}
/**
* Retrieves the handicap value from the HTML element.
* @returns {number} The handicap value.
*/
getHandicap() {
const html = this.element[0];
const handicapValue = html.querySelector('#handicap')?.value ?? '1';
return parseInt(handicapValue, 10);
}
/**
* Gets the roll type (ability or skill).
* @returns {string} The roll type: 'skill' or 'ability'.
*/
getRollType() {
const html = this.element[0];
return html.querySelector('select#skill')?.value ? "skill" : "ability";
}
/**
* Gets the label for the roll.
* @returns {string} The roll label.
*/
getLabel() {
const html = this.element[0];
const rollType = this.getRollType();
if (rollType === "skill") {
const skillSelect = html.querySelector('select#skill');
const selectedIndex = skillSelect?.selectedIndex ?? 0;
return skillSelect?.options[selectedIndex]?.dataset?.label ?? "";
}
const abilitySelect = html.querySelector('select#ability');
const selectedIndex = abilitySelect?.selectedIndex ?? 0;
return abilitySelect?.options[selectedIndex]?.dataset?.label ?? "";
}
/**
* Displays specialties related to the selected skill.
*/
displaySpecialties() {
const specialties = this.element[0]?.querySelectorAll('[data-spec-skill]');
if (specialties) {
specialties.forEach(specEl => {
specEl.style.display = "inline";
});
}
}
/**
* Retrieves the self control value from the HTML element.
* @returns {number} The self control value.
*/
getSelfControl() {
const html = this.element[0];
const selfControlValue = html.querySelector('#self_control')?.value ?? '0';
return parseInt(selfControlValue, 10);
}
/**
* Retrieves the maximum effort value from the HTML element.
* @returns {number} The maximum effort value.
*/
getMaxEffort() {
const html = this.element[0];
const abilityValue = html.querySelector('#ability')?.value ?? '0';
return parseInt(abilityValue, 10);
}
/**
* Retrieves the selected totems from the HTML element.
* @returns {Object} An object containing the selected totems {human: boolean, adapted: boolean}.
*/
getTotems() {
const html = this.element[0];
return {
human: html.querySelector('#human-totem')?.checked ?? false,
adapted: html.querySelector('#adapted-totem')?.checked ?? false
};
}
/**
* Handles the change in ability value.
* @param {Event} ev - The event triggering the change in ability value.
*/
_onChangeAbility(ev) {
const html = this.element[0];
const abilitySelect = html.querySelector('#ability');
const selectedIndex = abilitySelect?.selectedIndex ?? 0;
const score = abilitySelect?.options[selectedIndex]?.value ?? '0';
const scoreElement = html.querySelector('#abilityScore');
if (scoreElement) {
scoreElement.value = score;
}
const selfControlElement = html.querySelector('#self_control');
if (selfControlElement) {
selfControlElement.max = score;
}
}
/**
* Retrieves the total dice pool based on various factors.
* @returns {number} The total dice pool value.
*/
getDicePool() {
// Retrieve the HTML element
const html = this.element[0];
// Safely get ability value
const abilitySelect = html.querySelector('#ability');
const abilValue = abilitySelect?.options[abilitySelect?.selectedIndex]?.value ?? 0;
// Safely get skill value and pool
const skillSelect = html.querySelector('#skill');
const skillOption = skillSelect?.options[skillSelect?.selectedIndex];
const skillValue = skillOption?.dataset?.pool ?? 0;
// Get the self control value
const selfControl = html.querySelector('#self_control')?.value ?? 0;
// Calculate bonuses based on certain conditions
const bonuses =
(html.querySelector('#usingSpecialization')?.checked ? 1 : 0) +
(html.querySelector('#helped')?.checked ? 1 : 0) +
(html.querySelector('#usingTools')?.checked ? 1 : 0);
// Calculate the total dice pool
const total = parseInt(abilValue, 10) + parseInt(selfControl, 10) + parseInt(skillValue, 10) + bonuses;
return total || 0;
}
/**
* Retrieves the reroll value based on selected skill.
* @returns {number} The reroll value.
*/
getReroll() {
const html = this.element[0];
const skillSelect = html.querySelector('#skill');
const selectedIndex = skillSelect?.selectedIndex ?? 0;
const rerollValue = skillSelect?.options[selectedIndex]?.dataset?.reroll ?? '0';
return parseInt(rerollValue, 10) || 0;
}
/**
* Retrieves the difficulty value based on selected option.
* @returns {number} The difficulty value.
*/
getDifficulty() {
const html = this.element[0];
const difficultySelect = html.querySelector('#difficulty');
const selectedIndex = difficultySelect?.selectedIndex ?? 0;
const diffValue = difficultySelect?.options[selectedIndex]?.value ?? '0';
return parseInt(diffValue, 10) || 0;
}
/**
* Performs a dice roll based on the roll data and handles self control checks.
* @returns {Promise<Roll|false>} A promise that resolves with the Roll result or false if cancelled.
*/
async _onRoll() {
// Check if self control is required for the roll
if (this.rollData.self_control > 0) {
// Check if the actor has enough self control
const currentSelfControl = this.rollData.actor?.system?.attributes?.self_control?.value ?? 0;
if (currentSelfControl < this.rollData.self_control) {
// Display a warning message if self control is insufficient
ui.notifications.warn(game.i18n.localize('VERMINE.error_not_enough_self_control'));
// Re-render the dialog
this.render(true);
return false; // Exit the function if self control is insufficient
}
}
const caracName = this.element[0]?.querySelector('[name="ability"]')?.value;
if (caracName === "0" || caracName === undefined) {
// Display a warning message if no ability selected
ui.notifications.warn(game.i18n.localize('VERMINE.error_select_ability'));
// Re-render the dialog
this.render(true);
return false; // Exit the function if no ability
}
// Deduct self control points if necessary
if (this.rollData.self_control > 0) {
const newSelfControl = this.rollData.actor.system.attributes.self_control.value - this.rollData.self_control;
// Update the actor's self control value
await this.rollData.actor.update({
"system.attributes.self_control.value": newSelfControl
});
}
// Perform the dice roll using VermineUtils
return VermineUtils.roll({
...this.rollData,
skillLevel: this.getSkillLevel(),
hasSpecialty: this.hasSpecialtySelected()
});
}
}
import { VermineUtils } from "../roll.mjs";
const { HandlebarsApplicationMixin } = foundry.applications.api;
export default class RollDialog extends HandlebarsApplicationMixin(foundry.applications.api.ApplicationV2) {
#actor;
get title() {
return game.i18n.localize("VERMINE.roll");
}
static DEFAULT_OPTIONS = {
classes: ["vermine-roll"],
tag: "form",
window: {
icon: "fas fa-dice-d10",
resizable: false
},
position: {
width: 520,
height: 600
},
actions: {
roll: RollDialog.#onRoll,
cancel: RollDialog.#onCancel
}
};
static PARTS = {
main: { template: "systems/vermine2047/templates/dialogs/roll-dialog.hbs" }
};
static async create(data = {}) {
const actorId = data.actorId ?? game.user.character?.id ?? canvas.tokens.controlled[0]?.actor?.id;
if (!actorId || typeof actorId !== "string") {
ui.notifications.warn(game.i18n.localize("VERMINE.error_no_actor_selected"));
return null;
}
const actor = await game.actors.get(actorId);
if (!actor) {
ui.notifications.warn(game.i18n.localize("VERMINE.error_no_actor_selected"));
return null;
}
return new RollDialog({ actor, label: data.label, rolltype: data.rolltype });
}
constructor(options = {}) {
super(options);
this.#actor = options.actor;
this.label = options.label ?? null;
this.rolltype = options.rolltype ?? null;
}
async _prepareContext() {
const actor = this.#actor;
return {
actor,
system: actor.system,
config: CONFIG.VERMINE,
label: this.label,
rollType: this.rolltype,
labelKey: this.label,
speakerId: actor.id,
ability: null,
help: false,
specialty: false,
availableSpecialties: actor.items.filter(i => i.type === "specialty"),
availableItems: actor.items.filter(i => i.type === "item")
};
}
async _onRender(context, options) {
this.element.dataset.actorId = this.#actor.id;
for (const inp of this.element.querySelectorAll("[data-roll]")) {
inp.addEventListener("change", this.#onInputChange.bind(this));
}
const ability = this.element.querySelector("#ability");
if (ability) {
ability.addEventListener("change", this.#onChangeAbility.bind(this));
const selfControl = this.element.querySelector("#self_control");
if (selfControl) selfControl.max = ability.value;
}
const selfControl = this.element.querySelector("#self_control");
if (selfControl) {
selfControl.addEventListener("change", this.#onChangeSelfControl.bind(this));
}
this.element.querySelector("#difficulty")?.addEventListener("change", () => this.#updateUI());
this.element.querySelector("#handicap")?.addEventListener("change", () => this.#updateUI());
this.element.querySelector("#human-totem")?.addEventListener("change", () => this.#updateUI());
this.element.querySelector("#adapted-totem")?.addEventListener("change", () => this.#updateUI());
this.#displaySpecialties();
this.#updateUI();
if (ability?.value !== "0") {
this.element.querySelector("#self_control")?.dispatchEvent(new Event("change"));
}
}
// ── Getters ──────────────────────────────────────────────────────────
get #el() { return this.element; }
#getAbility() { return this.#el.querySelector("#ability"); }
#getSkill() { return this.#el.querySelector("#skill"); }
#getDifficulty() { return this.#el.querySelector("#difficulty"); }
#getHandicap() { return this.#el.querySelector("#handicap"); }
#getSelfCtrl() { return this.#el.querySelector("#self_control"); }
getDicePool() {
const abil = this.#getAbility();
const abilVal = parseInt(abil?.options[abil?.selectedIndex]?.value, 10) || 0;
const skill = this.#getSkill();
const skillPool = parseInt(skill?.options[skill?.selectedIndex]?.dataset?.pool, 10) || 0;
const sc = parseInt(this.#getSelfCtrl()?.value, 10) || 0;
const specChecked = this.#el.querySelector("#usingSpecialization")?.checked;
const helped = this.#el.querySelector("#helped")?.checked;
const tools = this.#el.querySelector("input[name='usingTools']:checked")?.value !== "0";
const bonuses = (specChecked ? 1 : 0) + (helped ? 1 : 0) + (tools ? 1 : 0);
return (abilVal + sc + skillPool + bonuses) || 0;
}
getDifficultySelect() {
const sel = this.#getDifficulty();
const idx = sel?.selectedIndex ?? 0;
return parseInt(sel?.options[idx]?.value, 10) || 7;
}
getReroll() {
const sel = this.#getSkill();
const idx = sel?.selectedIndex ?? 0;
return parseInt(sel?.options[idx]?.dataset?.reroll, 10) || 0;
}
getHandicapSelect() {
const sel = this.#getHandicap();
return parseInt(sel?.value, 10) || 1;
}
getSkillCategory() {
const sel = this.#getSkill();
const idx = sel?.selectedIndex ?? 0;
return sel?.options[idx]?.dataset?.category ?? null;
}
getSkillLevel() {
const sel = this.#getSkill();
const idx = sel?.selectedIndex ?? 0;
const val = sel?.options[idx]?.value;
return val ? parseInt(val, 10) : null;
}
hasSpecialtySelected() {
const checked = this.#el.querySelector("input[name='usingSpecialization']:checked");
return checked && checked.value !== "aucune";
}
getRollType() {
const sel = this.#getSkill();
return sel?.value ? "skill" : "ability";
}
getLabel() {
const type = this.getRollType();
if (type === "skill") {
const sel = this.#getSkill();
const idx = sel?.selectedIndex ?? 0;
return sel?.options[idx]?.dataset?.label ?? "";
}
const sel = this.#getAbility();
const idx = sel?.selectedIndex ?? 0;
return sel?.options[idx]?.dataset?.label ?? "";
}
getSelfControl() {
return parseInt(this.#getSelfCtrl()?.value, 10) || 0;
}
getMaxEffort() {
const sel = this.#getAbility();
return parseInt(sel?.value, 10) || 0;
}
getTotems() {
return {
human: this.#el.querySelector("#human-totem")?.checked ?? false,
adapted: this.#el.querySelector("#adapted-totem")?.checked ?? false
};
}
getKeepTotem() {
return this.#el.querySelector("#keep-totem-select")?.value ?? null;
}
// ── UI ───────────────────────────────────────────────────────────────
#displaySpecialties() {
for (const el of this.#el.querySelectorAll("[data-spec-skill]")) {
el.style.display = "inline";
}
}
#calculateBonusCount() {
let b = 0;
if (this.#el.querySelector("#helped")?.checked) b += 1;
b += parseInt(this.#el.querySelector("#group")?.value, 10) || 0;
b += parseInt(this.#getSelfCtrl()?.value, 10) || 0;
const tools = this.#el.querySelector("input[name='usingTools']:checked");
if (tools && tools.value !== "0") b += 1;
const human = this.#el.querySelector("#human-totem");
if (human?.checked) b += parseInt(this.#actor?.system?.adaptation?.totems?.human?.value, 10) || 0;
const adapted = this.#el.querySelector("#adapted-totem");
if (adapted?.checked) b += parseInt(this.#actor?.system?.adaptation?.totems?.adapted?.value, 10) || 0;
if (this.hasSpecialtySelected()) b += 1;
return b;
}
#updateUI() {
const total = this.getDicePool();
const totalEl = this.#el.querySelector("#dice-pool-total");
if (totalEl) totalEl.textContent = `${total}D`;
const bonusEl = this.#el.querySelector("#total-bonus");
if (bonusEl) bonusEl.textContent = this.#calculateBonusCount();
const diffSel = this.#getDifficulty();
const diffEl = this.#el.querySelector("#current-difficulty");
if (diffEl && diffSel) {
const idx = diffSel.selectedIndex;
const val = diffSel.options[idx].value;
const lbl = diffSel.options[idx].text.split(" ")[0];
diffEl.textContent = `${lbl} (${val})`;
}
const handSel = this.#getHandicap();
const handEl = this.#el.querySelector("#current-handicap");
if (handEl && handSel) {
handEl.textContent = handSel.options[handSel.selectedIndex].text;
}
const abilSel = this.#getAbility();
const abilValEl = this.#el.querySelector("#abilityScoreValue");
if (abilSel && abilValEl) {
const idx = abilSel.selectedIndex;
abilValEl.textContent = idx > 0 ? abilSel.options[idx].value : "0";
}
const specChecked = this.#el.querySelector("input[name='usingSpecialization']:checked");
const specEl = this.#el.querySelector(".current-specialty");
if (specEl && specChecked) {
specEl.textContent = specChecked.value === "aucune"
? game.i18n.localize("VERMINE.none")
: specChecked.value;
}
}
// ── Event handlers ───────────────────────────────────────────────────
#onInputChange() {
this.#updateUI();
}
#onChangeAbility(ev) {
const sel = ev.currentTarget;
const score = sel.options[sel.selectedIndex]?.value ?? "0";
const scoreEl = this.#el.querySelector("#abilityScore");
if (scoreEl) scoreEl.value = score;
const sc = this.#getSelfCtrl();
if (sc) sc.max = score;
this.#updateUI();
}
#onChangeSelfControl(ev) {
const valEl = this.#el.querySelector("#self_control_value");
if (valEl) valEl.textContent = ev.currentTarget.value;
}
static async #onCancel(event, target) {
this.close();
}
static async #onRoll(event, target) {
const selfCtrl = this.getSelfControl();
if (selfCtrl > 0) {
const current = this.#actor?.system?.attributes?.self_control?.value ?? 0;
if (current < selfCtrl) {
ui.notifications.warn(game.i18n.localize("VERMINE.error_not_enough_self_control"));
return;
}
}
const abilityVal = this.#el.querySelector('[name="ability"]')?.value;
if (!abilityVal || abilityVal === "0") {
ui.notifications.warn(game.i18n.localize("VERMINE.error_select_ability"));
return;
}
if (selfCtrl > 0) {
const newVal = this.#actor.system.attributes.self_control.value - selfCtrl;
await this.#actor.update({ "system.attributes.self_control.value": newVal });
}
await VermineUtils.roll({
actor: this.#actor,
NoD: this.getDicePool(),
Reroll: this.getReroll(),
difficulty: this.getDifficultySelect(),
handicap: this.getHandicapSelect(),
rollLabel: this.getLabel(),
totems: this.getTotems(),
self_control: selfCtrl,
max_effort: this.getMaxEffort(),
keepTotem: this.getKeepTotem(),
skillCategory: this.getSkillCategory(),
skillLevel: this.getSkillLevel(),
hasSpecialty: this.hasSpecialtySelected()
});
this.close();
}
}
+2 -2
View File
@@ -291,7 +291,7 @@ export class VermineFight {
console.log(data);
// render template
let html = await renderTemplate(data._template, data);
let html = await foundry.applications.handlebars.renderTemplate(data._template, data);
let ui = new Dialog({
title: game.i18n.localize("VERMINE.FightTool"),
@@ -415,7 +415,7 @@ export class VermineCombat extends Combat {
}
}
export class VermineCombatTracker extends CombatTracker {
export class VermineCombatTracker extends foundry.applications.sidebar.tabs.CombatTracker {
get template() {
return "systems/vermine2047/templates/combat-tracker.hbs";
+2 -1
View File
@@ -4,7 +4,7 @@
* @return {Promise}
*/
export const preloadHandlebarsTemplates = async function () {
return loadTemplates([
return foundry.applications.handlebars.loadTemplates([
// Actor partials.
@@ -31,6 +31,7 @@ export const preloadHandlebarsTemplates = async function () {
// npc partials
"systems/vermine2047/templates/actor/npc/npc-combat.hbs",
"systems/vermine2047/templates/actor/parts/npc-skill-category.hbs",
// creature partials
"systems/vermine2047/templates/actor/creature/creature-combat.hbs",
+5 -5
View File
@@ -38,16 +38,16 @@ export const registerHooks = function () {
});
Hooks.on('renderChatMessage', async (message, html, data) => {
let rerollTitle = html[0].querySelector(".reroll-fromroll h4");
Hooks.on('renderChatMessageHTML', async (message, html, data) => {
let rerollTitle = html.querySelector(".reroll-fromroll h4");
if (rerollTitle) {
rerollTitle.addEventListener("click", () => { html[0].querySelector(".reroll").classList.toggle('visible') })
rerollTitle.addEventListener("click", () => { html.querySelector(".reroll").classList.toggle('visible') })
}
if (message.author?._id != game.user._id || !game.user.isGM) {
// désactiver les inputs pour les joueurs non-auteurs du message
html[0].querySelectorAll("input").forEach(inp => inp.disabled = true);
html.querySelectorAll("input").forEach(inp => inp.disabled = true);
//cacher le boutton reroll
html[0].querySelectorAll("div.reroll-from-effort").forEach(el => el.style.display = "none")
html.querySelectorAll("div.reroll-from-effort").forEach(el => el.style.display = "none")
return
}
await VermineUtils.chatListenners(html)
+1 -1
View File
@@ -468,7 +468,7 @@ export class VermineUtils {
* @returns {Promise<ChatMessage>} The created chat message
*/
static async diplayChatRoll(roll, param) {
const content = await renderTemplate("systems/vermine2047/templates/roll-message.hbs", { roll, param });
const content = await foundry.applications.handlebars.renderTemplate("systems/vermine2047/templates/roll-message.hbs", { roll, param });
const chatData = {
user: game.user?._id,
speaker: ChatMessage.getSpeaker(),
+1 -1
View File
@@ -48,7 +48,7 @@ class CreateActorDialog extends FormApplication {
});
}
}
class VermineTour extends Tour {
class VermineTour extends foundry.nue.Tour {
/** @override */
async _preStep() {
var _a2, _b, _c, _d, _e;