diff --git a/lang/en.json b/lang/en.json
index 9e34162d..c6fbfa60 100644
--- a/lang/en.json
+++ b/lang/en.json
@@ -92,7 +92,9 @@
"BOL.ui.treasure" : "Trésor",
"BOL.ui.vehicles" : "Véhicules/Montures",
"BOL.ui.misc" : "Divers",
-
+ "BOL.ui.noWeaponName" : "Unknown Weapon",
+ "BOL.ui.targetDefence": "Defence",
+
"BOL.featureCategory.origins": "Origines",
"BOL.featureCategory.races": "Races",
"BOL.featureCategory.careers": "Carrières",
diff --git a/lang/fr.json b/lang/fr.json
index b7a7dd7f..9a150e93 100644
--- a/lang/fr.json
+++ b/lang/fr.json
@@ -94,6 +94,8 @@
"BOL.ui.misc" : "Divers",
"BOL.ui.vehicleProperties" : " Propriétés de véhicule",
"BOL.ui.speed" : "Vitesse",
+ "BOL.ui.noWeaponName" : "Arme Inconnue",
+ "BOL.ui.targetDefence": "Défense",
"BOL.featureCategory.origins": "Origines",
"BOL.featureCategory.races": "Races",
diff --git a/module/actor/actor-sheet.js b/module/actor/actor-sheet.js
index eb30cf3a..384bc5c2 100644
--- a/module/actor/actor-sheet.js
+++ b/module/actor/actor-sheet.js
@@ -186,7 +186,7 @@ export class BoLActorSheet extends ActorSheet {
BoLRoll.aptitudeCheck(this.actor, actorData, dataset, event);
break;
case "weapon":
- console.log("ROLL WEAPON !!!"); // TODO
+ BoLRoll.weaponCheck(this.actor, actorData, dataset, event);
break;
default : break;
}
diff --git a/module/actor/actor.js b/module/actor/actor.js
index c6f617e0..39417337 100644
--- a/module/actor/actor.js
+++ b/module/actor/actor.js
@@ -41,6 +41,9 @@ export class BoLActor extends Actor {
get aptitudes() {
return Object.values(this.data.data.aptitudes);
}
+ get defenseValue() {
+ return this.data.data.aptitudes.def.value;
+ }
get resources() {
return Object.values(this.data.data.resources);
}
@@ -84,7 +87,6 @@ export class BoLActor extends Actor {
get protections() {
return this.armors.concat(this.helms).concat(this.shields)
}
-
get melee() {
return this.weapons.filter(i => i.data.properties.melee === true);
}
diff --git a/module/controllers/bol-rolls.js b/module/controllers/bol-rolls.js
index c640a9b3..b72a8408 100644
--- a/module/controllers/bol-rolls.js
+++ b/module/controllers/bol-rolls.js
@@ -1,3 +1,5 @@
+import { BoLUtility } from "../system/bol-utility.js";
+
export class BoLRoll {
static options() {
return { classes: ["bol", "dialog"] };
@@ -25,6 +27,40 @@ export class BoLRoll {
return this.aptitudeRollDialog(actor, actorData, aptitude, label, description, adv, 0);
}
+ static weaponCheck(actor, actorData, dataset, event) {
+ // 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 !");
+ return;
+ }
+ let weaponData = weapon.data.data;
+ let attackDef= {
+ id:randomID(16),
+ attacker: actor,
+ attackerData: actorData,
+ weapon :weapon,
+ mod: 0,
+ target : target,
+ defender: game.actors.get(target.data.actorId),
+ adv :dataset.adv || 0,
+ attribute : eval(`actor.data.data.attributes.${weaponData.properties.attackAttribute}`),
+ aptitude : eval(`actor.data.data.aptitudes.${weaponData.properties.attackAptitude}`),
+ label : (weapon.name) ? weapon.name : game.i18n.localize('BOL.ui.noWeaponName'),
+ description : actor.name + " - " + game.i18n.localize('BOL.ui.weaponAttack'),
+ }
+ console.log("WEAPON!", attackDef, weaponData);
+ return this.weaponRollDialog(attackDef);
+ }
+
/* -------------------------------------------- */
/* ROLL DIALOGS */
/* -------------------------------------------- */
@@ -58,7 +94,7 @@ export class BoLRoll {
const adv = html.find('#adv').val();
const mod = html.find('#mod').val();
let careers = html.find('#career').val();
- const career = (!careers) ? 0 : Math.max(...careers.map(i => parseInt(i)));
+ 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(`actor.data.data.attributes.${attr}.value`);
@@ -75,6 +111,59 @@ export class BoLRoll {
return d.render(true);
}
+ static async weaponRollDialog( attackDef) {
+ const rollOptionTpl = 'systems/bol/templates/dialogs/weapon-roll-dialog.hbs';
+ const dialogData = {
+ attr:attackDef.attribute,
+ adv:attackDef.adv,
+ mod: attackDef.mod,
+ apt:attackDef.aptitude,
+ weapon: attackDef.weapon,
+ attackId: attackDef.id,
+ careers: attackDef.attackerData.features.careers,
+ boons: attackDef.attackerData.features.boons,
+ flaws: attackDef.attackerData.features.flaws,
+ defence: attackDef.defender.defenseValue,
+ };
+ const rollOptionContent = await renderTemplate(rollOptionTpl, dialogData);
+ let d = new Dialog({
+ title: attackDef.label,
+ content: rollOptionContent,
+ buttons: {
+ cancel: {
+ icon: '',
+ label: game.i18n.localize("BOL.ui.cancel"),
+ callback: () => {
+ }
+ },
+ submit: {
+ icon: '',
+ label: game.i18n.localize("BOL.ui.submit"),
+ callback: (html) => {
+ const attr = html.find('#attr').val();
+ const apt = html.find('#apt').val();
+ const adv = html.find('#adv').val();
+ const mod = html.find('#mod').val() || 0;
+ 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 formula = (isMalus) ? dicePool + "d6kl2 + " + modifiers : dicePool + "d6kh2 + " + modifiers;
+ attackDef.formula = formula;
+ let r = new BoLAttackRoll(attackDef);
+ r.roll();
+ }
+ }
+ },
+ default: 'submit',
+ close: () => {}
+ }, this.options());
+ return d.render(true);
+ }
+
static async aptitudeRollDialog(actor, actorData, aptitude, label, description, adv, mod, onEnter = "submit") {
const rollOptionTpl = 'systems/bol/templates/dialogs/aptitude-roll-dialog.hbs';
const dialogData = {
@@ -104,7 +193,7 @@ export class BoLRoll {
const adv = html.find('#adv').val();
const mod = html.find('#mod').val();
let careers = html.find('#career').val();
- const career = (!careers) ? 0 : Math.max(...careers.map(i => parseInt(i)));
+ 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 aptValue = eval(`actor.data.data.aptitudes.${apt}.value`);
@@ -123,12 +212,13 @@ export class BoLRoll {
}
export class BoLDefaultRoll {
- constructor(label, formula, description){
+ constructor(label, formula, description, isWeapon=false){
this._label = label;
this._formula = formula;
this._isSuccess = false;
this._isCritical = false;
this._isFumble = false;
+ this._isWeapon = isWeapon;
this._description = description;
}
@@ -148,6 +238,9 @@ export class BoLDefaultRoll {
flags : {msgType : "default"}
});
});
+ if (this._isSuccess && this._isWeapon) {
+
+ }
}
_buildChatMessage(actor) {
@@ -167,6 +260,75 @@ export class BoLDefaultRoll {
}
+export class BoLAttackRoll {
+ constructor(attackDef){
+ this.attackDef = attackDef;
+ this._isSuccess = false;
+ this._isCritical = false;
+ this._isFumble = false;
+}
+
+ async roll(){
+ const r = new Roll(this.attackDef.formula);
+ await r.roll({"async": true});
+ 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);
+ this._isCritical = (diceTotal === 12);
+ this._isFumble = (diceTotal === 2);
+ this._buildChatMessage(this.attackDef.attacker).then(msgFlavor => {
+ r.toMessage({
+ user: game.user.id,
+ flavor: msgFlavor,
+ speaker: ChatMessage.getSpeaker({actor: this.attackDef.attacker}),
+ flags : {msgType : "default"}
+ });
+ });
+
+ if (this._isSuccess ) {
+ let attrDamage = this.attackDef.weapon.data.data.properties.damageAttribute;
+ 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});
+ 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"}
+ });
+ });
+ }
+ }
+
+ _buildDamageChatMessage(actor, weapon, total) {
+ const rollMessageTpl = 'systems/bol/templates/chat/rolls/damage-roll-card.hbs';
+ const tplData = {
+ actor : actor,
+ label : this._label,
+ weapon: weapon,
+ damage: total,
+ };
+ return renderTemplate(rollMessageTpl, tplData);
+}
+
+ _buildChatMessage(actor) {
+ const rollMessageTpl = 'systems/bol/templates/chat/rolls/default-roll-card.hbs';
+ const tplData = {
+ actor : actor,
+ label : this._label,
+ isSuccess : this._isSuccess,
+ isFailure : !this._isSuccess,
+ isCritical : this._isCritical,
+ isFumble : this._isFumble,
+ hasDescription : this._description && this._description.length > 0,
+ description : this._description
+ };
+ return renderTemplate(rollMessageTpl, tplData);
+ }
+
+}
// export class BoLWeaponRoll {
// constructor(actor, label, formula, isCritical, description){
// this._label = label;
diff --git a/module/system/bol-utility.js b/module/system/bol-utility.js
index da69aa9a..09c97f84 100644
--- a/module/system/bol-utility.js
+++ b/module/system/bol-utility.js
@@ -179,6 +179,30 @@ export class BoLUtility {
});
}
+ /* -------------------------------------------- */
+ 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 modIndex = 3;
+ if ( res[3]) {
+ if ( res[3] == 'M') {
+ postForm = 'kl'+nbDice;
+ nbDice++;
+ modIndex = 4;
+ }
+ if ( res[3] == 'B') {
+ postForm = 'kh'+nbDice;
+ nbDice++;
+ modIndex = 4;
+ }
+ }
+ let formula = nbDice+"d"+res[2] + postForm + ((res[modIndex]) ? res[modIndex] : "");
+ return formula;
+ }
+
/* -------------------------------------------- */
static async confirmDelete(actorSheet, li) {
let itemId = li.data("item-id");
diff --git a/templates/chat/rolls/damage-roll-card.hbs b/templates/chat/rolls/damage-roll-card.hbs
new file mode 100644
index 00000000..4d6cb4c8
--- /dev/null
+++ b/templates/chat/rolls/damage-roll-card.hbs
@@ -0,0 +1,7 @@
+
+