Skip to content
Merged
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
14 changes: 12 additions & 2 deletions lang/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@
"Title": "Use custom compendium mappings?",
"Label": "Custom Compendium Mappings",
"Hint": "This is useful for things like Battlezoo or Cleric+ modules, or even if you have your own compendium of custom feats."
},
"AutoCreateTempFolder": {
"Name": "Automatically create folders?",
"Hint": "Creates required folders whenever a GM user logs in."
},
"UseTempFolder": {
"Name": "Use temporary folder for imports?",
"Hint": "Imports will go into a temporary folder which you can then move things out of later. This is useful for allowing players to import their own characters without cluttering up the main actor directory with temporary actors created during munching."
}
},
"Dialogs": {
Expand Down Expand Up @@ -95,7 +103,8 @@
},
"Chat": {},
"Notifications": {
"CreateActorPermission": "Pathmuncher requires the CREATE ACTOR permission for your user."
"CreateActorPermission": "Pathmuncher requires the CREATE ACTOR permission for your user.",
"CreateFolderError": "User {userName} lacks permission to create Folder {folderName}. Enable the setting or create it manually."
},
"Labels": {
"Character": "Character details",
Expand Down Expand Up @@ -134,7 +143,8 @@
},
"Folders": {
"Familiar": "Familiars",
"Familiars": "Familiars"
"Familiars": "Familiars",
"PathmuncherTemp": "Pathmuncher Scratchpad"
},
"CompendiumGroups": {
"feats": "Feats",
Expand Down
36 changes: 24 additions & 12 deletions src/app/Pathmuncher.js
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,9 @@ export class Pathmuncher {
}

this.immediateDiveAdd = utils.setting("USE_IMMEDIATE_DEEP_DIVE");

this.tempActorCounter = 1;
this.tempActorFolderName = game.i18n.localize(`${CONSTANTS.FLAG_NAME}.Folders.PathmuncherTemp`);
}

async #loadCompendiumMatchers() {
Expand Down Expand Up @@ -1024,7 +1027,7 @@ export class Pathmuncher {
});
throw err;
} finally {
await Actor.deleteDocuments([tempActor._id]);
await utils.deleteActor(tempActor);
}

logger.debug("Evaluate Choices failed", { choiceSet: cleansedChoiceSet, tempActor, document });
Expand Down Expand Up @@ -1085,7 +1088,7 @@ export class Pathmuncher {
});
throw err;
} finally {
await Actor.deleteDocuments([tempActor._id]);
await utils.deleteActor(tempActor);
}

logger.debug("Evaluate UUID failed", { choiceSet: cleansedRuleEntry, tempActor, document });
Expand Down Expand Up @@ -1142,7 +1145,7 @@ export class Pathmuncher {
});
throw err;
} finally {
await Actor.deleteDocuments([tempActor._id]);
await utils.deleteActor(tempActor);
}
}

Expand Down Expand Up @@ -1176,7 +1179,7 @@ export class Pathmuncher {
});
throw err;
} finally {
await Actor.deleteDocuments([tempActor._id]);
await utils.deleteActor(tempActor);
}
}

Expand Down Expand Up @@ -2643,7 +2646,9 @@ export class Pathmuncher {
removePassedDocuments = false } = {},
) {
const actorData = foundry.utils.mergeObject({ type: "character", flags: { pathmuncher: { temp: true } } }, this.result.character);
actorData.name = `Mr Temp (${this.result.character.name})`;
this.#setTempActorName(actorData);
await this.#setTempActorFolder(actorData);

if (documents.map((d) => d.name.split("(")[0].trim().toLowerCase()).includes("skill training")) {
delete actorData.system.skills;
}
Expand Down Expand Up @@ -2835,6 +2840,19 @@ export class Pathmuncher {
return actor;
}

#setTempActorName(actorData) {
const formattedNum = this.tempActorCounter.toString().padStart(4, "0");
actorData.name = `Mr Temp (${this.result.character.name}) ${formattedNum}`;
this.tempActorCounter += 1;
}

async #setTempActorFolder(actorData) {
if (!utils.setting("USE_TEMP_FOLDER")) return;

let tempActorFolder = await utils.getOrCreateFolder(null, "Actor", this.tempActorFolderName);
actorData.folder = tempActorFolder?.id;
}

async processCharacter() {
if (!this.source) return;
await this.#prepare();
Expand Down Expand Up @@ -3054,12 +3072,6 @@ export class Pathmuncher {
});
}

static async removeTempActors() {
for (const actor of game.actors.filter((a) => foundry.utils.getProperty(a, "flags.pathmuncher.temp") === true)) {
await actor.delete();
}
}

