Compare commits

...

8 Commits

21 changed files with 478 additions and 247 deletions

View File

@ -156,6 +156,7 @@
"BOL.ui.armorAgiMalus": "Rüschtung+Schild-Malus (Geschick)",
"BOL.ui.armorInitMalus": "Rüstungsmalus (Init)",
"BOL.ui.attackValue": "Angriffswert",
"BOL.ui.initMalus": "Init malus",
"BOL.featureCategory.origins": "Herkünfte",
"BOL.featureCategory.races": "Rassen",

View File

@ -154,6 +154,7 @@
"BOL.ui.armorInitMalus": "Armor Modifier (Init)",
"BOL.ui.attackValue": "Attack Value",
"BOL.ui.weaponbonus": "Cette arme bénéficie déja d'un Dé de Bonus (Arme Favorite prise en compte, par exemple)",
"BOL.ui.initMalus": "Init malus",
"BOL.featureCategory.origins": "Origins",
"BOL.featureCategory.races": "Races",

View File

@ -132,6 +132,7 @@
"BOL.ui.concentrate": "Concentration à maintenir ?",
"BOL.ui.aggressive": "Sort aggressif ?",
"BOL.ui.registerInit": "Enregistrer comme Init. de combat",
"BOL.ui.initMalus": "Malus d'initiative",
"BOL.ui.magicnewrules": "Règles supplémentaires (cf. supplément fan-made Sorcellerie!)",
"BOL.ui.isSorcerer": "Carrière de Sorcier ?",
@ -459,10 +460,10 @@
"BOL.chat.isdead": "{name} est mort !",
"BOL.chat.epitaph": "Que son nom soit honoré sur les champs de batailles de Lémurie !",
"BOL.chat.vitalityzero": "La Vitalité de {name} est {hp} : il va s'écrouler au sol et sombrer dans l'inconscience !",
"BOL.chat.vitalityheroism": "Vous pouvez dépenser 1 Point d'Héroisme pour reprendre vos esprits pendant 1 round.",
"BOL.chat.vitalityheroism": "Vous pouvez dépenser 1 Point d'Héroisme/Vilainie pour reprendre vos esprits pendant 1 round.",
"BOL.chat.vitalityheroismhint": "Dans ce cas votre vitalité remonte à son maximum divisé par 2 (arrondi au supérieur).",
"BOL.chat.vitalitydying": "La Vitalité de {name} est de {hp} ! Il est mourant ...",
"BOL.chat.vitalitydyingheroism": "Vous pouvez cependant dépenser 1 Point d'Héroisme pour Défier la Mort (cf. page 58).",
"BOL.chat.vitalitydyingheroism": "Vous pouvez cependant dépenser 1 Point d'Héroisme/Vilainie pour Défier la Mort (cf. page 58).",
"BOL.chat.alchemytitle": "Préparation Alchimique : {name}",
"BOL.chat.alchemypoints": "Points de Création Investis : {pcCostCurrent}",
"BOL.chat.alchemysuccess": "La préparation alchimique a été réalisée avec succès !<br>Créez l'item ou l'effet correspondant dans votre Inventaire.<br>L'avancement dans la préparation a été remis à 0.",
@ -475,19 +476,20 @@
"BOL.chat.applydamagetotarget": "Appliquer les dommages à la cible",
"BOL.chat.fightoption": "Option de combat",
"BOL.chat.reroll": "Relancer (1 P. Heroisme)",
"BOL.chat.toheroic": "Transformer en succés Héroïque (1 P. Héroisme)",
"BOL.chat.tolegend": "Transformer en succes Légendaire (1 P. Heroisme)",
"BOL.chat.heroicreminder": "En plus des actions indiquées sur les boutons ci-dessous, vous pouvez : <ul><li>Carnage : Attaquer une seconde fois le même adversaire</li><li>Coup précis : Un dé de malus à votre adversaire sur une localisation choisie</li><li>Désarmement</li><li>Massacrer la piétaille</li><li>Renversement : Renversez votre adversaire (1 taille de plus max)</li></ul>Si vous dépensez un Point dh'Eheoisme en plus, tout ces effets peuvent être doublés",
"BOL.chat.toheroic": "Transformer en succés Héroïque (1 P. Héroisme/Vilainie)",
"BOL.chat.tolegend": "Transformer en succes Légendaire (1 P. Heroisme/Vilainie)",
"BOL.chat.hurttitle": "{name} va encaisser {damageTotal} dégats !",
"BOL.chat.armordefault": "C'est une attaque au défaut de l'armure : vous devez encaisser SANS la protection de l'armure !",
"BOL.chat.witharmor": "Encaisser avec la protection de l'armure",
"BOL.chat.withoutarmor": "Encaisser sans la protection de l'armure",
"BOL.chat.shakeoff": "Juste une égratignure (1 Point d'Héroisme)",
"BOL.chat.splinteredshield": "Parade in Extremis avec {name} (1 Point d'Héroisme)",
"BOL.chat.shakeoff": "Juste une égratignure (1 Point d'Héroisme/Vilainie)",
"BOL.chat.splinteredshield": "Parade in Extremis avec {name} (1 Point d'Héroisme/Vilainie)",
"BOL.chat.damagesummary": "Dégats subis par {name}",
"BOL.chat.protectvalue": "Protection de l'armure",
"BOL.chat.noprotectvalue": "Aucune protection d'armure !",
"BOL.chat.heroreducedamage": "Un point d'héroisme dépensé, pour une réduction des dommages supplémentaire de {total}.",
"BOL.chat.herosplintered": "Aucun dommage encaissé, grâce à la parade in-extremis avec {weaponHero.name}. L'arme a été détruite pendant cette parade ! Un point d'héroisme a également été dépensé.",
"BOL.chat.heroreducedamage": "Un point d'héroisme/vilainie dépensé, pour une réduction des dommages supplémentaire de {total}.",
"BOL.chat.herosplintered": "Aucun dommage encaissé, grâce à la parade in-extremis avec {weaponHero.name}. L'arme a été détruite pendant cette parade ! Un point d'héroisme/vilainie a également été dépensé.",
"BOL.chat.finaldamage": "Encaissement final : {finalDamage} dégats !",
"BOL.chat.spell": "Sort",
"BOL.chat.spellcost": "Cout en Points de Pouvoir",
@ -518,23 +520,27 @@
"BOL.chat.horoscopepoints": "Coût : {points} Points d'Astrologie",
"BOL.chat.horoscopeminorsuccess": "Votre horoscope mineur est un succès : éditez le nom de l'horoscope sur votre fiche. Vous bénéficiez d'1 dé Bonus pour cette situation.",
"BOL.chat.horoscopeminorfailure": "Votre horoscope mineur est un échec : éditez le nom de l'horoscope sur votre fiche. Vous souffrez d'1 dé Malus pour cette situation.",
"BOL.chat.horoscopemajorsuccess": "Votre horoscope majeur est un succès : vous bénéficiez d'1 point d'Héroisme pour cette aventure. Ce point a été ajouté automatiquement.",
"BOL.chat.horoscopemajorfailure": "Votre horoscope majeur est un échec : vous perdez 1 point d'Héroisme pour cette aventure. Ce point a été enlevé automatiquement.",
"BOL.chat.horoscopemajorsuccess": "Votre horoscope majeur est un succès : {horoscopeName} bénéficie d'1 point d'Héroisme de plus pour cette aventure. Ce point a été ajouté automatiquement.",
"BOL.chat.horoscopemajorfailure": "Votre horoscope majeur est un échec : {horoscopeName} a perdu 1 point d'Héroisme pour cette aventure. Ce point a été enlevé automatiquement.",
"BOL.chat.horoscopemajorgroupsuccess": "Votre horoscope majeur de groupe est un succès. Vous et vos amis bénéficiez de {careerBonus} dés bonus pendant cette aventure.",
"BOL.chat.horoscopemajorgroupfailure": "Votre horoscope majeur de groupe est un échec. Vous et vos amis souffrez de {careerBonus} dés malus pendant cette aventure.",
"BOL.chat.usedhoroscope": "Horoscope utilisé",
"BOL.chat.horoscopedeleted": "Le(s) Horoscopes utilisé(s) a/ont été supprimé(s) automatiquement.",
"BOL.chat.criticaloptions": "Succès critique !! Vous pouvez faire (1 option au choix) :",
"BOL.chat.criticalcarnage": "Faire un Carnage : vous avez une attaque gratuite supplémentaire. Cette seconde attaque ne peut bénéficier d'un Point d'Héroisme.",
"BOL.chat.criticalcarnage": "Faire un Carnage : vous avez une attaque gratuite supplémentaire. Cette seconde attaque ne peut bénéficier d'un Point d'Héroisme/vilainie.",
"BOL.chat.criticalplus6": "Coup Dévastateur : +6 aux dommages (cf bouton ci-dessous).",
"BOL.chat.criticalprecise": "Coup Précis : Vous frappez pour diminuer les capacités de votre adversaire. Décrivez ce que vous faites, et si le MJ l'accepte, votre opposant subira un Dé de Malus pour les actions concernées.",
"BOL.chat.criticalunarm": "Désarmement : Si votre adversaire a une arme en main, vous le désarmez.",
"BOL.chat.criticalrabble": "Massacrer la piétaille : Si vous combattez de la Piétaille, les résultats des dommages indiquent le nombre d'adversaires mis hors de combat.",
"BOL.chat.criticalpush": "Renversement : Si la taille le permet, vous poussez votre adversaire au sol, il souffrira d'1 Dé de Malus pour toutes ses actions au round suivant.",
"BOL.chat.criticalup": "Transformer en Légendaire : En dépensant 1 point d'Héroisme, vous pouvez transformer ce Succès Héroïque en Légendaire, qui vous permet de prendre 2 options dans la liste ci-dessus (cf. bouton pour un +12 aux dommages par exemple).",
"BOL.chat.criticalup": "Transformer en Légendaire : En dépensant 1 point d'Héroisme/Vilainie, vous pouvez transformer ce Succès Héroïque en Légendaire, qui vous permet de prendre 2 options dans la liste ci-dessus (cf. bouton pour un +12 aux dommages par exemple).",
"BOL.chat.criticalinfo": "C'est un succès Héroïque ou Légendaire ! Choisissez vos options et effets !",
"BOL.chat.criticalbuttonjournal": "Succès Héroïque/Légendaire",
"BOL.chat.losshp": "{{name}} a perdu {lossHP} points de Vitalité. Si il se repose quelques minutes, il peut récupérer {recupHP} points de Vitalité.",
"BOL.chat.applyrecup": "Récupérer pendant quelques minutes (+{recupHP} Vitalité)",
"BOL.chat.inforecup": "{name} vient de récupérer {recupHP} points de Vitalité après quelques minutes de repos.",
"BOL.dialog.soeasy": "Inmanquable (+4)",
"BOL.dialog.veryeasy": "Trés Facile (+2)",
"BOL.dialog.easy": "Facile (+1)",
@ -573,6 +579,6 @@
"BOL.chat.welcome3": "Les cartes intégrées au système le sont grace à l'aimable autorisation de leur auteur Guillaume Tavernier et des éditions Ludospherik. Merci à eux !.",
"BOL.chat.welcome4": "Tout le support et le suivi de ce système est disponible via le <a href='https://discord.gg/pPSDNJk'>Discord Foundry FR</a>.",
"BOL.chat.welcome5": "Consulter l'aide en ligne pour plus d'informations : @UUID[Compendium.bol.aides-de-jeu.97rugQOtiwt8zPfQ]{Aide du Jeu}.",
"BOL.chat.welcome6": "Bon jeu en Lemurie !"
"BOL.chat.welcome6": "Bon jeu en Lemurie !",
"BOL.chat.nodamage": "Ne pas appliquer les dommages"
}

