From a5ba6d52159bef4faf6238de9491630aea637f74 Mon Sep 17 00:00:00 2001 From: sladecraven Date: Mon, 21 Mar 2022 23:21:05 +0100 Subject: [PATCH] Add HUD actions --- css/bol.css | 40 ++++ module/actor/actor-sheet.js | 11 +- module/actor/actor.js | 301 ++++++++++++++------------ module/bol.js | 30 +-- module/controllers/bol-rolls.js | 61 +++--- module/system/bol-action-hud.js | 88 ++++++++ system.json | 2 +- templates/token/hud-actor-actions.hbs | 10 + templates/token/hud-actor-rolls.hbs | 10 + 9 files changed, 357 insertions(+), 196 deletions(-) create mode 100644 module/system/bol-action-hud.js create mode 100644 templates/token/hud-actor-actions.hbs create mode 100644 templates/token/hud-actor-rolls.hbs diff --git a/css/bol.css b/css/bol.css index 8c9ea6f..cb9cdad 100644 --- a/css/bol.css +++ b/css/bol.css @@ -982,3 +982,43 @@ body.system-bol img#logo { visibility: visible; opacity: 1; } + +/** HUD SECTION */ +.tokenhudext { + display: flex; + flex: 0 !important; + font-family: CaslonPro; + font-weight: 600; +} +.tokenhudext.left { + justify-content: flex-start; + flex-direction: column; + position: absolute; + top: 2.75rem; + right: 4rem; +} +.tokenhudext.right { + justify-content: flex-start; + flex-direction: column; + position: absolute; + top: 2.75rem; + left: 4rem; +} +.control-icon.tokenhudicon { + width: fit-content; + height: fit-content; + min-width: 6rem; + flex-basis: auto; + padding: 0; + line-height: 1rem; + margin: 0.25rem; +} +.control-icon.tokenhudicon.right { + margin-left: 8px; +} +#token-hud .status-effects.active{ + z-index: 2; +} +.bol-hud-menu label { + font-size: 0.75rem; +} diff --git a/module/actor/actor-sheet.js b/module/actor/actor-sheet.js index c1d2561..c2e6f57 100644 --- a/module/actor/actor-sheet.js +++ b/module/actor/actor-sheet.js @@ -192,24 +192,23 @@ export class BoLActorSheet extends ActorSheet { event.preventDefault(); const element = event.currentTarget const dataset = element.dataset - const actorData = this.getData() const rollType = dataset.rollType const li = $(event.currentTarget).closest(".item") switch (rollType) { case "attribute": - BoLRoll.attributeCheck(this.actor, actorData, dataset, event); + BoLRoll.attributeCheck(this.actor, dataset.key, event) break; case "aptitude": - BoLRoll.aptitudeCheck(this.actor, actorData, dataset, event); + BoLRoll.aptitudeCheck(this.actor, dataset.key, event) break; case "weapon": - BoLRoll.weaponCheck(this.actor, actorData, dataset, event); + BoLRoll.weaponCheck(this.actor, event) break; case "spell": - BoLRoll.spellCheck(this.actor, actorData, dataset, event); + BoLRoll.spellCheck(this.actor, event) break; case "alchemy": - BoLRoll.alchemyCheck(this.actor, actorData, dataset, event); + BoLRoll.alchemyCheck(this.actor, event) break; case "protection": this.actor.rollProtection(li.data("item-id")) diff --git a/module/actor/actor.js b/module/actor/actor.js index 7e589ef..858027b 100644 --- a/module/actor/actor.js +++ b/module/actor/actor.js @@ -6,7 +6,7 @@ import { BoLUtility } from "../system/bol-utility.js"; * @extends {Actor} */ export class BoLActor extends Actor { - + /** @override */ prepareData() { const actorData = this.data; @@ -23,15 +23,15 @@ export class BoLActor extends Actor { } /* -------------------------------------------- */ - updateResourcesData( ) { - if ( this.type == 'character') { + updateResourcesData() { + if (this.type == 'character') { let newVitality = 10 + this.data.data.attributes.vigor.value + this.data.data.resources.hp.bonus - if ( this.data.data.resources.hp.max != newVitality) { - this.update( {'data.resources.hp.max': newVitality} ); + if (this.data.data.resources.hp.max != newVitality) { + this.update({ 'data.resources.hp.max': newVitality }); } let newPower = 10 + this.data.data.attributes.mind.value + this.data.data.resources.power.bonus - if ( this.data.data.resources.power.max != newPower) { - this.update( {'data.resources.power.max': newPower} ); + if (this.data.data.resources.power.max != newPower) { + this.update({ 'data.resources.power.max': newPower }); } } } @@ -44,7 +44,7 @@ export class BoLActor extends Actor { } /* -------------------------------------------- */ - get itemData(){ + get itemData() { return Array.from(this.data.items.values()).map(i => i.data) } get details() { @@ -58,11 +58,11 @@ export class BoLActor extends Actor { } /* -------------------------------------------- */ - clearRoundModifiers( ) { // Process data/items that are finished at end of a round + clearRoundModifiers() { // Process data/items that are finished at end of a round let foList = this.fightoptions - for(let fo of foList) { + for (let fo of foList) { if (fo.data.properties.used) { - this.updateEmbeddedDocuments("Item", [ {_id: fo._id, 'data.properties.used': false}] ) + this.updateEmbeddedDocuments("Item", [{ _id: fo._id, 'data.properties.used': false }]) } } } @@ -71,27 +71,27 @@ export class BoLActor extends Actor { get defenseValue() { let defMod = 0 let fo = this.getActiveFightOption() - if (fo && fo.data.properties.fightoptiontype == "intrepid" ) { + if (fo && fo.data.properties.fightoptiontype == "intrepid") { defMod += -2 } - if (fo && fo.data.properties.fightoptiontype == "fulldefense" ) { + if (fo && fo.data.properties.fightoptiontype == "fulldefense") { defMod += 2 } if (fo && fo.data.properties.fightoptiontype == "twoweaponsdef" && !fo.data.properties.used) { defMod += 1 - this.updateEmbeddedDocuments("Item", [ {_id: fo._id, 'data.properties.used': true}] ) + this.updateEmbeddedDocuments("Item", [{ _id: fo._id, 'data.properties.used': true }]) } - if (fo && fo.data.properties.fightoptiontype == "defense" ) { + if (fo && fo.data.properties.fightoptiontype == "defense") { defMod += 1 } - if (fo && fo.data.properties.fightoptiontype == "attack" ) { + if (fo && fo.data.properties.fightoptiontype == "attack") { defMod += -1 } return this.data.data.aptitudes.def.value + defMod } /* -------------------------------------------- */ - getActiveFightOption( ) { + getActiveFightOption() { let it = this.itemData.find(i => i.type === "feature" && i.data.subtype === "fightoption" && i.data.properties.activated) if (it) { return duplicate(it) @@ -100,15 +100,15 @@ export class BoLActor extends Actor { } /* -------------------------------------------- */ - incAttributeXP( key) { + incAttributeXP(key) { let attr = duplicate(this.data.data.attributes[key]) if (attr) { - let nextXP = (attr.value == -1) ? 2 : attr.value + (attr.value+1) + let nextXP = (attr.value == -1) ? 2 : attr.value + (attr.value + 1) let xp = duplicate(this.data.data.xp) - if ( xp.total - xp.spent >= nextXP) { + if (xp.total - xp.spent >= nextXP) { attr.value += 1 xp.spent += nextXP - this.update( { [`data.attributes.${key}`]: attr, [`data.xp`]: xp } ) + this.update({ [`data.attributes.${key}`]: attr, [`data.xp`]: xp }) } else { ui.notifications.warn("Pas assez de points d'expérience !") } @@ -116,31 +116,31 @@ export class BoLActor extends Actor { } /* -------------------------------------------- */ - incAptitudeXP( key) { + incAptitudeXP(key) { let apt = duplicate(this.data.data.aptitudes[key]) if (apt) { let nextXP = (apt.value == -1) ? 1 : apt.value + 2 let xp = duplicate(this.data.data.xp) - if ( xp.total - xp.spent >= nextXP) { + if (xp.total - xp.spent >= nextXP) { apt.value += 1 xp.spent += nextXP - this.update( { [`data.aptitudes.${key}`]: apt, [`data.xp`]: xp } ) + this.update({ [`data.aptitudes.${key}`]: apt, [`data.xp`]: xp }) } else { ui.notifications.warn("Pas assez de points d'expérience !") } } } /* -------------------------------------------- */ - incCareerXP( itemId) { - let career = this.data.items.get( itemId ) + incCareerXP(itemId) { + let career = this.data.items.get(itemId) if (career) { career = duplicate(career) let nextXP = career.data.rank + 1 let xp = duplicate(this.data.data.xp) - if ( xp.total - xp.spent >= nextXP) { + if (xp.total - xp.spent >= nextXP) { xp.spent += nextXP - this.update( { [`data.xp`]: xp } ) - this.updateEmbeddedDocuments( 'Item', [ { _id: career._id, 'data.rank': career.data.rank + 1 } ]) + this.update({ [`data.xp`]: xp }) + this.updateEmbeddedDocuments('Item', [{ _id: career._id, 'data.rank': career.data.rank + 1 }]) } else { ui.notifications.warn("Pas assez de points d'expérience !") } @@ -148,25 +148,25 @@ export class BoLActor extends Actor { } /* -------------------------------------------- */ - async toggleFightOption( itemId) { + async toggleFightOption(itemId) { let fightOption = this.data.items.get(itemId) let state let updates = [] - if ( fightOption) { + if (fightOption) { fightOption = duplicate(fightOption) if (fightOption.data.properties.activated) { state = false } else { state = true } - updates.push( {_id: fightOption._id, 'data.properties.activated': state} ) // Update the selected one + updates.push({ _id: fightOption._id, 'data.properties.activated': state }) // Update the selected one await this.updateEmbeddedDocuments("Item", updates) // Apply all changes // Then notify ChatMessage.create({ alias: this.name, whisper: BoLUtility.getWhisperRecipientsAndGMs(this.name), - content: await renderTemplate('systems/bol/templates/chat/chat-activate-fight-option.hbs', { name: this.name, img: fightOption.img, foName: fightOption.name, state: state} ) + content: await renderTemplate('systems/bol/templates/chat/chat-activate-fight-option.hbs', { name: this.name, img: fightOption.img, foName: fightOption.name, state: state }) }) } @@ -174,14 +174,14 @@ export class BoLActor extends Actor { /* -------------------------------------------- */ get armorMalusValue() { // used for Fight Options - for(let armor of this.armors) { - if (armor.data.properties.armorQuality.includes("light")) { + for (let armor of this.armors) { + if (armor.data.properties.armorQuality.includes("light")) { return 1 } - if (armor.data.properties.armorQuality.includes("medium")) { + if (armor.data.properties.armorQuality.includes("medium")) { return 2 } - if (armor.data.properties.armorQuality.includes("heavy")) { + if (armor.data.properties.armorQuality.includes("heavy")) { return 3 } } @@ -219,7 +219,7 @@ export class BoLActor extends Actor { return this.itemData.filter(i => i.type === "item") } get armors() { - return this.itemData.filter(i => i.type === "item" && i.data.category === "equipment" && i.data.subtype === "armor"); + return this.itemData.filter(i => i.type === "item" && i.data.category === "equipment" && i.data.subtype === "armor"); } get helms() { return this.itemData.filter(i => i.type === "item" && i.data.category === "equipment" && i.data.subtype === "helm"); @@ -227,7 +227,7 @@ export class BoLActor extends Actor { get shields() { return this.itemData.filter(i => i.type === "item" && i.data.category === "equipment" && i.data.subtype === "shield"); } - + get weapons() { return this.itemData.filter(i => i.type === "item" && i.data.category === "equipment" && i.data.subtype === "weapon"); } @@ -235,10 +235,10 @@ export class BoLActor extends Actor { return this.armors.concat(this.helms).concat(this.shields) } get spells() { - return this.itemData.filter(i => i.type === "item" && i.data.category === "spell"); + return this.itemData.filter(i => i.type === "item" && i.data.category === "spell"); } get alchemy() { - return this.itemData.filter(i => i.type === "item" && i.data.category === "alchemy"); + return this.itemData.filter(i => i.type === "item" && i.data.category === "alchemy"); } get melee() { return this.weapons.filter(i => i.data.properties.melee === true); @@ -264,9 +264,9 @@ export class BoLActor extends Actor { } get misc() { - return this.itemData.filter(i => i.type === "item" && i.data.category === "equipment" && (i.data.subtype === "other" ||i.data.subtype === "container" ||i.data.subtype === "scroll" || i.data.subtype === "jewel")); + return this.itemData.filter(i => i.type === "item" && i.data.category === "equipment" && (i.data.subtype === "other" || i.data.subtype === "container" || i.data.subtype === "scroll" || i.data.subtype === "jewel")); } - + get bonusBoons() { return this.itemData.filter(i => i.type === "feature" && i.data.subtype === "boon" && i.data.properties.isbonusdice); } @@ -274,67 +274,67 @@ export class BoLActor extends Actor { return this.itemData.filter(i => i.type === "feature" && i.data.subtype === "flaw" && i.data.properties.ismalusdice); } - isSorcerer( ) { - if ( this.careers.find( item => item.data.properties.sorcerer == true) ) + isSorcerer() { + if (this.careers.find(item => item.data.properties.sorcerer == true)) return true return false } - isAlchemist( ) { - if ( this.careers.find( item => item.data.properties.alchemist == true) ) + isAlchemist() { + if (this.careers.find(item => item.data.properties.alchemist == true)) return true return false } - isPriest( ) { - if ( this.careers.find( item => item.data.properties.priest == true) ) + isPriest() { + if (this.careers.find(item => item.data.properties.priest == true)) return true return false } - spendPowerPoint( ppCost ) { + spendPowerPoint(ppCost) { let newPP = this.data.data.resources.power.value - ppCost - newPP = (newPP<0) ? 0 : newPP - this.update( {'data.resources.power.value': newPP}) + newPP = (newPP < 0) ? 0 : newPP + this.update({ 'data.resources.power.value': newPP }) } - resetAlchemyStatus( alchemyId ) { - let alchemy = this.data.items.get( alchemyId) + resetAlchemyStatus(alchemyId) { + let alchemy = this.data.items.get(alchemyId) if (alchemy) { - this.updateEmbeddedDocuments('Item', [{_id: alchemy.id, 'data.properties.pccurrent': 0}] ) + this.updateEmbeddedDocuments('Item', [{ _id: alchemy.id, 'data.properties.pccurrent': 0 }]) } } - async spendAlchemyPoint( alchemyId, pcCost) { - let alchemy = this.data.items.get( alchemyId) + async spendAlchemyPoint(alchemyId, pcCost) { + let alchemy = this.data.items.get(alchemyId) if (alchemy) { - pcCost = Number(pcCost)?? 0 - if ( this.data.data.resources.alchemypoints.value >= pcCost) { + pcCost = Number(pcCost) ?? 0 + if (this.data.data.resources.alchemypoints.value >= pcCost) { let newPC = this.data.data.resources.alchemypoints.value - pcCost - newPC = (newPC<0) ? 0 : newPC - this.update( {'data.resources.alchemypoints.value': newPC} ) + newPC = (newPC < 0) ? 0 : newPC + this.update({ 'data.resources.alchemypoints.value': newPC }) newPC = alchemy.data.data.properties.pccurrent + pcCost - await this.updateEmbeddedDocuments('Item', [{_id: alchemy.id, 'data.properties.pccurrent': newPC}] ) + await this.updateEmbeddedDocuments('Item', [{ _id: alchemy.id, 'data.properties.pccurrent': newPC }]) } else { ui.notifications.warn("Plus assez de Points de Création !") } } - } + } getAlchemistBonus() { - let sorcerer = this.careers.find( item => item.data.properties.alchemist == true) + let sorcerer = this.careers.find(item => item.data.properties.alchemist == true) if (sorcerer) { return sorcerer.data.rank } return 0; } getSorcererBonus() { - let sorcerer = this.careers.find( item => item.data.properties.sorcerer == true) + let sorcerer = this.careers.find(item => item.data.properties.sorcerer == true) if (sorcerer) { return sorcerer.data.rank } return 0; } - heroReroll( ) { + heroReroll() { if (this.type == 'character') { return this.data.data.resources.hero.value > 0; } else { @@ -363,7 +363,7 @@ export class BoLActor extends Actor { return resources } - buildFeatures(){ + buildFeatures() { return { "careers": { "label": "BOL.featureCategory.careers", @@ -402,68 +402,89 @@ export class BoLActor extends Actor { } } } - - buildCombat(){ + + buildCombat() { return { - "melee" : { - "label" : "BOL.combatCategory.melee", - "weapon" : true, - "protection" : false, - "blocking" : false, - "ranged" : false, + "melee": { + "label": "BOL.combatCategory.melee", + "weapon": true, + "protection": false, + "blocking": false, + "ranged": false, "options": false, - "items" : this.melee + "items": this.melee }, - "ranged" : { - "label" : "BOL.combatCategory.ranged", - "weapon" : true, - "protection" : false, - "blocking" : false, - "ranged" : true, + "ranged": { + "label": "BOL.combatCategory.ranged", + "weapon": true, + "protection": false, + "blocking": false, + "ranged": true, "options": false, - "items" : this.ranged + "items": this.ranged }, - "protections" : { - "label" : "BOL.combatCategory.protections", - "weapon" : false, - "protection" : true, - "blocking" : false, - "ranged" : false, + "protections": { + "label": "BOL.combatCategory.protections", + "weapon": false, + "protection": true, + "blocking": false, + "ranged": false, "options": false, - "items" : this.protections + "items": this.protections }, - "shields" : { - "label" : "BOL.combatCategory.shields", - "weapon" : false, - "protection" : false, - "blocking" : true, - "ranged" : false, + "shields": { + "label": "BOL.combatCategory.shields", + "weapon": false, + "protection": false, + "blocking": true, + "ranged": false, "options": false, - "items" : this.shields + "items": this.shields }, - "fightoptions" : { - "label" : "BOL.combatCategory.fightOptions", - "weapon" : false, - "protection" : false, - "blocking" : false, - "ranged" : false, + "fightoptions": { + "label": "BOL.combatCategory.fightOptions", + "weapon": false, + "protection": false, + "blocking": false, + "ranged": false, "options": true, - "items" : this.fightoptions + "items": this.fightoptions } } } + /*-------------------------------------------- */ + buildRollList() { + let rolls = [] + for ( let key in this.data.data.attributes) { + let attr = this.data.data.attributes[key] + rolls.push( { key: key, value: attr.value, name: attr.label, type: "attribute"}) + } + for ( let key in this.data.data.aptitudes) { + if ( key != "def") { + let apt = this.data.data.aptitudes[key] + rolls.push( { key: key, value: apt.value, name: apt.label, type: "aptitude"}) + } + } + return rolls + } + + /*-------------------------------------------- */ + buildListeActions() { + return this.melee.concat(this.ranged) + } + /*-------------------------------------------- */ async manageHealthState() { - let hpID = "lastHP"+this.id - let lastHP = await this.getFlag("world", hpID ) - if ( lastHP != this.data.data.resources.hp.value ) { + let hpID = "lastHP" + this.id + let lastHP = await this.getFlag("world", hpID) + if (lastHP != this.data.data.resources.hp.value) { await this.setFlag("world", hpID, this.data.data.resources.hp.value) - if (this.data.data.resources.hp.value <= 0 ) { + if (this.data.data.resources.hp.value <= 0) { ChatMessage.create({ alias: this.name, whisper: BoLUtility.getWhisperRecipientsAndGMs(this.name), - content: await renderTemplate('systems/bol/templates/chat/chat-vitality-zero.hbs', { name: this.name, img: this.img, hp: this.data.data.resources.hp.value} ) + content: await renderTemplate('systems/bol/templates/chat/chat-vitality-zero.hbs', { name: this.name, img: this.img, hp: this.data.data.resources.hp.value }) }) } } @@ -471,7 +492,7 @@ export class BoLActor extends Actor { /*-------------------------------------------- */ registerInit(initScore, isCritical, isFumble) { - this.update( { 'data.combat.lastinit': initScore, 'data.combat.iscritical': isCritical, 'data.combat.isfumble': isFumble} ) + this.update({ 'data.combat.lastinit': initScore, 'data.combat.iscritical': isCritical, 'data.combat.isfumble': isFumble }) } /*-------------------------------------------- */ @@ -480,60 +501,60 @@ export class BoLActor extends Actor { } /*-------------------------------------------- */ - async subHeroPoints( nb) { + async subHeroPoints(nb) { let newHeroP = this.data.data.resources.hero.value - nb; - newHeroP = (newHeroP < 0 ) ? 0 : newHeroP; - await this.update( { 'data.resources.hero.value': newHeroP} ); + newHeroP = (newHeroP < 0) ? 0 : newHeroP; + await this.update({ 'data.resources.hero.value': newHeroP }); } /*-------------------------------------------- */ - async sufferDamage( damage) { + async sufferDamage(damage) { let newHP = this.data.data.resources.hp.value - damage; - await this.update( { 'data.resources.hp.value': newHP} ); + await this.update({ 'data.resources.hp.value': newHP }); } /* -------------------------------------------- */ - getArmorFormula( ) { - let protectWorn = this.protections.filter( item => item.data.worn) + getArmorFormula() { + let protectWorn = this.protections.filter(item => item.data.worn) let formula = "" for (let protect of protectWorn) { - if ( protect.data.subtype == 'helm') { - formula += "+1" - } else if ( protect.data.subtype == 'armor') { - if ( BoLUtility.getRollArmor() ) { - if ( !protect.data.properties.soak.formula || protect.data.properties.soak.formula=="") { - ui.notifications.warn(`L'armure ${protect.name} n'a pas de formule pour la protection !`) - } else { + if (protect.data.subtype == 'helm') { + formula += "+1" + } else if (protect.data.subtype == 'armor') { + if (BoLUtility.getRollArmor()) { + if (!protect.data.properties.soak.formula || protect.data.properties.soak.formula == "") { + ui.notifications.warn(`L'armure ${protect.name} n'a pas de formule pour la protection !`) + } else { formula += "+" + protect.data.properties.soak.formula } - } else { - if ( protect.data.properties.soak.value == undefined ) { - ui.notifications.warn(`L'armure ${protect.name} n'a pas de valeur fixe pour la protection !`) - } else { - formula += "+ " + protect.data.properties.soak.value + } else { + if (protect.data.properties.soak.value == undefined) { + ui.notifications.warn(`L'armure ${protect.name} n'a pas de valeur fixe pour la protection !`) + } else { + formula += "+ " + protect.data.properties.soak.value } } } } console.log("Protect Formula", formula) - return (formula == "") ? "0" :formula; + return (formula == "") ? "0" : formula; } /* -------------------------------------------- */ - rollProtection( itemId) { - let armor = this.data.items.get( itemId ) - if ( armor ) { - let armorFormula = armor.data.data.properties.soak.formula; + rollProtection(itemId) { + let armor = this.data.items.get(itemId) + if (armor) { + let armorFormula = armor.data.data.properties.soak.formula; let rollArmor = new Roll(armorFormula) - rollArmor.roll( {async: false} ).toMessage(); + rollArmor.roll({ async: false }).toMessage(); } } /* -------------------------------------------- */ - rollWeaponDamage( itemId) { - let weapon = this.data.items.get(itemId ) - if ( weapon ) { - let r = new BoLDefaultRoll( { id: randomID(16), isSuccess: true, mode: "weapon", weapon: weapon, actor: this} ) + rollWeaponDamage(itemId) { + let weapon = this.data.items.get(itemId) + if (weapon) { + let r = new BoLDefaultRoll({ id: randomID(16), isSuccess: true, mode: "weapon", weapon: weapon, actor: this }) r.setSuccess(true) r.rollDamage() } @@ -542,7 +563,7 @@ export class BoLActor extends Actor { /* -------------------------------------------- */ toggleEquipItem(item) { const equipable = item.data.data.properties.equipable; - if(equipable){ + if (equipable) { let itemData = duplicate(item.data); itemData.data.worn = !itemData.data.worn; return item.update(itemData); diff --git a/module/bol.js b/module/bol.js index c75b4c2..0de91ee 100644 --- a/module/bol.js +++ b/module/bol.js @@ -1,16 +1,19 @@ +/* -------------------------------------------- */ // Import Modules -import { BoLActor } from "./actor/actor.js"; -import { BoLActorSheet } from "./actor/actor-sheet.js"; -import { BoLItem } from "./item/item.js"; -import { BoLItemSheet } from "./item/item-sheet.js"; -import { System, BOL } from "./system/config.js"; -import { preloadHandlebarsTemplates } from "./system/templates.js"; -import { registerHandlebarsHelpers } from "./system/helpers.js"; -import registerHooks from "./system/hooks.js"; -import { Macros } from "./system/macros.js"; -import { BoLUtility } from "./system/bol-utility.js"; -import { BoLCombatManager } from "./system/bol-combat.js"; +import { BoLActor } from "./actor/actor.js" +import { BoLActorSheet } from "./actor/actor-sheet.js" +import { BoLItem } from "./item/item.js" +import { BoLItemSheet } from "./item/item-sheet.js" +import { System, BOL } from "./system/config.js" +import { preloadHandlebarsTemplates } from "./system/templates.js" +import { registerHandlebarsHelpers } from "./system/helpers.js" +import registerHooks from "./system/hooks.js" +import { Macros } from "./system/macros.js" +import { BoLUtility } from "./system/bol-utility.js" +import { BoLCombatManager } from "./system/bol-combat.js" +import { BoLTokenHud } from "./system/bol-action-hud.js" +/* -------------------------------------------- */ Hooks.once('init', async function () { game.bol = { @@ -47,8 +50,9 @@ Hooks.once('init', async function () { Items.registerSheet("bol", BoLItemSheet, { makeDefault: true }); // Inot useful stuff - BoLUtility.init(); - + BoLUtility.init() + BoLTokenHud.init() + // Preload Handlebars Templates await preloadHandlebarsTemplates(); diff --git a/module/controllers/bol-rolls.js b/module/controllers/bol-rolls.js index c631b13..69f4365 100644 --- a/module/controllers/bol-rolls.js +++ b/module/controllers/bol-rolls.js @@ -1,6 +1,5 @@ import { BoLUtility } from "../system/bol-utility.js"; -const __adv2dice = { ["1B"]: 3, ["2B"]: 4, ["2"]: 2, ["1M"]: 3, ["2M"]: 4 } const _apt2attr = { init: "mind", melee: "agility", ranged: "agility", def: "vigor" } /* -------------------------------------------- */ @@ -11,57 +10,45 @@ export class BoLRoll { return { classes: ["bol", "dialog"], width: 480, height: 540 }; } - /* -------------------------------------------- */ - static convertToAdv(adv) { - if (adv == 0) return "2" - return Math.abs(adv) + (adv < 0) ? 'M' : 'B'; - } /* -------------------------------------------- */ static getDefaultAttribute(key) { return _apt2attr[key] } /* -------------------------------------------- */ - static attributeCheck(actor, actorData, dataset, event) { - const key = dataset.key - const adv = dataset.adv + static attributeCheck(actor, key ) { let attribute = eval(`actor.data.data.attributes.${key}`) let label = (attribute.label) ? game.i18n.localize(attribute.label) : null - let description = actor.name + " - " + game.i18n.localize('BOL.ui.attributeCheck') + " - " + game.i18n.localize(attribute.label) + let description = game.i18n.localize('BOL.ui.attributeCheck') + " - " + game.i18n.localize(attribute.label) let rollData = { mode: "attribute", actor: actor, - actorData: actorData, attribute: attribute, attrValue: attribute.value, aptValue: 0, label: label, careerBonus: 0, description: description, - adv: this.convertToAdv(adv), mod: 0 } return this.displayRollDialog( rollData ) } /* -------------------------------------------- */ - static aptitudeCheck(actor, actorData, dataset, event) { - const key = dataset.key; - const adv = dataset.adv; + static aptitudeCheck(actor, key ) { - let aptitude = eval(`actor.data.data.aptitudes.${key}`); + let aptitude = eval(`actor.data.data.aptitudes.${key}`) let attrKey = this.getDefaultAttribute(key) - let attribute = eval(`actor.data.data.attributes.${attrKey}`); + let attribute = eval(`actor.data.data.attributes.${attrKey}`) let label = (aptitude.label) ? game.i18n.localize(aptitude.label) : null; - let description = actor.name + " - " + game.i18n.localize('BOL.ui.aptitudeCheck') + " - " + game.i18n.localize(aptitude.label); + let description = game.i18n.localize('BOL.ui.aptitudeCheck') + " - " + game.i18n.localize(aptitude.label); return this.displayRollDialog( { mode: "aptitude", actor: actor, - actorData: actorData, attribute: attribute, aptitude: aptitude, attrValue: attribute.value, @@ -69,20 +56,15 @@ export class BoLRoll { label: label, careerBonus: 0, description: description, - adv: this.convertToAdv(adv), mod: 0 }); } /* -------------------------------------------- */ - static weaponCheck(actor, actorData, dataset, event) { + static weaponCheckWithWeapon(actor, weapon) { + let target = BoLUtility.getTarget() - const li = $(event.currentTarget).parents(".item") - const weapon = actor.items.get(li.data("item-id")) - if (!weapon) { - ui.notifications.warn("Unable to find weapon !") - return; - } + let weaponData = weapon.data.data let attribute = eval(`actor.data.data.attributes.${weaponData.properties.attackAttribute}`) let aptitude = eval(`actor.data.data.aptitudes.${weaponData.properties.attackAptitude}`) @@ -97,7 +79,6 @@ export class BoLRoll { let rolldata = { mode: "weapon", actor: actor, - actorData: actorData, weapon: weapon, isRanged: weaponData.properties.ranged || weaponData.properties.throwing, target: target, @@ -111,13 +92,23 @@ export class BoLRoll { mod: 0, modRanged: 0, label: (weapon.name) ? weapon.name : game.i18n.localize('BOL.ui.noWeaponName'), - description: actor.name + " - " + game.i18n.localize('BOL.ui.weaponAttack'), + description: game.i18n.localize('BOL.ui.weaponAttack') + " : " + weapon.name, } return this.displayRollDialog(rolldata) } + /* -------------------------------------------- */ + static weaponCheck(actor, event) { + const li = $(event.currentTarget).parents(".item") + const weapon = actor.items.get(li.data("item-id")) + if (!weapon) { + ui.notifications.warn("Unable to find weapon !") + return + } + return this.weaponCheckWithWeapon(actor, weapon) + } /* -------------------------------------------- */ - static alchemyCheck(actor, actorData, dataset, event) { + static alchemyCheck(actor, event) { const li = $(event.currentTarget).parents(".item"); const alchemy = actor.items.get(li.data("item-id")); if (!alchemy) { @@ -133,7 +124,6 @@ export class BoLRoll { let alchemyDef = { mode: "alchemy", actor: actor, - actorData: actorData, alchemy: alchemy, attribute: actor.data.data.attributes.mind, attrValue: actor.data.data.attributes.mind.value, @@ -143,7 +133,7 @@ export class BoLRoll { pcCostCurrent: Number(alchemyData.properties.pccurrent), mod: Number(alchemyData.properties.difficulty), label: alchemy.name, - description: actor.name + " - " + game.i18n.localize('BOL.ui.makeAlchemy'), + description: game.i18n.localize('BOL.ui.makeAlchemy') + "+" + alchemy.name, } console.log("ALCHEMY!", alchemyDef); return this.displayRollDialog(alchemyDef); @@ -151,7 +141,7 @@ export class BoLRoll { /* -------------------------------------------- */ - static spellCheck(actor, actorData, dataset, event) { + static spellCheck(actor, event) { if (actor.data.data.resources.power.value <= 0) { ui.notifications.warn("Plus assez de points de Pouvoir !") return @@ -166,7 +156,6 @@ export class BoLRoll { let spellDef = { mode: "spell", actor: actor, - actorData: actorData, spell: spell, attribute: actor.data.data.attributes.mind, attrValue: actor.data.data.attributes.mind.value, @@ -176,7 +165,7 @@ export class BoLRoll { ppCost: Number(spell.data.data.properties.ppcost), mod: Number(spellData.properties.difficulty), label: spell.name, - description: actor.name + " - " + game.i18n.localize('BOL.ui.focusSpell'), + description: game.i18n.localize('BOL.ui.focusSpell') + " : " + spell.name, } console.log("SPELL!", spellDef); return this.displayRollDialog(spellDef); @@ -319,7 +308,7 @@ export class BoLRoll { // initialize default flags/values const rollOptionTpl = `systems/bol/templates/dialogs/${rollData.mode}-roll-dialog.hbs` - rollData.careers = rollData.actorData.features.careers + rollData.careers = rollData.actor.careers rollData.boons = rollData.actor.bonusBoons rollData.flaws = rollData.actor.malusFlaws rollData.defence = 0 diff --git a/module/system/bol-action-hud.js b/module/system/bol-action-hud.js new file mode 100644 index 0000000..94a1c0e --- /dev/null +++ b/module/system/bol-action-hud.js @@ -0,0 +1,88 @@ +/* -------------------------------------------- */ +import { BoLRoll } from "../controllers/bol-rolls.js"; + +/* -------------------------------------------- */ +export class BoLTokenHud { + + static init() { + // Integration du TokenHUD + Hooks.on('renderTokenHUD', (app, html, data) => { BoLTokenHud.addTokenHudExtensions(app, html, data._id) }); + } + + /* -------------------------------------------- */ + static async removeExtensionHud(app, html, tokenId) { + html.find('.control-icon.bol-roll').remove() + html.find('.control-icon.bol-action').remove() + } + + /* -------------------------------------------- */ + static async addExtensionHud(app, html, tokenId) { + + let token = canvas.tokens.get(tokenId) + let actor = token.actor + app.hasExtension = true + + const hudData = { actor: actor, actionsList: actor.buildListeActions(), rollsList: actor.buildRollList() } + + const controlIconActions = html.find('.control-icon[data-action=combat]'); + // initiative + await BoLTokenHud._configureSubMenu(controlIconActions, 'systems/bol/templates/token/hud-actor-actions.hbs', hudData, + (event) => { + let actionIndex = Number(event.currentTarget.attributes['data-action-index'].value) + let action = hudData.actionsList[actionIndex] + const weapon = actor.items.get( action._id ) + BoLRoll.weaponCheckWithWeapon(hudData.actor, weapon) + //console.log("Clicked", action) + } ) + + const controlIconTarget = html.find('.control-icon[data-action=target]'); + // att+apt+career + await BoLTokenHud._configureSubMenu(controlIconTarget, 'systems/bol/templates/token/hud-actor-rolls.hbs', hudData, + (event) => { + let rollIndex = Number(event.currentTarget.attributes['data-roll-index'].value) + let roll = hudData.rollsList[rollIndex] + if ( roll.type == "aptitude") { + BoLRoll.aptitudeCheck(actor, roll.key ) + } else if ( roll.type == "attribute") { + BoLRoll.attributeCheck(actor, roll.key ) + } + }) + } + + /* -------------------------------------------- */ + static async addTokenHudExtensions(app, html, tokenId) { + const controlIconCombat = html.find('.control-icon[data-action=combat]') + if (controlIconCombat.length>0 ) { + BoLTokenHud.addExtensionHud(app, html, tokenId); + } + } + + /* -------------------------------------------- */ + static async _configureSubMenu(insertionPoint, template, hudData, onMenuItem) { + const hud = $(await renderTemplate(template, hudData)) + const list = hud.find('div.bol-hud-list') + + BoLTokenHud._toggleHudListActive(hud, list); + + hud.find('img.bol-hud-togglebutton').click(event => BoLTokenHud._toggleHudListActive(hud, list)); + list.find('.bol-hud-menu').click(onMenuItem); + + insertionPoint.after(hud); + } + + /* -------------------------------------------- */ + static _showControlWhen(control, condition) { + if (condition) { + control.show() + } + else { + control.hide() + } + } + + /* -------------------------------------------- */ + static _toggleHudListActive(hud, list) { + hud.toggleClass('active') + BoLTokenHud._showControlWhen(list, hud.hasClass('active')) + } +} \ No newline at end of file diff --git a/system.json b/system.json index c94e2db..6d803f6 100644 --- a/system.json +++ b/system.json @@ -7,7 +7,7 @@ "url": "https://github.com/ZigmundKreud/bol", "license": "LICENSE.txt", "flags": {}, - "version": "1.2.4", + "version": "1.2.6", "templateVersion": 22, "minimumCoreVersion": "0.8.6", "compatibleCoreVersion": "9", diff --git a/templates/token/hud-actor-actions.hbs b/templates/token/hud-actor-actions.hbs new file mode 100644 index 0000000..b529c9c --- /dev/null +++ b/templates/token/hud-actor-actions.hbs @@ -0,0 +1,10 @@ +
+ +
+ {{#each actionsList as |action key|}} +
+ +
+ {{/each}} +
+
diff --git a/templates/token/hud-actor-rolls.hbs b/templates/token/hud-actor-rolls.hbs new file mode 100644 index 0000000..5dad7b2 --- /dev/null +++ b/templates/token/hud-actor-rolls.hbs @@ -0,0 +1,10 @@ +
+ +
+ {{#each rollsList as |roll key|}} +
+ +
+ {{/each}} +
+