AJout d'une aide pour les macros
This commit is contained in:
@@ -191,6 +191,11 @@ export class BoLRoll {
|
||||
return;
|
||||
}
|
||||
alchemy = foundry.utils.duplicate(alchemy)
|
||||
return this.alchemyCheckWithItem(actor, alchemy)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static alchemyCheckWithItem(actor, alchemy) {
|
||||
let alchemyData = alchemy.system
|
||||
if (alchemyData.properties.pccurrent < alchemyData.properties.pccost) {
|
||||
ui.notifications.warn("Pas assez de Points de Création investis dans la Préparation !")
|
||||
|
||||
@@ -1,52 +1,228 @@
|
||||
import {BoLRoll} from "../controllers/bol-rolls.js";
|
||||
import { BoLRoll } from "../controllers/bol-rolls.js";
|
||||
|
||||
/**
|
||||
* BoL Macro API — accessible via game.bol.macros
|
||||
*
|
||||
* Usage examples (in a Foundry macro):
|
||||
*
|
||||
* game.bol.macros.rollAttribute("vigor")
|
||||
* game.bol.macros.rollAttribute("mind")
|
||||
*
|
||||
* game.bol.macros.rollAptitude("melee")
|
||||
* game.bol.macros.rollAptitude("ranged")
|
||||
* game.bol.macros.rollAptitude("def")
|
||||
* game.bol.macros.rollAptitude("init")
|
||||
*
|
||||
* game.bol.macros.rollWeapon("Épée courte") // by name (partial match)
|
||||
* game.bol.macros.rollWeapon(0) // by index (first weapon)
|
||||
*
|
||||
* game.bol.macros.rollSpell("Boule de feu") // by name (partial match)
|
||||
* game.bol.macros.rollSpell(0) // by index
|
||||
*
|
||||
* game.bol.macros.rollAlchemy("Potion de soin") // by name (partial match)
|
||||
* game.bol.macros.rollAlchemy(0) // by index
|
||||
*
|
||||
* game.bol.macros.rollHoroscope("minor")
|
||||
* game.bol.macros.rollHoroscope("major")
|
||||
*
|
||||
* // Generic dispatcher:
|
||||
* game.bol.macros.roll("attribute", "vigor")
|
||||
* game.bol.macros.roll("aptitude", "melee")
|
||||
* game.bol.macros.roll("weapon", "Épée courte")
|
||||
* game.bol.macros.roll("spell", "Boule de feu")
|
||||
* game.bol.macros.roll("alchemy", 0)
|
||||
* game.bol.macros.roll("horoscope", "minor")
|
||||
*/
|
||||
export class Macros {
|
||||
/**
|
||||
* @name getSpeakersActor
|
||||
* @description
|
||||
*
|
||||
* @returns
|
||||
*/
|
||||
static getSpeakersActor = function(){
|
||||
// Vérifie qu'un seul token est sélectionné
|
||||
const tokens = canvas.tokens.controlled;
|
||||
if (tokens.length > 1) {
|
||||
ui.notifications.warn(game.i18n.localize('BOL.notification.MacroMultipleTokensSelected'));
|
||||
return null;
|
||||
}
|
||||
|
||||
const speaker = ChatMessage.getSpeaker();
|
||||
let actor;
|
||||
// Si un token est sélectionné, le prendre comme acteur cible
|
||||
if (speaker.token) actor = game.actors.tokens[speaker.token];
|
||||
// Sinon prendre l'acteur par défaut pour l'utilisateur courrant
|
||||
if (!actor) actor = game.actors.get(speaker.actor);
|
||||
return actor;
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/**
|
||||
* Resolves the actor for macro use:
|
||||
* - If multiple tokens are selected → error (always)
|
||||
* - If exactly one token is selected → use it (GM or player)
|
||||
* - If no token selected and user is GM → error (GM must select a token)
|
||||
* - If no token selected and user is a player → use their assigned character
|
||||
* @returns {Actor|null}
|
||||
*/
|
||||
static getSpeakersActor() {
|
||||
const tokens = canvas.tokens?.controlled ?? []
|
||||
|
||||
if (tokens.length > 1) {
|
||||
ui.notifications.warn(game.i18n.localize('BOL.notification.MacroMultipleTokensSelected'))
|
||||
return null
|
||||
}
|
||||
|
||||
static rollMacro = async function (rollType, key, adv, mod){
|
||||
const actor = this.getSpeakersActor();
|
||||
// Several tokens selected
|
||||
if (actor === null) return;
|
||||
// No token selected
|
||||
if (actor === undefined) return ui.notifications.error(game.i18n.localize("BOL.notification.MacroNoTokenSelected"));
|
||||
|
||||
const actorData = {};
|
||||
actorData.data = {
|
||||
features : actor.buildFeatures()
|
||||
};
|
||||
|
||||
if(rollType === "attribute") {
|
||||
let attribute = eval(`actor.system.attributes.${key}`);
|
||||
let rollLabel = (attribute.label) ? game.i18n.localize(attribute.label) : null;
|
||||
let description = actor.name + " - " + game.i18n.localize('BOL.ui.attributeCheck') + " - " + game.i18n.localize(attribute.label) ;
|
||||
BoLRoll.attributeRollDialog(actor, actorData, attribute, rollLabel, description, adv, mod);
|
||||
}
|
||||
else if(rollType === "aptitude") {
|
||||
let aptitude = eval(`actor.system.aptitudes.${key}`);
|
||||
let rollLabel = (aptitude.label) ? game.i18n.localize(aptitude.label) : null;
|
||||
let description = actor.name + " - " + game.i18n.localize('BOL.ui.aptitudeCheck') + " - " + game.i18n.localize(aptitude.label) ;
|
||||
BoLRoll.aptitudeRollDialog(actor, actorData, aptitude, rollLabel, description, adv, mod);
|
||||
}
|
||||
if (tokens.length === 1) {
|
||||
return tokens[0].actor ?? null
|
||||
}
|
||||
|
||||
// No token selected
|
||||
if (game.user.isGM) {
|
||||
ui.notifications.error(game.i18n.localize("BOL.notification.MacroNoTokenSelected"))
|
||||
return null
|
||||
}
|
||||
|
||||
// Player: fall back to their assigned character
|
||||
const actor = game.user.character
|
||||
if (!actor) {
|
||||
ui.notifications.error(game.i18n.localize("BOL.notification.MacroNoTokenSelected"))
|
||||
return null
|
||||
}
|
||||
return actor
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/**
|
||||
* Finds an item on an actor by name (partial, case-insensitive) or index.
|
||||
* @param {Actor} actor
|
||||
* @param {string} type - item type: "weapon", "spell", "alchemy"
|
||||
* @param {string|number} nameOrIndex
|
||||
* @returns {object|undefined}
|
||||
*/
|
||||
static _findItem(actor, type, nameOrIndex) {
|
||||
const items = actor.items.filter(i => i.type === type)
|
||||
if (items.length === 0) {
|
||||
ui.notifications.warn(`${actor.name} : aucun(e) ${type} trouvé(e).`)
|
||||
return undefined
|
||||
}
|
||||
if (nameOrIndex === undefined || nameOrIndex === null) {
|
||||
if (items.length === 1) return foundry.utils.duplicate(items[0])
|
||||
const names = items.map((it, i) => `[${i}] ${it.name}`).join(', ')
|
||||
ui.notifications.warn(`Précisez le nom ou l'index : ${names}`)
|
||||
return undefined
|
||||
}
|
||||
if (typeof nameOrIndex === "number") {
|
||||
const item = items[nameOrIndex]
|
||||
if (!item) {
|
||||
ui.notifications.warn(`${actor.name} : index ${nameOrIndex} invalide pour ${type}.`)
|
||||
return undefined
|
||||
}
|
||||
return foundry.utils.duplicate(item)
|
||||
}
|
||||
const lower = String(nameOrIndex).toLowerCase()
|
||||
const found = items.find(i => i.name.toLowerCase().includes(lower))
|
||||
if (!found) {
|
||||
ui.notifications.warn(`${actor.name} : ${type} "${nameOrIndex}" introuvable.`)
|
||||
return undefined
|
||||
}
|
||||
return foundry.utils.duplicate(found)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/**
|
||||
* Roll an attribute check.
|
||||
* @param {string} key - "vigor" | "agility" | "mind" | "appeal"
|
||||
* @param {Actor} [actor] - optional, defaults to selected/owned token
|
||||
*/
|
||||
static rollAttribute(key = "vigor", actor = undefined) {
|
||||
actor = actor ?? this.getSpeakersActor()
|
||||
if (!actor) return
|
||||
if (!actor.system.attributes[key]) {
|
||||
ui.notifications.warn(`Attribut inconnu : "${key}". Valeurs : vigor, agility, mind, appeal`)
|
||||
return
|
||||
}
|
||||
return BoLRoll.attributeCheck(actor, key)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/**
|
||||
* Roll an aptitude check.
|
||||
* @param {string} key - "init" | "melee" | "ranged" | "def"
|
||||
* @param {Actor} [actor] - optional, defaults to selected/owned token
|
||||
*/
|
||||
static rollAptitude(key = "melee", actor = undefined) {
|
||||
actor = actor ?? this.getSpeakersActor()
|
||||
if (!actor) return
|
||||
if (!actor.system.aptitudes[key]) {
|
||||
ui.notifications.warn(`Aptitude inconnue : "${key}". Valeurs : init, melee, ranged, def`)
|
||||
return
|
||||
}
|
||||
return BoLRoll.aptitudeCheck(actor, key)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/**
|
||||
* Roll a weapon attack.
|
||||
* @param {string|number} [nameOrIndex] - weapon name (partial) or index. Defaults to first weapon if only one.
|
||||
* @param {Actor} [actor] - optional, defaults to selected/owned token
|
||||
*/
|
||||
static rollWeapon(nameOrIndex = undefined, actor = undefined) {
|
||||
actor = actor ?? this.getSpeakersActor()
|
||||
if (!actor) return
|
||||
const weapon = this._findItem(actor, "weapon", nameOrIndex)
|
||||
if (!weapon) return
|
||||
return BoLRoll.weaponCheckWithWeapon(actor, weapon)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/**
|
||||
* Roll a spell check.
|
||||
* @param {string|number} [nameOrIndex] - spell name (partial) or index
|
||||
* @param {Actor} [actor] - optional, defaults to selected/owned token
|
||||
*/
|
||||
static rollSpell(nameOrIndex = undefined, actor = undefined) {
|
||||
actor = actor ?? this.getSpeakersActor()
|
||||
if (!actor) return
|
||||
if ((actor.system.resources.power?.value ?? 1) <= 0) {
|
||||
ui.notifications.warn("Plus assez de points de Pouvoir !")
|
||||
return
|
||||
}
|
||||
const spell = this._findItem(actor, "spell", nameOrIndex)
|
||||
if (!spell) return
|
||||
return BoLRoll.spellCheckWithSpell(actor, spell)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/**
|
||||
* Roll an alchemy check.
|
||||
* @param {string|number} [nameOrIndex] - alchemy item name (partial) or index
|
||||
* @param {Actor} [actor] - optional, defaults to selected/owned token
|
||||
*/
|
||||
static rollAlchemy(nameOrIndex = undefined, actor = undefined) {
|
||||
actor = actor ?? this.getSpeakersActor()
|
||||
if (!actor) return
|
||||
const alchemy = this._findItem(actor, "alchemy", nameOrIndex)
|
||||
if (!alchemy) return
|
||||
return BoLRoll.alchemyCheckWithItem(actor, alchemy)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/**
|
||||
* Roll a horoscope check.
|
||||
* @param {"minor"|"major"} [type="minor"]
|
||||
* @param {Actor} [actor] - optional, defaults to selected/owned token
|
||||
*/
|
||||
static rollHoroscope(type = "minor", actor = undefined) {
|
||||
actor = actor ?? this.getSpeakersActor()
|
||||
if (!actor) return
|
||||
return BoLRoll.horoscopeCheck(actor, undefined, type)
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
/**
|
||||
* Generic roll dispatcher.
|
||||
* @param {"attribute"|"aptitude"|"weapon"|"spell"|"alchemy"|"horoscope"} type
|
||||
* @param {string|number} [key] - attribute/aptitude key, item name/index, or horoscope type
|
||||
* @param {Actor} [actor] - optional, defaults to selected/owned token
|
||||
*/
|
||||
static roll(type, key = undefined, actor = undefined) {
|
||||
switch (type) {
|
||||
case "attribute": return this.rollAttribute(key ?? "vigor", actor)
|
||||
case "aptitude": return this.rollAptitude(key ?? "melee", actor)
|
||||
case "weapon": return this.rollWeapon(key, actor)
|
||||
case "spell": return this.rollSpell(key, actor)
|
||||
case "alchemy": return this.rollAlchemy(key, actor)
|
||||
case "horoscope": return this.rollHoroscope(key ?? "minor", actor)
|
||||
default:
|
||||
ui.notifications.warn(`Type de jet inconnu : "${type}". Types valides : attribute, aptitude, weapon, spell, alchemy, horoscope`)
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
// Kept for backward-compat (previously called by old macros)
|
||||
static rollMacro = async function (rollType, key) {
|
||||
const actor = Macros.getSpeakersActor()
|
||||
if (!actor) return
|
||||
return Macros.roll(rollType, key, actor)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user