Add HUD actions

This commit is contained in:
sladecraven 2022-03-21 23:21:05 +01:00
parent fff6b2a733
commit a5ba6d5215
9 changed files with 357 additions and 196 deletions

View File

@ -982,3 +982,43 @@ body.system-bol img#logo {
visibility: visible; visibility: visible;
opacity: 1; opacity: 1;
} }
/** HUD SECTION */
.tokenhudext {
display: flex;
flex: 0 !important;
font-family: CaslonPro;
font-weight: 600;
}
.tokenhudext.left {
justify-content: flex-start;
flex-direction: column;
position: absolute;
top: 2.75rem;
right: 4rem;
}
.tokenhudext.right {
justify-content: flex-start;
flex-direction: column;
position: absolute;
top: 2.75rem;
left: 4rem;
}
.control-icon.tokenhudicon {
width: fit-content;
height: fit-content;
min-width: 6rem;
flex-basis: auto;
padding: 0;
line-height: 1rem;
margin: 0.25rem;
}
.control-icon.tokenhudicon.right {
margin-left: 8px;
}
#token-hud .status-effects.active{
z-index: 2;
}
.bol-hud-menu label {
font-size: 0.75rem;
}

View File

@ -192,24 +192,23 @@ export class BoLActorSheet extends ActorSheet {
event.preventDefault(); event.preventDefault();
const element = event.currentTarget const element = event.currentTarget
const dataset = element.dataset const dataset = element.dataset
const actorData = this.getData()
const rollType = dataset.rollType const rollType = dataset.rollType
const li = $(event.currentTarget).closest(".item") const li = $(event.currentTarget).closest(".item")
switch (rollType) { switch (rollType) {
case "attribute": case "attribute":
BoLRoll.attributeCheck(this.actor, actorData, dataset, event); BoLRoll.attributeCheck(this.actor, dataset.key, event)
break; break;
case "aptitude": case "aptitude":
BoLRoll.aptitudeCheck(this.actor, actorData, dataset, event); BoLRoll.aptitudeCheck(this.actor, dataset.key, event)
break; break;
case "weapon": case "weapon":
BoLRoll.weaponCheck(this.actor, actorData, dataset, event); BoLRoll.weaponCheck(this.actor, event)
break; break;
case "spell": case "spell":
BoLRoll.spellCheck(this.actor, actorData, dataset, event); BoLRoll.spellCheck(this.actor, event)
break; break;
case "alchemy": case "alchemy":
BoLRoll.alchemyCheck(this.actor, actorData, dataset, event); BoLRoll.alchemyCheck(this.actor, event)
break; break;
case "protection": case "protection":
this.actor.rollProtection(li.data("item-id")) this.actor.rollProtection(li.data("item-id"))

View File

@ -23,15 +23,15 @@ export class BoLActor extends Actor {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
updateResourcesData( ) { updateResourcesData() {
if ( this.type == 'character') { if (this.type == 'character') {
let newVitality = 10 + this.data.data.attributes.vigor.value + this.data.data.resources.hp.bonus let newVitality = 10 + this.data.data.attributes.vigor.value + this.data.data.resources.hp.bonus
if ( this.data.data.resources.hp.max != newVitality) { if (this.data.data.resources.hp.max != newVitality) {
this.update( {'data.resources.hp.max': newVitality} ); this.update({ 'data.resources.hp.max': newVitality });
} }
let newPower = 10 + this.data.data.attributes.mind.value + this.data.data.resources.power.bonus let newPower = 10 + this.data.data.attributes.mind.value + this.data.data.resources.power.bonus
if ( this.data.data.resources.power.max != newPower) { if (this.data.data.resources.power.max != newPower) {
this.update( {'data.resources.power.max': newPower} ); this.update({ 'data.resources.power.max': newPower });
} }
} }
} }
@ -44,7 +44,7 @@ export class BoLActor extends Actor {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
get itemData(){ get itemData() {
return Array.from(this.data.items.values()).map(i => i.data) return Array.from(this.data.items.values()).map(i => i.data)
} }
get details() { get details() {
@ -58,11 +58,11 @@ export class BoLActor extends Actor {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
clearRoundModifiers( ) { // Process data/items that are finished at end of a round clearRoundModifiers() { // Process data/items that are finished at end of a round
let foList = this.fightoptions let foList = this.fightoptions
for(let fo of foList) { for (let fo of foList) {
if (fo.data.properties.used) { if (fo.data.properties.used) {
this.updateEmbeddedDocuments("Item", [ {_id: fo._id, 'data.properties.used': false}] ) this.updateEmbeddedDocuments("Item", [{ _id: fo._id, 'data.properties.used': false }])
} }
} }
} }
@ -71,27 +71,27 @@ export class BoLActor extends Actor {
get defenseValue() { get defenseValue() {
let defMod = 0 let defMod = 0
let fo = this.getActiveFightOption() let fo = this.getActiveFightOption()
if (fo && fo.data.properties.fightoptiontype == "intrepid" ) { if (fo && fo.data.properties.fightoptiontype == "intrepid") {
defMod += -2 defMod += -2
} }
if (fo && fo.data.properties.fightoptiontype == "fulldefense" ) { if (fo && fo.data.properties.fightoptiontype == "fulldefense") {
defMod += 2 defMod += 2
} }
if (fo && fo.data.properties.fightoptiontype == "twoweaponsdef" && !fo.data.properties.used) { if (fo && fo.data.properties.fightoptiontype == "twoweaponsdef" && !fo.data.properties.used) {
defMod += 1 defMod += 1
this.updateEmbeddedDocuments("Item", [ {_id: fo._id, 'data.properties.used': true}] ) this.updateEmbeddedDocuments("Item", [{ _id: fo._id, 'data.properties.used': true }])
} }
if (fo && fo.data.properties.fightoptiontype == "defense" ) { if (fo && fo.data.properties.fightoptiontype == "defense") {
defMod += 1 defMod += 1
} }
if (fo && fo.data.properties.fightoptiontype == "attack" ) { if (fo && fo.data.properties.fightoptiontype == "attack") {
defMod += -1 defMod += -1
} }
return this.data.data.aptitudes.def.value + defMod return this.data.data.aptitudes.def.value + defMod
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
getActiveFightOption( ) { getActiveFightOption() {
let it = this.itemData.find(i => i.type === "feature" && i.data.subtype === "fightoption" && i.data.properties.activated) let it = this.itemData.find(i => i.type === "feature" && i.data.subtype === "fightoption" && i.data.properties.activated)
if (it) { if (it) {
return duplicate(it) return duplicate(it)
@ -100,15 +100,15 @@ export class BoLActor extends Actor {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
incAttributeXP( key) { incAttributeXP(key) {
let attr = duplicate(this.data.data.attributes[key]) let attr = duplicate(this.data.data.attributes[key])
if (attr) { if (attr) {
let nextXP = (attr.value == -1) ? 2 : attr.value + (attr.value+1) let nextXP = (attr.value == -1) ? 2 : attr.value + (attr.value + 1)
let xp = duplicate(this.data.data.xp) let xp = duplicate(this.data.data.xp)
if ( xp.total - xp.spent >= nextXP) { if (xp.total - xp.spent >= nextXP) {
attr.value += 1 attr.value += 1
xp.spent += nextXP xp.spent += nextXP
this.update( { [`data.attributes.${key}`]: attr, [`data.xp`]: xp } ) this.update({ [`data.attributes.${key}`]: attr, [`data.xp`]: xp })
} else { } else {
ui.notifications.warn("Pas assez de points d'expérience !") ui.notifications.warn("Pas assez de points d'expérience !")
} }
@ -116,31 +116,31 @@ export class BoLActor extends Actor {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
incAptitudeXP( key) { incAptitudeXP(key) {
let apt = duplicate(this.data.data.aptitudes[key]) let apt = duplicate(this.data.data.aptitudes[key])
if (apt) { if (apt) {
let nextXP = (apt.value == -1) ? 1 : apt.value + 2 let nextXP = (apt.value == -1) ? 1 : apt.value + 2
let xp = duplicate(this.data.data.xp) let xp = duplicate(this.data.data.xp)
if ( xp.total - xp.spent >= nextXP) { if (xp.total - xp.spent >= nextXP) {
apt.value += 1 apt.value += 1
xp.spent += nextXP xp.spent += nextXP
this.update( { [`data.aptitudes.${key}`]: apt, [`data.xp`]: xp } ) this.update({ [`data.aptitudes.${key}`]: apt, [`data.xp`]: xp })
} else { } else {
ui.notifications.warn("Pas assez de points d'expérience !") ui.notifications.warn("Pas assez de points d'expérience !")
} }
} }
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
incCareerXP( itemId) { incCareerXP(itemId) {
let career = this.data.items.get( itemId ) let career = this.data.items.get(itemId)
if (career) { if (career) {
career = duplicate(career) career = duplicate(career)
let nextXP = career.data.rank + 1 let nextXP = career.data.rank + 1
let xp = duplicate(this.data.data.xp) let xp = duplicate(this.data.data.xp)
if ( xp.total - xp.spent >= nextXP) { if (xp.total - xp.spent >= nextXP) {
xp.spent += nextXP xp.spent += nextXP
this.update( { [`data.xp`]: xp } ) this.update({ [`data.xp`]: xp })
this.updateEmbeddedDocuments( 'Item', [ { _id: career._id, 'data.rank': career.data.rank + 1 } ]) this.updateEmbeddedDocuments('Item', [{ _id: career._id, 'data.rank': career.data.rank + 1 }])
} else { } else {
ui.notifications.warn("Pas assez de points d'expérience !") ui.notifications.warn("Pas assez de points d'expérience !")
} }
@ -148,25 +148,25 @@ export class BoLActor extends Actor {
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
async toggleFightOption( itemId) { async toggleFightOption(itemId) {
let fightOption = this.data.items.get(itemId) let fightOption = this.data.items.get(itemId)
let state let state
let updates = [] let updates = []
if ( fightOption) { if (fightOption) {
fightOption = duplicate(fightOption) fightOption = duplicate(fightOption)
if (fightOption.data.properties.activated) { if (fightOption.data.properties.activated) {
state = false state = false
} else { } else {
state = true state = true
} }
updates.push( {_id: fightOption._id, 'data.properties.activated': state} ) // Update the selected one updates.push({ _id: fightOption._id, 'data.properties.activated': state }) // Update the selected one
await this.updateEmbeddedDocuments("Item", updates) // Apply all changes await this.updateEmbeddedDocuments("Item", updates) // Apply all changes
// Then notify // Then notify
ChatMessage.create({ ChatMessage.create({
alias: this.name, alias: this.name,
whisper: BoLUtility.getWhisperRecipientsAndGMs(this.name), whisper: BoLUtility.getWhisperRecipientsAndGMs(this.name),
content: await renderTemplate('systems/bol/templates/chat/chat-activate-fight-option.hbs', { name: this.name, img: fightOption.img, foName: fightOption.name, state: state} ) content: await renderTemplate('systems/bol/templates/chat/chat-activate-fight-option.hbs', { name: this.name, img: fightOption.img, foName: fightOption.name, state: state })
}) })
} }
@ -174,14 +174,14 @@ export class BoLActor extends Actor {
/* -------------------------------------------- */ /* -------------------------------------------- */
get armorMalusValue() { // used for Fight Options get armorMalusValue() { // used for Fight Options
for(let armor of this.armors) { for (let armor of this.armors) {
if (armor.data.properties.armorQuality.includes("light")) { if (armor.data.properties.armorQuality.includes("light")) {
return 1 return 1
} }
if (armor.data.properties.armorQuality.includes("medium")) { if (armor.data.properties.armorQuality.includes("medium")) {
return 2 return 2
} }
if (armor.data.properties.armorQuality.includes("heavy")) { if (armor.data.properties.armorQuality.includes("heavy")) {
return 3 return 3
} }
} }
@ -219,7 +219,7 @@ export class BoLActor extends Actor {
return this.itemData.filter(i => i.type === "item") return this.itemData.filter(i => i.type === "item")
} }
get armors() { get armors() {
return this.itemData.filter(i => i.type === "item" && i.data.category === "equipment" && i.data.subtype === "armor"); return this.itemData.filter(i => i.type === "item" && i.data.category === "equipment" && i.data.subtype === "armor");
} }
get helms() { get helms() {
return this.itemData.filter(i => i.type === "item" && i.data.category === "equipment" && i.data.subtype === "helm"); return this.itemData.filter(i => i.type === "item" && i.data.category === "equipment" && i.data.subtype === "helm");
@ -235,10 +235,10 @@ export class BoLActor extends Actor {
return this.armors.concat(this.helms).concat(this.shields) return this.armors.concat(this.helms).concat(this.shields)
} }
get spells() { get spells() {
return this.itemData.filter(i => i.type === "item" && i.data.category === "spell"); return this.itemData.filter(i => i.type === "item" && i.data.category === "spell");
} }
get alchemy() { get alchemy() {
return this.itemData.filter(i => i.type === "item" && i.data.category === "alchemy"); return this.itemData.filter(i => i.type === "item" && i.data.category === "alchemy");
} }
get melee() { get melee() {
return this.weapons.filter(i => i.data.properties.melee === true); return this.weapons.filter(i => i.data.properties.melee === true);
@ -264,7 +264,7 @@ export class BoLActor extends Actor {
} }
get misc() { get misc() {
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")); 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"));
} }
get bonusBoons() { get bonusBoons() {
@ -274,45 +274,45 @@ export class BoLActor extends Actor {
return this.itemData.filter(i => i.type === "feature" && i.data.subtype === "flaw" && i.data.properties.ismalusdice); return this.itemData.filter(i => i.type === "feature" && i.data.subtype === "flaw" && i.data.properties.ismalusdice);
} }
isSorcerer( ) { isSorcerer() {
if ( this.careers.find( item => item.data.properties.sorcerer == true) ) if (this.careers.find(item => item.data.properties.sorcerer == true))
return true return true
return false return false
} }
isAlchemist( ) { isAlchemist() {
if ( this.careers.find( item => item.data.properties.alchemist == true) ) if (this.careers.find(item => item.data.properties.alchemist == true))
return true return true
return false return false
} }
isPriest( ) { isPriest() {
if ( this.careers.find( item => item.data.properties.priest == true) ) if (this.careers.find(item => item.data.properties.priest == true))
return true return true
return false return false
} }
spendPowerPoint( ppCost ) { spendPowerPoint(ppCost) {
let newPP = this.data.data.resources.power.value - ppCost let newPP = this.data.data.resources.power.value - ppCost
newPP = (newPP<0) ? 0 : newPP newPP = (newPP < 0) ? 0 : newPP
this.update( {'data.resources.power.value': newPP}) this.update({ 'data.resources.power.value': newPP })
} }
resetAlchemyStatus( alchemyId ) { resetAlchemyStatus(alchemyId) {
let alchemy = this.data.items.get( alchemyId) let alchemy = this.data.items.get(alchemyId)
if (alchemy) { if (alchemy) {
this.updateEmbeddedDocuments('Item', [{_id: alchemy.id, 'data.properties.pccurrent': 0}] ) this.updateEmbeddedDocuments('Item', [{ _id: alchemy.id, 'data.properties.pccurrent': 0 }])
} }
} }
async spendAlchemyPoint( alchemyId, pcCost) { async spendAlchemyPoint(alchemyId, pcCost) {
let alchemy = this.data.items.get( alchemyId) let alchemy = this.data.items.get(alchemyId)
if (alchemy) { if (alchemy) {
pcCost = Number(pcCost)?? 0 pcCost = Number(pcCost) ?? 0
if ( this.data.data.resources.alchemypoints.value >= pcCost) { if (this.data.data.resources.alchemypoints.value >= pcCost) {
let newPC = this.data.data.resources.alchemypoints.value - pcCost let newPC = this.data.data.resources.alchemypoints.value - pcCost
newPC = (newPC<0) ? 0 : newPC newPC = (newPC < 0) ? 0 : newPC
this.update( {'data.resources.alchemypoints.value': newPC} ) this.update({ 'data.resources.alchemypoints.value': newPC })
newPC = alchemy.data.data.properties.pccurrent + pcCost newPC = alchemy.data.data.properties.pccurrent + pcCost
await this.updateEmbeddedDocuments('Item', [{_id: alchemy.id, 'data.properties.pccurrent': newPC}] ) await this.updateEmbeddedDocuments('Item', [{ _id: alchemy.id, 'data.properties.pccurrent': newPC }])
} else { } else {
ui.notifications.warn("Plus assez de Points de Création !") ui.notifications.warn("Plus assez de Points de Création !")
} }
@ -320,21 +320,21 @@ export class BoLActor extends Actor {
} }
getAlchemistBonus() { getAlchemistBonus() {
let sorcerer = this.careers.find( item => item.data.properties.alchemist == true) let sorcerer = this.careers.find(item => item.data.properties.alchemist == true)
if (sorcerer) { if (sorcerer) {
return sorcerer.data.rank return sorcerer.data.rank
} }
return 0; return 0;
} }
getSorcererBonus() { getSorcererBonus() {
let sorcerer = this.careers.find( item => item.data.properties.sorcerer == true) let sorcerer = this.careers.find(item => item.data.properties.sorcerer == true)
if (sorcerer) { if (sorcerer) {
return sorcerer.data.rank return sorcerer.data.rank
} }
return 0; return 0;
} }
heroReroll( ) { heroReroll() {
if (this.type == 'character') { if (this.type == 'character') {
return this.data.data.resources.hero.value > 0; return this.data.data.resources.hero.value > 0;
} else { } else {
@ -363,7 +363,7 @@ export class BoLActor extends Actor {
return resources return resources
} }
buildFeatures(){ buildFeatures() {
return { return {
"careers": { "careers": {
"label": "BOL.featureCategory.careers", "label": "BOL.featureCategory.careers",
@ -403,67 +403,88 @@ export class BoLActor extends Actor {
} }
} }
buildCombat(){ buildCombat() {
return { return {
"melee" : { "melee": {
"label" : "BOL.combatCategory.melee", "label": "BOL.combatCategory.melee",
"weapon" : true, "weapon": true,
"protection" : false, "protection": false,
"blocking" : false, "blocking": false,
"ranged" : false, "ranged": false,
"options": false, "options": false,
"items" : this.melee "items": this.melee
}, },
"ranged" : { "ranged": {
"label" : "BOL.combatCategory.ranged", "label": "BOL.combatCategory.ranged",
"weapon" : true, "weapon": true,
"protection" : false, "protection": false,
"blocking" : false, "blocking": false,
"ranged" : true, "ranged": true,
"options": false, "options": false,
"items" : this.ranged "items": this.ranged
}, },
"protections" : { "protections": {
"label" : "BOL.combatCategory.protections", "label": "BOL.combatCategory.protections",
"weapon" : false, "weapon": false,
"protection" : true, "protection": true,
"blocking" : false, "blocking": false,
"ranged" : false, "ranged": false,
"options": false, "options": false,
"items" : this.protections "items": this.protections
}, },
"shields" : { "shields": {
"label" : "BOL.combatCategory.shields", "label": "BOL.combatCategory.shields",
"weapon" : false, "weapon": false,
"protection" : false, "protection": false,
"blocking" : true, "blocking": true,
"ranged" : false, "ranged": false,
"options": false, "options": false,
"items" : this.shields "items": this.shields
}, },
"fightoptions" : { "fightoptions": {
"label" : "BOL.combatCategory.fightOptions", "label": "BOL.combatCategory.fightOptions",
"weapon" : false, "weapon": false,
"protection" : false, "protection": false,
"blocking" : false, "blocking": false,
"ranged" : false, "ranged": false,
"options": true, "options": true,
"items" : this.fightoptions "items": this.fightoptions
} }
} }
} }
/*-------------------------------------------- */
buildRollList() {
let rolls = []
for ( let key in this.data.data.attributes) {
let attr = this.data.data.attributes[key]
rolls.push( { key: key, value: attr.value, name: attr.label, type: "attribute"})
}
for ( let key in this.data.data.aptitudes) {
if ( key != "def") {
let apt = this.data.data.aptitudes[key]
rolls.push( { key: key, value: apt.value, name: apt.label, type: "aptitude"})
}
}
return rolls
}
/*-------------------------------------------- */
buildListeActions() {
return this.melee.concat(this.ranged)
}
/*-------------------------------------------- */ /*-------------------------------------------- */
async manageHealthState() { async manageHealthState() {
let hpID = "lastHP"+this.id let hpID = "lastHP" + this.id
let lastHP = await this.getFlag("world", hpID ) let lastHP = await this.getFlag("world", hpID)
if ( lastHP != this.data.data.resources.hp.value ) { if (lastHP != this.data.data.resources.hp.value) {
await this.setFlag("world", hpID, this.data.data.resources.hp.value) await this.setFlag("world", hpID, this.data.data.resources.hp.value)
if (this.data.data.resources.hp.value <= 0 ) { if (this.data.data.resources.hp.value <= 0) {
ChatMessage.create({ ChatMessage.create({
alias: this.name, alias: this.name,
whisper: BoLUtility.getWhisperRecipientsAndGMs(this.name), whisper: BoLUtility.getWhisperRecipientsAndGMs(this.name),
content: await renderTemplate('systems/bol/templates/chat/chat-vitality-zero.hbs', { name: this.name, img: this.img, hp: this.data.data.resources.hp.value} ) content: await renderTemplate('systems/bol/templates/chat/chat-vitality-zero.hbs', { name: this.name, img: this.img, hp: this.data.data.resources.hp.value })
}) })
} }
} }
@ -471,7 +492,7 @@ export class BoLActor extends Actor {
/*-------------------------------------------- */ /*-------------------------------------------- */
registerInit(initScore, isCritical, isFumble) { registerInit(initScore, isCritical, isFumble) {
this.update( { 'data.combat.lastinit': initScore, 'data.combat.iscritical': isCritical, 'data.combat.isfumble': isFumble} ) this.update({ 'data.combat.lastinit': initScore, 'data.combat.iscritical': isCritical, 'data.combat.isfumble': isFumble })
} }
/*-------------------------------------------- */ /*-------------------------------------------- */
@ -480,60 +501,60 @@ export class BoLActor extends Actor {
} }
/*-------------------------------------------- */ /*-------------------------------------------- */
async subHeroPoints( nb) { async subHeroPoints(nb) {
let newHeroP = this.data.data.resources.hero.value - nb; let newHeroP = this.data.data.resources.hero.value - nb;
newHeroP = (newHeroP < 0 ) ? 0 : newHeroP; newHeroP = (newHeroP < 0) ? 0 : newHeroP;
await this.update( { 'data.resources.hero.value': newHeroP} ); await this.update({ 'data.resources.hero.value': newHeroP });
} }
/*-------------------------------------------- */ /*-------------------------------------------- */
async sufferDamage( damage) { async sufferDamage(damage) {
let newHP = this.data.data.resources.hp.value - damage; let newHP = this.data.data.resources.hp.value - damage;
await this.update( { 'data.resources.hp.value': newHP} ); await this.update({ 'data.resources.hp.value': newHP });
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
getArmorFormula( ) { getArmorFormula() {
let protectWorn = this.protections.filter( item => item.data.worn) let protectWorn = this.protections.filter(item => item.data.worn)
let formula = "" let formula = ""
for (let protect of protectWorn) { for (let protect of protectWorn) {
if ( protect.data.subtype == 'helm') { if (protect.data.subtype == 'helm') {
formula += "+1" formula += "+1"
} else if ( protect.data.subtype == 'armor') { } else if (protect.data.subtype == 'armor') {
if ( BoLUtility.getRollArmor() ) { if (BoLUtility.getRollArmor()) {
if ( !protect.data.properties.soak.formula || protect.data.properties.soak.formula=="") { if (!protect.data.properties.soak.formula || protect.data.properties.soak.formula == "") {
ui.notifications.warn(`L'armure ${protect.name} n'a pas de formule pour la protection !`) ui.notifications.warn(`L'armure ${protect.name} n'a pas de formule pour la protection !`)
} else { } else {
formula += "+" + protect.data.properties.soak.formula formula += "+" + protect.data.properties.soak.formula
} }
} else { } else {
if ( protect.data.properties.soak.value == undefined ) { if (protect.data.properties.soak.value == undefined) {
ui.notifications.warn(`L'armure ${protect.name} n'a pas de valeur fixe pour la protection !`) ui.notifications.warn(`L'armure ${protect.name} n'a pas de valeur fixe pour la protection !`)
} else { } else {
formula += "+ " + protect.data.properties.soak.value formula += "+ " + protect.data.properties.soak.value
} }
} }
} }
} }
console.log("Protect Formula", formula) console.log("Protect Formula", formula)
return (formula == "") ? "0" :formula; return (formula == "") ? "0" : formula;
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
rollProtection( itemId) { rollProtection(itemId) {
let armor = this.data.items.get( itemId ) let armor = this.data.items.get(itemId)
if ( armor ) { if (armor) {
let armorFormula = armor.data.data.properties.soak.formula; let armorFormula = armor.data.data.properties.soak.formula;
let rollArmor = new Roll(armorFormula) let rollArmor = new Roll(armorFormula)
rollArmor.roll( {async: false} ).toMessage(); rollArmor.roll({ async: false }).toMessage();
} }
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
rollWeaponDamage( itemId) { rollWeaponDamage(itemId) {
let weapon = this.data.items.get(itemId ) let weapon = this.data.items.get(itemId)
if ( weapon ) { if (weapon) {
let r = new BoLDefaultRoll( { id: randomID(16), isSuccess: true, mode: "weapon", weapon: weapon, actor: this} ) let r = new BoLDefaultRoll({ id: randomID(16), isSuccess: true, mode: "weapon", weapon: weapon, actor: this })
r.setSuccess(true) r.setSuccess(true)
r.rollDamage() r.rollDamage()
} }
@ -542,7 +563,7 @@ export class BoLActor extends Actor {
/* -------------------------------------------- */ /* -------------------------------------------- */
toggleEquipItem(item) { toggleEquipItem(item) {
const equipable = item.data.data.properties.equipable; const equipable = item.data.data.properties.equipable;
if(equipable){ if (equipable) {
let itemData = duplicate(item.data); let itemData = duplicate(item.data);
itemData.data.worn = !itemData.data.worn; itemData.data.worn = !itemData.data.worn;
return item.update(itemData); return item.update(itemData);

View File

@ -1,16 +1,19 @@
/* -------------------------------------------- */
// Import Modules // Import Modules
import { BoLActor } from "./actor/actor.js"; import { BoLActor } from "./actor/actor.js"
import { BoLActorSheet } from "./actor/actor-sheet.js"; import { BoLActorSheet } from "./actor/actor-sheet.js"
import { BoLItem } from "./item/item.js"; import { BoLItem } from "./item/item.js"
import { BoLItemSheet } from "./item/item-sheet.js"; import { BoLItemSheet } from "./item/item-sheet.js"
import { System, BOL } from "./system/config.js"; import { System, BOL } from "./system/config.js"
import { preloadHandlebarsTemplates } from "./system/templates.js"; import { preloadHandlebarsTemplates } from "./system/templates.js"
import { registerHandlebarsHelpers } from "./system/helpers.js"; import { registerHandlebarsHelpers } from "./system/helpers.js"
import registerHooks from "./system/hooks.js"; import registerHooks from "./system/hooks.js"
import { Macros } from "./system/macros.js"; import { Macros } from "./system/macros.js"
import { BoLUtility } from "./system/bol-utility.js"; import { BoLUtility } from "./system/bol-utility.js"
import { BoLCombatManager } from "./system/bol-combat.js"; import { BoLCombatManager } from "./system/bol-combat.js"
import { BoLTokenHud } from "./system/bol-action-hud.js"
/* -------------------------------------------- */
Hooks.once('init', async function () { Hooks.once('init', async function () {
game.bol = { game.bol = {
@ -47,7 +50,8 @@ Hooks.once('init', async function () {
Items.registerSheet("bol", BoLItemSheet, { makeDefault: true }); Items.registerSheet("bol", BoLItemSheet, { makeDefault: true });
// Inot useful stuff // Inot useful stuff
BoLUtility.init(); BoLUtility.init()
BoLTokenHud.init()
// Preload Handlebars Templates // Preload Handlebars Templates
await preloadHandlebarsTemplates(); await preloadHandlebarsTemplates();

View File

@ -1,6 +1,5 @@
import { BoLUtility } from "../system/bol-utility.js"; import { BoLUtility } from "../system/bol-utility.js";
const __adv2dice = { ["1B"]: 3, ["2B"]: 4, ["2"]: 2, ["1M"]: 3, ["2M"]: 4 }
const _apt2attr = { init: "mind", melee: "agility", ranged: "agility", def: "vigor" } const _apt2attr = { init: "mind", melee: "agility", ranged: "agility", def: "vigor" }
/* -------------------------------------------- */ /* -------------------------------------------- */
@ -11,57 +10,45 @@ export class BoLRoll {
return { classes: ["bol", "dialog"], width: 480, height: 540 }; return { classes: ["bol", "dialog"], width: 480, height: 540 };
} }
/* -------------------------------------------- */
static convertToAdv(adv) {
if (adv == 0) return "2"
return Math.abs(adv) + (adv < 0) ? 'M' : 'B';
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static getDefaultAttribute(key) { static getDefaultAttribute(key) {
return _apt2attr[key] return _apt2attr[key]
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static attributeCheck(actor, actorData, dataset, event) { static attributeCheck(actor, key ) {
const key = dataset.key
const adv = dataset.adv
let attribute = eval(`actor.data.data.attributes.${key}`) let attribute = eval(`actor.data.data.attributes.${key}`)
let label = (attribute.label) ? game.i18n.localize(attribute.label) : null 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) let description = game.i18n.localize('BOL.ui.attributeCheck') + " - " + game.i18n.localize(attribute.label)
let rollData = { let rollData = {
mode: "attribute", mode: "attribute",
actor: actor, actor: actor,
actorData: actorData,
attribute: attribute, attribute: attribute,
attrValue: attribute.value, attrValue: attribute.value,
aptValue: 0, aptValue: 0,
label: label, label: label,
careerBonus: 0, careerBonus: 0,
description: description, description: description,
adv: this.convertToAdv(adv),
mod: 0 mod: 0
} }
return this.displayRollDialog( rollData ) return this.displayRollDialog( rollData )
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static aptitudeCheck(actor, actorData, dataset, event) { static aptitudeCheck(actor, key ) {
const key = dataset.key;
const adv = dataset.adv;
let aptitude = eval(`actor.data.data.aptitudes.${key}`); let aptitude = eval(`actor.data.data.aptitudes.${key}`)
let attrKey = this.getDefaultAttribute(key) let attrKey = this.getDefaultAttribute(key)
let attribute = eval(`actor.data.data.attributes.${attrKey}`); let attribute = eval(`actor.data.data.attributes.${attrKey}`)
let label = (aptitude.label) ? game.i18n.localize(aptitude.label) : null; 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); let description = game.i18n.localize('BOL.ui.aptitudeCheck') + " - " + game.i18n.localize(aptitude.label);
return this.displayRollDialog( return this.displayRollDialog(
{ {
mode: "aptitude", mode: "aptitude",
actor: actor, actor: actor,
actorData: actorData,
attribute: attribute, attribute: attribute,
aptitude: aptitude, aptitude: aptitude,
attrValue: attribute.value, attrValue: attribute.value,
@ -69,20 +56,15 @@ export class BoLRoll {
label: label, label: label,
careerBonus: 0, careerBonus: 0,
description: description, description: description,
adv: this.convertToAdv(adv),
mod: 0 mod: 0
}); });
} }
/* -------------------------------------------- */ /* -------------------------------------------- */
static weaponCheck(actor, actorData, dataset, event) { static weaponCheckWithWeapon(actor, weapon) {
let target = BoLUtility.getTarget() let target = BoLUtility.getTarget()
const li = $(event.currentTarget).parents(".item")
const weapon = actor.items.get(li.data("item-id"))
if (!weapon) {
ui.notifications.warn("Unable to find weapon !")
return;
}
let weaponData = weapon.data.data let weaponData = weapon.data.data
let attribute = eval(`actor.data.data.attributes.${weaponData.properties.attackAttribute}`) let attribute = eval(`actor.data.data.attributes.${weaponData.properties.attackAttribute}`)
let aptitude = eval(`actor.data.data.aptitudes.${weaponData.properties.attackAptitude}`) let aptitude = eval(`actor.data.data.aptitudes.${weaponData.properties.attackAptitude}`)
@ -97,7 +79,6 @@ export class BoLRoll {
let rolldata = { let rolldata = {
mode: "weapon", mode: "weapon",
actor: actor, actor: actor,
actorData: actorData,
weapon: weapon, weapon: weapon,
isRanged: weaponData.properties.ranged || weaponData.properties.throwing, isRanged: weaponData.properties.ranged || weaponData.properties.throwing,
target: target, target: target,
@ -111,13 +92,23 @@ export class BoLRoll {
mod: 0, mod: 0,
modRanged: 0, modRanged: 0,
label: (weapon.name) ? weapon.name : game.i18n.localize('BOL.ui.noWeaponName'), label: (weapon.name) ? weapon.name : game.i18n.localize('BOL.ui.noWeaponName'),
description: actor.name + " - " + game.i18n.localize('BOL.ui.weaponAttack'), description: game.i18n.localize('BOL.ui.weaponAttack') + " : " + weapon.name,
} }
return this.displayRollDialog(rolldata) return this.displayRollDialog(rolldata)
} }
/* -------------------------------------------- */
static weaponCheck(actor, event) {
const li = $(event.currentTarget).parents(".item")
const weapon = actor.items.get(li.data("item-id"))
if (!weapon) {
ui.notifications.warn("Unable to find weapon !")
return
}
return this.weaponCheckWithWeapon(actor, weapon)
}
/* -------------------------------------------- */ /* -------------------------------------------- */
static alchemyCheck(actor, actorData, dataset, event) { static alchemyCheck(actor, event) {
const li = $(event.currentTarget).parents(".item"); const li = $(event.currentTarget).parents(".item");
const alchemy = actor.items.get(li.data("item-id")); const alchemy = actor.items.get(li.data("item-id"));
if (!alchemy) { if (!alchemy) {
@ -133,7 +124,6 @@ export class BoLRoll {
let alchemyDef = { let alchemyDef = {
mode: "alchemy", mode: "alchemy",
actor: actor, actor: actor,
actorData: actorData,
alchemy: alchemy, alchemy: alchemy,
attribute: actor.data.data.attributes.mind, attribute: actor.data.data.attributes.mind,
attrValue: actor.data.data.attributes.mind.value, attrValue: actor.data.data.attributes.mind.value,
@ -143,7 +133,7 @@ export class BoLRoll {
pcCostCurrent: Number(alchemyData.properties.pccurrent), pcCostCurrent: Number(alchemyData.properties.pccurrent),
mod: Number(alchemyData.properties.difficulty), mod: Number(alchemyData.properties.difficulty),
label: alchemy.name, label: alchemy.name,
description: actor.name + " - " + game.i18n.localize('BOL.ui.makeAlchemy'), description: game.i18n.localize('BOL.ui.makeAlchemy') + "+" + alchemy.name,
} }
console.log("ALCHEMY!", alchemyDef); console.log("ALCHEMY!", alchemyDef);
return this.displayRollDialog(alchemyDef); return this.displayRollDialog(alchemyDef);
@ -151,7 +141,7 @@ export class BoLRoll {
/* -------------------------------------------- */ /* -------------------------------------------- */
static spellCheck(actor, actorData, dataset, event) { static spellCheck(actor, event) {
if (actor.data.data.resources.power.value <= 0) { if (actor.data.data.resources.power.value <= 0) {
ui.notifications.warn("Plus assez de points de Pouvoir !") ui.notifications.warn("Plus assez de points de Pouvoir !")
return return
@ -166,7 +156,6 @@ export class BoLRoll {
let spellDef = { let spellDef = {
mode: "spell", mode: "spell",
actor: actor, actor: actor,
actorData: actorData,
spell: spell, spell: spell,
attribute: actor.data.data.attributes.mind, attribute: actor.data.data.attributes.mind,
attrValue: actor.data.data.attributes.mind.value, attrValue: actor.data.data.attributes.mind.value,
@ -176,7 +165,7 @@ export class BoLRoll {
ppCost: Number(spell.data.data.properties.ppcost), ppCost: Number(spell.data.data.properties.ppcost),
mod: Number(spellData.properties.difficulty), mod: Number(spellData.properties.difficulty),
label: spell.name, label: spell.name,
description: actor.name + " - " + game.i18n.localize('BOL.ui.focusSpell'), description: game.i18n.localize('BOL.ui.focusSpell') + " : " + spell.name,
} }
console.log("SPELL!", spellDef); console.log("SPELL!", spellDef);
return this.displayRollDialog(spellDef); return this.displayRollDialog(spellDef);
@ -319,7 +308,7 @@ export class BoLRoll {
// initialize default flags/values // initialize default flags/values
const rollOptionTpl = `systems/bol/templates/dialogs/${rollData.mode}-roll-dialog.hbs` const rollOptionTpl = `systems/bol/templates/dialogs/${rollData.mode}-roll-dialog.hbs`
rollData.careers = rollData.actorData.features.careers rollData.careers = rollData.actor.careers
rollData.boons = rollData.actor.bonusBoons rollData.boons = rollData.actor.bonusBoons
rollData.flaws = rollData.actor.malusFlaws rollData.flaws = rollData.actor.malusFlaws
rollData.defence = 0 rollData.defence = 0

View File

@ -0,0 +1,88 @@
/* -------------------------------------------- */
import { BoLRoll } from "../controllers/bol-rolls.js";
/* -------------------------------------------- */
export class BoLTokenHud {
static init() {
// Integration du TokenHUD
Hooks.on('renderTokenHUD', (app, html, data) => { BoLTokenHud.addTokenHudExtensions(app, html, data._id) });
}
/* -------------------------------------------- */
static async removeExtensionHud(app, html, tokenId) {
html.find('.control-icon.bol-roll').remove()
html.find('.control-icon.bol-action').remove()
}
/* -------------------------------------------- */
static async addExtensionHud(app, html, tokenId) {
let token = canvas.tokens.get(tokenId)
let actor = token.actor
app.hasExtension = true
const hudData = { actor: actor, actionsList: actor.buildListeActions(), rollsList: actor.buildRollList() }
const controlIconActions = html.find('.control-icon[data-action=combat]');
// initiative
await BoLTokenHud._configureSubMenu(controlIconActions, 'systems/bol/templates/token/hud-actor-actions.hbs', hudData,
(event) => {
let actionIndex = Number(event.currentTarget.attributes['data-action-index'].value)
let action = hudData.actionsList[actionIndex]
const weapon = actor.items.get( action._id )
BoLRoll.weaponCheckWithWeapon(hudData.actor, weapon)
//console.log("Clicked", action)
} )
const controlIconTarget = html.find('.control-icon[data-action=target]');
// att+apt+career
await BoLTokenHud._configureSubMenu(controlIconTarget, 'systems/bol/templates/token/hud-actor-rolls.hbs', hudData,
(event) => {
let rollIndex = Number(event.currentTarget.attributes['data-roll-index'].value)
let roll = hudData.rollsList[rollIndex]
if ( roll.type == "aptitude") {
BoLRoll.aptitudeCheck(actor, roll.key )
} else if ( roll.type == "attribute") {
BoLRoll.attributeCheck(actor, roll.key )
}
})
}
/* -------------------------------------------- */
static async addTokenHudExtensions(app, html, tokenId) {
const controlIconCombat = html.find('.control-icon[data-action=combat]')
if (controlIconCombat.length>0 ) {
BoLTokenHud.addExtensionHud(app, html, tokenId);
}
}
/* -------------------------------------------- */
static async _configureSubMenu(insertionPoint, template, hudData, onMenuItem) {
const hud = $(await renderTemplate(template, hudData))
const list = hud.find('div.bol-hud-list')
BoLTokenHud._toggleHudListActive(hud, list);
hud.find('img.bol-hud-togglebutton').click(event => BoLTokenHud._toggleHudListActive(hud, list));
list.find('.bol-hud-menu').click(onMenuItem);
insertionPoint.after(hud);
}
/* -------------------------------------------- */
static _showControlWhen(control, condition) {
if (condition) {
control.show()
}
else {
control.hide()
}
}
/* -------------------------------------------- */
static _toggleHudListActive(hud, list) {
hud.toggleClass('active')
BoLTokenHud._showControlWhen(list, hud.hasClass('active'))
}
}

View File

@ -7,7 +7,7 @@
"url": "https://github.com/ZigmundKreud/bol", "url": "https://github.com/ZigmundKreud/bol",
"license": "LICENSE.txt", "license": "LICENSE.txt",
"flags": {}, "flags": {},
"version": "1.2.4", "version": "1.2.6",
"templateVersion": 22, "templateVersion": 22,
"minimumCoreVersion": "0.8.6", "minimumCoreVersion": "0.8.6",
"compatibleCoreVersion": "9", "compatibleCoreVersion": "9",

View File

@ -0,0 +1,10 @@
<div class="control-icon bol-action ">
<img class="bol-hud-togglebutton" src="icons/svg/sword.svg" width="36" height="36" title="Action" />
<div class="bol-hud-list tokenhudext right">
{{#each actionsList as |action key|}}
<div class="control-icon tokenhudicon bol-hud-menu" data-action-index="{{key}}" title="{{action.name}}">
<label>{{action.name}}</label>
</div>
{{/each}}
</div>
</div>

View File

@ -0,0 +1,10 @@
<div class="control-icon bol-roll ">
<img class="bol-hud-togglebutton" src="icons/svg/dice-target.svg" width="36" height="36" title="Jets" />
<div class="bol-hud-list tokenhudext left">
{{#each rollsList as |roll key|}}
<div class="control-icon tokenhudicon bol-hud-menu" data-roll-index="{{key}}" title="{{localize roll.name}}">
<label>{{ localize roll.name}}</label>
</div>
{{/each}}
</div>
</div>