Support des jets d'attributs et d'aptitudes

Ajout des macros
Amélioration des cartons dans le chat avec gestion des succès/échecs/échecs critiques.
Support des carrières dans les dialogues de tests d'attibuts et d'aptitudes.
This commit is contained in:
ZigmundKreud 2021-12-22 05:12:40 +01:00
parent 4c349d89a8
commit d5a5990faa
41 changed files with 750 additions and 578 deletions

View File

@ -149,6 +149,24 @@
border: none;
border-radius: 0;
}
.bol select[multiple] {
box-shadow: none;
border: none;
font-size: 12px;
}
.bol select[multiple]:focus option:checked {
background: darkred linear-gradient(0deg, darkred 0%, darkred 100%);
color: white;
}
.bol option:hover,
.bol option:focus,
.bol option:active,
.bol option:checked,
.bol option[selected] {
cursor: pointer;
background: darkred linear-gradient(0deg, darkred 0%, darkred 100%);
color: white;
}
.bol label.checkbox {
flex: auto;
padding: 0;
@ -425,6 +443,13 @@
background: #cd071e;
color: #fff;
}
.darkred {
color: darkred;
}
.bg-darkred {
background: darkred;
color: #fff;
}
.purple {
color: purple;
}
@ -432,6 +457,30 @@
background: purple;
color: #fff;
}
.message-header h2.damage {
color: orangered;
font-weight: bold;
}
.message-header h2.critical {
color: green;
font-weight: bold;
}
.message-header h2.fumble {
color: red;
font-weight: bold;
}
.message-header h2.success {
color: darkgreen;
font-weight: bold;
}
.message-header h2.failure {
color: darkred;
font-weight: bold;
}
.message-header h2.roll {
color: darkslategrey;
font-weight: bold;
}
.bol.sheet .window-content {
height: 100%;
padding: 5px;
@ -609,6 +658,13 @@
}
.bol.sheet.actor .stat-roll {
font-size: 1.5rem;
color: #4b4a44;
}
.bol.sheet.actor .stat-roll.malus {
color: darkred;
}
.bol.sheet.actor .stat-roll.bonus {
color: darkgreen;
}
.bol.sheet.actor .header-field-label,
.bol.sheet.actor .stat-label {
@ -624,6 +680,7 @@
}
.bol.sheet.actor .rounded-border {
border: 3px solid #4b4a44;
box-shadow: 5px 5px 5px gray;
border-radius: 100px;
width: 4rem;
height: 4rem;
@ -675,23 +732,23 @@
font-size: 12px;
line-height: 18px;
}
.bol.dialog .sheet-header h3 {
font-family: "Wolfsbane2Expanded", cursive;
font-size: 24px;
color: black;
}
.editor,
.editor-content {
height: 100%;
}
.rollable {
color: #4b4a44;
cursor: pointer;
}
.malus {
color: darkred;
}
.bonus {
color: darkgreen;
}
.chat-message .chat-icon {
flex: 0 0 64px;
float: right;
border: 1px outset lightgray;
box-shadow: 3px 3px 3px black;
margin: 3px;
width: 64px;
height: 64px;
}

View File

@ -38,6 +38,9 @@
"BOL.ui.type": "Type",
"BOL.ui.subtype": "Sous-type",
"BOL.ui.properties": "Propriétés",
"BOL.ui.attribute" : "Attribut",
"BOL.ui.aptitude" : "Aptitude",
"BOL.ui.advantages" : "Avantages/Désavantages",
"BOL.ui.modifiers": "Modificateurs",
"BOL.ui.item": "Objet",
"BOL.ui.edit": "Editer",
@ -45,12 +48,31 @@
"BOL.ui.equip": "Equiper",
"BOL.ui.delete": "Supprimer",
"BOL.ui.roll" : "Utiliser",
"BOL.ui.equipment" : "Équipement",
"BOL.ui.weapon" : "Arme",
"BOL.ui.melee" : "Arme de contact",
"BOL.ui.ranged" : "Arme à distance",
"BOL.ui.protection" : "Protection",
"BOL.ui.shield" : "Bouclier",
"BOL.ui.blocking" : "Blocage",
"BOL.ui.range" : "Portée",
"BOL.ui.quantity" : "Quantité",
"BOL.ui.weight" : "Poids",
"BOL.ui.price": "Prix",
"BOL.ui.cancel": "Annuler",
"BOL.ui.submit": "OK",
"BOL.ui.attributeCheck" : "Test d'attribut",
"BOL.ui.aptitudeCheck" : "Test d'aptitude",
"BOL.ui.weaponCheck" : "Jet d'attaque",
"BOL.ui.spellCheck" : "Jet de sort",
"BOL.ui.careers" : "Carrières",
"BOL.ui.boons" : "Avantages",
"BOL.ui.flaws" : "Désavantages",
"BOL.ui.rank" : "Rang",
"BOL.ui.success" : "Succès",
"BOL.ui.failure" : "Échec",
"BOL.ui.fumble" : "Échec critique",
"BOL.ui.critical" : "Succès critique",
"BOL.featureCategory.origins": "Origines",
"BOL.featureCategory.races": "Races",
@ -144,78 +166,7 @@
"BOL.range.Extreme": "Extrême",
"BOL.range.Maximum": "Maximale",
"Careers": "Carrieres",
"Name": "Nom",
"Boons": "Avantages",
"Flaws": "Desavantages",
"Inventory": "Inventaire",
"Career": "Carrière",
"Boon": "Avantage",
"Flaw": "Désavantage",
"d6B": "d6 Bonus",
"d6M": "d6 Malus",
"ranged": "A Distance",
"melee": "Melée",
"spell" : "Sort",
"protection" : "Protection",
"weapon" : "Arme",
"armor" : "Armure",
"helm" : "Casque",
"shield" : "Bouclier",
"equipable": "Equipable",
"stackable": "Empilable",
"consumable" : "Consommable",
"magical" : "Magique",
"2H" : "A Deux Mains",
"reloadable" : "A Recharger",
"bow" : "Arc",
"crossbow" : "Arbalète",
"throwing" : "Lancer",
"rank": "Niveau",
"attribut": "Attribut",
"subtype": "Type",
"Language": "Langue",
"Level": "Niveau",
"Roll": "Lancer",
"Melee": "Mêlée",
"Ranged": "Tir",
"Quantity": "Quantité",
"Weight":"Poids",
"Damage": "Dommages",
"Two Hands": "A Deux Mains",
"Thrown": "Peut-être lancé",
"Range": "Portée (m.)",
"Reload": "A Recharger",
"Reload Duration": "Durée de rechargement (rounds)" ,
"Can be hidden": "Peut être cachée",
"Ignore Shield": "Ignore les boucliers",
"Improvised": "Improvisée",
"Properties": "Propriétés",
"Protection Roll": "Jet de Protection",
"Protection Fixed": "Protection statique (si pas de jet)",
"None": "Aucun",
"Social Malus": "Malus Social",
"Agility Malus":"Malus d'Agilité",
"Initiative Malus": "Malus d'Initiative",
"Power Cost": "Cout en Points de Pouvoir",
"Equipped": "Equipé",
"Attack with": "Attaque avec",
"Range Modifier": "Modificateur de portée",
"Point Blank": "Bout portant",
"Short": "Courte",
"Medium": "Moyenne",
"Long": "Long",
"Very Long": "Très longue",
"Extreme": "Extrême",
"Maximum": "Maximale",
"Defender": "Défenseur",
"Defense score": "Score de défense",
"Modifier": "Modificateur"
"BOL.notification.MacroMultipleTokensSelected": "Vous avez sélectionné plusieurs tokens",
"BOL.notification.MacroNoActorAvailable": "Aucun acteur n'a pu être ciblé",
"BOL.notification.MacroNoTokenSelected": "Vous devez sélectionner un token"
}

