From e9b08843b60486263c03f8870053fadddf4985fd Mon Sep 17 00:00:00 2001 From: Mike Bloy Date: Mon, 9 Mar 2026 12:17:03 -0500 Subject: [PATCH] add folder option for non-transient power actors --- CHANGELOG.md | 7 ++++ src/lang/en.json | 4 +- src/module/globals.js | 1 + src/module/powers/basePowers.js | 65 ++++++++++++++++++++++++++++----- src/module/settings.js | 17 +++++++++ 5 files changed, 82 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 81f33c9..02790e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [4.2.2] +### Added + +- Added a setting for a Power Actors folder along side the compendium folder. + Powers that require non-transient actors (eg: PC companions or familiars) can + use a duplicate folder structure under the Power Actors folder to define an + actor that will stick around. + ### Changed - Minimum SWADE version is now 5.2.0 diff --git a/src/lang/en.json b/src/lang/en.json index 2c391d6..327aa45 100644 --- a/src/lang/en.json +++ b/src/lang/en.json @@ -1,6 +1,6 @@ { "mbhelpers.settings.powerActorsCompendiumName": "Power Actors Compendium", "mbhelpers.settings.powerActorsCompendiumHint": "Identifier of a compendium that holds all the actor helpers for powers. See the documentation for details on the structure of this compendium.", - "mbhelpers.settings.powersJournalName": "Powers Journal", - "mbhelpers.settings.powersJournalHint": "UUID of a helper journal for actor-based powers (summonables and morphables)." + "mbhelpers.settings.powerActorsFolderName": "Power Actors Library Folder", + "mbhelpers.settings.powerActorsFolderHint": "Name of a Sidebar actor folder that holds all the actor helpers for powers. See the documentation for details on the structure of this folder. Note that the compendium is preferred, and the folder should be used for things like named animal companions or familiars." } diff --git a/src/module/globals.js b/src/module/globals.js index 4a23244..1122ce0 100644 --- a/src/module/globals.js +++ b/src/module/globals.js @@ -2,6 +2,7 @@ export const moduleName = 'swade-mb-helpers'; export const settingKeys = { powerActorsCompendium: 'powerActorsCompendium', + powerActorsFolder: 'powerActorsFolder', }; export function log(...args) { diff --git a/src/module/powers/basePowers.js b/src/module/powers/basePowers.js index 97a28c6..3a3cc58 100644 --- a/src/module/powers/basePowers.js +++ b/src/module/powers/basePowers.js @@ -808,10 +808,42 @@ export class ActorFolderEffect extends PowerEffect { : undefined; } + static get actorLibraryFolderName() { + const folderName = moduleHelpers.getSetting(settingKeys.powerActorsFolder); + return folderName; + } + get actorFolder() { return `${this.actorFolderBase}/${this.name}`; } + static getLibraryFolderByPath(path) { + const baseFolder = ActorFolderEffect.actorLibraryFolderName; + if (!baseFolder) { + return undefined; + } + const sep = path[0] == '/' ? '' : '/'; + const fullPath = `${baseFolder}${sep}${path}`; + const names = fullPath.split('/'); + if (names[0] === '') { + names.shift(); + } + let name = names.shift(); + let folder = game.folders.filter((f) => f.type === 'Actor' && !f.folder).find((f) => f.name === name); + if (!folder) { + return undefined; + } + while (names.length > 0) { + name = names.shift(); + folder = folder.children.find((c) => c.folder.name === name); + if (!folder) { + return undefined; + } + folder = folder.folder; + } + return folder; + } + static getPackFolderByPath(path) { const names = path.split('/'); if (names[0] === '') { @@ -838,7 +870,7 @@ export class ActorFolderEffect extends PowerEffect { return folder; } - static getPackActorsInFolder(inFolder) { + static getActorsInFolder(inFolder) { const prefixStack = ['']; const actors = {}; const folderStack = [inFolder]; @@ -858,7 +890,8 @@ export class ActorFolderEffect extends PowerEffect { } prepFolders() { - const folders = []; + const packFolders = []; + const libFolders = []; const folderNames = [ this.actorFolder, `${this.actorFolder} - Default`, @@ -869,23 +902,31 @@ export class ActorFolderEffect extends PowerEffect { `${this.actorFolder}/${this.source.actor.name}`, ]; for (const folderName of folderNames) { - const folder = ActorFolderEffect.getPackFolderByPath(folderName); - if (folder) { + const packFolder = ActorFolderEffect.getPackFolderByPath(folderName); + if (packFolder) { log(`Found actor folder ${folderName}`); - folders.push(folder); + packFolders.push(packFolder); + } + const libFolder = ActorFolderEffect.getLibraryFolderByPath(folderName); + if (libFolder) { + log(`Found library actor folder ${folderName}`); + libFolders.push(libFolder); } } - if (folders.length > 1) { - folders.shift(); + if (packFolders.length > 1) { + packFolders.shift(); } - return folders; + if (libFolders.length > 1) { + libFolders.shift(); + } + return [...packFolders, ...libFolders]; } prepActors() { const folders = this.prepFolders(); const actors = {}; for (const folder of folders) { - const folderActors = ActorFolderEffect.getPackActorsInFolder(folder); + const folderActors = ActorFolderEffect.getActorsInFolder(folder); for (const key in folderActors) { actors[key] = folderActors[key]; } @@ -999,7 +1040,11 @@ export class ActorFolderEffect extends PowerEffect { async parseValues() { await super.parseValues(); this.data.maintId = foundry.utils.randomID(); - this.targetActor = await game.tcal.importTransientActor(this.data.actorId, { preferExisting: true }); + if (this.data.actorId.startsWith('Compendium')) { + this.targetActor = await game.tcal.importTransientActor(this.data.actorId, { preferExisting: true }); + } else { + this.targetActor = await foundry.utils.fromUuid(this.data.actorId); + } this.targetTokenDoc = await this.targetActor.getTokenDocument(); const perm = CONST?.DOCUMENT_PERMISSION_LEVELS?.OWNER ?? CONST?.DOCUMENT_OWNERSHIP_LEVELS?.OWNER; const sourceUpdates = { diff --git a/src/module/settings.js b/src/module/settings.js index 4ee6e4d..ef666cb 100644 --- a/src/module/settings.js +++ b/src/module/settings.js @@ -17,4 +17,21 @@ export function registerSettings() { requiresReload: false, type: String, }); + + const folders = game.folders.filter((f) => f.type === 'Actor' && !f.folder); + const folderChoices = {}; + for (const folder of folders) { + folderChoices[folder.name] = folder.name; + } + + moduleHelpers.registerSetting(settingKeys.powerActorsFolder, { + name: 'mbhelpers.settings.powerActorsFolderName', + hint: 'mbhelpers.settings.powerActorsFolderHint', + scope: 'world', + config: true, + choices: folderChoices, + requiresReload: false, + default: 'Power Actors', + type: String, + }); }