Files
fvtt-mournblade-cyd-2-0/modules/mournblade-cyd2-actor.js

920 lines
34 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* -------------------------------------------- */
import { MournbladeCYD2Utility } from "./mournblade-cyd2-utility.js";
import { MournbladeCYD2RollDialog } from "./applications/mournblade-cyd2-roll-dialog.mjs";
/* -------------------------------------------- */
const __degatsBonus = [-2, -2, -1, -1, 0, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 8, 8, 9, 9, 10, 10]
const __vitesseBonus = [-2, -2, -1, -1, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8]
/* -------------------------------------------- */
/**
* Extend the base Actor entity by defining a custom roll data structure which is ideal for the Simple system.
* @extends {Actor}
*/
export class MournbladeCYD2Actor extends Actor {
/* -------------------------------------------- */
/**
* Override the create() function to provide additional SoS functionality.
*
* This overrided create() function adds initial items
* Namely: Basic skills, money,
*
* @param {Object} data Barebones actor data which this function adds onto.
* @param {Object} options (Unused) Additional options which customize the creation workflow.
*
*/
static async create(data, options) {
// Case of compendium global import
if (data instanceof Array) {
return super.create(data, options);
}
// If the created actor has items (only applicable to duplicated actors) bypass the new actor creation logic
if (data.items) {
let actor = super.create(data, options);
return actor;
}
if (data.type == 'personnage') {
console.log("Loading skills for personnage")
const skills = await MournbladeCYD2Utility.loadCompendium("fvtt-mournblade-cyd2.skills")
data.items = skills.map(i => i.toObject())
}
if (data.type == 'creature') {
const skills = await MournbladeCYD2Utility.loadCompendium("fvtt-mournblade-cyd2.skills-creatures")
data.items = skills.map(i => i.toObject())
data.items.push({ name: "Arme naturelle 1", type: 'arme', img: "systems/fvtt-mournblade-cyd2/assets/icons/melee.webp", system: { typearme: "contact", bonusmaniementoff: 0, seuildefense: 0, degats: "0" } })
data.items.push({ name: "Arme naturelle 2", type: 'arme', img: "systems/fvtt-mournblade-cyd2/assets/icons/melee.webp", system: { typearme: "contact", bonusmaniementoff: 0, seuildefense: 0, degats: "0" } })
}
return super.create(data, options);
}
/* -------------------------------------------- */
getBonusDefenseFromTalents() {
let talents = this.items.filter(item => item.type == "talent" && item.system.isautomated)
let bonus = 0
for (let talent of talents) {
for (let auto of talent.system.automations) {
if (auto.eventtype == "bonus-permanent" && auto.bonusname == "bonus-defensif") {
bonus += Number(auto.bonus || 0)
}
}
}
return bonus
}
/* -------------------------------------------- */
prepareArme(arme) {
arme = foundry.utils.duplicate(arme)
let combat = this.getCombatValues()
if (arme.system.typearme == "contact" || arme.system.typearme == "contactjet") {
let bonusDefense = this.getBonusDefenseFromTalents()
arme.system.competence = foundry.utils.duplicate(this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "mêlée"))
arme.system.attrKey = "pui"
arme.system.totalDegats = arme.system.degats + "+" + combat.bonusDegatsTotal
arme.system.totalOffensif = this.system.attributs.pui.value + arme.system.competence.system.niveau + arme.system.bonusmaniementoff + (this.system.combat.monte ? 3 : 0)
arme.system.totalDefensif = combat.defenseTotal + arme.system.competence.system.niveau + arme.system.seuildefense + bonusDefense + (this.system.combat.monte ? 3 : 0)
console.log("Arme", arme.system.totalDefensif, combat, arme.system.competence.system.niveau, arme.system.seuildefense, bonusDefense)
arme.system.isdefense = true
arme.system.isMelee = true
arme.system.isDistance = false
}
if (arme.system.typearme == "jet" || arme.system.typearme == "tir") {
arme.system.competence = foundry.utils.duplicate(this.items.find(item => item.type == "competence" && item.name.toLowerCase() == "armes à distance"))
arme.system.attrKey = "adr"
arme.system.totalOffensif = this.system.attributs.adr.value + arme.system.competence.system.niveau + arme.system.bonusmaniementoff
arme.system.totalDegats = arme.system.degats
arme.system.isMelee = false
arme.system.isDistance = true
if (arme.system.isdefense) {
arme.system.totalDefensif = combat.defenseTotal + arme.system.competence.system.niveau + arme.system.seuildefense
}
}
return arme
}
/* -------------------------------------------- */
getItemSorted(types) {
let items = this.items.filter(item => types.includes(item.type)) || []
MournbladeCYD2Utility.sortArrayObjectsByName(items)
return items
}
getWeapons() {
let armes = []
for (let arme of this.items) {
if (arme.type == "arme") {
armes.push(this.prepareArme(arme))
}
}
MournbladeCYD2Utility.sortArrayObjectsByName(armes)
return armes
}
getMonnaies() {
return this.getItemSorted(["monnaie"])
}
getEquipments() {
return this.getItemSorted(["equipement"])
}
getArmors() {
return this.getItemSorted(["protection"])
}
getHistoriques() {
return this.getItemSorted(["historique"])
}
getProfils() {
return this.getItemSorted(["profil"])
}
getTalents() {
return this.getItemSorted(["talent"])
}
getRessources() {
return this.getItemSorted(["ressource"])
}
getDons() {
return this.getItemSorted(["don"])
}
getPactes() {
return this.getItemSorted(["pacte"])
}
getTendances() {
return this.getItemSorted(["tendance"])
}
getRunes() {
return this.getItemSorted(["rune"])
}
getRuneEffects() {
return this.getItemSorted(["runeeffect"])
}
getProfil() {
return this.getProfils()[0] ?? null
}
getTraitsChaotiques() {
return this.getItemSorted(["traitchaotique"])
}
getTraitsEspeces() {
return this.getItemSorted(["traitespece"])
}
/* -------------------------------------------- */
getAspect() {
return (this.system.balance.loi > this.system.balance.chaos) ? this.system.balance.loi : this.system.balance.chaos
}
getMarge() {
return Math.abs(this.system.balance.loi - this.system.balance.chaos)
}
getAlignement() {
return (this.system.balance.loi > this.system.balance.chaos) ? "loyal" : "chaotique"
}
/* -------------------------------------------- */
getSkills() {
let comp = []
for (let item of this.items) {
item = foundry.utils.duplicate(item)
if (item.type == "competence") {
item.system.attribut1total = item.system.niveau + (this.system.attributs[item.system.attribut1]?.value || 0)
item.system.attribut2total = item.system.niveau + (this.system.attributs[item.system.attribut2]?.value || 0)
item.system.attribut3total = item.system.niveau + (this.system.attributs[item.system.attribut3]?.value || 0)
if (item.system.niveau == 0) {
item.system.attribut1total -= 3
item.system.attribut2total -= 3
item.system.attribut3total -= 3
}
item.system.attribut1label = this.system.attributs[item.system.attribut1]?.label || ""
item.system.attribut2label = this.system.attributs[item.system.attribut2]?.label || ""
item.system.attribut3label = this.system.attributs[item.system.attribut3]?.label || ""
comp.push(item)
}
}
MournbladeCYD2Utility.sortArrayObjectsByName(comp)
return comp
}
/* ----------------------- --------------------- */
addMember(actorId) {
let members = foundry.utils.duplicate(this.system.members)
members.push({ id: actorId })
this.update({ 'system.members': members })
}
async removeMember(actorId) {
let members = this.system.members.filter(it => it.id != actorId)
this.update({ 'system.members': members })
}
/* -------------------------------------------- */
getDefenseBase() {
return Math.max(this.system.attributs.tre.value, this.system.attributs.adr.value)
}
/* -------------------------------------------- */
getVitesseBase() {
return 5 + __vitesseBonus[this.system.attributs.adr.value]
}
/* -------------------------------------------- */
getProtection() {
let equipProtection = 0
for (let armor of this.items) {
if (armor.type == "protection" && armor.system.equipped) {
equipProtection += Number(armor.system.protection)
}
}
if (equipProtection < 4) {
return 4 + equipProtection // Cas des boucliers + sans armure
}
return equipProtection // Uniquement la protection des armures + boucliers
}
getProtectionTotal() {
return this.getProtection()
}
/* -------------------------------------------- */
getCombatValues() {
let combat = {
initBase: this.system.attributs.adr.value,
initTotal: this.system.attributs.adr.value + this.system.combat.initbonus,
bonusDegats: this.getBonusDegats(),
bonusDegatsTotal: this.getBonusDegats() + this.system.combat.bonusdegats,
vitesseBase: this.getVitesseBase(),
vitesseTotal: this.getVitesseBase() + this.system.combat.vitessebonus,
defenseBase: this.getDefenseBase(),
protection: this.getProtection(),
defenseTotal: this.getDefenseBase() + this.system.combat.defensebonus + this.getProtection() - this.getTotalAdversite() + (this.system.combat.defensetotale ? 3 : 0)
}
return combat
}
/* -------------------------------------------- */
prepareBaseData() {
}
/* -------------------------------------------- */
async prepareData() {
super.prepareData();
}
/* -------------------------------------------- */
prepareDerivedData() {
if (this.type == 'personnage') {
let talentBonus = this.getVigueurBonus()
let vigueur = Math.floor((this.system.attributs.pui.value + this.system.attributs.tre.value) / 2) + talentBonus + this.system.sante.vigueurmodifier
if (vigueur != this.system.sante.vigueur) {
this.update({ 'system.sante.vigueur': vigueur })
}
let seuilPouvoirBonus = this.getSeuilPouvoirBonus()
let seuilPouvoir = Math.floor((this.system.attributs.tre.value + this.system.attributs.cla.value) / 2) + seuilPouvoirBonus + this.system.ame.seuilpouvoirmodifier
if (seuilPouvoir != this.system.ame.seuilpouvoir) {
this.update({ 'system.ame.seuilpouvoir': seuilPouvoir })
}
}
super.prepareDerivedData()
}
/* -------------------------------------------- */
_preUpdate(changed, options, user) {
if (changed?.system?.sante?.etat !== undefined && changed.system.sante.etat != this.system.sante.etat) {
const oldEtat = this.system.sante.etat
setTimeout(() => {
this.processCombativite(changed.system.sante, oldEtat)
}, 800)
}
if (changed?.system?.ame?.etat !== undefined && changed.system.ame.etat != this.system.ame.etat) {
// L'état d'Âme ne peut pas être inférieur au minimum (max dans le système)
let minAme = this.system.ame.max !== undefined ? this.system.ame.max : 0
if (changed.system.ame.etat < minAme) {
changed.system.ame.etat = minAme
}
// L'état d'Âme ne peut pas dépasser nbame (Brisé)
if (changed.system.ame.etat > this.system.ame.nbame) {
changed.system.ame.etat = this.system.ame.nbame
}
const oldEtat = this.system.ame.etat
setTimeout(() => {
this.processAme(changed.system.ame, oldEtat)
}, 800)
}
// Si le max d'Âme change, ajuster l'état actuel si nécessaire
if (changed?.system?.ame?.max !== undefined && changed.system.ame.max != this.system.ame.max) {
if (this.system.ame.etat < changed.system.ame.max) {
changed.system.ame.etat = changed.system.ame.max
}
}
super._preUpdate(changed, options, user);
}
/* -------------------------------------------- */
_onUpdate(data, options, user) {
super._onUpdate(data, options, user);
}
/* -------------------------------------------- */
getItemById(id) {
let item = this.items.find(item => item.id == id);
if (item) {
item = foundry.utils.duplicate(item)
}
return item;
}
/* -------------------------------------------- */
async equipItem(itemId) {
let item = this.items.find(item => item.id == itemId)
if (item && item.system) {
let update = { _id: item.id, "system.equipped": !item.system.equipped }
await this.updateEmbeddedDocuments('Item', [update]); // Updates one EmbeddedEntity
}
}
/* -------------------------------------------- */
editItemField(itemId, itemType, itemField, dataType, value) {
let item = this.items.find(item => item.id == itemId)
if (item) {
console.log("Item ", item, itemField, dataType, value)
if (dataType.toLowerCase() == "number") {
value = Number(value)
} else {
value = String(value)
}
let update = { _id: item.id, [`system.${itemField}`]: value };
this.updateEmbeddedDocuments("Item", [update])
}
}
/* -------------------------------------------- */
checkAttribut(attribut, minLevel) {
let attr = this.system.attributs.find(at => at.labelnorm == attribut.toLowerCase())
if (attr && attr.value >= minLevel) {
return { isValid: true, attr: foundry.utils.duplicate(attr) }
}
return { isValid: false }
}
/* -------------------------------------------- */
checkAttributOrCompetenceLevel(compName, minLevel) {
let comp = this.items.find(i => i.type == "competence" && i.name.toLowerCase() == compName.toLowerCase() && i.system.niveau >= minLevel)
if (comp) {
return { isValid: true, item: foundry.utils.duplicate(comp) }
} else {
for (let attrKey in this.system.attributs) {
if (this.system.attributs[attrKey].label.toLowerCase() == compName.toLowerCase() && this.system.attributs[attrKey].value >= minLevel) {
return { isValid: true, item: foundry.utils.duplicate(this.system.attributs[attrKey]) }
}
}
}
return { isValid: false, warningMessage: `Prérequis insuffisant : la compétence/attribut ${compName} doit être de niveau ${minLevel} au minimum` }
}
/* -------------------------------------------- */
addCompetenceBonus(compName, bonus, baCost) {
let comp = this.items.find(i => i.type == "competence" && i.name.toLowerCase() == compName.toLowerCase())
if (comp) {
comp = foundry.utils.duplicate(comp)
comp.system.bonus = bonus
comp.system.baCost = baCost
return { isValid: true, item: comp }
}
return { isValid: false, warningMessage: `Compétence ${compName} non trouvée` }
}
/* -------------------------------------------- */
checkIfCompetence(compName) {
let comp = this.items.find(i => i.type == "competence" && i.name.toLowerCase() == compName.toLowerCase())
if (comp) {
return { isValid: true, item: comp }
}
return { isValid: false }
}
/* -------------------------------------------- */
getVigueur() {
return this.system.sante.vigueur
}
/* -------------------------------------------- */
getVigueurBonus() {
let talents = this.items.filter(item => item.type == "talent" && item.system.isautomated)
let bonus = 0
for (let talent of talents) {
for (let auto of talent.system.automations) {
if (auto.eventtype == "bonus-permanent" && auto.bonusname == "vigueur") {
bonus += Number(auto.bonus || 0)
}
}
}
return bonus
}
/* -------------------------------------------- */
getSeuilPouvoir() {
return this.system.ame.seuilpouvoir
}
/* -------------------------------------------- */
getSeuilPouvoirBonus() {
let talents = this.items.filter(item => item.type == "talent" && item.system.isautomated)
let bonus = 0
for (let talent of talents) {
for (let auto of talent.system.automations) {
if (auto.eventtype == "bonus-permanent" && auto.bonusname == "seuilpouvoir") {
bonus += Number(auto.bonus || 0)
}
}
}
return bonus
}
/* -------------------------------------------- */
getAmeMax() {
// Utiliser une vérification stricte car 0 (Serein) est une valeur valide
return this.system.ame.max !== undefined ? this.system.ame.max : this.system.ame.nbame
}
/* -------------------------------------------- */
getBonneAventure() {
return this.system.bonneaventure.actuelle
}
/* -------------------------------------------- */
checkBonneAventure(cost) {
return (this.system.bonneaventure.actuelle >= cost)
}
/* -------------------------------------------- */
changeBonneAventure(value) {
let newBA = this.system.bonneaventure.actuelle
newBA += value
this.update({ 'system.bonneaventure.actuelle': newBA })
}
/* -------------------------------------------- */
getEclat() {
return this.system.eclat.value
}
/* -------------------------------------------- */
changeEclat(value) {
let newE = this.system.eclat.value
newE += value
this.update({ 'system.eclat.value': newE })
}
/* -------------------------------------------- */
compareName(a, b) {
if (a.name < b.name) {
return -1;
}
if (a.name > b.name) {
return 1;
}
return 0;
}
/* -------------------------------------------- */
getAttribute(attrKey) {
return this.system.attributes[attrKey]
}
/* -------------------------------------------- */
getBonusDegats() {
return 0;
}
/* -------------------------------------------- */
changeEtatCombativite(value) {
if (value === "vaincu") {
value = 200
}
let sante = foundry.utils.duplicate(this.system.sante)
sante.etat += Number(value)
sante.etat = Math.max(sante.etat, 0)
sante.etat = Math.min(sante.etat, this.system.sante.nbcombativite)
this.update({ 'system.sante': sante })
if (sante.etat == this.system.sante.nbcombativite) {
ChatMessage.create({ content: `<strong>${this.name} est vaincu !</strong>` })
}
// Duplicated ! this.processCombativite(sante)
}
/* -------------------------------------------- */
changeEtatAme(value) {
if (value === "brise") {
value = 200
}
let ame = foundry.utils.duplicate(this.system.ame)
ame.etat += Number(value)
// L'état ne peut pas être inférieur au minimum (max dans le système)
let minAme = this.system.ame.max !== undefined ? this.system.ame.max : 0
ame.etat = Math.max(ame.etat, minAme)
// L'état ne peut pas dépasser nbame (Brisé)
ame.etat = Math.min(ame.etat, this.system.ame.nbame)
this.update({ 'system.ame': ame })
if (ame.etat >= this.system.ame.nbame) {
ChatMessage.create({ content: `<strong>${this.name} est brisé !</strong>` })
}
}
/* -------------------------------------------- */
processCombativite(sante, oldEtat = undefined) {
sante = sante || foundry.utils.duplicate(this.system.sante)
const affaibli = this.system.sante.nbcombativite - 2
const tresAffaibli = this.system.sante.nbcombativite - 1
// oldEtat permet de détecter les sauts qui franchissent Affaibli ou Très Affaibli
// sans y atterrir exactement (ex: 0 → 5 doit déclencher les deux seuils)
const prev = oldEtat !== undefined ? oldEtat : sante.etat
const curr = sante.etat
const passedAffaibli = curr >= affaibli && prev < affaibli
const passedTresAffaibli = curr >= tresAffaibli && prev < tresAffaibli
if (passedAffaibli) {
if (this.items.find(item => item.type == "talent" && item.name.toLowerCase() == "encaissement")) {
ChatMessage.create({ content: `<strong>${this.name} ne subit pas les 2 adversités rouge (Affaibli) grâce à Encaissement. Pensez à les ajouter à la fin de la scène !</strong>` })
} else {
ChatMessage.create({ content: `<strong>${this.name} est Affaibli et subit 2 adversités rouge !</strong>` })
this.incDecAdversite("rouge", 2)
}
}
if (passedTresAffaibli) {
if (this.items.find(item => item.type == "talent" && item.name.toLowerCase().includes("vaillant"))) {
ChatMessage.create({ content: `<strong>${this.name} ne subit pas les 2 adversités rouge (Très Affaibli) grâce à Vaillant. Pensez à les ajouter à la fin de la scène !</strong>` })
} else {
ChatMessage.create({ content: `<strong>${this.name} est Très Affaibli et subit 2 adversités rouge supplémentaires !</strong>` })
this.incDecAdversite("rouge", 2)
}
}
}
/* -------------------------------------------- */
processAme(ame, oldEtat = undefined) {
ame = ame || foundry.utils.duplicate(this.system.ame)
const traumatiseValue = this.system.ame.nbame - 2
const tresTraumatiseValue = this.system.ame.nbame - 1
const briseValue = this.system.ame.nbame
const prev = oldEtat !== undefined ? oldEtat : ame.etat
const curr = ame.etat
// Déclencher pour chaque seuil franchi ou atteint, même en cas de saut
if (curr >= traumatiseValue && prev < traumatiseValue) {
ChatMessage.create({ content: `<strong>${this.name} est Traumatisé et subit 1 adversité bleue et 1 adversité noire !</strong>` })
this.incDecAdversite("bleue", 1)
this.incDecAdversite("noire", 1)
}
if (curr >= tresTraumatiseValue && prev < tresTraumatiseValue) {
ChatMessage.create({ content: `<strong>${this.name} est Très Traumatisé et subit 1 adversité bleue et 1 adversité noire !</strong>` })
this.incDecAdversite("bleue", 1)
this.incDecAdversite("noire", 1)
}
if (curr >= briseValue && prev < briseValue) {
ChatMessage.create({ content: `<strong>${this.name} est Brisé et subit 1 adversité noire !</strong>` })
this.incDecAdversite("noire", 1)
}
}
/* -------------------------------------------- */
async equipGear(equipmentId) {
let item = this.items.find(item => item.id == equipmentId);
if (item?.system?.data) {
let update = { _id: item.id, "system.equipped": !item.system.equipped };
await this.updateEmbeddedDocuments('Item', [update]); // Updates one EmbeddedEntity
}
}
/* -------------------------------------------- */
getSubActors() {
let subActors = [];
for (let id of this.system.subactors) {
subActors.push(foundry.utils.duplicate(game.actors.get(id)));
}
return subActors;
}
/* -------------------------------------------- */
async addSubActor(subActorId) {
let subActors = foundry.utils.duplicate(this.system.subactors);
subActors.push(subActorId);
await this.update({ 'system.subactors': subActors });
}
/* -------------------------------------------- */
async delSubActor(subActorId) {
let newArray = [];
for (let id of this.system.subactors) {
if (id != subActorId) {
newArray.push(id);
}
}
await this.update({ 'system.subactors': newArray });
}
/* -------------------------------------------- */
getTotalAdversite() {
return this.system.adversite.bleue + this.system.adversite.rouge + this.system.adversite.noire
}
/* -------------------------------------------- */
async incDecAdversite(adv, incDec = 0) {
let adversite = foundry.utils.duplicate(this.system.adversite)
adversite[adv] += Number(incDec)
adversite[adv] = Math.max(adversite[adv], 0)
adversite[adv] = Math.min(adversite[adv], 20)
this.update({ 'system.adversite': adversite })
}
/* -------------------------------------------- */
async incDecQuantity(objetId, incDec = 0) {
let objetQ = this.items.get(objetId)
if (objetQ) {
let newQ = objetQ.system.quantite + incDec
newQ = Math.max(newQ, 0)
await this.updateEmbeddedDocuments('Item', [{ _id: objetQ.id, 'system.quantite': newQ }]); // pdates one EmbeddedEntity
}
}
/* -------------------------------------------- */
computeRichesse() {
let valueSC = 0
for (let monnaie of this.items) {
if (monnaie.type == "monnaie") {
valueSC += Number(monnaie.system.prixsc) * Number(monnaie.system.quantite)
}
}
return MournbladeCYD2Utility.computeMonnaieDetails(valueSC)
}
/* -------------------------------------------- */
computeValeurEquipement() {
let valueSC = 0
for (let equip of this.items) {
if (equip.type == "equipement" || equip.type == "arme" || equip.type == "protection") {
valueSC += Number(equip.system.prixsc) * Number(equip.system.quantite ?? 1)
valueSC += (Number(equip.system.prixca) * Number(equip.system.quantite ?? 1)) * 20
valueSC += (Number(equip.system.prixpo) * Number(equip.system.quantite ?? 1)) * 400
}
}
return MournbladeCYD2Utility.computeMonnaieDetails(valueSC)
}
/* -------------------------------------------- */
getCompetence(compId) {
return this.items.get(compId)
}
/* -------------------------------------------- */
async setPredilectionUsed(compId, predIdx) {
let comp = this.items.get(compId)
let pred = foundry.utils.duplicate(comp.system.predilections)
pred[predIdx].used = true
await this.updateEmbeddedDocuments('Item', [{ _id: compId, 'system.predilections': pred }])
}
/* -------------------------------------------- */
getInitiativeScore() {
let init = this.getFlag("world", "last-initiative")
return init || -1
}
/* -------------------------------------------- */
getBestAttackValue() {
let attackList = this.items.filter(item => (item.type == "arme" || item.type == "talent") && item.system.equipped)
let maxOff = 0
let bestArme
for (let arme of attackList) {
if (arme.type == "arme") {
arme = this.prepareArme(arme)
}
if (arme.system.totalOffensif > maxOff) {
maxOff = arme.system.totalOffensif
bestArme = foundry.utils.duplicate(arme)
}
}
return bestArme
}
/* -------------------------------------------- */
getBestDefenseValue() {
let defenseList = this.items.filter(item => (item.type == "arme") && item.system.equipped)
let maxDef = 0
let bestArme
for (let arme of defenseList) {
if (arme.type == "arme") {
arme = this.prepareArme(arme)
}
if (arme.system.totalDefensif > maxDef) {
maxDef = arme.system.totalDefensif
bestArme = foundry.utils.duplicate(arme)
}
}
return bestArme
}
/* -------------------------------------------- */
searchRelevantTalents(competence) {
let talents = []
for (let talent of this.items) {
if (talent.type == "talent" && talent.system.isautomated && talent.system.automations.length > 0) {
for (let auto of talent.system.automations) {
if (auto.eventtype === "prepare-roll") {
if (auto.competence.toLowerCase() == competence.name.toLowerCase()) {
talent = foundry.utils.duplicate(talent)
talent.system.bonus = auto.bonus
talent.system.baCost = auto.baCost
talents.push(talent)
}
}
}
}
}
return talents
}
/* -------------------------------------------- */
buildListeAdversites() {
return []
}
/* -------------------------------------------- */
getCommonRollData(attrKey = undefined, compId = undefined, compName = undefined) {
let rollData = MournbladeCYD2Utility.getBasicRollData()
rollData.alias = this.name
rollData.actorImg = this.img
rollData.actorId = this.id
rollData.tokenId = this.token?.id
rollData.img = this.img
rollData.attributs = MournbladeCYD2Utility.getAttributs()
rollData.maitriseId = "none"
rollData.nbEclat = this.system.eclat.value
rollData.nbBA = this.system.bonneaventure.actuelle
rollData.nbAdversites = this.getTotalAdversite()
rollData.talents = []
rollData.attrKey2 = "none"
rollData.coupDevastateur = this.items.find(it => it.type == "talent" && it.name.toLowerCase() == "coup dévastateur" && !it.system.used)
rollData.hasAmbidextre = this.items.find(it => it.type == "talent" && it.name.toLowerCase() == "ambidextre")
rollData.hasFeinte = this.system.bonneaventure.actuelle > 0 && this.items.find(it => it.type == "talent" && it.name.toLowerCase() == "feinte")
rollData.isMonte = this.system.combat.monte
rollData.config = game.system.mournbladecyd2.config
if (attrKey) {
rollData.attrKey = attrKey
if (attrKey != "tochoose") {
rollData.actionImg = "systems/fvtt-mournblade-cyd2/assets/icons/" + this.system.attributs[attrKey].labelnorm + ".webp"
rollData.attr = foundry.utils.duplicate(this.system.attributs[attrKey])
}
}
if (compId) {
rollData.competence = foundry.utils.duplicate(this.items.get(compId) || {})
let maitrises = [{ key: "none", label: "Aucune" }]
rollData.competence.system.predilections.forEach(function (item) {
if (item.maitrise) {
maitrises.push({ key: item.id, label: item.name });
}
})
rollData.maitrises = maitrises // rollData.competence.system.predilections.filter(p => p.maitrise)
rollData.actionImg = rollData.competence?.img
rollData.talents = this.searchRelevantTalents(rollData.competence)
}
if (compName) {
rollData.competence = foundry.utils.duplicate(this.items.find(item => item.name.toLowerCase() == compName.toLowerCase()) || {})
rollData.actionImg = rollData.competence?.img
}
return rollData
}
/* -------------------------------------------- */
async rollAttribut(attrKey, isInit = false) {
let rollData = this.getCommonRollData(attrKey)
rollData.multiplier = (isInit) ? 1 : 2
rollData.isInit = isInit
let rollDialog = await MournbladeCYD2RollDialog.create(this, rollData)
rollDialog.render(true)
}
/* -------------------------------------------- */
async rollCompetence(attrKey, compId) {
let rollData = this.getCommonRollData(attrKey, compId)
rollData.multiplier = 1 // Attr multiplier, always 1 in competence mode
console.log("RollDatra", rollData)
let rollDialog = await MournbladeCYD2RollDialog.create(this, rollData)
rollDialog.render(true)
}
/* -------------------------------------------- */
async rollArmeOffensif(armeId) {
let arme = this.items.get(armeId)
if (arme.type == "arme") {
arme = this.prepareArme(arme)
}
let rollData = this.getCommonRollData(arme.system.attrKey, arme.system.competence._id)
rollData.arme = arme
MournbladeCYD2Utility.updateWithTarget(rollData)
console.log("ARME!", rollData)
let rollDialog = await MournbladeCYD2RollDialog.create(this, rollData)
rollDialog.render(true)
}
/* -------------------------------------------- */
async rollAssommer() {
let rollData = this.getCommonRollData("pui", undefined, "Filouterie")
rollData.assomer = true
rollData.conditionsCommunes = true
MournbladeCYD2Utility.updateWithTarget(rollData)
let rollDialog = await MournbladeCYD2RollDialog.create(this, rollData)
rollDialog.render(true)
}
/* -------------------------------------------- */
async rollCoupBas() {
let rollData = this.getCommonRollData("pui", undefined, "Mêlée")
rollData.coupBas = true
rollData.conditionsCommunes = true
MournbladeCYD2Utility.updateWithTarget(rollData)
let rollDialog = await MournbladeCYD2RollDialog.create(this, rollData)
rollDialog.render(true)
}
/* -------------------------------------------- */
async rollImmobiliser() {
let rollData = this.getCommonRollData("pui", undefined, "Mêlée")
rollData.immobiliser = true
rollData.conditionsCommunes = true
rollData.cibleconsciente = true
MournbladeCYD2Utility.updateWithTarget(rollData)
let rollDialog = await MournbladeCYD2RollDialog.create(this, rollData)
rollDialog.render(true)
}
/* -------------------------------------------- */
async rollRepousser() {
let rollData = this.getCommonRollData("pui", undefined, "Mêlée")
rollData.repousser = true
rollData.conditionsCommunes = true
rollData.cibleconsciente = true
MournbladeCYD2Utility.updateWithTarget(rollData)
let rollDialog = await MournbladeCYD2RollDialog.create(this, rollData)
rollDialog.render(true)
}
/* -------------------------------------------- */
async rollDesengager() {
let rollData = this.getCommonRollData("adr", undefined, "Mouvements")
rollData.desengager = true
rollData.conditionsCommunes = true
MournbladeCYD2Utility.updateWithTarget(rollData)
let rollDialog = await MournbladeCYD2RollDialog.create(this, rollData)
rollDialog.render(true)
}
/* -------------------------------------------- */
async rollArmeDegats(armeId, targetVigueur = undefined, rollDataInput = undefined) {
let arme = this.items.get(armeId)
if (arme.type == "arme") {
arme = this.prepareArme(arme)
}
console.log("DEGATS", arme, targetVigueur, rollDataInput)
let roll
let bonus = 0
let bonus2 = 0
if (rollDataInput?.applyCoupDevastateur) {
bonus2 = Math.floor(this.system.attributs.pui.value / 2)
let talent = this.items.find(item => item.type == "talent" && item.name.toLowerCase() == "coup dévastateur")
this.updateEmbeddedDocuments('Item', [{ _id: talent.id, 'system.used': true }])
}
if (rollDataInput?.isHeroique) {
if (rollDataInput?.attaqueCharge) {
bonus = 5
}
if (rollDataInput?.chargeCavalerie) {
bonus = 6
}
roll = await new Roll("2d10rr10+" + arme.system.totalDegats + "+" + bonus + "+" + bonus2).roll()
} else {
if (rollDataInput?.attaqueCharge) {
bonus = 3
}
if (rollDataInput?.chargeCavalerie) {
bonus = 4
}
roll = await new Roll("1d10+" + arme.system.totalDegats + "+" + bonus + "+" + bonus2).roll()
}
await MournbladeCYD2Utility.showDiceSoNice(roll, game.settings.get("core", "rollMode"));
// CYD 2.0: états SUPPLÉMENTAIRES au-delà du -1 automatique à la réussite.
// Math.floor(total/SV) = 0 (<SV), 1 (≥SV), 2 (≥2×SV) → totaux finaux : 1, 2, 3
let nbEtatPerdus = 0
if (targetVigueur) {
nbEtatPerdus = Math.floor(roll.total / targetVigueur)
}
//console.log(roll)
let rollData = {
arme: arme,
finalResult: roll.total,
formula: roll.formula,
alias: this.name,
actorImg: this.img,
actorId: this.id,
defenderTokenId: rollDataInput?.defenderTokenId,
actionImg: arme.img,
targetVigueur: targetVigueur,
nbEtatPerdus: nbEtatPerdus
}
MournbladeCYD2Utility.createChatWithRollMode(rollData.alias, {
content: await renderTemplate(`systems/fvtt-mournblade-cyd2/templates/chat-degats-result.hbs`, rollData)
})
if (rollDataInput?.defenderTokenId && nbEtatPerdus) {
MournbladeCYD2Utility.applyCombativite(rollDataInput, nbEtatPerdus)
}
}
}