View File

@ -38,6 +38,9 @@
"BOL.ui.type": "Type",
"BOL.ui.subtype": "Sous-type",
"BOL.ui.properties": "Propriétés",
"BOL.ui.attribute" : "Attribut",
"BOL.ui.aptitude" : "Aptitude",
"BOL.ui.advantages" : "Avantages/Désavantages",
"BOL.ui.modifiers": "Modificateurs",
"BOL.ui.item": "Objet",
"BOL.ui.edit": "Editer",
@ -56,6 +59,20 @@
"BOL.ui.quantity" : "Quantité",
"BOL.ui.weight" : "Poids",
"BOL.ui.price": "Prix",
"BOL.ui.cancel": "Annuler",
"BOL.ui.submit": "OK",
"BOL.ui.attributeCheck" : "Test d'attribut",
"BOL.ui.aptitudeCheck" : "Test d'aptitude",
"BOL.ui.weaponCheck" : "Jet d'attaque",
"BOL.ui.spellCheck" : "Jet de sort",
"BOL.ui.careers" : "Carrières",
"BOL.ui.boons" : "Avantages",
"BOL.ui.flaws" : "Désavantages",
"BOL.ui.rank" : "Rang",
"BOL.ui.success" : "Succès",
"BOL.ui.failure" : "Échec",
"BOL.ui.fumble" : "Échec critique",
"BOL.ui.critical" : "Succès critique",
"BOL.featureCategory.origins": "Origines",
"BOL.featureCategory.races": "Races",
@ -149,78 +166,7 @@
"BOL.range.Extreme": "Extrême",
"BOL.range.Maximum": "Maximale",
"Careers": "Carrieres",
"Name": "Nom",
"Boons": "Avantages",
"Flaws": "Desavantages",
"Inventory": "Inventaire",
"Career": "Carrière",
"Boon": "Avantage",
"Flaw": "Désavantage",
"d6B": "d6 Bonus",
"d6M": "d6 Malus",
"ranged": "A Distance",
"melee": "Melée",
"spell" : "Sort",
"protection" : "Protection",
"weapon" : "Arme",
"armor" : "Armure",
"helm" : "Casque",
"shield" : "Bouclier",
"equipable": "Equipable",
"stackable": "Empilable",
"consumable" : "Consommable",
"magical" : "Magique",
"2H" : "A Deux Mains",
"reloadable" : "A Recharger",
"bow" : "Arc",
"crossbow" : "Arbalète",
"throwing" : "Lancer",
"rank": "Niveau",
"attribut": "Attribut",
"subtype": "Type",
"Language": "Langue",
"Level": "Niveau",
"Roll": "Lancer",
"Melee": "Mêlée",
"Ranged": "Tir",
"Quantity": "Quantité",
"Weight":"Poids",
"Damage": "Dommages",
"Two Hands": "A Deux Mains",
"Thrown": "Peut-être lancé",
"Range": "Portée (m.)",
"Reload": "A Recharger",
"Reload Duration": "Durée de rechargement (rounds)" ,
"Can be hidden": "Peut être cachée",
"Ignore Shield": "Ignore les boucliers",
"Improvised": "Improvisée",
"Properties": "Propriétés",
"Protection Roll": "Jet de Protection",
"Protection Fixed": "Protection statique (si pas de jet)",
"None": "Aucun",
"Social Malus": "Malus Social",
"Agility Malus":"Malus d'Agilité",
"Initiative Malus": "Malus d'Initiative",
"Power Cost": "Cout en Points de Pouvoir",
"Equipped": "Equipé",
"Attack with": "Attaque avec",
"Range Modifier": "Modificateur de portée",
"Point Blank": "Bout portant",
"Short": "Courte",
"Medium": "Moyenne",
"Long": "Long",
"Very Long": "Très longue",
"Extreme": "Extrême",
"Maximum": "Maximale",
"Defender": "Défenseur",
"Defense score": "Score de défense",
"Modifier": "Modificateur"
"BOL.notification.MacroMultipleTokensSelected": "Vous avez sélectionné plusieurs tokens",
"BOL.notification.MacroNoActorAvailable": "Aucun acteur n'a pu être ciblé",
"BOL.notification.MacroNoTokenSelected": "Vous devez sélectionner un token"
}

View File

