diff --git a/lang/en.json b/lang/en.json index 5551ad8..46b6453 100644 --- a/lang/en.json +++ b/lang/en.json @@ -33,6 +33,7 @@ "BOL.ui.tab.description": "Description", "BOL.ui.tab.details": "Details", "BOL.ui.tab.spellalchemy": "Spells & Alchemy", + "BOL.ui.tab.astrologer": "Astrologer", "BOL.ui.properties": "Properties", "BOL.ui.description": "Description", @@ -115,6 +116,7 @@ "BOL.ui.isSorcerer": "Is Sorcerer ?", "BOL.ui.isAlchemist": "Is Alchemist ?", "BOL.ui.isPriest": "Is Priest/Druid ?", + "BOL.ui.isAstrologer": "Is Astrologer?", "BOL.ui.circle": "Circle", "BOL.ui.spells": "Spells", "BOL.ui.focusSpell": "Cast a spell", diff --git a/lang/fr.json b/lang/fr.json index df942e3..cc15ce7 100644 --- a/lang/fr.json +++ b/lang/fr.json @@ -28,7 +28,9 @@ "BOL.resources.power": "Pouvoir", "BOL.resources.villainy": "Vilénie", "BOL.resources.alchemypoints": "Points de Creation", + "BOL.resources.astrologypoints": "Points d'Astrologie", "BOL.traits.xp": "Expérience", + "BOL.ui.tab.stats": "Attributs", "BOL.ui.tab.combat": "Combat", "BOL.ui.tab.actions": "Actions", @@ -36,7 +38,19 @@ "BOL.ui.tab.equipment": "Equipement", "BOL.ui.tab.description": "Description", "BOL.ui.tab.details": "Details", - "BOL.ui.tab.spellalchemy": "Sorts&Alchimie", + "BOL.ui.tab.spellalchemy": "Mystères", + + "BOL.ui.astrologerPoints": "Points d'Astrologie", + "BOL.ui.astrologerPointsLabel": "Points d'Astrologie actuels", + "BOL.ui.ishoroscopemajor": "Horoscope Majeur (ie de groupe) ?", + "BOL.ui.answer": "Réponse", + "BOL.ui.horoscopefavorable": "Favorable (1dB)", + "BOL.ui.horoscopeunfavorable": "Défavorable (1dM)", + "BOL.ui.horoscopes": "Horoscopes", + "BOL.ui.horoscopesBonus": "Horoscopes (Bonus)", + "BOL.ui.horoscopesMalus": "Horoscopes (Malus)", + "BOL.ui.groupHoroscope": "Horoscrope de Groupe de ", + "BOL.ui.properties": "Propriétés", "BOL.ui.description": "Description", "BOL.ui.actions": "Actions", @@ -117,9 +131,11 @@ "BOL.ui.spellkeep": "Prolongation", "BOL.ui.concentrate": "Concentration", "BOL.ui.registerInit": "Enregistrer comme Init. de combat", + "BOL.ui.isSorcerer": "Carrière de Sorcier ?", "BOL.ui.isAlchemist": "Carrière d'Alchimiste ?", "BOL.ui.isPriest": "Carrière de Prêtre/Druide ?", + "BOL.ui.isAstrologer": "Carrière d'Astrologue?", "BOL.ui.circle": "Cercle", "BOL.ui.spells": "Sorts", "BOL.ui.focusSpell": "Lance un sort", @@ -137,6 +153,17 @@ "BOL.ui.alchemyCostTotal": "Points de Création nécessaires pour la Préparation", "BOL.ui.alchemyInvest": "Points de Création investis", "BOL.ui.alchemyCurrent": "Points de Création actuel dans la Préparation", + "BOL.ui.astrology": "Astrologie et Horoscopes", + "BOL.ui.astrologyMinor": "Etablir un Horoscope Mineur", + "BOL.ui.astrologyMajor": "Etablir un Horoscope Majeur", + "BOL.ui.astrologyMajorGroup": "Etablir un Horoscope Majeur de Groupe", + "BOL.ui.makeHoroscope": "Etablir un Horoscope", + "BOL.ui.astrologerRank": "Rang de l'Astrologue", + "BOL.ui.horoscopeCost": "Cout en Points d'Astrologie", + "BOL.ui.minor": "Mineur", + "BOL.ui.major": "Majeur", + "BOL.ui.majorgroup": "Majeur de Groupe", + "BOL.ui.advance": "Avancement", "BOL.ui.isbonusdice": "Fourni un dé bonus?", "BOL.ui.ismalusdice": "Fourni un dé malus?", @@ -209,6 +236,7 @@ "BOL.featureSubtypes.effect": "Effet", "BOL.featureSubtypes.effects": "Effets", "BOL.featureSubtypes.boleffect": "Effet", + "BOL.featureSubtypes.horoscope": "Horoscope", "BOL.fightOptionTypes.armor": "Attaque au défaut d'armure", "BOL.fightOptionTypes.intrepid": "Attaque intrépide", @@ -480,6 +508,16 @@ "BOL.chat.bougettesuccess": "Votre bougette reste inchangée !", "BOL.chat.bougettefailure": "Vous avez trop dépensé, votre bougette s'est réduite...", "BOL.chat.initiative": "Rang d'intiative (10 à 1)", + "BOL.chat.horoscope": "Horoscope", + "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.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.dialog.soeasy": "Inmanquable (+4)", "BOL.dialog.veryeasy": "Trés Facile (+2)", diff --git a/module/actor/actor-sheet.js b/module/actor/actor-sheet.js index 579cfe9..c57bf4b 100644 --- a/module/actor/actor-sheet.js +++ b/module/actor/actor-sheet.js @@ -136,6 +136,7 @@ export class BoLActorSheet extends ActorSheet { formData.treasure = this.actor.treasure formData.boleffects = this.actor.boleffects formData.alchemyrecipe = this.actor.alchemyrecipe + formData.horoscopes = this.actor.horoscopes formData.vehicles = this.actor.vehicles formData.fightoptions = this.actor.fightoptions formData.ammos = this.actor.ammos @@ -156,6 +157,8 @@ export class BoLActorSheet extends ActorSheet { formData.notes = await TextEditor.enrichHTML(this.object.system.details.notes, {async: true}) formData.isSorcerer = this.actor.isSorcerer() formData.isAlchemist = this.actor.isAlchemist() + formData.isAstrologer = this.actor.isAstrologer() + formData.isMysteries = formData.isSorcerer || formData.isAlchemist || formData.isAstrologer formData.isPriest = this.actor.isPriest() formData.isGM = game.user.isGM @@ -244,7 +247,16 @@ export class BoLActorSheet extends ActorSheet { case "careerxp": this.actor.incCareerXP( li.data("item-id")) break; - + case "horoscope-minor": + BoLRoll.horoscopeCheck(this.actor, event, "minor") + break + case "horoscope-major": + BoLRoll.horoscopeCheck(this.actor, event, "major") + break + case "horoscope-major-group": + BoLRoll.horoscopeCheck(this.actor, event, "majorgroup") + break + default: break; } } diff --git a/module/actor/actor.js b/module/actor/actor.js index 5d29b1a..02d5ac5 100644 --- a/module/actor/actor.js +++ b/module/actor/actor.js @@ -25,7 +25,7 @@ export class BoLActor extends Actor { /* -------------------------------------------- */ getCharType() { if (this.type === 'character') { - return 'player' + return "player" } return this.system.chartype } @@ -249,6 +249,9 @@ export class BoLActor extends Actor { get boleffects() { return this.items.filter(i => i.type === "feature" && i.system.subtype === "boleffect") } + get horoscopes() { + return this.items.filter(i => i.type === "feature" && i.system.subtype === "horoscope") + } get boons() { return duplicate(this.items.filter(i => i.type === "feature" && i.system.subtype === "boon") || []); } @@ -354,6 +357,11 @@ export class BoLActor extends Actor { return true return false } + isAstrologer() { + if (this.careers.find(item => item.system.properties.astrologer == true)) + return true + return false + } isPriest() { if (this.careers.find(item => item.system.properties.priest == true)) return true @@ -424,6 +432,74 @@ 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} ) + } + + /*-------------------------------------------- */ + getHoroscopesBonus() { + 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") + return astro + } + + /*-------------------------------------------- */ + manageHoroscope(rollData) { + //Spent points + this.spentAstrologyPoints(rollData.astrologyPointsCost) + 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 + } + } + } + this.createEmbeddedDocuments('Item', [horoscope]) + } + if ( rollData.horoscopeType == "major" ) { + if ( rollData.isSuccess) { + this.subHeroPoints(1) + } else { + this.addHeroPoints(1) + } + } + if ( rollData.horoscopeType == "majorgroup" ) { + let rID = randomID(16) + let horoscopes = duplicate(game.settings.get("bol", "horoscope-group")) + horoscopes[rID] = { + name: game.i18n.localize("BOL.ui.groupHoroscope") + this.name, + maxDice: rollData.careerBonus, + availableDice: rollData.careerBonus, + type: (rollData.isSuccess) ? "bonus": "malus" + } + } + + } + + /*-------------------------------------------- */ + removeHoroscopeMinor( rollData) { + let toDel = [] + for(let horo of rollData.selectedHoroscope) { + toDel.push( horo._id ) + } + if (toDel.length > 0) { + this.deleteEmbeddedDocuments('Item', toDel) + } + } /*-------------------------------------------- */ async spendAlchemyPoint(alchemyId, pcCost) { @@ -441,7 +517,14 @@ export class BoLActor extends Actor { } } } - + /*-------------------------------------------- */ + getAstrologerBonus() { + let astrologer = this.careers.find(item => item.system.properties.astrologer == true) + if (astrologer) { + return astrologer.system.rank + } + return 0; + } /*-------------------------------------------- */ getAlchemistBonus() { let sorcerer = this.careers.find(item => item.system.properties.alchemist == true) @@ -536,6 +619,11 @@ export class BoLActor extends Actor { "label": "BOL.featureSubtypes.effects", "ranked": false, "items": this.boleffects + }, + "horoscopes": { + "label": "BOL.featureSubtypes.horoscope", + "ranked": false, + "items": this.horoscopes } } } @@ -707,6 +795,12 @@ export class BoLActor extends Actor { newHeroP = (newHeroP < 0) ? 0 : newHeroP; await this.update({ 'system.resources.hero.value': newHeroP }); } + /*-------------------------------------------- */ + async addHeroPoints(nb) { + let newHeroP = this.system.resources.hero.value + nb; + newHeroP = (newHeroP < 0) ? 0 : newHeroP; + await this.update({ 'system.resources.hero.value': newHeroP }); + } /*-------------------------------------------- */ async sufferDamage(damage) { diff --git a/module/controllers/bol-rolls.js b/module/controllers/bol-rolls.js index b026c31..6666de1 100644 --- a/module/controllers/bol-rolls.js +++ b/module/controllers/bol-rolls.js @@ -18,20 +18,20 @@ export class BoLRoll { /* -------------------------------------------- */ static updateApplicableEffects(rollData) { let appEffects = [] - for( let effect of rollData.bolEffects) { - if(effect.system.properties.identifier =="always") { - appEffects.push(effect) + for (let effect of rollData.bolEffects) { + if (effect.system.properties.identifier == "always") { + appEffects.push(effect) } else if (effect.system.properties.identifier.includes(rollData.attribute.key)) { - appEffects.push(effect) - } else if ( rollData.aptitude && effect.system.properties.identifier.includes(rollData.aptitude.key)) { - appEffects.push(effect) + appEffects.push(effect) + } else if (rollData.aptitude && effect.system.properties.identifier.includes(rollData.aptitude.key)) { + appEffects.push(effect) } } return appEffects } /* -------------------------------------------- */ - static getCommonRollData(actor, mode, attribute, aptitude = undefined ) { + static getCommonRollData(actor, mode, attribute, aptitude = undefined) { let rollData = { mode: mode, @@ -42,8 +42,13 @@ export class BoLRoll { attrValue: attribute.value, aptValue: 0, careerBonus: 0, + horoscopeBonus: 0, + horoscopeMalus: 0, + selectedHoroscope: [], armorAgiMalus: actor.getArmorAgiMalus(), armorInitMalus: actor.getArmorInitMalus(), + horoscopeBonusList: actor.getHoroscopesBonus(), + horoscopeMalusList: actor.getHoroscopesMalus(), adv: "0", mod: 0, modRanged: 0, @@ -62,7 +67,7 @@ export class BoLRoll { static attributeCheck(actor, key) { let attribute = eval(`actor.system.attributes.${key}`) - + let rollData = this.getCommonRollData(actor, "attribute", attribute) rollData.description = game.i18n.localize('BOL.ui.attributeCheck') + " - " + game.i18n.localize(attribute.label) rollData.label = (attribute.label) ? game.i18n.localize(attribute.label) : null @@ -83,11 +88,11 @@ export class BoLRoll { rollData.label = (aptitude.label) ? game.i18n.localize(aptitude.label) : null rollData.description = game.i18n.localize('BOL.ui.aptitudeCheck') + " - " + game.i18n.localize(aptitude.label) - return this.displayRollDialog( rollData) + return this.displayRollDialog(rollData) } /* -------------------------------------------- */ - static async detectDistance( weapon, target ) { + static async detectDistance(weapon, target) { let visible, dist if (weapon.system.properties.ranged || weapon.system.properties.throwing) { console.log("target", target, weapon) @@ -95,19 +100,19 @@ export class BoLRoll { dist = Number(canvas.grid.measureDistances([{ ray: new Ray(_token.center, target.center) }], { gridSpaces: false })).toFixed(2) let range = Number(weapon.system.properties.range) let rangeMsg = "BOL.chat.rangeout" - if ( dist <= range) { + if (dist <= range) { rangeMsg = "BOL.chat.range0" - } else if (dist < range*2) { + } else if (dist < range * 2) { rangeMsg = "BOL.chat.range1" - } else if (dist < range*3) { + } else if (dist < range * 3) { rangeMsg = "BOL.chat.range2" - } else if (dist < range*4) { + } else if (dist < range * 4) { rangeMsg = "BOL.chat.range3" - } else if (dist < range*5) { + } else if (dist < range * 5) { rangeMsg = "BOL.chat.range4" - } else if (dist < range*6) { + } else if (dist < range * 6) { rangeMsg = "BOL.chat.range5" - } else if (dist < range*7) { + } else if (dist < range * 7) { rangeMsg = "BOL.chat.range6" } ChatMessage.create({ @@ -121,7 +126,7 @@ export class BoLRoll { rangeMsg: rangeMsg }) }) - } + } } /* -------------------------------------------- */ @@ -136,7 +141,7 @@ export class BoLRoll { let rollData = this.getCommonRollData(actor, "weapon", attribute, aptitude) // Compute distance - this.detectDistance( weapon, target) + this.detectDistance(weapon, target) // Manage specific case let fightOption = actor.getActiveFightOption() @@ -144,14 +149,14 @@ export class BoLRoll { ui.notifications.warn(`{{actor.name}} est en Défense Totale ! Il ne peut pas attaquer ce round.`) return } - + // Update the roll structure - rollData.weapon = weapon + rollData.weapon = weapon rollData.isRanged = weaponData.properties.ranged || weaponData.properties.throwing rollData.targetId = target?.id rollData.fightOption = fightOption - rollData.defenderId = target?.actor.id - rollData.label = (weapon.name) ? weapon.name : game.i18n.localize('BOL.ui.noWeaponName') + rollData.defenderId = target?.actor.id + rollData.label = (weapon.name) ? weapon.name : game.i18n.localize('BOL.ui.noWeaponName') rollData.description = game.i18n.localize('BOL.ui.weaponAttack') + " : " + weapon.name return this.displayRollDialog(rollData) @@ -194,23 +199,38 @@ export class BoLRoll { rollData.label = alchemy.name rollData.description = game.i18n.localize('BOL.ui.makeAlchemy') + "+" + alchemy.name - console.log("ALCHEMY!", alchemyDef); - return this.displayRollDialog(alchemyDef); + console.log("ALCHEMY!", rollData); + return this.displayRollDialog(rollData); } /* -------------------------------------------- */ - static spellCheckWithSpell( actor, spell ) { + static horoscopeCheck(actor, event, horoscopeType) { + let rollData = this.getCommonRollData(actor, "horoscope", actor.system.attributes.mind) + + rollData.careerBonus = actor.getAstrologerBonus() + rollData.horoscopeType = horoscopeType + rollData.horoscopeTypeLabel = "BOL.ui."+horoscopeType + rollData.astrologyPointsCost = (horoscopeType == "minor") ? 1 : 2 + rollData.label = game.i18n.localize('BOL.ui.makeHoroscope') + rollData.description = game.i18n.localize('BOL.ui.makeHoroscope') + " " + game.i18n.localize(rollData.horoscopeTypeLabel) + + console.log("HOROSCOPE!", rollData); + return this.displayRollDialog(rollData); + } + + /* -------------------------------------------- */ + static spellCheckWithSpell(actor, spell) { let rollData = this.getCommonRollData(actor, "spell", actor.system.attributes.mind) rollData.spell = spell rollData.ppCurrent = Number(actor.system.resources.power.value), - rollData.careerBonus = actor.getSorcererBonus(), - rollData.ppCostArmor = actor.getPPCostArmor(), - rollData.ppCost = Number(spell.system.properties.ppcost), - rollData.mod = Number(spell.system.properties.difficulty), - rollData.label = spell.name, - rollData.description = game.i18n.localize('BOL.ui.focusSpell') + " : " + spell.name - + rollData.careerBonus = actor.getSorcererBonus(), + rollData.ppCostArmor = actor.getPPCostArmor(), + rollData.ppCost = Number(spell.system.properties.ppcost), + rollData.mod = Number(spell.system.properties.difficulty), + rollData.label = spell.name, + rollData.description = game.i18n.localize('BOL.ui.focusSpell') + " : " + spell.name + //console.log("SPELL!", spellDef) return this.displayRollDialog(rollData) } @@ -228,7 +248,7 @@ export class BoLRoll { return } spell = duplicate(spell) - return this.spellCheckWithSpell( actor, spell) + return this.spellCheckWithSpell(actor, spell) } /* -------------------------------------------- */ @@ -245,15 +265,18 @@ export class BoLRoll { if (effect.system.properties.modifier == "1B") { this.rollData.bmDice++; } else if (effect.system.properties.modifier == "1B") { - this.rollData.bmDice+=2; + this.rollData.bmDice += 2; } else if (effect.system.properties.modifier == "1M") { this.rollData.bmDice--; - } else if (effect.system.properties.modifier == "2M") { - this.rollData.bmDice-=2; + } else if (effect.system.properties.modifier == "2M") { + this.rollData.bmDice -= 2; } else { effectModifier += Number(effect.system.properties.modifier) } } + this.rollData.bmDice += this.rollData.horoscopeBonus + this.rollData.bmDice -= this.rollData.horoscopeMalus + // Keep track of the final effect modifier this.rollData.effectModifier = effectModifier @@ -267,7 +290,7 @@ export class BoLRoll { $('#roll-nbdice').val("2 + " + String(Math.abs(this.rollData.bmDice)) + letter) } let rollbase = this.rollData.attrValue + "+" + this.rollData.aptValue - if ( this.rollData.weapon && this.rollData.weapon.system.properties.onlymodifier ) { + if (this.rollData.weapon && this.rollData.weapon.system.properties.onlymodifier) { rollbase = "" } $('#roll-modifier').val(rollbase + "+" + this.rollData.careerBonus + "+" + this.rollData.mod + "+" + @@ -276,7 +299,7 @@ export class BoLRoll { // Rebuild lits of applicable effects let selectEffects = "" - for(let effect of this.rollData.bolApplicableEffects) { + for (let effect of this.rollData.bolApplicableEffects) { selectEffects += `` } $('#applicable-effects').html(selectEffects) @@ -339,7 +362,7 @@ export class BoLRoll { html.find('#optcond').change((event) => { // Dynamic change of PP cost of spell let pp = BoLUtility.computeSpellCost(this.rollData.spell, event.currentTarget.selectedOptions.length) this.rollData.ppCost = pp - this.updatePPCost( this.rollData) + this.updatePPCost(this.rollData) }) html.find('#mod').change((event) => { @@ -401,6 +424,23 @@ export class BoLRoll { this.rollData.mDice = Number(event.currentTarget.value) this.updateTotalDice() }) + html.find('#horoscope-bonus-applied').change((event) => { + if (event.currentTarget.value != undefined) { + this.rollData.selectedHoroscope.push( duplicate(this.rollData.horoscopeBonusList[Number(event.currentTarget.value)]) ) + } + let horoscopes = $('#horoscope-bonus-applied').val() + this.rollData.horoscopeBonus = (!horoscopes || horoscopes.length == 0) ? 0 : horoscopes.length + this.updateTotalDice() + }) + html.find('#horoscope-malus-applied').change((event) => { + if (event.currentTarget.value != undefined) { + this.rollData.selectedHoroscope.push( duplicate(this.rollData.horoscopeMalusList[Number(event.currentTarget.value)]) ) + } + let horoscopes = $('#horoscope-malus-applied').val() + this.rollData.horoscopeMalus = (!horoscopes || horoscopes.length == 0) ? 0 : horoscopes.length + this.updateTotalDice() + }) + } /* -------------------------------------------- */ @@ -408,7 +448,7 @@ export class BoLRoll { if (rollData.mode == "weapon") { rollData.weaponModifier = rollData.weapon.system.properties.attackModifiers ?? 0 rollData.attackBonusDice = rollData.weapon.system.properties.attackBonusDice - if ( rollData.attackBonusDice) { + if (rollData.attackBonusDice) { rollData.adv = "1B" rollData.bDice = 1 } @@ -435,7 +475,7 @@ export class BoLRoll { let actor = BoLUtility.getActorFromRollData(rollData) let defender - if ( rollData.targetId) { + if (rollData.targetId) { let token = game.scenes.current.tokens.get(rollData.targetId) defender = token.actor } @@ -498,14 +538,14 @@ export class BoLRoll { //console.log("ROLLMALUS", rollData) rollData.registerInit = (rollData.aptitude && rollData.aptitude.key == 'init') ? $('#register-init').is(":checked") : false; - - const isMalus = (rollData.bmDice < 0) + + const isMalus = (rollData.bmDice < 0) //rollData.nbDice += (rollData.attackBonusDice) ? 1 : 0 let rollbase = rollData.attrValue + rollData.aptValue - if ( rollData.weapon && rollData.weapon.system.properties.onlymodifier ) { + if (rollData.weapon && rollData.weapon.system.properties.onlymodifier) { rollbase = 0 - } + } const modifiers = rollbase + rollData.careerBonus + rollData.mod + rollData.weaponModifier - rollData.defence - rollData.modArmorMalus + rollData.shieldMalus + rollData.attackModifier + rollData.appliedArmorMalus + rollData.effectModifier const formula = (isMalus) ? rollData.nbDice + "d6kl2 + " + modifiers : rollData.nbDice + "d6kh2 + " + modifiers rollData.formula = formula @@ -535,10 +575,10 @@ export class BoLDefaultRoll { this.rollData.isFumble = false; } if (this.rollData.optionsId) { - BoLUtility.cleanupButtons( this.rollData.optionsId) + BoLUtility.cleanupButtons(this.rollData.optionsId) } if (this.rollData.applyId) { - BoLUtility.cleanupButtons( this.rollData.applyId) + BoLUtility.cleanupButtons(this.rollData.applyId) } this.rollData.optionsId = randomID(16) this.rollData.applyId = randomID(16) @@ -560,15 +600,15 @@ export class BoLDefaultRoll { this.rollData.isLegendary = false this.rollData.isFumble = (diceTotal === 2) this.rollData.isFailure = !this.rollData.isSuccess - + //this.rollData.isRealCritical = true //this.rollData.isFumble = true - + let actor = BoLUtility.getActorFromRollData(this.rollData) if (this.rollData.reroll == undefined) { this.rollData.reroll = actor.heroReroll() } - + if (this.rollData.registerInit) { actor.registerInit(this.rollData) this.rollData.initiativeRank = actor.getInitiativeRank(this.rollData) @@ -579,9 +619,15 @@ export class BoLDefaultRoll { if (this.rollData.mode == "alchemy") { // PP cost management actor.resetAlchemyStatus(this.rollData.alchemy._id) } - if ( this.rollData.mode == "bougette" && this.rollData.isFailure) { + if (this.rollData.mode == "bougette" && this.rollData.isFailure) { actor.decBougette() } + if (this.rollData.mode == "horoscope") { // PP cost management + actor.manageHoroscope(this.rollData) + } + if (this.rollData.selectedHoroscope.length > 0) { // PP cost management + actor.removeHoroscopeMinor(this.rollData) + } await this.sendChatMessage() } @@ -589,7 +635,7 @@ export class BoLDefaultRoll { /* -------------------------------------------- */ async sendChatMessage() { let actor = BoLUtility.getActorFromRollData(this.rollData) - this._buildChatMessage(this.rollData).then( async msgFlavor => { + this._buildChatMessage(this.rollData).then(async msgFlavor => { let msg = await this.rollData.roll.toMessage({ user: game.user.id, rollMode: game.settings.get("core", "rollMode"), @@ -627,7 +673,7 @@ export class BoLDefaultRoll { this.rollData.reroll = false this.sendChatMessage() } - + /* -------------------------------------------- */ setSuccess(flag) { this.rollData.isSuccess = flag @@ -651,7 +697,7 @@ export class BoLDefaultRoll { /* -------------------------------------------- */ getDamageAttributeValue(attrDamage, actorId = undefined) { let actor = BoLUtility.getActorFromRollData(this.rollData) - return actor.getDamageAttributeValue( attrDamage ) + return actor.getDamageAttributeValue(attrDamage) } /* -------------------------------------------- */ diff --git a/module/system/bol-utility.js b/module/system/bol-utility.js index 7999e81..54e2f51 100644 --- a/module/system/bol-utility.js +++ b/module/system/bol-utility.js @@ -53,6 +53,13 @@ export class BoLUtility { type: String, onChange: lang => window.location.reload() }) + game.settings.register("bol", "horoscope-group", { + name: "horoscope-group", + scope: "world", + config: false, + default: {}, + type: Object + }) this.rollArmor = game.settings.get("bol", "rollArmor") // Roll armor or not this.useBougette = game.settings.get("bol", "useBougette") // Use optionnal bougette rules diff --git a/module/system/config.js b/module/system/config.js index 3b80e6f..a8dd881 100644 --- a/module/system/config.js +++ b/module/system/config.js @@ -270,6 +270,7 @@ BOL.featureSubtypes = { "godsfaith" : "BOL.featureSubtypes.gods", "fightoption" : "BOL.featureSubtypes.fightOption", "boleffect": "BOL.featureSubtypes.effect", + "horoscope": "BOL.featureSubtypes.horoscope", } BOL.fightOptionTypes = { @@ -326,6 +327,11 @@ BOL.creatureSize = { "colossal": "BOL.size.colossal" } +BOL.horoscopeAnswer = { + "favorable": "BOL.ui.horoscopefavorable", + "unfavorable": "BOL.ui.horoscopeunfavorable", +} + BOL.bolEffectModifier = { "-8": "-8", "-6": "-6", diff --git a/module/system/templates.js b/module/system/templates.js index 1cabb6d..bed8b82 100644 --- a/module/system/templates.js +++ b/module/system/templates.js @@ -41,11 +41,14 @@ export const preloadHandlebarsTemplates = async function () { "systems/bol/templates/item/parts/properties/feature/race-properties.hbs", "systems/bol/templates/item/parts/properties/feature/fightoption-properties.hbs", "systems/bol/templates/item/parts/properties/item/weapon-vehicle-properties.hbs", + "systems/bol/templates/item/parts/properties/feature/horoscope-properties.hbs", // DIALOGS "systems/bol/templates/chat/rolls/attack-damage-card.hbs", "systems/bol/templates/chat/rolls/spell-roll-card.hbs", "systems/bol/templates/chat/rolls/alchemy-roll-card.hbs", + "systems/bol/templates/chat/rolls/selected-horoscope-roll-card.hbs", + "systems/bol/templates/chat/rolls/horoscope-roll-card.hbs", "systems/bol/templates/dialogs/aptitude-roll-part.hbs", "systems/bol/templates/dialogs/attribute-roll-part.hbs", "systems/bol/templates/dialogs/mod-roll-part.hbs", @@ -56,6 +59,7 @@ export const preloadHandlebarsTemplates = async function () { "systems/bol/templates/dialogs/flaws-roll-part.hbs", "systems/bol/templates/dialogs/total-roll-part.hbs", "systems/bol/templates/dialogs/fightoptions-roll-part.hbs", + "systems/bol/templates/dialogs/horoscope-roll-part.hbs" ]; // Load the template parts diff --git a/packs/careers.db b/packs/careers.db index 179c046..4801e98 100644 --- a/packs/careers.db +++ b/packs/careers.db @@ -24,4 +24,4 @@ {"name":"Assassin (agent/espion)","type":"feature","img":"/systems/bol/ui/icons/career.webp","data":{"subtype":"career","description":"
Tueurs à gages, quand ils ne sont pas des agents au service d’un souverain, les espions et les assassins ont élevé le meurtre et le vol au rang d’art. Ce sont des experts de la collecte secrète d’informations et de rumeurs, de l’attaque sournoise et du meurtre discret, parfois maquillé en mort naturelle (par l’usage de poison, par exemple). Ils utilisent des méthodes éprouvées pour réunir des informations sur un sujet ou un personnage, se bâtir un solide réseau de contacts (dont tous ne sont pas forcément très recommandables), et se rapprocher de leur cible (qu’il s’agisse de crocheter une serrure, de mentir ou de se déguiser). Ils savent se montrer patients et peuvent parfois rester en planque des jours durant, à attendre l’occasion parfaite de frapper. Il est de notoriété publique que les meilleurs assassins de Lémurie proviennent de la cité d’Halakh.
tous les attributs sont importants pour un assassin.
les assassins et les espions sont généralement des solitaires, ce qui n’en fait pas des candidats évidents pour rejoindre un groupe d’aventuriers. En revanche, d’anciens assassins ont des compétences très utiles à offrir à un groupe de héros.
un assassin peut obtenir un avantage ponctuel s’il attaque par surprise, en frappant un adversaire qui ne l’a pas vu approcher, ou en utilisant une arme dissimulée, par exemple.
amis dans la pègre, amis haut placés, arme favorite, beau parleur, discret, maître du déguisement, ouïe fine, résistant aux poisons, vigilant, vue perçante.
arrogant, gars de la ville, mauvaise réputation, obsession (achever la mission), traqué.
","properties":{}},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"EEnCVoPAR7pMjRym":3},"flags":{},"_id":"qVZ9uLwU5yL0bQ4J"} {"name":"Danseur (acrobate/saltimbanque)","type":"feature","img":"/systems/bol/ui/icons/career.webp","data":{"subtype":"career","description":"La danse est un divertissement apprécié en Lémurie. Toute cérémonie ou fête digne de ce nom se doit de comporter des danseurs ou des acrobates. Les danseurs sont de véritables athlètes, capables de prouesses en termes de précision, d’agilité et de coordination. Les saltimbanques maîtrisent aussi quelques tours de prestidigitation ou de jonglage, tandis que certaines danseuses pratiquent des danses exotiques et utilisent des voiles qui soulignent plus qu’ils ne masquent vraiment leurs attraits.
les danseurs font principalement appel à leur agilité et leur aura. Les acrobates et les saltimbanques sont souvent plus costauds qu’ils n’y paraissent.
les saltimbanques voyagent de ville en ville au sein de troupes itinérantes. Cela peut les confronter aux dangers de la route, qu’il s’agisse de bêtes féroces ou de brigands.
cette carrière n’a rien d’une carrière martiale et elle ne sera que rarement utile face à un combattant expérimenté. Dans certaines circonstances, une danseuse pourra obtenir un avantage ponctuel si son adversaire est déconcentré par ses charmes, ou en accomplissant une acrobatie, comme une roulade, pour surprendre son ennemi. Un acrobate pourra cependant tirer parti des lianes, des cordes ou des tentures murales pour effectuer de spectaculaires actions comme on peut en voir dans les films de cape et d’épée.
athlète, attirant, discret, doigts de fée, récupération rapide, roi de l’évasion, vigilant.
chétif, gars de la ville, non-combattant.
","properties":{}},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"EEnCVoPAR7pMjRym":3},"flags":{},"_id":"tFGoyoiteTlm6u1Q"} {"name":"Médecin (guérisseur/rebouteux)","type":"feature","img":"/systems/bol/ui/icons/career.webp","data":{"subtype":"career","description":"Les médecins et tous ceux capables de soigner les personnes blessées ou malades jouissent d’un grand respect au sein des villes de Lémurie. La population sait reconnaître l’importance de leur mission et l’étendue de leur savoir, même si les petites gens n’ont pas forcément les moyens de s’offrir les services d’un vrai médecin et s’en remettent plutôt à des charlatans.
Les médecins prescrivent des potions et des onguents, savent réduire les fractures, recoudre les plaies et aident à mettre les enfants au monde. Ils connaissent les maladies et leurs remèdes, savent administrer les premiers soins, et les plantes médicinales n’ont plus de secret pour eux. Souvent, ils possèdent un petit jardin où ils cultivent des herbes aux vertus curatives puissantes. Certains médecins ont des connaissances de base en alchimie (cf. chapitre 6), et tous se doivent bien sûr de savoir lire et écrire.
l’esprit est l’attribut essentiel pour un médecin.
la carrière de médecin n’ est pas en elle-même propice à une vie d’aventure, mais il arrive que des médecins soient recrutés pour accompagner une expédition maritime ou une armée en marche.
au combat, la carrière de médecin n’ est pas d’un grand secours. Ce n’ est qu’après le combat qu’elle révèle tout son intérêt !
bibliothèque savante, doigts de fée, érudit, mains guérisseuses, résistant aux poisons, santé de fer.
foie jaune, gars de la ville, incapable de mentir, non-combattant, soiffard.
","properties":{}},"effects":[],"folder":null,"sort":0,"permission":{"default":0,"EEnCVoPAR7pMjRym":3},"flags":{},"_id":"wUUDR1XCrwdFCzIe"} -{"name":"Astrologue","type":"feature","img":"/systems/bol/ui/icons/career.webp","data":{"category":null,"subtype":"career","description":"Lorsque les dieux veulent communiquer avec les hommes, ils utilisent le plus souvent les rêves ou les oracles. Mais leurs messages sont généralement abscons, énigmatiques, mystérieux. Pour les déchiffrer, il est nécessaire de faire appel aux talents d’un voyant, d’un devin ou de quelque autre interprète des volontés célestes. Encore faut-il que les dieux aient le désir de révéler leurs secrets aux simples mortels. La plupart du temps, ils préfèrent s’amuser à fourvoyer les malheureux plutôt que de tenter de les sauver de leur destin tragique. En effet, les prêtres affirment que l’avenir est écrit et que les dieux connaissent le début et la fin de toute chose.
\nL’astrologie naquit lorsque les hommes, anxieux de connaître leur sort sans devoir attendre la bonne volonté des dieux, levèrent les yeux vers leur domaine et scrutèrent les signes qui s’y manifestaient. Car Hurm arbitre le destin de tout être et de toute chose, et Sa’Tel a consigné les décrets du roi des dieux sur la voûte céleste. Ils découvrirent ainsi que le destin pouvait se laisser augurer dans les étoiles du firmament, à condition de savoir interpréter les conjonctions et les transits des astres, et de connaître la signification de chaque corps et phénomène célestes. Mais il n’est pas donné à tout le monde de pouvoir déchiffrer les Glyphes sacrés que constituent les constellations.
\nLes sages capables de lire l’avenir dans les étoiles sont désignés sous le nom d’astrologues. Leur art consiste à établir des cartes du ciel et à en donner une interprétation afin de révéler le destin d’un individu et le sort que lui réservent les dieux, par exemple à l’occasion d’un voyage, d’un choix à faire, d’une tâche à entreprendre. Qu’ils soient princes, généraux, marchands, aventuriers ou simples paysans, tous consultent des astrologues, car même le héros le plus brave peut hésiter devant une quête en apparence impossible, à moins d’avoir l’assurance que les dieux lui souriront.
\nétudier les conjonctions et analyser le mouvement des astres requièrent une solide érudition et une intelligence pénétrante. Tout personnage voulant se lancer dans cette carrière aura donc intérêt à privilégier l’esprit. Mais pour l’astrologue qui serait moins doué dans l’établissement d’un thème astral précis, une bonne aura permettra d’énoncer des prédictions avec conviction, même si lui-même n’est pas vraiment certain de leur fiabilité...
\nquitter son observatoire pour parcourir des centaines de lieues, affronter des dangers, risquer de se faire capturer et réduire en esclavage ? Parfois la connaissance est à ce prix. Même si un astrologue a plutôt tendance à vouloir rester en ville pour vendre ses horoscopes et examiner le ciel, il est des événements qui ne peuvent s’observer qu’à un endroit et à un moment précis. De plus, un groupe ne refusera probablement pas la présence d’un astrologue suffisamment doué pour déterminer si les conjonctures sont favorables avant de s’engager dans quelque périlleuse expédition.
\npour sauver sa vie, un astrologue en situation critique fera comme la plupart des gens sensés : il cherchera à se cacher ou à fuir. Et surtout, jamais il ne se risquera à provoquer une réaction qui pourrait entraîner un affrontement physique. À moins, peut-être, que son horoscope ne lui prédise une issue favorable !
\namis haut placés, beau parleur, bibliothèque savante, érudit, laboratoire fourni, savant.
\nchétif, distrait, foie jaune, gars de la ville, non-combattant, obsession, souffreteux.
\n","properties":{"sorcerer":false,"alchemist":false,"priest":false},"rank":0},"effects":[],"folder":null,"sort":0,"permission":{"ft3qeazHwKO7jjpx":3},"flags":{"core":{"sourceId":"Compendium.bol.careers.V8FaY7ampRH8qwK6"}},"_id":"zxY3sW0iCJBvwjOS"} +{"name":"Astrologue","type":"feature","img":"/systems/bol/ui/icons/career.webp","effects":[],"folder":null,"sort":0,"flags":{"core":{"sourceId":"Compendium.bol.careers.V8FaY7ampRH8qwK6"}},"_id":"zxY3sW0iCJBvwjOS","system":{"category":null,"subtype":"career","description":"
Lorsque les dieux veulent communiquer avec les hommes, ils utilisent le plus souvent les rêves ou les oracles. Mais leurs messages sont généralement abscons, énigmatiques, mystérieux. Pour les déchiffrer, il est nécessaire de faire appel aux talents d’un voyant, d’un devin ou de quelque autre interprète des volontés célestes. Encore faut-il que les dieux aient le désir de révéler leurs secrets aux simples mortels. La plupart du temps, ils préfèrent s’amuser à fourvoyer les malheureux plutôt que de tenter de les sauver de leur destin tragique. En effet, les prêtres affirment que l’avenir est écrit et que les dieux connaissent le début et la fin de toute chose.
\nL’astrologie naquit lorsque les hommes, anxieux de connaître leur sort sans devoir attendre la bonne volonté des dieux, levèrent les yeux vers leur domaine et scrutèrent les signes qui s’y manifestaient. Car Hurm arbitre le destin de tout être et de toute chose, et Sa’Tel a consigné les décrets du roi des dieux sur la voûte céleste. Ils découvrirent ainsi que le destin pouvait se laisser augurer dans les étoiles du firmament, à condition de savoir interpréter les conjonctions et les transits des astres, et de connaître la signification de chaque corps et phénomène célestes. Mais il n’est pas donné à tout le monde de pouvoir déchiffrer les Glyphes sacrés que constituent les constellations.
\nLes sages capables de lire l’avenir dans les étoiles sont désignés sous le nom d’astrologues. Leur art consiste à établir des cartes du ciel et à en donner une interprétation afin de révéler le destin d’un individu et le sort que lui réservent les dieux, par exemple à l’occasion d’un voyage, d’un choix à faire, d’une tâche à entreprendre. Qu’ils soient princes, généraux, marchands, aventuriers ou simples paysans, tous consultent des astrologues, car même le héros le plus brave peut hésiter devant une quête en apparence impossible, à moins d’avoir l’assurance que les dieux lui souriront.
\nétudier les conjonctions et analyser le mouvement des astres requièrent une solide érudition et une intelligence pénétrante. Tout personnage voulant se lancer dans cette carrière aura donc intérêt à privilégier l’esprit. Mais pour l’astrologue qui serait moins doué dans l’établissement d’un thème astral précis, une bonne aura permettra d’énoncer des prédictions avec conviction, même si lui-même n’est pas vraiment certain de leur fiabilité...
\nquitter son observatoire pour parcourir des centaines de lieues, affronter des dangers, risquer de se faire capturer et réduire en esclavage ? Parfois la connaissance est à ce prix. Même si un astrologue a plutôt tendance à vouloir rester en ville pour vendre ses horoscopes et examiner le ciel, il est des événements qui ne peuvent s’observer qu’à un endroit et à un moment précis. De plus, un groupe ne refusera probablement pas la présence d’un astrologue suffisamment doué pour déterminer si les conjonctures sont favorables avant de s’engager dans quelque périlleuse expédition.
\npour sauver sa vie, un astrologue en situation critique fera comme la plupart des gens sensés : il cherchera à se cacher ou à fuir. Et surtout, jamais il ne se risquera à provoquer une réaction qui pourrait entraîner un affrontement physique. À moins, peut-être, que son horoscope ne lui prédise une issue favorable !
\namis haut placés, beau parleur, bibliothèque savante, érudit, laboratoire fourni, savant.
\nchétif, distrait, foie jaune, gars de la ville, non-combattant, obsession, souffreteux.
\n","properties":{"sorcerer":false,"alchemist":false,"priest":false,"astrologer":true},"rank":0},"ownership":{"ft3qeazHwKO7jjpx":3},"_stats":{"systemId":"bol","systemVersion":"10.4.13","coreVersion":"10.291","createdTime":null,"modifiedTime":1671811997821,"lastModifiedBy":"kQghu0tL1dft5xLu"}} diff --git a/system.json b/system.json index 79e1b59..72cc202 100644 --- a/system.json +++ b/system.json @@ -14,7 +14,7 @@ ], "url": "https://www.uberwald.me/gitea/public/bol", "license": "LICENSE.txt", - "version": "10.4.13", + "version": "10.4.15", "compatibility": { "minimum": "10", "verified": "10", @@ -203,7 +203,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.4.13.zip", + "download": "https://www.uberwald.me/gitea/public/bol/archive/bol-v10.4.15.zip", "background": "systems/bol/ui/page_accueil.webp", "gridDistance": 1.5, "gridUnits": "m", diff --git a/template.json b/template.json index ffb0c49..a7e5749 100644 --- a/template.json +++ b/template.json @@ -102,6 +102,7 @@ "hp": { "key": "hp", "label": "BOL.resources.hp", + "ismain": true, "base": 1, "value": 1, "bonus": 0, @@ -110,18 +111,21 @@ "hero": { "key": "hero", "label": "BOL.resources.hero", + "ismain": true, "value": 5, "max": 5 }, "faith": { "key": "faith", "label": "BOL.resources.faith", + "ismain": true, "value": 0, "max": 0 }, "power": { "key": "power", "label": "BOL.resources.power", + "ismain": true, "value": 0, "bonus": 0, "max": 0 @@ -129,6 +133,15 @@ "alchemypoints": { "key": "alchemypoints", "label": "BOL.resources.alchemypoints", + "ismain": false, + "value": 0, + "bonus": 0, + "max": 0 + }, + "astrologypoints": { + "key": "astrologypoints", + "label": "BOL.resources.astrologypoints", + "ismain": false, "value": 0, "bonus": 0, "max": 0 diff --git a/templates/actor/actor-sheet.hbs b/templates/actor/actor-sheet.hbs index ee629fc..5f2483d 100644 --- a/templates/actor/actor-sheet.hbs +++ b/templates/actor/actor-sheet.hbs @@ -37,7 +37,7 @@ {{localize "BOL.ui.tab.actions"}} {{localize "BOL.ui.tab.features"}} {{localize "BOL.ui.tab.equipment"}} - {{#if (or isSorcerer isAlchemist)}} + {{#if isMysteries}} {{localize "BOL.ui.tab.spellalchemy"}} {{/if}} {{/if}} @@ -66,7 +66,7 @@ {{> "systems/bol/templates/actor/parts/tabs/actor-features.hbs"}} - {{#if (or isSorcerer isAlchemist)}} + {{#if isMysteries}}