diff --git a/daggerheart.mjs b/daggerheart.mjs index 4e88c148..f1d8c67a 100644 --- a/daggerheart.mjs +++ b/daggerheart.mjs @@ -16,10 +16,9 @@ import { settingsRegistration, socketRegistration } from './module/systemRegistration/_module.mjs'; -import { placeables, DhTokenLayer } from './module/canvas/_module.mjs'; +import { placeables } from './module/canvas/_module.mjs'; import './node_modules/@yaireo/tagify/dist/tagify.css'; import TemplateManager from './module/documents/templateManager.mjs'; -import TokenManager from './module/documents/tokenManager.mjs'; CONFIG.DH = SYSTEM; CONFIG.TextEditor.enrichers.push(...enricherConfig); @@ -52,8 +51,6 @@ CONFIG.ChatMessage.template = 'systems/daggerheart/templates/ui/chat/chat-messag CONFIG.Canvas.rulerClass = placeables.DhRuler; CONFIG.Canvas.layers.templates.layerClass = placeables.DhTemplateLayer; -CONFIG.Canvas.layers.tokens.layerClass = DhTokenLayer; - CONFIG.MeasuredTemplate.objectClass = placeables.DhMeasuredTemplate; CONFIG.Scene.documentClass = documents.DhScene; @@ -65,7 +62,6 @@ CONFIG.Token.rulerClass = placeables.DhTokenRuler; CONFIG.Token.hudClass = applications.hud.DHTokenHUD; CONFIG.ui.combat = applications.ui.DhCombatTracker; -CONFIG.ui.nav = applications.ui.DhSceneNavigation; CONFIG.ui.chat = applications.ui.DhChatLog; CONFIG.ui.effectsDisplay = applications.ui.DhEffectsDisplay; CONFIG.ui.hotbar = applications.ui.DhHotbar; @@ -77,7 +73,6 @@ CONFIG.ui.countdowns = applications.ui.DhCountdowns; CONFIG.ux.ContextMenu = applications.ux.DHContextMenu; CONFIG.ux.TooltipManager = documents.DhTooltipManager; CONFIG.ux.TemplateManager = new TemplateManager(); -CONFIG.ux.TokenManager = new TokenManager(); Hooks.once('init', () => { game.system.api = { @@ -89,8 +84,6 @@ Hooks.once('init', () => { fields }; - game.system.registeredTriggers = new RegisteredTriggers(); - const { DocumentSheetConfig } = foundry.applications.apps; DocumentSheetConfig.unregisterSheet(TokenDocument, 'core', foundry.applications.sheets.TokenConfig); DocumentSheetConfig.registerSheet(TokenDocument, SYSTEM.id, applications.sheetConfigs.DhTokenConfig, { @@ -309,7 +302,7 @@ Hooks.on('chatMessage', (_, message) => { target, difficulty, title, - label: game.i18n.localize('DAGGERHEART.GENERAL.dualityRoll'), + label: 'test', actionType: null, advantage }); @@ -323,7 +316,7 @@ const updateActorsRangeDependentEffects = async token => { CONFIG.DH.SETTINGS.gameSettings.variantRules ).rangeMeasurement; - for (let effect of token.actor?.allApplicableEffects() ?? []) { + for (let effect of token.actor.allApplicableEffects()) { if (!effect.system.rangeDependence?.enabled) continue; const { target, range, type } = effect.system.rangeDependence; @@ -337,14 +330,14 @@ const updateActorsRangeDependentEffects = async token => { break; } - // Get required distance and special case 5 feet to test adjacency - const required = rangeMeasurement[range]; + const distanceBetween = canvas.grid.measurePath([ + userTarget.document.movement.destination, + token.movement.destination + ]).distance; + const distance = rangeMeasurement[range]; + const reverse = type === CONFIG.DH.GENERAL.rangeInclusion.outsideRange.id; - const inRange = - required === 5 - ? userTarget.isAdjacentWith(token.object) - : userTarget.distanceTo(token.object) <= required; - if (reverse ? inRange : !inRange) { + if (reverse ? distanceBetween <= distance : distanceBetween > distance) { enabledEffect = false; break; } @@ -358,17 +351,16 @@ const updateAllRangeDependentEffects = async () => { const effectsAutomation = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).effects; if (!effectsAutomation.rangeDependent) return; - const tokens = canvas.scene?.tokens; - if (!tokens) return; - + // Only consider tokens on the active scene + const tokens = game.scenes.find(x => x.active).tokens; if (game.user.character) { // The character updates their character's token. There can be only one token. const characterToken = tokens.find(x => x.actor === game.user.character); updateActorsRangeDependentEffects(characterToken); - } else if (game.user.isActiveGM) { + } else if (game.user.isGM) { // The GM is responsible for all other tokens. const playerCharacters = game.users.players.filter(x => x.active).map(x => x.character); - for (const token of tokens.filter(x => !playerCharacters.includes(x.actor))) { + for (let token of tokens.filter(x => !playerCharacters.includes(x.actor))) { updateActorsRangeDependentEffects(token); } } @@ -376,62 +368,13 @@ const updateAllRangeDependentEffects = async () => { const debouncedRangeEffectCall = foundry.utils.debounce(updateAllRangeDependentEffects, 50); -Hooks.on('targetToken', () => { +Hooks.on('targetToken', async (user, token, targeted) => { debouncedRangeEffectCall(); }); -Hooks.on('refreshToken', (_, options) => { - if (options.refreshPosition) { - debouncedRangeEffectCall(); - } +Hooks.on('moveToken', async (movedToken, data) => { + debouncedRangeEffectCall(); }); Hooks.on('renderCompendiumDirectory', (app, html) => applications.ui.ItemBrowser.injectSidebarButton(html)); Hooks.on('renderDocumentDirectory', (app, html) => applications.ui.ItemBrowser.injectSidebarButton(html)); - -class RegisteredTriggers extends Map { - constructor() { - super(); - } - - async registerTriggers(trigger, actor, triggeringActorType, uuid, commands) { - const existingTrigger = this.get(trigger); - if (!existingTrigger) this.set(trigger, new Map()); - - this.get(trigger).set(uuid, { actor, triggeringActorType, commands }); - } - - async runTrigger(trigger, currentActor, ...args) { - const updates = []; - const triggerSettings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).triggers; - if (!triggerSettings.enabled) return updates; - - const dualityTrigger = this.get(trigger); - if (dualityTrigger) { - for (let { actor, triggeringActorType, commands } of dualityTrigger.values()) { - const triggerData = CONFIG.DH.TRIGGER.triggers[trigger]; - if (triggerData.usesActor && triggeringActorType !== 'any') { - if (triggeringActorType === 'self' && currentActor?.uuid !== actor) continue; - else if (triggeringActorType === 'other' && currentActor?.uuid === actor) continue; - } - - for (let command of commands) { - try { - const result = await command(...args); - if (result?.updates?.length) updates.push(...result.updates); - } catch (_) { - const triggerName = game.i18n.localize(triggerData.label); - ui.notifications.error( - game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerError', { - trigger: triggerName, - actor: currentActor?.name - }) - ); - } - } - } - } - - return updates; - } -} diff --git a/lang/en.json b/lang/en.json index 14915a81..828b2303 100755 --- a/lang/en.json +++ b/lang/en.json @@ -69,11 +69,7 @@ }, "summon": { "name": "Summon", - "tooltip": "Create tokens in the scene.", - "error": "You do not have permission to summon tokens or there is no active scene.", - "invalidDrop": "You can only drop Actor entities to summon.", - "chatMessageTitle": "Test2", - "chatMessageHeaderTitle": "Summoning" + "tooltip": "Create tokens in the scene." } }, "Config": { @@ -94,9 +90,7 @@ "customFormula": "Custom Formula", "formula": "Formula" }, - "displayInChat": "Display in chat", - "deleteTriggerTitle": "Delete Trigger", - "deleteTriggerContent": "Are you sure you want to delete the {trigger} trigger?" + "displayInChat": "Display in chat" }, "RollField": { "diceRolling": { @@ -126,9 +120,6 @@ }, "cost": { "stepTooltip": "+{step} per step" - }, - "summon": { - "dropSummonsHere": "Drop Summons Here" } } }, @@ -203,8 +194,6 @@ "unequip": "Unequip", "useItem": "Use Item" }, - "defaultHopeDice": "Default Hope Dice", - "defaultFearDice": "Default Fear Dice", "disadvantageSources": { "label": "Disadvantage Sources", "hint": "Add single words or short text as reminders and hints of what a character has disadvantage on." @@ -237,7 +226,6 @@ "confirmText": "Would you like to level up your companion {name} by {levelChange} levels at this time? (You can do it manually later)" }, "viewLevelups": "View Levelups", - "viewParty": "View Party", "InvalidOldCharacterImportTitle": "Old Character Import", "InvalidOldCharacterImportText": "Character data exported prior to system version 1.1 will not generate a complete character. Do you wish to continue?", "cancelBeastform": "Cancel Beastform" @@ -338,7 +326,6 @@ "equip": "Equip", "sendToChat": "Send To Chat", "toLoadout": "Send to Loadout", - "recall": "Recall", "toVault": "Send to Vault", "unequip": "Unequip", "useItem": "Use Item" @@ -1155,8 +1142,7 @@ "any": "Any", "friendly": "Friendly", "hostile": "Hostile", - "self": "Self", - "other": "Other" + "self": "Self" }, "TemplateTypes": { "circle": "Circle", @@ -1230,29 +1216,6 @@ } } }, - "Triggers": { - "postDamageReduction": { - "label": "After Damage Reduction" - }, - "preDamageReduction": { - "label": "Before Damage Reduction" - }, - "dualityRoll": { - "label": "Duality Roll" - }, - "fearRoll": { - "label": "Fear Roll" - }, - "triggerTexts": { - "strangePatternsContentTitle": "Matched {nr} times.", - "strangePatternsContentSubTitle": "Increase hope and stress to a total of {nr}.", - "ferocityContent": "Spend 2 Hope to gain {bonus} bonus Evasion until after the next attack against you?", - "ferocityEffectDescription": "Your evasion is increased by {bonus}. This bonus lasts until after the next attack made against you." - }, - "triggerType": "Trigger Type", - "triggeringActorType": "Triggering Actor Type", - "triggerError": "{trigger} trigger failed for {actor}. It's probably configured wrong." - }, "WeaponFeature": { "barrier": { "name": "Barrier", @@ -1837,9 +1800,7 @@ "label": "Long Rest: Bonus Long Rest Moves", "hint": "The number of extra Long Rest Moves the character can take during a Long Rest." } - }, - "target": "Target", - "targetSelf": "Self" + } }, "maxLoadout": { "label": "Max Loadout Cards Bonus" @@ -2090,10 +2051,10 @@ "partyMembers": "Party Members", "projects": "Projects", "types": "Types", + "itemFeatures": "Item Features", "questions": "Questions", "configuration": "Configuration", - "base": "Base", - "triggers": "Triggers" + "base": "Base" }, "Tiers": { "singular": "Tier", @@ -2214,10 +2175,6 @@ "stress": "Stress", "subclasses": "Subclasses", "success": "Success", - "summon": { - "single": "Summon", - "plural": "Summons" - }, "take": "Take", "Target": { "single": "Target", @@ -2284,8 +2241,7 @@ "placeholder": "Using character dimensions", "disabledPlaceholder": "Set by character size", "height": { "label": "Height" }, - "width": { "label": "Width" }, - "scale": { "label": "Token Scale" } + "width": { "label": "Width" } }, "evolved": { "maximumTier": { "label": "Maximum Tier" }, @@ -2334,9 +2290,6 @@ "DomainCard": { "type": "Type", "recallCost": "Recall Cost", - "vaultActive": "Active In Vault", - "loadoutIgnore": "Ignores Loadout Limits", - "domainTouched": "Domain Touched", "foundationTitle": "Foundation", "specializationTitle": "Specialization", "masteryTitle": "Mastery" @@ -2475,12 +2428,6 @@ "hint": "Automatically apply effects. Targets must be selected before the action is made and Reaction Roll Automation must be different than Never. Bypass users permissions." } }, - "triggers": { - "enabled": { - "label": "Enabled", - "hint": "Advanced automation such as triggering a popup for a wizard's Strange Patterns" - } - }, "summaryMessages": { "label": "Summary Messages" } @@ -2490,9 +2437,6 @@ }, "roll": { "title": "Actions" - }, - "trigger": { - "title": "Triggers" } }, "Homebrew": { @@ -2620,9 +2564,7 @@ } }, "disabledText": "Daggerheart Measurements are disabled in System Settings - Variant Rules", - "rangeMeasurement": "Range Measurement", - "sceneEnvironments": "Scene Environments", - "dragEnvironmentHere": "Drag environments here" + "rangeMeasurement": "Range Measurement" } }, "UI": { @@ -2835,10 +2777,7 @@ "gmRequired": "This action requires an online GM", "gmOnly": "This can only be accessed by the GM", "noActorOwnership": "You do not have permissions for this character", - "documentIsMissing": "The {documentType} is missing from the world.", - "tokenActorMissing": "{name} is missing an Actor", - "tokenActorsMissing": "[{names}] missing Actors", - "domainTouchRequirement": "This domain card requires {nr} {domain} cards in the loadout to be used" + "documentIsMissing": "The {documentType} is missing from the world." }, "Sidebar": { "actorDirectory": { @@ -2883,8 +2822,7 @@ "deleteItem": "Delete Item", "immune": "Immune", "middleClick": "[Middle Click] Keep tooltip view", - "tokenSize": "The token size used on the canvas", - "previewTokenHelp": "Left-click to place, right-click to cancel" + "tokenSize": "The token size used on the canvas" } } } diff --git a/module/applications/dialogs/beastformDialog.mjs b/module/applications/dialogs/beastformDialog.mjs index 09a9222b..3dd88d6c 100644 --- a/module/applications/dialogs/beastformDialog.mjs +++ b/module/applications/dialogs/beastformDialog.mjs @@ -278,26 +278,19 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat 'close', async () => { const selected = app.selected.toObject(); - const evolved = app.evolved.form ? app.evolved.form.toObject() : null; const data = await game.system.api.data.items.DHBeastform.getWildcardImage( app.configData.data.parent, - evolved ?? app.selected + app.selected ); if (data) { if (!data.selectedImage) selected = null; else { - const imageSource = evolved ?? selected; - if (imageSource.usesDynamicToken) imageSource.system.tokenRingImg = data.selectedImage; - else imageSource.system.tokenImg = data.selectedImage; + if (data.usesDynamicToken) selected.system.tokenRingImg = data.selectedImage; + else selected.system.tokenImg = data.selectedImage; } } - resolve({ - selected: selected, - evolved: { ...app.evolved, form: evolved }, - hybrid: app.hybrid, - item: featureItem - }); + resolve({ selected: selected, evolved: app.evolved, hybrid: app.hybrid, item: featureItem }); }, { once: true } ); diff --git a/module/applications/dialogs/d20RollDialog.mjs b/module/applications/dialogs/d20RollDialog.mjs index d8306923..d872a1f8 100644 --- a/module/applications/dialogs/d20RollDialog.mjs +++ b/module/applications/dialogs/d20RollDialog.mjs @@ -10,7 +10,6 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio this.config = config; this.config.experiences = []; this.reactionOverride = config.actionType === 'reaction'; - this.selectedEffects = this.config.bonusEffects; if (config.source?.action) { this.item = config.data.parent.items.get(config.source.item) ?? config.data.parent; @@ -36,7 +35,6 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio selectExperience: this.selectExperience, toggleReaction: this.toggleReaction, toggleTagTeamRoll: this.toggleTagTeamRoll, - toggleSelectedEffect: this.toggleSelectedEffect, submitRoll: this.submitRoll }, form: { @@ -78,9 +76,6 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio icon })); - context.hasSelectedEffects = Boolean(this.selectedEffects && Object.keys(this.selectedEffects).length); - context.selectedEffects = this.selectedEffects; - this.config.costs ??= []; if (this.config.costs?.length) { const updatedCosts = game.system.api.fields.ActionFields.CostField.calcCosts.call( @@ -109,7 +104,7 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio context.roll = this.roll; context.rollType = this.roll?.constructor.name; context.rallyDie = this.roll.rallyChoices; - const experiences = this.config.data?.system?.experiences || {}; + const experiences = this.config.data?.system.experiences || {}; context.experiences = Object.keys(experiences).map(id => ({ id, ...experiences[id] @@ -213,11 +208,6 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio this.render(); } - static toggleSelectedEffect(_event, button) { - this.selectedEffects[button.dataset.key].selected = !this.selectedEffects[button.dataset.key].selected; - this.render(); - } - static async submitRoll() { await this.close({ submitted: true }); } diff --git a/module/applications/dialogs/damageDialog.mjs b/module/applications/dialogs/damageDialog.mjs index b24570cc..fbc584e4 100644 --- a/module/applications/dialogs/damageDialog.mjs +++ b/module/applications/dialogs/damageDialog.mjs @@ -6,7 +6,6 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application this.roll = roll; this.config = config; - this.selectedEffects = this.config.bonusEffects; } static DEFAULT_OPTIONS = { @@ -21,7 +20,6 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application icon: 'fa-solid fa-dice' }, actions: { - toggleSelectedEffect: this.toggleSelectedEffect, submitRoll: this.submitRoll }, form: { @@ -59,9 +57,6 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application icon })); context.modifiers = this.config.modifiers; - context.hasSelectedEffects = Boolean(Object.keys(this.selectedEffects).length); - context.selectedEffects = this.selectedEffects; - return context; } @@ -74,11 +69,6 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application this.render(); } - static toggleSelectedEffect(_event, button) { - this.selectedEffects[button.dataset.key].selected = !this.selectedEffects[button.dataset.key].selected; - this.render(); - } - static async submitRoll() { await this.close({ submitted: true }); } diff --git a/module/applications/dialogs/downtime.mjs b/module/applications/dialogs/downtime.mjs index 9a9a9ddb..3d5b7f0f 100644 --- a/module/applications/dialogs/downtime.mjs +++ b/module/applications/dialogs/downtime.mjs @@ -93,29 +93,27 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV } getRefreshables() { - const actionItems = this.actor.items - .filter(x => this.actor.system.isItemAvailable(x)) - .reduce((acc, x) => { - if (x.system.actions) { - const recoverable = x.system.actions.reduce((acc, action) => { - if (refreshIsAllowed([this.shortrest ? 'shortRest' : 'longRest'], action.uses.recovery)) { - acc.push({ - title: x.name, - name: action.name, - uuid: action.uuid - }); - } - - return acc; - }, []); - - if (recoverable) { - acc.push(...recoverable); + const actionItems = this.actor.items.filter(x => this.actor.system.isItemAvailable(x)).reduce((acc, x) => { + if (x.system.actions) { + const recoverable = x.system.actions.reduce((acc, action) => { + if (refreshIsAllowed([this.shortrest ? 'shortRest' : 'longRest'], action.uses.recovery)) { + acc.push({ + title: x.name, + name: action.name, + uuid: action.uuid + }); } - } - return acc; - }, []); + return acc; + }, []); + + if (recoverable) { + acc.push(...recoverable); + } + } + + return acc; + }, []); const resourceItems = this.actor.items.reduce((acc, x) => { if ( x.system.resource && @@ -183,18 +181,12 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV .filter(x => category.moves[x].selected) .flatMap(key => { const move = category.moves[key]; - const needsTarget = move.actions.filter(x => x.target?.type && x.target.type !== 'self').length > 0; return [...Array(move.selected).keys()].map(_ => ({ ...move, - movePath: `${categoryKey}.moves.${key}`, - needsTarget: needsTarget + movePath: `${categoryKey}.moves.${key}` })); }); }); - const characters = game.actors - .filter(x => x.type === 'character') - .filter(x => x.testUserPermission(game.user, 'LIMITED')) - .filter(x => x.uuid !== this.actor.uuid); const cls = getDocumentClass('ChatMessage'); const msg = { @@ -214,9 +206,7 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV `DAGGERHEART.APPLICATIONS.Downtime.${this.shortrest ? 'shortRest' : 'longRest'}.title` ), actor: { name: this.actor.name, img: this.actor.img }, - moves: moves, - characters: characters, - selfId: this.actor.uuid + moves: moves } ), flags: { diff --git a/module/applications/dialogs/tagTeamDialog.mjs b/module/applications/dialogs/tagTeamDialog.mjs index d1a1e123..0bc4bf0c 100644 --- a/module/applications/dialogs/tagTeamDialog.mjs +++ b/module/applications/dialogs/tagTeamDialog.mjs @@ -77,7 +77,7 @@ export default class TagTeamDialog extends HandlebarsApplicationMixin(Applicatio cost: this.data.initiator.cost }; - const selectedMember = Object.values(context.members).find(x => x.selected && x.roll); + const selectedMember = Object.values(context.members).find(x => x.selected); const selectedIsCritical = selectedMember?.roll?.system?.isCritical; context.selectedData = { result: selectedMember diff --git a/module/applications/hud/tokenHUD.mjs b/module/applications/hud/tokenHUD.mjs index 87c3e88e..b1136995 100644 --- a/module/applications/hud/tokenHUD.mjs +++ b/module/applications/hud/tokenHUD.mjs @@ -21,8 +21,6 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD { async _prepareContext(options) { const context = await super._prepareContext(options); - if (!this.actor) return context; - context.partyOnCanvas = this.actor.type === 'party' && this.actor.system.partyMembers.some(member => member.getActiveTokens().length > 0); @@ -60,33 +58,14 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD { } static async #onToggleCombat() { - const tokensWithoutActors = canvas.tokens.controlled.filter(t => !t.actor); - const warning = - tokensWithoutActors.length === 1 - ? game.i18n.format('DAGGERHEART.UI.Notifications.tokenActorMissing', { - name: tokensWithoutActors[0].name - }) - : game.i18n.format('DAGGERHEART.UI.Notifications.tokenActorsMissing', { - names: tokensWithoutActors.map(x => x.name).join(', ') - }); - const tokens = canvas.tokens.controlled - .filter(t => t.actor && !DHTokenHUD.#nonCombatTypes.includes(t.actor.type)) + .filter(t => !t.actor || !DHTokenHUD.#nonCombatTypes.includes(t.actor.type)) .map(t => t.document); - if (!this.object.controlled && this.document.actor) tokens.push(this.document); + if (!this.object.controlled) tokens.push(this.document); try { - if (this.document.inCombat) { - const tokensInCombat = tokens.filter(t => t.inCombat); - await TokenDocument.implementation.deleteCombatants([...tokensInCombat, ...tokensWithoutActors]); - } else { - if (tokensWithoutActors.length) { - ui.notifications.warn(warning); - } - - const tokensOutOfCombat = tokens.filter(t => !t.inCombat); - await TokenDocument.implementation.createCombatants(tokensOutOfCombat); - } + if (this.document.inCombat) await TokenDocument.implementation.deleteCombatants(tokens); + else await TokenDocument.implementation.createCombatants(tokens); } catch (err) { ui.notifications.warn(err.message); } diff --git a/module/applications/scene/sceneConfigSettings.mjs b/module/applications/scene/sceneConfigSettings.mjs index 8a58db5c..be8f7b71 100644 --- a/module/applications/scene/sceneConfigSettings.mjs +++ b/module/applications/scene/sceneConfigSettings.mjs @@ -1,25 +1,16 @@ -import { RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; - export default class DhSceneConfigSettings extends foundry.applications.sheets.SceneConfig { - constructor(options) { - super(options); - - Hooks.on(socketEvent.Refresh, ({ refreshType }) => { - if (refreshType === RefreshType.Scene) this.render(); - }); - } - - static DEFAULT_OPTIONS = { - ...super.DEFAULT_OPTIONS, - actions: { - ...super.DEFAULT_OPTIONS.actions, - removeSceneEnvironment: DhSceneConfigSettings.#removeSceneEnvironment - } - }; + // static DEFAULT_OPTIONS = { + // ...super.DEFAULT_OPTIONS, + // form: { + // handler: this.updateData, + // closeOnSubmit: true + // } + // }; static buildParts() { const { footer, tabs, ...parts } = super.PARTS; const tmpParts = { + // tabs, tabs: { template: 'systems/daggerheart/templates/scene/tabs.hbs' }, ...parts, dh: { template: 'systems/daggerheart/templates/scene/dh-config.hbs' }, @@ -37,47 +28,27 @@ export default class DhSceneConfigSettings extends foundry.applications.sheets.S static TABS = DhSceneConfigSettings.buildTabs(); - async _preRender(context, options) { - await super._preFirstRender(context, options); - - if (!options.internalRefresh) - this.daggerheartFlag = new game.system.api.data.scenes.DHScene(this.document.flags.daggerheart); - } - _attachPartListeners(partId, htmlElement, options) { super._attachPartListeners(partId, htmlElement, options); - switch (partId) { case 'dh': htmlElement.querySelector('#rangeMeasurementSetting')?.addEventListener('change', async event => { - this.daggerheartFlag.updateSource({ rangeMeasurement: { setting: event.target.value } }); - this.render({ internalRefresh: true }); + const flagData = foundry.utils.mergeObject(this.document.flags.daggerheart, { + rangeMeasurement: { setting: event.target.value } + }); + this.document.flags.daggerheart = flagData; + this.render(); }); - - const dragArea = htmlElement.querySelector('.scene-environments'); - if (dragArea) dragArea.ondrop = this._onDrop.bind(this); - break; } } - async _onDrop(event) { - const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); - const item = await foundry.utils.fromUuid(data.uuid); - if (item instanceof game.system.api.documents.DhpActor && item.type === 'environment') { - await this.daggerheartFlag.updateSource({ - sceneEnvironments: [...this.daggerheartFlag.sceneEnvironments, data.uuid] - }); - this.render({ internalRefresh: true }); - } - } - /** @inheritDoc */ async _preparePartContext(partId, context, options) { context = await super._preparePartContext(partId, context, options); switch (partId) { case 'dh': - context.data = this.daggerheartFlag; + context.data = new game.system.api.data.scenes.DHScene(canvas.scene.flags.daggerheart); context.variantRules = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules); break; } @@ -85,24 +56,8 @@ export default class DhSceneConfigSettings extends foundry.applications.sheets.S return context; } - static async #removeSceneEnvironment(_event, button) { - await this.daggerheartFlag.updateSource({ - sceneEnvironments: this.daggerheartFlag.sceneEnvironments.filter( - (_, index) => index !== Number.parseInt(button.dataset.index) - ) - }); - this.render({ internalRefresh: true }); - } - - /** @override */ - async _processSubmitData(event, form, submitData, options) { - submitData.flags.daggerheart = this.daggerheartFlag.toObject(); - for (const key of Object.keys(this.document._source.flags.daggerheart?.sceneEnvironments ?? {})) { - if (!submitData.flags.daggerheart.sceneEnvironments[key]) { - submitData.flags.daggerheart.sceneEnvironments[`-=${key}`] = null; - } - } - - super._processSubmitData(event, form, submitData, options); - } + // static async updateData(event, _, formData) { + // const data = foundry.utils.expandObject(formData.object); + // this.close(data); + // } } diff --git a/module/applications/sheets-configs/action-base-config.mjs b/module/applications/sheets-configs/action-base-config.mjs index 7051ad2b..7190a5b7 100644 --- a/module/applications/sheets-configs/action-base-config.mjs +++ b/module/applications/sheets-configs/action-base-config.mjs @@ -7,7 +7,6 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) this.action = action; this.openSection = null; - this.openTrigger = this.action.triggers.length > 0 ? 0 : null; } get title() { @@ -16,7 +15,7 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) static DEFAULT_OPTIONS = { tag: 'form', - classes: ['daggerheart', 'dh-style', 'action-config', 'dialog', 'max-800'], + classes: ['daggerheart', 'dh-style', 'dialog', 'max-800'], window: { icon: 'fa-solid fa-wrench', resizable: false @@ -30,18 +29,13 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) removeElement: this.removeElement, editEffect: this.editEffect, addDamage: this.addDamage, - removeDamage: this.removeDamage, - editDoc: this.editDoc, - addTrigger: this.addTrigger, - removeTrigger: this.removeTrigger, - expandTrigger: this.expandTrigger + removeDamage: this.removeDamage }, form: { handler: this.updateForm, submitOnChange: true, closeOnSubmit: false - }, - dragDrop: [{ dragSelector: null, dropSelector: '#summon-drop-zone', handlers: ['_onDrop'] }] + } }; static PARTS = { @@ -61,10 +55,6 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) effect: { id: 'effect', template: 'systems/daggerheart/templates/sheets-settings/action-settings/effect.hbs' - }, - trigger: { - id: 'trigger', - template: 'systems/daggerheart/templates/sheets-settings/action-settings/trigger.hbs' } }; @@ -92,18 +82,10 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) id: 'effect', icon: null, label: 'DAGGERHEART.GENERAL.Tabs.effects' - }, - trigger: { - active: false, - cssClass: '', - group: 'primary', - id: 'trigger', - icon: null, - label: 'DAGGERHEART.GENERAL.Tabs.triggers' } }; - static CLEAN_ARRAYS = ['damage.parts', 'cost', 'effects', 'summon']; + static CLEAN_ARRAYS = ['damage.parts', 'cost', 'effects']; _getTabs(tabs) { for (const v of Object.values(tabs)) { @@ -114,24 +96,9 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) return tabs; } - _attachPartListeners(partId, htmlElement, options) { - super._attachPartListeners(partId, htmlElement, options); - - htmlElement.querySelectorAll('.summon-count-wrapper input').forEach(element => { - element.addEventListener('change', this.updateSummonCount.bind(this)); - }); - } - async _prepareContext(_options) { const context = await super._prepareContext(_options, 'action'); - context.source = this.action.toObject(true); - - context.summons = []; - for (const summon of context.source.summon ?? []) { - const actor = await foundry.utils.fromUuid(summon.actorUUID); - context.summons.push({ actor, count: summon.count }); - } - + context.source = this.action._source; context.openSection = this.openSection; context.tabs = this._getTabs(this.constructor.TABS); context.config = CONFIG.DH; @@ -144,16 +111,6 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) context.baseSaveDifficulty = this.action.actor?.baseSaveDifficulty; context.baseAttackBonus = this.action.actor?.system.attack?.roll.bonus; context.hasRoll = this.action.hasRoll; - context.triggers = context.source.triggers.map((trigger, index) => { - const { hint, returns, usesActor } = CONFIG.DH.TRIGGER.triggers[trigger.trigger]; - return { - ...trigger, - hint, - returns, - usesActor, - revealed: this.openTrigger === index - }; - }); const settingsTiers = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers; context.tierOptions = [ @@ -224,9 +181,8 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) } static async updateForm(event, _, formData) { - const submitData = this._prepareSubmitData(event, formData); - - const data = foundry.utils.mergeObject(this.action.toObject(), submitData); + const submitData = this._prepareSubmitData(event, formData), + data = foundry.utils.mergeObject(this.action.toObject(), submitData); this.action = await this.action.update(data); this.sheetUpdate?.(this.action); @@ -245,26 +201,12 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) static removeElement(event, button) { event.stopPropagation(); const data = this.action.toObject(), - key = event.target.closest('[data-key]').dataset.key; - - // Prefer explicit index, otherwise find by uuid - let index = button?.dataset.index; - if (index === undefined || index === null || index === '') { - const uuid = button?.dataset.uuid ?? button?.dataset.itemUuid; - index = data[key].findIndex(e => (e?.actorUUID ?? e?.uuid) === uuid); - if (index === -1) return; - } else index = Number(index); - + key = event.target.closest('[data-key]').dataset.key, + index = button.dataset.index; data[key].splice(index, 1); this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); } - static async editDoc(_event, target) { - const element = target.closest('[data-item-uuid]'); - const doc = (await foundry.utils.fromUuid(element.dataset.itemUuid)) ?? null; - if (doc) return doc.sheet.render({ force: true }); - } - static addDamage(_event) { if (!this.action.damage.parts) return; const data = this.action.toObject(), @@ -282,69 +224,6 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); } - static addTrigger() { - const data = this.action.toObject(); - data.triggers.push({ - trigger: CONFIG.DH.TRIGGER.triggers.dualityRoll.id, - triggeringActor: CONFIG.DH.TRIGGER.triggerActorTargetType.any.id - }); - this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); - } - - static async removeTrigger(_event, button) { - const trigger = CONFIG.DH.TRIGGER.triggers[this.action.triggers[button.dataset.index].trigger]; - const confirmed = await foundry.applications.api.DialogV2.confirm({ - window: { - title: game.i18n.localize('DAGGERHEART.ACTIONS.Config.deleteTriggerTitle') - }, - content: game.i18n.format('DAGGERHEART.ACTIONS.Config.deleteTriggerContent', { - trigger: game.i18n.localize(trigger.label) - }) - }); - - if (!confirmed) return; - - const data = this.action.toObject(); - data.triggers = data.triggers.filter((_, index) => index !== Number.parseInt(button.dataset.index)); - this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); - } - - static async expandTrigger(_event, button) { - const index = Number.parseInt(button.dataset.index); - const toggle = (element, codeMirror) => { - codeMirror.classList.toggle('revealed'); - const button = element.querySelector('a > i'); - button.classList.toggle('fa-angle-up'); - button.classList.toggle('fa-angle-down'); - }; - - const fieldset = button.closest('fieldset'); - const codeMirror = fieldset.querySelector('.code-mirror-wrapper'); - toggle(fieldset, codeMirror); - - if (this.openTrigger !== null && this.openTrigger !== index) { - const previouslyExpanded = fieldset - .closest(`section`) - .querySelector(`fieldset[data-index="${this.openTrigger}"]`); - const codeMirror = previouslyExpanded.querySelector('.code-mirror-wrapper'); - toggle(previouslyExpanded, codeMirror); - this.openTrigger = index; - } else if (this.openTrigger === index) { - this.openTrigger = null; - } else { - this.openTrigger = index; - } - } - - updateSummonCount(event) { - event.stopPropagation(); - const wrapper = event.target.closest('.summon-count-wrapper'); - const index = wrapper.dataset.index; - const data = this.action.toObject(); - data.summon[index].count = event.target.value; - this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(data) }); - } - /** Specific implementation in extending classes **/ static async addEffect(_event) {} static removeEffect(_event, _button) {} @@ -354,29 +233,4 @@ export default class DHActionBaseConfig extends DaggerheartSheet(ApplicationV2) this.tabGroups.primary = 'base'; await super.close(options); } - - async _onDrop(event) { - const data = foundry.applications.ux.TextEditor.getDragEventData(event); - const item = await foundry.utils.fromUuid(data.uuid); - if (!(item instanceof game.system.api.documents.DhpActor)) { - ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.summon.invalidDrop')); - return; - } - - const actionData = this.action.toObject(); - let countvalue = 1; - for (const entry of actionData.summon) { - if (entry.actorUUID === data.uuid) { - entry.count += 1; - countvalue = entry.count; - await this.constructor.updateForm.bind(this)(null, null, { - object: foundry.utils.flattenObject(actionData) - }); - return; - } - } - - actionData.summon.push({ actorUUID: data.uuid, count: countvalue }); - await this.constructor.updateForm.bind(this)(null, null, { object: foundry.utils.flattenObject(actionData) }); - } } diff --git a/module/applications/sheets-configs/adversary-settings.mjs b/module/applications/sheets-configs/adversary-settings.mjs index d3d215be..bcc8b1c9 100644 --- a/module/applications/sheets-configs/adversary-settings.mjs +++ b/module/applications/sheets-configs/adversary-settings.mjs @@ -51,19 +51,6 @@ export default class DHAdversarySettings extends DHBaseActorSettings { } }; - async _prepareContext(options) { - const context = await super._prepareContext(options); - - const featureForms = ['passive', 'action', 'reaction']; - context.features = context.document.system.features.sort((a, b) => - a.system.featureForm !== b.system.featureForm - ? featureForms.indexOf(a.system.featureForm) - featureForms.indexOf(b.system.featureForm) - : a.sort - b.sort - ); - - return context; - } - /* -------------------------------------------- */ /** @@ -111,16 +98,16 @@ export default class DHAdversarySettings extends DHBaseActorSettings { async _onDrop(event) { const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); - + const item = await fromUuid(data.uuid); if (item?.type === 'feature') { if (data.fromInternal && item.parent?.uuid === this.actor.uuid) { return; } - + const itemData = item.toObject(); delete itemData._id; - + await this.actor.createEmbeddedDocuments('Item', [itemData]); } } diff --git a/module/applications/sheets-configs/environment-settings.mjs b/module/applications/sheets-configs/environment-settings.mjs index 15f5701d..2efa3b38 100644 --- a/module/applications/sheets-configs/environment-settings.mjs +++ b/module/applications/sheets-configs/environment-settings.mjs @@ -49,19 +49,6 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings { } }; - async _prepareContext(options) { - const context = await super._prepareContext(options); - - const featureForms = ['passive', 'action', 'reaction']; - context.features = context.document.system.features.sort((a, b) => - a.system.featureForm !== b.system.featureForm - ? featureForms.indexOf(a.system.featureForm) - featureForms.indexOf(b.system.featureForm) - : a.sort - b.sort - ); - - return context; - } - /** * Adds a new category entry to the actor. * @type {ApplicationClickAction} diff --git a/module/applications/sheets-configs/prototype-token-config.mjs b/module/applications/sheets-configs/prototype-token-config.mjs index 9faf6e71..0bb9703a 100644 --- a/module/applications/sheets-configs/prototype-token-config.mjs +++ b/module/applications/sheets-configs/prototype-token-config.mjs @@ -1,30 +1,41 @@ -import DHTokenConfigMixin from './token-config-mixin.mjs'; -import { getActorSizeFromForm } from './token-config-mixin.mjs'; - -export default class DhPrototypeTokenConfig extends DHTokenConfigMixin( - foundry.applications.sheets.PrototypeTokenConfig -) { - /** @inheritDoc */ - static DEFAULT_OPTIONS = { - ...super.DEFAULT_OPTIONS, - form: { handler: DhPrototypeTokenConfig.#onSubmit } +export default class DhPrototypeTokenConfig extends foundry.applications.sheets.PrototypeTokenConfig { + /** @override */ + static PARTS = { + tabs: super.PARTS.tabs, + identity: super.PARTS.identity, + appearance: { + template: 'systems/daggerheart/templates/sheets-settings/token-config/appearance.hbs', + scrollable: [''] + }, + vision: super.PARTS.vision, + light: super.PARTS.light, + resources: super.PARTS.resources, + footer: super.PARTS.footer }; - /** - * Process form submission for the sheet - * @this {PrototypeTokenConfig} - * @type {ApplicationFormSubmission} - */ - static async #onSubmit(event, form, formData) { - const submitData = this._processFormData(event, form, formData); - submitData.detectionModes ??= []; // Clear detection modes array - this._processChanges(submitData); - const changes = { prototypeToken: submitData }; + /** @inheritDoc */ + async _prepareResourcesTab() { + const token = this.token; + const usesTrackableAttributes = !foundry.utils.isEmpty(CONFIG.Actor.trackableAttributes); + const attributeSource = + this.actor?.system instanceof foundry.abstract.DataModel && usesTrackableAttributes + ? this.actor?.type + : this.actor?.system; + const TokenDocument = foundry.utils.getDocumentClass('Token'); + const attributes = TokenDocument.getTrackedAttributes(attributeSource); + return { + barAttributes: TokenDocument.getTrackedAttributeChoices(attributes, attributeSource), + bar1: token.getBarAttribute?.('bar1'), + bar2: token.getBarAttribute?.('bar2'), + turnMarkerModes: DhPrototypeTokenConfig.TURN_MARKER_MODES, + turnMarkerAnimations: CONFIG.Combat.settings.turnMarkerAnimations + }; + } - const changedTokenSizeValue = getActorSizeFromForm(this.element, this.actor); - if (changedTokenSizeValue) changes.system = { size: changedTokenSizeValue }; + async _prepareAppearanceTab() { + const context = await super._prepareAppearanceTab(); + context.actorSizeUsed = this.token.actor ? Boolean(this.token.actor.system.size) : false; - this.actor.validate({ changes, clean: true, fallback: false }); - await this.actor.update(changes); + return context; } } diff --git a/module/applications/sheets-configs/token-config-mixin.mjs b/module/applications/sheets-configs/token-config-mixin.mjs deleted file mode 100644 index c29b54ff..00000000 --- a/module/applications/sheets-configs/token-config-mixin.mjs +++ /dev/null @@ -1,114 +0,0 @@ -export default function DHTokenConfigMixin(Base) { - class DHTokenConfigBase extends Base { - /** @override */ - static PARTS = { - tabs: super.PARTS.tabs, - identity: super.PARTS.identity, - appearance: { - template: 'systems/daggerheart/templates/sheets-settings/token-config/appearance.hbs', - scrollable: [''] - }, - vision: super.PARTS.vision, - light: super.PARTS.light, - resources: super.PARTS.resources, - footer: super.PARTS.footer - }; - - _attachPartListeners(partId, htmlElement, options) { - super._attachPartListeners(partId, htmlElement, options); - - switch (partId) { - case 'appearance': - htmlElement - .querySelector('#dhTokenSize') - ?.addEventListener('change', this.onTokenSizeChange.bind(this)); - break; - } - } - - /** @inheritDoc */ - async _prepareResourcesTab() { - const token = this.token; - const usesTrackableAttributes = !foundry.utils.isEmpty(CONFIG.Actor.trackableAttributes); - const attributeSource = - this.actor?.system instanceof foundry.abstract.DataModel && usesTrackableAttributes - ? this.actor?.type - : this.actor?.system; - const TokenDocument = foundry.utils.getDocumentClass('Token'); - const attributes = TokenDocument.getTrackedAttributes(attributeSource); - return { - barAttributes: TokenDocument.getTrackedAttributeChoices(attributes, attributeSource), - bar1: token.getBarAttribute?.('bar1'), - bar2: token.getBarAttribute?.('bar2'), - turnMarkerModes: DHTokenConfigBase.TURN_MARKER_MODES, - turnMarkerAnimations: CONFIG.Combat.settings.turnMarkerAnimations - }; - } - - async _prepareAppearanceTab() { - const context = await super._prepareAppearanceTab(); - context.tokenSizes = CONFIG.DH.ACTOR.tokenSize; - context.tokenSize = this.actor?.system?.size; - context.usesActorSize = this.actor?.system?.metadata?.usesSize; - context.actorSizeDisable = context.usesActorSize && this.actor.system.size !== 'custom'; - - return context; - } - - /** @inheritDoc */ - _previewChanges(changes) { - if (!changes || !this._preview) return; - - const tokenSizeSelect = this.element?.querySelector('#dhTokenSize'); - if (this.actor && tokenSizeSelect && tokenSizeSelect.value !== 'custom') { - const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes; - const tokenSize = tokenSizes[tokenSizeSelect.value]; - changes.width = tokenSize; - changes.height = tokenSize; - } - - const deletions = { '-=actorId': null, '-=actorLink': null }; - const mergeOptions = { inplace: false, performDeletions: true }; - this._preview.updateSource(mergeObject(changes, deletions, mergeOptions)); - - if (this._preview?.object?.destroyed === false) { - this._preview.object.initializeSources(); - this._preview.object.renderFlags.set({ refresh: true }); - } - } - - async onTokenSizeChange(event) { - const value = event.target.value; - const tokenSizeDimensions = this.element.querySelector('#tokenSizeDimensions'); - if (tokenSizeDimensions) { - const disabled = value !== 'custom'; - - tokenSizeDimensions.dataset.tooltip = disabled - ? game.i18n.localize('DAGGERHEART.APPLICATIONS.TokenConfig.actorSizeUsed') - : ''; - - const disabledIcon = tokenSizeDimensions.querySelector('i'); - if (disabledIcon) { - disabledIcon.style.opacity = disabled ? '' : '0'; - } - - const dimensionsInputs = tokenSizeDimensions.querySelectorAll('.form-fields input'); - for (const input of dimensionsInputs) { - input.disabled = disabled; - } - } - } - } - - return DHTokenConfigBase; -} - -export function getActorSizeFromForm(element, actor) { - const tokenSizeSelect = element.querySelector('#dhTokenSize'); - const isSizeDifferent = tokenSizeSelect?.value !== actor?.system?.size; - if (tokenSizeSelect && actor && isSizeDifferent) { - return tokenSizeSelect.value; - } - - return null; -} diff --git a/module/applications/sheets-configs/token-config.mjs b/module/applications/sheets-configs/token-config.mjs index d7450bd6..5f4e4f56 100644 --- a/module/applications/sheets-configs/token-config.mjs +++ b/module/applications/sheets-configs/token-config.mjs @@ -1,11 +1,41 @@ -import DHTokenConfigMixin from './token-config-mixin.mjs'; -import { getActorSizeFromForm } from './token-config-mixin.mjs'; +export default class DhTokenConfig extends foundry.applications.sheets.TokenConfig { + /** @override */ + static PARTS = { + tabs: super.PARTS.tabs, + identity: super.PARTS.identity, + appearance: { + template: 'systems/daggerheart/templates/sheets-settings/token-config/appearance.hbs', + scrollable: [''] + }, + vision: super.PARTS.vision, + light: super.PARTS.light, + resources: super.PARTS.resources, + footer: super.PARTS.footer + }; -export default class DhTokenConfig extends DHTokenConfigMixin(foundry.applications.sheets.TokenConfig) { - async _processSubmitData(event, form, submitData, options) { - const changedTokenSizeValue = getActorSizeFromForm(this.element, this.actor); - if (changedTokenSizeValue) this.token.actor.update({ 'system.size': changedTokenSizeValue }); + /** @inheritDoc */ + async _prepareResourcesTab() { + const token = this.token; + const usesTrackableAttributes = !foundry.utils.isEmpty(CONFIG.Actor.trackableAttributes); + const attributeSource = + this.actor?.system instanceof foundry.abstract.DataModel && usesTrackableAttributes + ? this.actor?.type + : this.actor?.system; + const TokenDocument = foundry.utils.getDocumentClass('Token'); + const attributes = TokenDocument.getTrackedAttributes(attributeSource); + return { + barAttributes: TokenDocument.getTrackedAttributeChoices(attributes, attributeSource), + bar1: token.getBarAttribute?.('bar1'), + bar2: token.getBarAttribute?.('bar2'), + turnMarkerModes: DhTokenConfig.TURN_MARKER_MODES, + turnMarkerAnimations: CONFIG.Combat.settings.turnMarkerAnimations + }; + } - super._processSubmitData(event, form, submitData, options); + async _prepareAppearanceTab() { + const context = await super._prepareAppearanceTab(); + context.actorSizeUsed = this.token.actor ? Boolean(this.token.actor.system.size) : false; + + return context; } } diff --git a/module/applications/sheets/actors/adversary.mjs b/module/applications/sheets/actors/adversary.mjs index d8a3df29..345f6fed 100644 --- a/module/applications/sheets/actors/adversary.mjs +++ b/module/applications/sheets/actors/adversary.mjs @@ -26,12 +26,7 @@ export default class AdversarySheet extends DHBaseActorSheet { } ] }, - dragDrop: [ - { - dragSelector: '[data-item-id][draggable="true"], [data-item-id] [draggable="true"]', - dropSelector: null - } - ] + dragDrop: [{ dragSelector: '[data-item-id]', dropSelector: null }] }; static PARTS = { @@ -93,13 +88,6 @@ export default class AdversarySheet extends DHBaseActorSheet { context.resources.stress.emptyPips = context.resources.stress.max < maxResource ? maxResource - context.resources.stress.max : 0; - const featureForms = ['passive', 'action', 'reaction']; - context.features = this.document.system.features.sort((a, b) => - a.system.featureForm !== b.system.featureForm - ? featureForms.indexOf(a.system.featureForm) - featureForms.indexOf(b.system.featureForm) - : a.sort - b.sort - ); - return context; } @@ -176,15 +164,6 @@ export default class AdversarySheet extends DHBaseActorSheet { }); } - /** @inheritdoc */ - async _onDragStart(event) { - const inventoryItem = event.currentTarget.closest('.inventory-item'); - if (inventoryItem) { - event.dataTransfer.setDragImage(inventoryItem.querySelector('img'), 60, 0); - } - super._onDragStart(event); - } - /* -------------------------------------------- */ /* Application Clicks Actions */ /* -------------------------------------------- */ diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index dd5f35fc..66ed6315 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -32,8 +32,7 @@ export default class CharacterSheet extends DHBaseActorSheet { handleResourceDice: CharacterSheet.#handleResourceDice, advanceResourceDie: CharacterSheet.#advanceResourceDie, cancelBeastform: CharacterSheet.#cancelBeastform, - useDowntime: this.useDowntime, - viewParty: CharacterSheet.#viewParty + useDowntime: this.useDowntime }, window: { resizable: true, @@ -319,45 +318,6 @@ export default class CharacterSheet extends DHBaseActorSheet { ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.loadoutMaxReached')); } }, - { - name: 'recall', - icon: 'fa-solid fa-bolt-lightning', - condition: target => { - const doc = getDocFromElementSync(target); - return doc && doc.system.inVault; - }, - callback: async (target, event) => { - const doc = await getDocFromElement(target); - const actorLoadout = doc.actor.system.loadoutSlot; - if (!actorLoadout.available) { - ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.loadoutMaxReached')); - return; - } - if (doc.system.recallCost == 0) { - return doc.update({ 'system.inVault': false }); - } - const type = 'effect'; - const cls = game.system.api.models.actions.actionsTypes[type]; - const action = new cls( - { - ...cls.getSourceConfig(doc.system), - type: type, - chatDisplay: false, - cost: [ - { - key: 'stress', - value: doc.system.recallCost - } - ] - }, - { parent: doc.system } - ); - const config = await action.use(event); - if (config) { - return doc.update({ 'system.inVault': false }); - } - } - }, { name: 'toVault', icon: 'fa-solid fa-arrow-down', @@ -712,10 +672,8 @@ export default class CharacterSheet extends DHBaseActorSheet { headerTitle: game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', { ability: abilityLabel }), - effects: Array.from(await this.document.allApplicableEffects()), roll: { - trait: button.dataset.attribute, - type: 'trait' + trait: button.dataset.attribute }, hasRoll: true, actionType: 'action', @@ -725,7 +683,6 @@ export default class CharacterSheet extends DHBaseActorSheet { }) }; const result = await this.document.diceRoll(config); - if (!result) return; /* This could be avoided by baking config.costs into config.resourceUpdates. Didn't feel like messing with it at the time */ const costResources = result.costs @@ -830,7 +787,7 @@ export default class CharacterSheet extends DHBaseActorSheet { static async #toggleVault(_event, button) { const doc = await getDocFromElement(button); const { available } = this.document.system.loadoutSlot; - if (doc.system.inVault && !available && !doc.system.loadoutIgnore) { + if (doc.system.inVault && !available) { return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.loadoutMaxReached')); } @@ -901,41 +858,6 @@ export default class CharacterSheet extends DHBaseActorSheet { game.system.api.fields.ActionFields.BeastformField.handleActiveTransformations.call(item); } - static async #viewParty(_, target) { - const parties = this.document.parties; - if (parties.size <= 1) { - parties.first()?.sheet.render({ force: true }); - return; - } - - const buttons = parties.map(p => { - const button = document.createElement('button'); - button.type = 'button'; - button.classList.add('plain'); - const img = document.createElement('img'); - img.src = p.img; - button.append(img); - const name = document.createElement('span'); - name.textContent = p.name; - button.append(name); - button.addEventListener('click', () => { - p.sheet?.render({ force: true }); - game.tooltip.dismissLockedTooltips(); - }); - return button; - }); - - const html = document.createElement('div'); - html.classList.add('party-list'); - html.append(...buttons); - - game.tooltip.dismissLockedTooltips(); - game.tooltip.activate(target, { - html, - locked: true - }); - } - /** * Open the downtime application. * @type {ApplicationClickAction} diff --git a/module/applications/sheets/actors/environment.mjs b/module/applications/sheets/actors/environment.mjs index f8ff74a6..9a09cd94 100644 --- a/module/applications/sheets/actors/environment.mjs +++ b/module/applications/sheets/actors/environment.mjs @@ -25,12 +25,7 @@ export default class DhpEnvironment extends DHBaseActorSheet { toggleResourceDice: DhpEnvironment.#toggleResourceDice, handleResourceDice: DhpEnvironment.#handleResourceDice }, - dragDrop: [ - { - dragSelector: '[data-item-id][draggable="true"], [data-item-id] [draggable="true"]', - dropSelector: null - } - ] + dragDrop: [{ dragSelector: '.inventory-item', dropSelector: null }] }; /**@override */ @@ -79,9 +74,6 @@ export default class DhpEnvironment extends DHBaseActorSheet { case 'header': await this._prepareHeaderContext(context, options); - break; - case 'features': - await this._prepareFeaturesContext(context, options); break; case 'notes': await this._prepareNotesContext(context, options); @@ -118,22 +110,6 @@ export default class DhpEnvironment extends DHBaseActorSheet { } } - /** - * Prepare render context for the features part. - * @param {ApplicationRenderContext} context - * @param {ApplicationRenderOptions} options - * @returns {Promise} - * @protected - */ - async _prepareFeaturesContext(context, _options) { - const featureForms = ['passive', 'action', 'reaction']; - context.features = this.document.system.features.sort((a, b) => - a.system.featureForm !== b.system.featureForm - ? featureForms.indexOf(a.system.featureForm) - featureForms.indexOf(b.system.featureForm) - : a.sort - b.sort - ); - } - /** * Prepare render context for the Header part. * @param {ApplicationRenderContext} context diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 7276316f..d25a1a4e 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -211,7 +211,7 @@ export default function DHApplicationMixin(Base) { const step = event.key === 'ArrowUp' ? 1 : event.key === 'ArrowDown' ? -1 : 0; if (step !== 0) { handleUpdate(step); - deltaInput.dispatchEvent(new Event('change', { bubbles: true })); + deltaInput.dispatchEvent(new Event("change", { bubbles: true })); } }); @@ -222,7 +222,7 @@ export default function DHApplicationMixin(Base) { if (deltaInput === document.activeElement) { event.preventDefault(); handleUpdate(Math.sign(-1 * event.deltaY)); - deltaInput.dispatchEvent(new Event('change', { bubbles: true })); + deltaInput.dispatchEvent(new Event("change", { bubbles: true })); } }, { passive: false } @@ -236,7 +236,7 @@ export default function DHApplicationMixin(Base) { // Handle contenteditable for (const input of htmlElement.querySelectorAll('[contenteditable][data-property]')) { const property = input.dataset.property; - input.addEventListener('blur', () => { + input.addEventListener("blur", () => { const selection = document.getSelection(); if (input.contains(selection.anchorNode)) { selection.empty(); @@ -244,12 +244,12 @@ export default function DHApplicationMixin(Base) { this.document.update({ [property]: input.textContent }); }); - input.addEventListener('keydown', event => { - if (event.key === 'Enter') input.blur(); + input.addEventListener("keydown", event => { + if (event.key === "Enter") input.blur(); }); // Chrome sometimes add
, which aren't a problem for the value but are for the placeholder - input.addEventListener('input', () => input.querySelectorAll('br').forEach(i => i.remove())); + input.addEventListener("input", () => input.querySelectorAll("br").forEach((i) => i.remove())); } } @@ -505,7 +505,6 @@ export default function DHApplicationMixin(Base) { const doc = await getDocFromElement(target), action = doc?.system?.attack ?? doc; const config = action.prepareConfig(event); - config.effects = Array.from(await this.document.allApplicableEffects()); config.hasRoll = false; return action && action.workflow.get('damage').execute(config, null, true); } @@ -586,9 +585,7 @@ export default function DHApplicationMixin(Base) { if (!doc || !descriptionElement) continue; // localize the description (idk if it's still necessary) - const description = doc.system?.getEnrichedDescription - ? await doc.system.getEnrichedDescription() - : game.i18n.localize(doc.system?.description ?? doc.description); + const description = game.i18n.localize(doc.system?.description ?? doc.description); // Enrich the description and attach it; const isAction = doc.documentName === 'Action'; @@ -739,7 +736,7 @@ export default function DHApplicationMixin(Base) { }; if (inVault) data['system.inVault'] = true; if (disabled) data.disabled = true; - if (type === 'domainCard' && parent?.system.domains?.length) { + if (type === "domainCard" && parent?.system.domains?.length) { data.system.domain = parent.system.domains[0]; } diff --git a/module/applications/sheets/api/base-item.mjs b/module/applications/sheets/api/base-item.mjs index e3568b23..42ed9426 100644 --- a/module/applications/sheets/api/base-item.mjs +++ b/module/applications/sheets/api/base-item.mjs @@ -76,10 +76,16 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) { /**@inheritdoc */ async _preparePartContext(partId, context, options) { await super._preparePartContext(partId, context, options); + const { TextEditor } = foundry.applications.ux; switch (partId) { case 'description': - context.enrichedDescription = await this.document.system.getEnrichedDescription(); + const value = foundry.utils.getProperty(this.document, 'system.description') ?? ''; + context.enrichedDescription = await TextEditor.enrichHTML(value, { + relativeTo: this.item, + rollData: this.item.getRollData(), + secrets: this.item.isOwner + }); break; case 'effects': await this._prepareEffectsContext(context, options); diff --git a/module/applications/ui/_module.mjs b/module/applications/ui/_module.mjs index 8c5c020e..d5f31906 100644 --- a/module/applications/ui/_module.mjs +++ b/module/applications/ui/_module.mjs @@ -5,5 +5,4 @@ export { default as DhCombatTracker } from './combatTracker.mjs'; export { default as DhEffectsDisplay } from './effectsDisplay.mjs'; export { default as DhFearTracker } from './fearTracker.mjs'; export { default as DhHotbar } from './hotbar.mjs'; -export { default as DhSceneNavigation } from './sceneNavigation.mjs'; export { ItemBrowser } from './itemBrowser.mjs'; diff --git a/module/applications/ui/chatLog.mjs b/module/applications/ui/chatLog.mjs index 20dfea8d..45d9c0b3 100644 --- a/module/applications/ui/chatLog.mjs +++ b/module/applications/ui/chatLog.mjs @@ -134,9 +134,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo async actionUseButton(event, message) { const { moveIndex, actionIndex, movePath } = event.currentTarget.dataset; - const targetUuid = event.currentTarget.closest('.action-use-button-parent').querySelector('select')?.value; - const parent = await foundry.utils.fromUuid(targetUuid || message.system.actor); - + const parent = await foundry.utils.fromUuid(message.system.actor); const actionType = message.system.moves[moveIndex].actions[actionIndex]; const cls = game.system.api.models.actions.actionsTypes[actionType.type]; const action = new cls( @@ -148,8 +146,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo type: CONFIG.DH.ITEM.originItemType.restMove, itemPath: movePath, actionIndex: actionIndex - }, - targetUuid: targetUuid + } }, { parent: parent.system } ); diff --git a/module/applications/ui/combatTracker.mjs b/module/applications/ui/combatTracker.mjs index 288ba8ad..babc4a65 100644 --- a/module/applications/ui/combatTracker.mjs +++ b/module/applications/ui/combatTracker.mjs @@ -127,7 +127,7 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C resource, active: index === combat.turn, canPing: combatant.sceneId === canvas.scene?.id && game.user.hasPermission('PING_CANVAS'), - type: combatant.actor?.system?.type, + type: combatant.actor.system.type, img: await this._getCombatantThumbnail(combatant) }; @@ -165,7 +165,7 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C if (this.viewed.turn !== toggleTurn) { const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns; - if (combatant.actor?.type === 'character') { + if (combatant.actor.type === 'character') { await updateCountdowns( CONFIG.DH.GENERAL.countdownProgressionTypes.spotlight.id, CONFIG.DH.GENERAL.countdownProgressionTypes.characterSpotlight.id diff --git a/module/applications/ui/itemBrowser.mjs b/module/applications/ui/itemBrowser.mjs index b35573f7..794c3fb6 100644 --- a/module/applications/ui/itemBrowser.mjs +++ b/module/applications/ui/itemBrowser.mjs @@ -230,14 +230,6 @@ export class ItemBrowser extends HandlebarsApplicationMixin(ApplicationV2) { result.flatMap(r => r), 'name' ); - - /* If any noticeable slowdown occurs, consider replacing with enriching description on clicking to expand descriptions */ - for (const item of this.items) { - item.system.enrichedDescription = - (await item.system.getEnrichedDescription?.()) ?? - (await foundry.applications.ux.TextEditor.implementation.enrichHTML(item.description)); - } - this.fieldFilter = this._createFieldFilter(); if (this.presets?.filter) { diff --git a/module/applications/ui/sceneNavigation.mjs b/module/applications/ui/sceneNavigation.mjs deleted file mode 100644 index 0a3e08a5..00000000 --- a/module/applications/ui/sceneNavigation.mjs +++ /dev/null @@ -1,89 +0,0 @@ -import { emitAsGM, GMUpdateEvent } from '../../systemRegistration/socket.mjs'; - -export default class DhSceneNavigation extends foundry.applications.ui.SceneNavigation { - /** @inheritdoc */ - static DEFAULT_OPTIONS = { - ...super.DEFAULT_OPTIONS, - classes: ['faded-ui', 'flexcol', 'scene-navigation'], - actions: { - openSceneEnvironment: DhSceneNavigation.#openSceneEnvironment - } - }; - - /** @inheritdoc */ - static PARTS = { - scenes: { - root: true, - template: 'systems/daggerheart/templates/ui/sceneNavigation/scene-navigation.hbs' - } - }; - - /** @inheritdoc */ - async _prepareContext(options) { - const context = await super._prepareContext(options); - - const extendScenes = scenes => - scenes.map(x => { - const scene = game.scenes.get(x.id); - if (!scene.flags.daggerheart) return x; - - const daggerheartInfo = new game.system.api.data.scenes.DHScene(scene.flags.daggerheart); - const environments = daggerheartInfo.sceneEnvironments.filter( - x => x && x.testUserPermission(game.user, 'LIMITED') - ); - const hasEnvironments = environments.length > 0 && x.isView; - return { - ...x, - hasEnvironments, - environmentImage: hasEnvironments ? environments[0].img : null, - environments: environments - }; - }); - context.scenes.active = extendScenes(context.scenes.active); - context.scenes.inactive = extendScenes(context.scenes.inactive); - - return context; - } - - static async #openSceneEnvironment(event, button) { - const scene = game.scenes.get(button.dataset.sceneId); - const sceneEnvironments = new game.system.api.data.scenes.DHScene( - scene.flags.daggerheart - ).sceneEnvironments.filter(x => x.testUserPermission(game.user, 'LIMITED')); - - if (sceneEnvironments.length === 1 || event.shiftKey) { - sceneEnvironments[0].sheet.render(true); - } else { - new foundry.applications.ux.ContextMenu.implementation( - button, - '.scene-environment', - sceneEnvironments.map(environment => ({ - name: environment.name, - callback: () => { - if (scene.flags.daggerheart.sceneEnvironments[0] !== environment.uuid) { - const newEnvironments = scene.flags.daggerheart.sceneEnvironments; - const newFirst = newEnvironments.splice( - newEnvironments.findIndex(x => x === environment.uuid) - )[0]; - newEnvironments.unshift(newFirst); - emitAsGM( - GMUpdateEvent.UpdateDocument, - scene.update.bind(scene), - { 'flags.daggerheart.sceneEnvironments': newEnvironments }, - scene.uuid - ); - } - - environment.sheet.render({ force: true }); - } - })), - { - jQuery: false, - fixed: true - } - ); - - CONFIG.ux.ContextMenu.triggerContextMenu(event, '.scene-environment'); - } - } -} diff --git a/module/applications/ux/contextMenu.mjs b/module/applications/ux/contextMenu.mjs index 081e6ba0..09454848 100644 --- a/module/applications/ux/contextMenu.mjs +++ b/module/applications/ux/contextMenu.mjs @@ -96,11 +96,11 @@ export default class DHContextMenu extends foundry.applications.ux.ContextMenu { * Trigger a context menu event in response to a normal click on a additional options button. * @param {PointerEvent} event */ - static triggerContextMenu(event, altSelector) { + static triggerContextMenu(event) { event.preventDefault(); event.stopPropagation(); const { clientX, clientY } = event; - const selector = altSelector ?? '[data-item-uuid]'; + const selector = '[data-item-uuid]'; const target = event.target.closest(selector) ?? event.currentTarget.closest(selector); target?.dispatchEvent( new PointerEvent('contextmenu', { diff --git a/module/canvas/_module.mjs b/module/canvas/_module.mjs index c211b549..6b8885f4 100644 --- a/module/canvas/_module.mjs +++ b/module/canvas/_module.mjs @@ -1,2 +1 @@ export * as placeables from './placeables/_module.mjs'; -export { default as DhTokenLayer } from './tokens.mjs'; diff --git a/module/canvas/placeables/token.mjs b/module/canvas/placeables/token.mjs index 2266d0da..64ec3fa9 100644 --- a/module/canvas/placeables/token.mjs +++ b/module/canvas/placeables/token.mjs @@ -1,12 +1,4 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { - /** @inheritdoc */ - async _draw(options) { - await super._draw(options); - - if (this.document.flags.daggerheart?.createPlacement) - this.previewHelp ||= this.addChild(this.#drawPreviewHelp()); - } - /** @inheritDoc */ async _drawEffects() { this.effects.renderable = false; @@ -42,69 +34,6 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { this.renderFlags.set({ refreshEffects: true }); } - /** - * Returns the distance from this token to another token object. - * This value is corrected to handle alternate token sizes and other grid types - * according to the diagonal rules. - */ - distanceTo(target) { - if (!canvas.ready) return NaN; - if (this === target) return 0; - - const originPoint = this.center; - const destinationPoint = target.center; - - // Compute for gridless. This version returns circular edge to edge + grid distance, - // so that tokens that are touching return 5. - if (canvas.grid.type === CONST.GRID_TYPES.GRIDLESS) { - const boundsCorrection = canvas.grid.distance / canvas.grid.size; - const originRadius = (this.bounds.width * boundsCorrection) / 2; - const targetRadius = (target.bounds.width * boundsCorrection) / 2; - const distance = canvas.grid.measurePath([originPoint, destinationPoint]).distance; - return distance - originRadius - targetRadius + canvas.grid.distance; - } - - // Compute what the closest grid space of each token is, then compute that distance - const originEdge = this.#getEdgeBoundary(this.bounds, originPoint, destinationPoint); - const targetEdge = this.#getEdgeBoundary(target.bounds, originPoint, destinationPoint); - const adjustedOriginPoint = canvas.grid.getTopLeftPoint({ - x: originEdge.x + Math.sign(originPoint.x - originEdge.x), - y: originEdge.y + Math.sign(originPoint.y - originEdge.y) - }); - const adjustDestinationPoint = canvas.grid.getTopLeftPoint({ - x: targetEdge.x + Math.sign(destinationPoint.x - targetEdge.x), - y: targetEdge.y + Math.sign(destinationPoint.y - targetEdge.y) - }); - return canvas.grid.measurePath([adjustedOriginPoint, adjustDestinationPoint]).distance; - } - - /** Returns the point at which a line starting at origin and ending at destination intersects the edge of the bounds */ - #getEdgeBoundary(bounds, originPoint, destinationPoint) { - const points = [ - { x: bounds.x, y: bounds.y }, - { x: bounds.x + bounds.width, y: bounds.y }, - { x: bounds.x + bounds.width, y: bounds.y + bounds.height }, - { x: bounds.x, y: bounds.y + bounds.height } - ]; - const pairsToTest = [ - [points[0], points[1]], - [points[1], points[2]], - [points[2], points[3]], - [points[3], points[0]] - ]; - for (const pair of pairsToTest) { - const result = foundry.utils.lineSegmentIntersection(originPoint, destinationPoint, pair[0], pair[1]); - if (result) return result; - } - - return null; - } - - /** Tests if the token is at least adjacent with another, with some leeway for diagonals */ - isAdjacentWith(token) { - return this.distanceTo(token) <= canvas.grid.distance * 1.5; - } - /** @inheritDoc */ _drawBar(number, bar, data) { const val = Number(data.value); @@ -140,25 +69,4 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { bar.position.set(0, posY); return true; } - - /** - * Draw a helptext for previews as a text object - * @returns {PreciseText} The Text object for the preview helper - */ - #drawPreviewHelp() { - const { uiScale } = canvas.dimensions; - - const textStyle = CONFIG.canvasTextStyle.clone(); - textStyle.fontSize = 18; - textStyle.wordWrapWidth = this.w * 2.5; - textStyle.fontStyle = 'italic'; - - const helpText = new foundry.canvas.containers.PreciseText( - `(${game.i18n.localize('DAGGERHEART.UI.Tooltip.previewTokenHelp')})`, - textStyle - ); - helpText.anchor.set(helpText.width / 900, 1); - helpText.scale.set(uiScale, uiScale); - return helpText; - } } diff --git a/module/canvas/tokens.mjs b/module/canvas/tokens.mjs deleted file mode 100644 index 9813cd48..00000000 --- a/module/canvas/tokens.mjs +++ /dev/null @@ -1,16 +0,0 @@ -export default class DhTokenLayer extends foundry.canvas.layers.TokenLayer { - async _createPreview(createData, options) { - if (options.actor) { - const tokenSizes = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).tokenSizes; - if (options.actor?.system.metadata.usesSize) { - const tokenSize = tokenSizes[options.actor.system.size]; - if (tokenSize && options.actor.system.size !== CONFIG.DH.ACTOR.tokenSize.custom.id) { - createData.width = tokenSize; - createData.height = tokenSize; - } - } - } - - return super._createPreview(createData, options); - } -} diff --git a/module/config/_module.mjs b/module/config/_module.mjs index 560f3fec..ef26b958 100644 --- a/module/config/_module.mjs +++ b/module/config/_module.mjs @@ -10,4 +10,3 @@ export * as itemConfig from './itemConfig.mjs'; export * as settingsConfig from './settingsConfig.mjs'; export * as systemConfig from './system.mjs'; export * as itemBrowserConfig from './itemBrowserConfig.mjs'; -export * as triggerConfig from './triggerConfig.mjs'; diff --git a/module/config/actionConfig.mjs b/module/config/actionConfig.mjs index c9b70193..d64fd9e9 100644 --- a/module/config/actionConfig.mjs +++ b/module/config/actionConfig.mjs @@ -2,7 +2,7 @@ export const actionTypes = { attack: { id: 'attack', name: 'DAGGERHEART.ACTIONS.TYPES.attack.name', - icon: 'fa-hand-fist', + icon: 'fa-khanda', tooltip: 'DAGGERHEART.ACTIONS.TYPES.attack.tooltip' }, countdown: { diff --git a/module/config/encounterConfig.mjs b/module/config/encounterConfig.mjs index 4e0f8a6e..7565652f 100644 --- a/module/config/encounterConfig.mjs +++ b/module/config/encounterConfig.mjs @@ -9,7 +9,7 @@ export const AdversaryBPPerEncounter = (adversaries, characters) => { ); if (existingEntry) { existingEntry.nr += 1; - } else if (adversary.type) { + } else { acc.push({ adversary, nr: 1 }); } return acc; diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index 37894644..2c2b1316 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -232,7 +232,7 @@ export const defaultRestOptions = { actionType: 'action', chatDisplay: false, target: { - type: 'friendly' + type: 'self' }, damage: { parts: [ @@ -298,7 +298,7 @@ export const defaultRestOptions = { actionType: 'action', chatDisplay: false, target: { - type: 'friendly' + type: 'self' }, damage: { parts: [ @@ -341,7 +341,7 @@ export const defaultRestOptions = { actionType: 'action', chatDisplay: false, target: { - type: 'friendly' + type: 'self' }, damage: { parts: [ @@ -407,7 +407,7 @@ export const defaultRestOptions = { actionType: 'action', chatDisplay: false, target: { - type: 'friendly' + type: 'self' }, damage: { parts: [ @@ -496,8 +496,6 @@ export const diceTypes = { d20: 'd20' }; -export const dieFaces = [4, 6, 8, 10, 12, 20]; - export const multiplierTypes = { prof: 'Proficiency', cast: 'Spellcast', diff --git a/module/config/hooksConfig.mjs b/module/config/hooksConfig.mjs index 9140ea0a..d316c4d4 100644 --- a/module/config/hooksConfig.mjs +++ b/module/config/hooksConfig.mjs @@ -1,3 +1,5 @@ -export const hooksConfig = { +const hooksConfig = { effectDisplayToggle: 'DHEffectDisplayToggle' }; + +export default hooksConfig; diff --git a/module/config/itemConfig.mjs b/module/config/itemConfig.mjs index 7d80e597..02914143 100644 --- a/module/config/itemConfig.mjs +++ b/module/config/itemConfig.mjs @@ -435,8 +435,7 @@ export const armorFeatures = { { key: 'system.resistance.magical.reduction', mode: 2, - value: '@system.armorScore', - priority: 21 + value: '@system.armorScore' } ] } @@ -710,8 +709,7 @@ export const weaponFeatures = { { key: 'system.evasion', mode: 2, - value: '@system.armorScore', - priority: 21 + value: '@system.armorScore' } ] } @@ -1326,8 +1324,7 @@ export const weaponFeatures = { { key: 'system.bonuses.damage.primaryWeapon.bonus', mode: 2, - value: '@system.traits.agility.value', - priority: 21 + value: '@system.traits.agility.value' } ] } @@ -1419,9 +1416,9 @@ export const orderedWeaponFeatures = () => { }; export const featureForm = { - passive: 'DAGGERHEART.CONFIG.FeatureForm.passive', - action: 'DAGGERHEART.CONFIG.FeatureForm.action', - reaction: 'DAGGERHEART.CONFIG.FeatureForm.reaction' + passive: "DAGGERHEART.CONFIG.FeatureForm.passive", + action: "DAGGERHEART.CONFIG.FeatureForm.action", + reaction: "DAGGERHEART.CONFIG.FeatureForm.reaction" }; export const featureTypes = { diff --git a/module/config/system.mjs b/module/config/system.mjs index 47a41e8d..ac15b1d9 100644 --- a/module/config/system.mjs +++ b/module/config/system.mjs @@ -7,8 +7,7 @@ import * as SETTINGS from './settingsConfig.mjs'; import * as EFFECTS from './effectConfig.mjs'; import * as ACTIONS from './actionConfig.mjs'; import * as FLAGS from './flagsConfig.mjs'; -import * as HOOKS from './hooksConfig.mjs'; -import * as TRIGGER from './triggerConfig.mjs'; +import HOOKS from './hooksConfig.mjs'; import * as ITEMBROWSER from './itemBrowserConfig.mjs'; export const SYSTEM_ID = 'daggerheart'; @@ -25,6 +24,5 @@ export const SYSTEM = { ACTIONS, FLAGS, HOOKS, - TRIGGER, ITEMBROWSER }; diff --git a/module/config/triggerConfig.mjs b/module/config/triggerConfig.mjs deleted file mode 100644 index 35609bd1..00000000 --- a/module/config/triggerConfig.mjs +++ /dev/null @@ -1,42 +0,0 @@ -/* hints and returns are intentionally not translated. They are programatical terms and best understood in english */ -export const triggers = { - dualityRoll: { - id: 'dualityRoll', - usesActor: true, - args: ['roll', 'actor'], - label: 'DAGGERHEART.CONFIG.Triggers.dualityRoll.label', - hint: 'this: Action, roll: DhRoll, actor: DhActor', - returns: '{ updates: [{ key, value, total }] }' - }, - fearRoll: { - id: 'fearRoll', - usesActor: true, - args: ['roll', 'actor'], - label: 'DAGGERHEART.CONFIG.Triggers.fearRoll.label', - hint: 'this: Action, roll: DhRoll, actor: DhActor', - returns: '{ updates: [{ key, value, total }] }' - }, - postDamageReduction: { - id: 'postDamageReduction', - usesActor: true, - args: ['damageUpdates', 'actor'], - label: 'DAGGERHEART.CONFIG.Triggers.postDamageReduction.label', - hint: 'damageUpdates: ResourceUpdates, actor: DhActor', - returns: '{ updates: [{ originActor: this.actor, updates: [{ key, value, total }] }] }' - } -}; - -export const triggerActorTargetType = { - any: { - id: 'any', - label: 'DAGGERHEART.CONFIG.TargetTypes.any' - }, - self: { - id: 'self', - label: 'DAGGERHEART.CONFIG.TargetTypes.self' - }, - other: { - id: 'other', - label: 'DAGGERHEART.CONFIG.TargetTypes.other' - } -}; diff --git a/module/data/action/attackAction.mjs b/module/data/action/attackAction.mjs index 7be7461d..ed97072f 100644 --- a/module/data/action/attackAction.mjs +++ b/module/data/action/attackAction.mjs @@ -36,7 +36,6 @@ export default class DHAttackAction extends DHDamageAction { async use(event, options) { const result = await super.use(event, options); - if (!result.message) return; if (result.message.system.action.roll?.type === 'attack') { const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns; diff --git a/module/data/action/baseAction.mjs b/module/data/action/baseAction.mjs index dac4cf68..239bfa1e 100644 --- a/module/data/action/baseAction.mjs +++ b/module/data/action/baseAction.mjs @@ -2,7 +2,6 @@ import DhpActor from '../../documents/actor.mjs'; import D20RollDialog from '../../applications/dialogs/d20RollDialog.mjs'; import { ActionMixin } from '../fields/actionField.mjs'; import { originItemField } from '../chat-message/actorRoll.mjs'; -import TriggerField from '../fields/triggerField.mjs'; const fields = foundry.data.fields; @@ -34,9 +33,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel initial: 'action', nullable: false, required: true - }), - targetUuid: new fields.StringField({ initial: undefined }), - triggers: new fields.ArrayField(new TriggerField()) + }) }; this.extraSchemas.forEach(s => { @@ -165,8 +162,12 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel * @returns {object} */ getRollData(data = {}) { - const actorData = this.actor ? this.actor.getRollData(false) : {}; + if (!this.actor) return null; + const actorData = this.actor.getRollData(false); + + // Add Roll results to RollDatas actorData.result = data.roll?.total ?? 1; + actorData.scale = data.costs?.length // Right now only return the first scalable cost. ? (data.costs.find(c => c.scalable)?.total ?? 1) : 1; @@ -198,8 +199,6 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel let config = this.prepareConfig(event); if (!config) return; - await this.addEffects(config); - if (Hooks.call(`${CONFIG.DH.id}.preUseAction`, this, config) === false) return; // Display configuration window if necessary @@ -245,8 +244,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel selectedRollMode: game.settings.get('core', 'rollMode'), data: this.getRollData(), evaluate: this.hasRoll, - resourceUpdates: new ResourceUpdateMap(this.actor), - targetUuid: this.targetUuid + resourceUpdates: new ResourceUpdateMap(this.actor) }; DHBaseAction.applyKeybindings(config); @@ -266,16 +264,6 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel return config; } - /** */ - async addEffects(config) { - let effects = []; - if (this.actor) { - effects = Array.from(await this.actor.allApplicableEffects()); - } - - config.effects = effects; - } - /** * Method used to know if a configuration dialog must be shown or not when there is no roll. * @param {*} config Object that contains workflow datas. Usually made from Action Fields prepareConfig methods. @@ -356,10 +344,6 @@ export class ResourceUpdateMap extends Map { } addResources(resources) { - if (!resources?.length) return; - const invalidResources = resources.some(resource => !resource.key); - if (invalidResources) return; - for (const resource of resources) { if (!resource.key) continue; diff --git a/module/data/action/summonAction.mjs b/module/data/action/summonAction.mjs index 1505ce2d..b06f1d38 100644 --- a/module/data/action/summonAction.mjs +++ b/module/data/action/summonAction.mjs @@ -1,5 +1,19 @@ import DHBaseAction from './baseAction.mjs'; export default class DHSummonAction extends DHBaseAction { - static extraSchemas = [...super.extraSchemas, 'summon']; + static defineSchema() { + const fields = foundry.data.fields; + return { + ...super.defineSchema(), + documentUUID: new fields.DocumentUUIDField({ type: 'Actor' }) + }; + } + + async trigger(event, ...args) { + if (!this.canSummon || !canvas.scene) return; + } + + get canSummon() { + return game.user.can('TOKEN_CREATE'); + } } diff --git a/module/data/activeEffect/baseEffect.mjs b/module/data/activeEffect/baseEffect.mjs index ea74531d..770e3462 100644 --- a/module/data/activeEffect/baseEffect.mjs +++ b/module/data/activeEffect/baseEffect.mjs @@ -1,17 +1,3 @@ -/** -- Changes Type Priorities -- - * - Base Number - - * Custom: 0 - * Multiply: 10 - * Add: 20 - * Downgrade: 30 - * Upgrade: 40 - * Override: 50 - * - * - Changes Value Priorities - - * Standard: +0 - * "Anything that uses another data model value as its value": +1 - Effects that increase traits have to be calculated first at Base priority. (EX: Raise evasion by half your agility) - */ - export default class BaseEffect extends foundry.abstract.TypeDataModel { static defineSchema() { const fields = foundry.data.fields; diff --git a/module/data/activeEffect/beastformEffect.mjs b/module/data/activeEffect/beastformEffect.mjs index 65e36606..b041b59e 100644 --- a/module/data/activeEffect/beastformEffect.mjs +++ b/module/data/activeEffect/beastformEffect.mjs @@ -19,7 +19,6 @@ export default class BeastformEffect extends BaseEffect { base64: false }), tokenSize: new fields.SchemaField({ - scale: new fields.NumberField({ nullable: false, initial: 1 }), height: new fields.NumberField({ integer: false, nullable: true }), width: new fields.NumberField({ integer: false, nullable: true }) }) @@ -56,9 +55,7 @@ export default class BeastformEffect extends BaseEffect { const update = { ...baseUpdate, texture: { - src: this.characterTokenData.tokenImg, - scaleX: this.characterTokenData.tokenSize.scale, - scaleY: this.characterTokenData.tokenSize.scale + src: this.characterTokenData.tokenImg }, ring: { enabled: this.characterTokenData.usesDynamicToken, @@ -69,29 +66,19 @@ export default class BeastformEffect extends BaseEffect { }; const updateToken = token => { - let x = null, - y = null; - if (token.object?.scene?.grid) { - const positionData = game.system.api.documents.DhToken.getSnappedPositionInSquareGrid( - token.object.scene.grid, - { x: token.x, y: token.y, elevation: token.elevation }, - baseUpdate.width, - baseUpdate.height - ); - - x = positionData.x; - y = positionData.y; - } - + const { x, y } = game.system.api.documents.DhToken.getSnappedPositionInSquareGrid( + token.object.scene.grid, + { x: token.x, y: token.y, elevation: token.elevation }, + baseUpdate.width, + baseUpdate.height + ); return { ...baseUpdate, x, y, 'texture': { enabled: this.characterTokenData.usesDynamicToken, - src: token.flags.daggerheart?.beastformTokenImg ?? this.characterTokenData.tokenImg, - scaleX: this.characterTokenData.tokenSize.scale, - scaleY: this.characterTokenData.tokenSize.scale + src: token.flags.daggerheart?.beastformTokenImg ?? this.characterTokenData.tokenImg }, 'ring': { subject: { diff --git a/module/data/actor/adversary.mjs b/module/data/actor/adversary.mjs index 16e7e37a..32f5c979 100644 --- a/module/data/actor/adversary.mjs +++ b/module/data/actor/adversary.mjs @@ -1,6 +1,6 @@ import DHAdversarySettings from '../../applications/sheets-configs/adversary-settings.mjs'; import { ActionField } from '../fields/actionField.mjs'; -import BaseDataActor, { commonActorRules } from './base.mjs'; +import BaseDataActor from './base.mjs'; import { resourceField, bonusField } from '../fields/actorField.mjs'; export default class DhpAdversary extends BaseDataActor { @@ -56,11 +56,25 @@ export default class DhpAdversary extends BaseDataActor { }) }), resources: new fields.SchemaField({ - hitPoints: resourceField(0, 0, 'DAGGERHEART.GENERAL.HitPoints.plural', true), - stress: resourceField(0, 0, 'DAGGERHEART.GENERAL.stress', true) + hitPoints: resourceField( + 0, + 0, + 'DAGGERHEART.GENERAL.HitPoints.plural', + true + ), + stress: resourceField( + 0, + 0, + 'DAGGERHEART.GENERAL.stress', + true + ) }), rules: new fields.SchemaField({ - ...commonActorRules() + conditionImmunities: new fields.SchemaField({ + hidden: new fields.BooleanField({ initial: false }), + restrained: new fields.BooleanField({ initial: false }), + vulnerable: new fields.BooleanField({ initial: false }) + }) }), attack: new ActionField({ initial: { diff --git a/module/data/actor/base.mjs b/module/data/actor/base.mjs index b90361e2..29b0af28 100644 --- a/module/data/actor/base.mjs +++ b/module/data/actor/base.mjs @@ -2,23 +2,21 @@ import DHBaseActorSettings from '../../applications/sheets/api/actor-setting.mjs import DHItem from '../../documents/item.mjs'; import { getScrollTextData } from '../../helpers/utils.mjs'; -const fields = foundry.data.fields; - const resistanceField = (resistanceLabel, immunityLabel, reductionLabel) => - new fields.SchemaField({ - resistance: new fields.BooleanField({ + new foundry.data.fields.SchemaField({ + resistance: new foundry.data.fields.BooleanField({ initial: false, label: `${resistanceLabel}.label`, hint: `${resistanceLabel}.hint`, isAttributeChoice: true }), - immunity: new fields.BooleanField({ + immunity: new foundry.data.fields.BooleanField({ initial: false, label: `${immunityLabel}.label`, hint: `${immunityLabel}.hint`, isAttributeChoice: true }), - reduction: new fields.NumberField({ + reduction: new foundry.data.fields.NumberField({ integer: true, initial: 0, label: `${reductionLabel}.label`, @@ -26,25 +24,6 @@ const resistanceField = (resistanceLabel, immunityLabel, reductionLabel) => }) }); -/* Common rules applying to Characters and Adversaries */ -export const commonActorRules = (extendedData = { damageReduction: {} }) => ({ - conditionImmunities: new fields.SchemaField({ - hidden: new fields.BooleanField({ initial: false }), - restrained: new fields.BooleanField({ initial: false }), - vulnerable: new fields.BooleanField({ initial: false }) - }), - damageReduction: new fields.SchemaField({ - thresholdImmunities: new fields.SchemaField({ - minor: new fields.BooleanField({ initial: false }) - }), - reduceSeverity: new fields.SchemaField({ - magical: new fields.NumberField({ initial: 0, min: 0 }), - physical: new fields.NumberField({ initial: 0, min: 0 }) - }), - ...extendedData.damageReduction - }) -}); - /** * Describes metadata about the actor data model type * @typedef {Object} ActorDataModelMetadata @@ -75,6 +54,7 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel { /** @inheritDoc */ static defineSchema() { + const fields = foundry.data.fields; const schema = {}; if (this.metadata.hasAttribution) { diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index f6ab7e3a..5bce5c55 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -1,7 +1,7 @@ import { burden } from '../../config/generalConfig.mjs'; import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs'; import DhLevelData from '../levelData.mjs'; -import BaseDataActor, { commonActorRules } from './base.mjs'; +import BaseDataActor from './base.mjs'; import { attributeField, resourceField, stressDamageReductionRule, bonusField } from '../fields/actorField.mjs'; import { ActionField } from '../fields/actionField.mjs'; import DHCharacterSettings from '../../applications/sheets-configs/character-settings.mjs'; @@ -217,41 +217,44 @@ export default class DhCharacter extends BaseDataActor { }), companion: new ForeignDocumentUUIDField({ type: 'Actor', nullable: true, initial: null }), rules: new fields.SchemaField({ - ...commonActorRules({ - damageReduction: { - magical: new fields.BooleanField({ initial: false }), - physical: new fields.BooleanField({ initial: false }), - maxArmorMarked: new fields.SchemaField({ - value: new fields.NumberField({ - required: true, - integer: true, - initial: 1, - label: 'DAGGERHEART.GENERAL.Rules.damageReduction.maxArmorMarkedBonus' - }), - stressExtra: new fields.NumberField({ - required: true, - integer: true, - initial: 0, - label: 'DAGGERHEART.GENERAL.Rules.damageReduction.maxArmorMarkedStress.label', - hint: 'DAGGERHEART.GENERAL.Rules.damageReduction.maxArmorMarkedStress.hint' - }) - }), - stressDamageReduction: new fields.SchemaField({ - severe: stressDamageReductionRule( - 'DAGGERHEART.GENERAL.Rules.damageReduction.stress.severe' - ), - major: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.major'), - minor: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.minor'), - any: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.any') - }), - increasePerArmorMark: new fields.NumberField({ + damageReduction: new fields.SchemaField({ + maxArmorMarked: new fields.SchemaField({ + value: new fields.NumberField({ + required: true, integer: true, initial: 1, - label: 'DAGGERHEART.GENERAL.Rules.damageReduction.increasePerArmorMark.label', - hint: 'DAGGERHEART.GENERAL.Rules.damageReduction.increasePerArmorMark.hint' + label: 'DAGGERHEART.GENERAL.Rules.damageReduction.maxArmorMarkedBonus' }), - disabledArmor: new fields.BooleanField({ intial: false }) - } + stressExtra: new fields.NumberField({ + required: true, + integer: true, + initial: 0, + label: 'DAGGERHEART.GENERAL.Rules.damageReduction.maxArmorMarkedStress.label', + hint: 'DAGGERHEART.GENERAL.Rules.damageReduction.maxArmorMarkedStress.hint' + }) + }), + stressDamageReduction: new fields.SchemaField({ + severe: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.severe'), + major: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.major'), + minor: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.minor'), + any: stressDamageReductionRule('DAGGERHEART.GENERAL.Rules.damageReduction.stress.any') + }), + increasePerArmorMark: new fields.NumberField({ + integer: true, + initial: 1, + label: 'DAGGERHEART.GENERAL.Rules.damageReduction.increasePerArmorMark.label', + hint: 'DAGGERHEART.GENERAL.Rules.damageReduction.increasePerArmorMark.hint' + }), + magical: new fields.BooleanField({ initial: false }), + physical: new fields.BooleanField({ initial: false }), + thresholdImmunities: new fields.SchemaField({ + minor: new fields.BooleanField({ initial: false }) + }), + reduceSeverity: new fields.SchemaField({ + magical: new fields.NumberField({ initial: 0, min: 0 }), + physical: new fields.NumberField({ initial: 0, min: 0 }) + }), + disabledArmor: new fields.BooleanField({ intial: false }) }), attack: new fields.SchemaField({ damage: new fields.SchemaField({ @@ -280,23 +283,10 @@ export default class DhCharacter extends BaseDataActor { }) }) }), - dualityRoll: new fields.SchemaField({ - defaultHopeDice: new fields.NumberField({ - nullable: false, - required: true, - integer: true, - choices: CONFIG.DH.GENERAL.dieFaces, - initial: 12, - label: 'DAGGERHEART.ACTORS.Character.defaultHopeDice' - }), - defaultFearDice: new fields.NumberField({ - nullable: false, - required: true, - integer: true, - choices: CONFIG.DH.GENERAL.dieFaces, - initial: 12, - label: 'DAGGERHEART.ACTORS.Character.defaultFearDice' - }) + conditionImmunities: new fields.SchemaField({ + hidden: new fields.BooleanField({ initial: false }), + restrained: new fields.BooleanField({ initial: false }), + vulnerable: new fields.BooleanField({ initial: false }) }), runeWard: new fields.BooleanField({ initial: false }), burden: new fields.SchemaField({ @@ -463,7 +453,8 @@ export default class DhCharacter extends BaseDataActor { if ( item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.foundation || - (item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.specialization && subclassState >= 2) || + (item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.specialization && + subclassState >= 2) || (item.system.identifier === CONFIG.DH.ITEM.featureSubTypes.mastery && subclassState >= 3) ) { return true; diff --git a/module/data/actor/environment.mjs b/module/data/actor/environment.mjs index 0aaf8eb0..4ed3819e 100644 --- a/module/data/actor/environment.mjs +++ b/module/data/actor/environment.mjs @@ -1,11 +1,8 @@ import BaseDataActor from './base.mjs'; import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs'; import DHEnvironmentSettings from '../../applications/sheets-configs/environment-settings.mjs'; -import { RefreshType, socketEvent } from '../../systemRegistration/socket.mjs'; export default class DhEnvironment extends BaseDataActor { - scenes = new Set(); - /**@override */ static LOCALIZATION_PREFIXES = ['DAGGERHEART.ACTORS.Environment']; @@ -56,31 +53,6 @@ export default class DhEnvironment extends BaseDataActor { } isItemValid(source) { - return source.type === 'feature'; - } - - _onUpdate(changes, options, userId) { - super._onUpdate(changes, options, userId); - for (const scene of this.scenes) { - scene.render(); - } - } - - _onDelete(options, userId) { - super._onDelete(options, userId); - for (const scene of this.scenes) { - if (game.user.isActiveGM) { - const newSceneEnvironments = scene.flags.daggerheart.sceneEnvironments.filter( - x => x !== this.parent.uuid - ); - scene.update({ 'flags.daggerheart.sceneEnvironments': newSceneEnvironments }).then(() => { - Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.Scene }); - game.socket.emit(`system.${CONFIG.DH.id}`, { - action: socketEvent.Refresh, - data: { refreshType: RefreshType.TagTeamRoll } - }); - }); - } - } + return source.type === "feature"; } } diff --git a/module/data/combat.mjs b/module/data/combat.mjs index 3671a2cd..565afffc 100644 --- a/module/data/combat.mjs +++ b/module/data/combat.mjs @@ -15,9 +15,8 @@ export default class DhCombat extends foundry.abstract.TypeDataModel { get extendedBattleToggles() { const modifiers = CONFIG.DH.ENCOUNTER.BPModifiers; const adversaries = - this.parent.turns?.filter(x => x.actor && x.isNPC)?.map(x => ({ ...x.actor, type: x.actor.system.type })) ?? - []; - const characters = this.parent.turns?.filter(x => x.actor && !x.isNPC) ?? []; + this.parent.turns?.filter(x => x.isNPC)?.map(x => ({ ...x.actor, type: x.actor.system.type })) ?? []; + const characters = this.parent.turns?.filter(x => !x.isNPC) ?? []; const activeAutomatic = Object.keys(modifiers).reduce((acc, categoryKey) => { const category = modifiers[categoryKey]; diff --git a/module/data/fields/_module.mjs b/module/data/fields/_module.mjs index 2a8ba454..8d36b76d 100644 --- a/module/data/fields/_module.mjs +++ b/module/data/fields/_module.mjs @@ -2,6 +2,5 @@ export { ActionCollection } from './actionField.mjs'; export { default as FormulaField } from './formulaField.mjs'; export { default as ForeignDocumentUUIDField } from './foreignDocumentUUIDField.mjs'; export { default as ForeignDocumentUUIDArrayField } from './foreignDocumentUUIDArrayField.mjs'; -export { default as TriggerField } from './triggerField.mjs'; export { default as MappingField } from './mappingField.mjs'; export * as ActionFields from './action/_module.mjs'; diff --git a/module/data/fields/action/_module.mjs b/module/data/fields/action/_module.mjs index 0bdffca2..ef69394a 100644 --- a/module/data/fields/action/_module.mjs +++ b/module/data/fields/action/_module.mjs @@ -9,4 +9,3 @@ export { default as BeastformField } from './beastformField.mjs'; export { default as DamageField } from './damageField.mjs'; export { default as RollField } from './rollField.mjs'; export { default as MacroField } from './macroField.mjs'; -export { default as SummonField } from './summonField.mjs'; diff --git a/module/data/fields/action/beastformField.mjs b/module/data/fields/action/beastformField.mjs index e19807c7..6185f0f8 100644 --- a/module/data/fields/action/beastformField.mjs +++ b/module/data/fields/action/beastformField.mjs @@ -76,7 +76,7 @@ export default class BeastformField extends fields.SchemaField { * @returns */ static async transform(selectedForm, evolvedData, hybridData) { - const formData = evolvedData?.form ?? selectedForm; + const formData = evolvedData?.form ? evolvedData.form.toObject() : selectedForm; const beastformEffect = formData.effects.find(x => x.type === 'beastform'); if (!beastformEffect) { ui.notifications.error('DAGGERHEART.UI.Notifications.beastformMissingEffect'); diff --git a/module/data/fields/action/rollField.mjs b/module/data/fields/action/rollField.mjs index 63d48990..e2196c1c 100644 --- a/module/data/fields/action/rollField.mjs +++ b/module/data/fields/action/rollField.mjs @@ -87,7 +87,7 @@ export class DHActionRollData extends foundry.abstract.DataModel { if (this.type === CONFIG.DH.GENERAL.rollTypes.attack.id) modifiers.push({ label: 'Bonus to Hit', - value: this.bonus ?? this.parent.actor.system.attack.roll.bonus ?? 0 + value: this.bonus ?? this.parent.actor.system.attack.roll.bonus }); break; default: diff --git a/module/data/fields/action/summonField.mjs b/module/data/fields/action/summonField.mjs deleted file mode 100644 index dce6414c..00000000 --- a/module/data/fields/action/summonField.mjs +++ /dev/null @@ -1,89 +0,0 @@ -import FormulaField from '../formulaField.mjs'; - -const fields = foundry.data.fields; - -export default class DHSummonField extends fields.ArrayField { - /** - * Action Workflow order - */ - static order = 120; - - constructor(options = {}, context = {}) { - const summonFields = new fields.SchemaField({ - actorUUID: new fields.DocumentUUIDField({ - type: 'Actor', - required: true - }), - count: new FormulaField({ - required: true, - default: '1' - }) - }); - super(summonFields, options, context); - } - - static async execute() { - if (!canvas.scene) { - ui.notifications.warn(game.i18n.localize('DAGGERHEART.ACTIONS.TYPES.summon.error')); - return; - } - - if (this.summon.length === 0) { - ui.notifications.warn('No actors configured for this Summon action.'); - return; - } - - const rolls = []; - const summonData = []; - for (const summon of this.summon) { - let count = summon.count; - const roll = new Roll(summon.count); - if (!roll.isDeterministic) { - await roll.evaluate(); - if (game.modules.get('dice-so-nice')?.active) rolls.push(roll); - count = roll.total; - } - - const actor = DHSummonField.getWorldActor(await foundry.utils.fromUuid(summon.actorUUID)); - /* Extending summon data in memory so it's available in actionField.toChat. Think it's harmless, but ugly. Could maybe find a better way. */ - summon.rolledCount = count; - summon.actor = actor.toObject(); - - summonData.push({ actor, count: count }); - } - - if (rolls.length) await Promise.all(rolls.map(roll => game.dice3d.showForRoll(roll, game.user, true))); - - this.actor.sheet?.minimize(); - DHSummonField.handleSummon(summonData, this.actor); - } - - /* Check for any available instances of the actor present in the world if we're missing artwork in the compendium */ - static getWorldActor(baseActor) { - const dataType = game.system.api.data.actors[`Dh${baseActor.type.capitalize()}`]; - if (baseActor.inCompendium && dataType && baseActor.img === dataType.DEFAULT_ICON) { - const worldActorCopy = game.actors.find(x => x.name === baseActor.name); - return worldActorCopy ?? baseActor; - } - - return baseActor; - } - - static async handleSummon(summonData, actionActor, summonIndex = 0) { - const summon = summonData[summonIndex]; - const result = await CONFIG.ux.TokenManager.createPreviewAsync(summon.actor, { - name: `${summon.actor.prototypeToken.name}${summon.count > 1 ? ` (${summon.count}x)` : ''}` - }); - - if (!result) return actionActor.sheet?.maximize(); - summon.actor = result.actor; - - summon.count--; - if (summon.count <= 0) { - summonIndex++; - if (summonIndex === summonData.length) return actionActor.sheet?.maximize(); - } - - DHSummonField.handleSummon(summonData, actionActor, summonIndex); - } -} diff --git a/module/data/fields/action/targetField.mjs b/module/data/fields/action/targetField.mjs index 3a4f12df..73766118 100644 --- a/module/data/fields/action/targetField.mjs +++ b/module/data/fields/action/targetField.mjs @@ -25,12 +25,9 @@ export default class TargetField extends fields.SchemaField { config.hasTarget = true; let targets; // If the Action is configured as self-targeted, set targets as the owner. Probably better way than to fallback to getDependentTokens - if (this.target?.type === CONFIG.DH.GENERAL.targetTypes.self.id) { + if (this.target?.type === CONFIG.DH.GENERAL.targetTypes.self.id) targets = [this.actor.token ?? this.actor.prototypeToken]; - } else if (config.targetUuid) { - const actor = fromUuidSync(config.targetUuid); - targets = [actor.token ?? actor.prototypeToken]; - } else { + else { targets = Array.from(game.user.targets); if (this.target.type !== CONFIG.DH.GENERAL.targetTypes.any.id) { targets = targets.filter(target => TargetField.isTargetFriendly(this.actor, target, this.target.type)); diff --git a/module/data/fields/actionField.mjs b/module/data/fields/actionField.mjs index 0d71ab86..6257da38 100644 --- a/module/data/fields/actionField.mjs +++ b/module/data/fields/actionField.mjs @@ -267,23 +267,17 @@ export function ActionMixin(Base) { action: { name: this.name, img: this.baseAction ? this.parent.parent.img : this.img, - tags: this.tags ? this.tags : ['Spell', 'Arcana', 'Lv 10'], - summon: this.summon + tags: this.tags ? this.tags : ['Spell', 'Arcana', 'Lv 10'] }, itemOrigin: this.item, description: this.description || (this.item instanceof Item ? this.item.system.description : '') }; - - const speaker = cls.getSpeaker(); const msg = { type: 'abilityUse', user: game.user.id, actor: { name: this.actor.name, img: this.actor.img }, author: this.author, - speaker: { - speaker, - actor: speaker.actor ?? this.actor - }, + speaker: cls.getSpeaker(), title: game.i18n.localize('DAGGERHEART.UI.Chat.action.title'), system: systemData, content: await foundry.applications.handlebars.renderTemplate( diff --git a/module/data/fields/triggerField.mjs b/module/data/fields/triggerField.mjs deleted file mode 100644 index 8378ea19..00000000 --- a/module/data/fields/triggerField.mjs +++ /dev/null @@ -1,24 +0,0 @@ -export default class TriggerField extends foundry.data.fields.SchemaField { - constructor(context) { - super( - { - trigger: new foundry.data.fields.StringField({ - nullable: false, - blank: false, - initial: CONFIG.DH.TRIGGER.triggers.dualityRoll.id, - choices: CONFIG.DH.TRIGGER.triggers, - label: 'DAGGERHEART.CONFIG.Triggers.triggerType' - }), - triggeringActorType: new foundry.data.fields.StringField({ - nullable: false, - blank: false, - initial: CONFIG.DH.TRIGGER.triggerActorTargetType.any.id, - choices: CONFIG.DH.TRIGGER.triggerActorTargetType, - label: 'DAGGERHEART.CONFIG.Triggers.triggeringActorType' - }), - command: new foundry.data.fields.JavaScriptField({ async: true }) - }, - context - ); - } -} diff --git a/module/data/item/armor.mjs b/module/data/item/armor.mjs index 3d4a62fa..e35fae46 100644 --- a/module/data/item/armor.mjs +++ b/module/data/item/armor.mjs @@ -54,21 +54,6 @@ export default class DHArmor extends AttachableItem { ); } - /**@inheritdoc */ - async getDescriptionData() { - const baseDescription = this.description; - const allFeatures = CONFIG.DH.ITEM.allArmorFeatures(); - const features = this.armorFeatures.map(x => allFeatures[x.value]); - if (!features.length) return { prefix: null, value: baseDescription, suffix: null }; - - const prefix = await foundry.applications.handlebars.renderTemplate( - 'systems/daggerheart/templates/sheets/items/armor/description.hbs', - { features } - ); - - return { prefix, value: baseDescription, suffix: null }; - } - /**@inheritdoc */ async _preUpdate(changes, options, user) { const allowed = await super._preUpdate(changes, options, user); diff --git a/module/data/item/base.mjs b/module/data/item/base.mjs index 415fc8d4..11be0a52 100644 --- a/module/data/item/base.mjs +++ b/module/data/item/base.mjs @@ -8,7 +8,7 @@ * @property {boolean} isInventoryItem- Indicates whether items of this type is a Inventory Item */ -import { addLinkedItemsDiff, getScrollTextData, updateLinkedItemApps } from '../../helpers/utils.mjs'; +import { addLinkedItemsDiff, createScrollText, getScrollTextData, updateLinkedItemApps } from '../../helpers/utils.mjs'; import { ActionsField } from '../fields/actionField.mjs'; import FormulaField from '../fields/formulaField.mjs'; @@ -124,33 +124,6 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { return [source, page ? `pg ${page}.` : null].filter(x => x).join('. '); } - /** - * Augments the description for the item with type specific info to display. Implemented in applicable item subtypes. - * @param {object} [options] - Options that modify the styling of the rendered template. { headerStyle: undefined|'none'|'large' } - * @returns {string} - */ - async getDescriptionData(_options) { - return { prefix: null, value: this.description, suffix: null }; - } - - /** - * Gets the enriched and augmented description for the item. - * @param {object} [options] - Options that modify the styling of the rendered template. { headerStyle: undefined|'none'|'large' } - * @returns {string} - */ - async getEnrichedDescription() { - if (!this.metadata.hasDescription) return ''; - - const { prefix, value, suffix } = await this.getDescriptionData(); - const fullDescription = [prefix, value, suffix].filter(p => !!p).join('\n
\n'); - - return await foundry.applications.ux.TextEditor.implementation.enrichHTML(fullDescription, { - relativeTo: this, - rollData: this.getRollData(), - secrets: this.isOwner - }); - } - /** * Obtain a data object used to evaluate any dice rolls associated with this Item Type * @param {object} [options] - Options which modify the getRollData method. @@ -162,30 +135,6 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel { return data; } - prepareBaseData() { - super.prepareBaseData(); - - for (const action of this.actions ?? []) { - if (!action.actor) continue; - - const actionsToRegister = []; - for (let i = 0; i < action.triggers.length; i++) { - const trigger = action.triggers[i]; - const { args } = CONFIG.DH.TRIGGER.triggers[trigger.trigger]; - const fn = new foundry.utils.AsyncFunction(...args, `{${trigger.command}\n}`); - actionsToRegister.push(fn.bind(action)); - if (i === action.triggers.length - 1) - game.system.registeredTriggers.registerTriggers( - trigger.trigger, - action.actor?.uuid, - trigger.triggeringActorType, - this.parent.uuid, - actionsToRegister - ); - } - } - } - async _preCreate(data, options, user) { // Skip if no initial action is required or actions already exist if (this.metadata.hasInitialAction && foundry.utils.isEmpty(this.actions)) { diff --git a/module/data/item/beastform.mjs b/module/data/item/beastform.mjs index dd491169..669cd4b1 100644 --- a/module/data/item/beastform.mjs +++ b/module/data/item/beastform.mjs @@ -49,7 +49,6 @@ export default class DHBeastform extends BaseDataItem { choices: CONFIG.DH.ACTOR.tokenSize, initial: CONFIG.DH.ACTOR.tokenSize.custom.id }), - scale: new fields.NumberField({ nullable: false, min: 0.2, max: 3, step: 0.05, initial: 1 }), height: new fields.NumberField({ integer: true, min: 1, initial: null, nullable: true }), width: new fields.NumberField({ integer: true, min: 1, initial: null, nullable: true }) }), @@ -185,7 +184,6 @@ export default class DHBeastform extends BaseDataItem { tokenImg: this.parent.parent.prototypeToken.texture.src, tokenRingImg: this.parent.parent.prototypeToken.ring.subject.texture, tokenSize: { - scale: this.parent.parent.prototypeToken.texture.scaleX, height: this.parent.parent.prototypeToken.height, width: this.parent.parent.prototypeToken.width } @@ -211,9 +209,7 @@ export default class DHBeastform extends BaseDataItem { height, width, texture: { - src: this.tokenImg, - scaleX: this.tokenSize.scale, - scaleY: this.tokenSize.scale + src: this.tokenImg }, ring: { subject: { @@ -222,20 +218,12 @@ export default class DHBeastform extends BaseDataItem { } }; const tokenUpdate = token => { - let x = null, - y = null; - if (token.object?.scene?.grid) { - const positionData = game.system.api.documents.DhToken.getSnappedPositionInSquareGrid( - token.object.scene.grid, - { x: token.x, y: token.y, elevation: token.elevation }, - width ?? token.width, - height ?? token.height - ); - - x = positionData.x; - y = positionData.y; - } - + const { x, y } = game.system.api.documents.DhToken.getSnappedPositionInSquareGrid( + token.object.scene.grid, + { x: token.x, y: token.y, elevation: token.elevation }, + width ?? token.width, + height ?? token.height + ); return { ...prototypeTokenUpdate, x, diff --git a/module/data/item/domainCard.mjs b/module/data/item/domainCard.mjs index 327dafce..92d8828c 100644 --- a/module/data/item/domainCard.mjs +++ b/module/data/item/domainCard.mjs @@ -29,21 +29,7 @@ export default class DHDomainCard extends BaseDataItem { required: true, initial: CONFIG.DH.DOMAIN.cardTypes.ability.id }), - inVault: new fields.BooleanField({ initial: false }), - vaultActive: new fields.BooleanField({ - required: true, - nullable: false, - initial: false - }), - loadoutIgnore: new fields.BooleanField({ - required: true, - nullable: false, - initial: false - }), - domainTouched: new fields.NumberField({ - nullable: true, - initial: null - }) + inVault: new fields.BooleanField({ initial: false }) }; } @@ -52,19 +38,6 @@ export default class DHDomainCard extends BaseDataItem { return game.i18n.localize(allDomainData[this.domain].label); } - get isVaultSupressed() { - return this.inVault && !this.vaultActive; - } - - get isDomainTouchedSuppressed() { - if (!this.parent.system.domainTouched || this.parent.parent?.type !== 'character') return false; - - const matchingDomainCards = this.parent.parent.items.filter( - item => !item.system.inVault && item.system.domain === this.parent.system.domain - ).length; - return matchingDomainCards < this.parent.system.domainTouched; - } - /* -------------------------------------------- */ /**@override */ diff --git a/module/data/item/weapon.mjs b/module/data/item/weapon.mjs index f333e5f3..295cc0c5 100644 --- a/module/data/item/weapon.mjs +++ b/module/data/item/weapon.mjs @@ -110,21 +110,6 @@ export default class DHWeapon extends AttachableItem { ); } - /**@inheritdoc */ - async getDescriptionData() { - const baseDescription = this.description; - const allFeatures = CONFIG.DH.ITEM.allWeaponFeatures(); - const features = this.weaponFeatures.map(x => allFeatures[x.value]); - if (!features.length) return { prefix: null, value: baseDescription, suffix: null }; - - const prefix = await foundry.applications.handlebars.renderTemplate( - 'systems/daggerheart/templates/sheets/items/weapon/description.hbs', - { features } - ); - - return { prefix, value: baseDescription, suffix: null }; - } - prepareDerivedData() { this.attack.roll.trait = this.rules.attack.roll.trait ?? this.attack.roll.trait; } diff --git a/module/data/scene/scene.mjs b/module/data/scene/scene.mjs index f2a24308..7cf74ade 100644 --- a/module/data/scene/scene.mjs +++ b/module/data/scene/scene.mjs @@ -1,8 +1,3 @@ -import ForeignDocumentUUIDArrayField from '../fields/foreignDocumentUUIDArrayField.mjs'; - -/* Foundry does not add any system data for subtyped Scenes. The data model is therefore used by instantiating a new instance of it for sceneConfigSettings.mjs. - Needed dataprep and lifetime hooks are handled in documents/scene. -*/ export default class DHScene extends foundry.abstract.DataModel { static defineSchema() { const fields = foundry.data.fields; @@ -18,8 +13,7 @@ export default class DHScene extends foundry.abstract.DataModel { veryClose: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.veryClose.name' }), close: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.close.name' }), far: new fields.NumberField({ integer: true, label: 'DAGGERHEART.CONFIG.Range.far.name' }) - }), - sceneEnvironments: new ForeignDocumentUUIDArrayField({ type: 'Actor', prune: true }) + }) }; } } diff --git a/module/data/settings/Automation.mjs b/module/data/settings/Automation.mjs index 3376b153..2aec990f 100644 --- a/module/data/settings/Automation.mjs +++ b/module/data/settings/Automation.mjs @@ -173,13 +173,6 @@ export default class DhAutomation extends foundry.abstract.DataModel { label: 'DAGGERHEART.GENERAL.player.plurial' }) }) - }), - triggers: new fields.SchemaField({ - enabled: new fields.BooleanField({ - nullable: false, - initial: true, - label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.triggers.enabled.label' - }) }) }; } diff --git a/module/dice/d20Roll.mjs b/module/dice/d20Roll.mjs index 3ddd8027..0256f281 100644 --- a/module/dice/d20Roll.mjs +++ b/module/dice/d20Roll.mjs @@ -35,9 +35,7 @@ export default class D20Roll extends DHRoll { get isCritical() { if (!this.d20._evaluated) return; - - const criticalThreshold = this.options.actionType === 'reaction' ? 20 : this.data.system.criticalThreshold; - return this.d20.total >= criticalThreshold; + return this.d20.total >= this.data.system.criticalThreshold; } get hasAdvantage() { @@ -129,55 +127,15 @@ export default class D20Roll extends DHRoll { const modifiers = foundry.utils.deepClone(this.options.roll.baseModifiers) ?? []; modifiers.push( - ...this.getBonus( - `system.bonuses.roll.${this.options.actionType}`, - `${this.options.actionType?.capitalize()} Bonus` - ) + ...this.getBonus(`roll.${this.options.actionType}`, `${this.options.actionType?.capitalize()} Bonus`) + ); + modifiers.push( + ...this.getBonus(`roll.${this.options.roll.type}`, `${this.options.roll.type?.capitalize()} Bonus`) ); - if (this.options.roll.type !== CONFIG.DH.GENERAL.rollTypes.attack.id) { - modifiers.push( - ...this.getBonus( - `system.bonuses.roll.${this.options.roll.type}`, - `${this.options.roll.type?.capitalize()} Bonus` - ) - ); - } - - if ( - this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.attack.id || - (this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.spellcast.id && this.options.hasDamage) - ) { - modifiers.push( - ...this.getBonus(`system.bonuses.roll.attack`, `${this.options.roll.type?.capitalize()} Bonus`) - ); - } - return modifiers; } - getActionChangeKeys() { - const changeKeys = new Set([`system.bonuses.roll.${this.options.actionType}`]); - - if (this.options.roll.type !== CONFIG.DH.GENERAL.rollTypes.attack.id) { - changeKeys.add(`system.bonuses.roll.${this.options.roll.type}`); - } - - if ( - this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.attack.id || - (this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.spellcast.id && this.options.hasDamage) - ) { - changeKeys.add(`system.bonuses.roll.attack`); - } - - if (this.options.roll.trait && this.data.traits?.[this.options.roll.trait]) { - if (this.options.roll.type !== CONFIG.DH.GENERAL.rollTypes.spellcast.id) - changeKeys.add('system.bonuses.roll.trait'); - } - - return changeKeys; - } - static postEvaluate(roll, config = {}) { const data = super.postEvaluate(roll, config); data.type = config.actionType; diff --git a/module/dice/damageRoll.mjs b/module/dice/damageRoll.mjs index cd26eb21..c10ee6ff 100644 --- a/module/dice/damageRoll.mjs +++ b/module/dice/damageRoll.mjs @@ -93,6 +93,7 @@ export default class DamageRoll extends DHRoll { type = this.options.messageType ?? (this.options.hasHealing ? 'healing' : 'damage'), options = part ?? this.options; + modifiers.push(...this.getBonus(`${type}`, `${type.capitalize()} Bonus`)); if (!this.options.hasHealing) { options.damageTypes?.forEach(t => { modifiers.push(...this.getBonus(`${type}.${t}`, `${t.capitalize()} ${type.capitalize()} Bonus`)); @@ -107,31 +108,6 @@ export default class DamageRoll extends DHRoll { return modifiers; } - getActionChangeKeys() { - const type = this.options.messageType ?? (this.options.hasHealing ? 'healing' : 'damage'); - const changeKeys = []; - - for (const roll of this.options.roll) { - for (const damageType of roll.damageTypes?.values?.() ?? []) { - changeKeys.push(`system.bonuses.${type}.${damageType}`); - } - } - - const item = this.data.parent?.items?.get(this.options.source.item); - if (item) { - switch (item.type) { - case 'weapon': - if (!this.options.hasHealing) - ['primaryWeapon', 'secondaryWeapon'].forEach(w => - changeKeys.push(`system.bonuses.damage.${w}`) - ); - break; - } - } - - return changeKeys; - } - constructFormula(config) { this.options.roll.forEach((part, index) => { part.roll = new Roll(Roll.replaceFormulaData(part.formula, config.data)); diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index a5ac5091..ea24f238 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -4,7 +4,6 @@ export default class DHRoll extends Roll { baseTerms = []; constructor(formula, data = {}, options = {}) { super(formula, data, options); - options.bonusEffects = this.bonusEffectBuilder(); if (!this.data || !Object.keys(this.data).length) this.data = options.data; } @@ -165,17 +164,11 @@ export default class DHRoll extends Roll { new foundry.dice.terms.OperatorTerm({ operator: '+' }), ...this.constructor.parse(modifier.join(' + '), this.options.data) ]; - } else if (Number.isNumeric(modifier)) { - const numTerm = modifier < 0 ? '-' : '+'; - return [ - new foundry.dice.terms.OperatorTerm({ operator: numTerm }), - new foundry.dice.terms.NumericTerm({ number: Math.abs(modifier) }) - ]; } else { const numTerm = modifier < 0 ? '-' : '+'; return [ new foundry.dice.terms.OperatorTerm({ operator: numTerm }), - ...this.constructor.parse(modifier, this.options.data) + new foundry.dice.terms.NumericTerm({ number: Math.abs(modifier) }) ]; } } @@ -192,20 +185,18 @@ export default class DHRoll extends Roll { } getBonus(path, label) { - const modifiers = []; - for (const effect of Object.values(this.options.bonusEffects)) { - if (!effect.selected) continue; - for (const change of effect.changes) { - if (!change.key.includes(path)) continue; - const changeValue = game.system.api.documents.DhActiveEffect.getChangeValue( - this.data, - change, - effect.origEffect - ); - modifiers.push({ label: label, value: changeValue }); - } - } - + const bonus = foundry.utils.getProperty(this.data.bonuses, path), + modifiers = []; + if (bonus?.bonus) + modifiers.push({ + label: label, + value: bonus?.bonus + }); + if (bonus?.dice?.length) + modifiers.push({ + label: label, + value: bonus?.dice + }); return modifiers; } @@ -244,28 +235,4 @@ export default class DHRoll extends Roll { static temporaryModifierBuilder(config) { return {}; } - - bonusEffectBuilder() { - const changeKeys = this.getActionChangeKeys(); - return ( - this.options.effects?.reduce((acc, effect) => { - if (effect.changes.some(x => changeKeys.some(key => x.key.includes(key)))) { - acc[effect.id] = { - id: effect.id, - name: effect.name, - description: effect.description, - changes: effect.changes, - origEffect: effect, - selected: !effect.disabled - }; - } - - return acc; - }, {}) ?? [] - ); - } - - getActionChangeKeys() { - return []; - } } diff --git a/module/dice/dualityRoll.mjs b/module/dice/dualityRoll.mjs index aaca7400..91c0a197 100644 --- a/module/dice/dualityRoll.mjs +++ b/module/dice/dualityRoll.mjs @@ -130,14 +130,9 @@ export default class DualityRoll extends D20Roll { this.terms = [this.terms[0], this.terms[1], this.terms[2]]; return; } - - this.terms[0] = new foundry.dice.terms.Die({ - faces: this.data.rules.dualityRoll?.defaultHopeDice ?? 12 - }); + this.terms[0] = new foundry.dice.terms.Die({ faces: 12 }); this.terms[1] = new foundry.dice.terms.OperatorTerm({ operator: '+' }); - this.terms[2] = new foundry.dice.terms.Die({ - faces: this.data.rules.dualityRoll?.defaultFearDice ?? 12 - }); + this.terms[2] = new foundry.dice.terms.Die({ faces: 12 }); } applyAdvantage() { @@ -178,34 +173,6 @@ export default class DualityRoll extends D20Roll { return modifiers; } - getActionChangeKeys() { - const changeKeys = new Set([`system.bonuses.roll.${this.options.actionType}`]); - - if (this.options.roll.type !== CONFIG.DH.GENERAL.rollTypes.attack.id) { - changeKeys.add(`system.bonuses.roll.${this.options.roll.type}`); - } - - if ( - this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.attack.id || - (this.options.roll.type === CONFIG.DH.GENERAL.rollTypes.spellcast.id && this.options.hasDamage) - ) { - changeKeys.add(`system.bonuses.roll.attack`); - } - - if (this.options.roll.trait && this.data.traits?.[this.options.roll.trait]) { - if (this.options.roll.type !== CONFIG.DH.GENERAL.rollTypes.spellcast.id) - changeKeys.add('system.bonuses.roll.trait'); - } - - const weapons = ['primaryWeapon', 'secondaryWeapon']; - weapons.forEach(w => { - if (this.options.source.item && this.options.source.item === this.data[w]?.id) - changeKeys.add(`system.bonuses.roll.${w}`); - }); - - return changeKeys; - } - static async buildEvaluate(roll, config = {}, message = {}) { await super.buildEvaluate(roll, config, message); @@ -257,32 +224,6 @@ export default class DualityRoll extends D20Roll { await super.buildPost(roll, config, message); await DualityRoll.dualityUpdate(config); - await DualityRoll.handleTriggers(roll, config); - } - - static async handleTriggers(roll, config) { - if (!config.source?.actor) return; - - const updates = []; - const dualityUpdates = await game.system.registeredTriggers.runTrigger( - CONFIG.DH.TRIGGER.triggers.dualityRoll.id, - roll.data?.parent, - roll, - roll.data?.parent - ); - if (dualityUpdates?.length) updates.push(...dualityUpdates); - - if (config.roll.result.duality === -1) { - const fearUpdates = await game.system.registeredTriggers.runTrigger( - CONFIG.DH.TRIGGER.triggers.fearRoll.id, - roll.data?.parent, - roll, - roll.data?.parent - ); - if (fearUpdates?.length) updates.push(...fearUpdates); - } - - config.resourceUpdates.addResources(updates); } static async addDualityResourceUpdates(config) { @@ -357,7 +298,7 @@ export default class DualityRoll extends D20Roll { if (looseSpotlight && game.combat?.active) { const currentCombatant = game.combat.combatants.get(game.combat.current?.combatantId); - if (currentCombatant?.actorId == config.data.id) ui.combat.setCombatantSpotlight(currentCombatant.id); + if (currentCombatant?.actorId == actor.id) ui.combat.setCombatantSpotlight(currentCombatant.id); } } diff --git a/module/documents/_module.mjs b/module/documents/_module.mjs index 8073cfe1..22718bea 100644 --- a/module/documents/_module.mjs +++ b/module/documents/_module.mjs @@ -8,4 +8,3 @@ export { default as DhScene } from './scene.mjs'; export { default as DhToken } from './token.mjs'; export { default as DhTooltipManager } from './tooltipManager.mjs'; export { default as DhTemplateManager } from './templateManager.mjs'; -export { default as DhTokenManager } from './tokenManager.mjs'; diff --git a/module/documents/activeEffect.mjs b/module/documents/activeEffect.mjs index 5e9b0c3b..2297ea27 100644 --- a/module/documents/activeEffect.mjs +++ b/module/documents/activeEffect.mjs @@ -20,10 +20,7 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect { } if (this.parent?.type === 'domainCard') { - const isVaultSupressed = this.parent.system.isVaultSupressed; - const domainTouchedSupressed = this.parent.system.isDomainTouchedSuppressed; - - return isVaultSupressed || domainTouchedSupressed; + return this.parent.system.inVault; } return super.isSuppressed; @@ -109,29 +106,23 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect { /**@inheritdoc*/ static applyField(model, change, field) { - change.value = DhActiveEffect.getChangeValue(model, change, change.effect); - super.applyField(model, change, field); - } - - /** */ - static getChangeValue(model, change, effect) { - let value = change.value; - const isOriginTarget = value.toLowerCase().includes('origin.@'); + const isOriginTarget = change.value.toLowerCase().includes('origin.@'); let parseModel = model; - if (isOriginTarget && effect.origin) { - value = change.value.replaceAll(/origin\.@/gi, '@'); + if (isOriginTarget && change.effect.origin) { + change.value = change.value.replaceAll(/origin\.@/gi, '@'); try { - const originEffect = foundry.utils.fromUuidSync(effect.origin); + const effect = foundry.utils.fromUuidSync(change.effect.origin); const doc = - originEffect.parent?.parent instanceof game.system.api.documents.DhpActor - ? originEffect.parent - : originEffect.parent.parent; + effect.parent?.parent instanceof game.system.api.documents.DhpActor + ? effect.parent + : effect.parent.parent; if (doc) parseModel = doc; } catch (_) {} } - const evalValue = this.effectSafeEval(itemAbleRollParse(value, parseModel, effect.parent)); - return evalValue ?? value; + const evalValue = this.effectSafeEval(itemAbleRollParse(change.value, parseModel, change.effect.parent)); + change.value = evalValue ?? change.value; + super.applyField(model, change, field); } /** diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index 1a4153ad..06b60447 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -1,7 +1,7 @@ import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs'; import { LevelOptionType } from '../data/levelTier.mjs'; import DHFeature from '../data/item/feature.mjs'; -import { createScrollText, damageKeyToNumber, getDamageKey } from '../helpers/utils.mjs'; +import { createScrollText, damageKeyToNumber } from '../helpers/utils.mjs'; import DhCompanionLevelUp from '../applications/levelup/companionLevelup.mjs'; import { ResourceUpdateMap } from '../data/action/baseAction.mjs'; @@ -539,11 +539,7 @@ export default class DhpActor extends Actor { /**@inheritdoc */ getRollData() { - const rollData = foundry.utils.deepClone(super.getRollData()); - /* system gets repeated infinately which causes issues when trying to use the data for document creation */ - delete rollData.system; - - rollData.id = this.id; + const rollData = super.getRollData().clone(); rollData.name = this.name; rollData.system = this.system.getRollData(); rollData.prof = this.system.proficiency ?? 1; @@ -631,32 +627,6 @@ export default class DhpActor extends Actor { } } } - if (this.type === 'adversary') { - const reducedSeverity = hpDamage.damageTypes.reduce((value, curr) => { - return Math.max(this.system.rules.damageReduction.reduceSeverity[curr], value); - }, 0); - hpDamage.value = Math.max(hpDamage.value - reducedSeverity, 0); - - if ( - hpDamage.value && - this.system.rules.damageReduction.thresholdImmunities[getDamageKey(hpDamage.value)] - ) { - hpDamage.value -= 1; - } - } - } - - const results = await game.system.registeredTriggers.runTrigger( - CONFIG.DH.TRIGGER.triggers.postDamageReduction.id, - this, - updates, - this - ); - - if (results?.length) { - const resourceMap = new ResourceUpdateMap(results[0].originActor); - for (var result of results) resourceMap.addResources(result.updates); - resourceMap.updateResources(); } updates.forEach( diff --git a/module/documents/chatMessage.mjs b/module/documents/chatMessage.mjs index d85bcb45..7e313891 100644 --- a/module/documents/chatMessage.mjs +++ b/module/documents/chatMessage.mjs @@ -157,10 +157,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { event.stopPropagation(); const config = foundry.utils.deepClone(this.system); config.event = event; - if (this.system.action) { - await this.system.action.addEffects(config); - await this.system.action.workflow.get('damage')?.execute(config, this._id, true); - } + await this.system.action?.workflow.get('damage')?.execute(config, this._id, true); Hooks.callAll(socketEvent.Refresh, { refreshType: RefreshType.TagTeamRoll }); await game.socket.emit(`system.${CONFIG.DH.id}`, { diff --git a/module/documents/item.mjs b/module/documents/item.mjs index 7607658c..2c6d68b5 100644 --- a/module/documents/item.mjs +++ b/module/documents/item.mjs @@ -31,7 +31,7 @@ export default class DHItem extends foundry.documents.Item { static async createDocuments(sources, operation) { // Ensure that items being created are valid to the actor its being added to const actor = operation.parent; - sources = actor?.system?.isItemValid ? sources.filter(s => actor.system.isItemValid(s)) : sources; + sources = actor?.system?.isItemValid ? sources.filter((s) => actor.system.isItemValid(s)) : sources; return super.createDocuments(sources, operation); } @@ -146,16 +146,6 @@ export default class DHItem extends foundry.documents.Item { /* -------------------------------------------- */ async use(event) { - /* DomainCard check. Can be expanded or made neater */ - if (this.system.isDomainTouchedSuppressed) { - return ui.notifications.warn( - game.i18n.format('DAGGERHEART.UI.Notifications.domainTouchRequirement', { - nr: this.domainTouched, - domain: game.i18n.localize(CONFIG.DH.DOMAIN.allDomains()[this.domain].label) - }) - ); - } - const actions = new Set(this.system.actionsList); if (actions?.size) { let action = actions.first(); diff --git a/module/documents/scene.mjs b/module/documents/scene.mjs index 7f880b1d..c6cdd2c2 100644 --- a/module/documents/scene.mjs +++ b/module/documents/scene.mjs @@ -37,30 +37,4 @@ export default class DhScene extends Scene { this.#sizeSyncBatch.clear(); this.updateEmbeddedDocuments('Token', entries, { animation: { movementSpeed: 1.5 } }); }, 0); - - prepareBaseData() { - super.prepareBaseData(); - - if (this instanceof game.system.api.documents.DhScene) { - const system = new game.system.api.data.scenes.DHScene(this.flags.daggerheart); - - // Register this scene to all environements - for (const environment of system.sceneEnvironments) { - environment.system.scenes?.add(this); - } - } - } - - _onDelete(options, userId) { - super._onDelete(options, userId); - - if (this instanceof game.system.api.documents.DhScene) { - const system = new game.system.api.data.scenes.DHScene(this.flags.daggerheart); - - // Clear this scene from all environments that aren't deleted - for (const environment of system.sceneEnvironments) { - environment?.system?.scenes?.delete(this); - } - } - } } diff --git a/module/documents/token.mjs b/module/documents/token.mjs index 4ac29264..c3babaa1 100644 --- a/module/documents/token.mjs +++ b/module/documents/token.mjs @@ -83,7 +83,7 @@ export default class DHToken extends CONFIG.Token.documentClass { if (combat?.system?.battleToggles?.length) { await combat.toggleModifierEffects( true, - tokens.filter(x => x.actor).map(x => x.actor) + tokens.map(x => x.actor) ); } super.createCombatants(tokens, combat ?? {}); @@ -95,7 +95,7 @@ export default class DHToken extends CONFIG.Token.documentClass { if (combat?.system?.battleToggles?.length) { await combat.toggleModifierEffects( false, - tokens.filter(x => x.actor).map(x => x.actor) + tokens.map(x => x.actor) ); } super.deleteCombatants(tokens, combat ?? {}); diff --git a/module/documents/tokenManager.mjs b/module/documents/tokenManager.mjs deleted file mode 100644 index be5467da..00000000 --- a/module/documents/tokenManager.mjs +++ /dev/null @@ -1,104 +0,0 @@ -/** - * A singleton class that handles preview tokens. - */ - -export default class DhTokenManager { - #activePreview; - #actor; - #resolve; - - /** - * Create a template preview, deactivating any existing ones. - * @param {object} data - */ - async createPreview(actor, tokenData) { - this.#actor = actor; - const token = await canvas.tokens._createPreview( - { - ...actor.prototypeToken, - displayName: 50, - ...tokenData - }, - { renderSheet: false, actor } - ); - - this.#activePreview = { - document: token.document, - object: token, - origin: { x: token.document.x, y: token.document.y } - }; - - this.#activePreview.events = { - contextmenu: this.#cancelTemplate.bind(this), - mousedown: this.#confirmTemplate.bind(this), - mousemove: this.#onDragMouseMove.bind(this) - }; - - canvas.stage.on('mousemove', this.#activePreview.events.mousemove); - canvas.stage.on('mousedown', this.#activePreview.events.mousedown); - canvas.app.view.addEventListener('contextmenu', this.#activePreview.events.contextmenu); - } - - /* Currently intended for using as a preview of where to create a token. (note the flag) */ - async createPreviewAsync(actor, tokenData = {}) { - return new Promise(resolve => { - this.#resolve = resolve; - this.createPreview(actor, { ...tokenData, flags: { daggerheart: { createPlacement: true } } }); - }); - } - - /** - * Handles the movement of the token preview on mousedrag. - * @param {mousemove Event} event - */ - #onDragMouseMove(event) { - event.stopPropagation(); - const { moveTime, object } = this.#activePreview; - const update = {}; - - const now = Date.now(); - if (now - (moveTime || 0) <= 16) return; - this.#activePreview.moveTime = now; - - let cursor = event.getLocalPosition(canvas.templates); - - Object.assign(update, canvas.grid.getTopLeftPoint(cursor)); - - object.document.updateSource(update); - object.renderFlags.set({ refresh: true }); - } - - /** - * Cancels the preview token on right-click. - * @param {contextmenu Event} event - */ - #cancelTemplate(_event, resolved) { - const { mousemove, mousedown, contextmenu } = this.#activePreview.events; - this.#activePreview.object.destroy(); - - canvas.stage.off('mousemove', mousemove); - canvas.stage.off('mousedown', mousedown); - canvas.app.view.removeEventListener('contextmenu', contextmenu); - if (this.#resolve && !resolved) this.#resolve(false); - } - - /** - * Creates a real Actor and token at the preview location and cancels the preview. - * @param {click Event} event - */ - async #confirmTemplate(event) { - event.stopPropagation(); - this.#cancelTemplate(event, true); - - const actor = this.#actor.inCompendium - ? await game.system.api.documents.DhpActor.create(this.#actor.toObject()) - : this.#actor; - const tokenData = await actor.getTokenDocument(); - const result = await canvas.scene.createEmbeddedDocuments('Token', [ - { ...tokenData, x: this.#activePreview.document.x, y: this.#activePreview.document.y } - ]); - - this.#activePreview = undefined; - if (this.#resolve && result.length) this.#resolve(result[0]); - } -} diff --git a/module/documents/tooltipManager.mjs b/module/documents/tooltipManager.mjs index c4b52bb5..b0a107b9 100644 --- a/module/documents/tooltipManager.mjs +++ b/module/documents/tooltipManager.mjs @@ -67,7 +67,7 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti if (item) { const isAction = item instanceof game.system.api.models.actions.actionsTypes.base; const isEffect = item instanceof ActiveEffect; - await this.enrichText(item); + await this.enrichText(item, isAction || isEffect); const type = isAction ? 'action' : isEffect ? 'effect' : item.type; html = await foundry.applications.handlebars.renderTemplate( @@ -202,20 +202,10 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti } } - async enrichText(item) { + async enrichText(item, flatStructure) { const { TextEditor } = foundry.applications.ux; - - if (item.system?.metadata?.hasDescription) { - const enrichedValue = - (await item.system?.getEnrichedDescription?.()) ?? - (await TextEditor.enrichHTML(item.system.description)); - foundry.utils.setProperty(item, 'system.enrichedDescription', enrichedValue); - } else if (item.description) { - const enrichedValue = await TextEditor.enrichHTML(item.description); - foundry.utils.setProperty(item, 'enrichedDescription', enrichedValue); - } - const enrichPaths = [ + { path: flatStructure ? '' : 'system', name: 'description' }, { path: 'system', name: 'features' }, { path: 'system', name: 'actions' }, { path: 'system', name: 'customActions' } @@ -230,15 +220,12 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti for (const [index, itemValue] of pathValue.entries()) { const itemIsAction = itemValue instanceof game.system.api.models.actions.actionsTypes.base; const value = itemIsAction || !itemValue?.item ? itemValue : itemValue.item; - const enrichedValue = - (await value.system?.getEnrichedDescription?.()) ?? - (await TextEditor.enrichHTML(value.system?.description ?? value.description)); + const enrichedValue = await TextEditor.enrichHTML(value.system?.description ?? value.description); if (itemIsAction) value.enrichedDescription = enrichedValue; else foundry.utils.setProperty(item, `${basePath}.${index}.enrichedDescription`, enrichedValue); } } else { - const enrichedValue = - (await item.system?.getEnrichedDescription?.()) ?? (await TextEditor.enrichHTML(pathValue)); + const enrichedValue = await TextEditor.enrichHTML(pathValue); foundry.utils.setProperty( item, `${data.path ? `${data.path}.` : ''}enriched${data.name.capitalize()}`, @@ -275,7 +262,7 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti const combat = game.combats.get(combatId); const adversaries = combat.turns?.filter(x => x.actor?.isNPC)?.map(x => ({ ...x.actor, type: x.actor.system.type })) ?? []; - const characters = combat.turns?.filter(x => !x.isNPC && x.actor) ?? []; + const characters = combat.turns?.filter(x => !x.isNPC) ?? []; const nrCharacters = characters.length; const currentBP = AdversaryBPPerEncounter(adversaries, characters); @@ -285,7 +272,7 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti ); const categories = combat.combatants.reduce((acc, combatant) => { - if (combatant.actor?.type === 'adversary') { + if (combatant.actor.type === 'adversary') { const keyData = Object.keys(acc).reduce((identifiers, categoryKey) => { if (identifiers) return identifiers; const category = acc[categoryKey]; @@ -365,7 +352,7 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti await combat.toggleModifierEffects( event.target.checked, - combat.combatants.filter(x => x.actor?.type === 'adversary').map(x => x.actor), + combat.combatants.filter(x => x.actor.type === 'adversary').map(x => x.actor), category, grouping ); diff --git a/module/enrichers/DualityRollEnricher.mjs b/module/enrichers/DualityRollEnricher.mjs index 536847f7..1d6404ff 100644 --- a/module/enrichers/DualityRollEnricher.mjs +++ b/module/enrichers/DualityRollEnricher.mjs @@ -86,9 +86,9 @@ export const enrichedDualityRoll = async ( const config = { event: event ?? {}, title: title, - headerTitle: label, roll: { trait: traitValue && target ? traitValue : null, + label: label, difficulty: difficulty, advantage, type: reaction ? 'reaction' : null @@ -101,7 +101,7 @@ export const enrichedDualityRoll = async ( await target.diceRoll(config); } else { // For no target, call DualityRoll directly with basic data - config.data = { experiences: {}, traits: {}, rules: {} }; + config.data = { experiences: {}, traits: {} }; config.source = { actor: null }; await CONFIG.Dice.daggerheart.DualityRoll.build(config); } diff --git a/module/enrichers/LookupEnricher.mjs b/module/enrichers/LookupEnricher.mjs index cc9af608..3566e112 100644 --- a/module/enrichers/LookupEnricher.mjs +++ b/module/enrichers/LookupEnricher.mjs @@ -3,11 +3,6 @@ import { parseInlineParams } from './parser.mjs'; export default function DhLookupEnricher(match, { rollData }) { const results = parseInlineParams(match[1], { first: 'formula' }); const element = document.createElement('span'); - - const lookupCommand = match[0]; - const lookupParam = match[1]; - const lookupText = Roll.replaceFormulaData(String(results.formula), rollData); - element.textContent = lookupText === lookupParam ? lookupCommand : lookupText; - + element.textContent = Roll.replaceFormulaData(String(results.formula), rollData); return element; } diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index a28725b1..396ed2fa 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -119,7 +119,7 @@ export const tagifyElement = (element, baseOptions, onChange, tagifyOptions = {} spellcheck='false' tabIndex="${this.settings.a11y.focusableTags ? 0 : -1}" class="${this.settings.classNames.tag} ${tagData.class ? tagData.class : ''}" - data-tooltip="${tagData.description ? htmlToText(tagData.description) : tagData.name}" + data-tooltip="${tagData.description || tagData.name}" ${this.getAttributes(tagData)}>
@@ -198,7 +198,7 @@ foundry.dice.terms.Die.prototype.selfCorrecting = function (modifier) { }; export const getDamageKey = damage => { - return ['none', 'minor', 'major', 'severe', 'massive', 'any'][damage]; + return ['none', 'minor', 'major', 'severe', 'massive','any'][damage]; }; export const getDamageLabel = damage => { @@ -474,10 +474,3 @@ export async function getCritDamageBonus(formula) { const critRoll = new Roll(formula); return critRoll.dice.reduce((acc, dice) => acc + dice.faces * dice.number, 0); } - -export function htmlToText(html) { - var tempDivElement = document.createElement('div'); - tempDivElement.innerHTML = html; - - return tempDivElement.textContent || tempDivElement.innerText || ''; -} diff --git a/module/systemRegistration/handlebars.mjs b/module/systemRegistration/handlebars.mjs index 97769181..32e047fd 100644 --- a/module/systemRegistration/handlebars.mjs +++ b/module/systemRegistration/handlebars.mjs @@ -32,7 +32,6 @@ export const preloadHandlebarsTemplates = async function () { 'systems/daggerheart/templates/actionTypes/effect.hbs', 'systems/daggerheart/templates/actionTypes/beastform.hbs', 'systems/daggerheart/templates/actionTypes/countdown.hbs', - 'systems/daggerheart/templates/actionTypes/summon.hbs', 'systems/daggerheart/templates/settings/components/settings-item-line.hbs', 'systems/daggerheart/templates/ui/tooltip/parts/tooltipChips.hbs', 'systems/daggerheart/templates/ui/tooltip/parts/tooltipTags.hbs', diff --git a/module/systemRegistration/socket.mjs b/module/systemRegistration/socket.mjs index a9e86917..046f1b68 100644 --- a/module/systemRegistration/socket.mjs +++ b/module/systemRegistration/socket.mjs @@ -37,8 +37,7 @@ export const GMUpdateEvent = { export const RefreshType = { Countdown: 'DhCoundownRefresh', TagTeamRoll: 'DhTagTeamRollRefresh', - EffectsDisplay: 'DhEffectsDisplayRefresh', - Scene: 'DhSceneRefresh' + EffectsDisplay: 'DhEffectsDisplayRefresh' }; export const registerSocketHooks = () => { diff --git a/src/packs/adversaries/adversary_Arch_Necromancer_WPEOIGfclNJxWb87.json b/src/packs/adversaries/adversary_Arch_Necromancer_WPEOIGfclNJxWb87.json index d4e506cb..4fc58990 100644 --- a/src/packs/adversaries/adversary_Arch_Necromancer_WPEOIGfclNJxWb87.json +++ b/src/packs/adversaries/adversary_Arch_Necromancer_WPEOIGfclNJxWb87.json @@ -533,31 +533,33 @@ "description": "

Spend a Fear to summon a @UUID[Compendium.daggerheart.adversaries.Actor.YhJrP7rTBiRdX5Fp]{Zombie Legion}, which appears at Close range and immediately takes the spotlight.

", "resource": null, "actions": { - "qSuWxC8xQOhnbBx9": { - "type": "summon", - "_id": "qSuWxC8xQOhnbBx9", + "gZg3AkzCYUTExjE6": { + "type": "effect", + "_id": "gZg3AkzCYUTExjE6", "systemPath": "actions", - "baseAction": false, "description": "", "chatDisplay": true, - "originItem": { - "type": "itemCollection" - }, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "fear", + "value": 1, + "step": null + } + ], "uses": { "value": null, "max": "", - "recovery": null, - "consumeOnSuccess": false + "recovery": null + }, + "effects": [], + "target": { + "type": "any", + "amount": null }, - "summon": [ - { - "actorUUID": "Compendium.daggerheart.adversaries.Actor.YhJrP7rTBiRdX5Fp", - "count": "1" - } - ], "name": "Spend Fear", + "img": "icons/magic/death/undead-zombie-grave-green.webp", "range": "" } }, diff --git a/src/packs/adversaries/adversary_Demon_of_Avarice_pnyjIGxxvurcWmTv.json b/src/packs/adversaries/adversary_Demon_of_Avarice_pnyjIGxxvurcWmTv.json index e4ba41fb..6bfb88a6 100644 --- a/src/packs/adversaries/adversary_Demon_of_Avarice_pnyjIGxxvurcWmTv.json +++ b/src/packs/adversaries/adversary_Demon_of_Avarice_pnyjIGxxvurcWmTv.json @@ -284,7 +284,7 @@ "key": "system.bonuses.roll.attack.bonus", "mode": 2, "value": "ITEM.@system.resource.value", - "priority": 21 + "priority": null } ], "disabled": false, diff --git a/src/packs/adversaries/adversary_Demon_of_Despair_kE4dfhqmIQpNd44e.json b/src/packs/adversaries/adversary_Demon_of_Despair_kE4dfhqmIQpNd44e.json index 188b2687..b1804074 100644 --- a/src/packs/adversaries/adversary_Demon_of_Despair_kE4dfhqmIQpNd44e.json +++ b/src/packs/adversaries/adversary_Demon_of_Despair_kE4dfhqmIQpNd44e.json @@ -312,14 +312,7 @@ "range": "melee" } }, - "changes": [ - { - "key": "system.rules.dualityRoll.defaultHopeDice", - "mode": 5, - "value": "d8", - "priority": null - } - ], + "changes": [], "disabled": false, "duration": { "startTime": null, @@ -330,7 +323,7 @@ "startRound": null, "startTurn": null }, - "description": "

All targets affected replace their Hope Die with a d8 until they roll a success with Hope or their next rest.

", + "description": "

All targets aff ected replace their Hope Die with a d8 until they roll a success with Hope or their next rest.

", "tint": "#ffffff", "statuses": [], "sort": 0, diff --git a/src/packs/adversaries/adversary_Demon_of_Wrath_5lphJAgzoqZI3VoG.json b/src/packs/adversaries/adversary_Demon_of_Wrath_5lphJAgzoqZI3VoG.json index 2341ee8a..9e838d6d 100644 --- a/src/packs/adversaries/adversary_Demon_of_Wrath_5lphJAgzoqZI3VoG.json +++ b/src/packs/adversaries/adversary_Demon_of_Wrath_5lphJAgzoqZI3VoG.json @@ -256,45 +256,34 @@ "description": "

Spend a Fear to boil the blood of all PCs within Far range. They use a d20 as their Fear Die until the end of the scene.

@Template[type:emanation|range:f]

", "resource": null, "actions": { - "jKvzbQT0vp66DDOH": { + "V142qYppCGJn8OiN": { "type": "effect", - "_id": "jKvzbQT0vp66DDOH", + "_id": "V142qYppCGJn8OiN", "systemPath": "actions", - "baseAction": false, "description": "", "chatDisplay": true, - "originItem": { - "type": "itemCollection" - }, "actionType": "action", "cost": [ { "scalable": false, "key": "fear", "value": 1, - "itemId": null, - "step": null, - "consumeOnSuccess": false + "step": null } ], "uses": { "value": null, "max": "", - "recovery": null, - "consumeOnSuccess": false + "recovery": null }, - "effects": [ - { - "_id": "gFeHLGgeRoDdd3VG", - "onSave": false - } - ], + "effects": [], "target": { - "type": "hostile", + "type": "self", "amount": null }, "name": "Spend Fear", - "range": "far" + "img": "icons/skills/melee/maneuver-greatsword-yellow.webp", + "range": "" } }, "originItemType": null, @@ -303,51 +292,7 @@ }, "_id": "a33PW8UkziliowlR", "img": "icons/skills/melee/maneuver-greatsword-yellow.webp", - "effects": [ - { - "name": "Battle Lust", - "img": "icons/skills/melee/maneuver-greatsword-yellow.webp", - "origin": "Compendium.daggerheart.adversaries.Actor.5lphJAgzoqZI3VoG.Item.a33PW8UkziliowlR", - "transfer": false, - "_id": "gFeHLGgeRoDdd3VG", - "type": "base", - "system": { - "rangeDependence": { - "enabled": false, - "type": "withinRange", - "target": "hostile", - "range": "melee" - } - }, - "changes": [ - { - "key": "system.rules.dualityRoll.defaultFearDice", - "mode": 5, - "value": "d20", - "priority": null - } - ], - "disabled": false, - "duration": { - "startTime": null, - "combat": null, - "seconds": null, - "rounds": null, - "turns": null, - "startRound": null, - "startTurn": null - }, - "description": "

You use a d20 as your Fear Die until the end of the scene.

", - "tint": "#ffffff", - "statuses": [], - "sort": 0, - "flags": {}, - "_stats": { - "compendiumSource": null - }, - "_key": "!actors.items.effects!5lphJAgzoqZI3VoG.a33PW8UkziliowlR.gFeHLGgeRoDdd3VG" - } - ], + "effects": [], "folder": null, "sort": 0, "ownership": { @@ -512,12 +457,11 @@ "img": "icons/creatures/unholy/demon-fire-horned-clawed.webp", "range": "" }, - "FlE6i0tbKEguF9wz": { - "type": "summon", - "_id": "FlE6i0tbKEguF9wz", + "7G6uWlFEeOLsJIWY": { + "type": "effect", + "_id": "7G6uWlFEeOLsJIWY", "systemPath": "actions", - "baseAction": false, - "description": "", + "description": "

Summon [[/r 1d4]]@UUID[Compendium.daggerheart.adversaries.Actor.3tqCjDwJAQ7JKqMb]{Minor Demons}, who appear at Close range.

", "chatDisplay": true, "originItem": { "type": "itemCollection" @@ -530,13 +474,13 @@ "recovery": null, "consumeOnSuccess": false }, - "summon": [ - { - "actorUUID": "Compendium.daggerheart.adversaries.Actor.3tqCjDwJAQ7JKqMb", - "count": "1d4" - } - ], + "effects": [], + "target": { + "type": "any", + "amount": null + }, "name": "Summon", + "img": "icons/creatures/unholy/demon-fire-horned-clawed.webp", "range": "" } }, diff --git a/src/packs/adversaries/adversary_Dryad_wR7cFKrHvRzbzhBT.json b/src/packs/adversaries/adversary_Dryad_wR7cFKrHvRzbzhBT.json index ca9ce647..f0a5d81c 100644 --- a/src/packs/adversaries/adversary_Dryad_wR7cFKrHvRzbzhBT.json +++ b/src/packs/adversaries/adversary_Dryad_wR7cFKrHvRzbzhBT.json @@ -363,31 +363,33 @@ "description": "

Spend a Fear to grow three @UUID[Compendium.daggerheart.adversaries.Actor.o63nS0k3wHu6EgKP]{Treant Sapling Minions}, who appear at Close range and immediately take the spotlight.

", "resource": null, "actions": { - "R84DdS0OIx2cUt1w": { - "type": "summon", - "_id": "R84DdS0OIx2cUt1w", + "84Q2b0zIY9c7Yhho": { + "type": "effect", + "_id": "84Q2b0zIY9c7Yhho", "systemPath": "actions", - "baseAction": false, "description": "", "chatDisplay": true, - "originItem": { - "type": "itemCollection" - }, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "fear", + "value": 1, + "step": null + } + ], "uses": { "value": null, "max": "", - "recovery": null, - "consumeOnSuccess": false + "recovery": null + }, + "effects": [], + "target": { + "type": "self", + "amount": null }, - "summon": [ - { - "actorUUID": "Compendium.daggerheart.adversaries.Actor.o63nS0k3wHu6EgKP", - "count": "3" - } - ], "name": "Spend Fear", + "img": "icons/magic/unholy/orb-hands-pink.webp", "range": "" } }, diff --git a/src/packs/adversaries/adversary_Green_Ooze_SHXedd9zZPVfUgUa.json b/src/packs/adversaries/adversary_Green_Ooze_SHXedd9zZPVfUgUa.json index b03b5495..c7446a11 100644 --- a/src/packs/adversaries/adversary_Green_Ooze_SHXedd9zZPVfUgUa.json +++ b/src/packs/adversaries/adversary_Green_Ooze_SHXedd9zZPVfUgUa.json @@ -510,41 +510,34 @@ "description": "

When the @Lookup[@name] has 3 or more HP marked, you can spend a Fear to split them into two @UUID[Compendium.daggerheart.adversaries.Actor.aLkLFuVoKz2NLoBK]{Tiny Green Oozes} (with no marked HP or Stress). Immediately spotlight both of them.

", "resource": null, "actions": { - "J8U7dw3cDSsEirr5": { - "type": "summon", - "_id": "J8U7dw3cDSsEirr5", + "s5mLw6DRGd76MLcC": { + "type": "effect", + "_id": "s5mLw6DRGd76MLcC", "systemPath": "actions", - "baseAction": false, "description": "", "chatDisplay": true, - "originItem": { - "type": "itemCollection" - }, "actionType": "action", "cost": [ { "scalable": false, "key": "fear", "value": 1, - "itemId": null, - "step": null, - "consumeOnSuccess": false + "step": null } ], "uses": { "value": null, "max": "", - "recovery": null, - "consumeOnSuccess": false + "recovery": null + }, + "effects": [], + "target": { + "type": "self", + "amount": null }, - "summon": [ - { - "actorUUID": "Compendium.daggerheart.adversaries.Actor.aLkLFuVoKz2NLoBK", - "count": "2" - } - ], "name": "Spend Fear", - "range": "self" + "img": "icons/creatures/slimes/slime-movement-pseudopods-green.webp", + "range": "" } }, "originItemType": null, diff --git a/src/packs/adversaries/adversary_Head_Vampire_i2UNbRvgyoSs07M6.json b/src/packs/adversaries/adversary_Head_Vampire_i2UNbRvgyoSs07M6.json index d5891359..9e948594 100644 --- a/src/packs/adversaries/adversary_Head_Vampire_i2UNbRvgyoSs07M6.json +++ b/src/packs/adversaries/adversary_Head_Vampire_i2UNbRvgyoSs07M6.json @@ -474,31 +474,33 @@ "description": "

Spend 2 Fear to summon [[/r 1d4]] @UUID[Compendium.daggerheart.adversaries.Actor.WWyUp6Mxl1S3KYUG]{Vampires}, who appear at Far range and immediately take the spotlight.

", "resource": null, "actions": { - "jGFOnU6PNdWU6iF4": { - "type": "summon", - "_id": "jGFOnU6PNdWU6iF4", + "5Q6RMUTiauKw0tDj": { + "type": "effect", + "_id": "5Q6RMUTiauKw0tDj", "systemPath": "actions", - "baseAction": false, "description": "", "chatDisplay": true, - "originItem": { - "type": "itemCollection" - }, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "fear", + "value": 2, + "step": null + } + ], "uses": { "value": null, "max": "", - "recovery": null, - "consumeOnSuccess": false + "recovery": null }, - "summon": [ - { - "actorUUID": "Compendium.daggerheart.adversaries.Actor.WWyUp6Mxl1S3KYUG", - "count": "1d4" - } - ], - "name": "Spend Fear", + "effects": [], + "target": { + "type": "any", + "amount": null + }, + "name": "Summon Vampires", + "img": "icons/creatures/mammals/bat-giant-tattered-purple.webp", "range": "" } }, diff --git a/src/packs/adversaries/adversary_Huge_Green_Ooze_6hbqmxDXFOzZJDk4.json b/src/packs/adversaries/adversary_Huge_Green_Ooze_6hbqmxDXFOzZJDk4.json index 3bb8ae96..6f64f883 100644 --- a/src/packs/adversaries/adversary_Huge_Green_Ooze_6hbqmxDXFOzZJDk4.json +++ b/src/packs/adversaries/adversary_Huge_Green_Ooze_6hbqmxDXFOzZJDk4.json @@ -479,31 +479,33 @@ "description": "

When the @Lookup[@name] has 4 or more HP marked, you can spend a Fear to split them into two @UUID[Compendium.daggerheart.adversaries.Actor.SHXedd9zZPVfUgUa]{Green Oozes}(with no marked HP or Stress). Immediately spotlight both of them.

", "resource": null, "actions": { - "aeRdkiRsDNagTKhp": { - "type": "summon", - "_id": "aeRdkiRsDNagTKhp", + "iQsYAqpUFvJslRDr": { + "type": "effect", + "_id": "iQsYAqpUFvJslRDr", "systemPath": "actions", - "baseAction": false, "description": "", "chatDisplay": true, - "originItem": { - "type": "itemCollection" - }, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "fear", + "value": 1, + "step": null + } + ], "uses": { "value": null, "max": "", - "recovery": null, - "consumeOnSuccess": false + "recovery": null + }, + "effects": [], + "target": { + "type": "any", + "amount": null }, - "summon": [ - { - "actorUUID": "Compendium.daggerheart.adversaries.Actor.SHXedd9zZPVfUgUa", - "count": "2" - } - ], "name": "Spend Fear", + "img": "icons/creatures/slimes/slime-movement-pseudopods-green.webp", "range": "" } }, diff --git a/src/packs/adversaries/adversary_Jagged_Knife_Lieutenant_aTljstqteGoLpCBq.json b/src/packs/adversaries/adversary_Jagged_Knife_Lieutenant_aTljstqteGoLpCBq.json index c139d76f..165bb160 100644 --- a/src/packs/adversaries/adversary_Jagged_Knife_Lieutenant_aTljstqteGoLpCBq.json +++ b/src/packs/adversaries/adversary_Jagged_Knife_Lieutenant_aTljstqteGoLpCBq.json @@ -287,35 +287,7 @@ "system": { "description": "

Summon three @Compendium[daggerheart.adversaries.Actor.C0OMQqV7pN6t7ouR], who appear at Far range.

", "resource": null, - "actions": { - "MCTBsw9lusUdubj0": { - "type": "summon", - "_id": "MCTBsw9lusUdubj0", - "systemPath": "actions", - "baseAction": false, - "description": "", - "chatDisplay": true, - "originItem": { - "type": "itemCollection" - }, - "actionType": "action", - "cost": [], - "uses": { - "value": null, - "max": "", - "recovery": null, - "consumeOnSuccess": false - }, - "summon": [ - { - "actorUUID": "Compendium.daggerheart.adversaries.Actor.C0OMQqV7pN6t7ouR", - "count": "3" - } - ], - "name": "Summon", - "range": "" - } - }, + "actions": {}, "originItemType": null, "subType": null, "originId": null, diff --git a/src/packs/adversaries/adversary_Petty_Noble_wycLpvebWdUqRhpP.json b/src/packs/adversaries/adversary_Petty_Noble_wycLpvebWdUqRhpP.json index db284f40..4ac7e746 100644 --- a/src/packs/adversaries/adversary_Petty_Noble_wycLpvebWdUqRhpP.json +++ b/src/packs/adversaries/adversary_Petty_Noble_wycLpvebWdUqRhpP.json @@ -258,40 +258,57 @@ "description": "

Once per scene, mark a Stress to summon 1d4 @UUID[Compendium.daggerheart.adversaries.Actor.B4LZcGuBAHzyVdzy]{Bladed Guards}, who appear at Far range to enforce the @Lookup[@name]’s will.

", "resource": null, "actions": { - "tioTtYfIGFIXRITN": { - "type": "summon", - "_id": "tioTtYfIGFIXRITN", + "cUKwhq1imsTVru8D": { + "type": "attack", + "_id": "cUKwhq1imsTVru8D", "systemPath": "actions", - "baseAction": false, - "description": "", + "description": "

Once per scene, mark a Stress to summon 1d4 @UUID[Compendium.daggerheart.adversaries.Actor.B4LZcGuBAHzyVdzy]{Bladed Guards}, who appear at Far range to enforce the Noble’s will.

", "chatDisplay": true, - "originItem": { - "type": "itemCollection" - }, "actionType": "action", "cost": [ { "scalable": false, "key": "stress", "value": 1, - "itemId": null, - "step": null, - "consumeOnSuccess": false + "step": null } ], "uses": { "value": null, - "max": "1", - "recovery": "scene", - "consumeOnSuccess": false + "max": "", + "recovery": null + }, + "damage": { + "parts": [], + "includeBase": false + }, + "target": { + "type": "any", + "amount": null + }, + "effects": [], + "roll": { + "type": "diceSet", + "trait": null, + "difficulty": null, + "bonus": null, + "advState": "neutral", + "diceRolling": { + "multiplier": "prof", + "flatMultiplier": 1, + "dice": "d4", + "compare": null, + "treshold": null + }, + "useDefault": false + }, + "save": { + "trait": null, + "difficulty": null, + "damageMod": "none" }, - "summon": [ - { - "actorUUID": "Compendium.daggerheart.adversaries.Actor.B4LZcGuBAHzyVdzy", - "count": "1d4" - } - ], "name": "Summon Guards", + "img": "icons/environment/people/infantry-armored.webp", "range": "" } }, diff --git a/src/packs/adversaries/adversary_Pirate_Captain_OROJbjsqagVh7ECV.json b/src/packs/adversaries/adversary_Pirate_Captain_OROJbjsqagVh7ECV.json index 5b00ec60..409d7698 100644 --- a/src/packs/adversaries/adversary_Pirate_Captain_OROJbjsqagVh7ECV.json +++ b/src/packs/adversaries/adversary_Pirate_Captain_OROJbjsqagVh7ECV.json @@ -313,43 +313,36 @@ "_id": "WGEGO0DSOs5cF0EL", "img": "icons/environment/people/charge.webp", "system": { - "description": "

Once per scene, mark a Stress to summon a @UUID[Compendium.daggerheart.adversaries.Actor.5YgEajn0wa4i85kC]{Pirate Raider Horde}, which appears at Far range.

", + "description": "

Once per scene, mark a Stress to summon a Pirate Raiders Horde, which appears at Far range.

", "resource": null, "actions": { - "nuYk5WeLLpIKa69q": { - "type": "summon", - "_id": "nuYk5WeLLpIKa69q", + "NlgIp0KrmZoS27Xy": { + "type": "effect", + "_id": "NlgIp0KrmZoS27Xy", "systemPath": "actions", - "baseAction": false, "description": "", "chatDisplay": true, - "originItem": { - "type": "itemCollection" - }, "actionType": "action", "cost": [ { "scalable": false, "key": "stress", "value": 1, - "itemId": null, - "step": null, - "consumeOnSuccess": false + "step": null } ], "uses": { "value": null, "max": "", - "recovery": null, - "consumeOnSuccess": false + "recovery": null + }, + "effects": [], + "target": { + "type": "any", + "amount": null }, - "summon": [ - { - "actorUUID": "Compendium.daggerheart.adversaries.Actor.5YgEajn0wa4i85kC", - "count": "1" - } - ], "name": "Mark Stress", + "img": "icons/environment/people/charge.webp", "range": "" } }, diff --git a/src/packs/adversaries/adversary_Red_Ooze_9rVlbJVrDNn1x7PS.json b/src/packs/adversaries/adversary_Red_Ooze_9rVlbJVrDNn1x7PS.json index 2c10ae3f..320b71af 100644 --- a/src/packs/adversaries/adversary_Red_Ooze_9rVlbJVrDNn1x7PS.json +++ b/src/packs/adversaries/adversary_Red_Ooze_9rVlbJVrDNn1x7PS.json @@ -454,40 +454,33 @@ "description": "

When the @Lookup[@name] has 3 or more HP marked, you can spend a Fear to split them into two @UUID[Compendium.daggerheart.adversaries.Actor.1fkLQXVtmILqfJ44]{Tiny Red Oozes} (with no marked HP or Stress). Immediately spotlight both of them.

", "resource": null, "actions": { - "BMEr77hDxaQyYBna": { - "type": "summon", - "_id": "BMEr77hDxaQyYBna", + "dw6Juw8mriH7sg0e": { + "type": "effect", + "_id": "dw6Juw8mriH7sg0e", "systemPath": "actions", - "baseAction": false, "description": "", "chatDisplay": true, - "originItem": { - "type": "itemCollection" - }, "actionType": "action", "cost": [ { "scalable": false, "key": "fear", "value": 1, - "itemId": null, - "step": null, - "consumeOnSuccess": false + "step": null } ], "uses": { "value": null, "max": "", - "recovery": null, - "consumeOnSuccess": false + "recovery": null + }, + "effects": [], + "target": { + "type": "any", + "amount": null }, - "summon": [ - { - "actorUUID": "Compendium.daggerheart.adversaries.Actor.1fkLQXVtmILqfJ44", - "count": "2" - } - ], "name": "Spend Fear", + "img": "icons/creatures/slimes/slime-movement-splashing-red.webp", "range": "" } }, diff --git a/src/packs/adversaries/adversary_Secret_Keeper_sLAccjvCWfeedbpI.json b/src/packs/adversaries/adversary_Secret_Keeper_sLAccjvCWfeedbpI.json index d17c3f86..0c8757c5 100644 --- a/src/packs/adversaries/adversary_Secret_Keeper_sLAccjvCWfeedbpI.json +++ b/src/packs/adversaries/adversary_Secret_Keeper_sLAccjvCWfeedbpI.json @@ -416,6 +416,28 @@ "description": "

Countdown (6). When the @Lookup[@name] is in the spotlight for the first time, activate the countdown. When they mark HP, tick down this countdown by the number of HP marked. When it triggers, summon a @UUID[Compendium.daggerheart.adversaries.Actor.3tqCjDwJAQ7JKqMb]{Minor Demon} who appears at Close range.

", "resource": null, "actions": { + "0rixG6jLRynAYNqA": { + "type": "effect", + "_id": "0rixG6jLRynAYNqA", + "systemPath": "actions", + "description": "

Summon a @UUID[Compendium.daggerheart.adversaries.Actor.3tqCjDwJAQ7JKqMb]{Minor Demon} who appears at Close range.

", + "chatDisplay": true, + "actionType": "action", + "cost": [], + "uses": { + "value": null, + "max": "", + "recovery": null + }, + "effects": [], + "target": { + "type": "any", + "amount": null + }, + "name": "Summon", + "img": "icons/magic/unholy/silhouette-light-fire-blue.webp", + "range": "close" + }, "ZVXHY2fpomoKV7jG": { "type": "countdown", "_id": "ZVXHY2fpomoKV7jG", @@ -452,33 +474,6 @@ "name": "Start Countdown", "img": "icons/magic/unholy/silhouette-light-fire-blue.webp", "range": "" - }, - "YReYG6DrWp4QGSij": { - "type": "summon", - "_id": "YReYG6DrWp4QGSij", - "systemPath": "actions", - "baseAction": false, - "description": "", - "chatDisplay": true, - "originItem": { - "type": "itemCollection" - }, - "actionType": "action", - "cost": [], - "uses": { - "value": null, - "max": "", - "recovery": null, - "consumeOnSuccess": false - }, - "summon": [ - { - "actorUUID": "Compendium.daggerheart.adversaries.Actor.3tqCjDwJAQ7JKqMb", - "count": "1" - } - ], - "name": "Summon", - "range": "" } }, "originItemType": null, @@ -507,31 +502,33 @@ "description": "

Once per scene, when the @Lookup[@name] marks 2 or more HP, you can mark a Stress to summon a @UUID[Compendium.daggerheart.adversaries.Actor.NoRZ1PqB8N5wcIw0]{Demonic Hound Pack}, which appears at Close range and is immediately spotlighted.

", "resource": null, "actions": { - "tfmY6HYkkY27NBaF": { - "type": "summon", - "_id": "tfmY6HYkkY27NBaF", + "JBuQUJhif2A7IlJd": { + "type": "effect", + "_id": "JBuQUJhif2A7IlJd", "systemPath": "actions", - "baseAction": false, "description": "", "chatDisplay": true, - "originItem": { - "type": "itemCollection" - }, "actionType": "action", - "cost": [], - "uses": { - "value": null, - "max": "", - "recovery": null, - "consumeOnSuccess": false - }, - "summon": [ + "cost": [ { - "actorUUID": "Compendium.daggerheart.adversaries.Actor.NoRZ1PqB8N5wcIw0", - "count": "1" + "scalable": false, + "key": "stress", + "value": 1, + "step": null } ], + "uses": { + "value": null, + "max": "1", + "recovery": "scene" + }, + "effects": [], + "target": { + "type": "self", + "amount": null + }, "name": "Mark Stress", + "img": "icons/creatures/unholy/demon-fire-horned-clawed.webp", "range": "" } }, diff --git a/src/packs/adversaries/adversary_Tangle_Bramble_XcAGOSmtCFLT1unN.json b/src/packs/adversaries/adversary_Tangle_Bramble_XcAGOSmtCFLT1unN.json index 0f1ba28f..a6e5ca17 100644 --- a/src/packs/adversaries/adversary_Tangle_Bramble_XcAGOSmtCFLT1unN.json +++ b/src/packs/adversaries/adversary_Tangle_Bramble_XcAGOSmtCFLT1unN.json @@ -340,35 +340,7 @@ "system": { "description": "

When an attack from the @Lookup[@name] causes a target to mark HP and there are three or more @Lookup[@name] Minions within Close range, you can combine the Minions into a @UUID[Compendium.daggerheart.adversaries.Actor.PKSXFuaIHUCoH63A]{Tangle Bramble Swarm Horde}. The Horde’s HP is equal to the number of Minions combined.

", "resource": null, - "actions": { - "g1OQ5xlMHFWsoktd": { - "type": "summon", - "_id": "g1OQ5xlMHFWsoktd", - "systemPath": "actions", - "baseAction": false, - "description": "", - "chatDisplay": true, - "originItem": { - "type": "itemCollection" - }, - "actionType": "action", - "cost": [], - "uses": { - "value": null, - "max": "", - "recovery": null, - "consumeOnSuccess": false - }, - "summon": [ - { - "actorUUID": "Compendium.daggerheart.adversaries.Actor.PKSXFuaIHUCoH63A", - "count": "1" - } - ], - "name": "Summon", - "range": "" - } - }, + "actions": {}, "originItemType": null, "subType": null, "originId": null, diff --git a/src/packs/ancestries/feature_Shell_A6a87OWA3tx16g9V.json b/src/packs/ancestries/feature_Shell_A6a87OWA3tx16g9V.json index f07fff69..0620296a 100644 --- a/src/packs/ancestries/feature_Shell_A6a87OWA3tx16g9V.json +++ b/src/packs/ancestries/feature_Shell_A6a87OWA3tx16g9V.json @@ -36,13 +36,13 @@ "key": "system.damageThresholds.major", "mode": 2, "value": "@prof", - "priority": 21 + "priority": null }, { "key": "system.damageThresholds.severe", "mode": 2, "value": "@prof", - "priority": 21 + "priority": null } ], "disabled": false, diff --git a/src/packs/beastforms/feature_Armored_Shell_nDQZdIF2epKlhauX.json b/src/packs/beastforms/feature_Armored_Shell_nDQZdIF2epKlhauX.json index 7aaefef8..e56cfa68 100644 --- a/src/packs/beastforms/feature_Armored_Shell_nDQZdIF2epKlhauX.json +++ b/src/packs/beastforms/feature_Armored_Shell_nDQZdIF2epKlhauX.json @@ -100,7 +100,7 @@ "key": "system.resistance.physical.reduction", "mode": 2, "value": "@system.armorScore", - "priority": 21 + "priority": null } ], "disabled": false, diff --git a/src/packs/classes/feature_Strange_Patterns_6YsfFjmCGuFYVhT4.json b/src/packs/classes/feature_Strange_Patterns_6YsfFjmCGuFYVhT4.json index 95f42c06..bd364e6f 100644 --- a/src/packs/classes/feature_Strange_Patterns_6YsfFjmCGuFYVhT4.json +++ b/src/packs/classes/feature_Strange_Patterns_6YsfFjmCGuFYVhT4.json @@ -80,14 +80,7 @@ }, "name": "Clear Stress", "img": "icons/magic/symbols/rune-sigil-black-pink.webp", - "range": "", - "triggers": [ - { - "trigger": "dualityRoll", - "triggeringActorType": "self", - "command": "/* Ignore if it's a TagTeam roll */\nconst tagTeam = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.TagTeamRoll);\nif (tagTeam.members[actor.id]) return;\n\n/* Check if there's a Strange Pattern match */\nconst dice = [roll.dFear.total, roll.dHope.total];\nconst resource = this.parent.resource?.diceStates ? Object.values(this.parent.resource.diceStates).map(x => x.value)[0] : null;\nconst nrMatches = dice.filter(x => x === resource).length;\n\nif (!nrMatches) return;\n\n/* Create a dialog to choose Hope or Stress - or to cancel*/\nconst content = `\n
${game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsContentTitle', { nr: nrMatches })}
\n
${game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.strangePatternsContentSubTitle', { nr: nrMatches })}
\n
\n \n \n
\n
`;\n\nconst result = await foundry.applications.api.DialogV2.input({\n classes: ['dh-style', 'two-big-buttons'],\n window: { title: this.item.name },\n content: content,\n render: (_, dialog) => {\n const hopeButton = dialog.element.querySelector('#hopeButton');\n const stressButton = dialog.element.querySelector('#stressButton');\ndialog.element.querySelector('button[type=\"submit\"]').disabled = true;\n \n const updateFunc = (event, selector, adding, clamp) => {\n const button = event.target.closest(`#${selector}Button`);\n const parent = event.target.closest('.flexrow');\n const hope = Number.parseInt(parent.querySelector('#hopeButton label').innerHTML);\n const stress = Number.parseInt(parent.querySelector('#stressButton label').innerHTML);\n const currentTotal = (Number.isNumeric(hope) ? hope : 0) + (Number.isNumeric(stress) ? stress : 0);\n if (adding && currentTotal === nrMatches) return;\n \n const current = Number.parseInt(button.querySelector('label').innerHTML);\n if (!adding && current === 0) return;\n \n const value = Number.isNumeric(current) ? adding ? current+1 : current-1 : 1;\n if (!dialog.data) dialog.data = {};\n dialog.data[selector] = clamp(value);\n button.querySelector('label').innerHTML = dialog.data[selector];\n\n event.target.closest('.dialog-form').querySelector('button[type=\"submit\"]').disabled = !adding || currentTotal < (nrMatches-1);\n \n };\n hopeButton.addEventListener('click', event => updateFunc(event, 'hope', true, x => Math.min(x, nrMatches)));\n hopeButton.addEventListener('contextmenu', event => updateFunc(event, 'hope', false, x => Math.max(x, 0)));\n stressButton.addEventListener('click', event => updateFunc(event, 'stress', true, x => Math.min(x, nrMatches)));\n stressButton.addEventListener('contextmenu', event => updateFunc(event, 'stress', false, x => Math.max(x, 0)));\n },\n ok: { callback: (_event, _result, dialog) => {\n const hope = dialog.data.hope ?? 0;\n const stress = dialog.data.stress ?? 0;\n if (!hope && !stress) return;\n\n /* Return resource update according to choices */\n const hopeUpdate = hope ? { key: 'hope', value: hope, total: -hope, enabled: true } : null;\n const stressUpdate = stress ? { key: 'stress', value: -stress, total: stress, enabled: true } : null;\n return { updates: [hopeUpdate, stressUpdate].filter(x => x) };\n }}\n});\n\nreturn result;" - } - ] + "range": "" } }, "originItemType": null, diff --git a/src/packs/domains/domainCard_Arcana_Touched_5PvMQKCjrgSxzstn.json b/src/packs/domains/domainCard_Arcana_Touched_5PvMQKCjrgSxzstn.json index 556d5074..80d9797f 100644 --- a/src/packs/domains/domainCard_Arcana_Touched_5PvMQKCjrgSxzstn.json +++ b/src/packs/domains/domainCard_Arcana_Touched_5PvMQKCjrgSxzstn.json @@ -54,8 +54,7 @@ "source": "Daggerheart SRD", "page": 120, "artist": "" - }, - "domainTouched": 4 + } }, "flags": {}, "_id": "5PvMQKCjrgSxzstn", diff --git a/src/packs/domains/domainCard_Blade_Touched_Gb5bqpFSBiuBxUix.json b/src/packs/domains/domainCard_Blade_Touched_Gb5bqpFSBiuBxUix.json index ebb95570..d2d5dafc 100644 --- a/src/packs/domains/domainCard_Blade_Touched_Gb5bqpFSBiuBxUix.json +++ b/src/packs/domains/domainCard_Blade_Touched_Gb5bqpFSBiuBxUix.json @@ -13,8 +13,7 @@ "source": "Daggerheart SRD", "page": 121, "artist": "" - }, - "domainTouched": 4 + } }, "flags": {}, "_id": "Gb5bqpFSBiuBxUix", diff --git a/src/packs/domains/domainCard_Body_Basher_aQz8jKkCd8M9aKMA.json b/src/packs/domains/domainCard_Body_Basher_aQz8jKkCd8M9aKMA.json index f0d4c988..dfb5af6d 100644 --- a/src/packs/domains/domainCard_Body_Basher_aQz8jKkCd8M9aKMA.json +++ b/src/packs/domains/domainCard_Body_Basher_aQz8jKkCd8M9aKMA.json @@ -38,13 +38,13 @@ "key": "system.bonuses.damage.primaryWeapon.bonus", "mode": 2, "value": "@system.traits.strength.value", - "priority": 21 + "priority": null }, { "key": "system.bonuses.damage.secondaryWeapon.bonus", "mode": 2, "value": "@system.traits.strength.value", - "priority": 21 + "priority": null } ], "disabled": false, @@ -57,7 +57,7 @@ "startRound": null, "startTurn": null }, - "description": "

On a successful attack using a weapon with a Melee range, gain a bonus to your damage roll equal to your Strength.

", + "description": "

On a successful attack using a weapon with a Melee range, gain a bonus to your damage roll equal to your Strength.

", "origin": null, "tint": "#ffffff", "transfer": true, diff --git a/src/packs/domains/domainCard_Bone_Touched_ON5bvnoQBy0SYc9Y.json b/src/packs/domains/domainCard_Bone_Touched_ON5bvnoQBy0SYc9Y.json index 8880bb07..770ddd63 100644 --- a/src/packs/domains/domainCard_Bone_Touched_ON5bvnoQBy0SYc9Y.json +++ b/src/packs/domains/domainCard_Bone_Touched_ON5bvnoQBy0SYc9Y.json @@ -46,8 +46,7 @@ "source": "Daggerheart SRD", "page": 123, "artist": "" - }, - "domainTouched": 4 + } }, "flags": {}, "_id": "ON5bvnoQBy0SYc9Y", diff --git a/src/packs/domains/domainCard_Codex_Touched_7Pu83ABdMukTxu3e.json b/src/packs/domains/domainCard_Codex_Touched_7Pu83ABdMukTxu3e.json index 6443ed6a..1e2d5de3 100644 --- a/src/packs/domains/domainCard_Codex_Touched_7Pu83ABdMukTxu3e.json +++ b/src/packs/domains/domainCard_Codex_Touched_7Pu83ABdMukTxu3e.json @@ -71,8 +71,7 @@ "source": "Daggerheart SRD", "page": 125, "artist": "" - }, - "domainTouched": 4 + } }, "flags": {}, "_id": "7Pu83ABdMukTxu3e", diff --git a/src/packs/domains/domainCard_Cruel_Precision_bap1eCWryPNowbyo.json b/src/packs/domains/domainCard_Cruel_Precision_bap1eCWryPNowbyo.json index 9e5c414e..cdb5ffe6 100644 --- a/src/packs/domains/domainCard_Cruel_Precision_bap1eCWryPNowbyo.json +++ b/src/packs/domains/domainCard_Cruel_Precision_bap1eCWryPNowbyo.json @@ -37,13 +37,13 @@ "key": "system.bonuses.damage.primaryWeapon.bonus", "mode": 2, "value": "@system.traits.agility.value", - "priority": 21 + "priority": null }, { "key": "system.bonuses.damage.secondaryWeapon.bonus", "mode": 2, "value": "@system.traits.agility.value", - "priority": 21 + "priority": null } ], "disabled": true, diff --git a/src/packs/domains/domainCard_Ferocity_jSQsSP61CX4MhSN7.json b/src/packs/domains/domainCard_Ferocity_jSQsSP61CX4MhSN7.json index 78593c62..9e46e6ba 100644 --- a/src/packs/domains/domainCard_Ferocity_jSQsSP61CX4MhSN7.json +++ b/src/packs/domains/domainCard_Ferocity_jSQsSP61CX4MhSN7.json @@ -17,16 +17,7 @@ "description": "

When you cause an adversary to mark 1 or more Hit Points, you can spend 2 Hope to increase your Evasion by the number of Hit Points they marked. This bonus lasts until after the next attack made against you.

", "chatDisplay": true, "actionType": "action", - "cost": [ - { - "scalable": false, - "key": "hope", - "value": 2, - "itemId": null, - "step": null, - "consumeOnSuccess": false - } - ], + "cost": [], "uses": { "value": null, "max": "", @@ -39,15 +30,8 @@ "amount": null }, "name": "Spend Hope", - "img": "icons/skills/melee/maneuver-sword-katana-yellow.webp", - "range": "", - "triggers": [ - { - "trigger": "postDamageReduction", - "triggeringActorType": "other", - "command": "/* Check if sufficient hope */\nif (this.actor.system.resources.hope.value < 2) return;\n\n/* Check if hit point damage was dealt */\nconst hpDamage = damageUpdates.find(u => u.key === CONFIG.DH.GENERAL.healingTypes.hitPoints.id);\nif (hpDamage.value < 0) return;\n\n/* Dialog to give player choice */\nconst confirmed = await foundry.applications.api.DialogV2.confirm({\n window: { title: this.item?.name ?? '' },\n content: game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.ferocityContent', { bonus: hpDamage.value }),\n});\n\nif (!confirmed) return;\n\n/* Create the effect */\nthis.actor.createEmbeddedDocuments('ActiveEffect', [{\n name: this.item.name,\n img: 'icons/skills/melee/maneuver-sword-katana-yellow.webp',\n description: game.i18n.format('DAGGERHEART.CONFIG.Triggers.triggerTexts.ferocityEffectDescription', { bonus: hpDamage.value }),\n changes: [{ key: 'system.evasion', mode: 2, value: hpDamage.value }]\n}]);\n\n/* Update hope */\nreturn { updates: [{ \n originActor: this.actor, \n updates: [{\n key: CONFIG.DH.GENERAL.healingTypes.hope.id,\n value: -2,\n total: 2\n }] \n}]}" - } - ] + "img": "icons/skills/melee/maneuver-daggers-paired-orange.webp", + "range": "" } }, "attribution": { diff --git a/src/packs/domains/domainCard_Get_Back_Up_BFWN2cObMdlk9uVz.json b/src/packs/domains/domainCard_Get_Back_Up_BFWN2cObMdlk9uVz.json index 571a3fb4..ab74e805 100644 --- a/src/packs/domains/domainCard_Get_Back_Up_BFWN2cObMdlk9uVz.json +++ b/src/packs/domains/domainCard_Get_Back_Up_BFWN2cObMdlk9uVz.json @@ -18,7 +18,7 @@ }, "flags": {}, "_id": "BFWN2cObMdlk9uVz", - "sort": 3500000, + "sort": 3400000, "effects": [ { "name": "Get Back Up", diff --git a/src/packs/domains/domainCard_Grace_Touched_KAuNb51AwhD8KEXk.json b/src/packs/domains/domainCard_Grace_Touched_KAuNb51AwhD8KEXk.json index 346a81f2..b87ea24d 100644 --- a/src/packs/domains/domainCard_Grace_Touched_KAuNb51AwhD8KEXk.json +++ b/src/packs/domains/domainCard_Grace_Touched_KAuNb51AwhD8KEXk.json @@ -96,8 +96,7 @@ "source": "Daggerheart SRD", "page": 127, "artist": "" - }, - "domainTouched": 4 + } }, "flags": {}, "_id": "KAuNb51AwhD8KEXk", diff --git a/src/packs/domains/domainCard_Midnight_Touched_uSyGKVxOJcnp28po.json b/src/packs/domains/domainCard_Midnight_Touched_uSyGKVxOJcnp28po.json index 10c42418..3370c30e 100644 --- a/src/packs/domains/domainCard_Midnight_Touched_uSyGKVxOJcnp28po.json +++ b/src/packs/domains/domainCard_Midnight_Touched_uSyGKVxOJcnp28po.json @@ -111,8 +111,7 @@ "source": "Daggerheart SRD", "page": 129, "artist": "" - }, - "domainTouched": 4 + } }, "flags": {}, "_id": "uSyGKVxOJcnp28po", diff --git a/src/packs/domains/domainCard_Notorious_IqxzvvjZiYbgx21A.json b/src/packs/domains/domainCard_Notorious_IqxzvvjZiYbgx21A.json index 2e5f5ffd..dfb581e7 100644 --- a/src/packs/domains/domainCard_Notorious_IqxzvvjZiYbgx21A.json +++ b/src/packs/domains/domainCard_Notorious_IqxzvvjZiYbgx21A.json @@ -44,8 +44,7 @@ "source": "Daggerheart SRD", "page": 127, "artist": "" - }, - "loadoutIgnore": true + } }, "flags": {}, "_id": "IqxzvvjZiYbgx21A", diff --git a/src/packs/domains/domainCard_Overwhelming_Aura_iEBLySZD9z8CLdz7.json b/src/packs/domains/domainCard_Overwhelming_Aura_iEBLySZD9z8CLdz7.json index cc04c9c9..e5282328 100644 --- a/src/packs/domains/domainCard_Overwhelming_Aura_iEBLySZD9z8CLdz7.json +++ b/src/packs/domains/domainCard_Overwhelming_Aura_iEBLySZD9z8CLdz7.json @@ -101,7 +101,7 @@ "key": "system.traits.presence.value", "mode": 5, "value": "@cast", - "priority": 51 + "priority": null } ], "disabled": false, diff --git a/src/packs/domains/domainCard_Rage_Up_GRL0cvs96vrTDckZ.json b/src/packs/domains/domainCard_Rage_Up_GRL0cvs96vrTDckZ.json index 6f8b481d..94472445 100644 --- a/src/packs/domains/domainCard_Rage_Up_GRL0cvs96vrTDckZ.json +++ b/src/packs/domains/domainCard_Rage_Up_GRL0cvs96vrTDckZ.json @@ -113,13 +113,13 @@ "key": "system.bonuses.damage.magical.bonus", "mode": 2, "value": "2*@system.traits.strength.value", - "priority": 21 + "priority": null }, { "key": "system.bonuses.damage.physical.bonus", "mode": 2, "value": "2*@system.traits.strength.value", - "priority": 21 + "priority": null } ], "disabled": false, @@ -162,13 +162,13 @@ "key": "system.bonuses.damage.magical.bonus", "mode": 2, "value": "4*@system.traits.strength.value", - "priority": 21 + "priority": null }, { "key": "system.bonuses.damage.physical.bonus", "mode": 2, "value": "4*@system.traits.strength.value", - "priority": 21 + "priority": null } ], "disabled": false, diff --git a/src/packs/domains/domainCard_Rise_Up_oDIZoC4l19Nli0Fj.json b/src/packs/domains/domainCard_Rise_Up_oDIZoC4l19Nli0Fj.json index 38c900b2..28fd2f1f 100644 --- a/src/packs/domains/domainCard_Rise_Up_oDIZoC4l19Nli0Fj.json +++ b/src/packs/domains/domainCard_Rise_Up_oDIZoC4l19Nli0Fj.json @@ -106,7 +106,7 @@ "key": "system.damageThresholds.severe", "mode": 2, "value": "@system.proficiency", - "priority": 21 + "priority": null } ], "disabled": false, @@ -119,7 +119,7 @@ "startRound": null, "startTurn": null }, - "description": "

Gain a bonus to your Severe threshold equal to your Proficiency.

", + "description": "

Gain a bonus to your Severe threshold equal to your Proficiency.

", "origin": null, "tint": "#ffffff", "transfer": true, diff --git a/src/packs/domains/domainCard_Sage_Touched_VOSFaQHZbmhMyXwi.json b/src/packs/domains/domainCard_Sage_Touched_VOSFaQHZbmhMyXwi.json index 432ff638..dc9ac3d3 100644 --- a/src/packs/domains/domainCard_Sage_Touched_VOSFaQHZbmhMyXwi.json +++ b/src/packs/domains/domainCard_Sage_Touched_VOSFaQHZbmhMyXwi.json @@ -94,8 +94,7 @@ "source": "Daggerheart SRD", "page": 131, "artist": "" - }, - "domainTouched": 4 + } }, "flags": {}, "_id": "VOSFaQHZbmhMyXwi", diff --git a/src/packs/domains/domainCard_Salvation_Beam_4uAFGp3LxiC07woC.json b/src/packs/domains/domainCard_Salvation_Beam_4uAFGp3LxiC07woC.json index c7aeb02f..d637f611 100644 --- a/src/packs/domains/domainCard_Salvation_Beam_4uAFGp3LxiC07woC.json +++ b/src/packs/domains/domainCard_Salvation_Beam_4uAFGp3LxiC07woC.json @@ -95,7 +95,7 @@ }, "flags": {}, "_id": "4uAFGp3LxiC07woC", - "sort": 3500000, + "sort": 3400000, "effects": [], "ownership": { "default": 0 diff --git a/src/packs/domains/domainCard_Splendor_Touched_JT5dM3gVL6chDBYU.json b/src/packs/domains/domainCard_Splendor_Touched_JT5dM3gVL6chDBYU.json index 6b530289..45d0dc96 100644 --- a/src/packs/domains/domainCard_Splendor_Touched_JT5dM3gVL6chDBYU.json +++ b/src/packs/domains/domainCard_Splendor_Touched_JT5dM3gVL6chDBYU.json @@ -13,8 +13,7 @@ "source": "Daggerheart SRD", "page": 133, "artist": "" - }, - "domainTouched": 4 + } }, "flags": {}, "_id": "JT5dM3gVL6chDBYU", diff --git a/src/packs/domains/domainCard_Untouchable_9QElncQUDSakuSdR.json b/src/packs/domains/domainCard_Untouchable_9QElncQUDSakuSdR.json index 11a1a841..b1563416 100644 --- a/src/packs/domains/domainCard_Untouchable_9QElncQUDSakuSdR.json +++ b/src/packs/domains/domainCard_Untouchable_9QElncQUDSakuSdR.json @@ -37,7 +37,7 @@ "key": "system.evasion", "mode": 2, "value": "ceil(@system.traits.agility.value / 2)", - "priority": 21 + "priority": null } ], "disabled": false, diff --git a/src/packs/domains/domainCard_Valor_Touched_k1AtYd3lSchIymBr.json b/src/packs/domains/domainCard_Valor_Touched_k1AtYd3lSchIymBr.json index 20fe18ea..99546d6f 100644 --- a/src/packs/domains/domainCard_Valor_Touched_k1AtYd3lSchIymBr.json +++ b/src/packs/domains/domainCard_Valor_Touched_k1AtYd3lSchIymBr.json @@ -82,8 +82,7 @@ "source": "Daggerheart SRD", "page": 134, "artist": "" - }, - "domainTouched": 4 + } }, "flags": {}, "_id": "k1AtYd3lSchIymBr", diff --git a/src/packs/domains/domainCard_Vitality_sWUlSPOJEaXyQLCj.json b/src/packs/domains/domainCard_Vitality_sWUlSPOJEaXyQLCj.json index ec47c9f9..729aa251 100644 --- a/src/packs/domains/domainCard_Vitality_sWUlSPOJEaXyQLCj.json +++ b/src/packs/domains/domainCard_Vitality_sWUlSPOJEaXyQLCj.json @@ -51,8 +51,7 @@ "source": "Daggerheart SRD", "page": 121, "artist": "" - }, - "vaultActive": true + } }, "flags": {}, "_id": "sWUlSPOJEaXyQLCj", diff --git a/src/packs/domains/domainCard_Zone_of_Protection_lOZaRb4fCVgQsWB5.json b/src/packs/domains/domainCard_Zone_of_Protection_lOZaRb4fCVgQsWB5.json index 5669173d..681dc276 100644 --- a/src/packs/domains/domainCard_Zone_of_Protection_lOZaRb4fCVgQsWB5.json +++ b/src/packs/domains/domainCard_Zone_of_Protection_lOZaRb4fCVgQsWB5.json @@ -4,7 +4,7 @@ "type": "domainCard", "folder": "OwsbTSWzKq2WJmQN", "system": { - "description": "

Make a Spellcast Roll (16). Once per long rest on a success, choose a point within Far range and create a visible zone of protection there for all allies within Very Close range of that point. When you do, place a d6 on this card with the 1 value facing up. When an ally in this zone takes damage, they reduce it by the die’s value. You then increase the die’s value by one. When the die’s value would exceed 6, this effect ends.

@Template[type:emanation|range:vc]

", + "description": "

Make a Spellcast Roll (16). Once per long rest on a success, choose a point within Far range and create a visible zone of protection there for all allies within Very Close range of that point. When you do, place a d6 on this card with the 1 value facing up. When an ally in this zone takes damage, they reduce it by the die’s value. You then increase the die’s value by one. When the die’s value would exceed 6, this effect ends.

@Template[type:emanation|range:vc]

", "domain": "splendor", "recallCost": 2, "level": 6, diff --git a/src/packs/domains/folders_Level_10_7pKKYgRQAKlQAksV.json b/src/packs/domains/folders_Level_10_7pKKYgRQAKlQAksV.json index 126323da..613aa993 100644 --- a/src/packs/domains/folders_Level_10_7pKKYgRQAKlQAksV.json +++ b/src/packs/domains/folders_Level_10_7pKKYgRQAKlQAksV.json @@ -6,7 +6,7 @@ "sorting": "a", "_id": "7pKKYgRQAKlQAksV", "description": "", - "sort": 950000, + "sort": 1000000, "flags": {}, "_key": "!folders!7pKKYgRQAKlQAksV" } diff --git a/src/packs/domains/folders_Level_1_9Xc6KzNyjDtTGZkp.json b/src/packs/domains/folders_Level_1_9Xc6KzNyjDtTGZkp.json index 2d9c78f9..095ff6fb 100644 --- a/src/packs/domains/folders_Level_1_9Xc6KzNyjDtTGZkp.json +++ b/src/packs/domains/folders_Level_1_9Xc6KzNyjDtTGZkp.json @@ -6,7 +6,7 @@ "sorting": "a", "_id": "9Xc6KzNyjDtTGZkp", "description": "", - "sort": 700000, + "sort": 100000, "flags": {}, "_key": "!folders!9Xc6KzNyjDtTGZkp" } diff --git a/src/packs/domains/folders_Level_2_o7t2fsAmRxKLoHrO.json b/src/packs/domains/folders_Level_2_o7t2fsAmRxKLoHrO.json index 68cc5f04..b242e121 100644 --- a/src/packs/domains/folders_Level_2_o7t2fsAmRxKLoHrO.json +++ b/src/packs/domains/folders_Level_2_o7t2fsAmRxKLoHrO.json @@ -6,7 +6,7 @@ "sorting": "a", "_id": "o7t2fsAmRxKLoHrO", "description": "", - "sort": 800000, + "sort": 200000, "flags": {}, "_key": "!folders!o7t2fsAmRxKLoHrO" } diff --git a/src/packs/domains/folders_Level_3_wWL9mV6i2EGX5xHS.json b/src/packs/domains/folders_Level_3_wWL9mV6i2EGX5xHS.json index e04c6f09..3a4b0055 100644 --- a/src/packs/domains/folders_Level_3_wWL9mV6i2EGX5xHS.json +++ b/src/packs/domains/folders_Level_3_wWL9mV6i2EGX5xHS.json @@ -6,7 +6,7 @@ "sorting": "a", "_id": "wWL9mV6i2EGX5xHS", "description": "", - "sort": 850000, + "sort": 300000, "flags": {}, "_key": "!folders!wWL9mV6i2EGX5xHS" } diff --git a/src/packs/domains/folders_Level_4_yalAnCU3SndrYImF.json b/src/packs/domains/folders_Level_4_yalAnCU3SndrYImF.json index 2b70a495..ab0ba963 100644 --- a/src/packs/domains/folders_Level_4_yalAnCU3SndrYImF.json +++ b/src/packs/domains/folders_Level_4_yalAnCU3SndrYImF.json @@ -6,7 +6,7 @@ "sorting": "a", "_id": "yalAnCU3SndrYImF", "description": "", - "sort": 900000, + "sort": 400000, "flags": {}, "_key": "!folders!yalAnCU3SndrYImF" } diff --git a/src/packs/domains/folders_Level_5_Emnx4o1DWGTVKoAg.json b/src/packs/domains/folders_Level_5_Emnx4o1DWGTVKoAg.json index 5bde56f3..0a821a2d 100644 --- a/src/packs/domains/folders_Level_5_Emnx4o1DWGTVKoAg.json +++ b/src/packs/domains/folders_Level_5_Emnx4o1DWGTVKoAg.json @@ -6,7 +6,7 @@ "sorting": "a", "_id": "Emnx4o1DWGTVKoAg", "description": "", - "sort": 901563, + "sort": 500000, "flags": {}, "_key": "!folders!Emnx4o1DWGTVKoAg" } diff --git a/src/packs/domains/folders_Level_6_EiP5dLozOFZKIeWN.json b/src/packs/domains/folders_Level_6_EiP5dLozOFZKIeWN.json index e20ae6b5..5a58c052 100644 --- a/src/packs/domains/folders_Level_6_EiP5dLozOFZKIeWN.json +++ b/src/packs/domains/folders_Level_6_EiP5dLozOFZKIeWN.json @@ -6,7 +6,7 @@ "sorting": "a", "_id": "EiP5dLozOFZKIeWN", "description": "", - "sort": 903125, + "sort": 600000, "flags": {}, "_key": "!folders!EiP5dLozOFZKIeWN" } diff --git a/src/packs/domains/folders_Level_7_HAGbPLHwm0UozDeG.json b/src/packs/domains/folders_Level_7_HAGbPLHwm0UozDeG.json index e53c0e2a..233e3756 100644 --- a/src/packs/domains/folders_Level_7_HAGbPLHwm0UozDeG.json +++ b/src/packs/domains/folders_Level_7_HAGbPLHwm0UozDeG.json @@ -6,7 +6,7 @@ "sorting": "a", "_id": "HAGbPLHwm0UozDeG", "description": "", - "sort": 906250, + "sort": 700000, "flags": {}, "_key": "!folders!HAGbPLHwm0UozDeG" } diff --git a/src/packs/domains/folders_Level_8_me7ywrVh38j6T8Sm.json b/src/packs/domains/folders_Level_8_me7ywrVh38j6T8Sm.json index 9a0ad8d9..2b125f0d 100644 --- a/src/packs/domains/folders_Level_8_me7ywrVh38j6T8Sm.json +++ b/src/packs/domains/folders_Level_8_me7ywrVh38j6T8Sm.json @@ -6,7 +6,7 @@ "sorting": "a", "_id": "me7ywrVh38j6T8Sm", "description": "", - "sort": 912500, + "sort": 800000, "flags": {}, "_key": "!folders!me7ywrVh38j6T8Sm" } diff --git a/src/packs/domains/folders_Level_9_QYdeGsmVYIF34kZR.json b/src/packs/domains/folders_Level_9_QYdeGsmVYIF34kZR.json index 3547b169..c7984fb9 100644 --- a/src/packs/domains/folders_Level_9_QYdeGsmVYIF34kZR.json +++ b/src/packs/domains/folders_Level_9_QYdeGsmVYIF34kZR.json @@ -6,7 +6,7 @@ "sorting": "a", "_id": "QYdeGsmVYIF34kZR", "description": "", - "sort": 925000, + "sort": 900000, "flags": {}, "_key": "!folders!QYdeGsmVYIF34kZR" } diff --git a/src/packs/environments/environment_Burning_Heart_of_the_Woods_oY69NN4rYxoRE4hl.json b/src/packs/environments/environment_Burning_Heart_of_the_Woods_oY69NN4rYxoRE4hl.json index ea4f1951..dc42fb07 100644 --- a/src/packs/environments/environment_Burning_Heart_of_the_Woods_oY69NN4rYxoRE4hl.json +++ b/src/packs/environments/environment_Burning_Heart_of_the_Woods_oY69NN4rYxoRE4hl.json @@ -314,7 +314,7 @@ "name": "Charcoal Constructs", "type": "feature", "system": { - "description": "

Warped animals wreathed in indigo flame trample through a point of your choice. All targets within Close range of that point must make an Agility Reaction Roll. Targets who fail take 3d12+3 physical damage. Targets who succeed take half damage instead.

@Template[type:emanation|range:c]

Are these real animals consumed by the fl ame or merely constructs of the corrupting magic?

", + "description": "

Warped animals wreathed in indigo f l ame trample through a point of your choice. All targets within Close range of that point must make an Agility Reaction Roll. Targets who fail take 3d12+3 physical damage. Targets who succeed take half damage instead.

@Template[type:emanation|range:c]

Are these real animals consumed by the fl ame or merely constructs of the corrupting magic?

", "resource": null, "actions": { "gbXIaKr8em134IZC": { diff --git a/src/packs/environments/environment_Chaos_Realm_2Z1mKc65LxNk2PqR.json b/src/packs/environments/environment_Chaos_Realm_2Z1mKc65LxNk2PqR.json index 361b15bc..77781de0 100644 --- a/src/packs/environments/environment_Chaos_Realm_2Z1mKc65LxNk2PqR.json +++ b/src/packs/environments/environment_Chaos_Realm_2Z1mKc65LxNk2PqR.json @@ -467,48 +467,33 @@ "description": "

Spend a Fear to summon an @UUID[Compendium.daggerheart.adversaries.Actor.A0SeeDzwjvqOsyof]{Outer Realms Abomination}, an@UUID[Compendium.daggerheart.adversaries.Actor.ms6nuOl3NFkhPj1k]{Outer Realms Corrupter}, and [[/r 2d6]] @UUID[Compendium.daggerheart.adversaries.Actor.moJhHgKqTKPS2WYS]{Outer Realms Thrall}, who appear at Close range of a chosen PC in defiance of logic and causality. Immediately spotlight one of these adversaries, and you can spend an additional Fear to automatically succeed on that adversary’s standard attack.

What halfconsumed remnants of the shattered world do these monstrosities cast aside in pursuit of living flesh? What jagged refl ections of former personhood do you catch between moments of unquestioning malice?

", "resource": null, "actions": { - "KCzdCu2KhAx9KyhT": { - "type": "summon", - "_id": "KCzdCu2KhAx9KyhT", + "5a8ESNroEQHAm7rO": { + "type": "effect", + "_id": "5a8ESNroEQHAm7rO", "systemPath": "actions", - "baseAction": false, - "description": "", + "description": "

Spend a Fear to summon an @UUID[Compendium.daggerheart.adversaries.Actor.A0SeeDzwjvqOsyof]{Outer Realms Abomination}, an@UUID[Compendium.daggerheart.adversaries.Actor.ms6nuOl3NFkhPj1k]{Outer Realms Corrupter}, and [[/r 2d6]] @UUID[Compendium.daggerheart.adversaries.Actor.moJhHgKqTKPS2WYS]{Outer Realms Thrall}, who appear at Close range of a chosen PC in defiance of logic and causality. Immediately spotlight one of these adversaries, and you can spend an additional Fear to automatically succeed on that adversary’s standard attack.

What halfconsumed remnants of the shattered world do these monstrosities cast aside in pursuit of living flesh? What jagged refl ections of former personhood do you catch between moments of unquestioning malice?

", "chatDisplay": true, - "originItem": { - "type": "itemCollection" - }, "actionType": "action", "cost": [ { "scalable": false, "key": "fear", "value": 1, - "itemId": null, - "step": null, - "consumeOnSuccess": false + "step": null } ], "uses": { "value": null, "max": "", - "recovery": null, - "consumeOnSuccess": false + "recovery": null + }, + "effects": [], + "target": { + "type": "any", + "amount": null }, - "summon": [ - { - "actorUUID": "Compendium.daggerheart.adversaries.Actor.A0SeeDzwjvqOsyof", - "count": "1" - }, - { - "actorUUID": "Compendium.daggerheart.adversaries.Actor.ms6nuOl3NFkhPj1k", - "count": "1" - }, - { - "actorUUID": "Compendium.daggerheart.adversaries.Actor.moJhHgKqTKPS2WYS", - "count": "2d6" - } - ], "name": "Spend Fear", + "img": "icons/creatures/unholy/demons-horned-glowing-pink.webp", "range": "" } }, diff --git a/src/packs/environments/environment_Cult_Ritual_QAXXiOKBDmCTauHD.json b/src/packs/environments/environment_Cult_Ritual_QAXXiOKBDmCTauHD.json index 1295db59..705c9585 100644 --- a/src/packs/environments/environment_Cult_Ritual_QAXXiOKBDmCTauHD.json +++ b/src/packs/environments/environment_Cult_Ritual_QAXXiOKBDmCTauHD.json @@ -136,89 +136,13 @@ "system": { "description": "

Cultists dedicated this place to the Fallen Gods, and their foul influence seeps into it. Reduce the PCs’ Hope Die to a d10 while in this environment. The desecration can be removed with a Progress Countdown (6).

How do the PCs fist notice that something is wrong about this place? What fears resurface while hope is kept at bay?

", "resource": null, - "actions": { - "7W3sWRLzjG3dKcgq": { - "type": "effect", - "_id": "7W3sWRLzjG3dKcgq", - "systemPath": "actions", - "baseAction": false, - "description": "", - "chatDisplay": true, - "originItem": { - "type": "itemCollection" - }, - "actionType": "action", - "cost": [], - "uses": { - "value": null, - "max": "", - "recovery": null, - "consumeOnSuccess": false - }, - "effects": [ - { - "_id": "8yNIw8Y7rfMdOqWC", - "onSave": false - } - ], - "target": { - "type": "any", - "amount": null - }, - "name": "Influence", - "range": "" - } - }, + "actions": {}, "originItemType": null, "originId": null }, "_id": "iiHjguQG2aBn9g8i", "img": "icons/magic/unholy/orb-contained-pink.webp", - "effects": [ - { - "name": "Desecrated Ground", - "img": "icons/magic/unholy/orb-contained-pink.webp", - "origin": "Compendium.daggerheart.environments.Actor.QAXXiOKBDmCTauHD.Item.iiHjguQG2aBn9g8i", - "transfer": false, - "_id": "8yNIw8Y7rfMdOqWC", - "type": "base", - "system": { - "rangeDependence": { - "enabled": false, - "type": "withinRange", - "target": "hostile", - "range": "melee" - } - }, - "changes": [ - { - "key": "system.rules.dualityRoll.defaultHopeDice", - "mode": 5, - "value": "d10", - "priority": null - } - ], - "disabled": false, - "duration": { - "startTime": null, - "combat": null, - "seconds": null, - "rounds": null, - "turns": null, - "startRound": null, - "startTurn": null - }, - "description": "

Your Hope Die is reduced to a d10 while in the Desecrated Grounds.

", - "tint": "#ffffff", - "statuses": [], - "sort": 0, - "flags": {}, - "_stats": { - "compendiumSource": null - }, - "_key": "!actors.items.effects!QAXXiOKBDmCTauHD.iiHjguQG2aBn9g8i.8yNIw8Y7rfMdOqWC" - } - ], + "effects": [], "folder": null, "sort": 0, "ownership": { @@ -419,12 +343,11 @@ "img": "icons/magic/unholy/barrier-fire-pink.webp", "range": "" }, - "HG7tbEdlYl3yLQnR": { - "type": "summon", - "_id": "HG7tbEdlYl3yLQnR", + "suFEnfpOfeVRvnJF": { + "type": "effect", + "_id": "suFEnfpOfeVRvnJF", "systemPath": "actions", - "baseAction": false, - "description": "", + "description": "

Summon a @UUID[Compendium.daggerheart.adversaries.Actor.3tqCjDwJAQ7JKqMb]{Minor Demon} within Very Close range of the ritual’s leader.

", "chatDisplay": true, "originItem": { "type": "itemCollection" @@ -437,13 +360,13 @@ "recovery": null, "consumeOnSuccess": false }, - "summon": [ - { - "actorUUID": "Compendium.daggerheart.adversaries.Actor.3tqCjDwJAQ7JKqMb", - "count": "1" - } - ], + "effects": [], + "target": { + "type": "any", + "amount": null + }, "name": "Summon Demon", + "img": "icons/magic/unholy/barrier-fire-pink.webp", "range": "" } }, diff --git a/src/packs/environments/environment_Divine_Usurpation_4DLYez7VbMCFDAuZ.json b/src/packs/environments/environment_Divine_Usurpation_4DLYez7VbMCFDAuZ.json index d8e9cded..aacf87e9 100644 --- a/src/packs/environments/environment_Divine_Usurpation_4DLYez7VbMCFDAuZ.json +++ b/src/packs/environments/environment_Divine_Usurpation_4DLYez7VbMCFDAuZ.json @@ -248,31 +248,33 @@ "description": "

Spend 2 Fear to summon [[/r 1d4+2]] @UUID[Compendium.daggerheart.adversaries.Actor.OsLG2BjaEdTZUJU9]{Fallen Shock Troop} that appear within Close range of the Usurper to assist their divine siege. Immediately spotlight the Shock Troops to use a “Group Attack” action.

Which High Fallen do these troops serve? Which god’s fl esh do they wish to feast upon?

", "resource": null, "actions": { - "okcqGrI4rdghugUi": { - "type": "summon", - "_id": "okcqGrI4rdghugUi", + "qIQTEO5t72xFtKYI": { + "type": "effect", + "_id": "qIQTEO5t72xFtKYI", "systemPath": "actions", - "baseAction": false, - "description": "", + "description": "

Spend 2 Fear to summon [[/r 1d4+2]] @UUID[Compendium.daggerheart.adversaries.Actor.OsLG2BjaEdTZUJU9]{Fallen Shock Troop} that appear within Close range of the Usurper to assist their divine siege. Immediately spotlight the Shock Troops to use a “Group Attack” action.

Which High Fallen do these troops serve? Which god’s fl esh do they wish to feast upon?

", "chatDisplay": true, - "originItem": { - "type": "itemCollection" - }, "actionType": "action", - "cost": [], + "cost": [ + { + "scalable": false, + "key": "fear", + "value": 2, + "step": null + } + ], "uses": { "value": null, "max": "", - "recovery": null, - "consumeOnSuccess": false + "recovery": null + }, + "effects": [], + "target": { + "type": "self", + "amount": null }, - "summon": [ - { - "actorUUID": "Compendium.daggerheart.adversaries.Actor.OsLG2BjaEdTZUJU9", - "count": "1d4+2" - } - ], "name": "Spend Fear", + "img": "icons/magic/unholy/orb-hands-pink.webp", "range": "" } }, diff --git a/src/packs/environments/environment_Mountain_Pass_acMu9wJrMZZzLSTJ.json b/src/packs/environments/environment_Mountain_Pass_acMu9wJrMZZzLSTJ.json index 9ba6a918..8e7cf1c8 100644 --- a/src/packs/environments/environment_Mountain_Pass_acMu9wJrMZZzLSTJ.json +++ b/src/packs/environments/environment_Mountain_Pass_acMu9wJrMZZzLSTJ.json @@ -246,35 +246,7 @@ "system": { "description": "

When the PCs enter the raptors’ hunting grounds, two @UUID[Compendium.daggerheart.adversaries.Actor.OMQ0v6PE8s1mSU0K]{Giant Eagles} appear at Very Far range of a chosen PC, identifying the PCs as likely prey.

How long has it been since the eagles last found prey? Do they have eggs in their nest or unfl edged young?

", "resource": null, - "actions": { - "88MyOC3IRcct6VLk": { - "type": "summon", - "_id": "88MyOC3IRcct6VLk", - "systemPath": "actions", - "baseAction": false, - "description": "", - "chatDisplay": true, - "originItem": { - "type": "itemCollection" - }, - "actionType": "action", - "cost": [], - "uses": { - "value": null, - "max": "", - "recovery": null, - "consumeOnSuccess": false - }, - "summon": [ - { - "actorUUID": "Compendium.daggerheart.adversaries.Actor.OMQ0v6PE8s1mSU0K", - "count": "2" - } - ], - "name": "Summon", - "range": "" - } - }, + "actions": {}, "originItemType": null, "originId": null, "featureForm": "reaction" diff --git a/src/packs/environments/environment_Raging_River_t4cdqTfzcqP3H1vJ.json b/src/packs/environments/environment_Raging_River_t4cdqTfzcqP3H1vJ.json index 6c34c296..5c973fa6 100644 --- a/src/packs/environments/environment_Raging_River_t4cdqTfzcqP3H1vJ.json +++ b/src/packs/environments/environment_Raging_River_t4cdqTfzcqP3H1vJ.json @@ -360,40 +360,33 @@ "description": "

Spend a Fear to summon a @UUID[Compendium.daggerheart.adversaries.Actor.8KWVLWXFhlY2kYx0]{Glass Snake} within Close range of a chosen PC. The Snake appears in or near the river and immediately takes the spotlight to use their “Spinning Serpent” action.

What treasures does the beast have in their burrow? What travelers have already fallen victim to this predator?

", "resource": null, "actions": { - "uY9HMKE4Q5g7bRKg": { - "type": "summon", - "_id": "uY9HMKE4Q5g7bRKg", + "Mnp0Yzc7EPVXm8So": { + "type": "effect", + "_id": "Mnp0Yzc7EPVXm8So", "systemPath": "actions", - "baseAction": false, - "description": "", + "description": "

Spend a Fear to summon a @UUID[Compendium.daggerheart.adversaries.Actor.8KWVLWXFhlY2kYx0]{Glass Snake} within Close range of a chosen PC. The Snake appears in or near the river and immediately takes the spotlight to use their “Spinning Serpent” action.

What treasures does the beast have in their burrow? What travelers have already fallen victim to this predator?

", "chatDisplay": true, - "originItem": { - "type": "itemCollection" - }, "actionType": "action", "cost": [ { "scalable": false, "key": "fear", "value": 1, - "itemId": null, - "step": null, - "consumeOnSuccess": false + "step": null } ], "uses": { "value": null, "max": "", - "recovery": null, - "consumeOnSuccess": false + "recovery": null + }, + "effects": [], + "target": { + "type": "self", + "amount": null }, - "summon": [ - { - "actorUUID": "Compendium.daggerheart.adversaries.Actor.8KWVLWXFhlY2kYx0", - "count": "1" - } - ], "name": "Spend Fear", + "img": "icons/creatures/reptiles/snake-fangs-bite-green-yellow.webp", "range": "" } }, diff --git a/src/packs/items/armors/armor_Bare_Bones_ITAjcigTcUw5pMCN.json b/src/packs/items/armors/armor_Bare_Bones_ITAjcigTcUw5pMCN.json index 5158b100..5c061083 100644 --- a/src/packs/items/armors/armor_Bare_Bones_ITAjcigTcUw5pMCN.json +++ b/src/packs/items/armors/armor_Bare_Bones_ITAjcigTcUw5pMCN.json @@ -39,7 +39,7 @@ "key": "system.armorScore", "mode": 2, "value": "@system.traits.strength.value", - "priority": 21 + "priority": null } ], "disabled": false, diff --git a/src/packs/items/armors/armor_Elundrian_Chain_Armor_Q6LxmtFetDDkoZVZ.json b/src/packs/items/armors/armor_Elundrian_Chain_Armor_Q6LxmtFetDDkoZVZ.json index d63ce4df..abf81dae 100644 --- a/src/packs/items/armors/armor_Elundrian_Chain_Armor_Q6LxmtFetDDkoZVZ.json +++ b/src/packs/items/armors/armor_Elundrian_Chain_Armor_Q6LxmtFetDDkoZVZ.json @@ -42,8 +42,7 @@ { "key": "system.resistance.magical.reduction", "mode": 2, - "value": "@system.armorScore", - "priority": 21 + "value": "@system.armorScore" } ], "_id": "xGxqTCO8MjNq5Cw6", diff --git a/src/packs/items/consumables/consumable_Stardrop_y4c1jrlHrf0wBWOq.json b/src/packs/items/consumables/consumable_Stardrop_y4c1jrlHrf0wBWOq.json index 1a8800cd..91cc1861 100644 --- a/src/packs/items/consumables/consumable_Stardrop_y4c1jrlHrf0wBWOq.json +++ b/src/packs/items/consumables/consumable_Stardrop_y4c1jrlHrf0wBWOq.json @@ -4,7 +4,7 @@ "_id": "y4c1jrlHrf0wBWOq", "img": "icons/magic/light/projectiles-star-purple.webp", "system": { - "description": "

You can use this stardrop to summon a hailstorm of comets that deals 8d20 physical damage to all targets within Very Far range.

", + "description": "

You can use this stardrop to summon a hailstorm of comets that deals 8d20 physical damage to all targets within Very Far range.

@Template[type:emanation|range:vf]

", "quantity": 1, "actions": { "pt5U6hlyx4T7MUOa": { diff --git a/src/packs/items/weapons/weapon_Buckler_EmFTp9wzT6MHSaNz.json b/src/packs/items/weapons/weapon_Buckler_EmFTp9wzT6MHSaNz.json index 4d815a6c..86859acd 100644 --- a/src/packs/items/weapons/weapon_Buckler_EmFTp9wzT6MHSaNz.json +++ b/src/packs/items/weapons/weapon_Buckler_EmFTp9wzT6MHSaNz.json @@ -155,8 +155,7 @@ { "key": "system.evasion", "mode": 2, - "value": "@system.armorScore", - "priority": 21 + "value": "@system.armorScore" } ], "transfer": false, diff --git a/src/packs/items/weapons/weapon_Flickerfly_Blade_xLJ5RRpUoTRmAC3G.json b/src/packs/items/weapons/weapon_Flickerfly_Blade_xLJ5RRpUoTRmAC3G.json index 5dd5f04e..a00d4767 100644 --- a/src/packs/items/weapons/weapon_Flickerfly_Blade_xLJ5RRpUoTRmAC3G.json +++ b/src/packs/items/weapons/weapon_Flickerfly_Blade_xLJ5RRpUoTRmAC3G.json @@ -117,8 +117,7 @@ { "key": "system.bonuses.damage.primaryWeapon.bonus", "mode": 2, - "value": "@system.traits.agility.value", - "priority": 21 + "value": "@system.traits.agility.value" } ], "_id": "jMIrOhpPUncn7dWg", diff --git a/src/packs/journals/journal_Daggerheart_SRD_uNs7ne9VCbbu5dcG.json b/src/packs/journals/journal_Daggerheart_SRD_uNs7ne9VCbbu5dcG.json index 944467eb..1c7bff64 100644 --- a/src/packs/journals/journal_Daggerheart_SRD_uNs7ne9VCbbu5dcG.json +++ b/src/packs/journals/journal_Daggerheart_SRD_uNs7ne9VCbbu5dcG.json @@ -125,7 +125,7 @@ "image": {}, "text": { "format": 1, - "content": "

FLOW OF THE GAME

Daggerheart is a conversation. The GM describes fictional scenarios involving the PCs, and the players take turns describing how their characters react. The goal of every person at the table is to build upon everyone else’s ideas and collaboratively tell a satisfying story. The system facilitates this collaborative process by providing structure to the conversation and mechanics for resolving moments of tension where fate or fortune determine the outcome of events.

PLAYER PRINCIPLES & BEST PRACTICES

To get the most out of Daggerheart, we recommend players keep the following principles and practices in mind throughout each session:

PRINCIPLES

BEST PRACTICES

For more information, see the Daggerheart Core Rulebook, pages 9 and 108.

CORE GAMEPLAY LOOP

The core gameplay loop is the procedure that drives every scene, both in and out of combat:

STEP 1: SET THE SCENE

The GM describes a scenario, establishing the PCs’ surroundings and any dangers, NPCs, or other important details the characters would notice.

STEP 2: ASK AND ANSWER QUESTIONS

The players ask clarifying questions to explore the scene more deeply and gather information that could inform their characters’ actions. The GM responds to these questions by giving the players information their characters could easily obtain, or by asking questions of their own to the players. The players also respond to any questions the GM poses to them.

In this way, the table builds out the fiction collaboratively.

STEP 3: BUILD ON THE FICTION

As the scene develops, the players find opportunities to take action—problems to solve, obstacles to overcome, mysteries to investigate, and so on. The players describe how their characters proceed; if their proposed actions carry no chance of failure (or if failure would be boring), they automatically succeed. But if the outcome of their action is unknown, the GM calls for an action roll. Either way, the table works the outcome into the story and moves the fiction forward, narrating how the PC’s actions have changed things.

STEP 4: GO BACK TO STEP 1

The process repeats from the beginning, with the GM relaying any updated details or material changes to the players. This process continues until the end of the scene is triggered by a mechanic or arrives organically.

THE SPOTLIGHT

The spotlight is a symbol that represents the table’s attention—and therefore the immediate focus of both the narrative and the game mechanics. Any time a character or player becomes the focus of a scene, they “are in the spotlight” or “have the spotlight.”

The spotlight moves around the table organically as scenes unfold unless a mechanical trigger determines where the spotlight goes next. For example, when a player fails an action roll, the mechanics prompt the GM to seize the spotlight and make a GM move.

TURN ORDER & ACTION ECONOMY

Daggerheart’s turns don’t follow a traditional, rigid format:
there is no explicit initiative mechanic and characters don’t have a set number of actions they can take or things they can do before the spotlight passes to someone else. A player with the spotlight describes what their character does and the spotlight simply swings to whoever:

  1. the fiction would naturally turn it toward

  2. hasn’t had the focus in a while, or

  3. a triggered mechanic puts it on


Optional: Spotlight Tracker Tool

If your group prefers a more traditional action economy, you can use tokens to track how many times a player has had the spotlight: At the start of a session or scene, each player adds a certain number of tokens (we recommend 3) to their character sheet and removes a token each time they take an action. If the spotlight would swing to someone without any tokens, it swings to someone else instead. Once every player has used all their available tokens, players refill their character sheet with the same number of tokens as before, then continue playing.


MAKING MOVES & TAKING ACTION

Any time a character does something to advance the story, such as speaking with another character, interacting with the environment, making an attack, casting a spell, or using a class feature, they are making a move.

ACTION ROLLS

Any move where success would be trivial or failure would be boring automatically succeeds, but any move that’s difficult to accomplish or risky to attempt triggers an action roll.

OVERVIEW

All action rolls require a pair of d12s called Duality Dice.

These are two visually distinct twelve-sided dice, with one die representing Hope and the other representing Fear.

To make an action roll, you roll the Duality Dice, sum the results, apply any relevant modifiers, and compare the total to a Difficulty number to determine the outcome:

Note: A Critical Success counts as a roll “with Hope.”

After resolving the action roll, the table works together to weave the outcome into the narrative and play continues.

FAILING FORWARD

In Daggerheart, every time you roll the dice, the scene changes in some way. There is no such thing as a roll where “nothing happens,” because the fiction constantly evolves based on the successes and failures of the characters.

PROCEDURE

The following steps describe in more detail the procedure that all action rolls utilize:

STEP 1: PICK AN APPROPRIATE TRAIT

Some actions and effects specify in their description which trait applies to the roll; otherwise, the GM tells the acting player which character trait best applies to the action being attempted. If more than one trait could apply to the roll, the GM chooses or lets the acting player decide.

STEP 2: DETERMINE THE DIFFICULTY

Some actions and features say in their description what the Difficulty is. Otherwise, the GM determines the Difficulty based on the scenario. The GM can choose whether to share the Difficulty with the table. In either case, the GM should communicate the potential consequences of failure to the acting player.

STEP 3: APPLY EXTRA DICE AND MODIFIERS

The acting player decides whether to Utilize an Experience or activate other effects, then, if applicable, adds the appropriate tokens and dice (such as advantage or Rally dice) to their dice pool.


Note: Unless an action, ability, or feature specifically allows for it, a player must declare the use of any Experiences, extra dice, or other modifiers before they roll.


STEP 4: ROLL THE DICE

The acting player rolls their entire dice pool and announces the results in the format of “[total result] with [Hope/Fear]”— or “Critical Success!” in the case of matching Duality Dice.


Example: A player is making an action roll with a +1 in the relevant trait and no other modifiers; they roll the Duality Dice and get a result of 5 on their Hope Die and 7 on their Fear Die, then announce “I rolled a 13 with Fear!”


STEP 5: RESOLVE THE OUTCOME

The active player and the GM work together, along with the suggestions and support of the rest of the table, to resolve the outcome of the action.

GM MOVES AND ADVERSARY ACTIONS

GMs also make moves. They should consider making a move when a player does one of the following things:

After the GM turn is done, the spotlight goes back to the PCs.

Many adversaries and environments have Fear Features, especially powerful or consequential moves that the GM must spend Fear to activate.


Note: This Fear is in addition to any Fear the GM has previously spent to seize the spotlight or activate another action or ability.


ADVERSARY ACTIONS

When play passes to the GM, the GM can make a GM move to spotlight an adversary. A spotlighted adversary can:

The GM can spend additional Fear to spotlight additional adversaries. Once the GM has finished, the spotlight swings back to the PCs.

SPECIAL ROLLS

Some rolls have unique specifications or otherwise modify the action roll procedure: trait rolls, Spellcast Rolls, attack rolls, and damage rolls. Unless otherwise noted, you can apply any bonus, modifier, or effect to a special roll as if it were a standard action roll.

TRAIT ROLLS

An action roll that specifies which character trait applies to it is called a trait roll. In the text of a feature or effect, a trait roll is referenced with the format “[Trait] Roll (Difficulty)” (e.g., “Agility Roll (12)”). If the text of an effect doesn’t specify a trait roll’s Difficulty, the GM sets the Difficulty based on the circumstances.

Features and effects that affect a trait roll also affect any action roll that uses the same trait, including attack rolls, Spellcast rolls, and standard action rolls.


Example: The katari’s ancestry feature “Feline Instincts,” which allows the katari to reroll an Agility Roll, can also be used on a standard action roll using Agility to traverse dangerous terrain or on an attack roll made with a weapon that uses Agility.


SPELLCAST ROLLS

Spellcast Rolls are trait rolls that require you to use your Spellcast trait. Your Spellcast trait, if you have one, is determined by your subclass.

Spellcast Rolls are only made when a character uses a feature that requires one. A successful Spellcast Roll activates the effect as described by the feature.


Notes: A Spellcast Roll that can damage a target is also considered an attack roll.

When you cast a spell, the text tells you when the effect ends. The GM can spend a Fear to end a temporary effect. If your spell doesn’t specify when it ends, it ends when you choose or at a natural moment of the story. You can choose to end your spell early.

You can cast and maintain the effects of more than one spell at the same time.


REACTION ROLLS

A reaction roll is made in response to an attack or a hazard, representing a character’s attempt to avoid or withstand an imminent effect.

Reaction rolls work like action rolls, except they don’t generate Hope or Fear, don’t trigger additional GM moves, and other characters can’t aid you with Help an Ally.

If you critically succeed on a reaction roll, you don’t clear a Stress or gain a Hope, but you do ignore any effects that would have impacted you on a success, such as taking damage or marking Stress.

GROUP ACTION ROLLS

When multiple PCs take action together, the party chooses one PC to lead the action. Each other player then describes how their character collaborates on the task. The leader makes an action roll as usual, while the other players make reaction rolls using whichever traits they and the GM decide fit best.

The lead character gains a +1 bonus to their lead action roll for each of these reaction rolls that succeeded and a −1 penalty for each these reaction rolls that failed.

TAG TEAM ROLLS

Each player can, once per session, initiate a Tag Team Roll between their character and another PC by spending 3 Hope. The players work with one another to describe how they combine their actions in a unique and exciting way. Both players make separate action rolls; before resolving the roll’s outcome, choose one of the rolls to apply to both actions. On a roll with Hope, all PCs involved gain a Hope. On a roll with Fear, the GM gains a Fear token for each PC involved.

On a successful Tag Team attack roll, both players roll damage and add the totals together to determine the damage dealt, which is then treated as if it came from a single source. If the attacks deal different types of damage, the players choose which type to deal.


Notes:

A Tag Team Roll counts as a single action roll for the purposes of any countdowns or features that track action rolls.

Though each player may only initiate one Tag Team Roll per session, one PC can be involved in multiple Tag Team Rolls.


ADVANTAGE & DISADVANTAGE

Some features and effects let you roll with advantage or disadvantage on an action or reaction roll:

Advantage or disadvantage can be granted or imposed by mechanical triggers or at the GM’s discretion. When a PC aids you with Help an Ally, they roll their own advantage die and you add it to your total.

Advantage and disadvantage dice cancel each out, one-for-one, when they would be added to the same dice pool, so you’ll never roll both at the same time. If you have advantage or disadvantage from other sources that don’t affect your own dice pool, such as another player’s Help an Ally move, their effects stack with your rolled results.

HOPE & FEAR

Hope and Fear are metacurrencies representing the cosmic forces that shape the events of your table’s story. Hope powers PC abilities and features, while Fear powers the abilities of the GM and the adversaries and environments they control.

HOPE

Every PC starts with 2 Hope at character creation and gains more throughout play. A PC can have a maximum of 6 Hope at one time, and Hope carries over between sessions.

Players can spend Hope to:


Note: When using a Hope Feature, if you rolled with Hope for that action, the Hope you gain from that roll can be spent on that feature (or toward it, if it requires spending multiple Hope).


FEAR

The GM gains Fear whenever a player rolls with Fear and can spend Fear at any time to make or enhance a GM move or to use a Fear Feature. The GM can have up to 12 Fear at one time. Fear carries over between sessions.

COMBAT

Though Daggerheart relies on the same flow of collaborative storytelling in and out of combat, physical conflicts rely more heavily on several key mechanics related to attacking, maneuvering, and taking damage.

EVASION

Evasion represents a character’s ability to avoid attacks and other unwanted effects. Any roll made against a PC has a Difficulty equal to the target’s Evasion. A PC’s base Evasion is determined by their class, but can be modified by domain cards, equipment, conditions, and other effects.


Note: attacks rolled against adversaries use the target’s Difficulty instead of Evasion.


HIT POINTS & DAMAGE THRESHOLDS

Hit Points (HP) represent a character’s ability to withstand physical injury. When a character takes damage, they mark 1 to 3 HP, based on their damage thresholds:

A PC’s damage thresholds are calculated by adding their level to the listed damage thresholds of their equipped armor. A PC’s starting HP is based on their class, but they can gain additional Hit Points through advancements, features, and other effects.

An adversary’s Damage Thresholds and HP are listed in their stat blocks.

When a character marks their last Hit Point, they fall. If a PC falls, they make a death move.

Characters can clear Hit Points by taking downtime moves (see: Downtime) or by activating relevant special abilities or effects.


Optional Rule: Massive Damage

If a character ever takes damage equal to twice their Severe threshold, they mark 4 HP instead of 3.


STRESS

Stress represents how much mental, physical, and emotional strain a character can endure. Some special abilities or effects require the character activating them to mark Stress, and the GM can require a PC to mark Stress as a GM move or to represent the cost, complication, or consequence of an action roll.

When a character marks their last Stress, they become Vulnerable (see: Conditions) until they clear at least 1 Stress.

When a character must mark 1 or more Stress but can’t, they mark 1 HP instead. A character can’t use a move that requires them to mark Stress if all of their Stress is marked.

PCs can clear Stress by making downtime moves (see: Downtime). A PC’s maximum Stress is determined by their class, but they can increase it through advancements, abilities, and other effects.

ATTACKING

ATTACK ROLLS

An attack roll is an action roll intended to inflict harm. The trait that applies to an attack roll is specified by the weapon or spell being used. Unarmed attack rolls use either Strength or Finesse (GM’s choice). An attack roll’s Difficulty, unless otherwise noted, is equal to the Difficulty score of its target.

DAMAGE ROLLS

On a successful attack, roll damage. Damage is calculated from the damage roll listed in the attack’s description with the format “xdy+[modifier]” (e.g., for a spell that inflicts “1d8+2” damage, you roll an eight-sided and add 2 to the result; the damage dealt is equal to the total).

Any time an effect says to deal damage using your Spellcast trait, you roll a number of dice equal to your Spellcast trait.


Note: If your Spellcast trait is +0 or lower, you don’t roll anything.


For weapons, the number of damage dice you roll is equal to your Proficiency. Note that your Proficiency multiplies the number of dice you roll, but doesn’t affect the modifier. For example, a PC with Proficiency 2 and wielding a weapon with adamage rating of “d8+2” deals damage equal to “2d8+2” on a successful attack.

Successful unarmed attacks inflict [Proficiency]d4 damage.

CRITICAL DAMAGE

When you get a critical success (i.e., you roll matching values on your Duality Dice) on an attack roll, you deal extra damage.

Make the damage roll as usual, but add the maximum possible result of the damage dice to the final total. For instance, if an attack would normally deal 2d8+1 damage, a critical success would deal 2d8+1+16.

DAMAGE TYPES

There are two damage types: physical damage (phy) and magic damage (mag). Unless stated otherwise, mundane weapons and unarmed attacks deal physical damage, and spells deal magic damage.

RESISTANCE, IMMUNITY, AND DIRECT DAMAGE

If a target has resistance to a damage type, then they reduce incoming damage of that type by half before comparing it to their Hit Point Thresholds. If the target has additional ways of reducing incoming damage, such as marking Armor Slots, they apply the resistance effect first. The effects of multiple resistances to the same damage type do not stack.

If a target has immunity to a damage type, they ignore incoming damage of that type.

If an attack deals both physical and magic damage, a character can only benefit from resistance or immunity if they are resistant or immune to both damage types.

Direct damage is damage that can’t be reduced by marking Armor Slots.

MULTI-TARGET ATTACK ROLLS

If a spell or ability allows you to target multiple adversaries, make one attack roll and one damage roll, then apply the results to each target individually.

MULTIPLE DAMAGE SOURCES

Damage dealt simultaneously from multiple sources is always totaled before it’s compared to its target’s damage thresholds.


For example, if a PC with orc ancestry makes a successful attack against a target in Melee range and decides to spend a Hope to use their “Tusks” feature (which gives them an extra 1d6 damage on a damage roll), they would roll their normal weapon damage and add a d6 to the result, then deal that total damage to the adversary.


MAPS, RANGE, AND MOVEMENT

You can play Daggerheart using “theater of the mind” or maps and miniatures. The conversions below from abstract ranges to physical measurements assume 1 inch of map represents about 5 feet of fictional space.

Daggerheart uses the following ranges to translate fictional positioning into relative distance for the purposes of targeting, movement, and other game mechanics:

Range is measured from the source of an effect, such as the attacker or spellcaster, to the target or object of an effect.

A weapon, spell, ability, item, or other effect’s stated range is a maximum range; unless otherwise noted, it can be used at closer distances.


Optional Rule: Defined Ranges

If your table would rather operate with more precise range rules, you can use a 1-inch grid battle map during combat.

If you do, use the following guidelines for play:

  • Melee: 1 square

  • Very Close: 3 squares

  • Close: 6 squares

  • Far: 12 squares

  • Very Far: 13+ squares

  • Out of Range: Off the battlemap


MOVEMENT UNDER PRESSURE

When you’re under pressure or in danger and make an action roll, you can move to a location within Close range as part of that action. If you’re not already making an action roll, or if you want to move farther than your Close range, you need to succeed on an Agility Roll to safely reposition yourself.

An adversary can move within Close range for free as part of an action, or within Very Far range as a separate action.

AREA OF EFFECT

Unless stated otherwise, all the targets of a group effect must be within Very Close range of a single origin point within your effect’s range.

LINE OF SIGHT & COVER

Unless stated otherwise, a ranged attacker must have line of sight to their intended target to make an attack roll. If a partial obstruction lies between the attacker and target, the target has cover. Attacks made through cover are rolled with disadvantage. If the obstruction is total, there is no line of sight.

CONDITIONS

Conditions are effects that grant specific benefits or drawbacks to the target they are attached to.

STANDARD CONDITIONS

Daggerheart has three standard conditions:

HIDDEN

While you’re out of sight from all enemies and they don’t otherwise know your location, you gain the Hidden condition. Any rolls against a Hidden creature have disadvantage. After an adversary moves to where they would see you, you move into their line of sight, or you make an attack, you are no longer Hidden.

RESTRAINED

Restrained characters can’t move, but you can still take actions from their current position.

VULNERABLE

When a creature is Vulnerable, all rolls targeting them have advantage.

Some features can apply special or unique conditions, which work as described in the feature text.

Unless otherwise noted, the same condition can’t be applied more than once to the same target.

TEMPORARY TAGS & SPECIAL CONDITIONS

The temporary tag denotes a condition or effect that the affected creature can clear by making a move against it. When an affected PC makes a move to clear a temporary condition or effect, it normally requires a successful action roll using an appropriate trait. When an affected adversary makes a move to clear a temporary condition or effect, the GM puts the spotlight on the adversary and describes how they do it; this doesn’t require a roll but it does use up that adversary’s spotlight.

Special conditions are only cleared when specific requirements are met, such as completing a certain action or using a particular item. The requirements for clearing these conditions are stated in the text of the effect that applies the condition.

DOWNTIME

Between conflicts, the party can take a rest to recover expended resources and deepen their bonds. During a rest, each PC can make up to two downtime moves.

When the party rests, they must choose between a short rest and a long rest. If a party takes three short rests in a row, their next rest must be a long rest.

If a short rest is interrupted, such as by an adversary's attack, the characters don’t gain its benefits. If a long rest is interrupted, the characters only gain the benefits of a short rest.

A short rest lasts enough time for the party to catch its breath, about an hour in-world. Each player can move domain cards between their loadout and vault for free, then choose twice from the following list of downtime moves (players can choose the same move twice):

At the end of a short rest, any features or effects with a limited number of uses per rest refresh and any features or effects that last until your next rest expire.

A long rest is when the characters make camp and relax or sleep for several in-game hours. Each player can move domain cards between their loadout and vault for free, then choose twice from the following list of downtime moves (players can choose the same move twice):

At the end of a long rest, any features or effects with a limited number of uses per rest or per long rest refresh and any features or effects that last until your next rest or until your next long rest expire.

DOWNTIME CONSEQUENCES

On a short rest, the GM gains 1d4 Fear. On a long rest, they gain Fear equal to 1d4 + the number of PCs, and they can advance a long-term countdown of their choice.

DEATH

When a PC marks their last Hit Point, they must make a death move by choosing one of the following options:

If your character dies, work with the GM before the next session to create a new character at the current level of the rest of the party.

ADDITIONAL RULES

The following rules apply to many aspects of the game.

ROUNDING UP

This game doesn’t use fractions; if you need to round to a whole number, round up unless otherwise specified. When in doubt, resolve any ambiguity in favor of the PCs.

REROLLING DICE

When a feature allows you to reroll a die, you always take the new result unless the feature specifically says otherwise.

INCOMING DAMAGE

Incoming damage means the total damage from a single attack or source, before Armor Slots are marked.

SIMULTANEOUS EFFECTS

If the resolution order of multiple effects is unclear, the person in control of the effects (player or GM) decides what order to resolve them in.

STACKING EFFECTS

Unless stated otherwise, all effects beside conditions and advantage/disadvantage can stack.

ONGOING SPELL EFFECTS

If an effect doesn’t have a listed mechanical expiration, it only ends when decided by the controlling player, the GM, or the demands of the fiction.

SPENDING RESOURCES

Unless an effect states otherwise, you can’t spend Hope or mark Stress multiple times on the same feature to increase or repeat its effects on the same roll.

USING FEATURES AFTER A ROLL

If a feature allows you to affect a roll after the result has been totaled, you can use it after the GM declares whether the roll succeeds or fails, but not after the consequences unfold or another roll is made.

LEVELING UP

Your party levels up whenever the GM decides you’ve reached a narrative milestone (usually about every 3 sessions). All party members level up at the same time.

Daggerheart has 10 PC levels divided into 4 tiers:


→ Tier 1 encompasses level 1 only.

→ Tier 2 encompasses levels 2–4.

→ Tier 3 encompasses levels 5–7.

→ Tier 4 encompasses levels 8–10.


Your tier affects your damage thresholds, tier achievements, and access to advancements.

STEP ONE: TIER ACHIEVEMENTS

Take any applicable tier achievements

STEP TWO: ADVANCEMENTS

Choose any two advancements with at least one unmarked slot from your tier or below. Options with multiple slots can be chosen more than once. When you choose an advancement, mark one of its slots.

STEP THREE: DAMAGE THRESHOLDS

Increase all damage thresholds by 1.

STEP FOUR: DOMAIN CARDS

Acquire a new domain card at your level or lower from one of your class’s domains and add it to your loadout or vault. If your loadout is already full, you can’t add the new card to it until you move another into your vault. You can also exchange one domain card you’ve previously acquired for a different domain card of the same level or lower.

MULTICLASSING

Starting at level 5, you can choose multiclassing as an option when leveling up. When you multiclass, you choose an additional class, gain access to one of its domains, and acquire its class feature. Take the appropriate multiclass module and add it to the right side of your character sheet, then choose a foundation card from one of its subclasses. If your foundation cards specify different Spellcast traits, you can choose which one to apply when making a Spellcast roll.

Whenever you have the option to acquire a new domain card, you can choose from cards at or below half your current level (rounded up) from the domain you chose when you selected the multiclass advancement.

EQUIPMENT

Your equipped weapons and armor are the ones listed in the “Active Weapons” and “Active Armor” sections of your character sheet. Your character can only attack with weapons, benefit from armor, and gain features from items they have equipped. You can’t equip weapons or armor with a higher tier than you.

PCs can carry up to two additional weapons in the “Inventory Weapon” areas of the character sheet.

You can swap an Inventory Weapon with an Active Weapon at no cost during a rest or moment of calm; otherwise, you must mark a Stress to do so.

Your character can only have one Active Armor at a time.

They can’t equip armor while in danger or under pressure; otherwise, they can equip or unequip armor without cost.

Each armor has its own Armor Slots; if your character unequips their armor, track how many of its Armor Slots are marked. You can't carry armor in your inventory. When your character equips or unequips armor, recalculate your damage thresholds.

WEAPONS

All weapons have a tier, trait, range, damage die, damage type, and burden. Some weapons also have a feature.

CATEGORY

A weapon’s category specifies whether it is a Primary or Secondary weapon. Your character can only equip up to one weapon of each category at a time.

TRAIT

A weapon’s trait specifies which trait to use when making an attack roll with it.

RANGE

A weapon’s range specifies the maximum distance between the attacker and their target when attacking with it.

DAMAGE

A weapon’s damage indicates the size of the damage dice you roll on a successful attack with it; you roll a number of dice equal to your Proficiency. If the damage includes a flat modifier, this number is added to the total damage rolled, but is not altered or affected by Proficiency.

DAMAGE TYPE

A weapon’s damage type indicates whether it deals physical or magic damage. Weapons that deal magic damage can only be wielded by characters with a Spellcast trait.

BURDEN

A weapon’s burden indicates how many hands it occupies when equipped. Your character’s maximum burden is 2 hands.

FEATURE

A weapon’s feature is a special rule that stays in effect while the weapon is equipped.

You can throw an equipped weapon at a target within Very Close range, making the attack roll with Finesse. On a success, deal damage as usual for that weapon. Once thrown, the weapon is no longer considered equipped. Until you retrieve and re-equip it, you can’t attack with it or benefit from its features.

Combat Wheelchair

By Mark Thompson

The combat wheelchair is a ruleset designed to help you play a wheelchair user in Daggerheart. This section provides mechanics and narrative guidance for you to work from, but feel free to adapt the flavor text to best suit your character. Have fun with your character’s wheelchair design, and make it as unique or tailored to them as you please.

ACTION AND MOVEMENT

When describing how your character moves, you can use descriptions such as the following:

CONSEQUENCES

Here are some ways you might describe complications you encounter when your character uses their wheelchair:

GMs should avoid breaking a character's wheelchair or otherwise removing it from play as a consequence, unless everyone at the table, especially the wheelchair user’s player, gives their approval.

EVASION

Your character is assumed to be skilled in moving their wheelchair and navigating numerous situations in it. As a result, the only wheelchair that gives a penalty to a PC's Evasion is the Heavy Frame model.

BURDEN

All wheelchairs can be maneuvered using one or two hands outside of combat. However, when being used as a weapon, the chair is restricted to requiring one or two hands to perform attacks, depending on the model you’ve chosen. If you’re playing a character who has limited to no mobility in their arms, their wheelchair can be attuned to them by magical means. For example, your character might use a psychic link to guide the chair around like a pseudo-electric wheelchair. All the rules presented here can be tailored and adapted to any character's needs.

CHOOSING YOUR MODEL

All combat wheelchairs are equipped as Primary Weapons.

There are three models of wheelchair available: light, heavy, and arcane. You’re encouraged to consider the type of character you’re playing and the class they belong to, then choose the model that best matches that character concept.

ARMOR

Every armor has a name, base damage thresholds, and a base Armor Score. Some armor also has a feature.

While unarmored, your character’s base Armor Score is 0, their Major threshold is equal to their level, and their Severe threshold is equal to twice their level.

REDUCING INCOMING DAMAGE

When you take damage, you can mark one Armor Slot to reduce the number of Hit Points you would mark by one. If your character has an Armor Score of 0, you can’t mark Armor Slots. If an effect temporarily increases your Armor Score,

it increases your available Armor Slots by the same amount; when the effect ends, so does the availability of these Armor Slots.

LOOT

Loot comprises any consumables or reusable items the party acquires.

Items can be used until sold, discarded, or lost.

To generate a random item, choose a rarity, roll the designated dice, and match the total to the item in the table:

@UUID[Compendium.daggerheart.rolltables.RollTable.S61Shlt2I5CbLRjz]{Loot}

CONSUMABLES

Consumables are loot that can only be used once. You can hold up to five of each consumable at a time. Using a consumable doesn’t require a roll unless required by the GM or the demands of the fiction.

To generate a random consumable, choose a rarity, roll the designated dice, and match the total to the item in the table:

@UUID[Compendium.daggerheart.rolltables.RollTable.tF04P02yVN1YDVel]{Consumables}

GOLD

Gold is an abstract measurement of how much wealth a character has, and is measured in handfuls, bags, and chests, with 10 handfuls to 1 bag, and 10 bags to 1 chest. When you have marked all of the slots in a category and you gain another gold reward in that category, mark a slot in the following category and clear all the slots in the current one.

For example, if you have 9 handfuls and gain another, you instead mark 1 bag and erase all handfuls. If you have 9 bags and gain another, you mark 1 chest and erase all bags.

You can’t have more than 1 chest, so if all your Gold slots are marked, you’ll need to spend some of your gold or store it somewhere else before you can acquire more.


Optional Rule: Gold Coins

If your group wants to track gold with more granularity, you can add coins as your lowest denomination. Following the established pattern, 10 coins equal 1 handful.


" + "content": "

FLOW OF THE GAME

Daggerheart is a conversation. The GM describes fictional scenarios involving the PCs, and the players take turns describing how their characters react. The goal of every person at the table is to build upon everyone else’s ideas and collaboratively tell a satisfying story. The system facilitates this collaborative process by providing structure to the conversation and mechanics for resolving moments of tension where fate or fortune determine the outcome of events.

PLAYER PRINCIPLES & BEST PRACTICES

To get the most out of Daggerheart, we recommend players keep the following principles and practices in mind throughout each session:

PRINCIPLES

BEST PRACTICES

For more information, see the Daggerheart Core Rulebook, pages 9 and 108.

CORE GAMEPLAY LOOP

The core gameplay loop is the procedure that drives every scene, both in and out of combat:

STEP 1: SET THE SCENE

The GM describes a scenario, establishing the PCs’ surroundings and any dangers, NPCs, or other important details the characters would notice.

STEP 2: ASK AND ANSWER QUESTIONS

The players ask clarifying questions to explore the scene more deeply and gather information that could inform their characters’ actions. The GM responds to these questions by giving the players information their characters could easily obtain, or by asking questions of their own to the players. The players also respond to any questions the GM poses to them.

In this way, the table builds out the fiction collaboratively.

STEP 3: BUILD ON THE FICTION

As the scene develops, the players find opportunities to take action—problems to solve, obstacles to overcome, mysteries to investigate, and so on. The players describe how their characters proceed; if their proposed actions carry no chance of failure (or if failure would be boring), they automatically succeed. But if the outcome of their action is unknown, the GM calls for an action roll. Either way, the table works the outcome into the story and moves the fiction forward, narrating how the PC’s actions have changed things.

STEP 4: GO BACK TO STEP 1

The process repeats from the beginning, with the GM relaying any updated details or material changes to the players. This process continues until the end of the scene is triggered by a mechanic or arrives organically.

THE SPOTLIGHT

The spotlight is a symbol that represents the table’s attention—and therefore the immediate focus of both the narrative and the game mechanics. Any time a character or player becomes the focus of a scene, they “are in the spotlight” or “have the spotlight.”

The spotlight moves around the table organically as scenes unfold unless a mechanical trigger determines where the spotlight goes next. For example, when a player fails an action roll, the mechanics prompt the GM to seize the spotlight and make a GM move.

TURN ORDER & ACTION ECONOMY

Daggerheart’s turns don’t follow a traditional, rigid format:
there is no explicit initiative mechanic and characters don’t have a set number of actions they can take or things they can do before the spotlight passes to someone else. A player with the spotlight describes what their character does and the spotlight simply swings to whoever:

  1. the fiction would naturally turn it toward

  2. hasn’t had the focus in a while, or

  3. a triggered mechanic puts it on


Optional: Spotlight Tracker Tool

If your group prefers a more traditional action economy, you can use tokens to track how many times a player has had the spotlight: At the start of a session or scene, each player adds a certain number of tokens (we recommend 3) to their character sheet and removes a token each time they take an action. If the spotlight would swing to someone without any tokens, it swings to someone else instead. Once every player has used all their available tokens, players refill their character sheet with the same number of tokens as before, then continue playing.


MAKING MOVES & TAKING ACTION

Any time a character does something to advance the story, such as speaking with another character, interacting with the environment, making an attack, casting a spell, or using a class feature, they are making a move.

ACTION ROLLS

Any move where success would be trivial or failure would be boring automatically succeeds, but any move that’s difficult to accomplish or risky to attempt triggers an action roll.

OVERVIEW

All action rolls require a pair of d12s called Duality Dice.

These are two visually distinct twelve-sided dice, with one die representing Hope and the other representing Fear.

To make an action roll, you roll the Duality Dice, sum the results, apply any relevant modifiers, and compare the total to a Difficulty number to determine the outcome:

Note: A Critical Success counts as a roll “with Hope.”

After resolving the action roll, the table works together to weave the outcome into the narrative and play continues.

FAILING FORWARD

In Daggerheart, every time you roll the dice, the scene changes in some way. There is no such thing as a roll where “nothing happens,” because the fiction constantly evolves based on the successes and failures of the characters.

PROCEDURE

The following steps describe in more detail the procedure that all action rolls utilize:

STEP 1: PICK AN APPROPRIATE TRAIT

Some actions and effects specify in their description which trait applies to the roll; otherwise, the GM tells the acting player which character trait best applies to the action being attempted. If more than one trait could apply to the roll, the GM chooses or lets the acting player decide.

STEP 2: DETERMINE THE DIFFICULTY

Some actions and features say in their description what the Difficulty is. Otherwise, the GM determines the Difficulty based on the scenario. The GM can choose whether to share the Difficulty with the table. In either case, the GM should communicate the potential consequences of failure to the acting player.

STEP 3: APPLY EXTRA DICE AND MODIFIERS

The acting player decides whether to Utilize an Experience or activate other effects, then, if applicable, adds the appropriate tokens and dice (such as advantage or Rally dice) to their dice pool.


Note: Unless an action, ability, or feature specifically allows for it, a player must declare the use of any Experiences, extra dice, or other modifiers before they roll.


STEP 4: ROLL THE DICE

The acting player rolls their entire dice pool and announces the results in the format of “[total result] with [Hope/Fear]”— or “Critical Success!” in the case of matching Duality Dice.


Example: A player is making an action roll with a +1 in the relevant trait and no other modifiers; they roll the Duality Dice and get a result of 5 on their Hope Die and 7 on their Fear Die, then announce “I rolled a 13 with Fear!”


STEP 5: RESOLVE THE OUTCOME

The active player and the GM work together, along with the suggestions and support of the rest of the table, to resolve the outcome of the action.

GM MOVES AND ADVERSARY ACTIONS

GMs also make moves. They should consider making a move when a player does one of the following things:

After the GM turn is done, the spotlight goes back to the PCs.

Many adversaries and environments have Fear Features, especially powerful or consequential moves that the GM must spend Fear to activate.


Note: This Fear is in addition to any Fear the GM has previously spent to seize the spotlight or activate another action or ability.


ADVERSARY ACTIONS

When play passes to the GM, the GM can make a GM move to spotlight an adversary. A spotlighted adversary can:

The GM can spend additional Fear to spotlight additional adversaries. Once the GM has finished, the spotlight swings back to the PCs.

SPECIAL ROLLS

Some rolls have unique specifications or otherwise modify the action roll procedure: trait rolls, Spellcast Rolls, attack rolls, and damage rolls. Unless otherwise noted, you can apply any bonus, modifier, or effect to a special roll as if it were a standard action roll.

TRAIT ROLLS

An action roll that specifies which character trait applies to it is called a trait roll. In the text of a feature or effect, a trait roll is referenced with the format “[Trait] Roll (Difficulty)” (e.g., “Agility Roll (12)”). If the text of an effect doesn’t specify a trait roll’s Difficulty, the GM sets the Difficulty based on the circumstances.

Features and effects that affect a trait roll also affect any action roll that uses the same trait, including attack rolls, Spellcast rolls, and standard action rolls.


Example: The katari’s ancestry feature “Feline Instincts,” which allows the katari to reroll an Agility Roll, can also be used on a standard action roll using Agility to traverse dangerous terrain or on an attack roll made with a weapon that uses Agility.


SPELLCAST ROLLS

Spellcast Rolls are trait rolls that require you to use your Spellcast trait. Your Spellcast trait, if you have one, is determined by your subclass.

Spellcast Rolls are only made when a character uses a feature that requires one. A successful Spellcast Roll activates the effect as described by the feature.


Notes: A Spellcast Roll that can damage a target is also considered an attack roll.

When you cast a spell, the text tells you when the effect ends. The GM can spend a Fear to end a temporary effect. If your spell doesn’t specify when it ends, it ends when you choose or at a natural moment of the story. You can choose to end your spell early.

You can cast and maintain the effects of more than one spell at the same time.


REACTION ROLLS

A reaction roll is made in response to an attack or a hazard, representing a character’s attempt to avoid or withstand an imminent effect.

Reaction rolls work like action rolls, except they don’t generate Hope or Fear, don’t trigger additional GM moves, and other characters can’t aid you with Help an Ally.

If you critically succeed on a reaction roll, you don’t clear a Stress or gain a Hope, but you do ignore any effects that would have impacted you on a success, such as taking damage or marking Stress.

GROUP ACTION ROLLS

When multiple PCs take action together, the party chooses one PC to lead the action. Each other player then describes how their character collaborates on the task. The leader makes an action roll as usual, while the other players make reaction rolls using whichever traits they and the GM decide fit best.

The lead character gains a +1 bonus to their lead action roll for each of these reaction rolls that succeeded and a −1 penalty for each these reaction rolls that failed.

TAG TEAM ROLLS

Each player can, once per session, initiate a Tag Team Roll between their character and another PC by spending 3 Hope. The players work with one another to describe how they combine their actions in a unique and exciting way. Both players make separate action rolls; before resolving the roll’s outcome, choose one of the rolls to apply to both actions. On a roll with Hope, all PCs involved gain a Hope. On a roll with Fear, the GM gains a Fear token for each PC involved.

On a successful Tag Team attack roll, both players roll damage and add the totals together to determine the damage dealt, which is then treated as if it came from a single source. If the attacks deal different types of damage, the players choose which type to deal.


Notes:

A Tag Team Roll counts as a single action roll for the purposes of any countdowns or features that track action rolls.

Though each player may only initiate one Tag Team Roll per session, one PC can be involved in multiple Tag Team Rolls.


ADVANTAGE & DISADVANTAGE

Some features and effects let you roll with advantage or disadvantage on an action or reaction roll:

Advantage or disadvantage can be granted or imposed by mechanical triggers or at the GM’s discretion. When a PC aids you with Help an Ally, they roll their own advantage die and you add it to your total.

Advantage and disadvantage dice cancel each out, one-for-one, when they would be added to the same dice pool, so you’ll never roll both at the same time. If you have advantage or disadvantage from other sources that don’t affect your own dice pool, such as another player’s Help an Ally move, their effects stack with your rolled results.

HOPE & FEAR

Hope and Fear are metacurrencies representing the cosmic forces that shape the events of your table’s story. Hope powers PC abilities and features, while Fear powers the abilities of the GM and the adversaries and environments they control.

HOPE

Every PC starts with 2 Hope at character creation and gains more throughout play. A PC can have a maximum of 6 Hope at one time, and Hope carries over between sessions.

Players can spend Hope to:


Note: When using a Hope Feature, if you rolled with Hope for that action, the Hope you gain from that roll can be spent on that feature (or toward it, if it requires spending multiple Hope).


FEAR

The GM gains Fear whenever a player rolls with Fear and can spend Fear at any time to make or enhance a GM move or to use a Fear Feature. The GM can have up to 12 Fear at one time. Fear carries over between sessions.

COMBAT

Though Daggerheart relies on the same flow of collaborative storytelling in and out of combat, physical conflicts rely more heavily on several key mechanics related to attacking, maneuvering, and taking damage.

EVASION

Evasion represents a character’s ability to avoid attacks and other unwanted effects. Any roll made against a PC has a Difficulty equal to the target’s Evasion. A PC’s base Evasion is determined by their class, but can be modified by domain cards, equipment, conditions, and other effects.


Note: attacks rolled against adversaries use the target’s Difficulty instead of Evasion.


HIT POINTS & DAMAGE THRESHOLDS

Hit Points (HP) represent a character’s ability to withstand physical injury. When a character takes damage, they mark 1 to 3 HP, based on their damage thresholds:

A PC’s damage thresholds are calculated by adding their level to the listed damage thresholds of their equipped armor. A PC’s starting HP is based on their class, but they can gain additional Hit Points through advancements, features, and other effects.

An adversary’s Damage Thresholds and HP are listed in their stat blocks.

When a character marks their last Hit Point, they fall. If a PC falls, they make a death move.

Characters can clear Hit Points by taking downtime moves (see: Downtime) or by activating relevant special abilities or effects.


Optional Rule: Massive Damage

If a character ever takes damage equal to twice their Severe threshold, they mark 4 HP instead of 3.


STRESS

Stress represents how much mental, physical, and emotional strain a character can endure. Some special abilities or effects require the character activating them to mark Stress, and the GM can require a PC to mark Stress as a GM move or to represent the cost, complication, or consequence of an action roll.

When a character marks their last Stress, they become Vulnerable (see: Conditions) until they clear at least 1 Stress.

When a character must mark 1 or more Stress but can’t, they mark 1 HP instead. A character can’t use a move that requires them to mark Stress if all of their Stress is marked.

PCs can clear Stress by making downtime moves (see: Downtime). A PC’s maximum Stress is determined by their class, but they can increase it through advancements, abilities, and other effects.

ATTACKING

ATTACK ROLLS

An attack roll is an action roll intended to inflict harm. The trait that applies to an attack roll is specified by the weapon or spell being used. Unarmed attack rolls use either Strength or Finesse (GM’s choice). An attack roll’s Difficulty, unless otherwise noted, is equal to the Difficulty score of its target.

DAMAGE ROLLS

On a successful attack, roll damage. Damage is calculated from the damage roll listed in the attack’s description with the format “xdy+[modifier]” (e.g., for a spell that inflicts “1d8+2” damage, you roll an eight-sided and add 2 to the result; the damage dealt is equal to the total).

Any time an effect says to deal damage using your Spellcast trait, you roll a number of dice equal to your Spellcast trait.


Note: If your Spellcast trait is +0 or lower, you don’t roll anything.


For weapons, the number of damage dice you roll is equal to your Proficiency. Note that your Proficiency multiplies the number of dice you roll, but doesn’t affect the modifier. For example, a PC with Proficiency 2 and wielding a weapon with adamage rating of “d8+2” deals damage equal to “2d8+2” on a successful attack.

Successful unarmed attacks inflict [Proficiency]d4 damage.

CRITICAL DAMAGE

When you get a critical success (i.e., you roll matching values on your Duality Dice) on an attack roll, you deal extra damage.

Make the damage roll as usual, but add the maximum possible result of the damage dice to the final total. For instance, if an attack would normally deal 2d8+1 damage, a critical success would deal 2d8+1+16.

DAMAGE TYPES

There are two damage types: physical damage (phy) and magic damage (mag). Unless stated otherwise, mundane weapons and unarmed attacks deal physical damage, and spells deal magic damage.

RESISTANCE, IMMUNITY, AND DIRECT DAMAGE

If a target has resistance to a damage type, then they reduce incoming damage of that type by half before comparing it to their Hit Point Thresholds. If the target has additional ways of reducing incoming damage, such as marking Armor Slots, they apply the resistance effect first. The effects of multiple resistances to the same damage type do not stack.

If a target has immunity to a damage type, they ignore incoming damage of that type.

If an attack deals both physical and magic damage, a character can only benefit from resistance or immunity if they are resistant or immune to both damage types.

Direct damage is damage that can’t be reduced by marking Armor Slots.

MULTI-TARGET ATTACK ROLLS

If a spell or ability allows you to target multiple adversaries, make one attack roll and one damage roll, then apply the results to each target individually.

MULTIPLE DAMAGE SOURCES

Damage dealt simultaneously from multiple sources is always totaled before it’s compared to its target’s damage thresholds.


For example, if a PC with orc ancestry makes a successful attack against a target in Melee range and decides to spend a Hope to use their “Tusks” feature (which gives them an extra 1d6 damage on a damage roll), they would roll their normal weapon damage and add a d6 to the result, then deal that total damage to the adversary.


MAPS, RANGE, AND MOVEMENT

You can play Daggerheart using “theater of the mind” or maps and miniatures. The conversions below from abstract ranges to physical measurements assume 1 inch of map represents about 5 feet of fictional space.

Daggerheart uses the following ranges to translate fictional positioning into relative distance for the purposes of targeting, movement, and other game mechanics:

Range is measured from the source of an effect, such as the attacker or spellcaster, to the target or object of an effect.

A weapon, spell, ability, item, or other effect’s stated range is a maximum range; unless otherwise noted, it can be used at closer distances.


Optional Rule: Defined Ranges

If your table would rather operate with more precise range rules, you can use a 1-inch grid battle map during combat.

If you do, use the following guidelines for play:

  • Melee: 1 square

  • Very Close: 3 squares

  • Close: 6 squares

  • Far: 12 squares

  • Very Far: 13+ squares

  • Out of Range: Off the battlemap


MOVEMENT UNDER PRESSURE

When you’re under pressure or in danger and make an action roll, you can move to a location within Close range as part of that action. If you’re not already making an action roll, or if you want to move farther than your Close range, you need to succeed on an Agility Roll to safely reposition yourself.

An adversary can move within Close range for free as part of an action, or within Very Far range as a separate action.

AREA OF EFFECT

Unless stated otherwise, all the targets of a group effect must be within Very Close range of a single origin point within your effect’s range.

LINE OF SIGHT & COVER

Unless stated otherwise, a ranged attacker must have line of sight to their intended target to make an attack roll. If a partial obstruction lies between the attacker and target, the target has cover. Attacks made through cover are rolled with disadvantage. If the obstruction is total, there is no line of sight.

CONDITIONS

Conditions are effects that grant specific benefits or drawbacks to the target they are attached to.

STANDARD CONDITIONS

Daggerheart has three standard conditions:

HIDDEN

While you’re out of sight from all enemies and they don’t otherwise know your location, you gain the Hidden condition. Any rolls against a Hidden creature have disadvantage. After an adversary moves to where they would see you, you move into their line of sight, or you make an attack, you are no longer Hidden.

RESTRAINED

Restrained characters can’t move, but you can still take actions from their current position.

VULNERABLE

When a creature is Vulnerable, all rolls targeting them have advantage.

Some features can apply special or unique conditions, which work as described in the feature text.

Unless otherwise noted, the same condition can’t be applied more than once to the same target.

TEMPORARY TAGS & SPECIAL CONDITIONS

The temporary tag denotes a condition or effect that the affected creature can clear by making a move against it. When an affected PC makes a move to clear a temporary condition or effect, it normally requires a successful action roll using an appropriate trait. When an affected adversary makes a move to clear a temporary condition or effect, the GM puts the spotlight on the adversary and describes how they do it; this doesn’t require a roll but it does use up that adversary’s spotlight.

Special conditions are only cleared when specific requirements are met, such as completing a certain action or using a particular item. The requirements for clearing these conditions are stated in the text of the effect that applies the condition.

DOWNTIME

Between conflicts, the party can take a rest to recover expended resources and deepen their bonds. During a rest, each PC can make up to two downtime moves.

When the party rests, they must choose between a short rest and a long rest. If a party takes three short rests in a row, their next rest must be a long rest.

If a short rest is interrupted, such as by an adversary's attack, the characters don’t gain its benefits. If a long rest is interrupted, the characters only gain the benefits of a short rest.

A short rest lasts enough time for the party to catch its breath, about an hour in-world. Each player can move domain cards between their loadout and vault for free, then choose twice from the following list of downtime moves (players can choose the same move twice):

At the end of a short rest, any features or effects with a limited number of uses per rest refresh and any features or effects that last until your next rest expire.

A long rest is when the characters make camp and relax or sleep for several in-game hours. Each player can move domain cards between their loadout and vault for free, then choose twice from the following list of downtime moves (players can choose the same move twice):

At the end of a long rest, any features or effects with a limited number of uses per rest or per long rest refresh and any features or effects that last until your next rest or until your next long rest expire.

DOWNTIME CONSEQUENCES

On a short rest, the GM gains 1d4 Fear. On a long rest, they gain Fear equal to 1d4 + the number of PCs, and they can advance a long-term countdown of their choice.

DEATH

When a PC marks their last Hit Point, they must make a death move by choosing one of the following options:

If your character dies, work with the GM before the next session to create a new character at the current level of the rest of the party.

ADDITIONAL RULES

The following rules apply to many aspects of the game.

ROUNDING UP

This game doesn’t use fractions; if you need to round to a whole number, round up unless otherwise specified. When in doubt, resolve any ambiguity in favor of the PCs.

REROLLING DICE

When a feature allows you to reroll a die, you always take the new result unless the feature specifically says otherwise.

INCOMING DAMAGE

Incoming damage means the total damage from a single attack or source, before Armor Slots are marked.

SIMULTANEOUS EFFECTS

If the resolution order of multiple effects is unclear, the person in control of the effects (player or GM) decides what order to resolve them in.

STACKING EFFECTS

Unless stated otherwise, all effects beside conditions and advantage/disadvantage can stack.

ONGOING SPELL EFFECTS

If an effect doesn’t have a listed mechanical expiration, it only ends when decided by the controlling player, the GM, or the demands of the fiction.

SPENDING RESOURCES

Unless an effect states otherwise, you can’t spend Hope or mark Stress multiple times on the same feature to increase or repeat its effects on the same roll.

USING FEATURES AFTER A ROLL

If a feature allows you to affect a roll after the result has been totaled, you can use it after the GM declares whether the roll succeeds or fails, but not after the consequences unfold or another roll is made.

LEVELING UP

Your party levels up whenever the GM decides you’ve reached a narrative milestone (usually about every 3 sessions). All party members level up at the same time.

Daggerheart has 10 PC levels divided into 4 tiers:


→ Tier 1 encompasses level 1 only.

→ Tier 2 encompasses levels 2–4.

→ Tier 3 encompasses levels 5–7.

→ Tier 4 encompasses levels 8–10.


Your tier affects your damage thresholds, tier achievements, and access to advancements.

STEP ONE: TIER ACHIEVEMENTS

Take any applicable tier achievements

STEP TWO: ADVANCEMENTS

Choose any two advancements with at least one unmarked slot from your tier or below. Options with multiple slots can be chosen more than once. When you choose an advancement, mark one of its slots.

STEP THREE: DAMAGE THRESHOLDS

Increase all damage thresholds by 1.

STEP FOUR: DOMAIN CARDS

Acquire a new domain card at your level or lower from one of your class’s domains and add it to your loadout or vault. If your loadout is already full, you can’t add the new card to it until you move another into your vault. You can also exchange one domain card you’ve previously acquired for a different domain card of the same level or lower.

MULTICLASSING

Starting at level 5, you can choose multiclassing as an option when leveling up. When you multiclass, you choose an additional class, gain access to one of its domains, and acquire its class feature. Take the appropriate multiclass module and add it to the right side of your character sheet, then choose a foundation card from one of its subclasses. If your foundation cards specify different Spellcast traits, you can choose which one to apply when making a Spellcast roll.

Whenever you have the option to acquire a new domain card, you can choose from cards at or below half your current level (rounded up) from the domain you chose when you selected the multiclass advancement.

EQUIPMENT

Your equipped weapons and armor are the ones listed in the “Active Weapons” and “Active Armor” sections of your character sheet. Your character can only attack with weapons, benefit from armor, and gain features from items they have equipped. You can’t equip weapons or armor with a higher tier than you.

PCs can carry up to two additional weapons in the “Inventory Weapon” areas of the character sheet.

You can swap an Inventory Weapon with an Active Weapon at no cost during a rest or moment of calm; otherwise, you must mark a Stress to do so.

Your character can only have one Active Armor at a time.

They can’t equip armor while in danger or under pressure; otherwise, they can equip or unequip armor without cost.

Each armor has its own Armor Slots; if your character unequips their armor, track how many of its Armor Slots are marked. You can't carry armor in your inventory. When your character equips or unequips armor, recalculate your damage thresholds.

WEAPONS

All weapons have a tier, trait, range, damage die, damage type, and burden. Some weapons also have a feature.

CATEGORY

A weapon’s category specifies whether it is a Primary or Secondary weapon. Your character can only equip up to one weapon of each category at a time.

TRAIT

A weapon’s trait specifies which trait to use when making an attack roll with it.

RANGE

A weapon’s range specifies the maximum distance between the attacker and their target when attacking with it.

DAMAGE

A weapon’s damage indicates the size of the damage dice you roll on a successful attack with it; you roll a number of dice equal to your Proficiency. If the damage includes a flat modifier, this number is added to the total damage rolled, but is not altered or affected by Proficiency.

DAMAGE TYPE

A weapon’s damage type indicates whether it deals physical or magic damage. Weapons that deal magic damage can only be wielded by characters with a Spellcast trait.

BURDEN

A weapon’s burden indicates how many hands it occupies when equipped. Your character’s maximum burden is 2 hands.

FEATURE

A weapon’s feature is a special rule that stays in effect while the weapon is equipped.

You can throw an equipped weapon at a target within Very Close range, making the attack roll with Finesse. On a success, deal damage as usual for that weapon. Once thrown, the weapon is no longer considered equipped. Until you retrieve and re-equip it, you can’t attack with it or benefit from its features.

Combat Wheelchair

By Mark Thompson

The combat wheelchair is a ruleset designed to help you play a wheelchair user in Daggerheart. This section provides mechanics and narrative guidance for you to work from, but feel free to adapt the flavor text to best suit your character. Have fun with your character’s wheelchair design, and make it as unique or tailored to them as you please.

ACTION AND MOVEMENT

When describing how your character moves, you can use descriptions such as the following:

CONSEQUENCES

Here are some ways you might describe complications you encounter when your character uses their wheelchair:

GMs should avoid breaking a character's wheelchair or otherwise removing it from play as a consequence, unless everyone at the table, especially the wheelchair user’s player, gives their approval.

EVASION

Your character is assumed to be skilled in moving their wheelchair and navigating numerous situations in it. As a result, the only wheelchair that gives a penalty to a PC's Evasion is the Heavy Frame model.

BURDEN

All wheelchairs can be maneuvered using one or two hands outside of combat. However, when being used as a weapon, the chair is restricted to requiring one or two hands to perform attacks, depending on the model you’ve chosen. If you’re playing a character who has limited to no mobility in their arms, their wheelchair can be attuned to them by magical means. For example, your character might use a psychic link to guide the chair around like a pseudo-electric wheelchair. All the rules presented here can be tailored and adapted to any character's needs.

CHOOSING YOUR MODEL

All combat wheelchairs are equipped as Primary Weapons.

There are three models of wheelchair available: light, heavy, and arcane. You’re encouraged to consider the type of character you’re playing and the class they belong to, then choose the model that best matches that character concept.

ARMOR

Every armor has a name, base damage thresholds, and a base Armor Score. Some armor also has a feature.

While unarmored, your character’s base Armor Score is 0, their Major threshold is equal to their level, and their Severe threshold is equal to twice their level.

REDUCING INCOMING DAMAGE

When you take damage, you can mark one Armor Slot to reduce the number of Hit Points you would mark by one. If your character has an Armor Score of 0, you can’t mark Armor Slots. If an effect temporarily increases your Armor Score,

it increases your available Armor Slots by the same amount; when the effect ends, so does the availability of these Armor Slots.

LOOT

Loot comprises any consumables or reusable items the party acquires.

Items can be used until sold, discarded, or lost.

To generate a random item, choose a rarity, roll the designated dice, and match the total to the item in the table:

@UUID[RollTable.KKqUrMMXPpm7uhYT]{Loot}

Consumables

Consumables are loot that can only be used once. You can hold up to five of each consumable at a time. Using a consumable doesn’t require a roll unless required by the GM or the demands of the fiction.

To generate a random consumable, choose a rarity, roll the designated dice, and match the total to the item in the table:

@UUID[RollTable.wZXyi343PSVVwWB3]{Consumables}

GOLD

Gold is an abstract measurement of how much wealth a character has, and is measured in handfuls, bags, and chests, with 10 handfuls to 1 bag, and 10 bags to 1 chest. When you have marked all of the slots in a category and you gain another gold reward in that category, mark a slot in the following category and clear all the slots in the current one.

For example, if you have 9 handfuls and gain another, you instead mark 1 bag and erase all handfuls. If you have 9 bags and gain another, you mark 1 chest and erase all bags.

You can’t have more than 1 chest, so if all your Gold slots are marked, you’ll need to spend some of your gold or store it somewhere else before you can acquire more.


Optional Rule: Gold Coins

If your group wants to track gold with more granularity, you can add coins as your lowest denomination. Following the established pattern, 10 coins equal 1 handful.


" }, "video": { "controls": true, @@ -142,11 +142,10 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.351", + "coreVersion": "13.346", "systemId": "daggerheart", "systemVersion": "0.0.1", - "lastModifiedBy": "Bgvu4A6AMkRFOTGR", - "modifiedTime": 1767971461044 + "lastModifiedBy": null }, "_key": "!journal.pages!uNs7ne9VCbbu5dcG.C123WDOcT5hAa95M" }, @@ -163,7 +162,7 @@ "image": {}, "text": { "format": 1, - "content": "

INTRODUCTION

The GM is responsible for guiding the narrative and roleplaying the world the PCs inhabit. This section provides you with advice for running Daggerheart: using the core mechanics; creating memorable encounters; planning exciting sessions; selecting, creating, and using GM moves; crafting a full campaign; running dynamic NPCs; and more.

GM GUIDANCE

These three sections provide a foundation to help you get the most out of this game. The “GM Principles” are your guiding star—when in doubt, return to these principles.

GM PRINCIPLES

BEGIN AND END WITH THE FICTION

Use the fiction to drive mechanics, then connect the mechanics back to the fiction.

COLLABORATE AT ALL TIMES, ESPECIALLY DURING CONFLICT

The PCs are the protagonists of the campaign; antagonism between player and GM should exist only in the fiction.

FILL THE WORLD WITH LIFE, WONDER, AND DANGER

Showcase rich cultures, take the PCs to wondrous places, and introduce them to dangerous creatures.

ASK QUESTIONS AND INCORPORATE THE ANSWERS

Ensuring that the players’ ideas are included results in a narrative that supports the whole group’s creativity.

GIVE EVERY ROLL IMPACT

Only ask the players to roll during meaningful moments.

PLAY TO FIND OUT WHAT HAPPENS

Be surprised by what the characters do, the choices they make, and the people they become.

HOLD ON GENTLY

Don’t worry if you need to abandon or alter something that came before.

GM PRACTICES

CULTIVATE A CURIOUS TABLE

Follow what catches the players’ interest to foster an environment of creative inquiry.

GAIN YOUR PLAYERS’ TRUST

Act in good faith, follow through on your promises, admit your mistakes.

KEEP THE STORY MOVING FORWARD

Advance the story through escalating action, new information, or changing circumstances after every action roll, whether it succeeds or fails.

CUT TO THE ACTION

Skip past the boring bits. When a scene drags on, end it.

HELP THE PLAYERS USE THE GAME

Players have more fun when you help them understand the system.

CREATE A META CONVERSATION

Empower players to speak out of character, use safety tools, and ask for clarification.

TELL THEM WHAT THEY WOULD KNOW

Don’t hide obvious details or important information from the players.

GROUND THE WORLD IN MOTIVE

An NPC’s actions flow from their goals and desires.

BRING THE GAME’S MECHANICS TO LIFE

Set a good example of how fiction and mechanics work together to enhance the game experience.

REFRAME RATHER THAN REJECT

If a player’s contribution conflicts with the fiction, work with them to reshape it.

WORK IN MOMENTS AND MONTAGES

When framing a scene, decide which beats should be savored and which shouldn’t linger.

PITFALLS TO AVOID

UNDERMINING THE HEROES

If a roll doesn’t go well, show how it was impacted by an adversary’s prowess, environmental factors, or unexpected surprises, rather than the PC’s incompetence.

ALWAYS TELLING THE PLAYERS WHAT TO ROLL

Let the players decide how to handle a challenge.

LETTING SCENES DRAG

Shake it up or cut away when a scene has concluded, the table’s energy is flagging, or people are talking in circles.

SINGULAR SOLUTIONS

Don’t get hung up on one right answer to a problem. If the players have a clever idea, make it work.

OVERPLANNING

Spend your prep time inventing situations instead of scripting scenes. If the players surprise you, take a break to think through your options.

HOARDING FEAR

Spend Fear when you have the opportunity. The players will always generate more.

For more in-depth GM guidance, see pg. 140 of the Daggerheart Core Rulebook.

CORE GM MECHANICS

ROLLING DICE

The GM has no Duality Dice; instead, they roll a single d20 called the GM’s Die.

ADVERSARY ATTACK ROLLS

When an adversary attacks a PC, roll your d20 and add the adversary’s attack bonus to the result. If the total meets or beats the target’s Evasion, the attack succeeds; otherwise, the attack fails. On a successful attack, roll the attack’s damage dice to determine how much it deals.

If you roll a natural 20 on an attack, your roll automatically succeeds and you deal extra damage. Roll damage normally, then add the highest number on the damage dice to the total.

For example, an attack that deals 3d6+2 deals 18+3d6+2 on a critical success; the critical success does not affect the flat damage modifier.


Note: a critical success on an adversary’s reaction roll automatically succeeds, but confers no additional benefit.


GUIDANCE ON ACTION ROLLS

After a player describes a move they want to make during the game, you might decide an action roll is necessary to determine how the scene progresses. Use this guide to determine what to present the player, choosing whichever option best fits the situation:

MAKING MOVES

As the GM, you have GM moves that change the story in response to the players’ actions. GM moves aren’t bound by specific spells or effects—when you make a GM move, you can describe the action in whatever way the fiction demands.

GM moves happen during GM turns. A GM turn begins when the spotlight passes to them and ends when the spotlight passes back to the players.

WHEN TO MAKE A MOVE

The GM can make a GM move whenever you want, but the frequency and severity depends on the type of story you’re telling, the actions your players take, and the tone of the session you’re running.

Make a GM move when the players:

CHOOSING GM MOVES

The result of a player’s action roll determines your response:

On a Critical Success, you let the player describe their success, then give them an additional opportunity or advantage.

On a Success with Hope, you let the player describe their success, then you show how the world reacts to it.

On a Success with Fear, you work with the player to describe their success, then take a Fear and make a GM move to introduce a minor consequence, complication, or cost:

On a Failure with Hope, you describe how the PC fails to get what they want, then make a GM move to introduce a minor consequence, complication, or cost:

On a Failure with Fear, you describe how things go wrong, then make a GM move to introduce a major consequence, complication, or cost:

QUICK REFERENCE:

RESOLVING ACTION ROLLS

If you’re unsure how to resolve a roll, think about these quick phrases:

Success with Hope: Yes, and… (You get what you want and gain a Hope.)

Success with Fear: Yes, but… (You get what you want, but there’s a consequence, and the GM gains a Fear.)

Failure with Hope: No, but… (Things don’t go as planned, but you gain a Hope.)

Failure with Fear: No, and… (Things don’t go as planned and it gets worse. The GM gains a Fear.)

If the move you should make is not obvious from the fiction, draw inspiration from the “Example GM Moves” list:

SOFT AND HARD MOVES

Soft moves go easier on the players—they give the party new information about the scene and offer them an opportunity to react to it. Hard moves are harsher, more impactful, or more direct—the PCs don’t get an opening to interrupt, alter, or anticipate the outcome.

Use softer moves on rolls with Hope and harder moves on rolls with Fear.


Example GM Moves

  • Show how the world reacts

  • Ask a question and build on the answer

  • Make an NPC act in accordance with their motive

  • Drive a PC to take action by dangling their goals in front of them

  • Signal an imminent off-screen threat

  • Reveal an unwelcome truth or unexpected danger

  • Force the group to split up

  • Make a PC mark Stress

  • Make a move the characters don’t see

  • Show the collateral damage

  • Clear an adversary’s condition

  • Shift the environment

  • Spotlight an adversary

  • Capture someone or something important

  • Use a PC’s backstory against them

  • Take away an opportunity permanently.


USING FEAR

You start a campaign with 1 Fear per PC in the party.

You gain Fear whenever a PC rolls with Fear, the PCs take a rest (see: Downtime), or when an ability or effect tells you to.

You can never have more than 12 Fear at one time.

Fear carries over between sessions.

Spend a Fear to:

The dramatic tension of a scene correlates with the amount of Fear you spend during it. For guidance on how much Fear you should spend in a scene, consult the following table:

Incidental

A catch-up between PCs after an emotionally charged scene; gathering information; resupplying at a local market; resting during downtime.

0–1 Fear

Minor

A travel sequence; a minor skirmish that introduces new foes or signals future trouble.

1–3 Fear

Standard

A substantial battle with a notable objective; perilous travel that tests might and wit; a tense social encounter seeking crucial information or aid.

2–4 Fear

Major

A large battle with a Solo or Leader adversary; a character-defining scene with a significant change to a character’s personal story (such as revelation, growth, and betrayal).

4–8 Fear

Climactic

A major confrontation with the villain of a story arc; an epic set piece battle; a judicial duel to determine an important NPC’s fate.

6–12 Fear

If you find yourself with a large amount of Fear, consider:

Spending Fear to make a move communicates the increased impact of your action. Fear moves often include one or more of these elements:

DIFFICULTY BENCHMARKS

The Difficulty of an attack roll against an adversary is equal to the adversary’s Difficulty score. The Difficulty of any other action rolls against an adversary is equal to the adversary’s Difficulty score, plus (if applicable) the value of one of the adversary relevant Experience modifiers.

When a player makes an action roll without a specified Difficulty, the GM sets the Difficulty according to the totality of the circumstances. Refer to the following benchmark table for more guidance:

AGILITY

ROLL

SPRINT

LEAP

MANEUVER

5

Sprint within Close range across an open field with an enemy present.

Make a running jump of half your height (about 3 feet for a human).

Walk slowly across a narrow beam.

10

Sprint within Far range across an open field with an enemy present.

Make a running jump of your height (about 6 feet for a human).

Walk quickly across a narrow beam.

15

Sprint within Close range across rough terrain with an enemy present.

Make a running jump of double your height (about 12 feet for a human).

Run across a narrow beam.

20

Sprint within Close range through an active battle of multiple enemies.

Make a running jump of three times your height (about 18 feet for a human).

Run across a narrow beam in heavy wind.

25

Sprint within Far range through a pitched battle in rough terrain.

Make a running jump of five times your height (about 30 feet for a human).

Run across a very narrow beam in an active rainstorm.

30

Sprint across the heads of your enemies in a pitched battle.

Make a running jump of ten times your height (about 60 feet for a human).

Run across an inch-wide, oil-slicked beam in an active rainstorm.

STRENGTH

ROLL

LIFT

SMASH

GRAPPLE

5

Lift a chair.

Destroy a glass cup.

Subdue a child.

10

Lift a table or small chest.

Destroy a small wooden table.

Subdue a weak adult.

15

Lift a grown person or large chest.

Break through a wooden door.

Subdue an average adult.

20

Lift the side of a laden cart or carry a large chest up stairs.

Break through a stone wall.

Subdue a skilled wrestler.

25

Lift a horse, an ox, or a large monster.

Break through a dragon’s teeth.

Subdue a large beast.

30

Lift a falling portcullis gate.

Break a god’s grip.

Subdue a legendary beast.

FINESSE

ROLL

CONTROL

HIDE

TINKER

5

Ride a horse through easy terrain.

Evade notice under full cover on a moonless night.

Open a sticky lock with the appropriate key.

10

Drive an ox-pulled cart.

Evade notice in limited cover on a moonless night.

Open a simple puzzle box.

15

Ride a horse through rough terrain.

Evade notice in limited cover on an average night.

Disable a standard trap.

20

Drive a cart through rough terrain.

Evade notice in the shadows on an average night.

Disable a complicated trap.

25

Ride a wild horse through dangerous terrain.

Evade notice with minimal cover in ample light.

Open a door secured by a sequence of elaborate locks.

30

Ride an enraged beast through dangerous terrain.

Evade notice with no cover in full daylight.

Disable an incredibly sensitive and deadly trap.

INSTINCT

ROLL

PERCEIVE

SENSE

NAVIGATE

5

Hear a loud noise twenty paces away.

Detect an obvious ambush or notice an obvious deception.

Follow a well-trod path in good lighting and weather.

10

Hear a speaking voice fifty paces away.

Detect a looming threat or notice an average person’s lies.

Follow an average path in good lighting and weather.

15

Hear someone walking in the woods fifty paces away.

Detect hostile intent from a foe or see through a merchant’s lies.

Follow a subtle path through rough conditions.

20

Hear someone sneaking through the woods fifty paces away.

Detect a politician’s veiled hostility or detect a nearby assassin.

Follow a subtle path through harsh conditions.

25

Hear a prowling animal fifty paces away.

Identify a spymaster’s plot or read a politican’s true intentions.

Find your way with no path through dangerous conditions.

30

Hear a diving bird a hundred paces away.

Sense a shred of doubt within a god’s pronouncement.

Find your way through a trickery god’s maze.

PRESENCE

ROLL

CHARM

PERFORM

DECEIVE

5

Win the trust of a friendly neighbor.

Earn a meal from a friendly crowd.

Trick a trusting acquaintance.

10

Win the trust of a friendly stranger.

Earn room and board in a small town or impress a small crowd.

Trick an average stranger.

15

Win the trust of a cautious stranger or talk your way into a noble’s party.

Earn room and board in a low-end tavern or impress a large crowd.

Trick an average merchant.

20

Win the trust of a sympathetic foe or talk your way into an enemy’s party.

Earn lodging in a high-end tavern or impress a full theater.

Trick a trained courtier.

25

Turn an enemy against their ruler or talk your way into a fae court.

Earn your keep in a royal court or impress a full colosseum.

Trick a spymaster.

30

Talk a hostile god into granting you a boon.

Save yourself from execution after offending the queen.

Trick a god.

KNOWLEDGE

ROLL

RECALL

ANALYZE

COMPREHEND

5

Recall uncommon facts about your community.

Unpack an obvious metaphor in a simple text.

Learn simple skills from an excellent teacher.

10

Recall uncommon facts about a neighboring community.

Identify obvious subtext in a conversation.

Learn simple skills from an average teacher.

15

Recall uncommon facts about a distant community.

Break an average cipher in a coded message.

Learn complicated skills from an excellent teacher.

20

Recall specialized facts about a distant community.

Identify a weakness in a complicated battle plan.

Learn complicated skills under poor conditions.

25

Recall specialized facts about a fallen kingdom.

Predict the downfall of a nation based on concealed misdeeds.

Learn complicated skills quickly under dangerous conditions.

30

Recall secret information about an obscure historical group.

Identify the weakness in a divine champion’s fighting form.

Learn complicated skills quickly from incomplete information.

GIVING ADVANTAGE AND DISADVANTAGE

To viscerally convey how a PC’s actions or circumstances affect their ability to act, grant them advantage die (or impose disadvantage die) instead of adjusting the Difficulty of an action roll.

ADVERSARY ACTION ROLLS

By default, adversaries don’t normally make action rolls except for attack rolls and any unique actions described in their stat blocks. Any other action an adversary attempts simply succeeds without an action roll; if you want an adversary’s action to have a chance of failure, have any relevant PCs make reaction rolls instead.

However, for especially dramatic or difficult tasks that the PCs can’t influence, you can give an adversary an action roll anyway. For an adversary’s action roll, roll a d20. If the result is equal to or greater than the action’s Difficulty, the action succeeds—otherwise it fails. You can spend a Fear before rolling to add a relevant Experience from the adversary’s stat block to the total. Use the same procedure when an adversary makes a reaction roll.

ADVERSARY ADVANTAGE & DISADVANTAGE

If an adversary has advantage on an action roll, the GM rolls an extra d20 and counts only the higher result. If an adversary has disadvantage on an action roll, the GM rolls an extra d20 and counts only the lower result.

ADVERSARY ATTACKS

When an adversary attacks a PC, the GM rolls a d20 and adds the adversary’s Attack Modifier to the result. If the total meets or beats the target’s Evasion, the attack succeeds; otherwise, it fails. Before rolling, the GM can grant the attacking adversary advantage, impose disadvantage, or spend a Fear to add a relevant Experience from the adversary’s stat block to the total.

On a success, the adversary deals the damage listed in their stat block to the target.

When an adversary’s action lets the GM make an attack against multiple targets, they make one attack roll and compare it to each target’s Evasion separately.

COUNTDOWNS

Countdowns represent a period of time or series of events preceding a future effect. A countdown begins at a starting value. When a countdown advances, it’s reduced by 1. The countdown’s effect is triggered when the countdown reaches 0.


Note: You can track countdowns by “spinning down” dice or ticking off boxes.


Standard countdowns advance every time a player makes an action roll. If an adversary or environment ability refers to a “Countdown [n],” then it means a standard countdown with a starting value of n.

Dynamic countdowns advance by up to 3 depending on the outcomes of action rolls. Consequence countdowns are dynamic countdowns to negative effects. Progress countdowns are dynamic countdowns to positive effects. Dynamic countdowns advance according to this chart:

DYNAMIC COUNTDOWN ADVANCEMENT

Roll Result

Progress Advancement

Consequence Advancement

Failure with Fear

No advancement

Tick down 3

Failure with Hope

No advancement

Tick down 2

Success with Fear

Tick down 1

Tick down 1

Success with Hope

Tick down 2

No advancement

Critical Success

Tick down 3

No advancement

ADVANCED COUNTDOWN FEATURES

GIVING OUT GOLD, EQUIPMENT, AND LOOT

It’s up to you and your players how much importance you want to place on gold, equipment, and loot in your campaign.

Adjust the availability and utility of wealth and equipment to reflect the tone, themes, and setting of your campaign.

If you don’t wish to track gold, then when PCs go shopping fornew items let them pick one or two from a short, preselected list that aligns with your campaign setting and the party’s current location.

Otherwise, set the prices of goods and services by adjusting the entries in the Average Costs table to reflect your campaign setting:

Meals for a party of adventurers per night

1 Handful

Standard inn room per night

1 Handful

Luxury inn room per night

1 Bag

Carriage ride

2 Handfuls

Mount (horse, mule, etc.)

3 Bags

Specialized tools

3 Handfuls

Fine clothing

3 Handfuls

Luxury clothing

1 Bag

Tier 1 equipment (weapons, armor)

1–5 Handfuls

Tier 2 equipment (weapons, armor)

1–2 Bags

Tier 3 equipment (weapons, armor)

5–10 Bags

Tier 4 equipment (weapons, armor)

1–2 Chests

RUNNING GM NPCS

When you run NPCs as the GM, you should always strive to follow your GM principles and use them to bring the world to life. Differentiate NPCs with unique manners of speech and action; let their individual goals and desires motivate their actions.

The only essential elements for a NPC are their name, description, and motive. If it’s likely that the PCs will roll actions against them, give them a Difficulty. Adversaries can be invented or improvised by modifying the stat block of another adversary.

If an NPC becomes an ally in combat, they don’t need a stat block—just put the spotlight on what they do and show how their involvement alters the fiction. If a PC capitalizes on their help during the scene, give the PC advantage. NPCs that don’t have Hit Points or Stress can still be injured or killed if the fiction demands it.

If you want an important NPC to mechanically interact with the system, you can give them one or more features with specific triggers and effects. An NPC might also have a choice that adjusts the parameters of their feature. For example:

ARCANE HOLD

Choice: When the battle begins, choose a favored PC.

Trigger: The first time during a battle the favored PC is within Close range and hit with an attack.

Effect: Make an attack roll with a +6 modifier against the adversary. On a success, the target is temporarily Restrained by tendrils of powerful magic.

NPC FEATURE EXAMPLES

VOLLEY OF ARROWS

Trigger: A battle begins and this NPC is involved.

Effect: Activate a countdown (Loop 3). It ticks down when a PC misses an attack. When it triggers, this NPC releases a volley of arrows at a target of the PCs’ choice, dealing 2d8+3 physical damage.

MENTOR

​​Choice: When the battle begins, choose a protégé PC.

Trigger: Your protégé is within Close range and fails an attack roll.

Effect: Move into Melee range with the PC and give them advice or guidance. The next attack roll they make has advantage.

REGROUP

​​Choice: When a battle begins, choose a point within Far range.

Trigger: All PCs have marked all of their Armor Slots.

Effect: Teleport all PCs and this NPC to the chosen spot and clear an Armor Slot on each target.

INTO THE NIGHT

Trigger: The PCs start a long rest with this NPC.

Effect: Roll 1d4. On a 2 or less, this NPC steals 1 handful of gold from the party while they are sleeping, then disappears into the night.

OPTIONAL GM MECHANICS

FATE ROLLS

When the GM wants to leave an outcome entirely up to chance, they call for a fate roll. The GM establishes what’s at stake and how the roll will be interpreted. Then a player rolls one of their Duality Dice and interprets the result.


Examples:

“Roll your Fear Die. On a 4 or lower, the fire spreads beyond this house.”

“I think it’s really up to chance whether reinforcements will make it to you in time. Go ahead and roll your Fear Die— that’ll determine the starting size of the countdown. When that triggers, reinforcements will arrive in your Far range.”

“Go ahead and roll your Hope Die to see how big the crowd at the inn is tonight. The higher the roll, the bigger the crowd.”

“Make a roll using your Hope Die to determine the number of Stamina Potions the shop has in stock.”


FALLING AND COLLISION DAMAGE

If a character falls to the ground, you can use the following as a guide to determine the damage they take:

If a character collides with an object or another character at a dangerous speed, they take 1d20+5 direct physical damage.

MOVING AND FIGHTING UNDERWATER

By default, attack rolls made while the attacker is underwater have disadvantage.

For creatures that can’t breathe underwater, use a standard countdown (3) to track how long they can hold their breath.

Advance the countdown whenever the PC takes an action. In addition if they fail a roll or roll with Fear while underwater, you can spend your GM move to advance it an additional time—or twice, if they rolled a failure with Fear.

Once the countdown ends, the underwater PC must mark a Stress whenever they take an action.

CONFLICT BETWEEN PCS

Sometimes a player might want their character to act against another PC in the scene. Before jumping to rolling dice, discuss the situation with both players to decide how to resolve the conflict. A roll might not be necessary to reach an outcome—but if rolling will be fun for everyone involved, come to a consensus on the terms of the roll, then facilitate the scene according to the results.

On an attack roll against a PC, the attacker rolls against the defender’s Evasion, just like an adversary. On any other kind of action roll, the instigator makes an action roll and the target makes a reaction roll. To succeed, the instigator must beat a Difficulty equal to the total value of the reaction roll.

ADVERSARIES AND ENVIRONMENTS

USING ADVERSARIES

ADVERSARY STAT BLOCKS

All the information required to run an adversary is contained in their stat block. An adversary’s stat block includes their:

NAME

Each stat block has a unique name. Abilities that affect adversaries with a certain name include all adversaries who use that stat block, regardless of their in-story name.

TIER

Each adversary is designed to oppose PCs of a certain tier. If you confront the party with an adversary from another tier, adjust their stats.

TYPE

The adversary’s type appears alongside their tier. An adversary’s type represents the role they play in a conflict.

The adversary types are:

DESCRIPTION

A summary of the adversary's appearance and demeanor.

MOTIVES & TACTICS

Suggusted impulses, actions and goals for the adversary.

DIFFICULTY

The Difficulty of any roll made against the adversary, unless otherwise noted.

DAMAGE THRESHOLDS, HIT POINTS, AND STRESS

These systems function the same way they do for PCs. The numbers listed after “Threshold” are the adversary’s Major and Severe Thresholds.

ATTACK MODIFIER

When you attack with the adversary, apply this bonus or penalty to your attack roll.

STANDARD ATTACK

A description of the adversary’s primary mode of inflicting harm on the PCs. It includes the attack’s name, its effective range, and the damage it deals on a success. Using an adversary’s standard attack is a GM move.

EXPERIENCE (OPTIONAL)

The GM can spend a Fear to add an adversary’s relevant Experience to raise their attack roll or increase the Difficulty of a roll made against them.

EXAMPLE EXPERIENCES:

Acrobatics

Hunt from Above

Navigation

Ambusher

Intimidation

Nobility

Bartering

Intrusion

Quick Reflexes

Blademaster

Keen Senses

Socialite

Bodyguard

Magical Knowledge

Stealth

Commander

Nature’s Friend

Tracker

FEATURE(S)

There are three kinds of adversary features: actions, reactions, and passives. Note: each adversaries stress is tracked individually. If a feature requires the GM to spend Stress to activate it, the Stress must come from the adversary whose feature is being activate. If a feature has a Fear requirement, it must be spent in addition to any Fear already spent—for instance, to interrupt the PCs and put the spotlight on the adversary.

FEAR FEATURE(S)

High-impact effects that cost a Fear to activate.

EXAMPLE ADVERSARY FEATURES:

ACTIONS

Haymaker - Action: Make an attack against a target within Very Close range. On a success, deal X direct physical damage.

Shredding Strike - Action: Make an attack against a target within Very Close range. On a success, deal X physical damage and the target must mark an Armor Slot without gaining its benefit (they can still use armor to reduce the damage).

More Where That Came From - Action: Summon three Jagged Knife Lackeys, who appear at Far range.

REACTIONS

Heavy Hitter - Reaction: When this adversary deals damage with a standard attack, you can spend a Fear to gain a +X bonus to the damage roll.

Team-Up - Reaction: When another adversary within Very Close range of this adversary deals X damage to a creature, you can mark a Stress to make a standard attack against that same creature. On a success, combine the damage.

Momentum - Reaction: When this adversary makes a successful attack against a PC, you gain a Fear.

PASSIVES

Horde (X) - Passive: When the Horde has marked half or more of their HP, their standard attack deals X damage instead.

Minion (X) - Passive: This adversary is defeated when they take any damage. For every X damage a PC deals to this adversary, defeat an additional Minion within range the attack would succeed against.

Relentless (X) - Passive: This adversary can be spotlighted up to X times per GM turn. Spend Fear as usual to spotlight them.

Slow - Passive: When you spotlight this adversary and they don’t have a token on their stat block, they can’t act yet. Place a token on their stat block and describe what they’re preparing to do. When you spotlight this adversary and they have a token on their stat block, clear the token and they can act.

Arcane Form - Passive: This adversary is resistant to magic damage.

Armored Carapace - Passive: When this adversary takes physical damage, reduce it by X.

FEAR FEATURES

Explosion - Action: Spend a Fear to erupt in a fiery explosion. Make an attack against all targets within Close range. Targets the adversary succeeds against take 1d8 magic damage and are knocked back to Far range.

BUILDING BALANCED ENCOUNTERS

When planning a battle, start with [(3 x the number of PCs in combat) + 2] Battle Points and make the following adjustments:

Then spend your Battle Points to add an adversary to the encounter:

DEFEATED ADVERSARIES

When an adversary marks their last Hit Point, they are defeated: incapacitated, tied up, routed, killed, or anything else the table decides makes sense.

ADVERSARY STAT BLOCK BENCHMARKS

ADVERSARY STATISTIC

TIER 1

TIER 2

TIER 3

TIER 4

Attack Modifier

+1

+2

+3

+4

Damage Dice

1d6+2 to 1d12+4

2d6+3 to 2d12+4

3d8+3 to 3d12+5

4d8+10 to 4d12+15

Difficulty

11

14

17

20

Damage Thresholds

Major 7/Severe 12

Major 10/Severe 20

Major 20/Severe 32

Major 25/Severe 45

ADVERSARIES BY TIER

This section contains the following stat blocks:

TIER 1 (LEVEL 1)

TIER 2 (LEVELS 2–4)

TIER 3 (LEVELS 5–7)

TIER 4 (LEVELS 8–10)

USING ENVIRONMENTS

Environments represent everything in a scene beyond the PCs and adversaries, such as the physical space, background NPCs, and natural forces.

ENVIRONMENT STAT BLOCK

Each environment’s stat block presents their necessary mechanical statistics:

NAME

The unique name of the environment stat block.

TIER

The PC tier the environment is designed to challenge.

TYPE

The type of scene it most easily supports:

DESCRIPTION

An evocative one-line summary of the environment.

IMPULSES

The manner or mode with which the environment pushs and pulls the people within them.

DIFFICULTY

The standard Difficulty for action rolls made to overcome, oppose, or resist the environment or its elements.

POTENTIAL ADVERSARIES

Suggested adversaries that might appear in scenes within the environment.

FEATURES

Features provide inspiration for GM moves you can use that represent the dynamic landscape or situation.

FEATURE QUESTIONS

Prompts for plot hooks, narrative engines, and connections to other story elements.

ADAPTING ENVIRONMENTS

Sometimes you want to use an environment but it’s at the wrong tier for your party. Or you might want to replace a feature or two, then present it as an entirely different environment. Whether planning your session or even improvising an environment mid-session, you can adjust an existing environment’s stat block to fit the needs of your scene or improvise elements as needed. The environments framework is there to help organize ideas, not to stifle creativity.

When you need to quickly adjust a stat block to a different tier, you can simply replace its existing statistics with those listed on the Environment Statistics by Tier table, using the column that corresponds to your party’s tier.

BENCHMARK STATISTICS FOR ENVIRONMENTS BY TIER

Environment Statistic

Tier 1

Tier 2

Tier 3

Tier 4

Damage Dice

1d6+1 to 1d8+3

2d6+3 to 2d10+2

3d8+3 to 3d10+1

4d8+3 to 4d10+10

Difficulty

11

14

17

20

ENVIRONMENT STAT BLOCKS BY TIER

This section contains the following stat blocks.

@UUID[Compendium.daggerheart.environments.Folder.GQ0VnOLrKBIHR6Us]{TIER 1 (LEVEL 1)}

@UUID[Compendium.daggerheart.environments.Folder.XMeecO3IRvu5ck6F]{TIER 2 (LEVELS 2–4)}

@UUID[Compendium.daggerheart.environments.Folder.MfrIkJK12PAEfbPL]{TIER 3 (LEVELS 5–7)}

@UUID[Compendium.daggerheart.environments.Folder.IKumu5HTLqONLYqb]{TIER 4 (LEVELS 8–10)}

ADDITIONAL GM GUIDANCE

This section provides additional guidance for preparing and running a session of Daggerheart.

STORY BEATS

In storytelling, a beat is a moment that changes the trajectory of the narrative—a shift in the world, a significant action or reaction, an emotional revelation, or an important decision.

Take turns with the players, narrating a beat and then letting them react and carry the scene forward with their own beats.

When preparing for a session, plan in terms of the moments that give shape to each scene or sequence, rather than pre-scripting specific details or exchanges.

PREPARING COMBAT ENCOUNTERS

Build the hurdles the PCs face around the question of “What helps tell the story?” Enemies, environments, and hazards are the tools for heightening tension and creating drama. Ensure that combat is being used to give players more information about the unfolding story, revealing the world, the plot, or the characters.

BATTLES AND NARRATIVE

Dynamic battles create suspense by forcing players to choose between their various objectives, engaging their character’s motivations and weaknesses, and creating the crucible that the players use to forge their characters into legendary heroes. When preparing combat encounters:

SESSION REWARDS

Reward players at the end of a session with:

CRAFTING SCENES

Whenever you start a session, arrive at a new place, or change the situation, tell the players what they need to know by thinking with all of your senses and sharing something unique or unexpected about the fiction.

ENGAGING YOUR PLAYERS

Keep your players engaged by:

@UUID[Compendium.daggerheart.rolltables.RollTable.I5L1dlgxXTNrCCkL]{Table of Random Objectives}

PHASED BATTLES

Make battles by shifting the nature of its enemies or environment mid-combat:

USING DOWNTIME

Use downtime scenes as a pressure release valve to vary the intensity of the story and give the PCs room to breathe.

Empower your players to frame their own downtime scenes.

Ask the players what it looks like as they tend to their wounds or unwind together, encouraging them to take the reins and work with other players whose characters are involved

PROJECTS DURING DOWNTIME

The Work on a Project downtime move requires more GM input than other downtime moves and is best suited for long-term endeavors the PCs wish to undertake.

These projects are typically tracked using a Progress Countdown. When deciding the starting value of the countdown, consider the complexity of the project, the availability of relevant tools, and the impact of the project on the story.

Simple projects advance their countdown each time a player uses the Work on a Project move, but complex projects require a roll.

EXTENDED DOWNTIME

When you fast-forward the story across an extended period, use montages to illustrate the passage of time. You gain 1d6 Fear per PC and advance any long-term countdowns as appropriate.

CAMPAIGN FRAMES

A campaign frame provides inspiration, tools, and mechanics to support a particular type of story at the table.

Every campaign frame has a complexity rating that indicates how much its mechanics deviate from or expand upon the Daggerheart core ruleset.

Each campaign frame includes the following sections.

You can find each campaign frame map in the appendix of the core rulebook or at www.daggerheart.com/downloads.

" + "content": "

INTRODUCTION

The GM is responsible for guiding the narrative and roleplaying the world the PCs inhabit. This section provides you with advice for running Daggerheart: using the core mechanics; creating memorable encounters; planning exciting sessions; selecting, creating, and using GM moves; crafting a full campaign; running dynamic NPCs; and more.

GM GUIDANCE

These three sections provide a foundation to help you get the most out of this game. The “GM Principles” are your guiding star—when in doubt, return to these principles.

GM PRINCIPLES

BEGIN AND END WITH THE FICTION

Use the fiction to drive mechanics, then connect the mechanics back to the fiction.

COLLABORATE AT ALL TIMES, ESPECIALLY DURING CONFLICT

The PCs are the protagonists of the campaign; antagonism between player and GM should exist only in the fiction.

FILL THE WORLD WITH LIFE, WONDER, AND DANGER

Showcase rich cultures, take the PCs to wondrous places, and introduce them to dangerous creatures.

ASK QUESTIONS AND INCORPORATE THE ANSWERS

Ensuring that the players’ ideas are included results in a narrative that supports the whole group’s creativity.

GIVE EVERY ROLL IMPACT

Only ask the players to roll during meaningful moments.

PLAY TO FIND OUT WHAT HAPPENS

Be surprised by what the characters do, the choices they make, and the people they become.

HOLD ON GENTLY

Don’t worry if you need to abandon or alter something that came before.

GM PRACTICES

CULTIVATE A CURIOUS TABLE

Follow what catches the players’ interest to foster an environment of creative inquiry.

GAIN YOUR PLAYERS’ TRUST

Act in good faith, follow through on your promises, admit your mistakes.

KEEP THE STORY MOVING FORWARD

Advance the story through escalating action, new information, or changing circumstances after every action roll, whether it succeeds or fails.

CUT TO THE ACTION

Skip past the boring bits. When a scene drags on, end it.

HELP THE PLAYERS USE THE GAME

Players have more fun when you help them understand the system.

CREATE A META CONVERSATION

Empower players to speak out of character, use safety tools, and ask for clarification.

TELL THEM WHAT THEY WOULD KNOW

Don’t hide obvious details or important information from the players.

GROUND THE WORLD IN MOTIVE

An NPC’s actions flow from their goals and desires.

BRING THE GAME’S MECHANICS TO LIFE

Set a good example of how fiction and mechanics work together to enhance the game experience.

REFRAME RATHER THAN REJECT

If a player’s contribution conflicts with the fiction, work with them to reshape it.

WORK IN MOMENTS AND MONTAGES

When framing a scene, decide which beats should be savored and which shouldn’t linger.

PITFALLS TO AVOID

UNDERMINING THE HEROES

If a roll doesn’t go well, show how it was impacted by an adversary’s prowess, environmental factors, or unexpected surprises, rather than the PC’s incompetence.

ALWAYS TELLING THE PLAYERS WHAT TO ROLL

Let the players decide how to handle a challenge.

LETTING SCENES DRAG

Shake it up or cut away when a scene has concluded, the table’s energy is flagging, or people are talking in circles.

SINGULAR SOLUTIONS

Don’t get hung up on one right answer to a problem. If the players have a clever idea, make it work.

OVERPLANNING

Spend your prep time inventing situations instead of scripting scenes. If the players surprise you, take a break to think through your options.

HOARDING FEAR

Spend Fear when you have the opportunity. The players will always generate more.

For more in-depth GM guidance, see pg. 140 of the Daggerheart Core Rulebook.

CORE GM MECHANICS

ROLLING DICE

The GM has no Duality Dice; instead, they roll a single d20 called the GM’s Die.

ADVERSARY ATTACK ROLLS

When an adversary attacks a PC, roll your d20 and add the adversary’s attack bonus to the result. If the total meets or beats the target’s Evasion, the attack succeeds; otherwise, the attack fails. On a successful attack, roll the attack’s damage dice to determine how much it deals.

If you roll a natural 20 on an attack, your roll automatically succeeds and you deal extra damage. Roll damage normally, then add the highest number on the damage dice to the total.

For example, an attack that deals 3d6+2 deals 18+3d6+2 on a critical success; the critical success does not affect the flat damage modifier.


Note: a critical success on an adversary’s reaction roll automatically succeeds, but confers no additional benefit.


GUIDANCE ON ACTION ROLLS

After a player describes a move they want to make during the game, you might decide an action roll is necessary to determine how the scene progresses. Use this guide to determine what to present the player, choosing whichever option best fits the situation:

MAKING MOVES

As the GM, you have GM moves that change the story in response to the players’ actions. GM moves aren’t bound by specific spells or effects—when you make a GM move, you can describe the action in whatever way the fiction demands.

GM moves happen during GM turns. A GM turn begins when the spotlight passes to them and ends when the spotlight passes back to the players.

WHEN TO MAKE A MOVE

The GM can make a GM move whenever you want, but the frequency and severity depends on the type of story you’re telling, the actions your players take, and the tone of the session you’re running.

Make a GM move when the players:

CHOOSING GM MOVES

The result of a player’s action roll determines your response:

On a Critical Success, you let the player describe their success, then give them an additional opportunity or advantage.

On a Success with Hope, you let the player describe their success, then you show how the world reacts to it.

On a Success with Fear, you work with the player to describe their success, then take a Fear and make a GM move to introduce a minor consequence, complication, or cost:

On a Failure with Hope, you describe how the PC fails to get what they want, then make a GM move to introduce a minor consequence, complication, or cost:

On a Failure with Fear, you describe how things go wrong, then make a GM move to introduce a major consequence, complication, or cost:

QUICK REFERENCE:

RESOLVING ACTION ROLLS

If you’re unsure how to resolve a roll, think about these quick phrases:

Success with Hope: Yes, and… (You get what you want and gain a Hope.)

Success with Fear: Yes, but… (You get what you want, but there’s a consequence, and the GM gains a Fear.)

Failure with Hope: No, but… (Things don’t go as planned, but you gain a Hope.)

Failure with Fear: No, and… (Things don’t go as planned and it gets worse. The GM gains a Fear.)

If the move you should make is not obvious from the fiction, draw inspiration from the “Example GM Moves” list:

SOFT AND HARD MOVES

Soft moves go easier on the players—they give the party new information about the scene and offer them an opportunity to react to it. Hard moves are harsher, more impactful, or more direct—the PCs don’t get an opening to interrupt, alter, or anticipate the outcome.

Use softer moves on rolls with Hope and harder moves on rolls with Fear.


Example GM Moves

  • Show how the world reacts

  • Ask a question and build on the answer

  • Make an NPC act in accordance with their motive

  • Drive a PC to take action by dangling their goals in front of them

  • Signal an imminent off-screen threat

  • Reveal an unwelcome truth or unexpected danger

  • Force the group to split up

  • Make a PC mark Stress

  • Make a move the characters don’t see

  • Show the collateral damage

  • Clear an adversary’s condition

  • Shift the environment

  • Spotlight an adversary

  • Capture someone or something important

  • Use a PC’s backstory against them

  • Take away an opportunity permanently.


USING FEAR

You start a campaign with 1 Fear per PC in the party.

You gain Fear whenever a PC rolls with Fear, the PCs take a rest (see: Downtime), or when an ability or effect tells you to.

You can never have more than 12 Fear at one time.

Fear carries over between sessions.

Spend a Fear to:

The dramatic tension of a scene correlates with the amount of Fear you spend during it. For guidance on how much Fear you should spend in a scene, consult the following table:

Incidental

A catch-up between PCs after an emotionally charged scene; gathering information; resupplying at a local market; resting during downtime.

0–1 Fear

Minor

A travel sequence; a minor skirmish that introduces new foes or signals future trouble.

1–3 Fear

Standard

A substantial battle with a notable objective; perilous travel that tests might and wit; a tense social encounter seeking crucial information or aid.

2–4 Fear

Major

A large battle with a Solo or Leader adversary; a character-defining scene with a significant change to a character’s personal story (such as revelation, growth, and betrayal).

4–8 Fear

Climactic

A major confrontation with the villain of a story arc; an epic set piece battle; a judicial duel to determine an important NPC’s fate.

6–12 Fear

If you find yourself with a large amount of Fear, consider:

Spending Fear to make a move communicates the increased impact of your action. Fear moves often include one or more of these elements:

DIFFICULTY BENCHMARKS

The Difficulty of an attack roll against an adversary is equal to the adversary’s Difficulty score. The Difficulty of any other action rolls against an adversary is equal to the adversary’s Difficulty score, plus (if applicable) the value of one of the adversary relevant Experience modifiers.

When a player makes an action roll without a specified Difficulty, the GM sets the Difficulty according to the totality of the circumstances. Refer to the following benchmark table for more guidance:

AGILITY

ROLL

SPRINT

LEAP

MANEUVER

5

Sprint within Close range across an open field with an enemy present.

Make a running jump of half your height (about 3 feet for a human).

Walk slowly across a narrow beam.

10

Sprint within Far range across an open field with an enemy present.

Make a running jump of your height (about 6 feet for a human).

Walk quickly across a narrow beam.

15

Sprint within Close range across rough terrain with an enemy present.

Make a running jump of double your height (about 12 feet for a human).

Run across a narrow beam.

20

Sprint within Close range through an active battle of multiple enemies.

Make a running jump of three times your height (about 18 feet for a human).

Run across a narrow beam in heavy wind.

25

Sprint within Far range through a pitched battle in rough terrain.

Make a running jump of five times your height (about 30 feet for a human).

Run across a very narrow beam in an active rainstorm.

30

Sprint across the heads of your enemies in a pitched battle.

Make a running jump of ten times your height (about 60 feet for a human).

Run across an inch-wide, oil-slicked beam in an active rainstorm.

STRENGTH

ROLL

LIFT

SMASH

GRAPPLE

5

Lift a chair.

Destroy a glass cup.

Subdue a child.

10

Lift a table or small chest.

Destroy a small wooden table.

Subdue a weak adult.

15

Lift a grown person or large chest.

Break through a wooden door.

Subdue an average adult.

20

Lift the side of a laden cart or carry a large chest up stairs.

Break through a stone wall.

Subdue a skilled wrestler.

25

Lift a horse, an ox, or a large monster.

Break through a dragon’s teeth.

Subdue a large beast.

30

Lift a falling portcullis gate.

Break a god’s grip.

Subdue a legendary beast.

FINESSE

ROLL

CONTROL

HIDE

TINKER

5

Ride a horse through easy terrain.

Evade notice under full cover on a moonless night.

Open a sticky lock with the appropriate key.

10

Drive an ox-pulled cart.

Evade notice in limited cover on a moonless night.

Open a simple puzzle box.

15

Ride a horse through rough terrain.

Evade notice in limited cover on an average night.

Disable a standard trap.

20

Drive a cart through rough terrain.

Evade notice in the shadows on an average night.

Disable a complicated trap.

25

Ride a wild horse through dangerous terrain.

Evade notice with minimal cover in ample light.

Open a door secured by a sequence of elaborate locks.

30

Ride an enraged beast through dangerous terrain.

Evade notice with no cover in full daylight.

Disable an incredibly sensitive and deadly trap.

INSTINCT

ROLL

PERCEIVE

SENSE

NAVIGATE

5

Hear a loud noise twenty paces away.

Detect an obvious ambush or notice an obvious deception.

Follow a well-trod path in good lighting and weather.

10

Hear a speaking voice fifty paces away.

Detect a looming threat or notice an average person’s lies.

Follow an average path in good lighting and weather.

15

Hear someone walking in the woods fifty paces away.

Detect hostile intent from a foe or see through a merchant’s lies.

Follow a subtle path through rough conditions.

20

Hear someone sneaking through the woods fifty paces away.

Detect a politician’s veiled hostility or detect a nearby assassin.

Follow a subtle path through harsh conditions.

25

Hear a prowling animal fifty paces away.

Identify a spymaster’s plot or read a politican’s true intentions.

Find your way with no path through dangerous conditions.

30

Hear a diving bird a hundred paces away.

Sense a shred of doubt within a god’s pronouncement.

Find your way through a trickery god’s maze.

PRESENCE

ROLL

CHARM

PERFORM

DECEIVE

5

Win the trust of a friendly neighbor.

Earn a meal from a friendly crowd.

Trick a trusting acquaintance.

10

Win the trust of a friendly stranger.

Earn room and board in a small town or impress a small crowd.

Trick an average stranger.

15

Win the trust of a cautious stranger or talk your way into a noble’s party.

Earn room and board in a low-end tavern or impress a large crowd.

Trick an average merchant.

20

Win the trust of a sympathetic foe or talk your way into an enemy’s party.

Earn lodging in a high-end tavern or impress a full theater.

Trick a trained courtier.

25

Turn an enemy against their ruler or talk your way into a fae court.

Earn your keep in a royal court or impress a full colosseum.

Trick a spymaster.

30

Talk a hostile god into granting you a boon.

Save yourself from execution after offending the queen.

Trick a god.

KNOWLEDGE

ROLL

RECALL

ANALYZE

COMPREHEND

5

Recall uncommon facts about your community.

Unpack an obvious metaphor in a simple text.

Learn simple skills from an excellent teacher.

10

Recall uncommon facts about a neighboring community.

Identify obvious subtext in a conversation.

Learn simple skills from an average teacher.

15

Recall uncommon facts about a distant community.

Break an average cipher in a coded message.

Learn complicated skills from an excellent teacher.

20

Recall specialized facts about a distant community.

Identify a weakness in a complicated battle plan.

Learn complicated skills under poor conditions.

25

Recall specialized facts about a fallen kingdom.

Predict the downfall of a nation based on concealed misdeeds.

Learn complicated skills quickly under dangerous conditions.

30

Recall secret information about an obscure historical group.

Identify the weakness in a divine champion’s fighting form.

Learn complicated skills quickly from incomplete information.

GIVING ADVANTAGE AND DISADVANTAGE

To viscerally convey how a PC’s actions or circumstances affect their ability to act, grant them advantage die (or impose disadvantage die) instead of adjusting the Difficulty of an action roll.

ADVERSARY ACTION ROLLS

By default, adversaries don’t normally make action rolls except for attack rolls and any unique actions described in their stat blocks. Any other action an adversary attempts simply succeeds without an action roll; if you want an adversary’s action to have a chance of failure, have any relevant PCs make reaction rolls instead.

However, for especially dramatic or difficult tasks that the PCs can’t influence, you can give an adversary an action roll anyway. For an adversary’s action roll, roll a d20. If the result is equal to or greater than the action’s Difficulty, the action succeeds—otherwise it fails. You can spend a Fear before rolling to add a relevant Experience from the adversary’s stat block to the total. Use the same procedure when an adversary makes a reaction roll.

ADVERSARY ADVANTAGE & DISADVANTAGE

If an adversary has advantage on an action roll, the GM rolls an extra d20 and counts only the higher result. If an adversary has disadvantage on an action roll, the GM rolls an extra d20 and counts only the lower result.

ADVERSARY ATTACKS

When an adversary attacks a PC, the GM rolls a d20 and adds the adversary’s Attack Modifier to the result. If the total meets or beats the target’s Evasion, the attack succeeds; otherwise, it fails. Before rolling, the GM can grant the attacking adversary advantage, impose disadvantage, or spend a Fear to add a relevant Experience from the adversary’s stat block to the total.

On a success, the adversary deals the damage listed in their stat block to the target.

When an adversary’s action lets the GM make an attack against multiple targets, they make one attack roll and compare it to each target’s Evasion separately.

COUNTDOWNS

Countdowns represent a period of time or series of events preceding a future effect. A countdown begins at a starting value. When a countdown advances, it’s reduced by 1. The countdown’s effect is triggered when the countdown reaches 0.


Note: You can track countdowns by “spinning down” dice or ticking off boxes.


Standard countdowns advance every time a player makes an action roll. If an adversary or environment ability refers to a “Countdown [n],” then it means a standard countdown with a starting value of n.

Dynamic countdowns advance by up to 3 depending on the outcomes of action rolls. Consequence countdowns are dynamic countdowns to negative effects. Progress countdowns are dynamic countdowns to positive effects. Dynamic countdowns advance according to this chart:

DYNAMIC COUNTDOWN ADVANCEMENT

Roll Result

Progress Advancement

Consequence Advancement

Failure with Fear

No advancement

Tick down 3

Failure with Hope

No advancement

Tick down 2

Success with Fear

Tick down 1

Tick down 1

Success with Hope

Tick down 2

No advancement

Critical Success

Tick down 3

No advancement

ADVANCED COUNTDOWN FEATURES

GIVING OUT GOLD, EQUIPMENT, AND LOOT

It’s up to you and your players how much importance you want to place on gold, equipment, and loot in your campaign.

Adjust the availability and utility of wealth and equipment to reflect the tone, themes, and setting of your campaign.

If you don’t wish to track gold, then when PCs go shopping fornew items let them pick one or two from a short, preselected list that aligns with your campaign setting and the party’s current location.

Otherwise, set the prices of goods and services by adjusting the entries in the Average Costs table to reflect your campaign setting:

Meals for a party of adventurers per night

1 Handful

Standard inn room per night

1 Handful

Luxury inn room per night

1 Bag

Carriage ride

2 Handfuls

Mount (horse, mule, etc.)

3 Bags

Specialized tools

3 Handfuls

Fine clothing

3 Handfuls

Luxury clothing

1 Bag

Tier 1 equipment (weapons, armor)

1–5 Handfuls

Tier 2 equipment (weapons, armor)

1–2 Bags

Tier 3 equipment (weapons, armor)

5–10 Bags

Tier 4 equipment (weapons, armor)

1–2 Chests

RUNNING GM NPCS

When you run NPCs as the GM, you should always strive to follow your GM principles and use them to bring the world to life. Differentiate NPCs with unique manners of speech and action; let their individual goals and desires motivate their actions.

The only essential elements for a NPC are their name, description, and motive. If it’s likely that the PCs will roll actions against them, give them a Difficulty. Adversaries can be invented or improvised by modifying the stat block of another adversary.

If an NPC becomes an ally in combat, they don’t need a stat block—just put the spotlight on what they do and show how their involvement alters the fiction. If a PC capitalizes on their help during the scene, give the PC advantage. NPCs that don’t have Hit Points or Stress can still be injured or killed if the fiction demands it.

If you want an important NPC to mechanically interact with the system, you can give them one or more features with specific triggers and effects. An NPC might also have a choice that adjusts the parameters of their feature. For example:

ARCANE HOLD

Choice: When the battle begins, choose a favored PC.

Trigger: The first time during a battle the favored PC is within Close range and hit with an attack.

Effect: Make an attack roll with a +6 modifier against the adversary. On a success, the target is temporarily Restrained by tendrils of powerful magic.

NPC FEATURE EXAMPLES

VOLLEY OF ARROWS

Trigger: A battle begins and this NPC is involved.

Effect: Activate a countdown (Loop 3). It ticks down when a PC misses an attack. When it triggers, this NPC releases a volley of arrows at a target of the PCs’ choice, dealing 2d8+3 physical damage.

MENTOR

​​Choice: When the battle begins, choose a protégé PC.

Trigger: Your protégé is within Close range and fails an attack roll.

Effect: Move into Melee range with the PC and give them advice or guidance. The next attack roll they make has advantage.

REGROUP

​​Choice: When a battle begins, choose a point within Far range.

Trigger: All PCs have marked all of their Armor Slots.

Effect: Teleport all PCs and this NPC to the chosen spot and clear an Armor Slot on each target.

INTO THE NIGHT

Trigger: The PCs start a long rest with this NPC.

Effect: Roll 1d4. On a 2 or less, this NPC steals 1 handful of gold from the party while they are sleeping, then disappears into the night.

OPTIONAL GM MECHANICS

FATE ROLLS

When the GM wants to leave an outcome entirely up to chance, they call for a fate roll. The GM establishes what’s at stake and how the roll will be interpreted. Then a player rolls one of their Duality Dice and interprets the result.


Examples:

“Roll your Fear Die. On a 4 or lower, the fire spreads beyond this house.”

“I think it’s really up to chance whether reinforcements will make it to you in time. Go ahead and roll your Fear Die— that’ll determine the starting size of the countdown. When that triggers, reinforcements will arrive in your Far range.”

“Go ahead and roll your Hope Die to see how big the crowd at the inn is tonight. The higher the roll, the bigger the crowd.”

“Make a roll using your Hope Die to determine the number of Stamina Potions the shop has in stock.”


FALLING AND COLLISION DAMAGE

If a character falls to the ground, you can use the following as a guide to determine the damage they take:

If a character collides with an object or another character at a dangerous speed, they take 1d20+5 direct physical damage.

MOVING AND FIGHTING UNDERWATER

By default, attack rolls made while the attacker is underwater have disadvantage.

For creatures that can’t breathe underwater, use a standard countdown (3) to track how long they can hold their breath.

Advance the countdown whenever the PC takes an action. In addition if they fail a roll or roll with Fear while underwater, you can spend your GM move to advance it an additional time—or twice, if they rolled a failure with Fear.

Once the countdown ends, the underwater PC must mark a Stress whenever they take an action.

CONFLICT BETWEEN PCS

Sometimes a player might want their character to act against another PC in the scene. Before jumping to rolling dice, discuss the situation with both players to decide how to resolve the conflict. A roll might not be necessary to reach an outcome—but if rolling will be fun for everyone involved, come to a consensus on the terms of the roll, then facilitate the scene according to the results.

On an attack roll against a PC, the attacker rolls against the defender’s Evasion, just like an adversary. On any other kind of action roll, the instigator makes an action roll and the target makes a reaction roll. To succeed, the instigator must beat a Difficulty equal to the total value of the reaction roll.

ADVERSARIES AND ENVIRONMENTS

USING ADVERSARIES

ADVERSARY STAT BLOCKS

All the information required to run an adversary is contained in their stat block. An adversary’s stat block includes their:

NAME

Each stat block has a unique name. Abilities that affect adversaries with a certain name include all adversaries who use that stat block, regardless of their in-story name.

TIER

Each adversary is designed to oppose PCs of a certain tier. If you confront the party with an adversary from another tier, adjust their stats.

TYPE

The adversary’s type appears alongside their tier. An adversary’s type represents the role they play in a conflict.

The adversary types are:

DESCRIPTION

A summary of the adversary's appearance and demeanor.

MOTIVES & TACTICS

Suggusted impulses, actions and goals for the adversary.

DIFFICULTY

The Difficulty of any roll made against the adversary, unless otherwise noted.

DAMAGE THRESHOLDS, HIT POINTS, AND STRESS

These systems function the same way they do for PCs. The numbers listed after “Threshold” are the adversary’s Major and Severe Thresholds.

ATTACK MODIFIER

When you attack with the adversary, apply this bonus or penalty to your attack roll.

STANDARD ATTACK

A description of the adversary’s primary mode of inflicting harm on the PCs. It includes the attack’s name, its effective range, and the damage it deals on a success. Using an adversary’s standard attack is a GM move.

EXPERIENCE (OPTIONAL)

The GM can spend a Fear to add an adversary’s relevant Experience to raise their attack roll or increase the Difficulty of a roll made against them.

EXAMPLE EXPERIENCES:

Acrobatics

Hunt from Above

Navigation

Ambusher

Intimidation

Nobility

Bartering

Intrusion

Quick Reflexes

Blademaster

Keen Senses

Socialite

Bodyguard

Magical Knowledge

Stealth

Commander

Nature’s Friend

Tracker

FEATURE(S)

There are three kinds of adversary features: actions, reactions, and passives. Note: each adversaries stress is tracked individually. If a feature requires the GM to spend Stress to activate it, the Stress must come from the adversary whose feature is being activate. If a feature has a Fear requirement, it must be spent in addition to any Fear already spent—for instance, to interrupt the PCs and put the spotlight on the adversary.

FEAR FEATURE(S)

High-impact effects that cost a Fear to activate.

EXAMPLE ADVERSARY FEATURES:

ACTIONS

Haymaker - Action: Make an attack against a target within Very Close range. On a success, deal X direct physical damage.

Shredding Strike - Action: Make an attack against a target within Very Close range. On a success, deal X physical damage and the target must mark an Armor Slot without gaining its benefit (they can still use armor to reduce the damage).

More Where That Came From - Action: Summon three Jagged Knife Lackeys, who appear at Far range.

REACTIONS

Heavy Hitter - Reaction: When this adversary deals damage with a standard attack, you can spend a Fear to gain a +X bonus to the damage roll.

Team-Up - Reaction: When another adversary within Very Close range of this adversary deals X damage to a creature, you can mark a Stress to make a standard attack against that same creature. On a success, combine the damage.

Momentum - Reaction: When this adversary makes a successful attack against a PC, you gain a Fear.

PASSIVES

Horde (X) - Passive: When the Horde has marked half or more of their HP, their standard attack deals X damage instead.

Minion (X) - Passive: This adversary is defeated when they take any damage. For every X damage a PC deals to this adversary, defeat an additional Minion within range the attack would succeed against.

Relentless (X) - Passive: This adversary can be spotlighted up to X times per GM turn. Spend Fear as usual to spotlight them.

Slow - Passive: When you spotlight this adversary and they don’t have a token on their stat block, they can’t act yet. Place a token on their stat block and describe what they’re preparing to do. When you spotlight this adversary and they have a token on their stat block, clear the token and they can act.

Arcane Form - Passive: This adversary is resistant to magic damage.

Armored Carapace - Passive: When this adversary takes physical damage, reduce it by X.

FEAR FEATURES

Explosion - Action: Spend a Fear to erupt in a fiery explosion. Make an attack against all targets within Close range. Targets the adversary succeeds against take 1d8 magic damage and are knocked back to Far range.

BUILDING BALANCED ENCOUNTERS

When planning a battle, start with [(3 x the number of PCs in combat) + 2] Battle Points and make the following adjustments:

Then spend your Battle Points to add an adversary to the encounter:

DEFEATED ADVERSARIES

When an adversary marks their last Hit Point, they are defeated: incapacitated, tied up, routed, killed, or anything else the table decides makes sense.

ADVERSARY STAT BLOCK BENCHMARKS

ADVERSARY STATISTIC

TIER 1

TIER 2

TIER 3

TIER 4

Attack Modifier

+1

+2

+3

+4

Damage Dice

1d6+2 to 1d12+4

2d6+3 to 2d12+4

3d8+3 to 3d12+5

4d8+10 to 4d12+15

Difficulty

11

14

17

20

Damage Thresholds

Major 7/Severe 12

Major 10/Severe 20

Major 20/Severe 32

Major 25/Severe 45

ADVERSARIES BY TIER

This section contains the following stat blocks:

TIER 1 (LEVEL 1)

TIER 2 (LEVELS 2–4)

TIER 3 (LEVELS 5–7)

TIER 4 (LEVELS 8–10)

USING ENVIRONMENTS

Environments represent everything in a scene beyond the PCs and adversaries, such as the physical space, background NPCs, and natural forces.

ENVIRONMENT STAT BLOCK

Each environment’s stat block presents their necessary mechanical statistics:

NAME

The unique name of the environment stat block.

TIER

The PC tier the environment is designed to challenge.

TYPE

The type of scene it most easily supports:

DESCRIPTION

An evocative one-line summary of the environment.

IMPULSES

The manner or mode with which the environment pushs and pulls the people within them.

DIFFICULTY

The standard Difficulty for action rolls made to overcome, oppose, or resist the environment or its elements.

POTENTIAL ADVERSARIES

Suggested adversaries that might appear in scenes within the environment.

FEATURES

Features provide inspiration for GM moves you can use that represent the dynamic landscape or situation.

FEATURE QUESTIONS

Prompts for plot hooks, narrative engines, and connections to other story elements.

ADAPTING ENVIRONMENTS

Sometimes you want to use an environment but it’s at the wrong tier for your party. Or you might want to replace a feature or two, then present it as an entirely different environment. Whether planning your session or even improvising an environment mid-session, you can adjust an existing environment’s stat block to fit the needs of your scene or improvise elements as needed. The environments framework is there to help organize ideas, not to stifle creativity.

When you need to quickly adjust a stat block to a different tier, you can simply replace its existing statistics with those listed on the Environment Statistics by Tier table, using the column that corresponds to your party’s tier.

BENCHMARK STATISTICS FOR ENVIRONMENTS BY TIER

Environment Statistic

Tier 1

Tier 2

Tier 3

Tier 4

Damage Dice

1d6+1 to 1d8+3

2d6+3 to 2d10+2

3d8+3 to 3d10+1

4d8+3 to 4d10+10

Difficulty

11

14

17

20

ENVIRONMENT STAT BLOCKS BY TIER

This section contains the following stat blocks.

@UUID[Compendium.daggerheart.environments.Folder.GQ0VnOLrKBIHR6Us]{TIER 1 (LEVEL 1)}

@UUID[Compendium.daggerheart.environments.Folder.XMeecO3IRvu5ck6F]{TIER 2 (LEVELS 2–4)}

@UUID[Compendium.daggerheart.environments.Folder.MfrIkJK12PAEfbPL]{TIER 3 (LEVELS 5–7)}

@UUID[Compendium.daggerheart.environments.Folder.IKumu5HTLqONLYqb]{TIER 4 (LEVELS 8–10)}

ADDITIONAL GM GUIDANCE

This section provides additional guidance for preparing and running a session of Daggerheart.

STORY BEATS

In storytelling, a beat is a moment that changes the trajectory of the narrative—a shift in the world, a significant action or reaction, an emotional revelation, or an important decision.

Take turns with the players, narrating a beat and then letting them react and carry the scene forward with their own beats.

When preparing for a session, plan in terms of the moments that give shape to each scene or sequence, rather than pre-scripting specific details or exchanges.

PREPARING COMBAT ENCOUNTERS

Build the hurdles the PCs face around the question of “What helps tell the story?” Enemies, environments, and hazards are the tools for heightening tension and creating drama. Ensure that combat is being used to give players more information about the unfolding story, revealing the world, the plot, or the characters.

BATTLES AND NARRATIVE

Dynamic battles create suspense by forcing players to choose between their various objectives, engaging their character’s motivations and weaknesses, and creating the crucible that the players use to forge their characters into legendary heroes. When preparing combat encounters:

SESSION REWARDS

Reward players at the end of a session with:

CRAFTING SCENES

Whenever you start a session, arrive at a new place, or change the situation, tell the players what they need to know by thinking with all of your senses and sharing something unique or unexpected about the fiction.

ENGAGING YOUR PLAYERS

Keep your players engaged by:

1d12

Objective

1

Acquire (obtain or steal) an important item or items.

2

Capture one or more of the opponents.

3

Activate a magical device.

4

Frame a character or tarnish their reputation.

5

Drive the opponent into a corner or ambush point.

6

Stop a magical ritual, legal ceremony, or time-sensitive spell.

7

Hold the line—keep the enemy from reaching a specific area or group.

8

Plant evidence or a tracking device on a target.

9

Secure a specific location ahead of another group’s arrival.

10

Harass the opponent to deplete their resources or keep them occupied.

11

Destroy a piece of architecture, a statue, a shrine, or a weapon.

12

Investigate a situation to confirm or deny existing

information.

PHASED BATTLES

Make battles by shifting the nature of its enemies or environment mid-combat:

USING DOWNTIME

Use downtime scenes as a pressure release valve to vary the intensity of the story and give the PCs room to breathe.

Empower your players to frame their own downtime scenes.

Ask the players what it looks like as they tend to their wounds or unwind together, encouraging them to take the reins and work with other players whose characters are involved

PROJECTS DURING DOWNTIME

The Work on a Project downtime move requires more GM input than other downtime moves and is best suited for long-term endeavors the PCs wish to undertake.

These projects are typically tracked using a Progress Countdown. When deciding the starting value of the countdown, consider the complexity of the project, the availability of relevant tools, and the impact of the project on the story.

Simple projects advance their countdown each time a player uses the Work on a Project move, but complex projects require a roll.

EXTENDED DOWNTIME

When you fast-forward the story across an extended period, use montages to illustrate the passage of time. You gain 1d6 Fear per PC and advance any long-term countdowns as appropriate.

CAMPAIGN FRAMES

A campaign frame provides inspiration, tools, and mechanics to support a particular type of story at the table.

Every campaign frame has a complexity rating that indicates how much its mechanics deviate from or expand upon the Daggerheart core ruleset.

Each campaign frame includes the following sections.

You can find each campaign frame map in the appendix of the core rulebook or at www.daggerheart.com/downloads.

" }, "video": { "controls": true, @@ -180,11 +179,10 @@ "compendiumSource": null, "duplicateSource": null, "exportSource": null, - "coreVersion": "13.351", + "coreVersion": "13.346", "systemId": "daggerheart", "systemVersion": "0.0.1", - "lastModifiedBy": "Bgvu4A6AMkRFOTGR", - "modifiedTime": 1767971861704 + "lastModifiedBy": null }, "_key": "!journal.pages!uNs7ne9VCbbu5dcG.xCcdczIt2x2HBihY" } diff --git a/src/packs/rolltables/tables_Consumables_tF04P02yVN1YDVel.json b/src/packs/rolltables/tables_Consumables_tF04P02yVN1YDVel.json deleted file mode 100644 index c2413ec3..00000000 --- a/src/packs/rolltables/tables_Consumables_tF04P02yVN1YDVel.json +++ /dev/null @@ -1,1519 +0,0 @@ -{ - "name": "Consumables", - "img": "icons/consumables/potions/bottle-corked-red.webp", - "description": "

To generate a random consumable, choose a rarity, roll the designated dice, and match the total to the item in the table:

", - "results": [ - { - "type": "document", - "weight": 1, - "range": [ - 1, - 1 - ], - "name": "Stride Potion", - "img": "icons/consumables/potions/potion-flask-corked-cyan.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.lNtcrkgFGOJNaroE", - "_id": "HitkUdk2TJyIifWX", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.HitkUdk2TJyIifWX" - }, - { - "type": "document", - "weight": 1, - "range": [ - 2, - 2 - ], - "name": "Bolster Potion", - "img": "icons/consumables/potions/potion-vial-tube-yellow.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.FOPQNqXbiVO0ilYL", - "_id": "nXdPwzbs1TlhuAPG", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.nXdPwzbs1TlhuAPG" - }, - { - "type": "document", - "weight": 1, - "range": [ - 3, - 3 - ], - "name": "Control Potion", - "img": "icons/consumables/potions/flask-corked-blue.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.eeBhZSGLjuNZuJuI", - "_id": "3vNNo1oURhYA3ku3", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.3vNNo1oURhYA3ku3" - }, - { - "type": "document", - "weight": 1, - "range": [ - 4, - 4 - ], - "name": "Attune Potion", - "img": "icons/consumables/potions/bottle-conical-corked-purple.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.JGD3M9hBHtVAA8XP", - "_id": "eIfprIpZdRJCoBQ7", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.eIfprIpZdRJCoBQ7" - }, - { - "type": "document", - "weight": 1, - "range": [ - 5, - 5 - ], - "name": "Charm Potion", - "img": "icons/consumables/potions/potion-tube-corked-blue.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.CVBbFfOY75YwyQsp", - "_id": "LFWeiTR9rC34Tg03", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.LFWeiTR9rC34Tg03" - }, - { - "type": "document", - "weight": 1, - "range": [ - 6, - 6 - ], - "name": "Enlighten Potion", - "img": "icons/consumables/potions/vial-cork-empty.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.aWHSO2AqDufi7nL4", - "_id": "9wcVzrpjZHL87dGg", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.9wcVzrpjZHL87dGg" - }, - { - "type": "document", - "weight": 1, - "range": [ - 7, - 7 - ], - "name": "Minor Health Potion", - "img": "icons/consumables/potions/potion-tube-corked-red.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.tPfKtKRRjv8qdSqy", - "_id": "1IBI7K2c7tyhq9nK", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.1IBI7K2c7tyhq9nK" - }, - { - "type": "document", - "weight": 1, - "range": [ - 8, - 8 - ], - "name": "Minor Stamina Potion", - "img": "icons/consumables/potions/potion-tube-corked-green.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.b6vGSPFWOlzZZDLO", - "_id": "1D9VOgGRjhy6eWqA", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.1D9VOgGRjhy6eWqA" - }, - { - "type": "document", - "weight": 1, - "range": [ - 9, - 9 - ], - "name": "Grindletooth Venom", - "img": "icons/consumables/potions/bottle-conical-corked-labeled-skull-poison-green.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.8WkhvSzeOmLdnoLJ", - "_id": "Nqj2K2UDP27sAkUK", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.Nqj2K2UDP27sAkUK" - }, - { - "type": "document", - "weight": 1, - "range": [ - 10, - 10 - ], - "name": "Varik Leaves", - "img": "icons/consumables/plants/leaf-serrated-pink.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.hvy5BkG3F6iOIXTx", - "_id": "6ynsrta3MiinHg38", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.6ynsrta3MiinHg38" - }, - { - "type": "document", - "weight": 1, - "range": [ - 11, - 11 - ], - "name": "Vial of Moondrip", - "img": "icons/consumables/potions/bottle-ornate-bat-teal.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.VqEX5YwK5oL3r1t6", - "_id": "UDPco0e4rvz8Ch8X", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.UDPco0e4rvz8Ch8X" - }, - { - "type": "document", - "weight": 1, - "range": [ - 12, - 12 - ], - "name": "Unstable Arcane Shard", - "img": "icons/commodities/gems/gem-faceted-cushion-teal-black.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.mUepnLbkvFk0ha4Z", - "_id": "xQV1Uvhy1TqjEpH1", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.xQV1Uvhy1TqjEpH1" - }, - { - "type": "document", - "weight": 1, - "range": [ - 13, - 13 - ], - "name": "Potion of Stability", - "img": "icons/consumables/potions/bottle-conical-corked-labeled-shell-cyan.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.dvL8oaxpEF6jKvYN", - "_id": "pz4dXNbMYuSbFdzN", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.pz4dXNbMYuSbFdzN" - }, - { - "type": "document", - "weight": 1, - "range": [ - 14, - 14 - ], - "name": "Improved Grindletooth Venom", - "img": "icons/consumables/potions/potion-jar-corked-labeled-poison-skull-green.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.BqBWXXe9T07AMV4u", - "_id": "sqdq6x4SEqLdHDPM", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.sqdq6x4SEqLdHDPM" - }, - { - "type": "document", - "weight": 1, - "range": [ - 15, - 15 - ], - "name": "Morphing Clay", - "img": "icons/commodities/stone/ore-chunk-brown.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.f1NHVSIHJJCIOaBl", - "_id": "tceGvaaN4diOnr6V", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.tceGvaaN4diOnr6V" - }, - { - "type": "document", - "weight": 1, - "range": [ - 16, - 16 - ], - "name": "Vial of Darksmoke", - "img": "icons/consumables/potions/bottle-bulb-empty-glass.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.Nwv5ydGf0MWnzq1n", - "_id": "QtGq1JKzwBzWX6Sd", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.QtGq1JKzwBzWX6Sd" - }, - { - "type": "document", - "weight": 1, - "range": [ - 17, - 17 - ], - "name": "Jumping Root", - "img": "icons/consumables/plants/dried-bundle-wrapped-stems-sticks-brown.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.c2putn9apuurJhWX", - "_id": "lidCdzbTqahcajZb", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.lidCdzbTqahcajZb" - }, - { - "type": "document", - "weight": 1, - "range": [ - 18, - 18 - ], - "name": "Snap Powder", - "img": "icons/commodities/materials/bowl-powder-gold.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.cg6VtQ0eVZjDdcK0", - "_id": "m37ithF7wXFYrv6H", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.m37ithF7wXFYrv6H" - }, - { - "type": "document", - "weight": 1, - "range": [ - 19, - 19 - ], - "name": "Health Potion", - "img": "icons/consumables/potions/bottle-corked-red.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.Aruc2NLutWuVIjP1", - "_id": "RFrvEnE7WyDw2YWK", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.RFrvEnE7WyDw2YWK" - }, - { - "type": "document", - "weight": 1, - "range": [ - 20, - 20 - ], - "name": "Stamina Potion", - "img": "icons/consumables/potions/bottle-corked-green.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.hf3k1POoVSooJyN2", - "_id": "Lgs8ccMBUaq0XCHs", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.Lgs8ccMBUaq0XCHs" - }, - { - "type": "document", - "weight": 1, - "range": [ - 21, - 21 - ], - "name": "Armor Stitcher", - "img": "icons/skills/trades/textiles-stitching-leather-brown.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.VlbsCjvvLNfTzNXb", - "_id": "S2nsHwVRSFMbVlKh", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.S2nsHwVRSFMbVlKh" - }, - { - "type": "document", - "weight": 1, - "range": [ - 22, - 22 - ], - "name": "Gill Salve", - "img": "icons/commodities/materials/bowl-powder-blue.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.Nvbb9mze6o5D0AEg", - "_id": "B3cL4jMj0eaW8utR", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.B3cL4jMj0eaW8utR" - }, - { - "type": "document", - "weight": 1, - "range": [ - 23, - 23 - ], - "name": "Replication Parchment", - "img": "icons/sundries/scrolls/scroll-worn-beige.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.yJkwz4AP6yhGo8Vj", - "_id": "sgkt4zYiFSQMOmvo", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.sgkt4zYiFSQMOmvo" - }, - { - "type": "document", - "weight": 1, - "range": [ - 24, - 24 - ], - "name": "Improved Arcane Shard", - "img": "icons/commodities/gems/gem-faceted-teardrop-blue.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.nQTo6mNoPTEVBtkm", - "_id": "CCuzy7Y3KasoCouV", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.CCuzy7Y3KasoCouV" - }, - { - "type": "document", - "weight": 1, - "range": [ - 25, - 25 - ], - "name": "Major Stride Potion", - "img": "icons/consumables/potions/potion-jar-capped-teal.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.yK6eEDUrsPbZA8G0", - "_id": "TtXvBYeeMqQ4gDhO", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.TtXvBYeeMqQ4gDhO" - }, - { - "type": "document", - "weight": 1, - "range": [ - 26, - 26 - ], - "name": "Major Bolster Potion", - "img": "icons/consumables/potions/potion-jar-corked-orange.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.mnyQDRtngWWQeRXF", - "_id": "cRwTxLNJXMVKFW75", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.cRwTxLNJXMVKFW75" - }, - { - "type": "document", - "weight": 1, - "range": [ - 27, - 27 - ], - "name": "Major Control Potion", - "img": "icons/consumables/potions/bottle-round-corked-blue.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.80s1FLmTLtohZ5GH", - "_id": "q15EknW1P2cw4oyb", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.q15EknW1P2cw4oyb" - }, - { - "type": "document", - "weight": 1, - "range": [ - 28, - 28 - ], - "name": "Major Attune Potion", - "img": "icons/consumables/potions/bottle-round-corked-pink.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.CCPFm5iXXwvyYYwR", - "_id": "tkj1X675uJMTkMTR", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.tkj1X675uJMTkMTR" - }, - { - "type": "document", - "weight": 1, - "range": [ - 29, - 29 - ], - "name": "Major Charm Potion", - "img": "icons/consumables/potions/potion-bottle-corked-blue.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.IJLAUlQymbSjzsri", - "_id": "N6ImVJLHEOXxFSAE", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.N6ImVJLHEOXxFSAE" - }, - { - "type": "document", - "weight": 1, - "range": [ - 30, - 30 - ], - "name": "Major Enlighten Potion", - "img": "icons/consumables/potions/bottle-corked-empty.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.SDdv1G2veMLKrxcJ", - "_id": "NuHqSzBX7daJRor8", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.NuHqSzBX7daJRor8" - }, - { - "type": "document", - "weight": 1, - "range": [ - 31, - 31 - ], - "name": "Blood of the Yorgi", - "img": "icons/consumables/potions/potion-tube-corked-bat-gold-red.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.pDGzmczoTlKGmKgd", - "_id": "jpOQNSqbtl0ry3UU", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.jpOQNSqbtl0ry3UU" - }, - { - "type": "document", - "weight": 1, - "range": [ - 32, - 32 - ], - "name": "Homet’s Secret Potion", - "img": "icons/consumables/potions/conical-ornate-purple.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.VSwa1LpQ9PjZKsWF", - "_id": "QQsRU8GRbQeFU3KP", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.QQsRU8GRbQeFU3KP" - }, - { - "type": "document", - "weight": 1, - "range": [ - 33, - 33 - ], - "name": "Redthorn Saliva", - "img": "icons/commodities/materials/slime-thick-blue.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.s2Exl2XFuoOhtIov", - "_id": "UJfqKp0GtIrUEPyb", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.UJfqKp0GtIrUEPyb" - }, - { - "type": "document", - "weight": 1, - "range": [ - 34, - 34 - ], - "name": "Channelstone", - "img": "icons/commodities/stone/engraved-symbol-water-grey.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.IKMVQ6VwtapwoUim", - "_id": "VHCnZip35MMw57CG", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.VHCnZip35MMw57CG" - }, - { - "type": "document", - "weight": 1, - "range": [ - 35, - 35 - ], - "name": "Mythic Dust", - "img": "icons/commodities/materials/bowl-powder-grey.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.Zsh2AvZr8EkGtLyw", - "_id": "zphlOIuZBXFZcEVa", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.zphlOIuZBXFZcEVa" - }, - { - "type": "document", - "weight": 1, - "range": [ - 36, - 36 - ], - "name": "Acidpaste", - "img": "icons/commodities/materials/bowl-powder-teal.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.cfVFmS8vT9dbq9s1", - "_id": "bVFt8cYycBuUItfq", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.bVFt8cYycBuUItfq" - }, - { - "type": "document", - "weight": 1, - "range": [ - 37, - 37 - ], - "name": "Hopehold Flare", - "img": "icons/commodities/tech/smoke-bomb-purple.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.EhaQCPJ8oiqpRIwB", - "_id": "atidisB2nCu9CF3b", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.atidisB2nCu9CF3b" - }, - { - "type": "document", - "weight": 1, - "range": [ - 38, - 38 - ], - "name": "Major Arcane Shard", - "img": "icons/commodities/gems/gem-rough-round-blue.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.AA7bmiwv00lshPrC", - "_id": "VQ75yVBccK73Gif3", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.VQ75yVBccK73Gif3" - }, - { - "type": "document", - "weight": 1, - "range": [ - 39, - 39 - ], - "name": "Featherbone", - "img": "icons/commodities/bones/bones-stack-worn-brown.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.DpxEMpwfasEBpORU", - "_id": "hPKdeNWzJqCuFfjX", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.hPKdeNWzJqCuFfjX" - }, - { - "type": "document", - "weight": 1, - "range": [ - 40, - 40 - ], - "name": "Circle of the Void", - "img": "icons/magic/unholy/orb-glowing-purple.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.elsyP6VhHw1JjGSl", - "_id": "sh98CSXJL593xj0h", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.sh98CSXJL593xj0h" - }, - { - "type": "document", - "weight": 1, - "range": [ - 41, - 41 - ], - "name": "Sun Tree Sap", - "img": "icons/consumables/drinks/wine-amphora-clay-pink.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.kwexUzdM9wm1Qums", - "_id": "lL2638NxooIcWgfL", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.lL2638NxooIcWgfL" - }, - { - "type": "document", - "weight": 1, - "range": [ - 42, - 42 - ], - "name": "Dripfang Poison", - "img": "icons/consumables/potions/potion-jug-corked-skull-poison-brown-green.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.eU8VpbWB2NHIL47n", - "_id": "xXPyThPKW3noUC1t", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.xXPyThPKW3noUC1t" - }, - { - "type": "document", - "weight": 1, - "range": [ - 43, - 43 - ], - "name": "Major Health Potion", - "img": "icons/consumables/potions/bottle-round-label-cork-red.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.cM7pHe8bBAxSZ2xR", - "_id": "x2EnrXxULti4Chpb", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.x2EnrXxULti4Chpb" - }, - { - "type": "document", - "weight": 1, - "range": [ - 44, - 44 - ], - "name": "Major Stamina Potion", - "img": "icons/consumables/potions/bottle-round-label-cork-green.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.I4cQ03xbxnc81EGa", - "_id": "2WsA7zyGFJROyf8n", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.2WsA7zyGFJROyf8n" - }, - { - "type": "document", - "weight": 1, - "range": [ - 45, - 45 - ], - "name": "Ogre Musk", - "img": "icons/commodities/materials/slime-thick-green.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.qr1bosjFcUfuwq4B", - "_id": "vNlix6NqUSWwq2MU", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.vNlix6NqUSWwq2MU" - }, - { - "type": "document", - "weight": 1, - "range": [ - 46, - 46 - ], - "name": "Wingsprout", - "img": "icons/consumables/plants/leaf-broad-blue.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.n10vozlmosVR6lo4", - "_id": "iCj1YAinJxuGT2EN", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.iCj1YAinJxuGT2EN" - }, - { - "type": "document", - "weight": 1, - "range": [ - 47, - 47 - ], - "name": "Jar of Lost Voices", - "img": "icons/containers/kitchenware/jug-wrapped-red.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.yUol6M5b8jsbk9za", - "_id": "mTq9bibanrSynx3t", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.mTq9bibanrSynx3t" - }, - { - "type": "document", - "weight": 1, - "range": [ - 48, - 48 - ], - "name": "Dragonbloom Tea", - "img": "icons/consumables/drinks/tea-jug-gourd-brown.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.wM18PWWW2Ami4fBG", - "_id": "oEKTJg1p52E1NVqm", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.oEKTJg1p52E1NVqm" - }, - { - "type": "document", - "weight": 1, - "range": [ - 49, - 49 - ], - "name": "Bridge Seed", - "img": "icons/consumables/plants/leaf-old-dried-curled-green.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.RrIasiMCt6mqVTps", - "_id": "viK90OvIqxwj8kcD", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.viK90OvIqxwj8kcD" - }, - { - "type": "document", - "weight": 1, - "range": [ - 50, - 50 - ], - "name": "Sleeping Sap", - "img": "icons/consumables/potions/bottle-bulb-corked-labeled-blue.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.XZavUVlHEvE2srEt", - "_id": "7bOHwnq9xyBXT0Or", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.7bOHwnq9xyBXT0Or" - }, - { - "type": "document", - "weight": 1, - "range": [ - 51, - 51 - ], - "name": "Feast of Xuria", - "img": "icons/consumables/food/bowl-stew-tofu-potato-red.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.aX6NyxkNzu0LcJpt", - "_id": "aF8qVVOuVHUCyDon", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.aF8qVVOuVHUCyDon" - }, - { - "type": "document", - "weight": 1, - "range": [ - 52, - 52 - ], - "name": "Bonding Honey", - "img": "icons/consumables/food/soup-broth-bowl-wooden-yellow.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.PfQvqopXgvroBklL", - "_id": "dXbNSYrZs1LpnX2D", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.dXbNSYrZs1LpnX2D" - }, - { - "type": "document", - "weight": 1, - "range": [ - 53, - 53 - ], - "name": "Shrinking Potion", - "img": "icons/consumables/potions/flask-decorated-label-pink.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.HGixKenQwhyRAYNk", - "_id": "YpBzj89kfEpjmhO7", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.YpBzj89kfEpjmhO7" - }, - { - "type": "document", - "weight": 1, - "range": [ - 54, - 54 - ], - "name": "Growing Potion", - "img": "icons/consumables/potions/potion-bottle-corked-labeled-green.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.fl2f3ees8RFMze9t", - "_id": "3QHLVvfKx73UTKmm", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.3QHLVvfKx73UTKmm" - }, - { - "type": "document", - "weight": 1, - "range": [ - 55, - 55 - ], - "name": "Knowledge Stone", - "img": "icons/commodities/treasure/token-engraved-blue-glowing.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.nL9IALzm9BNi5oSt", - "_id": "C4MpR7mBHjIiPRzo", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.C4MpR7mBHjIiPRzo" - }, - { - "type": "document", - "weight": 1, - "range": [ - 56, - 56 - ], - "name": "Sweet Moss", - "img": "icons/consumables/plants/succulent-bundle-green.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.GrDrRqWgv7gvl9vn", - "_id": "Z5UPngFMi7TSojoM", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.Z5UPngFMi7TSojoM" - }, - { - "type": "document", - "weight": 1, - "range": [ - 57, - 57 - ], - "name": "Blinding Orb", - "img": "icons/magic/light/explosion-star-large-blue-yellow.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.eAXHdzA5qNPldOpn", - "_id": "bMCETSm5oYxdpRyu", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.bMCETSm5oYxdpRyu" - }, - { - "type": "document", - "weight": 1, - "range": [ - 58, - 58 - ], - "name": "Death Tea", - "img": "icons/consumables/drinks/wine-amphora-clay-gray.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.xDnJeF1grkmKck8Q", - "_id": "wkAnGk1yLHE6bzfr", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.wkAnGk1yLHE6bzfr" - }, - { - "type": "document", - "weight": 1, - "range": [ - 59, - 59 - ], - "name": "Mirror of Marigold", - "img": "icons/commodities/treasure/token-silver-blue.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.UFQVwgYOUZ88UxcH", - "_id": "XKAAfJUyqwRSRgh1", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.XKAAfJUyqwRSRgh1" - }, - { - "type": "document", - "weight": 1, - "range": [ - 60, - 60 - ], - "name": "Stardrop", - "img": "icons/magic/light/projectiles-star-purple.webp", - "documentUuid": "Compendium.daggerheart.consumables.Item.y4c1jrlHrf0wBWOq", - "_id": "91ALaXvFQDOq5h2A", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!tF04P02yVN1YDVel.91ALaXvFQDOq5h2A" - } - ], - "replacement": true, - "displayRoll": true, - "folder": null, - "ownership": { - "default": 0, - "Bgvu4A6AMkRFOTGR": 3 - }, - "flags": {}, - "formula": "1d60", - "_id": "tF04P02yVN1YDVel", - "sort": 300000, - "_key": "!tables!tF04P02yVN1YDVel" -} diff --git a/src/packs/rolltables/tables_Loot_S61Shlt2I5CbLRjz.json b/src/packs/rolltables/tables_Loot_S61Shlt2I5CbLRjz.json deleted file mode 100644 index 9517eadd..00000000 --- a/src/packs/rolltables/tables_Loot_S61Shlt2I5CbLRjz.json +++ /dev/null @@ -1,1519 +0,0 @@ -{ - "name": "Loot", - "img": "icons/commodities/treasure/brooch-gold-ruby.webp", - "description": "

To generate a random item, choose a rarity, roll the designated dice, and match the total to the item in the table:

", - "results": [ - { - "type": "document", - "weight": 1, - "range": [ - 1, - 1 - ], - "_id": "U0jqWUc9SVQTwZOt", - "name": "Premium Bedroll", - "img": "icons/sundries/survival/bedroll-blue-red.webp", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "documentUuid": "Compendium.daggerheart.loot.Item.QGYPNBIufpBguwjC", - "_key": "!tables.results!S61Shlt2I5CbLRjz.U0jqWUc9SVQTwZOt" - }, - { - "type": "document", - "weight": 1, - "range": [ - 2, - 2 - ], - "_id": "jsje2l4MxyDSeiQ5", - "name": "Piper Whistle", - "img": "icons/tools/instruments/pipe-flue-tan.webp", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "documentUuid": "Compendium.daggerheart.loot.Item.v4PIoCCEjeE3acys", - "_key": "!tables.results!S61Shlt2I5CbLRjz.jsje2l4MxyDSeiQ5" - }, - { - "type": "document", - "weight": 1, - "range": [ - 3, - 3 - ], - "name": "Charging Quiver", - "img": "icons/containers/ammunition/arrows-quiver-grey-gold.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.gsUDP90d4SRtLEUn", - "_id": "OUEywrTcAKy9jOf2", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.OUEywrTcAKy9jOf2" - }, - { - "type": "document", - "weight": 1, - "range": [ - 4, - 4 - ], - "name": "Alistair’s Torch", - "img": "icons/sundries/lights/torch-brown-lit.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.MeEg57T6MKpw3sme", - "_id": "jYHROKTetnH9U1NF", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.jYHROKTetnH9U1NF" - }, - { - "type": "document", - "weight": 1, - "range": [ - 5, - 5 - ], - "name": "Speaking Orbs", - "img": "icons/commodities/materials/glass-orb-blue-gold.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.LZrG6CFiSjpLA2F1", - "_id": "1skyTdrVs2cPMbYd", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.1skyTdrVs2cPMbYd" - }, - { - "type": "document", - "weight": 1, - "range": [ - 6, - 6 - ], - "name": "Manacles", - "img": "icons/sundries/survival/cuffs-shackles-steel.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.GkmATIuemyFtQX1D", - "_id": "ew7VM7lktgEHUVNh", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.ew7VM7lktgEHUVNh" - }, - { - "type": "document", - "weight": 1, - "range": [ - 7, - 7 - ], - "name": "Arcane Cloak", - "img": "icons/equipment/back/cloak-layered-green-brown.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.4STt98biZwjFoKOe", - "_id": "ghM44MABWLVc0594", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.ghM44MABWLVc0594" - }, - { - "type": "document", - "weight": 1, - "range": [ - 8, - 8 - ], - "name": "Woven Net", - "img": "icons/tools/fishing/net-tan.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.ARuv48PWUGJGBC4n", - "_id": "NWltT1Sifub9cFxR", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.NWltT1Sifub9cFxR" - }, - { - "type": "document", - "weight": 1, - "range": [ - 9, - 9 - ], - "name": "Fire Jar", - "img": "icons/containers/kitchenware/jug-wrapped-red.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.X6RMkIt89wf7qX2E", - "_id": "2NbLnKKXmmx5m1Di", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.2NbLnKKXmmx5m1Di" - }, - { - "type": "document", - "weight": 1, - "range": [ - 10, - 10 - ], - "name": "Suspended Rod", - "img": "icons/commodities/tech/pipe-metal.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.nnj12RiFanq7s5zv", - "_id": "COpKr1HYsR8Jkwyn", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.COpKr1HYsR8Jkwyn" - }, - { - "type": "document", - "weight": 1, - "range": [ - 11, - 11 - ], - "name": "Glamour Stone", - "img": "icons/commodities/treasure/token-engraved-purple-glowing.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.Pj17cvdJ1XG1jv6I", - "_id": "e1vjhShtDZSnVUNu", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.e1vjhShtDZSnVUNu" - }, - { - "type": "document", - "weight": 1, - "range": [ - 12, - 12 - ], - "name": "Empty Chest", - "img": "icons/containers/chest/chest-simple-box-gold-brown.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.p2yy61uKsyIsl8cU", - "_id": "HIvObcLTrncfN7Qx", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.HIvObcLTrncfN7Qx" - }, - { - "type": "document", - "weight": 1, - "range": [ - 13, - 13 - ], - "name": "Companion Case", - "img": "icons/containers/chest/chest-reinforced-steel-red.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.V25uXkAQvK3hUta4", - "_id": "fCgRZndg111RI8MC", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.fCgRZndg111RI8MC" - }, - { - "type": "document", - "weight": 1, - "range": [ - 14, - 14 - ], - "name": "Piercing Arrows", - "img": "icons/weapons/ammunition/arrow-broadhead-glowing-orange.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.I63LTFD6GXHgyGpR", - "_id": "97oyM8w0xZjeQkeM", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.97oyM8w0xZjeQkeM" - }, - { - "type": "document", - "weight": 1, - "range": [ - 15, - 15 - ], - "name": "Valorstone", - "img": "icons/commodities/treasure/token-engraved-spiral-grey-white.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.7yywua9TmQ4WP5WH", - "_id": "EY4Rtt8KDpjhTxCH", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.EY4Rtt8KDpjhTxCH" - }, - { - "type": "document", - "weight": 1, - "range": [ - 16, - 16 - ], - "name": "Skeleton Key", - "img": "icons/sundries/misc/key-ornate-iron-black.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.edkNgwy4xghZreBa", - "_id": "XYORiEz8M7K3BNWM", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.XYORiEz8M7K3BNWM" - }, - { - "type": "document", - "weight": 1, - "range": [ - 17, - 17 - ], - "name": "Arcane Prism", - "img": "icons/commodities/gems/gem-faceted-trillion-blue.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.Mn1eo2Mdtu1kzyxB", - "_id": "Yc3x5ifOASG15MrD", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.Yc3x5ifOASG15MrD" - }, - { - "type": "document", - "weight": 1, - "range": [ - 18, - 18 - ], - "name": "Minor Stamina Potion Recipe", - "img": "icons/sundries/scrolls/scroll-bound-green.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.1TLpFsp3PLDsqoTw", - "_id": "akIE4Bu9zq6N4N0d", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.akIE4Bu9zq6N4N0d" - }, - { - "type": "document", - "weight": 1, - "range": [ - 19, - 19 - ], - "name": "Minor Health Potion Recipe", - "img": "icons/sundries/scrolls/scroll-bound-red.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.PQxvxAVBbkt0TleC", - "_id": "GVUbOeXrT0TLPlW9", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.GVUbOeXrT0TLPlW9" - }, - { - "type": "document", - "weight": 1, - "range": [ - 20, - 20 - ], - "name": "Homing Compasses", - "img": "icons/tools/navigation/compass-worn-copper.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.yrAGYlDyoe4OYl7d", - "_id": "0FaxK5udut2y79B7", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.0FaxK5udut2y79B7" - }, - { - "type": "document", - "weight": 1, - "range": [ - 21, - 21 - ], - "name": "Corrector Sprite", - "img": "icons/magic/light/orbs-smoke-pink.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.G0RktbmtnuAlKCRH", - "_id": "R1iPLFiRgHFhm5x2", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.R1iPLFiRgHFhm5x2" - }, - { - "type": "document", - "weight": 1, - "range": [ - 22, - 22 - ], - "name": "Gecko Gloves", - "img": "icons/equipment/hand/glove-ring-leather-green.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.CGzjBpHJRG8KSt5Y", - "_id": "MUWHmP6Bx55SSOpQ", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.MUWHmP6Bx55SSOpQ" - }, - { - "type": "document", - "weight": 1, - "range": [ - 23, - 23 - ], - "name": "Lorekeeper", - "img": "icons/sundries/books/book-backed-wood-tan.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.JsPYzrqpITqGj23I", - "_id": "X2vU11ltW4aMbPcc", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.X2vU11ltW4aMbPcc" - }, - { - "type": "document", - "weight": 1, - "range": [ - 24, - 24 - ], - "name": "Vial of Darksmoke Recipe", - "img": "icons/sundries/scrolls/scroll-bound-black-brown.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.MhCo8i0cRXzdnXbA", - "_id": "SJmfB1HDxXJB6xyS", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.SJmfB1HDxXJB6xyS" - }, - { - "type": "document", - "weight": 1, - "range": [ - 25, - 25 - ], - "name": "Bloodstone", - "img": "icons/commodities/treasure/token-engraved-eye-red.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.oMd78vhL2x2NO8Mg", - "_id": "dEyzIWTTbSk8LaoN", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.dEyzIWTTbSk8LaoN" - }, - { - "type": "document", - "weight": 1, - "range": [ - 26, - 26 - ], - "name": "Greatstone", - "img": "icons/commodities/treasure/token-etched-h-brown.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.y7zABzR0Q2fRskTw", - "_id": "sSwx1g6eqgLqPgBu", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.sSwx1g6eqgLqPgBu" - }, - { - "type": "document", - "weight": 1, - "range": [ - 27, - 27 - ], - "name": "Glider", - "img": "icons/commodities/leather/leather-patch-red.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.CiXwelozmBDcPY48", - "_id": "DSPDKm53Mu6PeaK1", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.DSPDKm53Mu6PeaK1" - }, - { - "type": "document", - "weight": 1, - "range": [ - 28, - 28 - ], - "name": "Ring of Silence", - "img": "icons/equipment/finger/ring-ball-purple.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.K1ysGnTpNyxPu5Au", - "_id": "SSuVJ6WbSdfpMxTx", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.SSuVJ6WbSdfpMxTx" - }, - { - "type": "document", - "weight": 1, - "range": [ - 29, - 29 - ], - "name": "Calming Pendant", - "img": "icons/equipment/neck/amulet-round-blue.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.tgFFMxpuRSiRrrEB", - "_id": "Fwq1wJzAGdO2rUWo", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.Fwq1wJzAGdO2rUWo" - }, - { - "type": "document", - "weight": 1, - "range": [ - 30, - 30 - ], - "name": "Dual Flask", - "img": "icons/consumables/potions/round-decorated-snake-green.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.HCvcAu3sdHCspGMP", - "_id": "elle12djjCs3D6Vr", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.elle12djjCs3D6Vr" - }, - { - "type": "document", - "weight": 1, - "range": [ - 31, - 31 - ], - "name": "Bag of Ficklesand", - "img": "icons/containers/bags/pouch-cloth-tan.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.v758j4FwNVAurhYK", - "_id": "yXyVHCrQEamt9DSt", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.yXyVHCrQEamt9DSt" - }, - { - "type": "document", - "weight": 1, - "range": [ - 32, - 32 - ], - "name": "Ring of Resistance", - "img": "icons/equipment/finger/ring-shield-silver.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.aUqRifqR5JXXa1dN", - "_id": "mHlhtw9BRyMeeGvz", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.mHlhtw9BRyMeeGvz" - }, - { - "type": "document", - "weight": 1, - "range": [ - 33, - 33 - ], - "name": "Phoenix Feather", - "img": "icons/commodities/materials/feather-red.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.QNtzJSVENww63THa", - "_id": "5R86TY964aeYZxLa", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.5R86TY964aeYZxLa" - }, - { - "type": "document", - "weight": 1, - "range": [ - 34, - 34 - ], - "name": "Box of Many Goods", - "img": "icons/containers/boxes/crate-heavy-yellow.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.bZyT7Qw7iafswlTY", - "_id": "6ZhCovGbkTxRJFFx", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.6ZhCovGbkTxRJFFx" - }, - { - "type": "document", - "weight": 1, - "range": [ - 35, - 35 - ], - "name": "Airblade Charm", - "img": "icons/equipment/neck/amulet-carved-stone-spiral-blue.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.cTYvyaSKBxosM9Y9", - "_id": "UYHcpVuL9P26VPBM", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.UYHcpVuL9P26VPBM" - }, - { - "type": "document", - "weight": 1, - "range": [ - 36, - 36 - ], - "name": "Portal Seed", - "img": "icons/commodities/treasure/egg-ornate-green-gold.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.eRd5Gk7J7hPCqp11", - "_id": "yOchOTcWwUwug2st", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.yOchOTcWwUwug2st" - }, - { - "type": "document", - "weight": 1, - "range": [ - 37, - 37 - ], - "name": "Paragon’s Chain", - "img": "icons/equipment/neck/choker-chain-thin-gold.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.F4hoRfvVdZq5bhhI", - "_id": "TFgxOPNDbLYDzxrl", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.TFgxOPNDbLYDzxrl" - }, - { - "type": "document", - "weight": 1, - "range": [ - 38, - 38 - ], - "name": "Elusive Amulet", - "img": "icons/equipment/neck/pendant-rough-silver-purple.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.PkmTZXRMZL022O75", - "_id": "GcJEXPoMDxxWNYno", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.GcJEXPoMDxxWNYno" - }, - { - "type": "document", - "weight": 1, - "range": [ - 39, - 39 - ], - "name": "Hopekeeper Locket", - "img": "icons/equipment/neck/amulet-round-engraved-spiral-gold.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.9DcFR75tsnBYIp6Z", - "_id": "iyiOPysR6uQE7QXR", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.iyiOPysR6uQE7QXR" - }, - { - "type": "document", - "weight": 1, - "range": [ - 40, - 40 - ], - "name": "Infinite Bag", - "img": "icons/containers/bags/pack-engraved-leather-blue.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.Iedjw1LVWEozVh0J", - "_id": "nsg8GxSABbvsZo5v", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.nsg8GxSABbvsZo5v" - }, - { - "type": "document", - "weight": 1, - "range": [ - 41, - 41 - ], - "name": "Stride Relic", - "img": "icons/commodities/treasure/token-runed-ing-brown.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.FfJISMzYATaPQPLc", - "_id": "DFEA1djJSnstOXym", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.DFEA1djJSnstOXym" - }, - { - "type": "document", - "weight": 1, - "range": [ - 42, - 42 - ], - "name": "Bolster Relic", - "img": "icons/commodities/treasure/token-runed-radr-brown.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.m3EpxlDgxn2tCDDR", - "_id": "xa075fPgUGCVVGiy", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.xa075fPgUGCVVGiy" - }, - { - "type": "document", - "weight": 1, - "range": [ - 43, - 43 - ], - "name": "Control Relic", - "img": "icons/commodities/treasure/token-runed-sigel-brown.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.QPGBDItjrRhXU6iJ", - "_id": "zNS3a6JZLlLgXxXI", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.zNS3a6JZLlLgXxXI" - }, - { - "type": "document", - "weight": 1, - "range": [ - 44, - 44 - ], - "name": "Attune Relic", - "img": "icons/commodities/treasure/token-runed-spiral-grey.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.vK6bKyQTT3m8WvMh", - "_id": "pf1zrtyDamclUpb5", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.pf1zrtyDamclUpb5" - }, - { - "type": "document", - "weight": 1, - "range": [ - 45, - 45 - ], - "name": "Charm Relic", - "img": "icons/commodities/treasure/token-runed-wyn-grey.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.9P9jqGSlxVCbTdLe", - "_id": "JNsJQefAYXARnzcB", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.JNsJQefAYXARnzcB" - }, - { - "type": "document", - "weight": 1, - "range": [ - 46, - 46 - ], - "name": "Enlighten Relic", - "img": "icons/commodities/treasure/token-runed-os-grey.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.vSGx1f9SYUiA29L3", - "_id": "Bk2zTfHlpNg4f4AB", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.Bk2zTfHlpNg4f4AB" - }, - { - "type": "document", - "weight": 1, - "range": [ - 47, - 47 - ], - "name": "Honing Relic", - "img": "icons/commodities/treasure/token-runed-nyd-yellow.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.SAAnEAeXDnhBbLjB", - "_id": "BxB4fAXy57tWHc98", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.BxB4fAXy57tWHc98" - }, - { - "type": "document", - "weight": 1, - "range": [ - 48, - 48 - ], - "name": "Flickerfly Pendant", - "img": "icons/equipment/neck/choker-rough-green.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.9VKYSBQxN9XFWlAm", - "_id": "b7JDKti5fXipswXs", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.b7JDKti5fXipswXs" - }, - { - "type": "document", - "weight": 1, - "range": [ - 49, - 49 - ], - "name": "Lakestrider Boots", - "img": "icons/equipment/feet/shoes-collared-leather-blue.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.NgvmrJYKpA2PrRSo", - "_id": "ngpVKb5GIJySdf2Y", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.ngpVKb5GIJySdf2Y" - }, - { - "type": "document", - "weight": 1, - "range": [ - 50, - 50 - ], - "name": "Clay Companion", - "img": "icons/commodities/gems/pearl-brown.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.lGIk9vBNz0jvskXD", - "_id": "IrKbVyqUcSruPPfZ", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.IrKbVyqUcSruPPfZ" - }, - { - "type": "document", - "weight": 1, - "range": [ - 51, - 51 - ], - "name": "Mythic Dust Recipe", - "img": "icons/sundries/scrolls/scroll-bound-green.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.5YZls8XH3MB7twNa", - "_id": "V1R3wUj4l7lnbph1", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.V1R3wUj4l7lnbph1" - }, - { - "type": "document", - "weight": 1, - "range": [ - 52, - 52 - ], - "name": "Shard of Memory", - "img": "icons/commodities/gems/gem-rough-navette-purple-pink.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.2ULPgNyqCrxea0v0", - "_id": "0dFhkeOokjbwptHh", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.0dFhkeOokjbwptHh" - }, - { - "type": "document", - "weight": 1, - "range": [ - 53, - 53 - ], - "name": "Gem of Alacrity", - "img": "icons/commodities/gems/gem-rough-ball-purple.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.zecFwBUSWtB3HW8X", - "_id": "CLObmHdtId5fEs4n", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.CLObmHdtId5fEs4n" - }, - { - "type": "document", - "weight": 1, - "range": [ - 54, - 54 - ], - "name": "Gem of Might", - "img": "icons/commodities/gems/gem-rough-brilliant-green.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.rtSInNPc4B3ChBUZ", - "_id": "17Zw4Tl1s1h5cFZK", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.17Zw4Tl1s1h5cFZK" - }, - { - "type": "document", - "weight": 1, - "range": [ - 55, - 55 - ], - "name": "Gem of Precision", - "img": "icons/commodities/gems/gem-rough-cushion-green.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.CrvJ7vb4s40YgEcy", - "_id": "6zmLU8WdmEWbIHBL", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.6zmLU8WdmEWbIHBL" - }, - { - "type": "document", - "weight": 1, - "range": [ - 56, - 56 - ], - "name": "Gem of Insight", - "img": "icons/commodities/gems/gem-rough-cushion-blue.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.TbgeT9ZxKHqFqJSN", - "_id": "AEeWEQnJwq51mBnM", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.AEeWEQnJwq51mBnM" - }, - { - "type": "document", - "weight": 1, - "range": [ - 57, - 57 - ], - "name": "Gem of Audacity", - "img": "icons/commodities/gems/gem-rough-cushion-pink-yellow.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.hMu9It3ThCLCXuCA", - "_id": "SuymUKc0CeisDgm2", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.SuymUKc0CeisDgm2" - }, - { - "type": "document", - "weight": 1, - "range": [ - 58, - 58 - ], - "name": "Gem of Sagacity", - "img": "icons/commodities/gems/gem-rough-cushion-purple.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.ua351S7CsH22X1x2", - "_id": "27LKVRatgX1asMpR", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.27LKVRatgX1asMpR" - }, - { - "type": "document", - "weight": 1, - "range": [ - 59, - 59 - ], - "name": "Ring of Unbreakable Resolve", - "img": "icons/equipment/finger/ring-faceted-gold-teal.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.kn71qCQY0DnjmQBJ", - "_id": "3A76l2jxYMqVfcQY", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.3A76l2jxYMqVfcQY" - }, - { - "type": "document", - "weight": 1, - "range": [ - 60, - 60 - ], - "name": "Belt of Unity", - "img": "icons/equipment/waist/belt-buckle-ornate-steel.webp", - "documentUuid": "Compendium.daggerheart.loot.Item.gFzkUGCjkRJtyoe9", - "_id": "dkLn9gwnlaD4kbZ2", - "description": "", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "_key": "!tables.results!S61Shlt2I5CbLRjz.dkLn9gwnlaD4kbZ2" - } - ], - "replacement": true, - "displayRoll": true, - "folder": null, - "ownership": { - "default": 0, - "Bgvu4A6AMkRFOTGR": 3 - }, - "flags": {}, - "formula": "1d60", - "_id": "S61Shlt2I5CbLRjz", - "sort": 200000, - "_key": "!tables!S61Shlt2I5CbLRjz" -} diff --git a/src/packs/rolltables/tables_Table_of_Random_Objectives_I5L1dlgxXTNrCCkL.json b/src/packs/rolltables/tables_Table_of_Random_Objectives_I5L1dlgxXTNrCCkL.json deleted file mode 100644 index b10127e7..00000000 --- a/src/packs/rolltables/tables_Table_of_Random_Objectives_I5L1dlgxXTNrCCkL.json +++ /dev/null @@ -1,319 +0,0 @@ -{ - "name": "Table of Random Objectives", - "img": "icons/sundries/documents/document-torn-diagram-tan.webp", - "description": "

Layering Goals Other than Attrition into Combat

", - "results": [ - { - "type": "text", - "weight": 1, - "range": [ - 1, - 1 - ], - "_id": "LDuVbmdvhJiEOe7U", - "name": "", - "img": "icons/svg/d12-grey.svg", - "description": "

Acquire (obtain or steal) an important item or items.

", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "documentUuid": null, - "_key": "!tables.results!I5L1dlgxXTNrCCkL.LDuVbmdvhJiEOe7U" - }, - { - "type": "text", - "weight": 1, - "range": [ - 2, - 2 - ], - "_id": "FxYpST4nQUTBp1mN", - "name": "", - "img": "icons/svg/d12-grey.svg", - "description": "

Capture one or more of the opponents.

", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "documentUuid": null, - "_key": "!tables.results!I5L1dlgxXTNrCCkL.FxYpST4nQUTBp1mN" - }, - { - "type": "text", - "weight": 1, - "range": [ - 3, - 3 - ], - "_id": "bTkZgxqEr4lNxzeK", - "name": "", - "img": "icons/svg/d12-grey.svg", - "description": "

Activate a magical device.

", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "documentUuid": null, - "_key": "!tables.results!I5L1dlgxXTNrCCkL.bTkZgxqEr4lNxzeK" - }, - { - "type": "text", - "weight": 1, - "range": [ - 4, - 4 - ], - "_id": "T39LgOL1cw5AIY59", - "name": "", - "img": "icons/svg/d12-grey.svg", - "description": "

Frame a character or tarnish their reputation.

", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "documentUuid": null, - "_key": "!tables.results!I5L1dlgxXTNrCCkL.T39LgOL1cw5AIY59" - }, - { - "type": "text", - "weight": 1, - "range": [ - 5, - 5 - ], - "_id": "MHgv8dlrwA3ZmBKq", - "name": "", - "img": "icons/svg/d12-grey.svg", - "description": "

Drive the opponent into a corner or ambush point.

", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "documentUuid": null, - "_key": "!tables.results!I5L1dlgxXTNrCCkL.MHgv8dlrwA3ZmBKq" - }, - { - "type": "text", - "weight": 1, - "range": [ - 6, - 6 - ], - "_id": "4USCNNavzVvBqldn", - "name": "", - "img": "icons/svg/d12-grey.svg", - "description": "

Stop a magical ritual, legal ceremony, or time-sensitive spell.

", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "documentUuid": null, - "_key": "!tables.results!I5L1dlgxXTNrCCkL.4USCNNavzVvBqldn" - }, - { - "type": "text", - "weight": 1, - "range": [ - 7, - 7 - ], - "_id": "gwZnWTauHsbb6rsr", - "name": "", - "img": "icons/svg/d12-grey.svg", - "description": "

Hold the line—keep the enemy from reaching a specific area or group.

", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "documentUuid": null, - "_key": "!tables.results!I5L1dlgxXTNrCCkL.gwZnWTauHsbb6rsr" - }, - { - "type": "text", - "weight": 1, - "range": [ - 8, - 8 - ], - "_id": "beDIxxPyCCVa8nlE", - "name": "", - "img": "icons/svg/d12-grey.svg", - "description": "

Plant evidence or a tracking device on a target.

", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "documentUuid": null, - "_key": "!tables.results!I5L1dlgxXTNrCCkL.beDIxxPyCCVa8nlE" - }, - { - "type": "text", - "weight": 1, - "range": [ - 9, - 9 - ], - "_id": "C70V6prVmZd5VRV8", - "name": "", - "img": "icons/svg/d12-grey.svg", - "description": "

Secure a specific location ahead of another group’s arrival.

", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "documentUuid": null, - "_key": "!tables.results!I5L1dlgxXTNrCCkL.C70V6prVmZd5VRV8" - }, - { - "type": "text", - "weight": 1, - "range": [ - 10, - 10 - ], - "_id": "i02rh05CvhHlKJCN", - "name": "", - "img": "icons/svg/d12-grey.svg", - "description": "

Harass the opponent to deplete their resources or keep them occupied.

", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "documentUuid": null, - "_key": "!tables.results!I5L1dlgxXTNrCCkL.i02rh05CvhHlKJCN" - }, - { - "type": "text", - "weight": 1, - "range": [ - 11, - 11 - ], - "_id": "AbNgD5GCbWuui9oP", - "name": "", - "img": "icons/svg/d12-grey.svg", - "description": "

Destroy a piece of architecture, a statue, a shrine, or a weapon.

", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "documentUuid": null, - "_key": "!tables.results!I5L1dlgxXTNrCCkL.AbNgD5GCbWuui9oP" - }, - { - "type": "text", - "weight": 1, - "range": [ - 12, - 12 - ], - "_id": "TCrdyh3qhl2vtQxO", - "name": "", - "img": "icons/svg/d12-grey.svg", - "description": "

Investigate a situation to confirm or deny existing information.

", - "drawn": false, - "flags": {}, - "_stats": { - "compendiumSource": null, - "duplicateSource": null, - "exportSource": null, - "coreVersion": "13.351", - "systemId": "daggerheart", - "systemVersion": "1.4.4", - "lastModifiedBy": null - }, - "documentUuid": null, - "_key": "!tables.results!I5L1dlgxXTNrCCkL.TCrdyh3qhl2vtQxO" - } - ], - "replacement": true, - "displayRoll": true, - "folder": null, - "ownership": { - "default": 0, - "Bgvu4A6AMkRFOTGR": 3 - }, - "flags": {}, - "formula": "1d12", - "_id": "I5L1dlgxXTNrCCkL", - "sort": 400000, - "_key": "!tables!I5L1dlgxXTNrCCkL" -} diff --git a/src/packs/subclasses/feature_Conjure_Shield_oirsCnN66GOlK3Fa.json b/src/packs/subclasses/feature_Conjure_Shield_oirsCnN66GOlK3Fa.json index 99b04487..8847b7af 100644 --- a/src/packs/subclasses/feature_Conjure_Shield_oirsCnN66GOlK3Fa.json +++ b/src/packs/subclasses/feature_Conjure_Shield_oirsCnN66GOlK3Fa.json @@ -35,7 +35,7 @@ "key": "system.evasion", "mode": 2, "value": "@system.proficiency", - "priority": 21 + "priority": null } ], "disabled": false, diff --git a/src/packs/subclasses/feature_Elemental_Incarnation_f37TTgCc0Q3Ih1A1.json b/src/packs/subclasses/feature_Elemental_Incarnation_f37TTgCc0Q3Ih1A1.json index 30ffb50b..6dcd5f13 100644 --- a/src/packs/subclasses/feature_Elemental_Incarnation_f37TTgCc0Q3Ih1A1.json +++ b/src/packs/subclasses/feature_Elemental_Incarnation_f37TTgCc0Q3Ih1A1.json @@ -255,13 +255,13 @@ "key": "system.damageThresholds.major", "mode": 2, "value": "@system.proficiency", - "priority": 21 + "priority": null }, { "key": "system.damageThresholds.severe", "mode": 2, "value": "@system.proficiency", - "priority": 21 + "priority": null } ], "disabled": false, diff --git a/styles/less/global/dialog.less b/styles/less/global/dialog.less index a3400700..8c532c2b 100644 --- a/styles/less/global/dialog.less +++ b/styles/less/global/dialog.less @@ -67,35 +67,6 @@ } } - .dialog-selection-container { - display: flex; - gap: 10px; - flex-wrap: wrap; - - .selection-chip { - display: flex; - align-items: center; - border-radius: 5px; - width: fit-content; - gap: 5px; - cursor: pointer; - padding: 5px; - background: light-dark(@dark-blue-10, @golden-10); - color: light-dark(@dark-blue, @golden); - - .label { - font-style: normal; - font-weight: 400; - font-size: var(--font-size-14); - line-height: 17px; - } - - &.selected { - background: light-dark(@dark-blue-40, @golden-40); - } - } - } - .standard-form { font-family: @font-body; } diff --git a/styles/less/global/global.less b/styles/less/global/global.less index 6c63fe7a..6cc63c2a 100644 --- a/styles/less/global/global.less +++ b/styles/less/global/global.less @@ -51,14 +51,3 @@ } } } - -/* TODO: Remove me when this issue is resolved https://github.com/foundryvtt/foundryvtt/issues/13734 */ -body.theme-dark, -.themed.theme-dark { - color-scheme: dark; -} - -body.theme-light, -.themed.theme-light { - color-scheme: light; -} diff --git a/styles/less/global/index.less b/styles/less/global/index.less index 216dc9f4..f51140de 100644 --- a/styles/less/global/index.less +++ b/styles/less/global/index.less @@ -10,6 +10,7 @@ @import './tab-description.less'; @import './tab-features.less'; @import './tab-effects.less'; +@import './tab-settings.less'; @import './item-header.less'; @import './feature-section.less'; @import './inventory-item.less'; diff --git a/styles/less/global/item-header.less b/styles/less/global/item-header.less index f47ca7dc..073762e0 100755 --- a/styles/less/global/item-header.less +++ b/styles/less/global/item-header.less @@ -160,7 +160,7 @@ .item-description { display: flex; flex-direction: column; - gap: 7px; + gap: 10px; } h3 { diff --git a/styles/less/global/tab-settings.less b/styles/less/global/tab-settings.less new file mode 100644 index 00000000..3d5248be --- /dev/null +++ b/styles/less/global/tab-settings.less @@ -0,0 +1,8 @@ +@import '../utils/colors.less'; +@import '../utils/fonts.less'; + +.sheet.daggerheart.dh-style { + .tab.settings { + margin-bottom: 36px; + } +} diff --git a/styles/less/sheets/actions/actions.less b/styles/less/sheets/actions/actions.less deleted file mode 100644 index 07c99491..00000000 --- a/styles/less/sheets/actions/actions.less +++ /dev/null @@ -1,104 +0,0 @@ -.application.daggerheart.dh-style.action-config { - .actor-summon-items { - width: 100%; - display: flex; - flex-direction: column; - gap: 10px; - - .actor-summon-line { - display: flex; - align-items: center; - gap: 5px; - border-radius: 3px; - - .actor-summon-name { - flex: 2; - display: flex; - align-items: center; - gap: 5px; - - img { - height: 40px; - } - } - - .actor-summon-controls { - flex: 1; - display: flex; - align-items: center; - gap: 5px; - - .controls { - display: flex; - gap: 5px; - } - } - } - - .summon-dragger { - display: flex; - align-items: center; - justify-content: center; - box-sizing: border-box; - height: 40px; - margin-top: 10px; - border: 1px dashed light-dark(@dark-blue-50, @beige-50); - border-radius: 3px; - color: light-dark(@dark-blue-50, @beige-50); - } - } - - .trigger-data { - width: 100%; - display: flex; - justify-content: space-between; - gap: 8px; - - .trigger-data-inner { - flex: 1; - display: flex; - flex-direction: column; - - select { - flex: 1; - } - - .select-section { - flex: 1; - display: flex; - gap: 8px; - } - - .programmer-section { - flex: 3; - display: flex; - flex-direction: column; - - .hint-section { - display: flex; - gap: 4px; - - .hint { - flex: 1; - flex-wrap: wrap; - } - } - } - } - - .expand-trigger { - font-size: 18px; - } - } - - .code-mirror-wrapper { - width: 100%; - height: 0; - min-height: 0; - transition: height 0.1s ease-in-out; - - &.revealed { - height: 300px; - } - } -} diff --git a/styles/less/sheets/actors/character/header.less b/styles/less/sheets/actors/character/header.less index 593f1b73..4115fbd5 100644 --- a/styles/less/sheets/actors/character/header.less +++ b/styles/less/sheets/actors/character/header.less @@ -145,11 +145,6 @@ button { flex: 1; - padding: 0 0.375rem; - } - - button[data-action='viewParty'] { - margin-right: 6px; } } diff --git a/styles/less/sheets/index.less b/styles/less/sheets/index.less index 216cda33..1de1b055 100644 --- a/styles/less/sheets/index.less +++ b/styles/less/sheets/index.less @@ -1,5 +1,3 @@ -@import './actions/actions.less'; - @import './actors/actor-sheet-shared.less'; @import './actors/adversary/actions.less'; @@ -39,5 +37,3 @@ @import './items/feature.less'; @import './items/heritage.less'; @import './items/item-sheet-shared.less'; - -@import './actions/actions.less'; diff --git a/styles/less/ui/chat/action.less b/styles/less/ui/chat/action.less index 8d309cfe..817b0acd 100644 --- a/styles/less/ui/chat/action.less +++ b/styles/less/ui/chat/action.less @@ -98,61 +98,6 @@ .description { padding: 8px; - - .summons-header { - font-size: var(--font-size-14); - text-align: center; - display: flex; - align-items: center; - justify-content: center; - - span { - width: 100%; - } - - &:before, - &:after { - content: ' '; - height: 1px; - width: 100%; - } - - &:before { - background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, light-dark(@dark-blue, @golden) 100%); - } - - &:after { - background: linear-gradient(90deg, light-dark(@dark-blue, @golden) 0%, rgba(0, 0, 0, 0) 100%); - } - } - - .summons-container { - display: flex; - flex-direction: column; - gap: 4px; - - .summon-container { - display: flex; - align-items: center; - justify-content: space-between; - - .summon-label-container { - flex: 1; - display: flex; - align-items: center; - gap: 4px; - - img { - height: 32px; - } - - label { - display: flex; - flex-wrap: wrap; - } - } - } - } } .ability-card-footer { diff --git a/styles/less/ui/chat/downtime.less b/styles/less/ui/chat/downtime.less index 2875ea10..5496a2a3 100644 --- a/styles/less/ui/chat/downtime.less +++ b/styles/less/ui/chat/downtime.less @@ -99,28 +99,6 @@ } } - .action-use-button-parent { - width: 100%; - - .action-use-target { - display: flex; - align-items: center; - justify-content: space-between; - gap: 4px; - width: 100%; - padding: 4px 8px 10px 40px; - font-size: var(--font-size-12); - - label { - font-weight: bold; - } - - select { - flex: 1; - } - } - } - .action-use-button { width: -webkit-fill-available; margin: 0 8px; diff --git a/styles/less/ui/index.less b/styles/less/ui/index.less index 25f51d0f..7f9ada25 100644 --- a/styles/less/ui/index.less +++ b/styles/less/ui/index.less @@ -33,5 +33,3 @@ @import './scene-config/scene-config.less'; @import './effects-display/sheet.less'; - -@import './scene-navigation/scene-navigation.less'; diff --git a/styles/less/ui/scene-config/scene-config.less b/styles/less/ui/scene-config/scene-config.less index 664e7526..fb36dd33 100644 --- a/styles/less/ui/scene-config/scene-config.less +++ b/styles/less/ui/scene-config/scene-config.less @@ -37,63 +37,4 @@ .helper-text { font-style: italic; } - - .scene-environments { - display: flex; - flex-direction: column; - gap: 8px; - - .scene-environment { - display: flex; - align-items: center; - gap: 8px; - - .scene-environment-inner { - display: flex; - align-items: center; - gap: 16px; - flex: 1; - - img { - height: 36px; - } - - h5 { - margin: 0; - } - - .tags { - display: flex; - gap: 4px; - padding-bottom: 0; - - .tag { - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - padding: 3px 5px; - font-size: var(--font-size-12); - font: @font-body; - - background: light-dark(@dark-15, @beige-15); - border: 1px solid light-dark(@dark, @beige); - border-radius: 3px; - } - - .label { - display: flex; - flex-direction: row; - justify-content: center; - align-items: center; - font-size: var(--font-size-12); - } - } - } - - .remove-icon { - font-size: 16px; - } - } - } } diff --git a/styles/less/ui/scene-navigation/scene-navigation.less b/styles/less/ui/scene-navigation/scene-navigation.less deleted file mode 100644 index 6b97ddec..00000000 --- a/styles/less/ui/scene-navigation/scene-navigation.less +++ /dev/null @@ -1,36 +0,0 @@ -#ui-left #ui-left-column-2 { - flex: 0 0 230px; - - .scene-navigation { - .scene-wrapper { - display: flex; - gap: 2px; - height: var(--control-size); - width: 100%; - - .scene-environment { - padding: 0; - - img { - border-radius: 4px; - } - } - } - - .scene { - justify-content: center; - align-content: center; - background: var(--control-bg-color); - border: 1px solid var(--control-border-color); - border-radius: 4px; - color: var(--control-icon-color); - pointer-events: all; - transition: - border 0.25s, - color 0.25s; - text-shadow: none; - width: 200px; - max-width: 200px; - } - } -} diff --git a/styles/less/ui/sidebar/tabs.less b/styles/less/ui/sidebar/tabs.less index c620ff91..e9de2924 100644 --- a/styles/less/ui/sidebar/tabs.less +++ b/styles/less/ui/sidebar/tabs.less @@ -1,4 +1,4 @@ -.theme-light#interface #ui-right #sidebar { +.theme-light #interface #ui-right #sidebar { menu li button img { filter: @grey-filter; } diff --git a/styles/less/ux/tooltip/tooltip.less b/styles/less/ux/tooltip/tooltip.less index 0f632772..d9a83d59 100644 --- a/styles/less/ux/tooltip/tooltip.less +++ b/styles/less/ux/tooltip/tooltip.less @@ -11,7 +11,7 @@ aside[role='tooltip']:has(div.daggerheart.dh-style.tooltip.card-style) { width: 18rem; background-image: url('../assets/parchments/dh-parchment-dark.png'); outline: 1px solid light-dark(@dark-80, @beige-80); - box-shadow: 0 0 25px rgba(0, 0, 0, 0.8); + box-shadow: 0 0 25px rgba(0, 0, 0, 0.80); .tooltip-title { font-size: var(--font-size-20); @@ -235,6 +235,7 @@ aside[role='tooltip'].locked-tooltip:has(div.daggerheart.dh-style.tooltip.card-s .theme-light aside[role='tooltip'].locked-tooltip:has(div.daggerheart.dh-style.tooltip) { box-shadow: 0 0 25px @dark-blue-90; outline: 1px solid light-dark(@dark-blue, @golden); + } #tooltip, @@ -343,20 +344,4 @@ aside[role='tooltip'].locked-tooltip:has(div.daggerheart.dh-style.tooltip.card-s margin-bottom: 4px; } } - - .party-list { - display: flex; - flex-direction: column; - button { - width: 100%; - align-items: center; - justify-content: start; - img { - border: none; - width: 1rem; - height: 1rem; - object-fit: contain; - } - } - } } diff --git a/system.json b/system.json index 43f82f06..c58be61d 100644 --- a/system.json +++ b/system.json @@ -2,7 +2,7 @@ "id": "daggerheart", "title": "Daggerheart", "description": "An unofficial implementation of the Daggerheart system", - "version": "1.5.0", + "version": "1.4.0", "compatibility": { "minimum": "13.346", "verified": "13.351", @@ -54,12 +54,6 @@ { "name": "chrisryan10", "discord": "lazjen" - }, - { - "name": "nsalyzyn", - "url": "https://github.com/nsalyzyn", - "email": "nsalyzyn@gmail.com", - "discord": "nsalyzyn" } ], "esmodules": ["build/daggerheart.js"], @@ -173,15 +167,6 @@ "private": false, "flags": {} }, - { - "name": "rolltables", - "label": "Rolltables", - "system": "daggerheart", - "path": "packs/rolltables.db", - "type": "RollTable", - "private": false, - "flags": {} - }, { "name": "beastforms", "label": "Beastforms", @@ -197,7 +182,7 @@ "name": "Daggerheart SRD", "sorting": "m", "color": "#08718c", - "packs": ["adversaries", "environments", "journals", "rolltables"], + "packs": ["adversaries", "environments", "journals"], "folders": [ { "name": "Character Options", diff --git a/templates/actionTypes/summon.hbs b/templates/actionTypes/summon.hbs deleted file mode 100644 index 429977d9..00000000 --- a/templates/actionTypes/summon.hbs +++ /dev/null @@ -1,50 +0,0 @@ -
- - {{localize "DAGGERHEART.ACTIONS.TYPES.summon.name"}} - - -
    - {{#each @root.summons as |summon index|}} -
  • -
    - -

    - {{summon.actor.name}} -

    -
    - -
    -
    -
    - -
    -
    - - -
    -
  • - {{/each}} -
    - {{localize "DAGGERHEART.ACTIONS.Settings.summon.dropSummonsHere"}} -
    -
-
\ No newline at end of file diff --git a/templates/dialogs/dice-roll/damageSelection.hbs b/templates/dialogs/dice-roll/damageSelection.hbs index c0dbae62..ba542666 100644 --- a/templates/dialogs/dice-roll/damageSelection.hbs +++ b/templates/dialogs/dice-roll/damageSelection.hbs @@ -2,20 +2,6 @@

{{title}}

- - {{#if hasSelectedEffects}} -
- {{localize "DAGGERHEART.GENERAL.Effect.plural"}} - - {{#each selectedEffects as |effect id|}} -
- - {{effect.name}} -
- {{/each}} -
- {{/if}} - {{#each @root.formula}}
{{localize "DAGGERHEART.GENERAL.formula"}}: {{roll.formula}} diff --git a/templates/dialogs/dice-roll/rollSelection.hbs b/templates/dialogs/dice-roll/rollSelection.hbs index e60f4683..c7a9b0f9 100644 --- a/templates/dialogs/dice-roll/rollSelection.hbs +++ b/templates/dialogs/dice-roll/rollSelection.hbs @@ -70,19 +70,6 @@ {{/if}}
- {{#if hasSelectedEffects}} -
- {{localize "DAGGERHEART.GENERAL.Effect.plural"}} - - {{#each selectedEffects as |effect id|}} -
- - {{effect.name}} -
- {{/each}} -
- {{/if}} - {{#if experiences.length}}
{{localize "DAGGERHEART.GENERAL.experience.plural"}} diff --git a/templates/scene/dh-config.hbs b/templates/scene/dh-config.hbs index 017613ee..1f7dcd81 100644 --- a/templates/scene/dh-config.hbs +++ b/templates/scene/dh-config.hbs @@ -21,39 +21,4 @@ {{localize "DAGGERHEART.SETTINGS.Scene.disabledText"}} {{/if}}
- -
- - {{localize "DAGGERHEART.SETTINGS.Scene.sceneEnvironments"}} - - -
- {{#each data.sceneEnvironments as |environment index|}} -
- {{#if environment}} -
- -
{{environment.name}}
-
-
- - {{localize (concat 'DAGGERHEART.GENERAL.Tiers.' environment.system.tier)}} - -
- {{#if environment.system.type}} -
- - {{localize (concat 'DAGGERHEART.CONFIG.EnvironmentType.' environment.system.type '.label')}} - -
- {{/if}} -
-
- {{/if}} - -
- {{/each}} - {{localize "DAGGERHEART.SETTINGS.Scene.dragEnvironmentHere"}} -
-
\ No newline at end of file diff --git a/templates/settings/automation-settings/roll.hbs b/templates/settings/automation-settings/roll.hbs index dc65f8ae..5769bf61 100644 --- a/templates/settings/automation-settings/roll.hbs +++ b/templates/settings/automation-settings/roll.hbs @@ -19,15 +19,4 @@ {{/each}} - -
- - {{localize "DAGGERHEART.SETTINGS.Automation.trigger.title"}} - - -
- {{formGroup settingFields.schema.fields.triggers.fields.enabled value=settingFields.triggers.enabled localize=true}} -

{{localize "DAGGERHEART.SETTINGS.Automation.FIELDS.triggers.enabled.hint"}}

-
-
\ No newline at end of file diff --git a/templates/settings/homebrew-settings/domains.hbs b/templates/settings/homebrew-settings/domains.hbs index b65422c5..0946f211 100644 --- a/templates/settings/homebrew-settings/domains.hbs +++ b/templates/settings/homebrew-settings/domains.hbs @@ -1,5 +1,5 @@
diff --git a/templates/settings/homebrew-settings/downtime.hbs b/templates/settings/homebrew-settings/downtime.hbs index 8612f3d5..890afddc 100644 --- a/templates/settings/homebrew-settings/downtime.hbs +++ b/templates/settings/homebrew-settings/downtime.hbs @@ -1,5 +1,5 @@
diff --git a/templates/settings/homebrew-settings/itemFeatures.hbs b/templates/settings/homebrew-settings/itemFeatures.hbs index 22c23af6..1f8595de 100644 --- a/templates/settings/homebrew-settings/itemFeatures.hbs +++ b/templates/settings/homebrew-settings/itemFeatures.hbs @@ -1,5 +1,5 @@
diff --git a/templates/settings/homebrew-settings/settings.hbs b/templates/settings/homebrew-settings/settings.hbs index cdcbd461..5da053f4 100644 --- a/templates/settings/homebrew-settings/settings.hbs +++ b/templates/settings/homebrew-settings/settings.hbs @@ -1,5 +1,5 @@
diff --git a/templates/settings/homebrew-settings/types.hbs b/templates/settings/homebrew-settings/types.hbs index bdb92ba0..f9d3bba3 100644 --- a/templates/settings/homebrew-settings/types.hbs +++ b/templates/settings/homebrew-settings/types.hbs @@ -1,5 +1,5 @@
diff --git a/templates/sheets-settings/action-settings/configuration.hbs b/templates/sheets-settings/action-settings/configuration.hbs index 5bd29e39..51b2a72b 100644 --- a/templates/sheets-settings/action-settings/configuration.hbs +++ b/templates/sheets-settings/action-settings/configuration.hbs @@ -2,7 +2,7 @@ class="tab {{this.tabs.config.cssClass}}" data-group="primary" data-tab="config" -> +> {{> 'systems/daggerheart/templates/actionTypes/uses.hbs' fields=fields.uses.fields source=source.uses}} {{> 'systems/daggerheart/templates/actionTypes/cost.hbs' fields=fields.cost.element.fields source=source.cost costOptions=costOptions}} {{> 'systems/daggerheart/templates/actionTypes/range-target.hbs' fields=(object range=fields.range target=fields.target.fields) source=(object target=source.target range=source.range)}} diff --git a/templates/sheets-settings/action-settings/effect.hbs b/templates/sheets-settings/action-settings/effect.hbs index e94f4328..bf2f3aa1 100644 --- a/templates/sheets-settings/action-settings/effect.hbs +++ b/templates/sheets-settings/action-settings/effect.hbs @@ -9,6 +9,5 @@ {{#if fields.macro}}{{> 'systems/daggerheart/templates/actionTypes/macro.hbs' fields=fields.macro source=source.macro}}{{/if}} {{#if fields.effects}}{{> 'systems/daggerheart/templates/actionTypes/effect.hbs' fields=fields.effects.element.fields source=source.effects}}{{/if}} {{#if fields.beastform}}{{> 'systems/daggerheart/templates/actionTypes/beastform.hbs' fields=fields.beastform.fields source=source.beastform}}{{/if}} - {{#if fields.summon}}{{> 'systems/daggerheart/templates/actionTypes/summon.hbs' fields=fields.summon.element.fields source=source.summon}}{{/if}} {{#if fields.countdown}}{{> 'systems/daggerheart/templates/actionTypes/countdown.hbs' fields=fields.countdown.element.fields source=source.countdown}}{{/if}}
\ No newline at end of file diff --git a/templates/sheets-settings/action-settings/trigger.hbs b/templates/sheets-settings/action-settings/trigger.hbs deleted file mode 100644 index b048461e..00000000 --- a/templates/sheets-settings/action-settings/trigger.hbs +++ /dev/null @@ -1,37 +0,0 @@ -
- - - {{#each @root.triggers as |trigger index|}} -
- - -
-
-
- {{formGroup @root.fields.triggers.element.fields.trigger value=trigger.trigger name=(concat "triggers." index ".trigger") blank=false localize=true}} - {{#if trigger.usesActor}}{{formGroup @root.fields.triggers.element.fields.triggeringActorType value=trigger.triggeringActorType name=(concat "triggers." index ".triggeringActorType") blank=false localize=true}}{{/if}} -
-
-
- {{localize "Context: "}} - {{localize trigger.hint}} -
-
- {{localize "Return: "}} - {{localize trigger.returns}} -
-
-
- -
- -
- {{formInput @root.fields.triggers.element.fields.command value=trigger.command elementType="code-mirror" name=(concat "triggers." index ".command") aria=(object label=(localize "Test")) }} -
-
- {{/each}} -
\ No newline at end of file diff --git a/templates/sheets-settings/adversary-settings/features.hbs b/templates/sheets-settings/adversary-settings/features.hbs index 2f2f5f47..bc6a1ddf 100644 --- a/templates/sheets-settings/adversary-settings/features.hbs +++ b/templates/sheets-settings/adversary-settings/features.hbs @@ -9,7 +9,7 @@
{{localize tabs.features.label}}
    - {{#each @root.features as |feature|}} + {{#each document.system.features as |feature|}}
  • diff --git a/templates/sheets-settings/environment-settings/features.hbs b/templates/sheets-settings/environment-settings/features.hbs index 579fe74e..13a76f06 100644 --- a/templates/sheets-settings/environment-settings/features.hbs +++ b/templates/sheets-settings/environment-settings/features.hbs @@ -9,7 +9,7 @@
    {{localize tabs.features.label}}
      - {{#each @root.features as |feature|}} + {{#each document.system.features as |feature|}}
    • diff --git a/templates/sheets-settings/token-config/appearance.hbs b/templates/sheets-settings/token-config/appearance.hbs index abdb49c2..0f6019ba 100644 --- a/templates/sheets-settings/token-config/appearance.hbs +++ b/templates/sheets-settings/token-config/appearance.hbs @@ -10,31 +10,22 @@
      {{/if}} -
      - {{localize "Token Size"}} - {{#if usesActorSize}} -
      - - -
      - {{/if}} -
      - -
      - - {{formInput fields.width value=source.width id=(concat rootId "-width") disabled=actorSizeDisable}} - - {{formInput fields.height value=source.height id=(concat rootId "-height") disabled=actorSizeDisable}} -
      +
      + +
      + + {{formInput fields.width value=source.width id=(concat rootId "-width") disabled=actorSizeUsed}} + + {{formInput fields.height value=source.height id=(concat rootId "-height") disabled=actorSizeUsed}}
      -
      +
    {{#if shapes}} {{formGroup fields.shape value=source.shape choices=shapes classes="slim" rootId=rootId}} diff --git a/templates/sheets/actors/adversary/features.hbs b/templates/sheets/actors/adversary/features.hbs index a24342fc..98c1cedb 100644 --- a/templates/sheets/actors/adversary/features.hbs +++ b/templates/sheets/actors/adversary/features.hbs @@ -4,7 +4,7 @@ {{> 'daggerheart.inventory-items' title=tabs.features.label type='feature' - collection=@root.features + collection=document.system.features hideContextMenu=true canCreate=true showActions=true diff --git a/templates/sheets/actors/character/header.hbs b/templates/sheets/actors/character/header.hbs index 1459e10b..be3557ff 100644 --- a/templates/sheets/actors/character/header.hbs +++ b/templates/sheets/actors/character/header.hbs @@ -87,16 +87,11 @@ {{/if}}
    - {{#if document.parties.size}} - - {{/if}} - -
    diff --git a/templates/sheets/actors/environment/features.hbs b/templates/sheets/actors/environment/features.hbs index 3ad36023..4c0e3519 100644 --- a/templates/sheets/actors/environment/features.hbs +++ b/templates/sheets/actors/environment/features.hbs @@ -7,7 +7,7 @@ {{> 'daggerheart.inventory-items' title=tabs.features.label type='feature' - collection=@root.features + collection=document.system.features hideContextMenu=true canCreate=true showActions=true diff --git a/templates/sheets/items/armor/description.hbs b/templates/sheets/items/armor/description.hbs deleted file mode 100644 index c5a9924e..00000000 --- a/templates/sheets/items/armor/description.hbs +++ /dev/null @@ -1,5 +0,0 @@ -
    - {{#each features as | feature |}} -
    {{localize feature.label}}: {{{localize feature.description}}}
    - {{/each}} -
    \ No newline at end of file diff --git a/templates/sheets/items/beastform/settings.hbs b/templates/sheets/items/beastform/settings.hbs index 82065ad3..844b9d61 100644 --- a/templates/sheets/items/beastform/settings.hbs +++ b/templates/sheets/items/beastform/settings.hbs @@ -47,9 +47,6 @@ disabled=dimensionsDisabled }} -
    - {{formGroup systemFields.tokenSize.fields.scale value=source.system.tokenSize.scale localize=true }} -
    {{else}} {{localize "DAGGERHEART.ITEMS.Beastform.evolvedTokenHint"}} {{/unless}} diff --git a/templates/sheets/items/domainCard/settings.hbs b/templates/sheets/items/domainCard/settings.hbs index f5781606..f3d05a2a 100644 --- a/templates/sheets/items/domainCard/settings.hbs +++ b/templates/sheets/items/domainCard/settings.hbs @@ -14,12 +14,6 @@ {{formField systemFields.level value=source.system.level data-dtype="Number"}} {{localize "DAGGERHEART.ITEMS.DomainCard.recallCost"}} {{formField systemFields.recallCost value=source.system.recallCost data-dtype="Number"}} - {{localize "DAGGERHEART.ITEMS.DomainCard.vaultActive"}} - {{formField systemFields.vaultActive value=source.system.vaultActive}} - {{localize "DAGGERHEART.ITEMS.DomainCard.loadoutIgnore"}} - {{formField systemFields.loadoutIgnore value=source.system.loadoutIgnore}} - {{localize "DAGGERHEART.ITEMS.DomainCard.domainTouched"}} - {{formField systemFields.domainTouched value=source.system.domainTouched placeholder=0 }}
{{> "systems/daggerheart/templates/sheets/global/partials/resource-section/resource-section.hbs" }} diff --git a/templates/sheets/items/feature/settings.hbs b/templates/sheets/items/feature/settings.hbs index e62db8e3..63aa9502 100644 --- a/templates/sheets/items/feature/settings.hbs +++ b/templates/sheets/items/feature/settings.hbs @@ -3,10 +3,12 @@ data-tab='{{tabs.settings.id}}' data-group='{{tabs.settings.group}}' > -
- {{localize "DAGGERHEART.GENERAL.general"}} - {{localize "DAGGERHEART.CONFIG.FeatureForm.label"}} - {{formInput document.system.schema.fields.featureForm value=document.system.featureForm choices=featureFormChoices localize=true}} -
+ {{#if (or (eq document.parent.type "adversary") (eq document.parent.type "environment"))}} +
+ {{localize "DAGGERHEART.GENERAL.general"}} + {{localize "DAGGERHEART.CONFIG.FeatureForm.label"}} + {{formInput document.system.schema.fields.featureForm value=document.system.featureForm choices=featureFormChoices localize=true}} +
+ {{/if}} {{> "systems/daggerheart/templates/sheets/global/partials/resource-section/resource-section.hbs" }}
\ No newline at end of file diff --git a/templates/sheets/items/weapon/description.hbs b/templates/sheets/items/weapon/description.hbs deleted file mode 100644 index c5a9924e..00000000 --- a/templates/sheets/items/weapon/description.hbs +++ /dev/null @@ -1,5 +0,0 @@ -
- {{#each features as | feature |}} -
{{localize feature.label}}: {{{localize feature.description}}}
- {{/each}} -
\ No newline at end of file diff --git a/templates/ui/chat/action.hbs b/templates/ui/chat/action.hbs index 65bb0762..6b505164 100644 --- a/templates/ui/chat/action.hbs +++ b/templates/ui/chat/action.hbs @@ -8,22 +8,6 @@ -
- {{{description}}} - {{#if action.summon}} -
{{localize "DAGGERHEART.GENERAL.summon.plural"}}
-
- {{#each action.summon}} -
-
- - -
- # {{this.rolledCount}} -
- {{/each}} -
- {{/if}} -
+
{{{description}}}
\ No newline at end of file diff --git a/templates/ui/chat/downtime.hbs b/templates/ui/chat/downtime.hbs index 373724dc..ef1f44c4 100644 --- a/templates/ui/chat/downtime.hbs +++ b/templates/ui/chat/downtime.hbs @@ -15,22 +15,9 @@ {{#each move.actions as | action index |}} -
- - {{#if move.needsTarget}} -
- - -
- {{/if}} -
+ {{/each}} {{/each}} diff --git a/templates/ui/itemBrowser/itemContainer.hbs b/templates/ui/itemBrowser/itemContainer.hbs index 0040a692..f6aefa6b 100644 --- a/templates/ui/itemBrowser/itemContainer.hbs +++ b/templates/ui/itemBrowser/itemContainer.hbs @@ -10,7 +10,7 @@
- {{{system.enrichedDescription}}} + {{{system.description}}}
{{/each}} \ No newline at end of file diff --git a/templates/ui/sceneNavigation/scene-navigation.hbs b/templates/ui/sceneNavigation/scene-navigation.hbs deleted file mode 100644 index 41e9e3e8..00000000 --- a/templates/ui/sceneNavigation/scene-navigation.hbs +++ /dev/null @@ -1,36 +0,0 @@ -