Finalisation complète du système Vermine2047 pour FoundryVTT v14
Implémentations majeures: - Classe GroupLink pour synchronisation bidirectionnelle acteurs↔groupes - Configuration complète des totems, PNJ et créatures - Redesign du RollDialog avec interface compacte et sélecteurs - Bonus/malus par domaine de totem - Réussites automatiques et seuils auto basés sur niveau de maîtrise - Choix du totem à garder avec recalcul des réussites - Conversion tous templates chat cards en .hbs - Fiches PNJ et Créature avec sélecteurs pour tous les niveaux - Documentation technique (ARCHITECTURE.md) et utilisateur (GUIDE_UTILISATEUR.md) - Mise à jour system.json pour compatibilité v14 - Tous les TODOs du README.md complétés Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
@@ -110,13 +110,19 @@ export default class RollDialog extends 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();
|
||||
let rollInputs = html.find('[data-roll');
|
||||
|
||||
// Set up event listeners for all roll-related inputs
|
||||
let rollInputs = html.find('[data-roll]');
|
||||
for (let inp of rollInputs) {
|
||||
// Add event listener for roll input changes
|
||||
inp.addEventListener('change', await this.getRollData.bind(this))
|
||||
inp.addEventListener('change', this._onRollInputChange.bind(this));
|
||||
};
|
||||
|
||||
this.displaySpecialties();
|
||||
|
||||
let selectAbil = html.find('#ability')[0];
|
||||
@@ -126,6 +132,19 @@ export default class RollDialog extends Dialog {
|
||||
let 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();
|
||||
|
||||
};
|
||||
|
||||
@@ -134,7 +153,6 @@ export default class RollDialog extends Dialog {
|
||||
* @param {Event} ev - The event triggering the roll data retrieval.
|
||||
*/
|
||||
async getRollData(ev) {
|
||||
console.log(this)
|
||||
// Calculate and store the roll data
|
||||
this.rollData = {
|
||||
actor: this.data.actor,
|
||||
@@ -146,10 +164,200 @@ export default class RollDialog extends Dialog {
|
||||
rollLabel: this.getLabel(),
|
||||
totems: this.getTotems(),
|
||||
self_control: this.getSelfControl(),
|
||||
max_effort: this.getMaxEffort()
|
||||
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
|
||||
*/
|
||||
async _onRollInputChange(ev) {
|
||||
await this.getRollData();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) || 0;
|
||||
bonus += groupValue;
|
||||
|
||||
// Self control bonus
|
||||
const selfControlValue = parseInt(this._html.find('#self_control')[0]?.value) || 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) || 0;
|
||||
}
|
||||
if (this._html.find('#adapted-totem')[0]?.checked) {
|
||||
bonus += parseInt(this.data.actor.system.adaptation.totems.adapted.value) || 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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
@@ -300,7 +508,7 @@ export default class RollDialog extends Dialog {
|
||||
// Check if the actor has enough self control
|
||||
if (this.rollData.actor.system.attributes.self_control.value < this.rollData.self_control) {
|
||||
// Display a warning message if self control is insufficient
|
||||
ui.notifications.warn('vous navez pas assez de sang-froid');
|
||||
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
|
||||
@@ -308,9 +516,9 @@ export default class RollDialog extends Dialog {
|
||||
|
||||
}
|
||||
let caracName = this.element[0].querySelector('[name="ability"]')?.value
|
||||
if (caracName == "0") {
|
||||
if (caracName == "0" || caracName === undefined) {
|
||||
// Display a warning message if no ability selected
|
||||
ui.notifications.warn('selectionnez une caractéristique.');
|
||||
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
|
||||
@@ -318,10 +526,17 @@ export default class RollDialog extends Dialog {
|
||||
// Deduct self control points if necessary
|
||||
if (this.rollData.self_control > 0) {
|
||||
// Update the actor's self control value
|
||||
await this.rollData.actor.update({ "system.attributes.self_control.value": this.rollData.actor.system.attributes.self_control.value - this.rollData.self_control });
|
||||
await this.rollData.actor.update({
|
||||
"system.attributes.self_control.value":
|
||||
this.rollData.actor.system.attributes.self_control.value - this.rollData.self_control
|
||||
});
|
||||
}
|
||||
|
||||
// Perform the dice roll using VermineUtils
|
||||
return VermineUtils.roll({ ...this.rollData });
|
||||
return VermineUtils.roll({
|
||||
...this.rollData,
|
||||
skillLevel: this.getSkillLevel(),
|
||||
hasSpecialty: this.hasSpecialtySelected()
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user