feat(effort): dépense des points d'Effort sur un test (règles p.35)
Le bouton d'Effort du message de jet déduit désormais les points de la Réserve d'Effort de l'acteur (1 point = 1 relance), s'ajoute aux relances de Compétence au lieu de les remplacer, et affiche le pool restant. Le curseur est plafonné à min(caractéristique, effort disponible) et la section est masquée pour les acteurs sans Réserve d'Effort (PNJ, créatures, effort vide).
This commit is contained in:
@@ -627,6 +627,15 @@ ol.chat-log {
|
||||
&.visible { max-height: 15rem; }
|
||||
}
|
||||
|
||||
.reroll-from-effort {
|
||||
h4.flexcol { gap: 2px; }
|
||||
.effort-pool {
|
||||
font-size: @font-size-12;
|
||||
opacity: 0.8;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Dice results ────────────────────────────────────────────────
|
||||
ul.roll-results {
|
||||
list-style: none;
|
||||
|
||||
@@ -268,8 +268,10 @@ export default class CombatDialog extends HandlebarsApplicationMixin(foundry.app
|
||||
}
|
||||
|
||||
getMaxEffort() {
|
||||
if (this.actor.type === "character") return this.actor.system?.abilities?.[this.abilityKey]?.value || 0;
|
||||
return 0;
|
||||
if (this.actor.type !== "character") return 0;
|
||||
const ability = this.actor.system?.abilities?.[this.abilityKey]?.value || 0;
|
||||
const effort = this.actor.system?.attributes?.effort?.value || 0;
|
||||
return Math.min(ability, effort);
|
||||
}
|
||||
|
||||
getPoolBreakdown() {
|
||||
|
||||
@@ -288,7 +288,9 @@ export default class RollDialog extends HandlebarsApplicationMixin(foundry.appli
|
||||
|
||||
getMaxEffort() {
|
||||
const sel = this.#getAbility();
|
||||
return parseInt(sel?.value, 10) || 0;
|
||||
const ability = parseInt(sel?.value, 10) || 0;
|
||||
const effort = this.#actor?.system?.attributes?.effort?.value || 0;
|
||||
return Math.min(ability, effort);
|
||||
}
|
||||
|
||||
getTotems() {
|
||||
|
||||
+54
-21
@@ -415,45 +415,78 @@ export class VermineUtils {
|
||||
});
|
||||
}
|
||||
|
||||
// Add click event for granting rerolls
|
||||
// Add click event for spending Effort points as rerolls (règles p.35) :
|
||||
// 1 point d'Effort = 1 relance, ajouté aux relances de Compétence restantes.
|
||||
html.querySelectorAll("button.grant-reroll").forEach(el => {
|
||||
el.addEventListener('click', async (ev) => {
|
||||
const btn = ev.currentTarget;
|
||||
const rollMessage = btn.closest(".vermine-roll-message");
|
||||
const grantedRerollElement = html.querySelector('#granted-reroll');
|
||||
const allowedRerollElement = html.querySelector("#allowed_reroll");
|
||||
const requested = parseInt(grantedRerollElement?.innerText, 10) || 0;
|
||||
|
||||
if (grantedRerollElement && allowedRerollElement) {
|
||||
allowedRerollElement.innerText = grantedRerollElement.innerText;
|
||||
// Déduit les points d'Effort de l'acteur (max : ce qu'il lui reste).
|
||||
const actor = await VermineUtils.#resolveMessageActor(rollMessage);
|
||||
const effort = actor?.system?.attributes?.effort;
|
||||
let spent = 0;
|
||||
if (effort && requested > 0) {
|
||||
spent = Math.min(requested, parseInt(effort.value, 10) || 0);
|
||||
if (spent > 0) {
|
||||
const remaining = (parseInt(effort.value, 10) || 0) - spent;
|
||||
await actor.update({ 'system.attributes.effort.value': remaining });
|
||||
const poolEl = rollMessage?.querySelector('.effort-pool');
|
||||
if (poolEl) poolEl.innerText = `${remaining}/${effort.max || 0}`;
|
||||
}
|
||||
}
|
||||
|
||||
// Les relances d'Effort s'ajoutent aux relances de Compétence restantes.
|
||||
if (allowedRerollElement) {
|
||||
const current = parseInt(allowedRerollElement.innerText, 10) || 0;
|
||||
allowedRerollElement.innerText = current + spent;
|
||||
}
|
||||
const effortSection = btn.closest('.reroll-from-effort');
|
||||
if (effortSection) effortSection.style.display = "none";
|
||||
|
||||
const mesEl = btn.closest('[data-message-id]');
|
||||
const messageId = mesEl?.dataset?.messageId;
|
||||
|
||||
if (messageId) {
|
||||
btn.closest('.reroll-from-effort').style.display = "none";
|
||||
|
||||
const rollMessage = btn.closest(".vermine-roll-message");
|
||||
if (rollMessage) {
|
||||
const content = rollMessage.outerHTML;
|
||||
// GM applies the update directly ; players relay through the
|
||||
// system socket (Foundry v14 ChatMessage updates are GM-only).
|
||||
if (game.user?.isGM) {
|
||||
const message = await game.messages.get(messageId);
|
||||
await message.update({ content: content });
|
||||
} else {
|
||||
game.socket?.emit(`system.${game.system.id}`, {
|
||||
type: "reroll",
|
||||
messageId: messageId,
|
||||
requestId: foundry.utils.randomID(8),
|
||||
content: content
|
||||
});
|
||||
}
|
||||
const content = rollMessage.outerHTML;
|
||||
// GM applies the update directly ; players relay through the
|
||||
// system socket (Foundry v14 ChatMessage updates are GM-only).
|
||||
if (game.user?.isGM) {
|
||||
const message = await game.messages.get(messageId);
|
||||
await message.update({ content: content });
|
||||
} else {
|
||||
game.socket?.emit(`system.${game.system.id}`, {
|
||||
type: "reroll",
|
||||
messageId: messageId,
|
||||
requestId: foundry.utils.randomID(8),
|
||||
content: content
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Résout l'acteur à l'origine d'un message de jet depuis les attributs
|
||||
* data-actor-id / data-actor-uuid posés sur le message. Le uuid est
|
||||
* privilégié : il distingue les tokens non liés (PNJ mineurs) issus de la
|
||||
* même fiche, qui partagent le même id d'acteur monde.
|
||||
* @param {HTMLElement} rollMessage
|
||||
* @returns {Promise<Actor|null>}
|
||||
*/
|
||||
static async #resolveMessageActor(rollMessage) {
|
||||
if (!rollMessage) return null;
|
||||
const id = rollMessage.dataset.actorId ?? null;
|
||||
const uuid = rollMessage.dataset.actorUuid ?? null;
|
||||
if (!id && !uuid) return null;
|
||||
const { VermineExchange } = await import('./exchange.mjs');
|
||||
return VermineExchange.resolveDefender({ id, uuid });
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays dice rolls in 3D if available.
|
||||
* @param {Roll} roll - The roll to display
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="vermine-roll-message" data-free-successes="{{verdict.bonusSuccesses}}">
|
||||
<div class="vermine-roll-message" data-free-successes="{{verdict.bonusSuccesses}}" data-actor-id="{{param.actor.id}}" data-actor-uuid="{{param.actor.uuid}}">
|
||||
{{!-- Verdict --}}
|
||||
<div class="roll-verdict {{#if verdict.success}}success{{else}}failure{{/if}}">
|
||||
{{#if verdict.success}}
|
||||
@@ -226,9 +226,11 @@
|
||||
<div class="reroll-fromroll">
|
||||
<h4>{{localize "VERMINE.rerolls_possible"}} : <span id="allowed_reroll">{{param.Reroll}}</span></h4>
|
||||
<div class="reroll flexrow">
|
||||
{{#ifgt param.actor.system.attributes.effort.value 0}}
|
||||
<div class="reroll-from-effort flexrow">
|
||||
<h4 class="flexcol">
|
||||
<span>{{localize "VERMINE.effort"}}</span>
|
||||
<span class="effort-pool">{{param.actor.system.attributes.effort.value}}/{{param.actor.system.attributes.effort.max}}</span>
|
||||
</h4>
|
||||
<input type="range"
|
||||
min="0"
|
||||
@@ -240,6 +242,7 @@
|
||||
<span id="granted-reroll">0</span>
|
||||
</button>
|
||||
</div>
|
||||
{{/ifgt}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user