diff --git a/tokenmagic/gui/apps/autoTemplate/pf2e.js b/tokenmagic/gui/apps/autoTemplate/pf2e.js index 13737f3..c311e9a 100644 --- a/tokenmagic/gui/apps/autoTemplate/pf2e.js +++ b/tokenmagic/gui/apps/autoTemplate/pf2e.js @@ -172,6 +172,62 @@ export class AutoTemplatePF2E extends TemplateSettings { } return template; } + + // In Foundry v14 / PF2e v8 spell areas are placed as Regions instead of MeasuredTemplates. + // flags.pf2e.origin keeps the same shape; flags.pf2e.areaShape carries one of the pf2e + // EFFECT_AREA_SHAPES values which is mapped onto the legacy template type used by the + // auto-template settings (circle/cone/rect/ray). + preCreateRegion(region) { + if (region.flags?.tokenmagic?.options || region.flags?.tokenmagic?.filters) return region; + + const origin = region.flags?.pf2e?.origin; + if (!origin) return region; + + const areaShape = region.flags?.pf2e?.areaShape; + const legacyType = AREA_SHAPE_TO_TEMPLATE_TYPE[areaShape] ?? 'circle'; + + const settings = game.settings.get('tokenmagic', 'autoTemplateSettings'); + const updated = settings.overrides + ? fromOverrides(Object.values(settings.overrides), origin, region) + : false; + if (!updated) { + fromCategoriesForType(settings.categories, origin, region, legacyType); + } + return region; + } +} + +const AREA_SHAPE_TO_TEMPLATE_TYPE = { + burst: 'circle', + emanation: 'circle', + cylinder: 'circle', + cone: 'cone', + cube: 'rect', + square: 'rect', + line: 'ray', +}; + +function fromCategoriesForType(categories = {}, origin, document, type) { + if (!origin.traits?.length) { + return false; + } + + let config, dmgSettings; + for (const trait of origin.traits) { + dmgSettings = categories[trait.toLowerCase()] || {}; + config = dmgSettings[type]; + if (config && config.preset !== emptyPreset) { + break; + } + } + if (!config) { + return false; + } + fromConfig( + foundry.utils.mergeObject(config, { opacity: dmgSettings.opacity, tint: dmgSettings.tint }, true, true), + document + ); + return true; } function fromConfig(config, template) { diff --git a/tokenmagic/module.json b/tokenmagic/module.json index 1d99368..10e88ac 100644 --- a/tokenmagic/module.json +++ b/tokenmagic/module.json @@ -7,7 +7,7 @@ "download": "https://github.com/Feu-Secret/Tokenmagic/releases/download/0.7.6.3/tokenmagic.zip", "compatibility": { "minimum": "13", - "verified": "13.350" + "verified": "14.360" }, "authors": [ { diff --git a/tokenmagic/module/tokenmagic.js b/tokenmagic/module/tokenmagic.js index 3dc637e..689a635 100644 --- a/tokenmagic/module/tokenmagic.js +++ b/tokenmagic/module/tokenmagic.js @@ -2198,6 +2198,89 @@ Hooks.on('preCreateMeasuredTemplate', (document) => { /* -------------------------------------------- */ +// Foundry v14 / PF2e v8 places spell areas as Regions instead of MeasuredTemplates. +// This handler mirrors preCreateMeasuredTemplate for regions: it lets the system +// auto-template object inject tmfx options based on the pf2e origin flag, then +// converts those options into the persisted filter flags used at render time. +Hooks.on('preCreateRegion', (document) => { + if (game.Levels3DPreview?._active) return; + + const templates = TokenMagicSettings.getSystemTemplates(); + if (templates?.enabled) { + templates.preCreateRegion?.(document); + } + + const opt = document.flags?.tokenmagic?.options; + if (!opt) return; + + let tmfxPreset = opt.tmfxPreset; + let tmfxTint = opt.tmfxTint; + let tmfxOpacity = opt.tmfxTextureAlpha; + const hasPreset = !!tmfxPreset; + const hasTint = tmfxTint !== undefined && tmfxTint !== null && tmfxTint !== ''; + + if (hasTint && typeof tmfxTint !== 'number') { + tmfxTint = Color.from(tmfxTint).valueOf(); + } + + let tmfxFiltersData = null; + if (hasPreset) { + const pstSearch = { + name: tmfxPreset, + library: PresetsLibrary.TEMPLATE, + anchorX: 0.5, + anchorY: 0.5, + }; + if (hasTint) pstSearch.color = tmfxTint; + + const preset = Magic.getPreset(pstSearch); + if (preset instanceof Array && preset.length) { + const newFilters = []; + let persist = true; + for (const params of preset) { + if (!params.filterType || !FilterType.hasOwnProperty(params.filterType)) { + persist = false; + break; + } + if (!params.filterId) { + persist = false; + break; + } + if (typeof params.enabled !== 'boolean') params.enabled = true; + params.placeableId = null; + params.filterInternalId = foundry.utils.randomID(); + params.filterOwner = game.data.userId; + params.placeableType = PlaceableType.REGION; + + newFilters.push({ + tmFilters: { + tmFilterId: params.filterId, + tmFilterInternalId: params.filterInternalId, + tmFilterType: params.filterType, + tmFilterOwner: params.filterOwner, + tmParams: params, + }, + }); + } + if (persist) tmfxFiltersData = newFilters; + } + } + + const alpha = typeof tmfxOpacity === 'number' && Number.isFinite(tmfxOpacity) ? tmfxOpacity : 0.5; + + document.updateSource({ + flags: { + tokenmagic: { + filters: tmfxFiltersData, + regionData: { alpha }, + options: null, + }, + }, + }); +}); + +/* -------------------------------------------- */ + Hooks.on('renderBasePlaceableHUD', (hud, form, data, options) => { if (!game.user.isGM || !hud.document._TMFXgetPlaceableType?.()) return;