From 420390eed719991d2857ac25e09e27bf9a4e9c3f Mon Sep 17 00:00:00 2001
From: sladecraven
Date: Wed, 29 Dec 2021 19:15:06 +0100
Subject: [PATCH 1/2] Attack process path
---
module/actor/actor.js | 41 +++
module/bol.js | 144 ++++----
module/controllers/bol-rolls.js | 30 +-
module/system/bol-combat.js | 21 ++
module/system/bol-utility.js | 344 ++++++++++++------
system.json | 51 ++-
templates/chat/rolls/defense-request-card.hbs | 14 +
templates/chat/rolls/defense-result-card.hbs | 22 ++
8 files changed, 470 insertions(+), 197 deletions(-)
create mode 100644 module/system/bol-combat.js
create mode 100644 templates/chat/rolls/defense-request-card.hbs
create mode 100644 templates/chat/rolls/defense-result-card.hbs
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..cee4274 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}`),
@@ -123,7 +118,7 @@ export class BoLRoll {
careers: attackDef.attackerData.features.careers,
boons: attackDef.attackerData.features.boons,
flaws: attackDef.attackerData.features.flaws,
- defence: attackDef.defender.defenseValue,
+ defence: (attackDef.defender) ? attackDef.defender.defenseValue : 0,
};
const rollOptionContent = await renderTemplate(rollOptionTpl, dialogData);
let d = new Dialog({
@@ -270,7 +265,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 +285,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..1192c7d 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"],
"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 !
+
+Encaisser avec la protection de l'armure
+Encaisser sans la protection de l'armure
+
+{{#if defender.data.data.resources.hero.value}}
+Juste une égratignure (1 Point d'Héroisme)
+
+{{#each defenderWeapons as |weapon idx|}}
+Parade in Extremis avec {{weapon.name}} (1 Point d'Héroisme)
+{{/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 @@
+
+Dégats subis par {{defender.name}}
+
+
+ {{#if (eq defenseMode "damage-with-armor")}}
+ Protection de l'armure : {{rollArmor.total}}.
+ {{/if}}
+ {{#if (eq defenseMode "damage-without-armor")}}
+ Aucune protection d'armure !
+ {{/if}}
+ {{#if (eq defenseMode "hero-reduce-damage")}}
+ Un point d'héroisme dépensé, pour une réduction des dommages de {{rollHero.total}}.
+ {{/if}}
+ {{#if (eq defenseMode "hero-in-extremis")}}
+ Aucun dommage encaissé, grâce à la parade in-extremis avec {{weaponHero.name}}. L'arme a été détruite pendant cette parade !
+ Un point d'héroisme a également été dépensé.
+ {{/if}}
+
+
+Encaissement final : {{finalDamage}} dégats !
+
+
--
2.35.3
From ad205f4669ce89121d47865301f9f861fe0d8e9d Mon Sep 17 00:00:00 2001
From: sladecraven
Date: Wed, 29 Dec 2021 20:33:59 +0100
Subject: [PATCH 2/2] Attack process path
---
lang/en.json | 6 +++++-
lang/fr.json | 4 ++++
module/controllers/bol-rolls.js | 22 +++++++++++++++++++---
system.json | 2 +-
templates/dialogs/weapon-roll-dialog.hbs | 20 ++++++++++++++++++++
templates/roll/roll-dialog-weapon.hbs | 4 ++--
6 files changed, 51 insertions(+), 7 deletions(-)
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/controllers/bol-rolls.js b/module/controllers/bol-rolls.js
index cee4274..b5f11b9 100644
--- a/module/controllers/bol-rolls.js
+++ b/module/controllers/bol-rolls.js
@@ -69,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,
@@ -118,8 +118,17 @@ export class BoLRoll {
careers: attackDef.attackerData.features.careers,
boons: attackDef.attackerData.features.boons,
flaws: attackDef.attackerData.features.flaws,
- defence: (attackDef.defender) ? attackDef.defender.defenseValue : 0,
};
+ 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,
@@ -139,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);
diff --git a/system.json b/system.json
index 1192c7d..d5fefc5 100644
--- a/system.json
+++ b/system.json
@@ -9,7 +9,7 @@
"flags": {},
"version": "0.8.9.0",
"minimumCoreVersion": "0.8.6",
- "compatibleCoreVersion": "0.8.9",
+ "compatibleCoreVersion": "9",
"scripts": [],
"esmodules": [
"module/bol.js"
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')}}
+
+
+ {{localize 'BOL.ui.shieldMalus'}}
+
+
{{shieldAttackMalus}}
+
+ {{/if}}
+ {{#if (eq shieldBlock 'blockone')}}
+
+
+ {{localize 'BOL.ui.shieldMalus'}}
+
+
+
+ {{localize "BOL.ui.applyShieldMalus"}}
+
+
+
+ {{/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 @@