import {ActionManager} from "../sheets/actions/action-manager.mjs"; const {ApplicationV2, HandlebarsApplicationMixin} = foundry.applications.api /** * @typedef TokenDistance * @property {Number} x * @property {Number} y * @property {Number} d * @property {Token} token */ export class DefenseActionDialog extends HandlebarsApplicationMixin(ApplicationV2) { static DEFAULT_OPTIONS = { classes: ['dsa41', 'dialog', 'combat'], tag: "form", position: { width: 480, height: 640 }, window: { resizable: false, }, form: { submitOnChange: false, closeOnSubmit: true, handler: DefenseActionDialog.#onSubmitForm }, actions: { selectWeaponAndSkill: DefenseActionDialog.#onSelectWeaponAndSkill, selectManeuver: DefenseActionDialog.#onSelectManeuver, } } static PARTS = { form: { template: 'systems/DSA_4-1/templates/dialog/defense-action.hbs', } } /** * @type {Actor} * @private */ _actor = null constructor(actor, attackData) { super(); this._attackData = attackData ?? { modToDefense: 0, attacker: null, weapon: null, // is important to note as weapons like Chain Weapons or Flails can ignore Shields } this._actor = actor this._skillId = null this._weaponId = null this._defenseManeuverId = null this._actionManager = new ActionManager(this._actor) //if (this._actor) { // this._actor.prepareDerivedData() //} } static async #onSelectTarget(event, target) { const {targetId} = target.dataset this._targetId = this._targetId === targetId ? null : targetId this.render({parts: ["form"]}) } static async #onSelectManeuver(event, target) { const {maneuverId} = target.dataset this._defenseManeuverId = this._defenseManeuverId === maneuverId ? null : maneuverId this.render({parts: ["form"]}) } static async #onSelectWeaponAndSkill(event, target) { const {weaponId, skillId} = target.dataset this._weaponId = this._weaponId === weaponId ? null : weaponId this._skillId = this._skillId === skillId ? null : skillId this.render({parts: ["form"]}) } static async #onSubmitForm(event, form, formData) { event.preventDefault() const attack = this._attackData const maneuver = this.#evaluateManeuvers().find(p => p.id === this._defenseManeuverId) this._actor.rollDefense({ weapon: this._weaponId, skill: this._skillId, attackData: attack, maneuver, mod: this._mod, circumstance: this._circumstance, penalty: this._penalty, targetNumber: this._targetNumber, modDescription: maneuver?.modDescription?.replace("{}", "" + this._mod) ?? "" }) } _configureRenderOptions(options) { super._configureRenderOptions(options) if (options.window) { options.window.title = `Gegen einen Angriff verteidigen` } return options } #evaluateWeapons() { // get equipped weapons and adjust AT/PA values by basis values from actor TODO: and W/M of weapons const equips = this._actor.system.heldenausruestung[this._actor.system.setEquipped] const weapons = [] const equippedWeapons = ["links", "rechts", "fernkampf"] const baseAt = { links: this._actor.system.at.links.aktuell, // TODO hook Beidhändigerkampf/linkhand rechts: this._actor.system.at.rechts.aktuell, fernkampf: this._actor.system.fk.aktuell, } const basePa = { links: this._actor.system.pa.links.aktuell, // TODO hook Beidhändigerkampf/linkhand rechts: this._actor.system.pa.rechts.aktuell, fernkampf: 0, } equippedWeapons.forEach(slot => { const equip = equips[slot] const weapon = this._actor.itemTypes["Equipment"].find(p => p._id === equip) if (weapon) { const variantWeaponSkills = [...weapon.system.rangedSkills, ...weapon.system.meleeSkills] variantWeaponSkills.forEach(weaponSkill => { const skill = this._actor.itemTypes["Skill"].find(p => p.name === weaponSkill) if (skill) { const skillAt = skill.system.at const skillPa = skill.system.pa weapons.push({ isSelected: this._skillId === skill._id && this._weaponId === weapon._id, weaponId: weapon._id, skillId: skill._id, name: weapon.name, skillName: skill.name, img: weapon.img, combatStatistics: { at: baseAt[slot] + weapon.system.attackModifier + skillAt, pa: basePa[slot] + weapon.system.parryModifier + skillPa } }) } }) } }) // TODO: also do this for combatAction const links = this._actor.system.heldenausruestung[this._actor.system.setEquipped].links const rechts = this._actor.system.heldenausruestung[this._actor.system.setEquipped].rechts if (!links && !rechts) { const unarmedSkills = [this._actor.itemTypes["Skill"].find(p => p.name === "Ringen"), this._actor.itemTypes["Skill"].find(p => p.name === "Raufen")] unarmedSkills.forEach(unarmedSkill => { const [skillAt, skillPa] = [unarmedSkill.system.at, unarmedSkill.system.pa] weapons.push({ isSelected: this._skillId === unarmedSkill._id, weaponId: "", skillId: unarmedSkill._id, name: unarmedSkill.name, skillName: unarmedSkill.name, img: "", combatStatistics: { at: baseAt["rechts"] + skillAt, pa: basePa["rechts"] + skillPa } }) }) } // Ausweichen as Weapon weapons.push({ isSelected: this._weaponId === "Ausweichen", weaponId: "Ausweichen", skillId: "Ausweichen", name: "Ausweichen", skillName: "Ausweichen", img: "", combatStatistics: { at: 0, pa: this._actor.system.ausweichen.aktuell } }) this._weapons = weapons.sort((a, b) => (a.isSelected ? 0 : 1) - (b.isSelected ? 0 : 1)) return this._weapons } #evaluateManeuvers() { const manager = this._actionManager const weapon = this._weaponId !== "Ausweichen" ? this._actor.itemTypes["Equipment"].find(p => p._id === this._weaponId) : "Ausweichen" const skill = this._actor.itemTypes["Skill"].find(p => p._id === this._skillId) //const target = game.actors.get(game.scenes.current.tokens.find(p => p._id === this._targetId).actorId) this._maneuvers = manager.evaluate({ //target, weapon, skill }).filter(p => p.type === ActionManager.DEFENSE).map(action => { return { isSelected: this._defenseManeuverId === action.name, id: action.name, name: action.name, type: action.type, source: action.source, cost: action.cost, penalty: action.eval?.mod ?? 0, mod: action.mod, modDescription: action.modDescription, cooldown: action.cooldown, activate: action.activate, } }).sort((a, b) => (a.isSelected ? 0 : 1) - (b.isSelected ? 0 : 1)) return this._maneuvers } async _prepareContext(options) { const context = await super._prepareContext(options) context.actor = this._actor context.distanceUnit = game.scenes.current.grid.units if (this._actor.getActiveTokens()[0]?.id) { context.weapons = this.#evaluateWeapons() if (this._skillId) { context.maneuver = this.#evaluateManeuvers() } const maneuver = this._maneuvers?.find(p => p.id === this._defenseManeuverId) if (maneuver) { context.canMod = maneuver.mod != undefined } context.targetNumber = context.weapons.find(p => ((this._weaponId != "") || p.weaponId === this._weaponId) && p.skillId === this._skillId)?.combatStatistics.pa // TODO get W/M of weapon NOW context.ready = this._targetId && this._weaponId && this._skillId && this._defenseManeuverId return context } else { ui.notifications.error(`Feature funktioniert nur wenn der Akteur ein Token auf der aktuellen Szene hat`); } } #update(context) { const target = this.element.querySelector(".actions button .value") const targetDescription = this.element.querySelector(".modResult") const at = Number(context.targetNumber) const maneuver = this._maneuvers?.find(p => p.id === this._defenseManeuverId) const mod = Number(this.element.querySelector('[name="mod"]').value) const penalty = 0 - (maneuver?.penalty ?? 0) + (maneuver?.mod?.(mod) ?? 0) ?? 0 const circumstance = Number(this.element.querySelector('[name="circumstance"]').value) this.element.querySelector('[name="penalty"]').value = penalty + circumstance const result = (at + circumstance + penalty) this._circumstance = circumstance this._penalty = penalty this._targetNumber = result this._mod = mod this._modDescription = maneuver?.modDescription?.replace("{}", "" + mod) ?? "" target.textContent = `(${result})` targetDescription.textContent = this._modDescription if (result <= 0) { context.ready = false this.element.querySelector(".actions button").classList.remove("ready") this.element.querySelector(".actions button").setAttribute("disabled", true) } else { context.ready = true this.element.querySelector(".actions button").classList.add("ready") this.element.querySelector(".actions button").removeAttribute("disabled") } } _onRender(context, options) { this.#update(context) this.element.querySelectorAll('[name="mod"], [name="malus"], [name="circumstance"]').forEach(e => e.addEventListener('change', (event) => { this.#update(context) })) } }