async updateActor() {
await this.#removeDocumentsToBeUpdated();

Expand All @@ -3079,7 +3091,7 @@ export class Pathmuncher {
await this.actor.update(this.result.character);
await this.#createActorEmbeddedDocuments();
await this.#restoreEmbeddedRuleLogic();
await Pathmuncher.removeTempActors();
await utils.removeTempActors();
}

async postImportCheck() {
Expand Down
29 changes: 29 additions & 0 deletions src/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ const CONSTANTS = {
CUSTOM_COMPENDIUM_MAPPINGS: "custom-compendium-mappings",
USE_IMMEDIATE_DEEP_DIVE: "use-immediate-deep-dive",
DISPLAY_TITLE: "display-title",
AUTO_CREATE_TEMP_FOLDER: "auto-create-temp-folder",
USE_TEMP_FOLDER: "use-temp-folder",
ACTIVE_GM: "active-gm",
},

FEAT_PRIORITY: [
Expand Down Expand Up @@ -165,6 +168,25 @@ CONSTANTS.DEFAULT_SETTINGS = {
default: true,
},

[CONSTANTS.SETTINGS.AUTO_CREATE_TEMP_FOLDER]: {
name: `${CONSTANTS.FLAG_NAME}.Settings.AutoCreateTempFolder.Name`,
hint: `${CONSTANTS.FLAG_NAME}.Settings.AutoCreateTempFolder.Hint`,
scope: "world",
config: true,
type: Boolean,
default: false,
onChange: debouncedReload,
},

[CONSTANTS.SETTINGS.USE_TEMP_FOLDER]: {
name: `${CONSTANTS.FLAG_NAME}.Settings.UseTempFolder.Name`,
hint: `${CONSTANTS.FLAG_NAME}.Settings.UseTempFolder.Hint`,
scope: "world",
config: true,
type: Boolean,
default: false,
},

[CONSTANTS.SETTINGS.RESTRICT_TO_TRUSTED]: {
name: `${CONSTANTS.FLAG_NAME}.Settings.RestrictToTrusted.Name`,
hint: `${CONSTANTS.FLAG_NAME}.Settings.RestrictToTrusted.Hint`,
Expand Down Expand Up @@ -216,6 +238,13 @@ CONSTANTS.DEFAULT_SETTINGS = {
default: "WARN",
},

[CONSTANTS.SETTINGS.ACTIVE_GM]: {
scope: "world",
config: false,
type: String,
default: "",
},

};

CONSTANTS.PATH = `modules/${CONSTANTS.MODULE_NAME}`;
Expand Down
14 changes: 14 additions & 0 deletions src/hooks/folder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import CONSTANTS from "../constants.js";
import utils from "../utils.js";

export function autoCreateFolders() {
if (!utils.setting("AUTO_CREATE_TEMP_FOLDER")) return;
if (!game.user.isGM) return;

utils.getOrCreateFolder(null, "Actor", game.i18n.localize(`${CONSTANTS.FLAG_NAME}.Folders.Familiars`));

if (utils.setting("USE_TEMP_FOLDER")) {
utils.getOrCreateFolder(null, "Actor", game.i18n.localize(`${CONSTANTS.FLAG_NAME}.Folders.PathmuncherTemp`));
}

}
11 changes: 11 additions & 0 deletions src/hooks/settings.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { CompendiumSelector } from "../app/CompendiumSelector.js";
import CONSTANTS from "../constants.js";
import utils from "../utils.js";

async function resetSettings() {
for (const [name, data] of Object.entries(CONSTANTS.GET_DEFAULT_SETTINGS())) {
Expand Down Expand Up @@ -36,6 +37,16 @@ class ResetSettingsDialog extends FormApplication {
}
}

export async function processActiveGM() {
// determine if this user is the active gm/first active in current session
if (game.user.isGM) {
const currentGMUser = game.users.get(utils.setting("ACTIVE_GM"));
if ((currentGMUser && !currentGMUser.active) || !currentGMUser) {
await utils.updateSetting("ACTIVE_GM", game.user.id);
}
}
}

export function registerSettings() {
game.settings.registerMenu(CONSTANTS.MODULE_NAME, "resetToDefaults", {
name: `${CONSTANTS.FLAG_NAME}.Settings.Reset.Title`,
Expand Down
12 changes: 10 additions & 2 deletions src/module.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import { registerAPI } from "./hooks/api.js";
import { registerSettings } from "./hooks/settings.js";
import { registerSettings, processActiveGM } from "./hooks/settings.js";
import { registerSheetButton } from "./hooks/sheets.js";
import { autoCreateFolders } from "./hooks/folder.js";
import utils from "./utils.js";

Hooks.once("init", () => {
registerSettings();
});

Hooks.once("ready", () => {
Hooks.once("ready", async () => {
await processActiveGM();
registerSheetButton();
registerAPI();
autoCreateFolders();
// cleanup temp actors on startup, but only for the active GM
if (utils.setting("ACTIVE_GM") === game.user.id) {
await utils.removeTempActors();
}
});
32 changes: 30 additions & 2 deletions src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,20 @@ const utils = {
// if a root folder we want to match the root id for the parent folder
&& (root ? root.id : null) === (f.folder?.id ?? null),
);
// console.warn(`Looking for ${root} ${entityType} ${folderName}`);
// console.warn(folder);
if (folder) return folder;

if (!Folder.canUserCreate(game.user)) {
const errorMsg = game.i18n.format(
`${CONSTANTS.FLAG_NAME}.Notifications.CreateFolderError`,
{
userName: game.user.name,
folderName: folderName,
},
);
ui.notifications.error(errorMsg);
throw new Error(errorMsg);
}

folder = await Folder.create(
{
name: folderName,
Expand Down Expand Up @@ -96,6 +107,23 @@ const utils = {
return (foundry.utils.isNewerVersion("5.9.0", game.version) && game.settings.get("pf2e", "ancestryParagonVariant"));
},

async deleteActor(actor) {
if (actor.canUserModify(game.user, "delete")) {
await Actor.deleteDocuments([actor._id]);
}
},

async removeTempActors() {
const actorIds = game.actors
.filter((a) =>
foundry.utils.getProperty(a, "flags.pathmuncher.temp") === true
&& a.canUserModify(game.user, "delete"),
)
.map((a) => a._id);
if (actorIds.length === 0) return;
await Actor.deleteDocuments(actorIds);
},

};


Expand Down