Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions tokenmagic/gui/apps/autoTemplate/pf2e.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion tokenmagic/module.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
{
Expand Down
83 changes: 83 additions & 0 deletions tokenmagic/module/tokenmagic.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down