@ -2,6 +2,8 @@
* Extend the basic ActorSheet with some very simple modifications
* @extends {ActorSheet}
*/
import {BoLRoll} from "../controllers/bol-rolls.js";
export class BoLActorSheet extends ActorSheet {
/** @override */
@ -33,18 +35,6 @@ export class BoLActorSheet extends ActorSheet {
const item = this.actor.items.get(li.data("itemId"));
item.sheet.render(true);
});
html.find('.roll-attribute').click(ev => {
this.actor.rollAttributeAptitude( $(ev.currentTarget).data("attr-key") );
});
html.find('.roll-career').click(ev => {
const li = $(ev.currentTarget).parents(".item");
this.actor.rollCareer( li.data("itemId") );
});
html.find('.roll-weapon').click(ev => {
const li = $(ev.currentTarget).parents(".item");
this.actor.rollWeapon( li.data("itemId") );
});
// Equip/Unequip item
html.find('.item-equip').click(this._onToggleEquip.bind(this));
@ -57,6 +47,18 @@ export class BoLActorSheet extends ActorSheet {
// Rollable abilities.
html.find('.rollable').click(this._onRoll.bind(this));
// html.find('.roll-attribute').click(ev => {
// this.actor.rollAttributeAptitude( $(ev.currentTarget).data("attr-key") );
// });
// html.find('.roll-career').click(ev => {
// const li = $(ev.currentTarget).parents(".item");
// this.actor.rollCareer( li.data("itemId") );
// });
// html.find('.roll-weapon').click(ev => {
// const li = $(ev.currentTarget).parents(".item");
// this.actor.rollWeapon( li.data("itemId") );
// });
}
/* -------------------------------------------- */
@ -121,14 +123,16 @@ export class BoLActorSheet extends ActorSheet {
event.preventDefault();
const element = event.currentTarget;
const dataset = element.dataset;
if (dataset.roll) {
let roll = new Roll(dataset.roll, this.actor.data.data);
let label = dataset.label ? `Rolling ${dataset.label}` : '';
roll.roll().toMessage({
speaker: ChatMessage.getSpeaker({ actor: this.actor }),
flavor: label
});
const actorData = this.getData();
const rollType = dataset.rollType;
switch(rollType) {
case "attribute" :
BoLRoll.attributeCheck(this.actor, actorData, dataset, event);
break;
case "aptitude" :
BoLRoll.aptitudeCheck(this.actor, actorData, dataset, event);
break;
default : break;
}
}

View File

@ -1,6 +1,3 @@
import { BoLRollDialog } from "../system/roll-dialog.js";
import { BoLUtility } from "../system/bol-utility.js";
/**
* Extend the base Actor entity by defining a custom roll data structure which is ideal for the Simple system.
* @extends {Actor}
@ -14,9 +11,7 @@ export class BoLActor extends Actor {
// console.log(actorData);
// const data = actorData.data;
// const flags = actorData.flags;
// Make separate methods for each Actor type (character, npc, etc.) to keep
// things organized.
// Make separate methods for each Actor type (character, npc, etc.) to keep things organized.
if (actorData.type === 'character') {
this._prepareCharacterData(actorData);
}
@ -165,97 +160,6 @@ export class BoLActor extends Actor {
};
}
/* -------------------------------------------- */
buildRollData(mode, title) {
return {
mode : mode,
title : title,
actorId: this.id,
actorImg: this.img,
boons : this.boons,
flaws : this.flaws,
d6Bonus: 0,
d6Malus: 0,
rollMode: game.settings.get("core", "rollMode"),
optionsBonusMalus: BoLUtility.buildListOptions(-8, +2),
bonusMalus: 0
}
}
saveRollData( rollData) {
this.currentRollData = rollData;
}
/* -------------------------------------------- */
async rollAttributeAptitude( attrKey ) {
let attr = this.data.data.attributes[attrKey];
if ( !attr) {
attr = this.data.data.aptitudes[attrKey];
}
if (attr) {
let rollData = this.buildRollData("attribute", game.i18n.localize(attr.label));
rollData.attribute = duplicate(attr);
let rollDialog = await BoLRollDialog.create( this, rollData);
rollDialog.render( true );
} else {
ui.notifications.warn("Unable to find attribute " + attrKey );
}
}
/* -------------------------------------------- */
async rollCareer( careerId ) {
let career = BoLUtility.data(this.data.items.find( item => item.type == 'feature' && item.id == careerId));
if (career) {
let rollData = this.buildRollData("career", `${career.name} : ${career.data.rank}`);
rollData.career = career;
rollData.rollAttribute = 'mind';
rollData.attributes = duplicate(this.data.data.attributes);
let rollDialog = await BoLRollDialog.create( this, rollData);
rollDialog.render( true );
} else {
ui.notifications.warn("Unable to find career for actor " + this.name + " - Career ID " + careerId);
}
}
/* -------------------------------------------- */
async rollWeapon( weaponId ) {
let weapon = BoLUtility.data(this.data.items.find( item => item.type == 'item' && item.id == weaponId));
if (weapon) {
let target = BoLUtility.getTarget();
// if ( !target) {
// ui.notifications.warn("You must have a target to attack with a Weapon");
// return;
// }
let objectDefender = (target) ? BoLUtility.data(game.actors.get(target.data.actorId)) : null;
objectDefender = (objectDefender) ? mergeObject(objectDefender, target.data.actorData) : null;
let rollData = this.buildRollData("weapon", weapon.name);
rollData.weapon = weapon;
rollData.target = target;
rollData.isRanged = BoLUtility.isRangedWeapon( weapon );
rollData.defender = objectDefender;
rollData.rollAttribute = 'agility';
rollData.attributes = duplicate(this.data.data.attributes); // For damage bonus
rollData.rangeModifier = 0;
if ( weapon.data.type == 'melee') {
rollData.aptitude = duplicate(this.data.data.aptitudes.melee);
} else {
rollData.aptitude = duplicate(this.data.data.aptitudes.ranged);
}
console.log("WEAPON ! ", rollData);
let rollDialog = await BoLRollDialog.create( this, rollData);
rollDialog.render( true );
} else {
ui.notifications.warn("Unable to find weapon for actor " + this.name + " - Weapon ID " + weaponId);
}
}
/**
*
* @param {*} item
* @param {*} bypassChecks
* @returns
*/
toggleEquipItem(item) {
const equipable = item.data.data.properties.equipable;
if(equipable){
@ -264,6 +168,4 @@ export class BoLActor extends Actor {
return item.update(itemData);
}
}
}

View File

@ -8,13 +8,15 @@ 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 {DataLoader} from "./system/data.js";
import {Macros} from "./system/macros.js";
Hooks.once('init', async function () {
game.bol = {
BoLActor,
BoLItem,
macros : Macros,
config:BOL
};

View File

@ -0,0 +1,207 @@
export class BoLRoll {
static options() {
return { classes: ["bol", "dialog"] };
}
static attributeCheck(actor, actorData, dataset, event) {
// const elt = $(event.currentTarget)[0];
// let key = elt.attributes["data-rolling"].value;
const key = dataset.key;
const adv = dataset.adv;
let attribute = eval(`actor.data.data.attributes.${key}`);
let label = (attribute.label) ? game.i18n.localize(attribute.label) : null;
let description = actor.name + " - " + game.i18n.localize('BOL.ui.attributeCheck') + " - " + game.i18n.localize(attribute.label) ;
return this.attributeRollDialog(actor, actorData, attribute, label, description, adv, 0);
}
static aptitudeCheck(actor, actorData, dataset, event) {
// const elt = $(event.currentTarget)[0];
// let key = elt.attributes["data-rolling"].value;
const key = dataset.key;
const adv = dataset.adv;
let aptitude = eval(`actor.data.data.aptitudes.${key}`);
let label = (aptitude.label) ? game.i18n.localize(aptitude.label) : null;
let description = actor.name + " - " + game.i18n.localize('BOL.ui.aptitudeCheck') + " - " + game.i18n.localize(aptitude.label) ;
return this.aptitudeRollDialog(actor, actorData, aptitude, label, description, adv, 0);
}
/* -------------------------------------------- */
/* ROLL DIALOGS */
/* -------------------------------------------- */
static async attributeRollDialog(actor, actorData, attribute, label, description, adv, mod, onEnter = "submit") {
const rollOptionTpl = 'systems/bol/templates/dialogs/attribute-roll-dialog.hbs';
const dialogData = {
adv:adv,
mod: mod,
attr:attribute,
careers:actorData.data.features.careers,
boons:actorData.data.features.boons,
flaws:actorData.data.features.flaws
};
const rollOptionContent = await renderTemplate(rollOptionTpl, dialogData);
let d = new Dialog({
title: label,
content: rollOptionContent,
buttons: {
cancel: {
icon: '<i class="fas fa-times"></i>',
label: game.i18n.localize("BOL.ui.cancel"),
callback: () => {
}
},
submit: {
icon: '<i class="fas fa-check"></i>',
label: game.i18n.localize("BOL.ui.submit"),
callback: (html) => {
const attr = html.find('#attr').val();
const adv = html.find('#adv').val();
const mod = html.find('#mod').val();
const careers = html.find('#career').val();
const career = (careers.size >0) ? Math.max(...html.find('#career').val().map(i => parseInt(i))) : 0;
const isMalus = adv < 0;
const dicePool = (isMalus) ? 2 - parseInt(adv) : 2 + parseInt(adv);
const attrValue = eval(`actor.data.data.attributes.${attr}.value`);
const modifiers = parseInt(attrValue) + parseInt(mod) + parseInt(career);
const formula = (isMalus) ? dicePool + "d6kl2 + " + modifiers : dicePool + "d6kh2 + " + modifiers;
let r = new BoLDefaultRoll(label, formula, description);
r.roll(actor);
}
}
},
default: onEnter,
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 = {
adv:adv,
mod: mod,
apt:aptitude,
careers:actorData.data.features.careers,
boons:actorData.data.features.boons,
flaws:actorData.data.features.flaws
};
const rollOptionContent = await renderTemplate(rollOptionTpl, dialogData);
let d = new Dialog({
title: label,
content: rollOptionContent,
buttons: {
cancel: {
icon: '<i class="fas fa-times"></i>',
label: game.i18n.localize("BOL.ui.cancel"),
callback: () => {
}
},
submit: {
icon: '<i class="fas fa-check"></i>',
label: game.i18n.localize("BOL.ui.submit"),
callback: (html) => {
const apt = html.find('#apt').val();
const adv = html.find('#adv').val();
const mod = html.find('#mod').val();
const careers = html.find('#career').val();
const career = (careers.size >0) ? Math.max(...html.find('#career').val().map(i => parseInt(i))) : 0;
const isMalus = adv < 0;
const dicePool = (isMalus) ? 2 - parseInt(adv) : 2 + parseInt(adv);
const aptValue = eval(`actor.data.data.aptitudes.${apt}.value`);
const modifiers = parseInt(aptValue) + parseInt(mod) + parseInt(career);
const formula = (isMalus) ? dicePool + "d6kl2 + " + modifiers : dicePool + "d6kh2 + " + modifiers;
let r = new BoLDefaultRoll(label, formula, description);
r.roll(actor);
}
}
},
default: onEnter,
close: () => {}
}, this.options());
return d.render(true);
}
}
export class BoLDefaultRoll {
constructor(label, formula, description){
this._label = label;
this._formula = formula;
this._isSuccess = false;
this._isCritical = false;
this._isFumble = false;
this._description = description;
}
async roll(actor){
const r = new Roll(this._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(actor).then(msgFlavor => {
r.toMessage({
user: game.user.id,
flavor: msgFlavor,
speaker: ChatMessage.getSpeaker({actor: actor}),
flags : {msgType : "default"}
});
});
}
_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;
// this._formula = formula;
// this._isCritical = isCritical;
// this._description = description;
// }
//
// _buildChatMessage() {
// const rollMessageTpl = 'systems/bol/templates/chat/rolls/weapon-roll-card.hbs';
// const tplData = {
// label : this._label,
// isCritical : this._isCritical,
// hasDescription : this._description && this._description.length > 0,
// description : this._description
// };
// return renderTemplate(rollMessageTpl, tplData);
// }
// }
// export class BoLSpellRoll {
// constructor(actor, label, formula, isCritical, description){
// this._label = label;
// this._formula = formula;
// this._isCritical = isCritical;
// this._description = description;
// }
//
// _buildChatMessage() {
// const rollMessageTpl = 'systems/bol/templates/chat/rolls/spell-roll-card.hbs';
// const tplData = {
// label : this._label,
// isCritical : this._isCritical,
// hasDescription : this._description && this._description.length > 0,
// description : this._description
// };
// return renderTemplate(rollMessageTpl, tplData);
// }
// }

View File

@ -98,8 +98,7 @@ BOL.featureSubtypes = {
"race" : "BOL.featureSubtypes.race",
"career" : "BOL.featureSubtypes.career",
"boon" : "BOL.featureSubtypes.boon",
"flaw" : "BOL.featureSubtypes.flaw",
"language" : "BOL.featureSubtypes.language"
"flaw" : "BOL.featureSubtypes.flaw"
}
BOL.itemIcons = {

View File

@ -1,79 +1,19 @@
export default function registerHooks() {
// Hooks.on("getChatLogEntryContext", (html, options) => {
// let canApplyDamage = li => li.find("h2.damage").length;
// let canApplyHealing = li => li.find("h2.heal").length;
// options.push(
// {
// name: game.i18n.localize("COF.ui.applyDamage"),
// icon: '<i class="fas fa-user-minus"></i>',
// condition: canApplyDamage,
// callback: li => {
// const dmg = parseInt(li.find(".dice-total").text());
// Hitpoints.applyToTargets(-dmg);
// }
// },
// {
// name: game.i18n.localize("COF.ui.applyDamage"),
// icon: '<i class="fas fa-user-minus"></i>',
// condition: canApplyHealing,
// callback: li => {
// const dmg = parseInt(li.find(".dice-total").text());
// Hitpoints.applyToTargets(-dmg);
// }
// },
// {
// name: game.i18n.localize("COF.ui.applyHalfDamage"),
// icon: '<i class="fas fa-user-shield"></i>',
// condition: canApplyDamage,
// callback: li => {
// const dmg = Math.ceil(parseInt(li.find(".dice-total").text()) / 2);
// Hitpoints.applyToTargets(-dmg);
// }
// },
// {
// name: game.i18n.localize("COF.ui.applyDoubleDamage"),
// icon: '<i class="fas fa-user-injured"></i>',
// condition: canApplyDamage,
// callback: li => {
// const dmg = parseInt(li.find(".dice-total").text())*2;
// Hitpoints.applyToTargets(-dmg);
// }
// },
// {
// name: game.i18n.localize("COF.ui.applyHealing"),
// icon: '<i class="fas fa-user-plus"></i>',
// condition: canApplyDamage,
// callback: li => {
// const dmg = parseInt(li.find(".dice-total").text());
// Hitpoints.applyToTargets(dmg);
// }
// },
// {
// name: game.i18n.localize("COF.ui.applyHealing"),
// icon: '<i class="fas fa-user-plus"></i>',
// condition: canApplyHealing,
// callback: li => {
// const dmg = parseInt(li.find(".dice-total").text());
// Hitpoints.applyToTargets(dmg);
// }
// }
// );
// });
//
// /**
// * Create a macro when dropping an entity on the hotbar
// * Item - open roll dialog for item
// * Actor - open actor sheet
// * Journal - open journal sheet
// */
//
// Hooks.on("hotbarDrop", async (bar, data, slot) => {
// // Create item macro if rollable item - weapon, spell, prayer, trait, or skill
// if (data.type == "Item") {
// let item = data.data;
/**
* Create a macro when dropping an entity on the hotbar
* Item - open roll dialog for item
* Actor - open actor sheet
* Journal - open journal sheet
*/
Hooks.on("hotbarDrop", async (bar, data, slot) => {
console.log(data.type);
// Create item macro if rollable item - weapon, spell, prayer, trait, or skill
if (data.type == "Item") {
let item = data.data;
console.log(item);
// let command = `let onlyDamage = false;\nlet customLabel = "";\nlet skillDescription = "";\nlet dmgDescription = "";\n\nif (event) {\n if (event.shiftKey) onlyDamage = true;\n}\n\ngame.cof.macros.rollItemMacro("${item._id}", "${item.name}", "${item.type}", 0, 0, 0, onlyDamage, customLabel, skillDescription, dmgDescription);`;
//
// let macro = game.macros.entities.find(m => (m.name === item.name) && (m.command === command));
// if (!macro) {
// macro = await Macro.create({
@ -84,119 +24,38 @@ export default function registerHooks() {
// }, {displaySheet: false})
// }
// game.user.assignHotbarMacro(macro, slot);
// }
// // Create a macro to open the actor sheet of the actor dropped on the hotbar
// else if (data.type == "Actor") {
// let actor = game.actors.get(data.id);
// let command = `game.actors.get("${data.id}").sheet.render(true)`
// let macro = game.macros.entities.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 (data.type == "JournalEntry") {
// let journal = game.journal.get(data.id);
// let command = `game.journal.get("${data.id}").sheet.render(true)`
// let macro = game.macros.entities.find(m => (m.name === journal.name) && (m.command === command));
// if (!macro) {
// macro = await Macro.create({
// name: journal.data.name,
// type: "script",
// img: (journal.data.img) ? journal.data.img : "icons/svg/book.svg",
// command: command
// }, {displaySheet: false})
// game.user.assignHotbarMacro(macro, slot);
// }
// }
// return false;
// });
//
//
// /**
// * Intercepte les commandes de chat
// * /stat - Jet de caractéristique
// * /skill stat - Jet de caractéristique
// * /stats - Génère les caractéristiques d'un personnage
// */
//
// Hooks.on("chatMessage", (html, content, msg) => {
// let regExp;
// regExp = /(\S+)/g;
// let commands = content.match(regExp);
// let command = (commands.length>0 && commands[0].split("/").length > 0) ? commands[0].split("/")[1].trim() : null;
// let arg1 = (commands.length > 1) ? commands[1].trim() : null;
// const actor = game.cof.macros.getSpeakersActor();
//
// const validCommands = ["for", "str", "dex", "con", "int", "sag", "wis", "cha", "atc", "melee", "atd", "ranged", "atm", "magic"];
//
// if(command && validCommands.includes(command)) {
// game.cof.macros.rollStatMacro(actor, command, 0, 0, null);
// return false;
// }
// else if(command && command === "skill") {
// if(arg1 && validCommands.includes(arg1)) {
// game.cof.macros.rollStatMacro(actor, arg1, 0, 0, null);
// } else {
// ui.notifications.error("Vous devez préciser la caractéristique à tester, par exemple \"/skill str\".");
// }
// return false;
// }
// else if(command && command === "stats") {
// CharacterGeneration.statsCommand();
// return false;
// }
// });
//
// Hooks.on("renderChatMessage", (message, html, data) => {
// // Affiche ou non les boutons d'application des dommages
// if (game.settings.get("cof", "displayChatDamageButtonsToAll")) {
// html.find(".apply-dmg").click(ev => Hitpoints.onClickChatMessageApplyButton(ev, html, data));
// }
// else {
// if (game.user.isGM){
// html.find(".apply-dmg").click(ev => Hitpoints.onClickChatMessageApplyButton(ev, html, data));
// }
// else {
// html.find(".apply-dmg").each((i, btn) => {
// btn.style.display = "none"
// });
// }
// }
// });
// Hooks.on("preCreateChatMessage", (data, options, user) => {
// console.debug("preCreateChatMessage");
// // console.log(data,options,user);
// return true;
// });
// Hooks.on("createChatMessage", (message, options, user) => {
// console.debug("createChatMessage");
// // console.log(message,options,user);
// return true;
// });
// Hooks.on("updateChatMessage", (message, update, options, user) => {
// console.debug("updateChatMessage");
// // console.log(message,update,options,user);
// return true;
// });
// Hooks.on("renderItemSheet", (app, html, data) => {
// console.debug("renderItemSheet");
// return true;
// });
// Hooks.on("renderChatLog", (app, html, data) => {
// console.debug("renderChatLog");
// return true;
// });
// Hooks.on('dropCanvasData', function (canvas, dropData) {
// console.debug("dropCanvasData");
// return true;
// });
}
// Create a macro to open the actor sheet of the actor dropped on the hotbar
else if (data.type == "Actor") {
let actor = game.actors.get(data.id);
let command = `/*\nPersonnalisez la macro selon vos besoins en suivant les exemples suivants : \ngame.bol.macros.rollMacro('attribute', 'vigor|agility|mind|appeal', adv, mod);\ngame.bol.macros.rollMacro('aptitude', 'init|melee|ranged|def', adv, mod);\n*/\ngame.bol.macros.rollMacro('attribute', 'vigor', 0, 0);`;
let macro = game.macros.entities.find(m => (m.name === actor.name) && (m.command === command));
if (!macro) {
macro = await Macro.create({
name: actor.data.name,
type: "script",
img: "icons/svg/dice-target.svg",
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 (data.type == "JournalEntry") {
let journal = game.journal.get(data.id);
console.log(journal);
let command = `game.journal.get("${data.id}").sheet.render(true)`
let macro = game.macros.entities.find(m => (m.name === journal.name) && (m.command === command));
if (!macro) {
macro = await Macro.create({
name: journal.data.name,
type: "script",
img: (journal.data.img) ? journal.data.img : "icons/svg/book.svg",
command: command
}, {displaySheet: false})
game.user.assignHotbarMacro(macro, slot);
}
}
return false;
});
}

52
module/system/macros.js Normal file
View File

@ -0,0 +1,52 @@
import {BoLRoll} from "../controllers/bol-rolls.js";
export class Macros {
/**
* @name getSpeakersActor
* @description
*
* @returns
*/
static getSpeakersActor = function(){
// Vérifie qu'un seul token est sélectionné
const tokens = canvas.tokens.controlled;
if (tokens.length > 1) {
ui.notifications.warn(game.i18n.localize('BOL.notification.MacroMultipleTokensSelected'));
return null;
}
const speaker = ChatMessage.getSpeaker();
let actor;
// Si un token est sélectionné, le prendre comme acteur cible
if (speaker.token) actor = game.actors.tokens[speaker.token];
// Sinon prendre l'acteur par défaut pour l'utilisateur courrant
if (!actor) actor = game.actors.get(speaker.actor);
return actor;
}
static rollMacro = async function (rollType, key, adv, mod){
const actor = this.getSpeakersActor();
// Several tokens selected
if (actor === null) return;
// No token selected
if (actor === undefined) return ui.notifications.error(game.i18n.localize("BOL.notification.MacroNoTokenSelected"));
const actorData = {};
actorData.data = {
features : actor.buildFeatures()
};
if(rollType === "attribute") {
let attribute = eval(`actor.data.data.attributes.${key}`);
let rollLabel = (attribute.label) ? game.i18n.localize(attribute.label) : null;
let description = actor.name + " - " + game.i18n.localize('BOL.ui.attributeCheck') + " - " + game.i18n.localize(attribute.label) ;
BoLRoll.attributeRollDialog(actor, actorData, attribute, rollLabel, description, adv, mod);
}
else if(rollType === "aptitude") {
let aptitude = eval(`actor.data.data.aptitudes.${key}`);
let rollLabel = (aptitude.label) ? game.i18n.localize(aptitude.label) : null;
let description = actor.name + " - " + game.i18n.localize('BOL.ui.aptitudeCheck') + " - " + game.i18n.localize(aptitude.label) ;
BoLRoll.aptitudeRollDialog(actor, actorData, aptitude, rollLabel, description, adv, mod);
}
}
}

View File

@ -1,4 +1,4 @@
import { BoLUtility } from "../system/bol-utility.js";
import { BoLUtility } from "./bol-utility.js";
export class BoLRollDialog extends Dialog {
@ -47,7 +47,7 @@ export class BoLRollDialog extends Dialog {
activateListeners(html) {
super.activateListeners(html);
var dialog = this;
let dialog = this;
function onLoad() {
}
$(function () { onLoad(); });

View File

@ -16,14 +16,19 @@ export const preloadHandlebarsTemplates = async function () {
// ITEMS
"systems/bol/templates/item/parts/item-header.hbs",
"systems/bol/templates/item/parts/properties/feature-properties.hbs",
"systems/bol/templates/item/parts/properties/equipment-properties.hbs",
"systems/bol/templates/item/parts/properties/protection-properties.hbs",
"systems/bol/templates/item/parts/properties/shield-properties.hbs",
"systems/bol/templates/item/parts/properties/weapon-properties.hbs",
"systems/bol/templates/item/parts/properties/armor-properties.hbs",
"systems/bol/templates/item/parts/properties/melee-properties.hbs",
"systems/bol/templates/item/parts/properties/ranged-properties.hbs",
"systems/bol/templates/item/parts/properties/item-properties.hbs",
"systems/bol/templates/item/parts/properties/item/equipment-properties.hbs",
"systems/bol/templates/item/parts/properties/item/protection-properties.hbs",
"systems/bol/templates/item/parts/properties/item/shield-properties.hbs",
"systems/bol/templates/item/parts/properties/item/weapon-properties.hbs",
"systems/bol/templates/item/parts/properties/item/armor-properties.hbs",
"systems/bol/templates/item/parts/properties/item/melee-properties.hbs",
"systems/bol/templates/item/parts/properties/item/ranged-properties.hbs",
"systems/bol/templates/item/parts/properties/feature/career-properties.hbs",
"systems/bol/templates/item/parts/properties/feature/boon-properties.hbs",
"systems/bol/templates/item/parts/properties/feature/flaw-properties.hbs",
"systems/bol/templates/item/parts/properties/feature/origin-properties.hbs",
"systems/bol/templates/item/parts/properties/feature/race-properties.hbs",
// DIALOGS
"systems/bol/templates/roll/parts/roll-dialog-modifiers.hbs",
"systems/bol/templates/roll/parts/roll-dialog-attribute.hbs"

View File

@ -3,6 +3,7 @@
@import "global/typography";
@import "global/item-list";
@import "global/colors";
@import 'global/chat';
@logo-width: 190px;
@logo-height: 115px;
@ -30,6 +31,13 @@
}
}
&.dialog {
.sheet-header{
h3 {
font-family: @font-tertiary;
font-size: 24px;
color: black;
}
}
}
}
@ -38,23 +46,16 @@
}
.rollable {
color: @colorOlive;
//color: @colorOlive;
cursor: pointer;
}
.malus {
color: darkred;
}
.bonus {
color: darkgreen;
}
.chat-message .chat-icon {
flex : 0 0 64px;
border: 1px outset lightgray;
//padding: 2px 6px 2px 2px;
//float: left;
float:right;
border:1px outset lightgray;
box-shadow: 3px 3px 3px black;
margin: 3px;
//flex : 0 0 64px;
width: 64px;
height: 64px;
}

View File

@ -115,11 +115,20 @@
.stat-value {
font-size: 1.5rem;
font-weight: bold;
color: darkred;
color: @c-darkred;
}
.stat-roll {
font-size: 1.5rem;
color: @colorOlive;
&.malus {
color: @c-darkred;
}
&.bonus {
color: darkgreen;
}
}
.header-field-label,
@ -139,6 +148,7 @@
.rounded-border {
border: 3px solid @colorOlive;
box-shadow: 5px 5px 5px gray;
border-radius: 100px;
width: 4rem;
height: 4rem;

26
styles/global/chat.less Normal file
View File

@ -0,0 +1,26 @@
.message-header {
h2.damage{
color: @colorDamage;
font-weight: bold;
}
h2.critical{
color: @colorCritical;
font-weight: bold;
}
h2.fumble{
color: @colorFumble;
font-weight: bold;
}
h2.success{
color: @colorSuccess;
font-weight: bold;
}
h2.failure{
color: @colorFailure;
font-weight: bold;
}
h2.roll{
color: @colorRoll;
font-weight: bold;
}
}

View File

@ -12,6 +12,7 @@
@c-blue: #009ee0;
@c-green: #44a12b;
@c-red: #cd071e;
@c-darkred: darkred;
@c-purple: purple;
@c-darkred: darkred;
@c-border:#736953a6;
@ -128,6 +129,14 @@
color: @c-white;
}
.darkred {
color: @c-darkred;
}
.bg-darkred {
background: @c-darkred;
color: @c-white;
}
.purple {
color: @c-purple;
}

View File

@ -49,11 +49,6 @@
}
select {
//-webkit-appearance: none;
//-moz-appearance: none;
//color: $c-black;
//color: $colorDark;
//font-family: $font-primary;
font-size: 14px;
text-align: center;
text-align-last: center;
@ -61,16 +56,31 @@
width: 100%;
border: none;
border-radius: 0;
// height: calc(100% - 2px);
// //border: 1px solid $colorTan;
// background: rgba(0, 0, 0, 0.05);
// border-radius: 0;
// border: none;
// box-shadow: none;
// color: $colorDark;
}
select[multiple]{
box-shadow: none;
border: none;
font-size: 12px;
&:focus {
option:checked {
background: @c-darkred linear-gradient(0deg, @c-darkred 0%, @c-darkred 100%);
color:white;
}
}
}
option:hover,
option:focus,
option:active,
option:checked,
option[selected] {
cursor: pointer;
background: @c-darkred linear-gradient(0deg, @c-darkred 0%, @c-darkred 100%);
color:white;
}
//option:not(:checked) {
// color: black;
//} /* or whatever your default style is */
label.checkbox {
flex: auto;

View File

@ -7,6 +7,12 @@
"biography": "",
"notes": "",
"languages": [],
"creation": {
"key" : "creation",
"label" : "BOL.resources.creation",
"value": 0,
"max": 0
},
"xp": {
"key" : "xp",
"label" : "BOL.traits.xp",
@ -19,12 +25,6 @@
"base": 0,
"value": 0,
"bonus": 0
},
"creation": {
"key" : "creation",
"label" : "BOL.resources.creation",
"value": 0,
"max": 0
}
},
"attributes": {
@ -119,12 +119,6 @@
"label" : "BOL.resources.power",
"value": 0,
"max": 0
},
"villainy" : {
"key" : "villainy",
"label" : "BOL.resources.villainy",
"value": 5,
"max": 5
}
}
}
@ -154,11 +148,9 @@
"armor" : false,
"helm" : false,
"shield" : false,
"equipable": false,
"consumable" : false,
"magical" : false,
"2H" : false,
"reloadable" : false,
"bow" : false,
@ -170,40 +162,10 @@
"item": {
"templates": ["base", "equipment"]
},
"weapon": {
"type": "",
"quantity": 1,
"weight": 0,
"damage": "",
"range": "",
"thrown": false,
"twohands": false,
"reload": false,
"reloadduration": 0,
"canbehidden": false,
"ignoreshield": false,
"improvised": false,
"equipped": false,
"description": ""
},
"armor": {
"type": "",
"protectionroll": "",
"protectionfixed": 0,
"socialmalus": false,
"agilitymalus": 0,
"initmalus": 0,
"powercost": 0,
"equipped": false,
"description": ""
},
"feature": {
"rank": 0,
"templates": ["base"],
"properties" : {
"d6B": false,
"d6M": false
}
"properties" : {}
}
}
}

View File

@ -2,12 +2,17 @@
<div class="wrap flexrow">
{{!-- Sidebar --}}
<div class="sidebar flex0">
<img class="profile-img" src="{{actor.img}}" data-edit="img" title="{{actor.name}}" height="100" width="100"/>
<img class="profile-img" src="{{actor.img}}" data-edit="img" title="{{actor.name}}" height="100" width="100" style="border:1px outset lightgray; box-shadow: 5px 5px 5px gray"/>
</div>
<div class="main flex1">
{{> "systems/bol/templates/actor/parts/actor-header.hbs"}}
{{!-- Sheet Tab Navigation --}}
<nav class="sheet-tabs tabs" data-group="primary">
<!-- <a class="item" data-tab="stats" title="{{localize "BOL.ui.tab.stats"}}"><i class="fas fa-id-card"></i></a>-->
<!-- <a class="item" data-tab="combat" title="{{localize "BOL.ui.tab.combat"}}"><i class="fas fa-fist-raised"></i></a>-->
<!-- <a class="item" data-tab="features" title="{{localize "BOL.ui.tab.features"}}"><i class="fas fa-wrench"></i></a>-->
<!-- <a class="item" data-tab="equipment" title="{{localize "BOL.ui.tab.equipment"}}"><i class="fas fa-box-open"></i></a>-->
<!-- <a class="item" data-tab="description" title="{{localize "BOL.ui.tab.description"}}"><i class="fas fa-portrait"></i></a>-->
<a class="item" data-tab="stats">{{localize "BOL.ui.tab.stats"}}</a>
<a class="item" data-tab="combat">{{localize "BOL.ui.tab.combat"}}</a>
<a class="item" data-tab="features">{{localize "BOL.ui.tab.features"}}</a>

View File

@ -4,14 +4,14 @@
<li class="item flexrow item-header">
<div class="item-name flex2">{{localize features.label}}</div>
{{#if ranked}}
<div class="item-field">{{localize "Level"}}</div>
<div class="item-field">{{localize "BOL.ui.rank"}}</div>
{{/if}}
</li>
{{#each features.items as |item id|}}
<li class="item flexrow" data-item-id="{{item._id}}">
<div class="item-image {{#if (equals key "careers")}}roll-career{{/if}}"><img src="{{item.img}}" title="{{item.name}}"/></div>
<h4 class="item-name flex2"><a class="item-edit">{{item.name}}</a></h4>
{{#if item.data.rank}}
{{#if (equals key "careers")}}
<span class="item-field">{{item.data.rank}}</span>
{{/if}}
<div class="item-controls-1">

View File

@ -1,11 +1,11 @@
<div class="attributes flexrow">
{{#each data.attributes as |attribute id|}}
<div class="attribute stat flex1 flex-group-center {{key}}">
<label class="stat-label"><a class="roll-attribute" data-attr-key="{{key}}">{{localize label}}</a></label><br/>
<label class="stat-label"><a class="rollable" data-roll-type="attribute" data-key="{{key}}">{{localize label}}</a></label><br/>
<input class="stat-value rounded" type="text" name="data.attributes.{{key}}.value" value="{{numberFormat value decimals=0 sign=true}}" data-dtype="Number"/><br/>
<span class="stat-roll malus rollable" title="2d6M" data-roll="3d6kh2+@attributes.{{key}}.value" data-label="{{key}}"><i class="fas fa-dice"></i></span>
<span class="stat-roll rollable" title="2d6" data-roll="2d6+@attributes.{{key}}.value" data-label="{{key}}"><i class="fas fa-dice"></i></span>
<span class="stat-roll bonus rollable" title="2d6B" data-roll="3d6kl2+@attributes.{{key}}.value" data-label="{{key}}"><i class="fas fa-dice"></i></span>
<span class="stat-roll malus rollable" title="2d6M" data-roll-type="attribute" data-roll="3d6kh2+@attributes.{{key}}.value" data-adv="-1" data-key="{{key}}"><i class="fas fa-dice"></i></span>
<span class="stat-roll rollable" title="2d6" data-roll-type="attribute" data-roll="2d6+@attributes.{{key}}.value" data-adv="0" data-key="{{key}}"><i class="fas fa-dice"></i></span>
<span class="stat-roll bonus rollable" title="2d6B" data-roll-type="attribute" data-roll="3d6kl2+@attributes.{{key}}.value" data-adv="1" data-key="{{key}}"><i class="fas fa-dice"></i></span>
</div>
{{/each}}
</div>
@ -13,11 +13,11 @@
<div class="aptitudes flexrow">
{{#each data.aptitudes as |aptitude id|}}
<div class="aptitude stat flex1 flex-group-center">
<label class="stat-label"><a class="roll-attribute" data-attr-key="{{key}}">{{localize label}}</a></label><br/>
<label class="stat-label"><a class="rollable" data-roll-type="aptitude" data-key="{{key}}">{{localize label}}</a></label><br/>
<input class="stat-value rounded-border" type="text" name="data.aptitudes.{{key}}.value" value="{{numberFormat value decimals=0 sign=true}}" data-dtype="Number"/><br/>
<span class="stat-roll malus rollable" title="2d6M" data-roll="3d6kh2+@aptitudes.{{key}}.value" data-label="{{key}}"><i class="fas fa-dice"></i></span>
<span class="stat-roll rollable" title="2d6" data-roll="2d6+@aptitudes.{{key}}.value" data-label="{{key}}"><i class="fas fa-dice"></i></span>
<span class="stat-roll bonus rollable" title="2d6B" data-roll="3d6kl2+@aptitudes.{{key}}.value" data-label="{{key}}"><i class="fas fa-dice"></i></span>
<span class="stat-roll malus rollable" title="2d6M" data-roll-type="aptitude" data-roll="3d6kh2+@aptitudes.{{key}}.value" data-adv="-1" data-key="{{key}}"><i class="fas fa-dice"></i></span>
<span class="stat-roll rollable" title="2d6" data-roll-type="aptitude" data-roll="2d6+@aptitudes.{{key}}.value" data-adv="0" data-key="{{key}}"><i class="fas fa-dice"></i></span>
<span class="stat-roll bonus rollable" title="2d6B" data-roll-type="aptitude" data-roll="3d6kl2+@aptitudes.{{key}}.value" data-adv="1" data-key="{{key}}"><i class="fas fa-dice"></i></span>
</div>
{{/each}}
</div>

View File

@ -0,0 +1,21 @@
<img class="chat-icon" src="{{actor.img}}" alt="{{actor.name}}"/>
{{#if isSuccess}}
{{#if isCritical}}
<h2 class="success critical"><i class="fas fa-check-double"></i>&nbsp;{{localize "BOL.ui.critical"}}...</h2>
{{else}}
<h2 class="success"><i class="fas fa-check"></i>&nbsp;{{localize "BOL.ui.success"}}...</h2>
{{/if}}
{{/if}}
{{#if isFailure}}
{{#if isFumble}}
<h2 class="failure fumble"><i class="fas fa-skull-crossbones"></i>&nbsp;{{localize "BOL.ui.fumble"}}...</h2>
{{else}}
<h2 class="failure"><i class="fas fa-times"></i>&nbsp;{{localize "BOL.ui.failure"}}...</h2>
{{/if}}
{{/if}}
<h3><strong>{{description}}</strong>
{{!#if hasDescription}}
<!--<h4>-->
<!-- <pre class="rollDescr-line">{{!description}}</pre>-->
<!--</h4>-->
{{!/if}}

View File

View File

@ -0,0 +1,51 @@
<form class="{{cssClass}}" autocomplete="off">
{{!-- Sheet Header --}}
<header class="sheet-header">
<div class="row flexrow table-header">
<div class="flex1 center">
<h3>{{localize 'BOL.ui.aptitudeCheck'}}</h3>
</div>
</div>
</header>
<div class="flexrow" style="margin-bottom: 1px;">
<div class="flex1 center bg-darkslate">
<label for="apt">{{localize 'BOL.ui.aptitude'}}</label>
</div>
<div class="flex1 center cell">
<select class="flex1" name="apt" id="apt" data-type="String">
<option value="init" {{#if (equals apt.key "init")}}selected{{/if}}>{{localize 'BOL.aptitudes.init'}}</option>
<option value="melee" {{#if (equals apt.key "melee")}}selected{{/if}}>{{localize 'BOL.aptitudes.melee'}}</option>
<option value="ranged" {{#if (equals apt.key "ranged")}}selected{{/if}}>{{localize 'BOL.aptitudes.ranged'}}</option>
<option value="def" {{#if (equals apt.key "def")}}selected{{/if}}>{{localize 'BOL.aptitudes.def'}}</option>
</select>
</div>
</div>
<div class="flexrow" style="margin-bottom: 1px;">
<div class="flex1 center bg-darkslate">
<label for="adv">{{localize 'BOL.ui.advantages'}}</label>
</div>
<div class="flex1 center cell">
<input type="text" class="field-value" name="adv" id="adv" value="{{adv}}" data-type="Number">
</div>
</div>
<div class="flexrow" style="margin-bottom: 1px;">
<div class="flex1 center bg-darkslate">
<label for="mod">{{localize 'BOL.ui.modifiers'}}</label>
</div>
<div class="flex1 center cell">
<input type="text" class="field-value" name="mod" id="mod" value="{{mod}}" data-type="Number">
</div>
</div>
<div class="flexrow" style="margin-bottom: 1px;">
<div class="flex1 center bg-darkslate">
<label for="mod">{{localize 'BOL.ui.careers'}}</label>
</div>
<div class="flex1 center cell">
<select class="flex1" name="career" id="career" data-type="String" multiple>
{{#each careers.items as | career id|}}
<option value="{{career.data.rank}}">{{career.name}} ({{numberFormat career.data.rank decimals=0 sign=true}})</option>
{{/each}}
</select>
</div>
</div>
</form>

View File

@ -0,0 +1,75 @@
<form class="{{cssClass}}" autocomplete="off">
{{!-- Sheet Header --}}
<header class="sheet-header">
<div class="row flexrow table-header">
<div class="flex1 center">
<h3>{{localize 'BOL.ui.attributeCheck'}}</h3>
</div>
</div>
</header>
<div class="flexrow" style="margin-bottom: 1px;">
<div class="flex1 center bg-darkred">
<label for="attr">{{localize 'BOL.ui.attribute'}}</label>
</div>
<div class="flex1 center cell">
<select class="flex1" name="attr" id="attr" data-type="String">
<option value="vigor" {{#if (equals attr.key "vigor")}}selected{{/if}}>{{localize 'BOL.attributes.vigor'}}</option>
<option value="agility" {{#if (equals attr.key "agility")}}selected{{/if}}>{{localize 'BOL.attributes.agility'}}</option>
<option value="mind" {{#if (equals attr.key "mind")}}selected{{/if}}>{{localize 'BOL.attributes.mind'}}</option>
<option value="appeal" {{#if (equals attr.key "appeal")}}selected{{/if}}>{{localize 'BOL.attributes.appeal'}}</option>
</select>
</div>
</div>
<div class="flexrow" style="margin-bottom: 1px;">
<div class="flex1 center bg-darkred">
<label for="adv">{{localize 'BOL.ui.advantages'}}</label>
</div>
<div class="flex1 center cell">
<input type="text" class="field-value" name="adv" id="adv" value="{{adv}}" data-type="Number">
</div>
</div>
<div class="flexrow" style="margin-bottom: 1px;">
<div class="flex1 center bg-darkred">
<label for="mod">{{localize 'BOL.ui.modifiers'}}</label>
</div>
<div class="flex1 center cell">
<input type="text" class="field-value" name="mod" id="mod" value="{{mod}}" data-type="Number">
</div>
</div>
<div class="flexrow" style="margin-bottom: 1px;">
<div class="flex1 center bg-darkred">
<label for="mod">{{localize 'BOL.ui.careers'}}</label>
</div>
<div class="flex1 center cell">
<select class="flex1" name="career" id="career" data-type="String" multiple>
{{#each careers.items as | career id|}}
<option value="{{career.data.rank}}">{{career.name}} ({{numberFormat career.data.rank decimals=0 sign=true}})</option>
{{/each}}
</select>
</div>
</div>
<!--<div class="flexrow" style="margin-bottom: 1px;">-->
<!-- <div class="flex1 center bg-darkred">-->
<!-- <label for="mod">{{!localize 'BOL.ui.boons'}}</label>-->
<!-- </div>-->
<!-- <div class="flex1 center cell">-->
<!-- <select class="flex1" name="boon" id="boon" data-type="String" multiple>-->
<!-- {{!#each boons.items as | boon id|}}-->
<!-- <option value="{{!boon._id}}">{{!boon.name}}</option>-->
<!-- {{!/each}}-->
<!-- </select>-->
<!-- </div>-->
<!--</div>-->
<!--<div class="flexrow" style="margin-bottom: 1px;">-->
<!-- <div class="flex1 center bg-darkred">-->
<!-- <label for="mod">{{!localize 'BOL.ui.flaws'}}</label>-->
<!-- </div>-->
<!-- <div class="flex1 center cell">-->
<!-- <select class="flex1" name="flaw" id="flaw" data-type="String" multiple>-->
<!-- {{!#each flaws.items as | flaw id|}}-->
<!-- <option value="{{!flaw._id}}">{{flaw.name}}</option>-->
<!-- {{!/each}}-->
<!-- </select>-->
<!-- </div>-->
<!--</div>-->
</form>

View File

@ -9,9 +9,18 @@
</select>
</div>
{{#each data.properties as |property key|}}
<div class="property flexrow">
<label class="property-label">{{localize key}}</label>
<label class="attribute-value checkbox"><input type="checkbox" name="data.properties.{{key}}" {{checked property}}/></label>
</div>
{{/each}}
{{#if (equals data.subtype "career")}}
{{> "systems/bol/templates/item/parts/properties/feature/career-properties.hbs"}}
{{/if}}
{{!#if (equals data.subtype "origin")}}
{{!> "systems/bol/templates/item/parts/properties/feature/origin-properties.hbs"}}
{{!/if}}
{{!#if (equals data.subtype "race")}}
{{!> "systems/bol/templates/item/parts/properties/feature/race-properties.hbs"}}
{{!/if}}
{{!#if (equals data.subtype "boon")}}
{{!> "systems/bol/templates/item/parts/properties/feature/boon-properties.hbs"}}
{{!/if}}
{{!#if (equals data.subtype "flaw")}}
{{!> "systems/bol/templates/item/parts/properties/feature/flaw-properties.hbs"}}
{{!/if}}

View File

@ -0,0 +1 @@
<h3 class="form-header">{{localize "BOL.featureSubtypes.boon"}}</h3>

View File

@ -0,0 +1,5 @@
<h3 class="form-header">{{localize 'BOL.featureSubtypes.career'}}</h3>
<div class="property flexrow">
<label class="property-label">{{localize "BOL.ui.rank"}}</label>
<input type="text" name="data.rank" value="{{data.rank}}" data-dtype="Number"/>
</div>

View File

@ -0,0 +1 @@
<h3 class="form-header">{{localize "BOL.featureSubtypes.flaw"}}</h3>

View File

@ -0,0 +1 @@
<h3 class="form-header">{{localize "BOL.featureSubtypes.origin"}}</h3>

View File

@ -0,0 +1 @@
<h3 class="form-header">{{localize "BOL.featureSubtypes.race"}}</h3>

View File

@ -49,14 +49,14 @@
</div>
{{#if data.properties.equipable}}
{{> "systems/bol/templates/item/parts/properties/equipment-properties.hbs"}}
{{> "systems/bol/templates/item/parts/properties/item/equipment-properties.hbs"}}
{{/if}}
{{#if data.properties.weapon}}
{{> "systems/bol/templates/item/parts/properties/weapon-properties.hbs"}}
{{> "systems/bol/templates/item/parts/properties/item/weapon-properties.hbs"}}
{{/if}}
{{#if (equals data.properties.protection)}}
{{> "systems/bol/templates/item/parts/properties/protection-properties.hbs"}}
{{> "systems/bol/templates/item/parts/properties/item/protection-properties.hbs"}}
{{/if}}
{{#if data.properties.shield}}
{{> "systems/bol/templates/item/parts/properties/shield-properties.hbs"}}
{{> "systems/bol/templates/item/parts/properties/item/shield-properties.hbs"}}
{{/if}}