diff --git a/README.md b/README.md index 2988080..50a151c 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,6 @@ # PF2E Persistent Damage -![Foundry Core Compatible Version](https://img.shields.io/badge/dynamic/json.svg?url=https%3A%2F%2Fraw.githubusercontent.com%2FCarlosFdez%2Fpf2e-persistent-damage%2Fmaster%2Fsrc%2Fmodule.json&label=Foundry%20Version&query=$.compatibleCoreVersion&colorB=orange) -Small module to keep track of persistent damage for pathfinder 2E using the effects system. Accessed via macros in the compendium, dragging and dropping certain inline rolls, and the condition hud. If certain settings are enabled, they will be automatically calculated and/or removed when the actor's turn has finished. - -Derived from the condition setter macro from mothringer. If you're feeling generous, you can send something through [Paypal](https://paypal.me/carlosfernandez1779?locale.x=en_US) if you want. +Small module that kept track of Persistent Damage before the feature was built into the core system. Unless you're running an older version of the Pathfinder system, this module can now be removed. ## How to Install diff --git a/module.json b/module.json index da8b3a8..73c8c5b 100644 --- a/module.json +++ b/module.json @@ -7,7 +7,7 @@ "name": "Supe" } ], - "version": "0.11.2", + "version": "1.0.0", "compatibility": { "minimum": "10", "verified": "10.291" @@ -18,39 +18,13 @@ { "id": "pf2e", "compatibility": { - "minimum": "4.6.0" + "minimum": "4.6.6" } } ] }, "esmodules": ["./index.js"], - "styles": ["./styles/styles.css"], - "languages": [ - { - "lang": "en", - "name": "English", - "path": "lang/en.json" - } - ], - "packs": [ - { - "name": "pf2e-persistent-damage-macros", - "label": "PF2E Persistent Damage Macros", - "system": "pf2e", - "path": "packs/pf2e-pd-macros.db", - "type": "Macro", - "module": "pf2e-persistent-damage" - }, - { - "name": "pf2e-persistent-damage-effects", - "label": "PF2E Persistent Damage Effects", - "system": "pf2e", - "path": "packs/pf2e-pd-effects.db", - "type": "Item", - "module": "pf2e-persistent-damage" - } - ], "url": "https://github.com/CarlosFdez/pf2e-persistent-damage", "manifest": "https://github.com/CarlosFdez/pf2e-persistent-damage/releases/latest/download/module.json", - "download": "https://github.com/CarlosFdez/pf2e-persistent-damage/releases/download/v0.11.2/module.zip" + "download": "https://github.com/CarlosFdez/pf2e-persistent-damage/releases/download/v1.0.0/module.zip" } diff --git a/src/index.ts b/src/index.ts index 7778863..ca1e133 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,51 +1,5 @@ -import { ChatMessagePF2e } from "@module/chat-message"; -import { DamageType } from "@module/system/damage"; -import { PersistentDamagePF2e } from "./module/pf2e-persistent-damage"; -import { registerSettings } from "./module/settings"; - -// will be extracted by webpack -import "./styles/styles.scss"; - -Hooks.on("init", () => { - registerSettings(); - window.PF2EPersistentDamage = new PersistentDamagePF2e(); -}); - -Hooks.on("renderChatMessage", async (message: ChatMessagePF2e, $html: JQuery) => { - const roll: any = message.rolls[0]; - if (!roll) return; - - if ("evaluatePersistent" in roll.options) { - const damageType = roll.instances[0].type as DamageType; - const template = await renderTemplate( - "modules/pf2e-persistent-damage/templates/chat/recover-persistent-button.html", - {}, - ); - - $html.find(".message-content").append(template); - $html - .find(".pf2e-pd-card button[data-action=check][data-check=flat]") - .on("click", (evt) => { - evt.preventDefault(); - - // Get the Actor from a synthetic Token - const actorOrToken = message.token ?? message.actor; - if (actorOrToken) { - PF2EPersistentDamage.rollRecoveryCheck(actorOrToken, damageType); - } - }); - } else if ("instances" in roll && roll.instances.some((i) => i.persistent)) { - const template = await renderTemplate("modules/pf2e-persistent-damage/templates/chat/apply-persistent-button.html"); - $html.find(".message-content").append(template); - - $html.find(".pf2e-pd-card button").on("click", (evt) => { - evt.preventDefault(); - const conditions = PF2EPersistentDamage.getPersistentDamageFromMessage(message); - - const tokens = canvas.tokens.controlled.filter((token) => token.actor); - for (const token of tokens) { - token.actor?.createEmbeddedDocuments("Item", conditions); - } - }) - } +Hooks.on("ready", () => { + ui.notifications.error("pf2e Persistent Damage is now built into the system and the module can be now removed", { + permanent: true, + }); }); diff --git a/src/lang/en.json b/src/lang/en.json deleted file mode 100644 index a938b9a..0000000 --- a/src/lang/en.json +++ /dev/null @@ -1,33 +0,0 @@ -{ - "PF2E-PD.SETTINGS": { - "AutoRecover": { - "name": "Auto D20 Recovery Check?", - "hint": "Should the D20 flat check for removal be done as part of the message, or separately?", - "option1": "Always Auto Recover", - "option2": "NPCs only", - "option3": "Never" - }, - "AutoResolve": { - "name": "Auto resolve?", - "hint": "Automatically remove upon successful flat check?" - } - }, - - "PF2E-PD.Notification": { - "Created": "Added persistent {damageType} damage", - "MissingActor": "Token(s) must be selected to apply persistent damage", - "Overwritten": "Existing persistent damage effect has been overwritten", - "NotOverwritten": "Did not apply new persistent damage effect due to being weaker" - }, - - "PF2E-PD.Button.SelectTarget": "Click to select the afflicted token.", - - "PF2E-PD.Migrating": "Applying migration for PF2E Persistent Damage", - "PF2E-PD.Migrated": "Applied PF2E Persistent Damage migration up to version {version}", - "PF2E-PD.PersistentDamageLabel": "Formula", - "PF2E-PD.PersistentDCLabel": "DC", - - "PF2E-PD.FastHealing": "Fast Healing", - "PF2E-PD.Regeneration": "Regeneration", - "PF2E-PD.HealingProcess": "Received healing ({sources})" -} diff --git a/src/module/pf2e-persistent-damage.ts b/src/module/pf2e-persistent-damage.ts deleted file mode 100644 index 5a837d1..0000000 --- a/src/module/pf2e-persistent-damage.ts +++ /dev/null @@ -1,152 +0,0 @@ -import type { ActorPF2e } from "@actor"; -import { ConditionSource } from "@item/data"; -import { TokenPF2e } from "@module/canvas"; -import { ChatMessagePF2e } from "@module/chat-message"; -import { TokenDocumentPF2e } from "@module/scene"; -import { DamageType } from "@module/system/damage"; -import { MODULE_NAME } from "./settings"; - -type TokenOrActor = TokenDocumentPF2e | TokenPF2e | ActorPF2e; -type TokenOrActorInput = TokenOrActor | TokenOrActor[]; -type ResolvedActor = { token?: TokenDocumentPF2e | null; actor: ActorPF2e }; - -function resolveActor(document: TokenOrActor): ResolvedActor | null { - if (document instanceof Actor) { - return { actor: document, token: document.token }; - } else if (!document.actor) { - ui.notifications.warn("TOKEN.WarningNoActor", { localize: true }); - return null; - } else { - const token = "document" in document ? document.document : document; - return token.actor ? { actor: token.actor, token } : null; - } -} - -/** - * Converts a single token/actor or list of tokens and/or actors into a list of actors. - * Necessary for backwards compatibility, the macros give tokens, but we need to process - * on the actors instead. - * @param documents - * @returns - */ -function resolveActors(documents: TokenOrActorInput): ResolvedActor[] { - const arr = Array.isArray(documents) ? documents : [documents]; - return arr - .map((document): ResolvedActor | null => { - return resolveActor(document); - }) - .filter((arr: ResolvedActor | null): arr is ResolvedActor => !!arr); -} - -/** - * The main class that handles the entire module. - * It handles more than persistent damage, but that's because the scope of the module increased. - * This should probably be renamed. - */ -export class PersistentDamagePF2e { - /** - * Shows a dialog that can be used to add persistent damage effects to selected tokens. - * If actor is given, the dialog will add a single effect to that actor. - * If not given, it can be used to add effects to selected tokens. - */ - async showDialog({ actor }: { actor?: ActorPF2e } = {}) { - const actors = actor ? [actor] : canvas.tokens.controlled?.map((t) => t.actor); - await game.pf2e.gm.editPersistent({ actors }); - } - - /** Returns all conditions the message should apply involving persistent damage */ - getPersistentDamageFromMessage(message: ChatMessagePF2e): DeepPartial[] { - const roll: any = message.rolls[0]; - const instances = roll.instances.filter((i) => i.persistent); - return instances.map((instance) => { - const damageType = instance.type; - const formula = instance.head.expression; - const dc = 15; // from a message, the dc is always 15 - - return { - type: "condition", - name: "Persistent Damage", - system: { - slug: "persistent-damage", - removable: true, - persistent: { - damageType, formula, dc - } - } - } - }); - } - - /** - * Removes persistent damage effects of a certain type from an actor - * @param actor - * @param type - * @param value - * @returns - */ - async removePersistentDamage(actor: ActorPF2e, type: DamageType) { - const effects = actor.items.filter((i) => i.flags.persistent?.damageType === type); - await actor?.deleteEmbeddedDocuments( - "Item", - effects.map((i) => i.id), - ); - } - - getPersistentDamage(actor: ActorPF2e, type: DamageType) { - return actor.items.find((i) => i.flags.persistent?.damageType === type); - } - - /** - * Creates a new message to perform a recover check for the given token - * @param token - * @param itemId - * @returns - */ - async rollRecoveryCheck(actorOrToken: ActorPF2e | TokenDocumentPF2e, damageType: DamageType) { - const { actor, token } = resolveActor(actorOrToken) ?? { actor: null }; - if (!actor) return; - - const condition = actor.getCondition(`persistent-damage-${damageType}`); - if (!condition?.system.persistent) { - ui.notifications.warn(`No persistent ${damageType} damage exists on the actor`); - return; - } - - const roll = await new Roll("1d20").evaluate({ async: true }); - const success = roll.total >= condition.system.persistent.dc; - const typeName = game.i18n.localize(CONFIG.PF2E.damageTypes[damageType]); - const templatePath = "modules/pf2e-persistent-damage/templates/chat/recover-persistent-card.html"; - const message = await roll.toMessage({ - speaker: ChatMessage.getSpeaker({ actor: actor, token: token }), - flavor: await renderTemplate(templatePath, { data: condition.system.persistent, typeName, success }), - }); - - // Auto-remove the condition if enabled and it passes the DC - if (success && game.settings.get(MODULE_NAME, "auto-resolve")) { - await actor.decreaseCondition(condition); - } - - return message; - } - - /** - * Checks for persistent damage effects on a token, and rolls damage and flat checks - * for each one. - * @param token one or more tokens to apply persistent damage to - */ - async processPersistentDamage(tokensOrActors: TokenOrActorInput): Promise { - const messages = []; - for (const { actor, token } of resolveActors(tokensOrActors)) { - const persistentDamageConditions = actor.itemTypes.condition.filter((c) => c.slug === "persistent-damage"); - if (!persistentDamageConditions) { - continue; - } - - for (const condition of persistentDamageConditions) { - await condition.onEndTurn({ token }); - } - } - - return messages; - } -} diff --git a/src/module/settings.ts b/src/module/settings.ts deleted file mode 100644 index 8a0128c..0000000 --- a/src/module/settings.ts +++ /dev/null @@ -1,68 +0,0 @@ -export const MODULE_NAME = "pf2e-persistent-damage"; - -function getVersion(): string | null { - const module = game.modules.get(MODULE_NAME); - if (module?.active) { - return (module as any).version; - } - return null; -} - -export enum AutoRecoverMode { - Always = 1, - NPCOnly = 2, - Never = 3, -} - -export enum RollHideMode { - Never = 1, - Normal = 2, - Always = 3, -} - -declare global { - interface ClientSettings { - get(name: typeof MODULE_NAME, key: "auto-roll"): boolean; - get(name: typeof MODULE_NAME, key: "auto-recover"): AutoRecoverMode; - get(name: typeof MODULE_NAME, key: "auto-resolve"): boolean; - get(name: typeof MODULE_NAME, key: "hide-rolls"): RollHideMode; - } -} - -/** - * Initializes settings. Must be called only once. - */ -export function registerSettings() { - // Special non-config flag to handle migrations - game.settings.register(MODULE_NAME, "migration", { - name: "Migration Version", - hint: "Used to perform migrations", - config: false, - default: { version: getVersion() }, - scope: "world", - type: Object, - }); - - game.settings.register(MODULE_NAME, "auto-recover", { - name: game.i18n.localize("PF2E-PD.SETTINGS.AutoRecover.name"), - hint: game.i18n.localize("PF2E-PD.SETTINGS.AutoRecover.hint"), - scope: "world", - config: true, - type: Number, - choices: { - [AutoRecoverMode.Always]: game.i18n.localize("PF2E-PD.SETTINGS.AutoRecover.option1"), - [AutoRecoverMode.NPCOnly]: game.i18n.localize("PF2E-PD.SETTINGS.AutoRecover.option2"), - [AutoRecoverMode.Never]: game.i18n.localize("PF2E-PD.SETTINGS.AutoRecover.option3"), - }, - default: AutoRecoverMode.NPCOnly, - }); - - game.settings.register(MODULE_NAME, "auto-resolve", { - name: game.i18n.localize("PF2E-PD.SETTINGS.AutoResolve.name"), - hint: game.i18n.localize("PF2E-PD.SETTINGS.AutoResolve.hint"), - scope: "world", - config: true, - type: Boolean, - default: true, - }); -} diff --git a/src/module/utils.ts b/src/module/utils.ts deleted file mode 100644 index ea48805..0000000 --- a/src/module/utils.ts +++ /dev/null @@ -1,12 +0,0 @@ -/** - * Calculates the average expected result for a roll. - * @param formula - * @returns - */ -export function calculateRoll(formula: string) { - if (!formula) return { max: 0, min: 0, average: 0 }; - const max = new Roll(formula).evaluate({ maximize: true }).total; - const min = new Roll(formula).evaluate({ minimize: true }).total; - const average = (max - min) / 2 + min; - return { min, average, max }; -} diff --git a/src/styles/styles.scss b/src/styles/styles.scss deleted file mode 100644 index 1034ed7..0000000 --- a/src/styles/styles.scss +++ /dev/null @@ -1,52 +0,0 @@ -.pf2e-pd-card { - header.card-header { - position: relative; - padding: 3px 0; - border-top: 2px groove #FFF; - border-bottom: 2px groove #FFF; - } - - header.card-header img { - flex: 0 0 36px; - margin-right: 5px; - } - - header.card-header h3 { - flex: 1; - margin: 0; - line-height: 36px; - font-family: "Modesto Condensed", "Palatino Linotype", serif; - font-size: 20px; - font-weight: 700; - color: #4b4a44; - } - - header.card-header h3:hover { - color: #111; - text-shadow: 0 0 10px red; - } - - .card-content, .persistent-header { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: center; - margin: 4px 0; - } - - .flat-check-success { - color: #005e00; - } - - .flat-check-failure { - color: #5e0000; - } - - .card-buttons button { - line-height: 2em; - } - - .apply-condition { - margin-top: 3px; - } -} diff --git a/src/templates/chat/apply-persistent-button.html b/src/templates/chat/apply-persistent-button.html deleted file mode 100644 index 03fa392..0000000 --- a/src/templates/chat/apply-persistent-button.html +++ /dev/null @@ -1,5 +0,0 @@ -
-
- -
-
diff --git a/src/templates/chat/recover-persistent-button.html b/src/templates/chat/recover-persistent-button.html deleted file mode 100644 index cd35573..0000000 --- a/src/templates/chat/recover-persistent-button.html +++ /dev/null @@ -1,17 +0,0 @@ -
-
- {{#if autoCheck}} -
- DC {{data.dc}} {{{inlineCheck}}} - - {{#if success}} - Success - {{else}} - Failure - {{/if}} -
- {{else}} -
- -
- {{/if}} -
diff --git a/src/templates/chat/recover-persistent-card.html b/src/templates/chat/recover-persistent-card.html deleted file mode 100644 index 4f3d50c..0000000 --- a/src/templates/chat/recover-persistent-card.html +++ /dev/null @@ -1,10 +0,0 @@ -
- Recovery Check: Persistent {{typeName}} DC{{data.dc}} -
- {{#if success}} - Success - {{else}} - Failure - {{/if}} -
-
diff --git a/webpack.config.ts b/webpack.config.ts index f88d229..4410d33 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -16,7 +16,7 @@ const pf2eSystemPath = (() => { })(); const outDir = pf2eSystemPath ?? path.join(__dirname, "dist/"); -console.log(`Destination Folder set to ${outDir}`) +console.log(`Destination Folder set to ${outDir}`); const config: Configuration = { context: __dirname, @@ -68,16 +68,7 @@ const config: Configuration = { BUILD_MODE: JSON.stringify(buildMode), }), new copyWebpackPlugin({ - patterns: [ - { from: "README.md" }, - { from: "module.json" }, - { from: "src/lang", to: 'lang' }, - { from: "src/templates", to: 'templates' }, - ], - }), - new MiniCssExtractPlugin({ - filename: "styles/styles.css", - insert: "head", + patterns: [{ from: "README.md" }, { from: "module.json" }], }), new WebpackBar({}), ],