View File

@ -60,6 +60,12 @@ export class BoLActorSheet extends ActorSheet {
const li = $(ev.currentTarget).parents(".item");
this.actor.spendAlchemyPoint(li.data("itemId"), 1)
})
html.find(".inc-dec-btns-resource").click((ev) => {
const dataset = ev.currentTarget.dataset;
const target = dataset.target
const incr = parseInt(dataset.incr)
this.actor.incDecResources(target, incr)
})
// Incr./Decr. career ranks
html.find(".inc-dec-btns").click((ev) => {

View File

@ -22,6 +22,17 @@ export class BoLActor extends Actor {
super.prepareData()
}
/* -------------------------------------------- */
isHeroAdversary() {
if (this.type === 'character') {
return true
}
if (this.type === 'encounter' && this.chartype == "adversary") {
return true
}
return false
}
/* -------------------------------------------- */
getCharType() {
if (this.type === 'character') {
@ -32,18 +43,24 @@ export class BoLActor extends Actor {
/* -------------------------------------------- */
getVillainy() {
if (this.type === 'character') {
return false
if (this.type === 'encounter' && this.chartype == "adversary") {
return true
}
return true
return false
}
/* -------------------------------------------- */
getInitiativeMalus() {
if ( this.type === 'encounter' && (this.chartype == "adversary" || this.chartype == "tough")) {
return this.system.aptitudes.init.value
}
return 0
}
/* -------------------------------------------- */
getBougette() {
if ( this.type == "character") {
if (this.type == "character") {
let b = duplicate(this.system.bougette)
b.label = game.i18n.localize( game.bol.config.bougetteState[String(this.system.bougette.value)] )
b.diceImg = "icons/dice/" + game.bol.config.bougetteDice[String(this.system.bougette.value)] + "black.svg"
b.label = game.i18n.localize(game.bol.config.bougetteState[String(this.system.bougette.value)])
b.diceImg = "icons/dice/" + game.bol.config.bougetteDice[String(this.system.bougette.value)] + "black.svg"
return b
}
return undefined
@ -51,9 +68,9 @@ export class BoLActor extends Actor {
/* -------------------------------------------- */
async rollBougette() {
if ( this.type == "character") {
if (this.type == "character") {
let attribute = duplicate(this.system.attributes.vigor)
let rollData = BoLRoll.getCommonRollData(this, "bougette", attribute, undefined )
let rollData = BoLRoll.getCommonRollData(this, "bougette", attribute, undefined)
rollData.formula = game.bol.config.bougetteDice[String(this.system.bougette.value)]
let r = new BoLDefaultRoll(rollData)
r.roll()
@ -62,23 +79,25 @@ export class BoLActor extends Actor {
/* -------------------------------------------- */
decBougette() {
if ( this.type == "character") {
let bougette = duplicate(this.system.bougette)
bougette.value = Math.max( Number(bougette.value) - 1, 0)
this.update( { 'system.bougette': bougette } )
if (this.type == "character") {
let bougette = duplicate(this.system.bougette)
bougette.value = Math.max(Number(bougette.value) - 1, 0)
this.update({ 'system.bougette': bougette })
}
}
/* -------------------------------------------- */
updateResourcesData() {
if (this.type == 'character') {
let newVitality = 10 + this.system.attributes.vigor.value + this.system.resources.hp.bonus
if (this.system.resources.hp.max != newVitality) {
this.update({ 'system.resources.hp.max': newVitality })
let actor = this
setTimeout( function() { actor.update({ 'system.resources.hp.max': newVitality }) }, 800 )
}
let newPower = 10 + this.system.attributes.mind.value + this.system.resources.power.bonus
if (this.system.resources.power.max != newPower) {
this.update({ 'system.resources.power.max': newPower })
let actor = this
setTimeout( function() { actor.update({ 'system.resources.power.max': newPower }) }, 800 )
}
}
}
@ -89,8 +108,10 @@ export class BoLActor extends Actor {
} else {
super.prepareDerivedData()
this.updateResourcesData()
this.manageHealthState();
if (this.id) {
this.updateResourcesData()
this.manageHealthState()
}
}
}
@ -98,11 +119,29 @@ export class BoLActor extends Actor {
get details() {
return this.system.details
}
addEffectModifiers( myList, dataPath) {
for (let attr of myList) {
attr.numModifier = 0
attr.diceModifier = ""
let effects = this.items.filter( i => i.type === "feature" && i.system.subtype === "boleffect" && i.system.properties.identifier == dataPath+attr.key)
for (let effect of effects) {
if ( Number(effect.system.properties.modifier)) {
attr.numModifier += Number(effect.system.properties.modifier)
} else {
attr.diceModifier += "+"+effect.system.properties.modifier
}
}
}
}
get attributes() {
return Object.values(this.system.attributes)
let attrList = duplicate(Object.values(this.system.attributes))
this.addEffectModifiers(attrList, "system.attributes.")
return attrList
}
get aptitudes() {
return Object.values(this.system.aptitudes)
let aptList = Object.values(this.system.aptitudes)
this.addEffectModifiers(aptList, "system.aptitudes.")
return aptList
}
/* -------------------------------------------- */
@ -137,7 +176,7 @@ export class BoLActor extends Actor {
}
// Apply defense effects
for (let i of this.items) {
if (i.type === "feature" && i.system.subtype === "boleffect" && i.system.properties.identifier.includes("aptitudes.def") ) {
if (i.type === "feature" && i.system.subtype === "boleffect" && i.system.properties.identifier.includes("aptitudes.def")) {
defMod += Number(i.system.properties.modifier)
}
}
@ -230,19 +269,18 @@ export class BoLActor extends Actor {
/*-------------------------------------------- */
get armorMalusValue() { // used for Fight Options
for (let armor of this.armors) {
if (armor.system.properties.armorQuality.includes("light")) {
if (armor.system.properties.armorQuality?.includes("light")) {
return 1
}
if (armor.system.properties.armorQuality.includes("medium")) {
if (armor.system.properties.armorQuality?.includes("medium")) {
return 2
}
if (armor.system.properties.armorQuality.includes("heavy")) {
if (armor.system.properties.armorQuality?.includes("heavy")) {
return 3
}
}
return 0
}
get resources() {
return Object.values(this.system.resources)
}
@ -389,7 +427,7 @@ export class BoLActor extends Actor {
}
// Apply vigor effects
for (let i of this.items) {
if (i.type === "feature" && i.system.subtype === "boleffect" && i.system.properties.identifier.includes("vigor") ) {
if (i.type === "feature" && i.system.subtype === "boleffect" && i.system.properties.identifier.includes("vigor")) {
attrDamageValue += Number(i.system.properties.modifier)
}
}
@ -432,25 +470,25 @@ export class BoLActor extends Actor {
this.updateEmbeddedDocuments('Item', [{ _id: alchemy.id, 'system.properties.pccurrent': 0 }])
}
}
/*-------------------------------------------- */
spentAstrologyPoints(points) {
let astrology = duplicate(this.system.resources.astrologypoints)
astrology.value -= points
astrology.value = Math.max(astrology.value,0)
this.update( { 'system.resources.astrologypoints': astrology} )
astrology.value = Math.max(astrology.value, 0)
this.update({ 'system.resources.astrologypoints': astrology })
}
/*-------------------------------------------- */
getHoroscopesBonus() {
let astro = this.items.filter(it => it.type == "feature" && it.system.subtype == "horoscope" && !it.system.properties.ishoroscopemajor
&& it.system.properties.horoscopeanswer == "favorable")
let astro = this.items.filter(it => it.type == "feature" && it.system.subtype == "horoscope" && !it.system.properties.ishoroscopemajor
&& it.system.properties.horoscopeanswer == "favorable")
return astro
}
/*-------------------------------------------- */
getHoroscopesMalus() {
let astro = this.items.filter(it => it.type == "feature" && it.system.subtype == "horoscope" && !it.system.properties.ishoroscopemajor
&& it.system.properties.horoscopeanswer == "unfavorable")
let astro = this.items.filter(it => it.type == "feature" && it.system.subtype == "horoscope" && !it.system.properties.ishoroscopemajor
&& it.system.properties.horoscopeanswer == "unfavorable")
return astro
}
@ -458,26 +496,34 @@ export class BoLActor extends Actor {
manageHoroscope(rollData) {
//Spent points
this.spentAstrologyPoints(rollData.astrologyPointsCost)
if ( rollData.horoscopeType == "minor") {
let horoscope = { name: "SITUATION A SPECIFIER", type :"feature",
if (rollData.horoscopeType == "minor") {
let horoscope = {
name: "SITUATION A SPECIFIER", type: "feature",
img: "icons/magic/perception/eye-ringed-glow-angry-large-red.webp",
system :{subtype: "horoscope", properties: {
ishoroscopemajor: false,
horoscopeanswer: (rollData.isSuccess) ? "favorable": "unfavorable",
rank: rollData.careerBonus
}
system: {
subtype: "horoscope", properties: {
ishoroscopemajor: false,
horoscopeanswer: (rollData.isSuccess) ? "favorable" : "unfavorable",
rank: rollData.careerBonus
}
}
}
this.createEmbeddedDocuments('Item', [horoscope])
}
if ( rollData.horoscopeType == "major" ) {
if ( rollData.isSuccess) {
this.subHeroPoints(1)
if (rollData.horoscopeType == "major") {
let actorHoroscope = this
if(rollData.targetId) {
let token = game.scenes.current.tokens.get(rollData.targetId)
actorHoroscope = token.actor
}
if (rollData.isSuccess) {
actorHoroscope.addHeroPoints(1)
} else {
this.addHeroPoints(1)
}
actorHoroscope.subHeroPoints(1)
}
rollData.horoscopeName = actorHoroscope.name
}
if ( rollData.horoscopeType == "majorgroup" ) {
if (rollData.horoscopeType == "majorgroup") {
let rID = randomID(16)
let horoscopes = duplicate(game.settings.get("bol", "horoscope-group"))
horoscopes[rID] = {
@ -485,7 +531,7 @@ export class BoLActor extends Actor {
name: game.i18n.localize("BOL.ui.groupHoroscope") + this.name,
maxDice: rollData.careerBonus,
availableDice: rollData.careerBonus,
type: (rollData.isSuccess) ? "bonus": "malus"
type: (rollData.isSuccess) ? "bonus" : "malus"
}
game.settings.set("bol", "horoscope-group", horoscopes)
}
@ -497,10 +543,10 @@ export class BoLActor extends Actor {
return this.system.resources.astrologypoints.value
}
/*-------------------------------------------- */
removeHoroscopeMinor( rollData) {
removeHoroscopeMinor(rollData) {
let toDel = []
for(let horo of rollData.selectedHoroscope) {
toDel.push( horo._id )
for (let horo of rollData.selectedHoroscope) {
toDel.push(horo._id)
}
if (toDel.length > 0) {
this.deleteEmbeddedDocuments('Item', toDel)
@ -519,7 +565,7 @@ export class BoLActor extends Actor {
newPC = alchemy.system.properties.pccurrent + pcCost
await this.updateEmbeddedDocuments('Item', [{ _id: alchemy.id, 'system.properties.pccurrent': newPC }])
} else {
ui.notifications.warn( game.i18n.localize("BOL.ui.nomorealchemypoints") )
ui.notifications.warn(game.i18n.localize("BOL.ui.nomorealchemypoints"))
}
}
}
@ -716,30 +762,30 @@ export class BoLActor extends Actor {
let lastHP = await this.getFlag("world", hpID)
if (lastHP != this.system.resources.hp.value && game.user.isGM) { // Only GM sends this
await this.setFlag("world", hpID, this.system.resources.hp.value)
let prone = this.effects.find( ef => ef.label == "EFFECT.StatusProne")
let dead = this.effects.find( ef => ef.label == "EFFECT.StatusDead")
let prone = this.effects.find(ef => ef.label == "EFFECT.StatusProne")
let dead = this.effects.find(ef => ef.label == "EFFECT.StatusDead")
if (this.system.resources.hp.value <= 0) {
if ( !prone) {
if (!prone) {
await this.createEmbeddedDocuments("ActiveEffect", [
{label: 'EFFECT.StatusProne', icon: 'icons/svg/falling.svg', flags: { core: { statusId: 'prone' } } }
{ label: 'EFFECT.StatusProne', icon: 'icons/svg/falling.svg', flags: { core: { statusId: 'prone' } } }
])
}
if ( this.system.resources.hp.value < -5 && !dead) {
if (this.system.resources.hp.value < -5 && !dead) {
await this.createEmbeddedDocuments("ActiveEffect", [
{label: 'EFFECT.StatusDead', icon: 'icons/svg/skull.svg', flags: { core: { statusId: 'dead' } } }
{ label: 'EFFECT.StatusDead', icon: 'icons/svg/skull.svg', flags: { core: { statusId: 'dead' } } }
])
}
ChatMessage.create({
alias: 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.system.resources.hp.value })
content: await renderTemplate('systems/bol/templates/chat/chat-vitality-zero.hbs', { name: this.name, img: this.img, hp: this.system.resources.hp.value, isHeroAdversary: this.isHeroAdversary() })
})
} else {
if ( prone ) {
await this.deleteEmbeddedDocuments("ActiveEffect", [ prone.id ] )
if (prone) {
await this.deleteEmbeddedDocuments("ActiveEffect", [prone.id])
}
if ( dead ) {
await this.deleteEmbeddedDocuments("ActiveEffect", [ dead.id ] )
if (dead) {
await this.deleteEmbeddedDocuments("ActiveEffect", [dead.id])
}
}
}
@ -751,9 +797,47 @@ export class BoLActor extends Actor {
await this.setFlag("world", "last-initiative", rollData)
}
/*-------------------------------------------- */
storeVitaliteCombat() {
this.setFlag("world", "vitalite-before-combat", duplicate(this.system.resources.hp))
}
/*-------------------------------------------- */
async displayRecuperation() {
let previousHP = this.getFlag("world", "vitalite-before-combat")
let lossHP = previousHP.value - this.system.resources.hp.value
//console.log(">>>>> RECUP INFO", previousHP, this.system.resources.hp.value)
if (previousHP && lossHP > 0 && this.system.resources.hp.value > 0) {
let msg = await ChatMessage.create({
alias: this.name,
whisper: BoLUtility.getWhisperRecipientsAndGMs(this.name),
content: await renderTemplate('systems/bol/templates/chat/chat-recup-information.hbs', {
name: this.name,
img: this.img,
actorId: this.id,
lossHP: lossHP,
recupHP: Math.ceil(lossHP / 2)
})
})
}
this.unsetFlag("world", "vitalite-before-combat")
}
/*-------------------------------------------- */
async applyRecuperation(recupHP) {
let hp = duplicate(this.system.resources.hp)
//console.log("RECUP !!!!", hp, recupHP)
hp.value += Number(recupHP)
hp.value = Math.min(hp.value, hp.max)
this.update({ 'system.resources.hp': hp })
let msg = await ChatMessage.create({
alias: this.name,
whisper: BoLUtility.getWhisperRecipientsAndGMs(this.name),
content: game.i18n.format( "BOL.chat.inforecup", {name: this.name, recupHP: recupHP} )
})
}
/*-------------------------------------------- */
clearInitiative() {
this.unsetFlag("world", "last-initiative" )
this.unsetFlag("world", "last-initiative")
}
/*-------------------------------------------- */
@ -763,53 +847,81 @@ export class BoLActor extends Actor {
}
return game.bol.config.creatureSize["medium"].order // Medium size per default
}
/*-------------------------------------------- */
checkNumeric(myObject) {
if ( myObject) {
for (let key in myObject) {
if ( myObject[key].value === null ) {
myObject[key].value = 0
}
if ( myObject[key].value === NaN ) {
myObject[key].value = 0
}
}
}
}
/*-------------------------------------------- */
_preUpdate(data, options, userId) {
if (data.system?.attributes) {
this.checkNumeric(data.system.attributes)
}
if (data.system?.aptitudes) {
this.checkNumeric(data.system.aptitudes)
}
if (data.system?.resources) {
this.checkNumeric(data.system.resources)
}
super._preUpdate(data, options, userId)
}
/*-------------------------------------------- */
getInitiativeRank(rollData = undefined, isCombat = false, combatData) {
if (!rollData) {
rollData = this.getFlag("world", "last-initiative")
}
let fvttInit = 4 // Pietaille par defaut
if (this.type == 'character' ) {
if (this.type == 'character') {
fvttInit = 5
if (!rollData) {
fvttInit = -1
if ( isCombat ) {
ui.notifications.info(game.i18n.localize("BOL.ui.warninitiative"))
BoLRoll.aptitudeCheck(this, "init", undefined, combatData )
if (!rollData) {
if (isCombat) {
if (game.user.isGM ) {
if (this.hasPlayerOwner) {
game.socket.emit("system.bol", { name: "msg_request_init_roll", data: { actorId: this.id, combatData } })
} else {
BoLRoll.aptitudeCheck(this, "init", undefined, combatData);
}
}
}
} else {
if (rollData.isLegendary) {
fvttInit = 10
} else if (rollData.isCritical) {
fvttInit = 9
} else if (rollData.isSuccess ) {
} else if (rollData.isSuccess) {
fvttInit = 8
} else if (rollData.isFumble) {
fvttInit = 3
}
}
}
}
if ( this.getCharType() == 'adversary') {
if (this.getCharType() == 'adversary') {
fvttInit = 7
}
if ( this.getCharType() == 'tough') {
}
if (this.getCharType() == 'tough') {
fvttInit = 6
}
if ( this.getCharType() == 'creature') {
if (this.getCharType() == 'creature') {
let mySize = this.getSize()
let sizeSmall = game.bol.config.creatureSize["small"].order
let sizeMedium = game.bol.config.creatureSize["medium"].order
if ( mySize >= sizeSmall && mySize <= sizeMedium) {
if (mySize >= sizeSmall && mySize <= sizeMedium) {
fvttInit = 6
}
if ( mySize > sizeMedium) {
if (mySize > sizeMedium) {
fvttInit = 7
}
}
return fvttInit
}
/*-------------------------------------------- */
async subHeroPoints(nb) {
let newHeroP = this.system.resources.hero.value - nb;
@ -823,6 +935,11 @@ export class BoLActor extends Actor {
await this.update({ 'system.resources.hero.value': newHeroP });
}
/*-------------------------------------------- */
incDecResources(target, value) {
let newValue = this.system.resources[target].value + value
this.update({ [`system.resources.${target}.value`]: newValue })
}
/*-------------------------------------------- */
async sufferDamage(damage) {
let newHP = this.system.resources.hp.value - damage
@ -839,13 +956,13 @@ export class BoLActor extends Actor {
} else if (protect.system.subtype == 'armor') {
if (BoLUtility.getRollArmor()) {
if (!protect.system.properties.soak.formula || protect.system.properties.soak.formula == "") {
ui.notifications.warn( game.i18n.localize("BOL.ui.armornoformula", protect.name) )
ui.notifications.warn(game.i18n.localize("BOL.ui.armornoformula", protect.name))
} else {
formula += "+" + " max(" + protect.system.properties.soak.formula + ",0)"
}
} else {
if (protect.system.properties.soak.value == undefined) {
ui.notifications.warn( game.i18n.localize("BOL.ui.armornoformula", protect.name) )
ui.notifications.warn(game.i18n.localize("BOL.ui.armornoformula", protect.name))
} else {
formula += "+ " + protect.system.properties.soak.value
}

View File

@ -122,7 +122,7 @@ Hooks.once('ready', async function () {
BoLUtility.ready()
BoLCharacterSummary.ready()
registerUsageCount('bol')
registerUsageCount(game.system.id)
welcomeMessage()

View File

@ -220,6 +220,9 @@ export class BoLRoll {
/* -------------------------------------------- */
static horoscopeCheck(actor, event, horoscopeType) {
let target = BoLUtility.getTarget()
let cost = (horoscopeType == "minor") ? 1 : 2
if (cost > actor.getAstrologyPoints()) {
ui.notifications.warn(game.i18n.localize("BOL.ui.astrologyNoPoints"))
@ -233,6 +236,7 @@ export class BoLRoll {
rollData.astrologyPointsCost = cost
rollData.label = game.i18n.localize('BOL.ui.makeHoroscope')
rollData.description = game.i18n.localize('BOL.ui.makeHoroscope') + " " + game.i18n.localize(rollData.horoscopeTypeLabel)
rollData.targetId = target?.id
console.log("HOROSCOPE!", rollData);
return this.displayRollDialog(rollData);
@ -284,7 +288,7 @@ export class BoLRoll {
for (let effect of this.rollData.bolApplicableEffects) {
if (effect.system.properties.modifier == "1B") {
this.rollData.bmDice++;
} else if (effect.system.properties.modifier == "1B") {
} else if (effect.system.properties.modifier == "2B") {
this.rollData.bmDice += 2;
} else if (effect.system.properties.modifier == "1M") {
this.rollData.bmDice--;
@ -523,6 +527,7 @@ export class BoLRoll {
rollData.nbBoons = 0
rollData.nbFlaws = 0
rollData.nbDice = 0
rollData.isHeroAdversary = actor.isHeroAdversary()
if (rollData.shieldBlock == 'blockall') {
rollData.shieldMalus = rollData.shieldAttackMalus;
} else {
@ -579,7 +584,8 @@ export class BoLRoll {
}
let diceData = BoLUtility.getDiceData()
const modifiers = rollbase + rollData.careerBonus + rollData.mod + rollData.weaponModifier - rollData.defence - rollData.modArmorMalus + rollData.shieldMalus + rollData.attackModifier + rollData.appliedArmorMalus + rollData.effectModifier
let malusInit = rollData.combatData?.malusInit || 0
const modifiers = rollbase + rollData.careerBonus + rollData.mod + rollData.weaponModifier - rollData.defence - rollData.modArmorMalus + rollData.shieldMalus + rollData.attackModifier + rollData.appliedArmorMalus + rollData.effectModifier - malusInit
const formula = (isMalus) ? rollData.nbDice + "d" + diceData.diceFormula + "kl2 + " + modifiers : rollData.nbDice + "d" + diceData.diceFormula + "kh2 + " + modifiers
rollData.formula = formula
rollData.modifiers = modifiers
@ -642,10 +648,11 @@ export class BoLDefaultRoll {
this.rollData.reroll = actor.heroReroll()
}
if (this.rollData.registerInit) {
actor.registerInit(this.rollData)
await actor.registerInit(this.rollData)
this.rollData.initiativeRank = actor.getInitiativeRank(this.rollData)
if (this.rollData.combatData) { // If combatData present
let combat = game.combats.get(this.rollData.combatData.combatId)
console.log("SET INIT!!!!!", this.rollData.initiativeRank)
combat.setInitiative(this.rollData.combatData.combatantId, this.rollData.initiativeRank)
}
}
@ -677,13 +684,14 @@ export class BoLDefaultRoll {
async sendChatMessage() {
let actor = BoLUtility.getActorFromRollData(this.rollData)
this._buildChatMessage(this.rollData).then(async msgFlavor => {
//console.log("MSG", msgFlavor )
let msg = await this.rollData.roll.toMessage({
user: game.user.id,
rollMode: game.settings.get("core", "rollMode"),
//whisper: BoLUtility.getWhisperRecipientsAndGMs(this.rollData.actor.name),
flavor: msgFlavor,
speaker: ChatMessage.getSpeaker({ actor: actor }),
})
this.rollData.roll = duplicate(this.rollData.roll) // Remove object, keep data (v111 ready)
msg.setFlag("world", "bol-roll-data", this.rollData)
})
}
@ -766,13 +774,13 @@ export class BoLDefaultRoll {
let weaponFormula = BoLUtility.getDamageFormula(this.rollData.weapon.system, this.rollData.fightOption)
let damageFormula = weaponFormula + "+" + bonusDmg + "+" + attrDamageValue
console.log("DAMAGE !!!", damageFormula, attrDamageValue, this.rollData)
//console.log("Formula", weaponFormula, damageFormula, this.rollData.weapon.data.data.properties.damage)
this.rollData.damageFormula = damageFormula
this.rollData.damageRoll = new Roll(damageFormula)
await this.rollData.damageRoll.roll({ "async": false })
this.rollData.damageTotal = this.rollData.damageRoll.total
console.log("DAMAGE !!!", damageFormula, attrDamageValue, this.rollData)
}
BoLUtility.cleanupButtons(this.rollData.optionsId)
this.sendDamageMessage()

View File

@ -20,12 +20,15 @@ export class BoLCombatManager extends Combat {
console.log(`${game.system.title} | Combat.rollInitiative()`, ids, formula, messageOptions);
// Structure input data
ids = typeof ids === "string" ? [ids] : ids;
//const currentId = this.combatant.id;
// Get initiative malus from tough/adversary
let malusInit = 0
for (let combatant of this.combatants) {
malusInit = Math.max(malusInit, combatant.actor.getInitiativeMalus())
}
// calculate initiative
for (let cId = 0; cId < ids.length; cId++) {
for (let cId = 0; cId < ids.length; cId++) {
const combatant = this.combatants.get(ids[cId])
let fvttInit = combatant.actor.getInitiativeRank(false, true, {combatId: this.id, combatantId: combatant.id } )
let fvttInit = combatant.actor.getInitiativeRank(false, true, { combatId: this.id, combatantId: combatant.id, malusInit })
fvttInit += (cId / 100)
await this.updateEmbeddedDocuments("Combatant", [{ _id: ids[cId], initiative: fvttInit }]);
}
@ -33,23 +36,49 @@ export class BoLCombatManager extends Combat {
/************************************************************************************/
nextRound() {
let combatants = this.combatants.contents
for (let c of combatants) {
let actor = game.actors.get( c.actorId )
actor.clearRoundModifiers()
if (game.user.isGM) {
let combatants = this.combatants.contents
let autoRemoveDead = game.settings.get("bol", "auto-remove-dead") // Optionnal auto-removal of dead char.
for (let c of combatants) {
//let actor = game.actors.get(c.actorId)
c.actor.clearRoundModifiers()
let toRemove = []
if (autoRemoveDead && c.actor.type == "encounter" && (c.actor.system.chartype == "tough" || c.actor.system.chartype == "creature" || c.actor.system.chartype == "base") && c.actor.system.resources.hp.value <= 0) {
toRemove.push(c.id || c._id)
}
//console.log("REM", autoRemoveDead, toRemove, c.actor)
if (toRemove.length > 0) {
this.deleteEmbeddedDocuments('Combatant', toRemove)
}
}
}
super.nextRound()
}
/************************************************************************************/
startCombat() {
if (game.user.isGM) {
let combatants = this.combatants.contents
for (let c of combatants) {
let actor = game.actors.get(c.actorId)
actor.storeVitaliteCombat()
}
}
return super.startCombat()
}
/*-***********************************************************************************/
_onDelete() {
let combatants = this.combatants.contents
for (let c of combatants) {
let actor = game.actors.get(c.actorId)
actor.clearInitiative()
if (game.user.isGM) {
let combatants = this.combatants.contents
for (let c of combatants) {
let actor = game.actors.get(c.actorId)
actor.clearInitiative()
actor.displayRecuperation()
}
}
super._onDelete()
}
}

View File

@ -1,4 +1,4 @@
import { BoLDefaultRoll } from "../controllers/bol-rolls.js";
import { BoLRoll, BoLDefaultRoll } from "../controllers/bol-rolls.js";
// Spell circle to min PP cost
const __circle2minpp = { 0: 0, 1: 2, 2: 6, 3: 11 }
@ -8,8 +8,6 @@ export class BoLUtility {
/* -------------------------------------------- */
static init() {
this.attackStore = {}
game.settings.register("bol", "rollArmor", {
name: "Effectuer des jets pour les armures",
hint: "Effectue un jet de dés pour les armures (valeur fixe si désactivé)",
@ -28,12 +26,20 @@ export class BoLUtility {
type: Boolean,
onChange: lang => window.location.reload()
})
game.settings.register("bol", "auto-remove-dead", {
name: "Enlever les PNJs morts automatiquement au round suivant",
hint: "Supprime les PNJ (piétaille, créatures, coriaces) automatiquement du combat lorsqu'ils sont à 0 Vitalité ou moins, lors du passage au round suivant",
scope: "world",
config: true,
default: false,
type: Boolean
})
game.settings.register("bol", "dice-formula", {
name: "Formule de dés",
hint: "Sélectionne la formule de dés (par défaut 2d6)",
scope: "world",
config: true,
default: "2d6",
default: "6",
type: String,
choices: { "6": "2d6", "8":"2d8", "10":"2d10", "12":"2d12", "20":"2d20"},
onChange: value => {
@ -151,7 +157,7 @@ export class BoLUtility {
df = "6"
}
return {
diceFormula: this.diceFormula,
diceFormula: df,
successValue : this.successValue,
criticalSuccessValue: this.criticalSuccessValue,
criticalFailureValue: this.criticalFailureValue
@ -193,29 +199,6 @@ export class BoLUtility {
CONFIG.statusEffects = duplicate(game.bol.config.statusEffects)
}
/* -------------------------------------------- */
static templateData(it) {
return BoLUtility.data(it)?.data ?? {}
}
/* -------------------------------------------- */
static data(it) {
if (it instanceof Actor || it instanceof Item || it instanceof Combatant) {
return it.data;
}
return it;
}
/* -------------------------------------------- */
static storeRoll(roll) {
this.rollTab[roll.id] = roll
}
/* -------------------------------------------- */
static getRoll(rollId) {
return this.rollTab[roll.id]
}
/* -------------------------------------------- */
static createDirectOptionList(min, max) {
let options = {};
@ -260,7 +243,7 @@ export class BoLUtility {
}
/* -------------------------------------------- */
static getUsers(filter) {
return game.users.filter(filter).map(user => user.data._id);
return game.users.filter(filter).map(user => user.id);
}
/* -------------------------------------------- */
static getWhisperRecipients(rollMode, name) {
@ -298,13 +281,13 @@ export class BoLUtility {
}
/* -------------------------------------------- */
static sendAttackSuccess(attackDef) {
if (attackDef.targetId) {
static sendAttackSuccess(rollData) {
if (rollData.targetId) {
// Broadcast to GM or process it directly in case of GM defense
if (!game.user.isGM) {
game.socket.emit("system.bol", { name: "msg_attack_success", data: duplicate(attackDef) })
game.socket.emit("system.bol", { name: "msg_attack_success", data: duplicate(rollData) })
} else {
BoLUtility.processAttackSuccess(attackDef)
BoLUtility.processAttackSuccess(rollData)
}
}
}
@ -330,6 +313,14 @@ export class BoLUtility {
let message = game.messages.get(messageId)
return message.getFlag("world", "bol-roll-data")
}
/* -------------------------------------------- */
static requestInitRoll(actorId, combatData ) {
let actor = game.actors.get( actorId )
if (actor && actor.isOwner) {
ui.notifications.info(game.i18n.localize("BOL.ui.warninitiative"))
BoLRoll.aptitudeCheck(actor, "init", undefined, combatData)
}
}
/* -------------------------------------------- */
static cleanupButtons(id) {
@ -397,6 +388,19 @@ export class BoLUtility {
game.socket.emit("system.bol", { name: "msg_damage_handling", data: { msgId: msgId, attackId: attackId, defenseMode: defenseMode, weaponId: weaponId } })
}
})
html.on("click", '.recup-vitalite', event => {
event.preventDefault()
let actorId = event.currentTarget.attributes['data-actor-id'].value
let recupHP = event.currentTarget.attributes['data-recup-hp'].value
let actor = game.actors.get(actorId)
let messageId = BoLUtility.findChatMessageId(event.currentTarget)
BoLUtility.removeChatMessageId(messageId)
actor.applyRecuperation(recupHP)
})
}
/* -------------------------------------------- */
@ -404,48 +408,50 @@ export class BoLUtility {
if (!game.user.isGM) {
return
}
let message = game.messages.get(msgId)
let rollData = message.getFlag("world", "bol-roll-data")
BoLUtility.removeChatMessageId(msgId)
console.log("Damage Handling", attackId, defenseMode, weaponId)
// Only GM process this
let attackDef = this.attackStore[attackId]
if (attackDef && attackDef.defenderId) {
if (attackDef.defenseDone) {
if (rollData && rollData.defenderId) {
if (rollData.defenseDone || defenseMode == 'damage-not-applied') {
return
} // ?? Why ???
attackDef.defenseDone = true
attackDef.defenseMode = defenseMode
let token = game.scenes.current.tokens.get(attackDef.targetId)
rollData.defenseDone = true
rollData.defenseMode = defenseMode
let token = game.scenes.current.tokens.get(rollData.targetId)
let defender = token.actor
if (defenseMode == 'damage-with-armor') {
let armorFormula = defender.getArmorFormula()
attackDef.rollArmor = new Roll(armorFormula)
attackDef.rollArmor.roll({ async: false })
attackDef.armorProtect = (attackDef.rollArmor.total < 0) ? 0 : attackDef.rollArmor.total
attackDef.finalDamage = attackDef.damageTotal - attackDef.armorProtect
attackDef.finalDamage = (attackDef.finalDamage < 0) ? 0 : attackDef.finalDamage
defender.sufferDamage(attackDef.finalDamage)
console.log("Armor roll -> result ", attackDef)
rollData.rollArmor = new Roll(armorFormula)
rollData.rollArmor.roll({ async: false })
rollData.armorProtect = (rollData.rollArmor.total < 0) ? 0 : rollData.rollArmor.total
rollData.finalDamage = rollData.damageTotal - rollData.armorProtect
rollData.finalDamage = (rollData.finalDamage < 0) ? 0 : rollData.finalDamage
defender.sufferDamage(rollData.finalDamage)
console.log("Armor roll -> result ", rollData)
}
if (defenseMode == 'damage-without-armor') {
attackDef.finalDamage = attackDef.damageTotal
defender.sufferDamage(attackDef.finalDamage)
rollData.finalDamage = atrollDatatackDef.damageTotal
defender.sufferDamage(rollData.finalDamage)
}
if (defenseMode == 'hero-reduce-damage') {
let armorFormula = defender.getArmorFormula()
attackDef.rollArmor = new Roll(armorFormula)
attackDef.rollArmor.roll({ async: false })
attackDef.armorProtect = (attackDef.rollArmor.total < 0) ? 0 : attackDef.rollArmor.total
attackDef.rollHero = new Roll("1d6")
attackDef.rollHero.roll({ async: false })
attackDef.finalDamage = attackDef.damageTotal - attackDef.rollHero.total - attackDef.armorProtect
attackDef.finalDamage = (attackDef.finalDamage < 0) ? 0 : attackDef.finalDamage
defender.sufferDamage(attackDef.finalDamage)
rollData.rollArmor = new Roll(armorFormula)
rollData.rollArmor.roll({ async: false })
rollData.armorProtect = (rollData.rollArmor.total < 0) ? 0 : rollData.rollArmor.total
rollData.rollHero = new Roll("1d6")
rollData.rollHero.roll({ async: false })
rollData.finalDamage = rollData.damageTotal - rollData.rollHero.total - rollData.armorProtect
rollData.finalDamage = (rollData.finalDamage < 0) ? 0 : rollData.finalDamage
defender.sufferDamage(rollData.finalDamage)
defender.subHeroPoints(1)
}
if (defenseMode == 'hero-in-extremis') {
attackDef.finalDamage = 0;
attackDef.weaponHero = defender.weapons.find(item => item._id == weaponId);
rollData.finalDamage = 0;
rollData.weaponHero = defender.weapons.find(item => item._id == weaponId);
defender.deleteEmbeddedDocuments("Item", [weaponId]);
}
@ -456,16 +462,16 @@ export class BoLUtility {
}
}
let damageResults = {
attackId: attackDef.id,
attacker: attackDef.attacker,
rollArmor: attackDef.rollArmor,
rollHero: attackDef.rollHero,
weaponHero: attackDef.weaponHero,
armorProtect: attackDef.armorProtect,
attackId: rollData.id,
attacker: rollData.attacker,
rollArmor: rollData.rollArmor,
rollHero: rollData.rollHero,
weaponHero: rollData.weaponHero,
armorProtect: rollData.armorProtect,
name: defender.name,
defender: defender,
defenseMode: attackDef.defenseMode,
finalDamage: attackDef.finalDamage
defenseMode: rollData.defenseMode,
finalDamage: rollData.finalDamage
}
ChatMessage.create({
alias: defender.name,
@ -509,7 +515,7 @@ export class BoLUtility {
}
/* -------------------------------------------- */
static isRangedWeapon(weapon) {
return weapon.data.type == 'ranged' || weapon.data.thrown;
return weapon.system.type == 'ranged' || weapon.system.thrown;
}
/* -------------------------------------------- */
@ -552,28 +558,28 @@ export class BoLUtility {
}
/* -------------------------------------------- */
static async processAttackSuccess(attackDef) {
console.log("Attack success processing", attackDef)
if (!game.user.isGM || !attackDef.defenderId) { // Only GM process this
static async processAttackSuccess(rollData) {
console.log("Attack success processing", rollData)
if (!game.user.isGM || !rollData.defenderId) { // Only GM process this
return
}
// Build and send the defense message to the relevant people (ie GM + defender)
let defender = game.actors.get(attackDef.defenderId)
console.log("DEF WEP", attackDef, defender)
let defender = game.actors.get(rollData.defenderId)
let defenderWeapons = defender.weapons || []
this.attackStore[attackDef.id] = attackDef // Store !
ChatMessage.create({
let msg = await ChatMessage.create({
alias: defender.name,
whisper: BoLUtility.getWhisperRecipientsAndGMs(defender.name),
content: await renderTemplate('systems/bol/templates/chat/rolls/defense-request-card.hbs', {
attackId: attackDef.id,
attacker: attackDef.attacker,
attackId: rollData.id,
attacker: rollData.attacker,
defender: defender,
defenderWeapons: defenderWeapons,
damageTotal: attackDef.damageRoll.total,
damagesIgnoresArmor: attackDef.damagesIgnoresArmor,
damageTotal: rollData.damageTotal,
damagesIgnoresArmor: rollData.damagesIgnoresArmor,
})
})
msg.setFlag("world", "bol-roll-data", rollData)
console.log("DEF WEP", rollData, defender)
}
/* -------------------------------------------- */
@ -584,6 +590,9 @@ export class BoLUtility {
if (sockmsg.name == "msg_cleanup_buttons") {
$(`#${sockmsg.data.id}`).hide() // Hide the options roll buttons
}
if (sockmsg.name == "msg_request_init_roll") {
this.requestInitRoll( sockmsg.data.actorId, sockmsg.data.combatData)
}
if (sockmsg.name == "msg_damage_handling") {
BoLUtility.processDamageHandling(sockmsg.data.attackId, sockmsg.data.defenseMode, sockmsg.data.weaponId, sockmsg.data.msgId)
}
@ -591,15 +600,15 @@ export class BoLUtility {
/* -------------------------------------------- */
static computeSpellCost(spell, nbOptCond = 0) {
let pp = spell.data.properties.ppcost
let minpp = __circle2minpp[spell.data.properties.circle]
let pp = spell.system.properties.ppcost
let minpp = __circle2minpp[spell.system.properties.circle]
pp = (pp - nbOptCond < minpp) ? minpp : pp - nbOptCond
return pp
}
/* -------------------------------------------- */
static getDamageFormula(weaponData, fightOption) {
let upgradeDamage = (fightOption && fightOption.data.properties.fightoptiontype == "twoweaponsatt")
let upgradeDamage = (fightOption && fightOption.system.properties.fightoptiontype == "twoweaponsatt")
let damageString = weaponData.properties.damage
let modifier = weaponData.properties.damageModifiers ?? 0
let multiplier = weaponData.properties.damageMultiplier ?? 1

View File

@ -118,7 +118,7 @@ export const registerHandlebarsHelpers = function () {
})
Handlebars.registerHelper('includesKey', function (items, type, key) {
// console.log(items);
return items.filter(i => i.type === type).map(i => i.data.key).includes(key);
return items.filter(i => i.type === type).map(i => i.system.key).includes(key);
})
Handlebars.registerHelper('includes', function (array, val) {
return array.includes(val);

View File

@ -49,7 +49,7 @@ export default function registerHooks() {
let macro = game.macros.entities.find(m => (m.name === actor.name) && (m.command === command));
if (!macro) {
macro = await Macro.create({
name: actor.data.name,
name: actor.name,
type: "script",
img: "icons/svg/dice-target.svg",
command: command
@ -65,9 +65,9 @@ export default function registerHooks() {
let macro = game.macros.entities.find(m => (m.name === journal.name) && (m.command === command));
if (!macro) {
macro = await Macro.create({
name: journal.data.name,
name: journal.name,
type: "script",
img: (journal.data.img) ? journal.data.img : "icons/svg/book.svg",
img: (journal.img) ? journal.img : "icons/svg/book.svg",
command: command
}, {displaySheet: false})
game.user.assignHotbarMacro(macro, slot);

View File

@ -37,13 +37,13 @@ export class Macros {
};
if(rollType === "attribute") {
let attribute = eval(`actor.data.data.attributes.${key}`);
let attribute = eval(`actor.system.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 aptitude = eval(`actor.system.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

@ -14,7 +14,7 @@
],
"url": "https://www.uberwald.me/gitea/public/bol",
"license": "LICENSE.txt",
"version": "10.5.8",
"version": "10.5.16",
"compatibility": {
"minimum": "10",
"verified": "10"
@ -202,7 +202,7 @@
],
"socket": true,
"manifest": "https://www.uberwald.me/gitea/public/bol/raw/v10/system.json",
"download": "https://www.uberwald.me/gitea/public/bol/archive/bol-v10.5.8.zip",
"download": "https://www.uberwald.me/gitea/public/bol/archive/bol-v10.5.16.zip",
"background": "systems/bol/ui/page_accueil.webp",
"gridDistance": 1.5,
"gridUnits": "m",

View File

@ -1,8 +1,18 @@
<div class="attributes flexrow">
{{#each attributes as |attribute id|}}
<div class="attribute stat flex1 flex-group-center {{key}}">
<label class="stat-label"><a class="rollable" data-roll-type="attribute" data-roll="2d6+@attributes.{{key}}.value" data-adv="0" data-key="{{key}}">{{localize label}}</a></label><br/>
<input class="stat-value rounded" type="text" name="system.attributes.{{key}}.value" value="{{numberFormat value decimals=0 sign=true}}" data-dtype="Number"/><br/>
<label class="stat-label"><a class="rollable" data-roll-type="attribute" data-roll="2d6+@attributes.{{key}}.value" data-adv="0" data-key="{{key}}">{{localize label}}</a></label>
{{#if attribute.numModifier}}
<label class="stat-value rounded">{{attribute.numModifier}}</label>
{{/if}}
{{#if (count attribute.diceModifier)}}
<label class="stat-value rounded">{{attribute.diceModifier}}</label>
{{/if}}
<br/>
<input class="stat-value rounded" type="text" name="system.attributes.{{key}}.value" value="{{numberFormat value decimals=0 sign=true}}" data-dtype="Number"/>
<br/>
<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="darkgreen fas fa-dice"></i>
</span>
@ -24,8 +34,17 @@
<div class="aptitudes flexrow">
{{#each aptitudes as |aptitude id|}}
<div class="aptitude stat flex1 flex-group-center">
<label class="stat-label"><a class="rollable" data-roll-type="aptitude" data-roll="2d6+@aptitudes.{{key}}.value" data-adv="0" data-key="{{key}}">{{localize label}}</a></label><br/>
<label class="stat-label"><a class="rollable" data-roll-type="aptitude" data-roll="2d6+@aptitudes.{{key}}.value" data-adv="0" data-key="{{key}}">{{localize label}}</a></label>
{{#if aptitude.numModifier}}
<label class="stat-value rounded">{{aptitude.numModifier}}</label>
{{/if}}
{{#if (count aptitude.diceModifier)}}
<label class="stat-value rounded">{{aptitude.diceModifier}}</label>
{{/if}}
<br/>
<input class="stat-value rounded-border" type="text" name="system.aptitudes.{{key}}.value" value="{{numberFormat value decimals=0 sign=true}}" data-dtype="Number"/><br/>
<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="darkgreen fas fa-dice"></i>
</span>
@ -50,6 +69,10 @@
<div class="resource stat flex1 flex-group-center">
<label class="stat-label">{{localize label}}</label><br/>
<input class="stat-value resources-value" type="text" name="system.resources.{{key}}.value" value="{{numberFormat value decimals=0 sign=false}}" data-dtype="Number"/>
<a class="inc-dec-btns-resource" data-target="{{key}}" data-incr="-1"><i class="fas fa-minus-square"></i></a>&nbsp;
<a class="inc-dec-btns-resource" data-target="{{key}}" data-incr="1"><i class="fas fa-plus-square"></i></a>&nbsp;
{{#if (eq @root.charType 'player')}}
{{#if (exists bonus)}}
<span class="flexrow"><label class="stat-max bonus-text">Bonus</label><input class="resource-bonus resources-value" type="text" name="system.resources.{{key}}.bonus" value="{{numberFormat bonus decimals=0 sign=false}}" data-dtype="Number"/></span>

View File

@ -0,0 +1,12 @@
<div>
<img class="chat-icon" src="{{img}}" alt="{{name}}"/>
<h2 class="bad"><strong>{{name}}</strong></h2>
</div>
<div class="flexrow">
{{localize "BOL.chat.losshp" lossHP=lossHP recupHP=recupHP}}
<button class="recup-vitalite" data-actor-id="{{actorId}}" data-recup-hp="{{recupHP}}">{{localize "BOL.chat.applyrecup" recupHP=recupHP}}</button>
</div>

View File

@ -4,20 +4,25 @@
</div>
<div class="flexrow">
{{#if (eq hp 0)}}
{{localize "BOL.chat.vitalityzero" name=name hp=hp}}
<br>{{localize "BOL.chat.vitalityheroism"}}
<br>{{localize "BOL.chat.vitalityheroismhint"}}
{{else}}
{{localize "BOL.chat.vitalitydying" name=name hp=hp}}
<br>{{localize "BOL.chat.vitalitydyingheroism"}}
{{/if}}
{{#if (lt hp -5)}}
<br><strong>{{localize "BOL.chat.isdead" name=name}}</strong>
<br>{{localize "BOL.chat.epitaph"}}
{{/if}}
{{#if isHeroAdversary}}
{{#if (eq hp 0)}}
{{localize "BOL.chat.vitalityzero" name=name hp=hp}}
<br>{{localize "BOL.chat.vitalityheroism"}}
<br>{{localize "BOL.chat.vitalityheroismhint"}}
{{else}}
{{localize "BOL.chat.vitalitydying" name=name hp=hp}}
<br>{{localize "BOL.chat.vitalitydyingheroism"}}
{{/if}}
{{#if (lt hp -5)}}
<br><strong>{{localize "BOL.chat.isdead" name=name}}</strong>
<br>{{localize "BOL.chat.epitaph"}}
{{/if}}
{{else}}
<br><strong>{{localize "BOL.chat.isdead" name=name}}</strong>
<br>{{localize "BOL.chat.epitaph"}}
{{/if}}
</div>

View File

@ -3,5 +3,7 @@
{{#if isCritical}}
<button class="chat-damage-roll bol-margin-tb-2 " data-damage-mode="damage-plus-6" data-attack-id="{{id}}">{{localize "BOL.chat.rolldamage6"}}</button>
<button class="chat-damage-roll bol-margin-tb-2" data-damage-mode="damage-plus-12" data-attack-id="{{id}}">{{localize "BOL.chat.rolldamage12"}}</button>
{{/if}}
{{#if isRealCritical}}
<button class="chat-damage-roll bol-margin-tb-2" data-damage-mode="damage-plus-12" data-attack-id="{{id}}">{{localize "BOL.chat.rolldamage12"}}</button>
{{/if}}
{{/if}}

View File

@ -1,20 +1,20 @@
<div>
{{#if isSuccess}}
{{#if isLegendary}}
<h2 class="success critical"><i class="fas fa-check-double"></i>&nbsp;{{localize "BOL.ui.criticallegend"}}...
<h2 class="success critical"><i class="fas fa-check-double"></i> {{localize "BOL.ui.criticallegend"}}...
{{else}}
{{#if isCritical}}
<h2 class="success critical"><i class="fas fa-check-double"></i>&nbsp;{{localize "BOL.ui.critical"}}...
<h2 class="success critical"><i class="fas fa-check-double"></i> {{localize "BOL.ui.critical"}}...
{{else}}
<h2 class="success"><i class="fas fa-check"></i>&nbsp;{{localize "BOL.ui.success"}}...
<h2 class="success"><i class="fas fa-check"></i> {{localize "BOL.ui.success"}}...
{{/if}}
{{/if}}
{{/if}}
{{#if isFailure}}
{{#if isFumble}}
<h2 class="failure fumble"><i class="fas fa-skull-crossbones"></i>&nbsp;{{localize "BOL.ui.fumble"}}...
<h2 class="failure fumble"><i class="fas fa-skull-crossbones"></i> {{localize "BOL.ui.fumble"}}...
{{else}}
<h2 class="failure"><i class="fas fa-times"></i>&nbsp;{{localize "BOL.ui.failure"}}...
<h2 class="failure"><i class="fas fa-times"></i> {{localize "BOL.ui.failure"}}...
{{/if}}
{{/if}}
<img class="chat-icon" src="{{img}}" alt="{{actor.name}}"/>
@ -52,9 +52,6 @@
{{/if}}
<div id="{{optionsId}}">
{{#if isCritical}}
<div>
{{localize "BOL.chat.criticalinfo"}}
@ -63,11 +60,17 @@
<a class="content-link" draggable="true" data-uuid="Compendium.bol.aides-de-jeu.Yl1RKQb0BjVUtilk" data-id="Yl1RKQb0BjVUtilk" data-type="JournalEntry" data-pack="bol.aides-de-jeu" data-tooltip="un journal">
<i class="fas fa-book-open"></i>{{localize "BOL.chat.criticalbuttonjournal"}}</a>
</div>
{{#if (and isCritical weapon)}}
<div>
{{{localize "BOL.chat.heroicreminder"}}}
</div>
{{/if}}
{{/if}}
{{#if (and isSuccess weapon)}}
{{> "systems/bol/templates/chat/rolls/attack-damage-card.hbs"}}
{{/if}}
{{#if (and isSuccess spell)}}
{{> "systems/bol/templates/chat/rolls/spell-roll-card.hbs"}}
{{/if}}
@ -83,14 +86,14 @@
{{/if}}
{{#if reroll}}
<button class="chat-button button hero-reroll bol-margin-tb-2" data-roll-id=="{{rollId}}" data-actor-id="{{actor.id}}">{{localize "BOL.chat.reroll"}}</button>
<button class="chat-button button hero-reroll bol-margin-tb-2" data-actor-id="{{actorId}}">{{localize "BOL.chat.reroll"}}</button>
{{/if}}
{{#if (and isSuccess (not isCritical))}}
<button class="chat-button button transform-heroic-roll bol-margin-tb-2" data-roll-id=="{{rollId}}" data-actor-id="{{actor.id}}">{{localize "BOL.chat.toheroic"}}</button>
<button class="chat-button button transform-heroic-roll bol-margin-tb-2" data-actor-id="{{actorId}}">{{localize "BOL.chat.toheroic"}}</button>
{{/if}}
{{#if isRealCritical}}
<button class="chat-button button transform-legendary-roll bol-margin-tb-2" data-roll-id=="{{rollId}}" data-actor-id="{{actor.id}}">{{localize "BOL.chat.tolegend"}}</button>
<button class="chat-button button transform-legendary-roll bol-margin-tb-2" data-actor-id="{{actorId}}">{{localize "BOL.chat.tolegend"}}</button>
{{/if}}
<br>

View File

@ -6,13 +6,17 @@
{{/if}}
<button class="damage-handling" data-defense-mode="damage-with-armor" data-attack-id="{{attackId}}">{{localize "BOL.chat.witharmor"}}</button>
<button class="damage-handling" data-defense-mode="damage-without-armor" data-attack-id="{{attackId}}">{{localize "BOL.chat.withoutarmor"}}</button>
<button class="damage-handling" data-defense-mode="damage-without-armor" data-attack--id="{{attackId}}">{{localize "BOL.chat.withoutarmor"}}</button>
{{#if defender.system.resources.hero.value}}
<button class="damage-handling" data-defense-mode="hero-reduce-damage" data-attack-id="{{attackId}}">{{localize "BOL.chat.shakeoff"}}</button>
{{#if isHeroAdversary}}
{{#if (gt defender.system.resources.hero.value 0)}}
<button class="damage-handling" data-defense-mode="hero-reduce-damage" data-attack-id="{{attackId}}">{{localize "BOL.chat.shakeoff"}}</button>
{{#each defenderWeapons as |weapon idx|}}
<button class="damage-handling" data-defense-mode="hero-in-extremis" data-attack-id="{{@root.attackId}}" data-weapon-id="{{weapon._id}}">{{localize "BOL.chat.splinteredshield" name=weapon.name}}</button>
{{/each}}
{{#each defenderWeapons as |weapon idx|}}
<button class="damage-handling" data-defense-mode="hero-in-extremis" data-attack-id="{{@root.attackId}}" data-weapon-id="{{weapon._id}}">{{localize "BOL.chat.splinteredshield" name=weapon.name}}</button>
{{/each}}
{{/if}}
{{/if}}
<button class="damage-handling" data-defense-mode="damage-not-applied" data-attack-id="{{attackId}}">{{localize "BOL.chat.nodamage"}}</button>
{{/if}}

View File

@ -11,9 +11,9 @@
{{#if (eq horoscopeType "major")}}
{{#if isSuccess}}
{{localize "BOL.chat.horoscopemajorsuccess"}}
{{localize "BOL.chat.horoscopemajorsuccess" horoscopeName=horoscopeName}}
{{else}}
{{localize "BOL.chat.horoscopemajorfailure"}}
{{localize "BOL.chat.horoscopemajorfailure" horoscopeName=horoscopeName}}
{{/if}}
{{/if}}

View File

@ -22,8 +22,13 @@
<input class="field-value" type="checkbox" name="register-init" id="register-init" checked />
</label>
</div>
</div>
</div>
<div class="flexrow" style="margin-bottom: 1px;">
<div class="flex1 center bg-darkred">
<label for="mod">{{localize 'BOL.ui.initMalus'}}</label>
</div>
<div class="flex1 center cell">{{combatData.malusInit}}</div>
</div>
{{/if}}
{{> "systems/bol/templates/dialogs/career-roll-part.hbs"}}