Add new features

This commit is contained in:
sladecraven 2022-03-27 22:56:43 +02:00
parent 9e5e07b227
commit 38025666e9
16 changed files with 5331 additions and 173 deletions

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 372 KiB

View File

@ -14,6 +14,7 @@ export class BoLActorSheet extends ActorSheet {
template: "systems/bol/templates/actor/actor-sheet.hbs",
width: 600,
height: 600,
dragDrop: [{ dragSelector: ".items-list .item", dropSelector: null }],
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "stats" }]
});
}

View File

@ -304,23 +304,17 @@ export class BoLActor extends Actor {
/*-------------------------------------------- */
getArmorAgiMalus() {
let malusAgi = 0
for (let armor of this.armors) {
for (let armor of this.protections) {
if (armor.data.worn) {
malusAgi += Number(armor.data.properties.modifiers.agility) || 0
}
}
for (let shield of this.shields) {
if (shield.data.worn) {
malusAgi += Number(shield.data.properties.modifiers.agility) || 0
}
}
return malusAgi
}
/*-------------------------------------------- */
getArmorInitMalus() {
let armors = this.armors
let malusInit = 0
for (let armor of armors) {
for (let armor of this.protections) {
if (armor.data.worn) {
malusInit += Number(armor.data.properties.modifiers.init) || 0
}

View File

@ -12,6 +12,7 @@ import { Macros } from "./system/macros.js"
import { BoLUtility } from "./system/bol-utility.js"
import { BoLCombatManager } from "./system/bol-combat.js"
import { BoLTokenHud } from "./system/bol-action-hud.js"
import { BoLHotbar } from "./system/bol-hotbar.js"
/* -------------------------------------------- */
Hooks.once('init', async function () {
@ -19,6 +20,7 @@ Hooks.once('init', async function () {
game.bol = {
BoLActor,
BoLItem,
BoLHotbar,
macros: Macros,
config: BOL
};
@ -52,6 +54,7 @@ Hooks.once('init', async function () {
// Inot useful stuff
BoLUtility.init()
BoLTokenHud.init()
BoLHotbar.init()
// Preload Handlebars Templates
await preloadHandlebarsTemplates();

View File

@ -32,7 +32,7 @@ export class BoLRoll {
careerBonus: 0,
description: description,
armorAgiMalus: actor.getArmorAgiMalus(),
armorInitMalus: actor.getArmorAgiMalus(),
armorInitMalus: actor.getArmorInitMalus(),
mod: 0
}
return this.displayRollDialog(rollData)
@ -56,12 +56,12 @@ export class BoLRoll {
attrValue: attribute.value,
aptValue: aptitude.value,
armorAgiMalus: actor.getArmorAgiMalus(),
armorInitMalus: actor.getArmorAgiMalus(),
armorInitMalus: actor.getArmorInitMalus(),
label: label,
careerBonus: 0,
description: description,
mod: 0
});
})
}
/* -------------------------------------------- */
@ -94,7 +94,7 @@ export class BoLRoll {
attrValue: attribute.value,
aptValue: aptitude.value,
armorAgiMalus: actor.getArmorAgiMalus(),
armorInitMalus: actor.getArmorAgiMalus(),
armorInitMalus: actor.getArmorInitMalus(),
mod: 0,
modRanged: 0,
label: (weapon.name) ? weapon.name : game.i18n.localize('BOL.ui.noWeaponName'),
@ -139,7 +139,7 @@ export class BoLRoll {
pcCostCurrent: Number(alchemyData.properties.pccurrent),
mod: Number(alchemyData.properties.difficulty),
armorAgiMalus: actor.getArmorAgiMalus(),
armorInitMalus: actor.getArmorAgiMalus(),
armorInitMalus: actor.getArmorInitMalus(),
label: alchemy.name,
description: game.i18n.localize('BOL.ui.makeAlchemy') + "+" + alchemy.name,
}
@ -147,20 +147,9 @@ export class BoLRoll {
return this.displayRollDialog(alchemyDef);
}
/* -------------------------------------------- */
static spellCheck(actor, event) {
if (actor.data.data.resources.power.value <= 0) {
ui.notifications.warn("Plus assez de points de Pouvoir !")
return
}
const li = $(event.currentTarget).parents(".item");
const spell = actor.items.get(li.data("item-id"));
if (!spell) {
ui.notifications.warn("Unable to find spell !");
return;
}
let spellData = spell.data.data;
static spellCheckWithSpell( actor, spell ) {
let spellData = spell.data.data
let spellDef = {
mode: "spell",
actor: actor,
@ -174,12 +163,27 @@ export class BoLRoll {
ppCost: Number(spell.data.data.properties.ppcost),
mod: Number(spellData.properties.difficulty),
armorAgiMalus: actor.getArmorAgiMalus(),
armorInitMalus: actor.getArmorAgiMalus(),
armorInitMalus: actor.getArmorInitMalus(),
label: spell.name,
description: game.i18n.localize('BOL.ui.focusSpell') + " : " + spell.name,
}
console.log("SPELL!", spellDef);
return this.displayRollDialog(spellDef);
console.log("SPELL!", spellDef)
return this.displayRollDialog(spellDef)
}
/* -------------------------------------------- */
static spellCheck(actor, event) {
if (actor.data.data.resources.power.value <= 0) {
ui.notifications.warn("Plus assez de points de Pouvoir !")
return
}
const li = $(event.currentTarget).parents(".item")
const spell = actor.items.get(li.data("item-id"))
if (!spell) {
ui.notifications.warn("Impossible de trouver ce sort !")
return
}
return this.spellCheckWithSpell( actor, spell)
}
/* -------------------------------------------- */
@ -347,6 +351,7 @@ export class BoLRoll {
rollData.careers = rollData.actor.careers
rollData.boons = rollData.actor.bonusBoons
rollData.flaws = rollData.actor.malusFlaws
rollData.rollOwnerID = rollData.actor.id
rollData.defence = 0
rollData.attackModifier = 0 // Used for fight options
rollData.modArmorMalus = 0 // Used for fight options
@ -455,6 +460,8 @@ export class BoLDefaultRoll {
this.rollData.isSuccess = (r.total >= 9)
this.rollData.isCritical = (diceTotal === 12)
this.rollData.isRealCritical = (diceTotal === 12)
this.rollData.isHeroic = (diceTotal === 12)
this.rollData.isLegendary = false
this.rollData.isFumble = (diceTotal === 2)
this.rollData.isFailure = !this.rollData.isSuccess
if (this.rollData.reroll == undefined) {
@ -479,26 +486,41 @@ export class BoLDefaultRoll {
this._buildChatMessage(this.rollData).then(msgFlavor => {
this.rollData.roll.toMessage({
user: game.user.id,
rollMode: game.settings.get("core", "rollMode"),
//whisper: BoLUtility.getWhisperRecipientsAndGMs(this.rollData.actor.name),
flavor: msgFlavor,
speaker: ChatMessage.getSpeaker({ actor: this.rollData.actor }),
flags: { msgType: "default" }
})
});
})
}
/* -------------------------------------------- */
upgradeToCritical() {
upgradeToLegendary() {
// Force to Critical roll
this.rollData.isCritical = true
this.rollData.isLegendary = true
this.rollData.isRealCritical = false
this.rollData.isSuccess = true
this.rollData.isFailure = false
this.rollData.reroll = false
this.rollData.roll = new Roll("12+" + this.rollData.modifiers)
this.rollData.reroll = false
this.sendChatMessage()
}
/* -------------------------------------------- */
upgradeToHeroic() {
// Force to Critical roll
this.rollData.isCritical = true
this.rollData.isHeroic = true
this.rollData.isLegendary = false
this.rollData.isRealCritical = false
this.rollData.isSuccess = true
this.rollData.isFailure = false
this.rollData.roll = new Roll("12+" + this.rollData.modifiers)
this.rollData.reroll = false
this.sendChatMessage()
}
/* -------------------------------------------- */
setSuccess(flag) {
this.rollData.isSuccess = flag
@ -510,7 +532,7 @@ export class BoLDefaultRoll {
this.rollData.damageRoll.toMessage({
user: game.user.id,
flavor: msgFlavor,
speaker: ChatMessage.getSpeaker({ actor: this.rollData.actor }),
speaker: ChatMessage.getSpeaker({ actor: this.rollData.actors }),
flags: { msgType: "default" }
})
});

View File

@ -12,7 +12,7 @@ export class BoLItemSheet extends ItemSheet {
classes: ["bol", "sheet", "item"],
template: "systems/bol/templates/item/item-sheet.hbs",
width: 650,
height: 750,
height: 780,
tabs: [{ navSelector: ".sheet-tabs", contentSelector: ".sheet-body", initial: "description" }]
});
}

View File

@ -0,0 +1,87 @@
import { BoLRoll } from "../controllers/bol-rolls.js";
export class BoLHotbar {
/**
* Create a macro when dropping an entity on the hotbar
* Item - open roll dialog for item
* Actor - open actor sheet
* Journal - open journal sheet
*/
static init( ) {
Hooks.on("hotbarDrop", async (bar, documentData, slot) => {
// Create item macro if rollable item - weapon, spell, prayer, trait, or skill
if (documentData.type == "Item") {
console.log("Drop done !!!", bar, documentData, slot)
let item = documentData.data
let command = `game.bol.BoLHotbar.rollMacro("${item.name}", "${item.type}");`
let macro = game.macros.contents.find(m => (m.name === item.name) && (m.command === command))
if (!macro) {
macro = await Macro.create({
name: item.name,
type: "script",
img: item.img,
command: command
}, { displaySheet: false })
}
game.user.assignHotbarMacro(macro, slot);
}
// Create a macro to open the actor sheet of the actor dropped on the hotbar
else if (documentData.type == "Actor") {
let actor = game.actors.get(documentData.id);
let command = `game.actors.get("${documentData.id}").sheet.render(true)`
let macro = game.macros.contents.find(m => (m.name === actor.name) && (m.command === command));
if (!macro) {
macro = await Macro.create({
name: actor.data.name,
type: "script",
img: actor.data.img,
command: command
}, { displaySheet: false })
game.user.assignHotbarMacro(macro, slot);
}
}
// Create a macro to open the journal sheet of the journal dropped on the hotbar
else if (documentData.type == "JournalEntry") {
let journal = game.journal.get(documentData.id);
let command = `game.journal.get("${documentData.id}").sheet.render(true)`
let macro = game.macros.contents.find(m => (m.name === journal.name) && (m.command === command));
if (!macro) {
macro = await Macro.create({
name: journal.data.name,
type: "script",
img: "systems/bol/icons/images/icone_parchement_vierge.webp",
command: command
}, { displaySheet: false })
game.user.assignHotbarMacro(macro, slot);
}
}
return false;
});
}
/** Roll macro */
static rollMacro(itemName, itemType, bypassData) {
const speaker = ChatMessage.getSpeaker()
let actor
if (speaker.token) actor = game.actors.tokens[speaker.token]
if (!actor) actor = game.actors.get(speaker.actor)
if (!actor) {
return ui.notifications.warn(`Selectionnez votre personnage pour utiliser la macro`)
}
let item = actor.items.find(it => it.name === itemName && it.type == itemType)
if (!item ) {
return ui.notifications.warn(`Impossible de trouver l'objet de cette macro`)
}
// Trigger the item roll
if (item.data.data.category === "equipment" && item.data.data.subtype === "weapon") {
return BoLRoll.weaponCheckWithWeapon( actor, item)
}
if (item.data.data.category === "spell") {
return BoLRoll.spellCheckWithSpell( actor, item)
}
}
}

View File

@ -32,7 +32,6 @@ export class BoLUtility {
this.rollArmor = game.settings.get("bol", "rollArmor") // Roll armor or not
this.useBougette = game.settings.get("bol", "useBougette") // Use optionnal bougette rules
console.log("UTIL", this)
}
@ -127,6 +126,17 @@ export class BoLUtility {
}
return undefined;
}
/* -------------------------------------------- */
static getOtherWhisperRecipients( name) {
let users = []
for( let user of game.users) {
if ( !user.isGM && user.name != name) {
users.push( user.data._id)
}
}
return users
}
/* -------------------------------------------- */
static getWhisperRecipientsAndGMs(name) {
let recep1 = ChatMessage.getWhisperRecipients(name) || [];
@ -154,9 +164,25 @@ export class BoLUtility {
}
}
/* -------------------------------------------- */
static async chatMessageHandler(message, html, data) {
const chatCard = html.find('.flavor-text')
if (chatCard.length > 0) {
// If the user is the message author or the actor owner, proceed
const actor = game.actors.get(data.message.speaker.actor)
console.log("FOUND 1!!! ", actor)
if (actor && actor.isOwner) return
else if (game.user.isGM || data.author.id === game.user.id) return
const divButtons = chatCard.find('.actions-section')
console.log("FOUND 2!! ", divButtons)
divButtons.hide()
}
}
/* -------------------------------------------- */
static async chatListeners(html) {
// Damage handling
html.on("click", '.chat-damage-apply', event => {
let rollData = BoLUtility.getLastRoll()
@ -172,13 +198,21 @@ export class BoLUtility {
bolRoll.rollDamage()
});
html.on("click", '.transform-legendary-roll', event => {
event.preventDefault();
let rollData = BoLUtility.getLastRoll()
rollData.actor.subHeroPoints(1)
let r = new BoLDefaultRoll(rollData)
r.upgradeToLegendary()
})
html.on("click", '.transform-heroic-roll', event => {
event.preventDefault();
let rollData = BoLUtility.getLastRoll()
rollData.actor.subHeroPoints(1)
let r = new BoLDefaultRoll(rollData)
r.upgradeToCritical();
});
r.upgradeToHeroic()
})
html.on("click", '.hero-reroll', event => {
event.preventDefault();

View File

@ -1,123 +1,129 @@
export const registerHandlebarsHelpers = function () {
Handlebars.registerHelper('isNull', function (val) {
return val == null;
});
Handlebars.registerHelper('isNull', function (val) {
return val == null;
});
Handlebars.registerHelper('exists', function (val) {
return val != null && val != undefined;
});
Handlebars.registerHelper('exists', function (val) {
return val != null && val != undefined;
});
Handlebars.registerHelper('isEmpty', function (list) {
if (list) return list.length == 0;
else return 0;
});
Handlebars.registerHelper('isEmpty', function (list) {
if (list) return list.length == 0;
else return 0;
});
Handlebars.registerHelper('notEmpty', function (list) {
return list.length > 0;
});
Handlebars.registerHelper('notEmpty', function (list) {
return list.length > 0;
});
Handlebars.registerHelper('isNegativeOrNull', function (val) {
return val <= 0;
});
Handlebars.registerHelper('isNegativeOrNull', function (val) {
return val <= 0;
});
Handlebars.registerHelper('isNegative', function (val) {
return val < 0;
});
Handlebars.registerHelper('isNegative', function (val) {
return val < 0;
});
Handlebars.registerHelper('isPositive', function (val) {
return val > 0;
});
Handlebars.registerHelper('isPositive', function (val) {
return val > 0;
});
Handlebars.registerHelper('equals', function (val1, val2) {
return val1 == val2;
});
Handlebars.registerHelper('equals', function (val1, val2) {
return val1 == val2;
});
Handlebars.registerHelper('neq', function (val1, val2) {
return val1 !== val2;
});
Handlebars.registerHelper('neq', function (val1, val2) {
return val1 !== val2;
});
Handlebars.registerHelper('gt', function (val1, val2) {
return val1 > val2;
})
Handlebars.registerHelper('gt', function (val1, val2) {
return val1 > val2;
})
Handlebars.registerHelper('lt', function (val1, val2) {
return val1 < val2;
})
Handlebars.registerHelper('lt', function (val1, val2) {
return val1 < val2;
})
Handlebars.registerHelper('gte', function (val1, val2) {
return val1 >= val2;
})
Handlebars.registerHelper('gte', function (val1, val2) {
return val1 >= val2;
})
Handlebars.registerHelper('lte', function (val1, val2) {
return val1 <= val2;
})
Handlebars.registerHelper('and', function (val1, val2) {
return val1 && val2;
})
Handlebars.registerHelper('or', function (val1, val2) {
return val1 || val2;
})
Handlebars.registerHelper('lte', function (val1, val2) {
return val1 <= val2;
})
Handlebars.registerHelper('and', function (val1, val2) {
return val1 && val2;
})
Handlebars.registerHelper('or', function (val1, val2) {
return val1 || val2;
})
Handlebars.registerHelper('or3', function (val1, val2, val3) {
return val1 || val2 || val3;
})
Handlebars.registerHelper('for', function(from, to, incr, block) {
var accum = '';
for(var i = from; i < to; i += incr)
accum += block.fn(i);
return accum;
})
Handlebars.registerHelper('not', function (cond) {
return !cond;
})
Handlebars.registerHelper('count', function (list) {
return list.length;
})
Handlebars.registerHelper('isEnabled', function (configKey) {
return game.settings.get("bol", configKey);
})
Handlebars.registerHelper('split', function (str, separator, keep) {
return str.split(separator)[keep];
})
Handlebars.registerHelper('or3', function (val1, val2, val3) {
return val1 || val2 || val3;
})
// 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('for', function (from, to, incr, block) {
var accum = '';
for (var i = from; i < to; i += incr)
accum += block.fn(i);
return accum;
})
Handlebars.registerHelper('add', function (a, b) {
return parseInt(a) + parseInt(b);
});
Handlebars.registerHelper('mul', function (a, b) {
return parseInt(a) * parseInt(b);
})
Handlebars.registerHelper('sub', function (a, b) {
return parseInt(a) - parseInt(b);
})
Handlebars.registerHelper('valueAtIndex', function (arr, idx) {
return arr[idx];
})
Handlebars.registerHelper('includesKey', function (items, type, key) {
// console.log(items);
return items.filter(i => i.type === type).map(i => i.data.key).includes(key);
})
Handlebars.registerHelper('includes', function (array, val) {
return array.includes(val);
})
Handlebars.registerHelper('eval', function (expr) {
return eval(expr);
})
Handlebars.registerHelper('not', function (cond) {
return !cond;
})
Handlebars.registerHelper('count', function (list) {
return list.length;
})
Handlebars.registerHelper('isEnabled', function (configKey) {
return game.settings.get("bol", configKey);
})
Handlebars.registerHelper('split', function (str, separator, keep) {
return str.split(separator)[keep];
})
// 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('add', function (a, b) {
return parseInt(a) + parseInt(b);
});
Handlebars.registerHelper('mul', function (a, b) {
return parseInt(a) * parseInt(b);
})
Handlebars.registerHelper('sub', function (a, b) {
return parseInt(a) - parseInt(b);
})
Handlebars.registerHelper('valueAtIndex', function (arr, idx) {
return arr[idx];
})
Handlebars.registerHelper('includesKey', function (items, type, key) {
// console.log(items);
return items.filter(i => i.type === type).map(i => i.data.key).includes(key);
})
Handlebars.registerHelper('includes', function (array, val) {
return array.includes(val);
})
Handlebars.registerHelper('eval', function (expr) {
return eval(expr);
})
Handlebars.registerHelper('isOwnerOrGM', function (actor) {
console.log("Testing actor", actor.isOwner, game.userId)
if (actor.isOwner || game.isGM) {
return true
}
return false
})
}

View File

@ -14,7 +14,8 @@ export default function registerHooks() {
html.find("img").attr("src", "systems/bol/ui/pause2.webp")
}))
Hooks.on('renderChatLog', (log, html, data) => BoLUtility.chatListeners(html));
Hooks.on('renderChatLog', (log, html, data) => BoLUtility.chatListeners(html))
Hooks.on('renderChatMessage', (message, html, data) => BoLUtility.chatMessageHandler(message, html, data))
/**
* Create a macro when dropping an entity on the hotbar

View File

@ -14,7 +14,7 @@
"url": "https://github.com/ZigmundKreud/bol",
"license": "LICENSE.txt",
"flags": {},
"version": "1.2.7",
"version": "1.3.0",
"minimumCoreVersion": "0.8.6",
"compatibleCoreVersion": "9",
"scripts": [],

View File

@ -1,9 +1,14 @@
<img class="chat-icon" src="{{weapon.img}}" alt="{{weapon.name}}"/>
<h3><strong>Dommages de {{weapon.name}} : {{damageRoll.total}}</strong></h3>
{{#if target}}
<div id="{{applyId}}">
<button class="chat-damage-apply" data-attack-id="{{id}}">Appliquer les dommages à la cible</button>
</div>
<br>
{{#if defender}}
<h3><strong>Cible : {{defender.name}}</strong></h3>
{{/if}}
<div class="actions-section">
{{#if target}}
<div id="{{applyId}}">
<button class="chat-damage-apply" data-attack-id="{{id}}">Appliquer les dommages à la cible</button>
</div>
<br>
{{/if}}
</div>

View File

@ -19,28 +19,35 @@
<h3><strong>{{description}}</strong></h3>
{{#if fightOption}}
<div>
Option de combat : {{fightOption.name}}
<div class="actions-section">
{{#if fightOption}}
<div>
Option de combat : {{fightOption.name}}
</div>
{{/if}}
<div id="{{optionsId}}">
{{#if (and isSuccess weapon)}}
{{> "systems/bol/templates/chat/rolls/attack-damage-card.hbs"}}
{{/if}}
{{#if (and isSuccess spell)}}
{{> "systems/bol/templates/chat/rolls/spell-roll-card.hbs"}}
{{/if}}
{{#if alchemy}}
{{> "systems/bol/templates/chat/rolls/alchemy-roll-card.hbs"}}
{{/if}}
{{#if reroll}}
<button class="chat-button button hero-reroll" data-roll-id=="{{rollId}}" data-actor-id="{{actor.id}}">Relancer (1 P. Heroisme)</button>
{{/if}}
{{#if (and isSuccess (not isCritical))}}
<button class="chat-button button transform-heroic-roll" data-roll-id=="{{rollId}}" data-actor-id="{{actor.id}}">Transformer en succés Héroïque (1 P. Héroisme)</button>
{{/if}}
{{#if isRealCritical}}
<button class="chat-button button transform-legendary-roll" data-roll-id=="{{rollId}}" data-actor-id="{{actor.id}}">Transformer en succes Légendaire (1 P. Heroisme)</button>
{{/if}}
<br>
</div>
</div>
{{/if}}
<div id="{{optionsId}}">
{{#if (and isSuccess weapon)}}
{{> "systems/bol/templates/chat/rolls/attack-damage-card.hbs"}}
{{/if}}
{{#if (and isSuccess spell)}}
{{> "systems/bol/templates/chat/rolls/spell-roll-card.hbs"}}
{{/if}}
{{#if alchemy}}
{{> "systems/bol/templates/chat/rolls/alchemy-roll-card.hbs"}}
{{/if}}
{{#if reroll}}
<button class="chat-button button hero-reroll" data-roll-id=="{{rollId}}" data-actor-id="{{actor.id}}">Relancer (1 P. Heroisme)</button>
{{/if}}
{{#if isRealCritical}}
<button class="chat-button button transform-heroic-roll" data-roll-id=="{{rollId}}" data-actor-id="{{actor.id}}">Transformer en succes héroique (1 P. Heroisme)</button>
{{/if}}
<br>
</div>

View File

@ -1,5 +1,5 @@
<img class="chat-icon" src="{{defender.img}}" alt="{{defender.name}}"/>
Va encaisser {{damageTotal}} dégats !
{{defender.name}} va encaisser {{damageTotal}} dégats !
{{#if damagesIgnoresArmor}}
<br>C'est une attaque au défaut de l'armure : vous devez encaisser SANS la protection de l'armure !