From 3d9fe1a0e83e1f35044599765c10b81712508e4b Mon Sep 17 00:00:00 2001 From: Idle <651462+reonZ@users.noreply.github.com> Date: Sun, 21 Apr 2024 12:56:54 +0200 Subject: [PATCH] fix visibility off-guard not applying --- src/check.js | 319 +++++++++++++++++++++++++++++---------------------- 1 file changed, 184 insertions(+), 135 deletions(-) diff --git a/src/check.js b/src/check.js index b562dcc..b319cca 100644 --- a/src/check.js +++ b/src/check.js @@ -1,234 +1,283 @@ -import { getActorToken, getCoverEffect, isProne } from './actor.js' -import { COVERS, COVER_UUID, COVER_VALUES, VISIBILITY_VALUES, attackCheckRoll, validCheckRoll } from './constants.js' -import { createCoverSource, createFlatFootedSource, findChoiceSetRule } from './effect.js' -import { MODULE_ID, getFlag, getSetting, localize } from './module.js' -import { getPerception, perceptionRules } from './rule-element.js' -import { validateTokens } from './scene.js' -import { getSeekTemplateTokens } from './template.js' -import { getCover, getVisibility } from './token.js' -import { asNumberOnly } from './utils.js' +import { getActorToken, getCoverEffect, isProne } from "./actor.js"; +import { + COVERS, + COVER_UUID, + COVER_VALUES, + VISIBILITY_VALUES, + attackCheckRoll, + validCheckRoll, +} from "./constants.js"; +import { createCoverSource, createFlatFootedSource, findChoiceSetRule } from "./effect.js"; +import { MODULE_ID, getFlag, getSetting, localize } from "./module.js"; +import { getPerception, perceptionRules } from "./rule-element.js"; +import { validateTokens } from "./scene.js"; +import { getSeekTemplateTokens } from "./template.js"; +import { getCover, getVisibility } from "./token.js"; +import { asNumberOnly } from "./utils.js"; export async function checkRoll(wrapped, ...args) { - const context = args[1] - if (!context) return wrapped(...args) - - if (Array.isArray(context.options)) context.options = new Set(context.options) - - const { actor, createMessage = 'true', type, token, target, isReroll, viewOnly, item } = context - const originToken = (token ?? getActorToken(actor))?.object - const targetToken = target?.token?.object - const flatCheck = getSetting('flat-check') - - if (viewOnly || isReroll || !createMessage || !originToken || actor.isOfType('hazard') || !validCheckRoll.includes(type)) { - return wrapped(...args) + const context = args[1]; + if (!context) return wrapped(...args); + + if (Array.isArray(context.options)) context.options = new Set(context.options); + + const { + actor, + createMessage = "true", + type, + token, + target, + isReroll, + viewOnly, + item, + } = context; + const originToken = (token ?? getActorToken(actor))?.object; + const targetToken = target?.token?.object; + const flatCheck = getSetting("flat-check"); + + if ( + viewOnly || + isReroll || + !createMessage || + !originToken || + actor.isOfType("hazard") || + !validCheckRoll.includes(type) + ) { + return wrapped(...args); } - const targetActor = targetToken?.actor + const targetActor = targetToken?.actor; if (attackCheckRoll.includes(type) && targetActor) { - const event = args[2] + const event = args[2]; // should we roll a flat check to attack the target - flatCheck: if (flatCheck !== 'none') { + flatCheck: if (flatCheck !== "none") { const perception = perceptionRules(originToken, targetToken, { - extraOptions: context.options.filter(o => o.startsWith('item:')), - }) + extraOptions: context.options.filter((o) => o.startsWith("item:")), + }); - const visibility = getVisibility(targetToken, originToken, { perception, affects: 'target' }) - if (!visibility) break flatCheck + const visibility = getVisibility(targetToken, originToken, { + perception, + affects: "target", + }); + if (!visibility) break flatCheck; const dc = (() => { - const dc = getPerception(perception, 'target', 'visibility', 'dc', visibility)?.first() - const numberedDC = asNumberOnly(dc) - if (!numberedDC) return numberedDC - - const sign = dc[0] - if (!['-', '+'].includes(sign)) return numberedDC - - return (visibility === 'concealed' ? 5 : 11) + numberedDC - })() - if (dc === 0) break flatCheck - - const isUndetected = VISIBILITY_VALUES[visibility] >= VISIBILITY_VALUES.undetected - const isBlind = event?.ctrlKey || event?.metaKey + const dc = getPerception( + perception, + "target", + "visibility", + "dc", + visibility + )?.first(); + const numberedDC = asNumberOnly(dc); + if (!numberedDC) return numberedDC; + + const sign = dc[0]; + if (!["-", "+"].includes(sign)) return numberedDC; + + return (visibility === "concealed" ? 5 : 11) + numberedDC; + })(); + if (dc === 0) break flatCheck; + + const isUndetected = VISIBILITY_VALUES[visibility] >= VISIBILITY_VALUES.undetected; + const isBlind = event?.ctrlKey || event?.metaKey; const roll = await new originToken.actor.saves.reflex.constructor(originToken.actor, { modifiers: [], - slug: 'visibility-check', - label: `${game.i18n.localize('PF2E.FlatCheck')}: ${game.i18n.localize(`PF2E.condition.${visibility}.name`)}`, - check: { type: 'flat-check' }, + slug: "visibility-check", + label: `${game.i18n.localize("PF2E.FlatCheck")}: ${game.i18n.localize( + `PF2E.condition.${visibility}.name` + )}`, + check: { type: "flat-check" }, }).roll({ - dc: { value: dc ?? (visibility === 'concealed' ? 5 : 11) }, + dc: { value: dc ?? (visibility === "concealed" ? 5 : 11) }, target: targetToken.actor, - rollMode: isUndetected || isBlind ? (game.user.isGM ? 'gmroll' : 'blindroll') : 'roll', - }) + rollMode: + isUndetected || isBlind ? (game.user.isGM ? "gmroll" : "blindroll") : "roll", + }); - const isSuccess = roll.degreeOfSuccess > 1 + const isSuccess = roll.degreeOfSuccess > 1; if (isUndetected) { - context.options.add('secret') + context.options.add("secret"); context.pf2ePerception = { isSuccess: isSuccess, visibility, - } + }; } - if (flatCheck !== 'roll' && !isUndetected && !isSuccess) return + if (flatCheck !== "roll" && !isUndetected && !isSuccess) return; } // this part replace formerly getRollContext - const itemOptions = item?.getRollOptions('item') ?? [] - const distance = originToken.distanceTo(targetToken) + const itemOptions = item?.getRollOptions("item") ?? []; + const distance = originToken.distanceTo(targetToken); const perception = perceptionRules(originToken, targetToken, { extraOptions: itemOptions, distance, - }) + }); - let visibility = getVisibility(originToken, targetToken, { perception, affects: 'origin' }) + let visibility = getVisibility(originToken, targetToken, { perception, affects: "origin" }); - if (visibility && getPerception(perception, 'target', 'visibility', 'noff', visibility)) { - visibility = undefined + if (visibility && getPerception(perception, "target", "visibility", "noff", visibility)) { + visibility = undefined; } - let cover = getCover(originToken, targetToken, { perception, affects: 'target', options: itemOptions }) - let coverBonus = undefined + let cover = getCover(originToken, targetToken, { + perception, + affects: "target", + options: itemOptions, + }); + let coverBonus = undefined; if (cover) { - let ac = getPerception(perception, 'target', 'cover', 'ac', cover)?.first() - if (ac != null) ac = Math.clamped(asNumberOnly(ac), 0, 4) - if (ac === 0) cover = undefined - else if (ac) coverBonus = ac + let ac = getPerception(perception, "target", "cover", "ac", cover)?.first(); + if (ac != null) ac = Math.clamped(asNumberOnly(ac), 0, 4); + if (ac === 0) cover = undefined; + else if (ac) coverBonus = ac; } - const overrideVisibility = VISIBILITY_VALUES[visibility] > VISIBILITY_VALUES.concealed - const overrideCover = COVER_VALUES[cover] > COVER_VALUES.none + const overrideVisibility = VISIBILITY_VALUES[visibility] > VISIBILITY_VALUES.concealed; + const overrideCover = COVER_VALUES[cover] > COVER_VALUES.none; if (overrideCover || overrideVisibility) { - const items = deepClone(targetActor._source.items) + const items = deepClone(targetActor._source.items); if (overrideCover) { - const source = createCoverSource(cover, coverBonus) - items.push(source) + const source = createCoverSource(cover, coverBonus); + items.push(source); } if (overrideVisibility) { - const source = createFlatFootedSource(visibility) - items.push(source) + const source = createFlatFootedSource(visibility); + items.push(source); } - target.actor = targetActor.clone({ items }, { keepId: true }) + target.actor = targetActor.clone({ items }, { keepId: true }); - const dc = context.dc - if (overrideCover && dc?.slug) { - const statistic = target.actor.getStatistic(dc.slug)?.dc + const dc = context.dc; + if (dc?.slug) { + const statistic = target.actor.getStatistic(dc.slug)?.dc; if (statistic) { - dc.value = statistic.value - dc.statistic = statistic + dc.value = statistic.value; + dc.statistic = statistic; } } } - return wrapped(...args) - } else if (context.options.has('action:hide')) { - setProperty(context, 'pf2ePerception.selected', game.user.targets.ids) + return wrapped(...args); + } else if (context.options.has("action:hide")) { + setProperty(context, "pf2ePerception.selected", game.user.targets.ids); // } else if (context.options.has('action:sneak')) { // context.selected = game.user.targets.ids - } else if (context.options.has('action:create-a-diversion')) { - setProperty(context, 'pf2ePerception.selected', game.user.targets.ids) - } else if (context.options.has('action:seek')) { - const highlighted = getSeekTemplateTokens(originToken) - const tokens = highlighted ?? Array.from(game.user.targets) + } else if (context.options.has("action:create-a-diversion")) { + setProperty(context, "pf2ePerception.selected", game.user.targets.ids); + } else if (context.options.has("action:seek")) { + const highlighted = getSeekTemplateTokens(originToken); + const tokens = highlighted ?? Array.from(game.user.targets); const selected = validateTokens(originToken, tokens) - .filter(t => !t.document.hidden) - .map(t => t.id) + .filter((t) => !t.document.hidden) + .map((t) => t.id); - setProperty(context, 'pf2ePerception.selected', selected) - setProperty(context, 'pf2ePerception.fromTemplate', !!highlighted) + setProperty(context, "pf2ePerception.selected", selected); + setProperty(context, "pf2ePerception.fromTemplate", !!highlighted); } - return wrapped(...args) + return wrapped(...args); } export function renderCheckModifiersDialog(dialog, html) { - const { createMessage = 'true', type, token, target, isReroll, options, dc } = dialog.context - const originToken = token - const targetToken = target?.token - const targetActor = target?.actor - - if (isReroll || !createMessage || !originToken || !targetToken || !targetActor || !attackCheckRoll.includes(type)) return + const { createMessage = "true", type, token, target, isReroll, options, dc } = dialog.context; + const originToken = token; + const targetToken = target?.token; + const targetActor = target?.actor; + + if ( + isReroll || + !createMessage || + !originToken || + !targetToken || + !targetActor || + !attackCheckRoll.includes(type) + ) + return; - const coverEffect = getCoverEffect(targetActor) + const coverEffect = getCoverEffect(targetActor); const currentCover = coverEffect - ? findChoiceSetRule(coverEffect)?.selection.level ?? getFlag(coverEffect, 'level') - : undefined - let coverOverride = dialog[MODULE_ID]?.coverOverride ?? currentCover + ? findChoiceSetRule(coverEffect)?.selection.level ?? getFlag(coverEffect, "level") + : undefined; + let coverOverride = dialog[MODULE_ID]?.coverOverride ?? currentCover; let template = `