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 @@ + +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 @@ + +