Cas rencontré une fois, un jet de chance d'un autre joueur, pouvait être tenté. Cas plus étrange, un jet de chance alors que le jet sauvegardé n'avait pas d'actorId
366 lines
15 KiB
JavaScript
366 lines
15 KiB
JavaScript
import { ChatUtility } from "../chat-utility.js"
|
|
import RollDialog, { ALL_ROLL_TYPES } 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 { RdDTextEditor } from "../apps/rdd-text-roll-editor.js"
|
|
import { RollTypeCuisine } from "./roll-type-cuisine.mjs"
|
|
import { RollTypeMeditation } from "./roll-type-meditation.mjs"
|
|
import { PART_DEFENSE } from "./roll-part-defense.mjs"
|
|
import { PART_ATTAQUE } from "./roll-part-attaque.mjs"
|
|
import { RdDRollTables } from "../rdd-rolltables.js"
|
|
import { RdDEmpoignade } from "../rdd-empoignade.js"
|
|
import { Misc } from "../misc.js"
|
|
import { RollBasicParts } from "./roll-basic-parts.mjs"
|
|
import { RdDPossessionV2 } from "../rdd-possession-v2.mjs"
|
|
import { Apprecier } from "../moral/apprecier.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-apprecier': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-apprecier.hbs',
|
|
'partial-appreciation': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-appreciation.hbs',
|
|
'partial-attaque-particuliere': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-attaque-particuliere.hbs',
|
|
'partial-choix-maladresse': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-choix-maladresse.hbs',
|
|
'partial-encaissement': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-encaissement.hbs',
|
|
'partial-infojet': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-infojet.hbs',
|
|
'partial-info-appel-moral': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-info-appel-moral.hbs',
|
|
'partial-maladresse': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-maladresse.hbs',
|
|
'partial-recul-choc': 'systems/foundryvtt-reve-de-dragon/templates/roll/result/partial-recul-choc.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)
|
|
|
|
await this.saveChatMessageRoll(chatMessage, 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.maladresse = this.$getMaladresse(roll)
|
|
}
|
|
|
|
$isAppelChancePossible(roll) {
|
|
return roll.active.actor.isPersonnage() &&
|
|
roll.rolled.isEchec &&
|
|
RdDCarac.isActionPhysique(roll.current.carac?.key) &&
|
|
!roll.type.appreciation
|
|
}
|
|
|
|
$isShowEncaissement(roll) {
|
|
switch (roll.type.current) {
|
|
case ROLL_TYPE_DEFENSE:
|
|
return roll.rolled.isEchec
|
|
}
|
|
return false
|
|
}
|
|
|
|
$getMaladresse(roll) {
|
|
switch (roll.type.current) {
|
|
case ROLL_TYPE_DEFENSE:
|
|
if (roll.rolled.isETotal) {
|
|
const arme = roll.current[PART_DEFENSE].arme
|
|
return arme ? 'avec-arme' : 'sans-arme'
|
|
}
|
|
break
|
|
case ROLL_TYPE_ATTAQUE:
|
|
if (roll.rolled.isETotal || (roll.rolled.isEchec && roll.active.surprise == 'demi')) {
|
|
const arme = roll.current[PART_ATTAQUE].arme
|
|
return arme.system.baseInit > 4 ? 'avec-arme' : 'sans-arme'
|
|
}
|
|
break
|
|
}
|
|
return undefined
|
|
}
|
|
|
|
$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 = ALL_ROLL_TYPES.find(it => it.code == roll.type.current).chatResultTemplate
|
|
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", '.button-defense', event => this.onClickDefense(event))
|
|
$(html).on("click", '.button-defense-possession', event => this.onClickDefensePossession(event))
|
|
$(html).on("click", '.marquer-point-possession', event => this.onClickMarquerPointPossession(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))
|
|
$(html).on("click", '.apprecier', event => this.onClickApprecier(event))
|
|
$(html).on("click", '.monter-tmr-normale', event => this.onClickMonteeTMR(event, 'normal'))
|
|
$(html).on("click", '.monter-tmr-rapide', event => this.onClickMonteeTMR(event, 'rapide'))
|
|
$(html).on("click", '.tirer-maladresse', event => this.onClickTirerMaladresse(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.opponentTokenId)
|
|
}
|
|
return undefined
|
|
}
|
|
|
|
getActiveActor(roll) {
|
|
return roll.active?.actor ?? (roll.ids?.actorId ? game.actors.get(roll.ids.actorId) : undefined)
|
|
}
|
|
|
|
async saveChatMessageRoll(chatMessage, savedRoll) {
|
|
await ChatUtility.setMessageData(chatMessage, 'rollData', savedRoll)
|
|
}
|
|
|
|
loadChatMessageRoll(chatMessage) {
|
|
const savedRoll = ChatUtility.getMessageData(chatMessage, 'rollData')
|
|
RollBasicParts.restore(savedRoll)
|
|
return savedRoll
|
|
}
|
|
|
|
async updateChatMessage(chatMessage, savedRoll) {
|
|
await this.saveChatMessageRoll(chatMessage, savedRoll)
|
|
const copy = foundry.utils.duplicate(savedRoll)
|
|
RollDialog.loadRollData(copy)
|
|
savedRoll.dmg = copy.current.attaque?.dmg
|
|
this.prepareDisplay(copy)
|
|
chatMessage.update({ content: await this.buildRollHtml(copy) })
|
|
chatMessage.render(true)
|
|
}
|
|
|
|
onClickAppelChance(event) {
|
|
event.preventDefault()
|
|
const chatMessage = ChatUtility.getChatMessage(event)
|
|
const savedRoll = this.loadChatMessageRoll(chatMessage)
|
|
const actor = this.getActiveActor(savedRoll)
|
|
Misc.doIfOwner(actor, it => it.rollAppelChance(
|
|
() => this.onAppelChanceSuccess(savedRoll, chatMessage),
|
|
() => this.onAppelChanceEchec(savedRoll, chatMessage))
|
|
)
|
|
}
|
|
|
|
async onAppelChanceSuccess(savedRoll, chatMessage) {
|
|
const reRoll = foundry.utils.duplicate(savedRoll)
|
|
reRoll.type.retry = true
|
|
await this.updateChatMessage(chatMessage, reRoll)
|
|
const callbacks = [ChatUtility.remover(chatMessage)]
|
|
|
|
// TODO: annuler les effets
|
|
switch (reRoll.type.current) {
|
|
case ROLL_TYPE_DEFENSE:
|
|
this.getCombat(reRoll)?.doRollDefense(reRoll, callbacks)
|
|
break
|
|
case ROLL_TYPE_ATTAQUE:
|
|
this.getCombat(reRoll)?.doRollAttaque(reRoll, callbacks)
|
|
break
|
|
default: {
|
|
await RollDialog.create(reRoll, { onRollDone: RollDialog.onRollDoneClose, callbacks })
|
|
}
|
|
}
|
|
}
|
|
|
|
async onAppelChanceEchec(savedRoll, chatMessage) {
|
|
savedRoll.type.retry = true
|
|
await this.updateChatMessage(chatMessage, savedRoll)
|
|
}
|
|
|
|
onClickAppelDestinee(event) {
|
|
event.preventDefault()
|
|
|
|
const chatMessage = ChatUtility.getChatMessage(event)
|
|
const savedRoll = this.loadChatMessageRoll(chatMessage)
|
|
const actor = this.getActiveActor(savedRoll)
|
|
|
|
Misc.doIfOwner(actor, it => it.appelDestinee(async () => {
|
|
const reRoll = foundry.utils.duplicate(savedRoll)
|
|
reRoll.type.retry = true
|
|
RdDResolutionTable.significativeRequise(reRoll.rolled)
|
|
await this.updateChatMessage(chatMessage, reRoll)
|
|
}))
|
|
}
|
|
|
|
async onClickDefense(event) {
|
|
const chatMessage = ChatUtility.getChatMessage(event)
|
|
const savedRoll = this.loadChatMessageRoll(chatMessage)
|
|
const actor = this.getActiveActor(savedRoll)
|
|
Misc.doIfOwner(actor, it => {
|
|
const attackerRoll = savedRoll.attackerRoll
|
|
RollDialog.loadRollData(attackerRoll)
|
|
this.getCombat(attackerRoll)?.defenseV2(attackerRoll,
|
|
[ChatUtility.remover(chatMessage)]
|
|
)
|
|
})
|
|
}
|
|
|
|
async onClickDefensePossession(event) {
|
|
const chatMessage = ChatUtility.getChatMessage(event)
|
|
const savedRoll = this.loadChatMessageRoll(chatMessage)
|
|
const actor = this.getActiveActor(savedRoll)
|
|
Misc.doIfOwner(actor, it => RdDPossessionV2.rollDefensePossession(savedRoll, chatMessage))
|
|
}
|
|
|
|
async onClickMarquerPointPossession(event) {
|
|
const chatMessage = ChatUtility.getChatMessage(event)
|
|
const savedRoll = this.loadChatMessageRoll(chatMessage)
|
|
const actor = this.getActiveActor(savedRoll)
|
|
Misc.doIfOwner(actor, async it => {
|
|
await RdDPossessionV2.onMarquerPointPossession(savedRoll)
|
|
ChatUtility.remover(chatMessage)()
|
|
})
|
|
}
|
|
|
|
async onClickEncaissement(event) {
|
|
const chatMessage = ChatUtility.getChatMessage(event)
|
|
const isMessageDemande = ChatUtility.getMessageData(chatMessage, 'demande-defense')
|
|
const savedRoll = this.loadChatMessageRoll(chatMessage)
|
|
const actor = this.getActiveActor(savedRoll)
|
|
Misc.doIfOwner(actor, async defender => {
|
|
const defenderToken = savedRoll.active.token
|
|
const attaque = savedRoll.attackerRoll
|
|
const attackerToken = savedRoll.ids.opponentTokenId ? canvas.tokens.get(savedRoll.ids.opponentTokenId) : undefined
|
|
const attacker = attackerToken?.actor ?? game.actors.get(savedRoll.ids.opponentId)
|
|
switch (attaque.dmg.mortalite) {
|
|
case RDD_CONFIG.encaissement.empoignade:
|
|
savedRoll.done = savedRoll.done ?? {}
|
|
savedRoll.done.empoignade = await RdDEmpoignade.ajustementEmpoignade(attackerToken.actor, defenderToken.actor)
|
|
break
|
|
case RDD_CONFIG.encaissement.entiteincarnee:
|
|
case RDD_CONFIG.encaissement.nonmortel:
|
|
case RDD_CONFIG.encaissement.mortel:
|
|
await defender?.encaisserDommages(attaque.dmg, attacker, undefined, attackerToken, defenderToken)
|
|
break
|
|
}
|
|
if (isMessageDemande) {
|
|
ChatUtility.remover(chatMessage)()
|
|
} else {
|
|
savedRoll.done.encaissement = true
|
|
await this.updateChatMessage(chatMessage, savedRoll)
|
|
}
|
|
})
|
|
}
|
|
|
|
async onClickRecul(event) {
|
|
const chatMessage = ChatUtility.getChatMessage(event)
|
|
const savedRoll = this.loadChatMessageRoll(chatMessage)
|
|
const actor = this.getActiveActor(savedRoll)
|
|
Misc.doIfOwner(actor, async defender => {
|
|
const attacker = game.actors.get(savedRoll.ids.opponentId)
|
|
savedRoll.done.recul = await defender.encaisserRecul(attacker.getForce(), savedRoll.attackerRoll.dmg.dmgArme)
|
|
await this.updateChatMessage(chatMessage, savedRoll)
|
|
})
|
|
}
|
|
|
|
async onClickChoixParticuliere(event) {
|
|
const choix = event.currentTarget.attributes['data-particuliere'].value
|
|
const chatMessage = ChatUtility.getChatMessage(event)
|
|
const savedRoll = this.loadChatMessageRoll(chatMessage)
|
|
const actor = this.getActiveActor(savedRoll)
|
|
Misc.doIfOwner(actor, async it => {
|
|
savedRoll.particuliere = choix
|
|
savedRoll.particulieres = [RDD_CONFIG.particuliere[choix]]
|
|
await this.updateChatMessage(chatMessage, savedRoll)
|
|
await this.getCombat(savedRoll)?.onAttaqueV2(savedRoll)
|
|
})
|
|
}
|
|
|
|
async onClickFaireGouter(event) {
|
|
const chatMessage = ChatUtility.getChatMessage(event)
|
|
const savedRoll = this.loadChatMessageRoll(chatMessage)
|
|
const actor = this.getActiveActor(savedRoll)
|
|
Misc.doIfOwner(actor, async it => {
|
|
if (!savedRoll.type.retry) {
|
|
savedRoll.type.retry = true
|
|
await this.updateChatMessage(chatMessage, savedRoll)
|
|
}
|
|
await new RollTypeCuisine().onFaireGouter(savedRoll)
|
|
})
|
|
}
|
|
async onClickApprecier(event) {
|
|
const chatMessage = ChatUtility.getChatMessage(event)
|
|
const savedRoll = this.loadChatMessageRoll(chatMessage)
|
|
Apprecier.onClickApprecier(savedRoll)
|
|
}
|
|
|
|
async onClickMonteeTMR(event, mode) {
|
|
const chatMessage = ChatUtility.getChatMessage(event)
|
|
const savedRoll = this.loadChatMessageRoll(chatMessage)
|
|
const actor = this.getActiveActor(savedRoll)
|
|
Misc.doIfOwner(actor, async it => {
|
|
if (await new RollTypeMeditation().onMonteeTMR(savedRoll, mode)) {
|
|
savedRoll.done.meditation = true
|
|
await this.updateChatMessage(chatMessage, savedRoll)
|
|
}
|
|
})
|
|
}
|
|
|
|
async onClickTirerMaladresse(event) {
|
|
const chatMessage = ChatUtility.getChatMessage(event)
|
|
const typeMaladresse = event.currentTarget.attributes['data-maladresse'].value
|
|
const savedRoll = this.loadChatMessageRoll(chatMessage)
|
|
const actor = this.getActiveActor(savedRoll)
|
|
Misc.doIfOwner(actor, async it => {
|
|
savedRoll.maladresse = await RdDRollTables.getMaladresse({ arme: typeMaladresse == 'avec-arme', toChat: false })
|
|
savedRoll.type.retry = true
|
|
await this.updateChatMessage(chatMessage, savedRoll)
|
|
})
|
|
}
|
|
}
|