diff --git a/lang/en.json b/lang/en.json index c6fbfa6..ed8bbeb 100644 --- a/lang/en.json +++ b/lang/en.json @@ -94,7 +94,11 @@ "BOL.ui.misc" : "Divers", "BOL.ui.noWeaponName" : "Unknown Weapon", "BOL.ui.targetDefence": "Defence", - + "BOL.ui.applyShieldMalus": "Apply Small Shield Malus", + "BOL.ui.shieldMalus": "Shield Malus", + "BOL.ui.defenseScore": "Defense Score", + "BOL.ui.defender": "Defender", + "BOL.featureCategory.origins": "Origines", "BOL.featureCategory.races": "Races", "BOL.featureCategory.careers": "Carrières", diff --git a/lang/fr.json b/lang/fr.json index 9a150e9..a7c195c 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -96,6 +96,10 @@ "BOL.ui.speed" : "Vitesse", "BOL.ui.noWeaponName" : "Arme Inconnue", "BOL.ui.targetDefence": "Défense", + "BOL.ui.applyShieldMalus": "Appliquer le Malus de Petit Bouclier", + "BOL.ui.shieldMalus": "Malus de Bouclier", + "BOL.ui.defenseScore": "Score de Defense", + "BOL.ui.defender": "Défenseur", "BOL.featureCategory.origins": "Origines", "BOL.featureCategory.races": "Races", diff --git a/module/actor/actor.js b/module/actor/actor.js index 3941733..1805466 100644 --- a/module/actor/actor.js +++ b/module/actor/actor.js @@ -28,6 +28,10 @@ export class BoLActor extends Actor { } } + /* -------------------------------------------- */ + _onUpdate() { + this.manageHealthState() + } /* -------------------------------------------- */ get itemData(){ return Array.from(this.data.items.values()).map(i => i.data); @@ -184,6 +188,43 @@ export class BoLActor extends Actor { } }; } + + /*-------------------------------------------- */ + manageHealthState() { + if (this.data.data.resources.hp.value == 0 ) { + // TODO : Message pour depense heroisme + } + } + + /*-------------------------------------------- */ + 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} ); + } + + /*-------------------------------------------- */ + async sufferDamage( damage) { + let newHP = this.data.data.resources.hp.value - damage; + await this.update( { 'data.resources.hp.value': newHP} ); + } + + /* -------------------------------------------- */ + getArmorFormula( ) { + let protectWorn = this.protections.filter( item => item.data.worn); + let formula = "" + console.log("Protections: ", protectWorn) + for (let protect of protectWorn) { + if ( protect.data.subtype == 'helm') { + formula += "+1" + } else { + formula += "+" + protect.data.properties.soak.formula; + } + } + console.log("Protect Formula", formula) + return (formula == "") ? 0 :formula; + } + /* -------------------------------------------- */ toggleEquipItem(item) { const equipable = item.data.data.properties.equipable; diff --git a/module/bol.js b/module/bol.js index 15ea9f5..472ab36 100644 --- a/module/bol.js +++ b/module/bol.js @@ -1,70 +1,80 @@ // 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 {registerSystemSettings} from "./system/settings.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 { registerSystemSettings } from "./system/settings.js"; import registerHooks from "./system/hooks.js"; // import {DataLoader} from "./system/data.js"; -import {Macros} from "./system/macros.js"; +import { Macros } from "./system/macros.js"; +import { BoLUtility } from "./system/bol-utility.js"; Hooks.once('init', async function () { - game.bol = { - BoLActor, - BoLItem, - macros : Macros, - config:BOL - }; + game.bol = { + BoLActor, + BoLItem, + macros: Macros, + config: BOL + }; + + // Game socket + game.socket.on("system.bol", sockmsg => { + BoLUtility.onSocketMessage(sockmsg); + }); - /** - * Set an initiative formula for the system - * @type {String} - */ - CONFIG.Combat.initiative = { - formula: "2d6+@attributes.mind.value+@aptitudes.init.value", - decimals: 0 - }; -0 - // Define custom Entity classes - CONFIG.Actor.documentClass = BoLActor; - CONFIG.Item.documentClass = BoLItem; - // Register sheet application classes - Actors.unregisterSheet("core", ActorSheet); - Actors.registerSheet("bol", BoLActorSheet, {makeDefault: true}); - Items.unregisterSheet("core", ItemSheet); - Items.registerSheet("bol", BoLItemSheet, {makeDefault: true}); + /** + * Set an initiative formula for the system + * @type {String} + */ + CONFIG.Combat.initiative = { + formula: "2d6+@attributes.mind.value+@aptitudes.init.value", + decimals: 0 + }; + 0 + // Define custom Entity classes + CONFIG.Actor.documentClass = BoLActor; + CONFIG.Item.documentClass = BoLItem; - // Register System Settings - registerSystemSettings(); + // Register sheet application classes + Actors.unregisterSheet("core", ActorSheet); + Actors.registerSheet("bol", BoLActorSheet, { makeDefault: true }); + Items.unregisterSheet("core", ItemSheet); + Items.registerSheet("bol", BoLItemSheet, { makeDefault: true }); - // Preload Handlebars Templates - await preloadHandlebarsTemplates(); + // Inot useful stuff + BoLUtility.init(); + + // Register System Settings + registerSystemSettings(); - // Register Handlebars helpers - registerHandlebarsHelpers(); + // Preload Handlebars Templates + await preloadHandlebarsTemplates(); - // Register hooks - registerHooks(); + // Register Handlebars helpers + registerHandlebarsHelpers(); - // // If you need to add Handlebars helpers, here are a few useful examples: - // Handlebars.registerHelper('concat', function() { - // var outStr = ''; - // for (var arg in arguments) { - // if (typeof arguments[arg] != 'object') { - // outStr += arguments[arg]; - // } - // } - // return outStr; - // }); - // - // Handlebars.registerHelper('toLowerCase', function(str) { - // return str.toLowerCase(); - // }); + // Register hooks + registerHooks(); + + // // If you need to add Handlebars helpers, here are a few useful examples: + // Handlebars.registerHelper('concat', function() { + // var outStr = ''; + // for (var arg in arguments) { + // if (typeof arguments[arg] != 'object') { + // outStr += arguments[arg]; + // } + // } + // return outStr; + // }); + // + // Handlebars.registerHelper('toLowerCase', function(str) { + // return str.toLowerCase(); + // }); }); @@ -74,20 +84,20 @@ Hooks.once('init', async function () { Hooks.once("ready", async () => { - console.debug("Importing data"); + console.debug("Importing data"); - // DataLoader.loadData("boons"); - // DataLoader.loadData("flaws"); - // DataLoader.loadData("careers"); - // DataLoader.loadData("origins"); - // DataLoader.loadData("races"); - // DataLoader.loadData("equipment"); + // DataLoader.loadData("boons"); + // DataLoader.loadData("flaws"); + // DataLoader.loadData("careers"); + // DataLoader.loadData("origins"); + // DataLoader.loadData("races"); + // DataLoader.loadData("equipment"); - // UpdateUtils.updatePacks(); - // UpdateUtils.updatePaths(); - // UpdateUtils.updateProfiles(); - // UpdateUtils.updateSpecies(); - // UpdateUtils.updateEncounters(); + // UpdateUtils.updatePacks(); + // UpdateUtils.updatePaths(); + // UpdateUtils.updateProfiles(); + // UpdateUtils.updateSpecies(); + // UpdateUtils.updateEncounters(); - console.info("BOL | System Initialized."); + console.info("BOL | System Initialized."); }); diff --git a/module/controllers/bol-rolls.js b/module/controllers/bol-rolls.js index b72a840..b5f11b9 100644 --- a/module/controllers/bol-rolls.js +++ b/module/controllers/bol-rolls.js @@ -31,12 +31,7 @@ export class BoLRoll { // const elt = $(event.currentTarget)[0]; // let key = elt.attributes["data-rolling"].value; let target = BoLUtility.getTarget() - if ( !target) { - ui.notifications.warn("No target selected for attack !"); - return; - } const li = $(event.currentTarget).parents(".item"); - console.log("ITEM", target); const weapon = actor.items.get(li.data("item-id")); if (!weapon) { ui.notifications.warn("Unable to find weapon !"); @@ -50,7 +45,7 @@ export class BoLRoll { weapon :weapon, mod: 0, target : target, - defender: game.actors.get(target.data.actorId), + defender: (target) ? game.actors.get(target.data.actorId) : undefined, adv :dataset.adv || 0, attribute : eval(`actor.data.data.attributes.${weaponData.properties.attackAttribute}`), aptitude : eval(`actor.data.data.aptitudes.${weaponData.properties.attackAptitude}`), @@ -74,7 +69,7 @@ export class BoLRoll { boons:actorData.features.boons, flaws:actorData.features.flaws }; - console.log(dialogData.careers); + const rollOptionContent = await renderTemplate(rollOptionTpl, dialogData); let d = new Dialog({ title: label, @@ -123,8 +118,17 @@ export class BoLRoll { careers: attackDef.attackerData.features.careers, boons: attackDef.attackerData.features.boons, flaws: attackDef.attackerData.features.flaws, - defence: attackDef.defender.defenseValue, }; + if ( attackDef.defender) { + dialogData.defence = attackDef.defender.defenseValue, + dialogData.shieldBlock = 'none' + let shields = attackDef.defender.shields + for( let shield of shields) { + dialogData.shieldBlock = (shield.data.properties.blocking.blockingAll) ? 'blockall' : 'blockone'; + dialogData.shieldAttackMalus = (shield.data.properties.blocking.malus)? shield.data.properties.blocking.malus : 1; + dialogData.applyShieldMalus = false + } + } const rollOptionContent = await renderTemplate(rollOptionTpl, dialogData); let d = new Dialog({ title: attackDef.label, @@ -144,13 +148,20 @@ export class BoLRoll { const apt = html.find('#apt').val(); const adv = html.find('#adv').val(); const mod = html.find('#mod').val() || 0; + + let shieldMalus = 0; + const applyShieldMalus = html.find('#applyShieldMalus').val() || false; + if (applyShieldMalus || dialogData.shieldBlock =='blockall') { + shieldMalus = dialogData.shieldAttackMalus; + } + let careers = html.find('#career').val(); const career = (careers.length == 0) ? 0 : Math.max(...careers.map(i => parseInt(i))); const isMalus = adv < 0; const dicePool = (isMalus) ? 2 - parseInt(adv) : 2 + parseInt(adv); const attrValue = eval(`attackDef.attacker.data.data.attributes.${attr}.value`); const aptValue = eval(`attackDef.attacker.data.data.aptitudes.${apt}.value`); - const modifiers = parseInt(attrValue) + parseInt(aptValue) + parseInt(mod) + parseInt(career) - dialogData.defence; + const modifiers = parseInt(attrValue) + parseInt(aptValue) + parseInt(mod) + parseInt(career) - dialogData.defence - shieldMalus; const formula = (isMalus) ? dicePool + "d6kl2 + " + modifiers : dicePool + "d6kh2 + " + modifiers; attackDef.formula = formula; let r = new BoLAttackRoll(attackDef); @@ -270,7 +281,7 @@ export class BoLAttackRoll { async roll(){ const r = new Roll(this.attackDef.formula); - await r.roll({"async": true}); + await r.roll({"async": false}); const activeDice = r.terms[0].results.filter(r => r.active); const diceTotal = activeDice.map(r => r.result).reduce((a, b) => a + b); this._isSuccess = (r.total >= 9); @@ -290,17 +301,30 @@ export class BoLAttackRoll { let weaponFormula = BoLUtility.getDamageFormula(this.attackDef.weapon.data.data.properties.damage) let damageFormula = weaponFormula + "+" + this.attackDef.attacker.data.data.attributes[attrDamage].value; this.damageRoll = new Roll(damageFormula); - await this.damageRoll.roll({"async": true}); + await this.damageRoll.roll({"async": false}); + // Update attackDef object + this.attackDef.damageFormula = damageFormula; + this.attackDef.damageRoll = this.damageRoll; + this._buildDamageChatMessage(this.attackDef.attacker, this.attackDef.weapon, this.damageRoll.total).then(msgFlavor => { this.damageRoll.toMessage({ user: game.user.id, flavor: msgFlavor, speaker: ChatMessage.getSpeaker({actor: this.attackDef.attacker}), flags : {msgType : "default"} + }).then( result => { + if (this.attackDef.target) { + // Broadcast to GM or process it directly in case of GM defense + if ( !game.user.isGM) { + game.socket.emit("system.bol", { msg: "msg_attack_success", data: this.attackDef }); + } else { + BoLUtility.processAttackSuccess( this.attackDef); + } + } }); - }); + }); } - } + } _buildDamageChatMessage(actor, weapon, total) { const rollMessageTpl = 'systems/bol/templates/chat/rolls/damage-roll-card.hbs'; diff --git a/module/system/bol-combat.js b/module/system/bol-combat.js new file mode 100644 index 0000000..2162ed5 --- /dev/null +++ b/module/system/bol-combat.js @@ -0,0 +1,21 @@ + +export class RdDCombatManager extends Combat { + + /************************************************************************************/ + async rollInitiative(ids, formula = undefined, messageOptions = {}) { + console.log(`${game.data.system.data.title} | Combat.rollInitiative()`, ids, formula, messageOptions); + // Structure input data + ids = typeof ids === "string" ? [ids] : ids; + const currentId = this.combatant._id; + + // calculate initiative + if ( game.combat.current.round == 1) { + for (let cId = 0; cId < ids.length; cId++) { + const combatant = this.combatants.get(ids[cId]); + // TODO + console.log("TODO : Compute init for actor"); + } + } + } + +} \ No newline at end of file diff --git a/module/system/bol-utility.js b/module/system/bol-utility.js index 09c97f8..320bfed 100644 --- a/module/system/bol-utility.js +++ b/module/system/bol-utility.js @@ -1,14 +1,17 @@ -export class BoLUtility { - + +export class BoLUtility { + /* -------------------------------------------- */ static async init() { + this.attackStore = {}; + Hooks.on('renderChatLog', (log, html, data) => BoLUtility.chatListeners(html)); } - + /* -------------------------------------------- */ static async ready() { } - + /* -------------------------------------------- */ static templateData(it) { return BoLUtility.data(it)?.data ?? {} @@ -21,11 +24,11 @@ export class BoLUtility { } return it; } - + /* -------------------------------------------- */ - static createDirectOptionList( min, max) { + static createDirectOptionList(min, max) { let options = {}; - for(let i=min; i<=max; i++) { + for (let i = min; i <= max; i++) { options[`${i}`] = `${i}`; } return options; @@ -64,64 +67,165 @@ export class BoLUtility { } } } - /* -------------------------------------------- */ - static getUsers(filter) { - return game.users.filter(filter).map(user => user.data._id); - } - /* -------------------------------------------- */ - static getWhisperRecipients(rollMode, name) { - switch (rollMode) { - case "blindroll": return this.getUsers(user => user.isGM); - case "gmroll": return this.getWhisperRecipientsAndGMs(name); - case "selfroll": return [game.user.id]; - } - return undefined; - } - /* -------------------------------------------- */ - static getWhisperRecipientsAndGMs(name) { - let recep1 = ChatMessage.getWhisperRecipients(name) || []; - return recep1.concat(ChatMessage.getWhisperRecipients('GM')); - } - - /* -------------------------------------------- */ - static blindMessageToGM(chatOptions) { - let chatGM = duplicate(chatOptions); - chatGM.whisper = this.getUsers(user => user.isGM); - chatGM.content = "Blinde message of " + game.user.name + "
" + chatOptions.content; - console.log("blindMessageToGM", chatGM); - game.socket.emit("system.fvtt-fragged-kingdom", { msg: "msg_gm_chat_message", data: chatGM }); - } - /* -------------------------------------------- */ - static createChatMessage(name, rollMode, chatOptions) { - switch (rollMode) { - case "blindroll": // GM only - if (!game.user.isGM) { - this.blindMessageToGM(chatOptions); - - chatOptions.whisper = [game.user.id]; - chatOptions.content = "Message only to the GM"; - } - else { - chatOptions.whisper = this.getUsers(user => user.isGM); - } - break; - default: - chatOptions.whisper = this.getWhisperRecipients(rollMode, name); - break; - } - chatOptions.alias = chatOptions.alias || name; - ChatMessage.create(chatOptions); - } - - /* -------------------------------------------- */ - static createChatWithRollMode(name, chatOptions) { - this.createChatMessage(name, game.settings.get("core", "rollMode"), chatOptions); - } /* -------------------------------------------- */ - static isRangedWeapon( weapon) { + static getUsers(filter) { + return game.users.filter(filter).map(user => user.data._id); + } + /* -------------------------------------------- */ + static getWhisperRecipients(rollMode, name) { + switch (rollMode) { + case "blindroll": return this.getUsers(user => user.isGM); + case "gmroll": return this.getWhisperRecipientsAndGMs(name); + case "selfroll": return [game.user.id]; + } + return undefined; + } + /* -------------------------------------------- */ + static getWhisperRecipientsAndGMs(name) { + let recep1 = ChatMessage.getWhisperRecipients(name) || []; + return recep1.concat(ChatMessage.getWhisperRecipients('GM')); + } + + /* -------------------------------------------- */ + static blindMessageToGM(chatOptions) { + let chatGM = duplicate(chatOptions); + chatGM.whisper = this.getUsers(user => user.isGM); + chatGM.content = "Blind message of " + game.user.name + "
" + chatOptions.content; + console.log("blindMessageToGM", chatGM); + game.socket.emit("system.fvtt-fragged-kingdom", { msg: "msg_gm_chat_message", data: chatGM }); + } + + /* -------------------------------------------- */ + static async chatListeners(html) { + // Damage handling + html.on("click", '.damage-handling', event => { + let attackId = event.currentTarget.attributes['data-attack-id'].value; + let defenseMode = event.currentTarget.attributes['data-defense-mode'].value; + let weaponId = (event.currentTarget.attributes['data-weapon-id']) ? event.currentTarget.attributes['data-weapon-id'].value : -1 + //console.log("DEFENSE1", event.currentTarget, attackId, defenseMode, weaponId); + if ( game.user.isGM) { + BoLUtility.processDamageHandling(event, attackId, defenseMode, weaponId) + } else { + game.socket.emit("system.bol", { msg: "msg_damage_handling", data: {event: event, attackId: attackId, defenseMode: defenseMode, weaponId: weaponId} }); + } + }); + } + + /* -------------------------------------------- */ + static async processDamageHandling(event, attackId, defenseMode, weaponId=-1) { + if ( !game.user.isGM) { + return; + } + BoLUtility.removeChatMessageId(BoLUtility.findChatMessageId(event.currentTarget)); + + // Only GM process this + let attackDef = this.attackStore[attackId]; + console.log("DEFENSE2", attackId, defenseMode, weaponId, attackDef); + if (attackDef) { + attackDef.defenseMode = defenseMode; + if (defenseMode == 'damage-with-armor') { + let armorFormula = attackDef.defender.getArmorFormula(); + attackDef.rollArmor = new Roll(armorFormula) + attackDef.rollArmor.roll( {async: false} ); + attackDef.finalDamage = attackDef.damageRoll.total - attackDef.rollArmor.total; + attackDef.finalDamage = (attackDef.finalDamage<0) ? 0 : attackDef.finalDamage; + attackDef.defender.sufferDamage(attackDef.finalDamage); + } + if (defenseMode == 'damage-without-armor') { + attackDef.finalDamage = attackDef.damageRoll.total; + attackDef.defender.sufferDamage(attackDef.finalDamage); + } + if (defenseMode == 'hero-reduce-damage') { + attackDef.rollHero = new Roll("1d6"); + attackDef.rollHero.roll( {async: false} ); + attackDef.finalDamage = attackDef.damageRoll.total - attackDef.rollHero.total; + attackDef.finalDamage = (attackDef.finalDamage<0) ? 0 : attackDef.finalDamage; + attackDef.defender.sufferDamage(attackDef.finalDamage); + attackDef.defender.subHeroPoints(1); + } + if (defenseMode == 'hero-in-extremis') { + attackDef.finalDamage = 0; + attackDef.weaponHero = attackDef.defender.weapons.find(item => item._id == weaponId); + attackDef.defender.deleteEmbeddedDocuments("Item", [ weaponId ]); + } + ChatMessage.create({ + alias: attackDef.defender.name, + whisper: BoLUtility.getWhisperRecipientsAndGMs(attackDef.defender.name), + content: await renderTemplate('systems/bol/templates/chat/rolls/defense-result-card.hbs', { + attackId: attackDef.id, + attacker: attackDef.attacker, + rollArmor: attackDef.rollArmor, + rollHero: attackDef.rollHero, + weaponHero : attackDef.weaponHero, + defender: attackDef.defender, + defenseMode: attackDef.defenseMode, + finalDamage: attackDef.finalDamage + }) + }) + } + } + + /* -------------------------------------------- */ + static createChatMessage(name, rollMode, chatOptions) { + switch (rollMode) { + case "blindroll": // GM only + if (!game.user.isGM) { + this.blindMessageToGM(chatOptions); + + chatOptions.whisper = [game.user.id]; + chatOptions.content = "Message only to the GM"; + } + else { + chatOptions.whisper = this.getUsers(user => user.isGM); + } + break; + default: + chatOptions.whisper = this.getWhisperRecipients(rollMode, name); + break; + } + chatOptions.alias = chatOptions.alias || name; + ChatMessage.create(chatOptions); + } + + /* -------------------------------------------- */ + static createChatWithRollMode(name, chatOptions) { + this.createChatMessage(name, game.settings.get("core", "rollMode"), chatOptions); + } + /* -------------------------------------------- */ + static isRangedWeapon(weapon) { return weapon.data.type == 'ranged' || weapon.data.thrown; } + /* -------------------------------------------- */ + + static removeChatMessageId(messageId) { + if (messageId){ + game.messages.get(messageId)?.delete(); + } + } + + static findChatMessageId(current) { + return BoLUtility.getChatMessageId(BoLUtility.findChatMessage(current)); + } + + static getChatMessageId(node) { + return node?.attributes.getNamedItem('data-message-id')?.value; + } + + static findChatMessage(current) { + return BoLUtility.findNodeMatching(current, it => it.classList.contains('chat-message') && it.attributes.getNamedItem('data-message-id')); + } + + static findNodeMatching(current, predicate) { + if (current) { + if (predicate(current)) { + return current; + } + return BoLUtility.findNodeMatching(current.parentElement, predicate); + } + return undefined; + } + /* -------------------------------------------- */ static getTarget() { if (game.user.targets && game.user.targets.size == 1) { @@ -131,75 +235,107 @@ export class BoLUtility { } return undefined; } - + /* -------------------------------------------- */ - static async rollBoL( rollData ) { + static async rollBoL(rollData) { // Dice bonus/malus selection let nbDice = 2; let d6BM = 0; let mode = ""; - if ( rollData.d6Malus > rollData.d6Bonus){ + if (rollData.d6Malus > rollData.d6Bonus) { d6BM = rollData.d6Malus - rollData.d6Bonus; mode = "kl2"; } - if ( rollData.d6Bonus > rollData.d6Malus){ + if (rollData.d6Bonus > rollData.d6Malus) { d6BM = rollData.d6Bonus - rollData.d6Malus; mode = "kh2"; } - nbDice += d6BM; + nbDice += d6BM; // Final modifier let modifier = Number(rollData.bonusMalus); - if ( rollData.mode == 'career') { + if (rollData.mode == 'career') { modifier += Number(rollData.attributes[rollData.rollAttribute].value) + Number(rollData.career.data.rank); - } else if ( rollData.mode == 'attribute' ) { + } else if (rollData.mode == 'attribute') { modifier += rollData.attribute.value; - } else if ( rollData.mode == 'weapon') { + } else if (rollData.mode == 'weapon') { modifier += Number(rollData.attributes[rollData.rollAttribute].value) + Number(rollData.aptitude.value) + Number(rollData.rangeModifier); modifier -= rollData.defender.data.aptitudes.def.value; } - let formula = nbDice+"d6"+mode+"+"+modifier; + let formula = nbDice + "d6" + mode + "+" + modifier; console.log("Going to roll ", formula, rollData.attributes, rollData.rollAttribute); - let myRoll = new Roll(formula).roll( { async: false}); - await this.showDiceSoNice(myRoll, game.settings.get("core", "rollMode") ); + let myRoll = new Roll(formula).roll({ async: false }); + await this.showDiceSoNice(myRoll, game.settings.get("core", "rollMode")); rollData.roll = myRoll; rollData.formula = formula; - rollData.modifier = modifier; + rollData.modifier = modifier; rollData.nbDice = nbDice; rollData.finalScore = myRoll.total; let actor = game.actors.get(rollData.actorId); - actor.saveRollData( rollData ); - - this.createChatWithRollMode( rollData.alias, { + actor.saveRollData(rollData); + + this.createChatWithRollMode(rollData.alias, { content: await renderTemplate(`systems/bol/templates/chat/chat-generic-result.hbs`, rollData) }); - } + } /* -------------------------------------------- */ - static getDamageFormula( damageString) { - if (damageString[0] == 'd') {damageString = "1" + damageString} // Help parsing + static async processAttackSuccess(attackDef) { + if (!game.user.isGM) { // Only GM process this + return; + } + // Build and send the defense message to the relevant people (ie GM + defender) + let defenderWeapons = attackDef.defender.weapons; + console.log("DEF WEP", attackDef) + this.attackStore[attackDef.id] = attackDef; // Store ! + ChatMessage.create({ + alias: attackDef.defender.name, + whisper: BoLUtility.getWhisperRecipientsAndGMs(attackDef.defender.name), + content: await renderTemplate('systems/bol/templates/chat/rolls/defense-request-card.hbs', { + attackId: attackDef.id, + attacker: attackDef.attacker, + defender: attackDef.defender, + defenderWeapons: defenderWeapons, + damageTotal: attackDef.damageRoll.total + }) + }); + } + + /* -------------------------------------------- */ + static onSocketMessage(sockmsg) { + if (sockmsg.name == "msg_attack_success") { + BoLUtility.processAttackSuccess(sockmsg.data); + } + if (sockmsg.name == "msg_damage_handling") { + BoLUtility.processDamageHandling(sockmsg.data.event, sockmsg.data.attackId, sockmsg.data.defenseMode) + } + } + + /* -------------------------------------------- */ + static getDamageFormula(damageString) { + if (damageString[0] == 'd') { damageString = "1" + damageString } // Help parsing var myReg = new RegExp('(\\d+)[dD]([\\d]+)([MB]*)?([\\+\\d]*)?', 'g'); let res = myReg.exec(damageString); let nbDice = parseInt(res[1]); - let postForm = 'kh'+nbDice; + let postForm = 'kh' + nbDice; let modIndex = 3; - if ( res[3]) { - if ( res[3] == 'M') { - postForm = 'kl'+nbDice; + if (res[3]) { + if (res[3] == 'M') { + postForm = 'kl' + nbDice; nbDice++; modIndex = 4; } - if ( res[3] == 'B') { - postForm = 'kh'+nbDice; + if (res[3] == 'B') { + postForm = 'kh' + nbDice; nbDice++; modIndex = 4; } } - let formula = nbDice+"d"+res[2] + postForm + ((res[modIndex]) ? res[modIndex] : ""); + let formula = nbDice + "d" + res[2] + postForm + ((res[modIndex]) ? res[modIndex] : ""); return formula; } @@ -209,26 +345,26 @@ export class BoLUtility { let msgTxt = "

Are you sure to remove this Item ?"; let buttons = { delete: { - icon: '', - label: "Yes, remove it", - callback: () => { - actorSheet.actor.deleteEmbeddedDocuments( "Item", [itemId] ); - li.slideUp(200, () => actorSheet.render(false)); - } - }, - cancel: { - icon: '', - label: "Cancel" + icon: '', + label: "Yes, remove it", + callback: () => { + actorSheet.actor.deleteEmbeddedDocuments("Item", [itemId]); + li.slideUp(200, () => actorSheet.render(false)); } + }, + cancel: { + icon: '', + label: "Cancel" } - msgTxt += "

"; - let d = new Dialog({ - title: "Confirm removal", - content: msgTxt, - buttons: buttons, - default: "cancel" - }); - d.render(true); + } + msgTxt += "

"; + let d = new Dialog({ + title: "Confirm removal", + content: msgTxt, + buttons: buttons, + default: "cancel" + }); + d.render(true); } } diff --git a/system.json b/system.json index 10611a0..d5fefc5 100644 --- a/system.json +++ b/system.json @@ -2,14 +2,21 @@ "name": "bol", "title": "Barbarians of Lemuria", "description": "The Barbarians of Lemuria system for FoundryVTT!", + "author": "Zigmund", + "authors": [], + "url": "https://github.com/ZigmundKreud/bol", + "license": "LICENSE.txt", + "flags": {}, "version": "0.8.9.0", "minimumCoreVersion": "0.8.6", - "compatibleCoreVersion": "0.8.9", - "templateVersion": 8, - "author": "Zigmund", - "esmodules": ["module/bol.js"], - "styles": ["css/bol.css"], + "compatibleCoreVersion": "9", "scripts": [], + "esmodules": [ + "module/bol.js" + ], + "styles": [ + "css/bol.css" + ], "languages": [ { "lang": "en", @@ -29,7 +36,9 @@ "system": "bol", "path": "./packs/boons.db", "entity": "Item", - "tag": "boon" + "tag": "boon", + "type": "Item", + "private": false }, { "name": "flaws", @@ -37,7 +46,9 @@ "system": "bol", "path": "./packs/flaws.db", "entity": "Item", - "tag": "flaw" + "tag": "flaw", + "type": "Item", + "private": false }, { "name": "careers", @@ -45,7 +56,9 @@ "system": "bol", "path": "./packs/careers.db", "entity": "Item", - "tag": "career" + "tag": "career", + "type": "Item", + "private": false }, { "name": "origins", @@ -53,7 +66,9 @@ "system": "bol", "path": "./packs/origins.db", "entity": "Item", - "tag": "origin" + "tag": "origin", + "type": "Item", + "private": false }, { "name": "races", @@ -61,7 +76,9 @@ "system": "bol", "path": "./packs/races.db", "entity": "Item", - "tag": "race" + "tag": "race", + "type": "Item", + "private": false }, { "name": "equipment", @@ -69,16 +86,20 @@ "system": "bol", "path": "./packs/equipment.db", "entity": "Item", - "tag": "item" + "tag": "item", + "type": "Item", + "private": false } ], + "system": [], + "dependencies": [], + "socket": true, + "manifest": "https://raw.githubusercontent.com/ZigmundKreud/bol/master/system.json", + "download": "https://github.com/ZigmundKreud/bol/archive/refs/heads/master.zip", + "protected": false, "background": "ui/splash-page.webp", "gridDistance": 1.5, "gridUnits": "m", "primaryTokenAttribute": "resources.hp", - "secondaryTokenAttribute": "resources.hero", - "url": "https://github.com/ZigmundKreud/bol", - "manifest": "https://raw.githubusercontent.com/ZigmundKreud/bol/master/system.json", - "download": "https://github.com/ZigmundKreud/bol/archive/refs/heads/master.zip", - "license": "LICENSE.txt" + "secondaryTokenAttribute": "resources.hero" } \ No newline at end of file diff --git a/templates/chat/rolls/defense-request-card.hbs b/templates/chat/rolls/defense-request-card.hbs new file mode 100644 index 0000000..01b42c3 --- /dev/null +++ b/templates/chat/rolls/defense-request-card.hbs @@ -0,0 +1,14 @@ +{{defender.name}} +Va encaisser {{damageTotal}} dégats ! + + + + +{{#if defender.data.data.resources.hero.value}} + + +{{#each defenderWeapons as |weapon idx|}} + +{{/each}} + +{{/if}} \ No newline at end of file diff --git a/templates/chat/rolls/defense-result-card.hbs b/templates/chat/rolls/defense-result-card.hbs new file mode 100644 index 0000000..69807bd --- /dev/null +++ b/templates/chat/rolls/defense-result-card.hbs @@ -0,0 +1,22 @@ +{{defender.name}} +

Dégats subis par {{defender.name}}

+ diff --git a/templates/dialogs/weapon-roll-dialog.hbs b/templates/dialogs/weapon-roll-dialog.hbs index c66e99a..036168e 100644 --- a/templates/dialogs/weapon-roll-dialog.hbs +++ b/templates/dialogs/weapon-roll-dialog.hbs @@ -55,6 +55,26 @@
{{defence}}
+ {{#if (eq shieldBlock 'blockall')}} +
+
+ +
+
{{shieldAttackMalus}}
+
+ {{/if}} + {{#if (eq shieldBlock 'blockone')}} +
+
+ +
+
+ +
+
+ {{/if}} {{#if careers.items}}
diff --git a/templates/roll/roll-dialog-weapon.hbs b/templates/roll/roll-dialog-weapon.hbs index 5e19670..b9278d2 100644 --- a/templates/roll/roll-dialog-weapon.hbs +++ b/templates/roll/roll-dialog-weapon.hbs @@ -1,11 +1,11 @@
{{#if defender}}
- +
- +
{{/if}}