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; }
|
&.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 ────────────────────────────────────────────────
|
// ── Dice results ────────────────────────────────────────────────
|
||||||
ul.roll-results {
|
ul.roll-results {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
|
|||||||
@@ -268,8 +268,10 @@ export default class CombatDialog extends HandlebarsApplicationMixin(foundry.app
|
|||||||
}
|
}
|
||||||
|
|
||||||
getMaxEffort() {
|
getMaxEffort() {
|
||||||
if (this.actor.type === "character") return this.actor.system?.abilities?.[this.abilityKey]?.value || 0;
|
if (this.actor.type !== "character") return 0;
|
||||||
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() {
|
getPoolBreakdown() {
|
||||||
|
|||||||
@@ -288,7 +288,9 @@ export default class RollDialog extends HandlebarsApplicationMixin(foundry.appli
|
|||||||
|
|
||||||
getMaxEffort() {
|
getMaxEffort() {
|
||||||
const sel = this.#getAbility();
|
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() {
|
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 => {
|
html.querySelectorAll("button.grant-reroll").forEach(el => {
|
||||||
el.addEventListener('click', async (ev) => {
|
el.addEventListener('click', async (ev) => {
|
||||||
const btn = ev.currentTarget;
|
const btn = ev.currentTarget;
|
||||||
|
const rollMessage = btn.closest(".vermine-roll-message");
|
||||||
const grantedRerollElement = html.querySelector('#granted-reroll');
|
const grantedRerollElement = html.querySelector('#granted-reroll');
|
||||||
const allowedRerollElement = html.querySelector("#allowed_reroll");
|
const allowedRerollElement = html.querySelector("#allowed_reroll");
|
||||||
|
const requested = parseInt(grantedRerollElement?.innerText, 10) || 0;
|
||||||
|
|
||||||
if (grantedRerollElement && allowedRerollElement) {
|
// Déduit les points d'Effort de l'acteur (max : ce qu'il lui reste).
|
||||||
allowedRerollElement.innerText = grantedRerollElement.innerText;
|
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 mesEl = btn.closest('[data-message-id]');
|
||||||
const messageId = mesEl?.dataset?.messageId;
|
const messageId = mesEl?.dataset?.messageId;
|
||||||
|
|
||||||
if (messageId) {
|
if (messageId) {
|
||||||
btn.closest('.reroll-from-effort').style.display = "none";
|
const content = rollMessage.outerHTML;
|
||||||
|
// GM applies the update directly ; players relay through the
|
||||||
const rollMessage = btn.closest(".vermine-roll-message");
|
// system socket (Foundry v14 ChatMessage updates are GM-only).
|
||||||
if (rollMessage) {
|
if (game.user?.isGM) {
|
||||||
const content = rollMessage.outerHTML;
|
const message = await game.messages.get(messageId);
|
||||||
// GM applies the update directly ; players relay through the
|
await message.update({ content: content });
|
||||||
// system socket (Foundry v14 ChatMessage updates are GM-only).
|
} else {
|
||||||
if (game.user?.isGM) {
|
game.socket?.emit(`system.${game.system.id}`, {
|
||||||
const message = await game.messages.get(messageId);
|
type: "reroll",
|
||||||
await message.update({ content: content });
|
messageId: messageId,
|
||||||
} else {
|
requestId: foundry.utils.randomID(8),
|
||||||
game.socket?.emit(`system.${game.system.id}`, {
|
content: content
|
||||||
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.
|
* Displays dice rolls in 3D if available.
|
||||||
* @param {Roll} roll - The roll to display
|
* @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 --}}
|
{{!-- Verdict --}}
|
||||||
<div class="roll-verdict {{#if verdict.success}}success{{else}}failure{{/if}}">
|
<div class="roll-verdict {{#if verdict.success}}success{{else}}failure{{/if}}">
|
||||||
{{#if verdict.success}}
|
{{#if verdict.success}}
|
||||||
@@ -226,9 +226,11 @@
|
|||||||
<div class="reroll-fromroll">
|
<div class="reroll-fromroll">
|
||||||
<h4>{{localize "VERMINE.rerolls_possible"}} : <span id="allowed_reroll">{{param.Reroll}}</span></h4>
|
<h4>{{localize "VERMINE.rerolls_possible"}} : <span id="allowed_reroll">{{param.Reroll}}</span></h4>
|
||||||
<div class="reroll flexrow">
|
<div class="reroll flexrow">
|
||||||
|
{{#ifgt param.actor.system.attributes.effort.value 0}}
|
||||||
<div class="reroll-from-effort flexrow">
|
<div class="reroll-from-effort flexrow">
|
||||||
<h4 class="flexcol">
|
<h4 class="flexcol">
|
||||||
<span>{{localize "VERMINE.effort"}}</span>
|
<span>{{localize "VERMINE.effort"}}</span>
|
||||||
|
<span class="effort-pool">{{param.actor.system.attributes.effort.value}}/{{param.actor.system.attributes.effort.max}}</span>
|
||||||
</h4>
|
</h4>
|
||||||
<input type="range"
|
<input type="range"
|
||||||
min="0"
|
min="0"
|
||||||
@@ -240,6 +242,7 @@
|
|||||||
<span id="granted-reroll">0</span>
|
<span id="granted-reroll">0</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
{{/ifgt}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user