import { ChatUtility } from "../chat-utility.js" import RollDialog from "./roll-dialog.mjs" import { RdDCarac } from "../rdd-carac.js" import { RdDCombat } from "../rdd-combat.js" import { ROLL_TYPE_ATTAQUE, ROLL_TYPE_DEFENSE } from "./roll-constants.mjs" import { RdDResolutionTable } from "../rdd-resolution-table.js" import { RDD_CONFIG, renderTemplate } from "../constants.js" import { EMPOIGNADE } from "../item/arme.js" import { RdDTextEditor } from "../apps/rdd-text-roll-editor.js" import { RollTypeCuisine } from "./roll-type-cuisine.mjs" export default class ChatRollResult { static init() { ChatRollResult.instance = new ChatRollResult() Hooks.on('renderChatLog', (log, html, chatLog) => ChatRollResult.instance.chatListeners(html)) } static onReady() { foundry.applications.handlebars.loadTemplates({ 'partial-appel-chance': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-appel-chance.hbs', 'partial-attaque-particuliere': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-attaque-particuliere.hbs', 'partial-encaissement': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-encaissement.hbs', 'partial-recul-choc': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-recul-choc.hbs', 'partial-info-appel-moral': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-info-appel-moral.hbs', }) } async display(roll, impacts) { this.prepareDisplay(roll) const chatMessage = await ChatUtility.createChatWithRollMode( { content: await this.buildRollHtml(roll) }, roll.active.actor, roll.current?.rollmode?.key ) const save = RollDialog.saveParts(roll, impacts) ChatUtility.setMessageData(chatMessage, 'rollData', save) return chatMessage } prepareDisplay(roll) { roll.done = roll.done ?? {} roll.show = roll.show ?? {} roll.show.chance = this.isAppelChancePossible(roll) roll.show.encaissement = this.isShowEncaissement(roll) roll.show.recul = this.getRecul(roll) //roll.show.particuliere = roll.show.particuliere ?? [] } isAppelChancePossible(roll) { return roll.active.actor.isPersonnage() && roll.rolled.isEchec && RdDCarac.isActionPhysique(roll.current.carac?.key) } isShowEncaissement(roll) { switch (roll.type.current) { case ROLL_TYPE_DEFENSE: return roll.rolled.isEchec && roll.attackerRoll?.dmg.mortalite != EMPOIGNADE } return false } getRecul(roll, defender = roll.active.actor, attacker = roll.opponent?.actor) { switch (roll.type.current) { case ROLL_TYPE_DEFENSE: { const attaque = roll.attackerRoll if (attaque && (roll.rolled.isEchec || !roll.current.defense.isEsquive) && (attaque.particuliere == 'force' || 'charge' == attaque.tactique?.key)) { const taille = defender.system.carac.taille.value const impact = attacker.system.carac.force.value + roll.attackerRoll?.dmg.dmgArme return { raison: 'charge' == attaque.tactique?.key ? 'charge' : 'particulière en force', taille: taille, impact: impact, chances: RdDResolutionTable.computeChances(10, taille - impact).norm, diff: taille - impact } } break } case ROLL_TYPE_ATTAQUE: { const attaque = roll if (attaque.particuliere == 'force' || 'charge' == attaque.tactique?.key) { return { raison: 'charge' == attaque.tactique?.key ? 'charge' : 'particulière en force', } } } } return undefined } async buildRollHtml(roll) { const template = `systems/foundryvtt-reve-de-dragon/templates/roll/result/chat-${roll.type.current}.hbs` const html = await renderTemplate(template, roll) return await RdDTextEditor.enrichHTML(html, undefined, { showLink: false }) } async chatListeners(html) { $(html).on("click", '.appel-chance', event => this.onClickAppelChance(event)) $(html).on("click", '.appel-destinee', event => this.onClickAppelDestinee(event)) $(html).on("click", '.encaissement', event => this.onClickEncaissement(event)) $(html).on("click", '.resister-recul', event => this.onClickRecul(event)) $(html).on("click", '.choix-particuliere', event => this.onClickChoixParticuliere(event)) $(html).on("click", '.faire-gouter', event => this.onClickFaireGouter(event)) } getCombat(roll) { switch (roll.type.current) { case ROLL_TYPE_DEFENSE: return RdDCombat.rddCombatForAttackerAndDefender(roll.ids.opponentId, roll.ids.opponentTokenId, roll.ids.actorTokenId) case ROLL_TYPE_ATTAQUE: return RdDCombat.rddCombatForAttackerAndDefender(roll.ids.actorId, roll.ids.actorTokenId, roll.ids.opponentId) } return undefined } async updateChatMessage(chatMessage, savedRoll) { ChatUtility.setMessageData(chatMessage, 'rollData', savedRoll) const copy = foundry.utils.duplicate(savedRoll) RollDialog.loadRollData(copy) this.prepareDisplay(copy) chatMessage.update({ content: await this.buildRollHtml(copy) }) chatMessage.render(true) } onClickAppelChance(event) { const chatMessage = ChatUtility.getChatMessage(event) const savedRoll = ChatUtility.getMessageData(chatMessage, 'rollData') const actor = game.actors.get(savedRoll.ids.actorId) actor.rollAppelChance( () => this.onAppelChanceSuccess(savedRoll, chatMessage), () => this.onAppelChanceEchec(savedRoll, chatMessage)) event.preventDefault() } onAppelChanceSuccess(savedRoll, chatMessage) { const reRoll = foundry.utils.duplicate(savedRoll) console.log('onAppelChanceSuccess savedRoll', savedRoll) reRoll.type.retry = true const callbacks = [r => ChatUtility.removeChatMessageId(chatMessage.id)] // TODO: annuler les effets switch (reRoll.type.current) { case ROLL_TYPE_DEFENSE: this.getCombat(reRoll)?.doRollDefense(reRoll, callbacks) break case ROLL_TYPE_ATTAQUE: // TODO this.getCombat(reRoll)?.doRollAttaque(reRoll, callbacks) break default: { RollDialog.create(reRoll, { callbacks: callbacks }) } } } async onAppelChanceEchec(savedRoll, chatMessage) { savedRoll.type.retry = true await this.updateChatMessage(chatMessage, savedRoll) } onClickAppelDestinee(event) { const chatMessage = ChatUtility.getChatMessage(event) const savedRoll = ChatUtility.getMessageData(chatMessage, 'rollData') const actor = game.actors.get(savedRoll.ids.actorId) actor.appelDestinee(async () => { const reRoll = foundry.utils.duplicate(savedRoll) reRoll.type.retry = true RdDResolutionTable.significativeRequise(reRoll.rolled) await this.updateChatMessage(chatMessage, reRoll) }) } async onClickEncaissement(event) { const chatMessage = ChatUtility.getChatMessage(event) const savedRoll = ChatUtility.getMessageData(chatMessage, 'rollData') const attaque = savedRoll.attackerRoll const defender = game.actors.get(savedRoll.ids.actorId) const attacker = game.actors.get(savedRoll.ids.opponentId) const defenderToken = savedRoll.ids.actorTokenId ? canvas.tokens.get(savedRoll.ids.actorTokenId) : undefined const attackerToken = savedRoll.ids.opponentTokenId ? canvas.tokens.get(savedRoll.ids.opponentTokenId) : undefined await defender?.encaisserDommages(attaque.dmg, attacker, undefined, attackerToken, defenderToken) savedRoll.done.encaissement = true await this.updateChatMessage(chatMessage, savedRoll) } async onClickRecul(event) { const chatMessage = ChatUtility.getChatMessage(event) const savedRoll = ChatUtility.getMessageData(chatMessage, 'rollData') const defender = game.actors.get(savedRoll.ids.actorId) const attacker = game.actors.get(savedRoll.ids.opponentId) savedRoll.done.recul = await defender.encaisserRecul(attacker.getForce(), savedRoll.attackerRoll.dmg.dmgArme) // const reculChoc = this.getReculChoc(savedRoll, defender, attacker) await this.updateChatMessage(chatMessage, savedRoll) } async onClickChoixParticuliere(event) { const choix = event.currentTarget.attributes['data-particuliere'].value const chatMessage = ChatUtility.getChatMessage(event) const savedRoll = ChatUtility.getMessageData(chatMessage, 'rollData') savedRoll.particuliere = choix savedRoll.particulieres = [RDD_CONFIG.particuliere[choix]] await this.updateChatMessage(chatMessage, savedRoll) await this.getCombat(savedRoll)?.onAttaqueV2(savedRoll, callbacks) } async onClickFaireGouter(event) { const chatMessage = ChatUtility.getChatMessage(event) const savedRoll = ChatUtility.getMessageData(chatMessage, 'rollData') await new RollTypeCuisine().onFaireGouter(savedRoll) } }