Add spells and alchemy
This commit is contained in:
@ -38,6 +38,11 @@ export class BoLActorSheet extends ActorSheet {
|
||||
// Equip/Unequip item
|
||||
html.find('.item-equip').click(this._onToggleEquip.bind(this));
|
||||
|
||||
html.find(".inc-dec-btns-alchemy").click((ev) => {
|
||||
const li = $(ev.currentTarget).parents(".item");
|
||||
this.actor.spendAlchemyPoint( li.data("itemId"), 1)
|
||||
})
|
||||
|
||||
// Incr./Decr. career ranks
|
||||
html.find(".inc-dec-btns").click((ev) => {
|
||||
const li = $(ev.currentTarget).parents(".item");
|
||||
@ -65,10 +70,6 @@ export class BoLActorSheet extends ActorSheet {
|
||||
item.update(update);
|
||||
}
|
||||
}
|
||||
// const input = html.find("#" + type);
|
||||
// let value = parseInt(input.val(), 10) || 0;
|
||||
// value += operator === "plus" ? 1 : -1;
|
||||
// input.val(value > 0 ? value : 0);
|
||||
});
|
||||
|
||||
|
||||
@ -110,26 +111,36 @@ export class BoLActorSheet extends ActorSheet {
|
||||
const data = super.getData(options);
|
||||
const actorData = duplicate(data.data);
|
||||
let formData = duplicate(data)
|
||||
formData.config = game.bol.config;
|
||||
formData.data = actorData.data;
|
||||
formData.details = this.actor.details;
|
||||
formData.attributes = this.actor.attributes;
|
||||
formData.aptitudes = this.actor.aptitudes;
|
||||
formData.resources = this.actor.getResourcesFromType();
|
||||
formData.equipment = this.actor.equipment;
|
||||
formData.weapons = this.actor.weapons;
|
||||
formData.protections = this.actor.protections;
|
||||
formData.containers = this.actor.containers;
|
||||
formData.treasure = this.actor.treasure;
|
||||
|
||||
formData.config = game.bol.config
|
||||
formData.data = actorData.data
|
||||
formData.details = this.actor.details
|
||||
formData.attributes = this.actor.attributes
|
||||
formData.aptitudes = this.actor.aptitudes
|
||||
formData.resources = this.actor.getResourcesFromType()
|
||||
formData.equipment = this.actor.equipment
|
||||
formData.weapons = this.actor.weapons
|
||||
formData.protections = this.actor.protections
|
||||
formData.spells = this.actor.spells
|
||||
formData.alchemy = this.actor.alchemy
|
||||
formData.containers = this.actor.containers
|
||||
formData.treasure = this.actor.treasure
|
||||
formData.treasure = this.actor.treasure
|
||||
formData.treasure = this.actor.alchemyrecipe
|
||||
formData.vehicles = this.actor.vehicles;
|
||||
formData.ammos = this.actor.ammos;
|
||||
formData.misc = this.actor.misc;
|
||||
formData.combat = this.actor.buildCombat();
|
||||
formData.features = this.actor.buildFeatures();
|
||||
formData.isGM = game.user.isGM;
|
||||
formData.options= this.options,
|
||||
formData.owner= this.document.isOwner,
|
||||
formData.editScore= this.options.editScore,
|
||||
formData.features = this.actor.buildFeatures()
|
||||
formData.isGM = game.user.isGM
|
||||
formData.options= this.options
|
||||
formData.owner= this.document.isOwner
|
||||
formData.editScore= this.options.editScore
|
||||
|
||||
formData.isSorcerer = this.actor.isSorcerer()
|
||||
formData.isAlchemist = this.actor.isAlchemist()
|
||||
formData.isPriest = this.actor.isPriest()
|
||||
|
||||
formData.isGM= game.user.isGM
|
||||
|
||||
console.log("ACTORDATA", formData);
|
||||
@ -193,6 +204,12 @@ export class BoLActorSheet extends ActorSheet {
|
||||
case "weapon":
|
||||
BoLRoll.weaponCheck(this.actor, actorData, dataset, event);
|
||||
break;
|
||||
case "spell":
|
||||
BoLRoll.spellCheck(this.actor, actorData, dataset, event);
|
||||
break;
|
||||
case "alchemy":
|
||||
BoLRoll.alchemyCheck(this.actor, actorData, dataset, event);
|
||||
break;
|
||||
case "protection":
|
||||
this.actor.rollProtection(li.data("item-id"))
|
||||
break;
|
||||
|
@ -109,6 +109,12 @@ export class BoLActor extends Actor {
|
||||
get protections() {
|
||||
return this.armors.concat(this.helms).concat(this.shields)
|
||||
}
|
||||
get spells() {
|
||||
return this.itemData.filter(i => i.type === "item" && i.data.category === "spell");
|
||||
}
|
||||
get alchemy() {
|
||||
return this.itemData.filter(i => i.type === "item" && i.data.category === "alchemy");
|
||||
}
|
||||
get melee() {
|
||||
return this.weapons.filter(i => i.data.properties.melee === true);
|
||||
}
|
||||
@ -136,6 +142,66 @@ export class BoLActor extends Actor {
|
||||
return this.itemData.filter(i => i.type === "item" && i.data.category === "equipment" && (i.data.subtype === "other" ||i.data.subtype === "container" ||i.data.subtype === "scroll" || i.data.subtype === "jewel"));
|
||||
}
|
||||
|
||||
isSorcerer( ) {
|
||||
if ( this.careers.find( item => item.data.properties.sorcerer == true) )
|
||||
return true
|
||||
return false
|
||||
}
|
||||
isAlchemist( ) {
|
||||
if ( this.careers.find( item => item.data.properties.alchemist == true) )
|
||||
return true
|
||||
return false
|
||||
}
|
||||
isPriest( ) {
|
||||
if ( this.careers.find( item => item.data.properties.priest == true) )
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
spendPowerPoint( ppCost ) {
|
||||
let newPP = this.data.data.resources.power.value - ppCost
|
||||
newPP = (newPP<0) ? 0 : newPP
|
||||
this.update( {'data.resources.power.value': newPP})
|
||||
}
|
||||
|
||||
resetAlchemyStatus( alchemyId ) {
|
||||
let alchemy = this.data.items.get( alchemyId)
|
||||
if (alchemy) {
|
||||
this.updateEmbeddedDocuments('Item', [{_id: alchemy.id, 'data.properties.pccurrent': 0}] )
|
||||
}
|
||||
}
|
||||
|
||||
async spendAlchemyPoint( alchemyId, pcCost) {
|
||||
let alchemy = this.data.items.get( alchemyId)
|
||||
if (alchemy) {
|
||||
pcCost = Number(pcCost)?? 0
|
||||
if ( this.data.data.resources.alchemypoints.value >= pcCost) {
|
||||
let newPC = this.data.data.resources.alchemypoints.value - pcCost
|
||||
newPC = (newPC<0) ? 0 : newPC
|
||||
this.update( {'data.resources.alchemypoints.value': newPC} )
|
||||
newPC = alchemy.data.data.properties.pccurrent + pcCost
|
||||
await this.updateEmbeddedDocuments('Item', [{_id: alchemy.id, 'data.properties.pccurrent': newPC}] )
|
||||
} else {
|
||||
ui.notifications.warn("Plus assez de Points de Création !")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAlchemistBonus() {
|
||||
let sorcerer = this.careers.find( item => item.data.properties.alchemist == true)
|
||||
if (sorcerer) {
|
||||
return sorcerer.data.rank
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
getSorcererBonus() {
|
||||
let sorcerer = this.careers.find( item => item.data.properties.sorcerer == true)
|
||||
if (sorcerer) {
|
||||
return sorcerer.data.rank
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
heroReroll( ) {
|
||||
if (this.type == 'character') {
|
||||
return this.data.data.resources.hero.value > 0;
|
||||
|
@ -63,8 +63,6 @@ export class BoLRoll {
|
||||
}
|
||||
|
||||
static weaponCheck(actor, actorData, dataset, event) {
|
||||
// const elt = $(event.currentTarget)[0];
|
||||
// let key = elt.attributes["data-rolling"].value;
|
||||
let target = BoLUtility.getTarget()
|
||||
const li = $(event.currentTarget).parents(".item");
|
||||
const weapon = actor.items.get(li.data("item-id"));
|
||||
@ -85,13 +83,83 @@ export class BoLRoll {
|
||||
label: (weapon.name) ? weapon.name : game.i18n.localize('BOL.ui.noWeaponName'),
|
||||
description: actor.name + " - " + game.i18n.localize('BOL.ui.weaponAttack'),
|
||||
adv: "2",
|
||||
|
||||
}
|
||||
console.debug("WEAPON!", attackDef, weaponData);
|
||||
return this.displayRollDialog(attackDef);
|
||||
}
|
||||
|
||||
static alchemyCheck( actor, actorData, dataset, event) {
|
||||
const li = $(event.currentTarget).parents(".item");
|
||||
const alchemy = actor.items.get(li.data("item-id"));
|
||||
if (!alchemy) {
|
||||
ui.notifications.warn("Unable to find Alchemy !");
|
||||
return;
|
||||
}
|
||||
let alchemyData = alchemy.data.data
|
||||
if (alchemyData.properties.pccurrent < alchemyData.properties.pccost) {
|
||||
ui.notifications.warn("Pas assez de Points de Cration investis dans la Préparation !")
|
||||
return
|
||||
}
|
||||
|
||||
let alchemyDef = {
|
||||
mode: "alchemy",
|
||||
actor: actor,
|
||||
actorData: actorData,
|
||||
alchemy: alchemy,
|
||||
attribute: actor.data.data.attributes.mind,
|
||||
careerBonus: actor.getAlchemistBonus(),
|
||||
pcCost: alchemyData.properties.pccost,
|
||||
pcCostCurrent: alchemyData.properties.pccurrent,
|
||||
mod: alchemyData.properties.difficulty,
|
||||
label: alchemy.name,
|
||||
adv: "2",
|
||||
description: actor.name + " - " + game.i18n.localize('BOL.ui.makeAlchemy'),
|
||||
}
|
||||
console.log("ALCHEMY!", alchemyDef);
|
||||
return this.displayRollDialog(alchemyDef);
|
||||
}
|
||||
|
||||
|
||||
static spellCheck( actor, actorData, dataset, 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;
|
||||
let spellDef = {
|
||||
mode: "spell",
|
||||
actor: actor,
|
||||
actorData: actorData,
|
||||
spell: spell,
|
||||
attribute: actor.data.data.attributes.mind,
|
||||
ppCurrent: actor.data.data.resources.power.value,
|
||||
careerBonus: actor.getSorcererBonus(),
|
||||
ppCost: spell.data.data.properties.ppcost,
|
||||
mod: spellData.properties.difficulty,
|
||||
label: spell.name,
|
||||
adv: "2",
|
||||
description: actor.name + " - " + game.i18n.localize('BOL.ui.focusSpell'),
|
||||
}
|
||||
console.log("SPELL!", spellDef);
|
||||
return this.displayRollDialog(spellDef);
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static rollDialogListener(html) {
|
||||
|
||||
html.find('#optcond').change((event) => { // Dynamic change of PP cost of spell
|
||||
let pp = BoLUtility.computeSpellCost(this.rollData.spell, event.currentTarget.selectedOptions.length)
|
||||
$('#ppcost').html(pp)
|
||||
this.rollData.ppCost = pp
|
||||
});
|
||||
}
|
||||
|
||||
/* ROLL DIALOGS */
|
||||
/* -------------------------------------------- */
|
||||
static async displayRollDialog(rollData, onEnter = "submit") {
|
||||
@ -101,8 +169,10 @@ export class BoLRoll {
|
||||
rollData.boons = rollData.actorData.features.boons
|
||||
rollData.flaws = rollData.actorData.features.flaws
|
||||
rollData.defence = 0
|
||||
rollData.mod = 0
|
||||
rollData.careerBonus = rollData.careerBonus?? 0
|
||||
rollData.mod = rollData.mod?? 0
|
||||
rollData.id = randomID(16)
|
||||
this.rollData = rollData
|
||||
|
||||
// Weapon mode specific management
|
||||
rollData.weaponModifier = 0
|
||||
@ -128,6 +198,7 @@ export class BoLRoll {
|
||||
title: rollData.label,
|
||||
content: rollOptionContent,
|
||||
rollData: rollData,
|
||||
render: html => this.rollDialogListener(html),
|
||||
buttons: {
|
||||
cancel: {
|
||||
icon: '<i class="fas fa-times"></i>',
|
||||
@ -139,6 +210,11 @@ export class BoLRoll {
|
||||
icon: '<i class="fas fa-check"></i>',
|
||||
label: game.i18n.localize("BOL.ui.submit"),
|
||||
callback: (html) => {
|
||||
if (rollData.mode == 'spell' && rollData.ppCurrent < rollData.ppCost) { // Check PP available
|
||||
ui.notifications.warn("Pas assez de Points de Pouvoir !")
|
||||
return
|
||||
}
|
||||
|
||||
rollData.attrKey = html.find('#attr').val();
|
||||
rollData.aptKey = html.find('#apt').val();
|
||||
rollData.adv = $("input[name='adv']:checked").val() || "2";
|
||||
@ -156,14 +232,14 @@ export class BoLRoll {
|
||||
}
|
||||
}
|
||||
|
||||
const isMalus = rollData.adv.includes('M');
|
||||
const isMalus = rollData.adv.includes('M')
|
||||
let dicePool = __adv2dice[rollData.adv]
|
||||
dicePool += (rollData.attackBonusDice) ? 1 : 0
|
||||
//// const dicePool = (isMalus) ? 2 - parseInt(rollData.adv) : 2 + parseInt(rollData.adv);
|
||||
|
||||
const attrValue = (rollData.attrKey) && eval(`rollData.actor.data.data.attributes.${rollData.attrKey}.value`) || 0;
|
||||
const aptValue = (rollData.aptKey) && eval(`rollData.actor.data.data.aptitudes.${rollData.aptKey}.value`) || 0
|
||||
|
||||
const modifiers = rollData.weaponModifier + parseInt(attrValue) + parseInt(aptValue) + parseInt(rollData.mod) + parseInt(rollData.career) - rollData.defence - shieldMalus;
|
||||
const modifiers = rollData.careerBonus + rollData.weaponModifier + parseInt(attrValue) + parseInt(aptValue) + parseInt(rollData.mod) + parseInt(rollData.career) - rollData.defence - shieldMalus;
|
||||
const formula = (isMalus) ? dicePool + "d6kl2 + " + modifiers : dicePool + "d6kh2 + " + modifiers;
|
||||
rollData.formula = formula;
|
||||
rollData.modifiers = modifiers
|
||||
@ -176,6 +252,7 @@ export class BoLRoll {
|
||||
default: onEnter,
|
||||
close: () => { }
|
||||
}, this.options());
|
||||
|
||||
return d.render(true);
|
||||
}
|
||||
}
|
||||
@ -219,10 +296,14 @@ export class BoLDefaultRoll {
|
||||
if (this.rollData.registerInit) {
|
||||
this.rollData.actor.registerInit(r.total, this.rollData.isCritical);
|
||||
}
|
||||
if (this.rollData.isSuccess && this.rollData.mode == "spell") { // PP cost management
|
||||
this.rollData.actor.spendPowerPoint(this.rollData.ppCost)
|
||||
}
|
||||
if (this.rollData.mode == "alchemy") { // PP cost management
|
||||
this.rollData.actor.resetAlchemyStatus(this.rollData.alchemy.id)
|
||||
}
|
||||
|
||||
console.log("ROLL", this.rollData)
|
||||
await this.sendChatMessage()
|
||||
|
||||
}
|
||||
|
||||
async sendChatMessage() {
|
||||
|
@ -17,18 +17,7 @@ export class BoLItemSheet extends ItemSheet {
|
||||
});
|
||||
}
|
||||
|
||||
// /** @override */
|
||||
// get template() {
|
||||
// const path = "systems/bol/templates/item";
|
||||
// // Return a single sheet for all item types.
|
||||
// //return `${path}/item-sheet.hbs`;
|
||||
// // Alternatively, you could use the following return statement to do a
|
||||
// // unique item sheet by type, like `weapon-sheet.html`.
|
||||
// return `${path}/item-${this.item.data.type}-sheet.hbs`;
|
||||
// }
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
||||
/** @override */
|
||||
getData(options) {
|
||||
const data = super.getData(options);
|
||||
@ -39,7 +28,18 @@ export class BoLItemSheet extends ItemSheet {
|
||||
data.category = itemData.category;
|
||||
data.itemProperties = this.item.itemProperties;
|
||||
data.isGM = game.user.isGM;
|
||||
console.debug("ITEMDATA", data);
|
||||
|
||||
// Dynamic spell fix
|
||||
if (itemData.type == "item" && itemData.data.category == 'spell') {
|
||||
for (let i=0; i<4; i++) {
|
||||
itemData.data.properties.mandatoryconditions[i] = itemData.data.properties.mandatoryconditions[i]?? ""
|
||||
}
|
||||
for (let i=0; i<8; i++) {
|
||||
itemData.data.properties.optionnalconditions[i] = itemData.data.properties.optionnalconditions[i]?? ""
|
||||
}
|
||||
}
|
||||
|
||||
console.log("ITEMDATA", data);
|
||||
return data;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,8 @@
|
||||
import { BoLDefaultRoll } from "../controllers/bol-rolls.js";
|
||||
|
||||
// Spell circle to min PP cost
|
||||
const __circle2minpp = { 0: 0, 1: 2,2: 6, 3: 11}
|
||||
|
||||
export class BoLUtility {
|
||||
|
||||
|
||||
@ -375,6 +378,14 @@ export class BoLUtility {
|
||||
if (sockmsg.name == "msg_damage_handling") {
|
||||
BoLUtility.processDamageHandling(sockmsg.data.event, sockmsg.data.attackId, sockmsg.data.defenseMode)
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
static computeSpellCost( spell, nbOptCond= 0) {
|
||||
let pp = spell.data.data.properties.ppcost
|
||||
let minpp = __circle2minpp[spell.data.data.properties.circle]
|
||||
pp = (pp-nbOptCond<minpp) ? minpp : pp-nbOptCond
|
||||
return pp
|
||||
}
|
||||
|
||||
/* -------------------------------------------- */
|
||||
|
@ -27,6 +27,20 @@ BOL.damageMultiplier = {
|
||||
"4": "x4",
|
||||
}
|
||||
|
||||
BOL.spellType = {
|
||||
"0": "BOL.spellItem.charm",
|
||||
"1": "BOL.spellItem.circle1",
|
||||
"2": "BOL.spellItem.circle2",
|
||||
"3": "BOL.spellItem.circle3"
|
||||
}
|
||||
|
||||
BOL.alchemyType = {
|
||||
"common": "BOL.alchemyItem.common",
|
||||
"scarce": "BOL.alchemyItem.scarce",
|
||||
"legend": "BOL.alchemyItem.legend",
|
||||
"mythic": "BOL.alchemyItem.mythic",
|
||||
}
|
||||
|
||||
BOL.equipmentSlots = {
|
||||
"none" : "BOL.equipmentSlots.none",
|
||||
"head" : "BOL.equipmentSlots.head",
|
||||
@ -115,6 +129,7 @@ BOL.itemCategories = {
|
||||
"equipment" : "BOL.itemCategory.equipment",
|
||||
"capacity" : "BOL.itemCategory.capacity",
|
||||
"spell" : "BOL.itemCategory.spell",
|
||||
"alchemy" : "BOL.itemCategory.alchemy",
|
||||
"vehicle" : "BOL.itemCategory.vehicle",
|
||||
"other" : "BOL.itemCategory.other"
|
||||
}
|
||||
|
@ -63,7 +63,14 @@ export const registerHandlebarsHelpers = function () {
|
||||
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;
|
||||
});
|
||||
@ -94,6 +101,10 @@ export const registerHandlebarsHelpers = function () {
|
||||
Handlebars.registerHelper('add', 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];
|
||||
|
@ -14,6 +14,7 @@ export const preloadHandlebarsTemplates = async function () {
|
||||
"systems/bol/templates/actor/parts/tabs/actor-actions.hbs",
|
||||
"systems/bol/templates/actor/parts/tabs/actor-features.hbs",
|
||||
"systems/bol/templates/actor/parts/tabs/actor-equipment.hbs",
|
||||
"systems/bol/templates/actor/parts/tabs/actor-spellalchemy.hbs",
|
||||
// ITEMS
|
||||
"systems/bol/templates/item/parts/item-header.hbs",
|
||||
"systems/bol/templates/item/parts/properties/feature-properties.hbs",
|
||||
@ -24,6 +25,7 @@ export const preloadHandlebarsTemplates = async function () {
|
||||
"systems/bol/templates/item/parts/properties/item/protection-properties.hbs",
|
||||
"systems/bol/templates/item/parts/properties/item/weapon-properties.hbs",
|
||||
"systems/bol/templates/item/parts/properties/item/spell-properties.hbs",
|
||||
"systems/bol/templates/item/parts/properties/item/alchemy-properties.hbs",
|
||||
"systems/bol/templates/item/parts/properties/item/magical-properties.hbs",
|
||||
"systems/bol/templates/item/parts/properties/feature/career-properties.hbs",
|
||||
"systems/bol/templates/item/parts/properties/feature/boon-properties.hbs",
|
||||
@ -33,6 +35,8 @@ export const preloadHandlebarsTemplates = async function () {
|
||||
|
||||
// DIALOGS
|
||||
"systems/bol/templates/chat/rolls/attack-damage-card.hbs",
|
||||
"systems/bol/templates/chat/rolls/spell-roll-card.hbs",
|
||||
"systems/bol/templates/chat/rolls/alchemy-roll-card.hbs",
|
||||
"systems/bol/templates/roll/parts/roll-dialog-modifiers.hbs",
|
||||
"systems/bol/templates/roll/parts/roll-dialog-attribute.hbs",
|
||||
"systems/bol/templates/dialogs/aptitude-roll-part.hbs",
|
||||
|
Reference in New Issue
Block a user