Merged with main

This commit is contained in:
WBHarry 2025-08-08 00:44:08 +02:00
commit 9b9632cf94
150 changed files with 1077 additions and 930 deletions

View file

@ -15,7 +15,7 @@ jobs:
- name: Build Packs
run: |
npm run pullYMLtoLDB
npm run pullYMLtoLDBBuild
mv --force src/packs/LICENSE packs/LICENSE
- name: Build daggerheart.js

View file

@ -259,7 +259,10 @@ Hooks.on('moveToken', async (movedToken, data) => {
const rangeDependantEffects = movedToken.actor.effects.filter(effect => effect.system.rangeDependence?.enabled);
const updateEffects = async (disposition, token, effects, effectUpdates) => {
const rangeMeasurement = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.RangeMeasurement);
const rangeMeasurement = game.settings.get(
CONFIG.DH.id,
CONFIG.DH.SETTINGS.gameSettings.variantRules
).rangeMeasurement;
for (let effect of effects.filter(x => x.system.rangeDependence?.enabled)) {
const { target, range, type } = effect.system.rangeDependence;

View file

@ -188,7 +188,8 @@
"manualMulticlassSubclass": {
"title": "Multiclass Subclass",
"text": "Do you want to add this subclass as your multiclass subclass?"
}
},
"cannotRemoveCoreExperience": "You are using Levelup Auto. You cannot remove an experience given to you by the rule progression."
},
"Companion": {
"FIELDS": {
@ -880,7 +881,7 @@
"Gold": {
"title": "Gold",
"coins": "Coins",
"handfulls": "Handfulls",
"handfuls": "Handfuls",
"bags": "Bags",
"chests": "Chests"
},
@ -2115,6 +2116,10 @@
"resourceScrollTexts": {
"label": "Show Resource Change Scrolltexts",
"hint": "When a character is damaged, uses armor etc, a scrolling text will briefly appear by the token to signify this."
},
"playerCanEditSheet": {
"label": "Players Can Manually Edit Character Settings",
"hint": "Players are allowed to access the manual Character Settings and change their statistics beyond the rules."
}
}
},
@ -2138,7 +2143,7 @@
"title": "Currency Overrides",
"currencyName": "Currency Name",
"coinName": "Coin Name",
"handfullName": "Handfull Name",
"handfulName": "Handful Name",
"bagName": "Bag Name",
"chestName": "Chest Name"
},
@ -2210,10 +2215,6 @@
"actionTokens": {
"enabled": { "label": "Enabled" },
"tokens": { "label": "Tokens" }
},
"useCoins": {
"label": "Use Coins",
"hint": "test"
}
}
},
@ -2362,7 +2363,8 @@
"openSheetSettings": "Open Settings",
"compendiumBrowser": "Compendium Browser",
"rulesOn": "Rules On",
"rulesOff": "Rules Off"
"rulesOff": "Rules Off",
"remainingUses": "Uses refresh on {type}"
}
}
}

View file

@ -1,7 +1,7 @@
import { abilities } from '../../config/actorConfig.mjs';
import { burden } from '../../config/generalConfig.mjs';
import { createEmbeddedItemWithEffects } from '../../helpers/utils.mjs';
import { ItemBrowser } from '../ui/itemBrowser.mjs';
import { createEmbeddedItemsWithEffects, createEmbeddedItemWithEffects } from '../../helpers/utils.mjs';
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
@ -21,8 +21,8 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
class: this.character.system.class?.value ?? {},
subclass: this.character.system.class?.subclass ?? {},
experiences: {
[foundry.utils.randomID()]: { name: '', value: 2 },
[foundry.utils.randomID()]: { name: '', value: 2 }
[foundry.utils.randomID()]: { name: '', value: 2, core: true },
[foundry.utils.randomID()]: { name: '', value: 2, core: true }
},
domainCards: {
[foundry.utils.randomID()]: {},
@ -373,13 +373,18 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
);
context.armor = {
...this.equipment.armor,
suggestion: { ...suggestions.armor, taken: suggestions.armor?.uuid === this.equipment.armor?.uuid },
suggestion: {
...suggestions.armor,
uuid: suggestions.armor?.uuid,
taken: suggestions.armor?.uuid === this.equipment.armor?.uuid
},
compendium: 'armors'
};
context.primaryWeapon = {
...this.equipment.primaryWeapon,
suggestion: {
...suggestions.primaryWeapon,
uuid: suggestions.primaryWeapon?.uuid,
taken: suggestions.primaryWeapon?.uuid === this.equipment.primaryWeapon?.uuid
},
compendium: 'weapons'
@ -388,6 +393,7 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
...this.equipment.secondaryWeapon,
suggestion: {
...suggestions.secondaryWeapon,
uuid: suggestions.secondaryWeapon?.uuid,
taken: suggestions.secondaryWeapon?.uuid === this.equipment.secondaryWeapon?.uuid
},
disabled: this.equipment.primaryWeapon?.system?.burden === burden.twoHanded.value,
@ -492,20 +498,20 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
const type = target.dataset.compendium ?? target.dataset.type;
const presets = {
compendium: "daggerheart",
compendium: 'daggerheart',
folder: type,
render: {
noFolder: true
}
};
if(type == "domains")
if (type == 'domains')
presets.filter = {
'level.max': { key: 'level.max', value: 1 },
'system.domain': { key: 'system.domain', value: this.setup.class?.system.domains ?? null },
'system.domain': { key: 'system.domain', value: this.setup.class?.system.domains ?? null }
};
return this.itemBrowser = await new ItemBrowser({ presets }).render({ force: true });
return (this.itemBrowser = await new ItemBrowser({ presets }).render({ force: true }));
}
static async viewItem(_, target) {
@ -574,13 +580,7 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
await createEmbeddedItemWithEffects(this.character, this.setup.community);
await createEmbeddedItemWithEffects(this.character, this.setup.class);
await createEmbeddedItemWithEffects(this.character, this.setup.subclass);
await this.character.createEmbeddedDocuments(
'Item',
Object.values(this.setup.domainCards).map(x => ({
...x,
effects: x.effects?.map(effect => effect.toObject())
}))
);
await createEmbeddedItemsWithEffects(this.character, Object.values(this.setup.domainCards));
if (this.equipment.armor.uuid)
await createEmbeddedItemWithEffects(this.character, this.equipment.armor, {
@ -602,22 +602,26 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
if (this.equipment.inventory.choiceB.uuid)
await createEmbeddedItemWithEffects(this.character, this.equipment.inventory.choiceB);
await this.character.createEmbeddedDocuments(
'Item',
this.setup.class.system.inventory.take
.filter(x => x)
.map(x => ({
...x,
effects: x.effects?.map(effect => effect.toObject())
}))
await createEmbeddedItemsWithEffects(
this.character,
this.setup.class.system.inventory.take.filter(x => x)
);
await this.character.update({
await this.character.update(
{
system: {
traits: this.setup.traits,
experiences: this.setup.experiences
experiences: {
...this.setup.experiences,
...Object.keys(this.character.system.experiences).reduce((acc, key) => {
acc[`-=${key}`] = null;
return acc;
}, {})
}
});
}
},
{ overwrite: true }
);
if (this.itemBrowser) this.itemBrowser.close();
this.close();
@ -710,6 +714,10 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
return;
}
if (item.system.burden === CONFIG.DH.GENERAL.burden.twoHanded.value) {
this.equipment.secondaryWeapon = {};
}
this.equipment.primaryWeapon = { ...item, uuid: item.uuid };
} else if (item.type === 'weapon' && event.target.closest('.secondary-weapon-card')) {
if (this.equipment.primaryWeapon?.system?.burden === burden.twoHanded.value) {

View file

@ -2,7 +2,6 @@ export { default as BeastformDialog } from './beastformDialog.mjs';
export { default as d20RollDialog } from './d20RollDialog.mjs';
export { default as DamageDialog } from './damageDialog.mjs';
export { default as DamageReductionDialog } from './damageReductionDialog.mjs';
export { default as DamageSelectionDialog } from './damageSelectionDialog.mjs';
export { default as DeathMove } from './deathMove.mjs';
export { default as Downtime } from './downtime.mjs';
export { default as MulticlassChoiceDialog } from './multiclassChoiceDialog.mjs';

View file

@ -38,7 +38,9 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
};
get title() {
return game.i18n.localize(`DAGGERHEART.EFFECTS.ApplyLocations.${this.config.isHealing ? 'healing' : 'damage'}Roll.name`);
return game.i18n.localize(
`DAGGERHEART.EFFECTS.ApplyLocations.${this.config.hasHealing ? 'healing' : 'damage'}Roll.name`
);
}
async _prepareContext(_options) {
@ -46,7 +48,7 @@ export default class DamageDialog extends HandlebarsApplicationMixin(Application
context.config = CONFIG.DH;
context.title = this.config.title ?? this.title;
context.formula = this.roll.constructFormula(this.config);
context.isHealing = this.config.isHealing;
context.hasHealing = this.config.hasHealing;
context.directDamage = this.config.directDamage;
context.selectedRollMode = this.config.selectedRollMode;
context.rollModes = Object.entries(CONFIG.Dice.rollModes).map(([action, { label, icon }]) => ({

View file

@ -1,128 +0,0 @@
// TO DELETE ?
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
export default class DamageSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(rollString, bonusDamage, resolve, hope = 0) {
super({});
this.data = {
rollString,
bonusDamage: bonusDamage.reduce((acc, x) => {
if (x.appliesOn === CONFIG.DH.EFFECTS.applyLocations.damageRoll.id) {
acc.push({
...x,
hopeUses: 0
});
}
return acc;
}, []),
hope
};
this.resolve = resolve;
}
static DEFAULT_OPTIONS = {
tag: 'form',
classes: ['daggerheart', 'dialog', 'dh-style', 'views', 'damage-selection'],
position: {
width: 400,
height: 'auto'
},
actions: {
decreaseHopeUse: this.decreaseHopeUse,
increaseHopeUse: this.increaseHopeUse,
rollDamage: this.rollDamage
},
form: {
handler: this.updateSelection,
submitOnChange: true,
closeOnSubmit: false
}
};
/** @override */
static PARTS = {
damageSelection: {
id: 'damageSelection',
template: 'systems/daggerheart/templates/dialogs/dice-roll/damageSelection.hbs'
}
};
/* -------------------------------------------- */
/** @inheritDoc */
get title() {
return `Damage Options`;
}
async _prepareContext(_options) {
return {
rollString: this.getRollString(),
bonusDamage: this.data.bonusDamage,
hope: this.data.hope + 1,
hopeUsed: this.getHopeUsed()
};
}
static updateSelection(event, _, formData) {
const { bonusDamage, ...rest } = foundry.utils.expandObject(formData.object);
for (var index in bonusDamage) {
this.data.bonusDamage[index].initiallySelected = bonusDamage[index].initiallySelected;
if (bonusDamage[index].hopeUses) {
const value = Number.parseInt(bonusDamage[index].hopeUses);
if (!Number.isNaN(value)) this.data.bonusDamage[index].hopeUses = value;
}
}
this.data = foundry.utils.mergeObject(this.data, rest);
this.render(true);
}
getRollString() {
return this.data.rollString.concat(
this.data.bonusDamage.reduce((acc, x) => {
if (x.initiallySelected) {
const nr = 1 + x.hopeUses;
const baseDamage = x.value;
return acc.concat(` + ${nr}${baseDamage}`);
}
return acc;
}, '')
);
}
getHopeUsed() {
return this.data.bonusDamage.reduce((acc, x) => acc + x.hopeUses, 0);
}
static decreaseHopeUse(_, button) {
const index = Number.parseInt(button.dataset.index);
if (this.data.bonusDamage[index].hopeUses - 1 >= 0) {
this.data.bonusDamage[index].hopeUses -= 1;
this.render(true);
}
}
static increaseHopeUse(_, button) {
const index = Number.parseInt(button.dataset.index);
if (this.data.bonusDamage[index].hopeUses <= this.data.hope + 1) {
this.data.bonusDamage[index].hopeUses += 1;
this.render(true);
}
}
static rollDamage(event) {
event.preventDefault();
this.resolve({
rollString: this.getRollString(),
bonusDamage: this.data.bonusDamage,
hopeUsed: this.getHopeUsed()
});
this.close();
}
}

View file

@ -94,7 +94,10 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
const actionItems = this.actor.items.reduce((acc, x) => {
if (x.system.actions) {
const recoverable = x.system.actions.reduce((acc, action) => {
if (action.uses.recovery && (action.uses.recovery === 'shortRest') === this.shortrest) {
if (
(action.uses.recovery && (action.uses.recovery === 'longRest') === !this.shortrest) ||
action.uses.recovery === 'shortRest'
) {
acc.push({
title: x.name,
name: action.name,
@ -116,7 +119,8 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
if (
x.system.resource &&
x.system.resource.type &&
(x.system.resource.recovery === 'shortRest') === this.shortrest
((x.system.resource.recovery === 'longRest') === !this.shortrest ||
x.system.resource.recovery === 'shortRest')
) {
acc.push({
title: game.i18n.localize(`TYPES.Item.${x.type}`),
@ -226,14 +230,18 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV
) {
for (var data of this.refreshables.actionItems) {
const action = await foundry.utils.fromUuid(data.uuid);
await action.parent.parent.update({ [`system.actions.${action.id}.uses.value`]: action.uses.max ?? 1 });
await action.parent.parent.update({ [`system.actions.${action.id}.uses.value`]: 0 });
}
for (var data of this.refreshables.resourceItems) {
const feature = await foundry.utils.fromUuid(data.uuid);
const increasing =
feature.system.resource.progression === CONFIG.DH.ITEM.itemResourceProgression.increasing.id;
const resetValue = increasing ? 0 : (feature.system.resource.max ?? 0);
const resetValue = increasing
? 0
: feature.system.resource.max
? Roll.replaceFormulaData(feature.system.resource.max, this.actor)
: 0;
await feature.update({ 'system.resource.value': resetValue });
}

View file

@ -289,7 +289,7 @@ export default class DhCharacterLevelUp extends LevelUpBase {
const experience = Object.keys(this.actor.system.experiences).find(
x => x === data
);
return this.actor.system.experiences[experience]?.description ?? '';
return this.actor.system.experiences[experience]?.name ?? '';
});
advancement[choiceKey].push({ data: data, value: checkbox.value });
break;

View file

@ -47,10 +47,12 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
static PARTS = {
tabs: { template: 'systems/daggerheart/templates/levelup/tabs/tab-navigation.hbs' },
advancements: { template: 'systems/daggerheart/templates/levelup/tabs/advancements.hbs' },
advancements: {
template: 'systems/daggerheart/templates/levelup/tabs/advancements.hbs'
},
selections: {
template: 'systems/daggerheart/templates/levelup/tabs/selections.hbs',
scrollable: ['.selections']
scrollable: ['.levelup-selections-container']
},
summary: { template: 'systems/daggerheart/templates/levelup/tabs/summary.hbs' },
footer: { template: 'systems/daggerheart/templates/levelup/tabs/footer.hbs' }
@ -538,27 +540,26 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
const type = target.dataset.compendium ?? target.dataset.type;
const presets = {
compendium: "daggerheart",
compendium: 'daggerheart',
folder: type,
render: {
noFolder: true
}
};
if(type == "domains") {
if (type == 'domains') {
const domains = this.actor.system.domains,
multiclassDomain = this.levelup.classUpgradeChoices?.multiclass?.domain;
if (multiclassDomain) {
if (!domains.includes(x => x === multiclassDomain))
domains.push(multiclassDomain);
if (!domains.includes(x => x === multiclassDomain)) domains.push(multiclassDomain);
}
presets.filter = {
'level.max': { key: 'level.max', value: this.levelup.currentLevel },
'system.domain': { key: 'system.domain', value: domains },
'system.domain': { key: 'system.domain', value: domains }
};
}
return this.itemBrowser = await new ItemBrowser({ presets }).render({ force: true });
return (this.itemBrowser = await new ItemBrowser({ presets }).render({ force: true }));
}
static async selectPreview(_, button) {

View file

@ -1,5 +1,4 @@
export { default as DhAppearanceSettings } from './appearanceSettings.mjs';
export { default as DhAutomationSettings } from './automationSettings.mjs';
export { default as DhHomebrewSettings } from './homebrewSettings.mjs';
export { default as DhRangeMeasurementSettings } from './rangeMeasurementSettings.mjs';
export { default as DhVariantRuleSettings } from './variantRuleSettings.mjs';

View file

@ -1,66 +0,0 @@
import { DhRangeMeasurement } from '../../data/settings/_module.mjs';
const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
export default class DhRangeMeasurementSettings extends HandlebarsApplicationMixin(ApplicationV2) {
constructor() {
super({});
this.settings = new DhRangeMeasurement(
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.RangeMeasurement).toObject()
);
}
get title() {
return game.i18n.localize('DAGGERHEART.SETTINGS.Menu.title');
}
static DEFAULT_OPTIONS = {
tag: 'form',
id: 'daggerheart-automation-settings',
classes: ['daggerheart', 'dialog', 'dh-style', 'setting'],
position: { width: '600', height: 'auto' },
window: {
icon: 'fa-solid fa-gears'
},
actions: {
reset: this.reset,
save: this.save
},
form: { handler: this.updateData, submitOnChange: true }
};
static PARTS = {
main: {
template: 'systems/daggerheart/templates/settings/range-measurement-settings.hbs'
}
};
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
context.settingFields = this.settings;
return context;
}
static async updateData(event, element, formData) {
const updatedSettings = foundry.utils.expandObject(formData.object);
await this.settings.updateSource(updatedSettings);
this.render();
}
static async reset() {
this.settings = new DhRangeMeasurement();
this.render();
}
static async save() {
await game.settings.set(
CONFIG.DH.id,
CONFIG.DH.SETTINGS.gameSettings.RangeMeasurement,
this.settings.toObject()
);
this.close();
}
}

View file

@ -27,7 +27,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
toggleResourceDice: CharacterSheet.#toggleResourceDice,
handleResourceDice: CharacterSheet.#handleResourceDice,
useDowntime: this.useDowntime,
tempBrowser: CharacterSheet.#tempBrowser,
tempBrowser: CharacterSheet.#tempBrowser
},
window: {
resizable: true
@ -158,7 +158,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
currency: {
title: game.i18n.localize('DAGGERHEART.CONFIG.Gold.title'),
coins: game.i18n.localize('DAGGERHEART.CONFIG.Gold.coins'),
handfulls: game.i18n.localize('DAGGERHEART.CONFIG.Gold.handfulls'),
handfuls: game.i18n.localize('DAGGERHEART.CONFIG.Gold.handfuls'),
bags: game.i18n.localize('DAGGERHEART.CONFIG.Gold.bags'),
chests: game.i18n.localize('DAGGERHEART.CONFIG.Gold.chests')
}
@ -180,6 +180,13 @@ export default class CharacterSheet extends DHBaseActorSheet {
async _preparePartContext(partId, context, options) {
context = await super._preparePartContext(partId, context, options);
switch (partId) {
case 'header':
const { playerCanEditSheet, levelupAuto } = game.settings.get(
CONFIG.DH.id,
CONFIG.DH.SETTINGS.gameSettings.Automation
);
context.showSettings = game.user.isGM || !levelupAuto || (levelupAuto && playerCanEditSheet);
break;
case 'loadout':
await this._prepareLoadoutContext(context, options);
break;
@ -190,6 +197,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
await this._prepareBiographyContext(context, options);
break;
}
return context;
}
@ -596,7 +604,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
const { key } = button.dataset;
const presets = {
compendium: "daggerheart",
compendium: 'daggerheart',
folder: key,
render: {
noFolder: true

View file

@ -7,7 +7,11 @@ export default class DhpEnvironment extends DHBaseActorSheet {
static DEFAULT_OPTIONS = {
classes: ['environment'],
position: {
width: 500
width: 500,
height: 725
},
window: {
resizable: true
},
actions: {},
dragDrop: [{ dragSelector: '.action-section .inventory-item', dropSelector: null }]

View file

@ -85,7 +85,7 @@ export default function DHApplicationMixin(Base) {
toggleEffect: DHSheetV2.#toggleEffect,
toggleExtended: DHSheetV2.#toggleExtended,
addNewItem: DHSheetV2.#addNewItem,
browseItem: DHSheetV2.#browseItem,
browseItem: DHSheetV2.#browseItem
},
contextMenus: [
{
@ -326,6 +326,20 @@ export default function DHApplicationMixin(Base) {
];
if (usable)
options.unshift({
name: 'DAGGERHEART.GENERAL.damage',
icon: 'fa-solid fa-explosion',
condition: target => {
const doc = getDocFromElementSync(target);
return doc?.system?.attack?.damage.parts.length || doc?.damage?.parts.length;
},
callback: async (target, event) => {
const doc = await getDocFromElement(target),
action = doc?.system?.attack ?? doc;
return action && action.use(event, { byPassRoll: true });
}
});
options.unshift({
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.useItem',
icon: 'fa-solid fa-burst',
@ -337,7 +351,7 @@ export default function DHApplicationMixin(Base) {
});
if (toChat)
options.unshift({
options.push({
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat',
icon: 'fa-solid fa-message',
callback: async target => (await getDocFromElement(target)).toChat(this.document.id)
@ -419,25 +433,22 @@ export default function DHApplicationMixin(Base) {
classes: ['dh-style', 'two-big-buttons'],
buttons: [
{
action: "create",
label: "Create Item",
icon: "fa-solid fa-plus"
action: 'create',
label: 'Create Item',
icon: 'fa-solid fa-plus'
},
{
action: "browse",
label: "Browse Compendium",
icon: "fa-solid fa-book"
action: 'browse',
label: 'Browse Compendium',
icon: 'fa-solid fa-book'
}
]
});
if (!createChoice) return;
if(createChoice === "browse")
return DHSheetV2.#browseItem.call(this, event, target);
else
return DHSheetV2.#createDoc.call(this, event, target);
if (createChoice === 'browse') return DHSheetV2.#browseItem.call(this, event, target);
else return DHSheetV2.#createDoc.call(this, event, target);
}
static async #browseItem(event, target) {
@ -450,8 +461,8 @@ export default function DHApplicationMixin(Base) {
case 'consumable':
case 'armor':
case 'weapon':
presets.compendium = "daggerheart";
presets.folder = "equipments";
presets.compendium = 'daggerheart';
presets.folder = 'equipments';
presets.render = {
noFolder: true
};
@ -460,14 +471,14 @@ export default function DHApplicationMixin(Base) {
};
break;
case 'domainCard':
presets.compendium = "daggerheart";
presets.folder = "domains";
presets.compendium = 'daggerheart';
presets.folder = 'domains';
presets.render = {
noFolder: true
};
presets.filter = {
'level.max': { key: 'level.max', value: this.document.system.levelData.level.current },
'system.domain': { key: 'system.domain', value: this.document.system.domains },
'system.domain': { key: 'system.domain', value: this.document.system.domains }
};
break;
default:

View file

@ -22,7 +22,8 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
},
actions: {
openSettings: DHBaseActorSheet.#openSettings,
sendExpToChat: DHBaseActorSheet.#sendExpToChat
sendExpToChat: DHBaseActorSheet.#sendExpToChat,
increaseActionUses: event => DHBaseActorSheet.#modifyActionUses(event, true)
},
contextMenus: [
{
@ -70,6 +71,15 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
return context;
}
/**@inheritdoc */
_attachPartListeners(partId, htmlElement, options) {
super._attachPartListeners(partId, htmlElement, options);
htmlElement.querySelectorAll('.item-button .action-uses-button').forEach(element => {
element.addEventListener('contextmenu', DHBaseActorSheet.#modifyActionUses);
});
}
/**
* Prepare render context for the Effect part.
* @param {ApplicationRenderContext} context
@ -154,6 +164,19 @@ export default class DHBaseActorSheet extends DHApplicationMixin(ActorSheetV2) {
cls.create(msg);
}
/**
*
*/
static async #modifyActionUses(event, increase) {
event.stopPropagation();
event.preventDefault();
const actionId = event.target.dataset.itemUuid;
const action = await foundry.utils.fromUuid(actionId);
const newValue = (action.uses.value ?? 0) + (increase ? 1 : -1);
await action.update({ 'uses.value': Math.min(Math.max(newValue, 0), action.uses.max ?? 0) });
}
/* -------------------------------------------- */
/* Application Drag/Drop */
/* -------------------------------------------- */

View file

@ -3,7 +3,6 @@ import DHBaseItemSheet from '../api/base-item.mjs';
export default class FeatureSheet extends DHBaseItemSheet {
/** @inheritDoc */
static DEFAULT_OPTIONS = {
id: 'daggerheart-feature',
classes: ['feature'],
actions: {}
};

View file

@ -5,7 +5,7 @@ export default class SubclassSheet extends DHBaseItemSheet {
static DEFAULT_OPTIONS = {
classes: ['subclass'],
position: { width: 600 },
window: { resizable: false }
window: { resizable: true }
};
/**@override */

View file

@ -194,8 +194,12 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
event.stopPropagation();
const item = await foundry.utils.fromUuid(message.system.origin);
const action = item.system.actions.get(event.currentTarget.id);
await item.use(action);
const action =
item.system.attack?.id === event.currentTarget.id
? item.system.attack
: item.system.actions.get(event.currentTarget.id);
if (event.currentTarget.dataset.directDamage) action.use(event, { byPassRoll: true });
else action.use(event);
}
async actionUseButton(event, message) {

View file

@ -88,7 +88,10 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C
}
}
await this.viewed.update({ turn: this.viewed.turn === toggleTurn ? null : toggleTurn });
await this.viewed.update({
turn: this.viewed.turn === toggleTurn ? null : toggleTurn,
round: this.viewed.round + 1
});
await combatant.update(update);
}

View file

@ -4,8 +4,8 @@ export default class DhMeasuredTemplate extends foundry.canvas.placeables.Measur
const rangeMeasurementSettings = game.settings.get(
CONFIG.DH.id,
CONFIG.DH.SETTINGS.gameSettings.RangeMeasurement
);
CONFIG.DH.SETTINGS.gameSettings.variantRules
).rangeMeasurement;
if (rangeMeasurementSettings.enabled) {
const splitRulerText = this.ruler.text.split(' ');
if (splitRulerText.length > 0) {
@ -29,7 +29,7 @@ export default class DhMeasuredTemplate extends foundry.canvas.placeables.Measur
if (distance <= settings.far) {
return game.i18n.localize('DAGGERHEART.CONFIG.Range.far.name');
}
if (distance <= settings.veryFar) {
if (distance > settings.far) {
return game.i18n.localize('DAGGERHEART.CONFIG.Range.veryFar.name');
}

View file

@ -5,7 +5,7 @@ export default class DhpRuler extends foundry.canvas.interaction.Ruler {
const context = super._getWaypointLabelContext(waypoint, state);
if (!context) return;
const range = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.RangeMeasurement);
const range = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).rangeMeasurement;
if (range.enabled) {
const distance = DhMeasuredTemplate.getDistanceLabel(waypoint.measurement.distance.toNearest(0.01), range);

View file

@ -53,4 +53,40 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
this.effects.renderable = true;
this.renderFlags.set({ refreshEffects: true });
}
/** @inheritDoc */
_drawBar(number, bar, data) {
const val = Number(data.value);
const pct = Math.clamp(val, 0, data.max) / data.max;
// Determine sizing
const { width, height } = this.document.getSize();
const s = canvas.dimensions.uiScale;
const bw = width;
const bh = 8 * (this.document.height >= 2 ? 1.5 : 1) * s;
// Determine the color to use
const fillColor =
number === 0 ? foundry.utils.Color.fromRGB([1, 0, 0]) : foundry.utils.Color.fromString('#0032b1');
// Draw the bar
const widthUnit = bw / data.max;
bar.clear().lineStyle(s, 0x000000, 1.0);
const sections = [...Array(data.max).keys()];
for (let mark of sections) {
const x = mark * widthUnit;
const marked = mark + 1 <= data.value;
const color = marked ? fillColor : foundry.utils.Color.fromRGB([0, 0, 0]);
if (mark === 0 || mark === sections.length - 1) {
bar.beginFill(color, marked ? 1.0 : 0.5).drawRect(x, 0, widthUnit, bh, 2 * s); // Would like drawRoundedRect, but it's very troublsome with the corners. Leaving for now.
} else {
bar.beginFill(color, marked ? 1.0 : 0.5).drawRect(x, 0, widthUnit, bh, 2 * s);
}
}
// Set position
const posY = number === 0 ? height - bh : 0;
bar.position.set(0, posY);
return true;
}
}

View file

@ -5,7 +5,7 @@ export default class DhpTokenRuler extends foundry.canvas.placeables.tokens.Toke
const context = super._getWaypointLabelContext(waypoint, state);
if (!context) return;
const range = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.RangeMeasurement);
const range = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).rangeMeasurement;
if (range.enabled) {
const distance = DhMeasuredTemplate.getDistanceLabel(waypoint.measurement.distance.toNearest(0.01), range);

View file

@ -21,7 +21,7 @@ export const ruleChoice = {
}
};
export const range = {
export const templateRanges = {
self: {
id: 'self',
short: 's',
@ -56,7 +56,11 @@ export const range = {
label: 'DAGGERHEART.CONFIG.Range.far.name',
description: 'DAGGERHEART.CONFIG.Range.far.description',
distance: 20
},
}
};
export const range = {
...templateRanges,
veryFar: {
id: 'veryFar',
short: 'vf',
@ -479,7 +483,8 @@ export const multiplierTypes = {
cast: 'Spellcast',
scale: 'Cost Scaling',
result: 'Roll Result',
flat: 'Flat'
flat: 'Flat',
tier: 'Tier'
};
export const diceSetNumbers = {

View file

@ -857,7 +857,7 @@ export const weaponFeatures = {
name: 'DAGGERHEART.CONFIG.WeaponFeature.greedy.name',
description: 'DAGGERHEART.CONFIG.WeaponFeature.greedy.description',
img: 'icons/commodities/currency/coins-crown-stack-gold.webp',
// Should cost handfull of gold,
// Should cost handful of gold,
effects: [
{
name: 'DAGGERHEART.CONFIG.WeaponFeature.greedy.actions.greed.name',

View file

@ -20,7 +20,6 @@ export const menu = {
export const gameSettings = {
Automation: 'Automation',
Homebrew: 'Homebrew',
RangeMeasurement: 'RangeMeasurement',
appearance: 'Appearance',
variantRules: 'VariantRules',
Resources: {

View file

@ -34,8 +34,8 @@ export default class DHAttackAction extends DHDamageAction {
};
}
async use(event, ...args) {
const result = await super.use(event, args);
async use(event, options) {
const result = await super.use(event, options);
const { updateCountdowns } = game.system.api.applications.ui.DhCountdowns;
await updateCountdowns(CONFIG.DH.GENERAL.countdownTypes.characterAttack.id);

View file

@ -111,12 +111,13 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
return actorData;
}
async use(event, ...args) {
async use(event, options = {}) {
if (!this.actor) throw new Error("An Action can't be used outside of an Actor context.");
if (this.chatDisplay) await this.toChat();
let config = this.prepareConfig(event);
let { byPassRoll } = options,
config = this.prepareConfig(event, byPassRoll);
for (let i = 0; i < this.constructor.extraSchemas.length; i++) {
let clsField = this.constructor.getActionField(this.constructor.extraSchemas[i]);
if (clsField?.prepareConfig) {
@ -133,14 +134,14 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
if (!config) return;
}
if (this.hasRoll) {
if (config.hasRoll) {
const rollConfig = this.prepareRoll(config);
config.roll = rollConfig;
config = await this.actor.diceRoll(config);
if (!config) return;
}
if (this.doFollowUp()) {
if (this.doFollowUp(config)) {
if (this.rollDamage && this.damage.parts.length) await this.rollDamage(event, config);
else if (this.trigger) await this.trigger(event, config);
else if (this.hasSave || this.hasEffect) {
@ -160,7 +161,8 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
}
/* */
prepareConfig(event) {
prepareConfig(event, byPass = false) {
const hasRoll = this.getUseHasRoll(byPass);
return {
event,
title: `${this.item.name}: ${this.name}`,
@ -170,10 +172,10 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
actor: this.actor.uuid
},
dialog: {
configure: this.hasRoll
configure: hasRoll
},
type: this.type,
hasRoll: this.hasRoll,
hasRoll: hasRoll,
hasDamage: this.damage?.parts?.length && this.type !== 'healing',
hasHealing: this.damage?.parts?.length && this.type === 'healing',
hasEffect: !!this.effects?.length,
@ -182,12 +184,12 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
selectedRollMode: game.settings.get('core', 'rollMode'),
isFastForward: event.shiftKey,
data: this.getRollData(),
evaluate: this.hasRoll
evaluate: hasRoll
};
}
requireConfigurationDialog(config) {
return !config.event.shiftKey && !this.hasRoll && (config.costs?.length || config.uses);
return !config.event.shiftKey && !config.hasRoll && (config.costs?.length || config.uses);
}
prepareRoll() {
@ -205,7 +207,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
}
doFollowUp(config) {
return !this.hasRoll;
return !config.hasRoll;
}
async consume(config, successCost = false) {
@ -222,14 +224,11 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
}
const resources = config.costs
.filter(c =>
c.enabled !== false
&&
(
(!successCost && (!c.consumeOnSuccess || config.roll?.success))
||
(successCost && c.consumeOnSuccess)
)
.filter(
c =>
c.enabled !== false &&
((!successCost && (!c.consumeOnSuccess || config.roll?.success)) ||
(successCost && c.consumeOnSuccess))
)
.map(c => {
const resource = usefulResources[c.key];
@ -242,21 +241,23 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
});
await this.actor.modifyResource(resources);
if (config.uses?.enabled
&&
(
(!successCost && (!config.uses?.consumeOnSuccess || config.roll?.success))
||
(successCost && config.uses?.consumeOnSuccess)
if (
config.uses?.enabled &&
((!successCost && (!config.uses?.consumeOnSuccess || config.roll?.success)) ||
(successCost && config.uses?.consumeOnSuccess))
)
) this.update({ 'uses.value': this.uses.value + 1 });
this.update({ 'uses.value': this.uses.value + 1 });
if (config.roll?.success || successCost)
(config.message ?? config.parent).update({'system.successConsumed': true})
(config.message ?? config.parent).update({ 'system.successConsumed': true });
}
/* */
/* ROLL */
getUseHasRoll(byPass = false) {
return this.hasRoll && !byPass;
}
get hasRoll() {
return !!this.roll?.type;
}
@ -301,11 +302,9 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
}
async applyEffect(effect, actor) {
const origin = effect.parent?.parent ? effect.parent.parent.uuid : effect.parent.uuid;
// Enable an existing effect on the target if it originated from this effect
const existingEffect = actor.effects.find(e => e.origin === origin);
const existingEffect = actor.effects.find(e => e.origin === effect.uuid);
if (existingEffect) {
return existingEffect.update(
return effect.update(
foundry.utils.mergeObject({
...effect.constructor.getInitialDuration(),
disabled: false
@ -318,7 +317,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
...effect.toObject(),
disabled: false,
transfer: false,
origin: origin
origin: effect.uuid
});
await ActiveEffect.implementation.create(effectData, { parent: actor });
}

View file

@ -4,7 +4,7 @@ import DHBaseAction from './baseAction.mjs';
export default class DhBeastformAction extends DHBaseAction {
static extraSchemas = [...super.extraSchemas, 'beastform'];
async use(event, ...args) {
async use(event, options) {
const beastformConfig = this.prepareBeastformConfig();
const abort = await this.handleActiveTransformations();
@ -20,7 +20,7 @@ export default class DhBeastformAction extends DHBaseAction {
const { selected, evolved, hybrid } = await BeastformDialog.configure(beastformConfig, this.item);
if (!selected) return;
const result = await super.use(event, args);
const result = await super.use(event, options);
if (!result) return;
await this.transform(selected, evolved, hybrid);

View file

@ -6,7 +6,7 @@ export default class DHDamageAction extends DHBaseAction {
getFormulaValue(part, data) {
let formulaValue = part.value;
if (this.hasRoll && part.resultBased && data.system.roll.result.duality === -1) return part.valueAlt;
if (data.hasRoll && part.resultBased && data.system.roll.result.duality === -1) return part.valueAlt;
const isAdversary = this.actor.type === 'adversary';
if (isAdversary && this.actor.system.type === CONFIG.DH.ACTOR.adversaryTypes.horde.id) {
@ -51,7 +51,7 @@ export default class DHDamageAction extends DHBaseAction {
dialog: {},
data: this.getRollData(),
targetSelection: systemData.targets.length > 0
}
};
if (this.hasSave) config.onSave = this.save.damageMod;
if (data.system) {
config.source.message = data._id;

View file

@ -10,8 +10,6 @@ export default class DHMacroAction extends DHBaseAction {
}
async trigger(event, ...args) {
// const config = await super.use(event, args);
// if (['error', 'warning'].includes(config.type)) return;
const fixUUID = !this.documentUUID.includes('Macro.') ? `Macro.${this.documentUUID}` : this.documentUUID,
macro = await fromUuid(fixUUID);
try {

View file

@ -11,7 +11,6 @@ export default class DHSummonAction extends DHBaseAction {
async trigger(event, ...args) {
if (!this.canSummon || !canvas.scene) return;
// const config = await super.use(event, args);
}
get canSummon() {

View file

@ -68,12 +68,13 @@ export default class DhCharacter extends BaseDataActor {
new fields.SchemaField({
name: new fields.StringField(),
value: new fields.NumberField({ integer: true, initial: 0 }),
description: new fields.StringField()
description: new fields.StringField(),
core: new fields.BooleanField({ initial: false })
})
),
gold: new fields.SchemaField({
coins: new fields.NumberField({ initial: 0, integer: true }),
handfulls: new fields.NumberField({ initial: 0, integer: true }),
handfuls: new fields.NumberField({ initial: 1, integer: true }),
bags: new fields.NumberField({ initial: 0, integer: true }),
chests: new fields.NumberField({ initial: 0, integer: true })
}),
@ -573,7 +574,10 @@ export default class DhCharacter extends BaseDataActor {
case 'experience':
selection.data.forEach(id => {
const experience = this.experiences[id];
if (experience) experience.value += selection.value;
if (experience) {
experience.value += selection.value;
experience.leveledUp = true;
}
});
break;
}
@ -620,6 +624,23 @@ export default class DhCharacter extends BaseDataActor {
};
}
async _preUpdate(changes, options, userId) {
const allowed = await super._preUpdate(changes, options, userId);
if (allowed === false) return;
/* The first two experiences are always marked as core */
if (changes.system?.experiences && Object.keys(this.experiences).length < 2) {
const experiences = new Set(Object.keys(this.experiences));
const changeExperiences = new Set(Object.keys(changes.system.experiences));
const newExperiences = Array.from(changeExperiences.difference(experiences));
for (var i = 0; i < Math.min(newExperiences.length, 2 - experiences.size); i++) {
const experience = newExperiences[i];
changes.system.experiences[experience].core = true;
}
}
}
async _preDelete() {
if (this.companion) {
this.companion.updateLevel(1);

View file

@ -1,6 +1,7 @@
const fields = foundry.data.fields;
const targetsField = () => new fields.ArrayField(
const targetsField = () =>
new fields.ArrayField(
new fields.SchemaField({
id: new fields.StringField({}),
actorId: new fields.StringField({}),
@ -14,7 +15,7 @@ const targetsField = () => new fields.ArrayField(
success: new fields.BooleanField({ nullable: true, initial: null })
})
})
)
);
export default class DHActorRoll extends foundry.abstract.TypeDataModel {
targetHook = null;
@ -40,9 +41,7 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
action: new fields.StringField()
}),
damage: new fields.ObjectField(),
costs: new fields.ArrayField(
new fields.ObjectField()
),
costs: new fields.ArrayField(new fields.ObjectField()),
successConsumed: new fields.BooleanField({ initial: false })
};
}
@ -76,18 +75,16 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
this.targetSelection = mode;
this.updateTargets();
this.registerTargetHook();
this.parent.update(
{
this.parent.update({
system: {
targetSelection: this.targetSelection,
oldTargets: this.oldTargets
}
}
);
});
}
get hitTargets() {
return this.currentTargets.filter(t => (t.hit || !this.hasRoll || !this.targetSelection));
return this.currentTargets.filter(t => t.hit || !this.hasRoll || !this.targetSelection);
}
async updateTargets() {
@ -96,31 +93,27 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
this.currentTargets.forEach(ct => {
if (this.targets.find(t => t.actorId === ct.actorId)) return;
const indexTarget = this.oldTargets.findIndex(ot => ot.actorId === ct.actorId);
if(indexTarget === -1)
this.oldTargets.push(ct);
if (indexTarget === -1) this.oldTargets.push(ct);
});
if (this.hasSave) this.setPendingSaves();
if (this.currentTargets.length) {
if (!this.parent._id) return;
const updates = await this.parent.update(
{
const updates = await this.parent.update({
system: {
oldTargets: this.oldTargets
}
}
);
if(!updates && ui.chat.collection.get(this.parent.id))
ui.chat.updateMessage(this.parent);
});
if (!updates && ui.chat.collection.get(this.parent.id)) ui.chat.updateMessage(this.parent);
}
}
}
registerTargetHook() {
if (this.targetSelection && this.targetHook !== null) {
Hooks.off("targetToken", this.targetHook);
Hooks.off('targetToken', this.targetHook);
this.targetHook = null;
} else if (!this.targetSelection && this.targetHook === null) {
this.targetHook = Hooks.on("targetToken", foundry.utils.debounce(this.updateTargets.bind(this), 50));
this.targetHook = Hooks.on('targetToken', foundry.utils.debounce(this.updateTargets.bind(this), 50));
}
}
@ -130,11 +123,14 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
this.updateTargets();
this.registerTargetHook();
if (this.targetSelection === true) {
this.targetShort = this.targets.reduce((a,c) => {
this.targetShort = this.targets.reduce(
(a, c) => {
if (c.hit) a.hit += 1;
else c.miss += 1;
else a.miss += 1;
return a;
}, {hit: 0, miss: 0})
},
{ hit: 0, miss: 0 }
);
}
if (this.hasSave) this.setPendingSaves();
}
@ -146,7 +142,9 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
return this.targetSelection !== true
? Array.from(game.user.targets).map(t => {
const target = game.system.api.fields.ActionFields.TargetField.formatTarget(t),
oldTarget = this.targets.find(ot => ot.actorId === target.actorId) ?? this.oldTargets.find(ot => ot.actorId === target.actorId);
oldTarget =
this.targets.find(ot => ot.actorId === target.actorId) ??
this.oldTargets.find(ot => ot.actorId === target.actorId);
if (oldTarget) return oldTarget;
return target;
})
@ -155,11 +153,7 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel {
setPendingSaves() {
this.pendingSaves = this.targetSelection
? this.targets.filter(
target => target.hit && target.saved.success === null
).length > 0
: this.currentTargets.filter(
target => target.saved.success === null
).length > 0;
? this.targets.filter(target => target.hit && target.saved.success === null).length > 0
: this.currentTargets.filter(target => target.saved.success === null).length > 0;
}
}

View file

@ -4,7 +4,25 @@ export default class BeastformField extends fields.SchemaField {
constructor(options = {}, context = {}) {
const beastformFields = {
tierAccess: new fields.SchemaField({
exact: new fields.NumberField({ integer: true, nullable: true, initial: null })
exact: new fields.NumberField({
integer: true,
nullable: true,
initial: null,
choices: () => {
const settingsTiers = game.settings.get(
CONFIG.DH.id,
CONFIG.DH.SETTINGS.gameSettings.LevelTiers
).tiers;
return Object.values(settingsTiers).reduce(
(acc, tier) => {
acc[tier.tier] = game.i18n.localize(tier.name);
return acc;
},
{ 1: game.i18n.localize('DAGGERHEART.GENERAL.Tiers.1') }
);
},
hint: 'DAGGERHEART.ACTIONS.Config.beastform.exactHint'
})
})
};
super(beastformFields, options, context);

View file

@ -12,7 +12,10 @@ export default class UsesField extends fields.SchemaField {
initial: null,
nullable: true
}),
consumeOnSuccess: new fields.BooleanField({ initial: false, label: "DAGGERHEART.ACTIONS.Settings.consumeOnSuccess.label" })
consumeOnSuccess: new fields.BooleanField({
initial: false,
label: 'DAGGERHEART.ACTIONS.Settings.consumeOnSuccess.label'
})
};
super(usesFields, options, context);
}
@ -30,6 +33,7 @@ export default class UsesField extends fields.SchemaField {
if (!uses) return null;
return {
...uses,
remaining: this.remainingUses,
enabled: uses.hasOwnProperty('enabled') ? uses.enabled : true
};
}

View file

@ -1,4 +1,5 @@
import DHActionConfig from '../../applications/sheets-configs/action-config.mjs';
import { itemAbleRollParse } from '../../helpers/utils.mjs';
import MappingField from './mappingField.mjs';
/**
@ -164,6 +165,15 @@ export function ActionMixin(Base) {
return foundry.utils.getProperty(this.parent, this.systemPath) instanceof Collection;
}
get remainingUses() {
if (!this.uses) return null;
return Math.max(
(this.uses.max ? itemAbleRollParse(this.uses.max, this.actor) : 0) - (this.uses.value ?? 0),
0
);
}
static async create(data, operation = {}) {
const { parent, renderSheet } = operation;
let { type } = data;

View file

@ -88,6 +88,26 @@ export default class DHBeastform extends BaseDataItem {
/* -------------------------------------------- */
get beastformAttackData() {
const effect = this.parent.effects.find(x => x.type === 'beastform');
if (!effect) return null;
const traitBonus = effect.changes.find(x => x.key === `system.traits.${this.mainTrait}.value`)?.value ?? 0;
const evasionBonus = effect.changes.find(x => x.key === 'system.evasion')?.value ?? 0;
const damageDiceIndex = effect.changes.find(x => x.key === 'system.rules.attack.damage.diceIndex');
const damageDice = damageDiceIndex ? Object.keys(CONFIG.DH.GENERAL.diceTypes)[damageDiceIndex.value] : null;
const damageBonus = effect.changes.find(x => x.key === 'system.rules.attack.damage.bonus')?.value ?? 0;
return {
trait: game.i18n.localize(CONFIG.DH.ACTOR.abilities[this.mainTrait].label),
traitBonus: traitBonus ? Number(traitBonus).signedString() : '',
evasionBonus: evasionBonus ? Number(evasionBonus).signedString() : '',
damageDice: damageDice,
damageBonus: damageBonus ? `${Number(damageBonus).signedString()}` : ''
};
}
async _preCreate() {
if (!this.actor) return;

View file

@ -45,6 +45,11 @@ export default class DhAutomation extends foundry.abstract.DataModel {
required: true,
initial: true,
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.resourceScrollTexts.label'
}),
playerCanEditSheet: new fields.BooleanField({
required: true,
initial: false,
label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.playerCanEditSheet.label'
})
};
}

View file

@ -45,10 +45,10 @@ export default class DhHomebrew extends foundry.abstract.DataModel {
initial: 'Coins',
label: 'DAGGERHEART.SETTINGS.Homebrew.currency.coinName'
}),
handfulls: new fields.StringField({
handfuls: new fields.StringField({
required: true,
initial: 'Handfulls',
label: 'DAGGERHEART.SETTINGS.Homebrew.currency.handfullName'
initial: 'Handfuls',
label: 'DAGGERHEART.SETTINGS.Homebrew.currency.handfulName'
}),
bags: new fields.StringField({
required: true,

View file

@ -1,25 +0,0 @@
export default class DhRangeMeasurement extends foundry.abstract.DataModel {
static defineSchema() {
const fields = foundry.data.fields;
return {
enabled: new fields.BooleanField({ required: true, initial: true, label: 'DAGGERHEART.GENERAL.enabled' }),
melee: new fields.NumberField({ required: true, initial: 5, label: 'DAGGERHEART.CONFIG.Range.melee.name' }),
veryClose: new fields.NumberField({
required: true,
initial: 15,
label: 'DAGGERHEART.CONFIG.Range.veryClose.name'
}),
close: new fields.NumberField({
required: true,
initial: 30,
label: 'DAGGERHEART.CONFIG.Range.close.name'
}),
far: new fields.NumberField({ required: true, initial: 60, label: 'DAGGERHEART.CONFIG.Range.far.name' }),
veryFar: new fields.NumberField({
required: true,
initial: 120,
label: 'DAGGERHEART.CONFIG.Range.veryFar.name'
})
};
}
}

View file

@ -17,9 +17,28 @@ export default class DhVariantRules extends foundry.abstract.DataModel {
label: 'DAGGERHEART.SETTINGS.VariantRules.FIELDS.actionTokens.tokens.label'
})
}),
useCoins: new fields.BooleanField({
initial: false,
label: 'DAGGERHEART.SETTINGS.VariantRules.FIELDS.useCoins.label'
rangeMeasurement: new fields.SchemaField({
enabled: new fields.BooleanField({
required: true,
initial: true,
label: 'DAGGERHEART.GENERAL.enabled'
}),
melee: new fields.NumberField({
required: true,
initial: 5,
label: 'DAGGERHEART.CONFIG.Range.melee.name'
}),
veryClose: new fields.NumberField({
required: true,
initial: 15,
label: 'DAGGERHEART.CONFIG.Range.veryClose.name'
}),
close: new fields.NumberField({
required: true,
initial: 30,
label: 'DAGGERHEART.CONFIG.Range.close.name'
}),
far: new fields.NumberField({ required: true, initial: 60, label: 'DAGGERHEART.CONFIG.Range.far.name' })
})
};
}

View file

@ -1,5 +1,4 @@
export { default as DhAppearance } from './Appearance.mjs';
export { default as DhAutomation } from './Automation.mjs';
export { default as DhHomebrew } from './Homebrew.mjs';
export { default as DhRangeMeasurement } from './RangeMeasurement.mjs';
export { default as DhVariantRules } from './VariantRules.mjs';

View file

@ -18,9 +18,7 @@ export default class D20Roll extends DHRoll {
static DefaultDialog = D20RollDialog;
get title() {
return game.i18n.localize(
"DAGGERHEART.GENERAL.d20Roll"
);
return game.i18n.localize('DAGGERHEART.GENERAL.d20Roll');
}
get d20() {
@ -145,9 +143,9 @@ export default class D20Roll extends DHRoll {
config.targetSelection = true;
config.targets.forEach(target => {
const difficulty = config.roll.difficulty ?? target.difficulty ?? target.evasion;
target.hit = this.isCritical || roll.total >= difficulty;
target.hit = roll.isCritical || roll.total >= difficulty;
});
data.success = config.targets.some(target => target.hit)
data.success = config.targets.some(target => target.hit);
} else if (config.roll.difficulty) {
data.difficulty = config.roll.difficulty;
data.success = roll.isCritical || roll.total >= config.roll.difficulty;

View file

@ -9,8 +9,7 @@ export default class DamageRoll extends DHRoll {
static DefaultDialog = DamageDialog;
static async buildEvaluate(roll, config = {}, message = {}) {
if (config.evaluate !== false)
for (const roll of config.roll) await roll.roll.evaluate();
if (config.evaluate !== false) for (const roll of config.roll) await roll.roll.evaluate();
roll._evaluated = true;
const parts = config.roll.map(r => this.postEvaluate(r));
@ -84,11 +83,11 @@ export default class DamageRoll extends DHRoll {
applyBaseBonus(part) {
const modifiers = [],
type = this.options.messageType ?? (this.options.isHealing ? 'healing' : 'damage'),
type = this.options.messageType ?? (this.options.hasHealing ? 'healing' : 'damage'),
options = part ?? this.options;
modifiers.push(...this.getBonus(`${type}`, `${type.capitalize()} Bonus`));
if (!this.options.isHealing) {
if (!this.options.hasHealing) {
options.damageTypes?.forEach(t => {
modifiers.push(...this.getBonus(`${type}.${t}`, `${t.capitalize()} ${type.capitalize()} Bonus`));
});

View file

@ -75,7 +75,8 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect {
if (isOriginTarget && change.effect.origin) {
change.value = change.value.replaceAll(/origin\.@/gi, '@');
try {
const doc = foundry.utils.fromUuidSync(change.effect.origin);
const effect = foundry.utils.fromUuidSync(change.effect.origin);
const doc = effect.parent?.parent;
if (doc) parseModel = doc;
} catch (_) {}
}

View file

@ -202,7 +202,8 @@ export default class DhpActor extends Actor {
await this.update({
[`system.experiences.${experienceKey}`]: {
name: experience.name,
value: experience.modifier
value: experience.modifier,
core: true
}
});
@ -210,7 +211,8 @@ export default class DhpActor extends Actor {
await this.system.companion.update({
[`system.experiences.${experienceKey}`]: {
name: '',
value: experience.modifier
value: experience.modifier,
core: true
}
});
}

View file

@ -130,7 +130,6 @@ export default class DHItem extends foundry.documents.Item {
/* -------------------------------------------- */
async use(event) {
const actions = new Set(this.system.actionsList);
if (actions?.size) {
@ -163,7 +162,7 @@ export default class DHItem extends foundry.documents.Item {
img: this.img,
tags: this._getTags()
},
actions: item.system.actions,
actions: item.system.actionsList,
description: this.system.description
};

View file

@ -161,7 +161,7 @@ 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 TextEditor.enrichHTML(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);
}

View file

@ -1,5 +1,3 @@
import { range as configRange } from '../config/generalConfig.mjs';
export default function DhTemplateEnricher(match, _options) {
const parts = match[1].split('|').map(x => x.trim());
@ -17,7 +15,7 @@ export default function DhTemplateEnricher(match, _options) {
type = matchedType;
break;
case 'range':
const matchedRange = Object.values(configRange).find(
const matchedRange = Object.values(CONFIG.DH.GENERAL.templateRanges).find(
x => x.id.toLowerCase() === split[1] || x.short === split[1]
);
range = matchedRange?.id;
@ -55,7 +53,9 @@ export const renderMeasuredTemplate = async event => {
? '180'
: undefined;
const baseDistance = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.RangeMeasurement)[range];
const baseDistance = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.variantRules).rangeMeasurement[
range
];
const distance = type === CONFIG.DH.GENERAL.templateTypes.EMANATION ? baseDistance + 2.5 : baseDistance;
const { width, height } = game.canvas.scene.dimensions;

View file

@ -313,8 +313,10 @@ export const itemAbleRollParse = (value, actor, item) => {
const isItemTarget = value.toLowerCase().includes('item.@');
const slicedValue = isItemTarget ? value.replaceAll(/item\.@/gi, '@') : value;
const model = isItemTarget ? item : actor;
try {
return Roll.replaceFormulaData(slicedValue, isItemTarget ? item : actor);
return Roll.replaceFormulaData(slicedValue, model?.getRollData?.() ?? model);
} catch (_) {
return '';
}
@ -362,6 +364,7 @@ export async function createEmbeddedItemWithEffects(actor, baseData, update) {
const [doc] = await actor.createEmbeddedDocuments('Item', [
{
...(update ?? data),
...baseData,
id: data.id,
uuid: data.uuid,
effects: data.effects?.map(effect => effect.toObject())
@ -371,6 +374,21 @@ export async function createEmbeddedItemWithEffects(actor, baseData, update) {
return doc;
}
export async function createEmbeddedItemsWithEffects(actor, baseData) {
const effectData = [];
for (let d of baseData) {
const data = d.uuid.startsWith('Compendium') ? await foundry.utils.fromUuid(d.uuid) : d;
effectData.push({
...data,
id: data.id,
uuid: data.uuid,
effects: data.effects?.map(effect => effect.toObject())
});
}
await actor.createEmbeddedDocuments('Item', effectData);
}
export const slugify = name => {
return name.toLowerCase().replaceAll(' ', '-').replaceAll('.', '');
};

View file

@ -1,17 +1,10 @@
import { defaultLevelTiers, DhLevelTiers } from '../data/levelTier.mjs';
import DhCountdowns from '../data/countdowns.mjs';
import {
DhAppearance,
DhAutomation,
DhHomebrew,
DhRangeMeasurement,
DhVariantRules
} from '../data/settings/_module.mjs';
import { DhAppearance, DhAutomation, DhHomebrew, DhVariantRules } from '../data/settings/_module.mjs';
import {
DhAppearanceSettings,
DhAutomationSettings,
DhHomebrewSettings,
DhRangeMeasurementSettings,
DhVariantRuleSettings
} from '../applications/settings/_module.mjs';
@ -58,12 +51,6 @@ const registerMenuSettings = () => {
}
}
});
game.settings.register(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.RangeMeasurement, {
scope: 'world',
config: false,
type: DhRangeMeasurement
});
};
const registerMenus = () => {
@ -83,14 +70,6 @@ const registerMenus = () => {
type: DhHomebrewSettings,
restricted: true
});
game.settings.registerMenu(CONFIG.DH.id, CONFIG.DH.SETTINGS.menu.Range.Name, {
name: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.range.name'),
label: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.range.label'),
hint: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.range.hint'),
icon: CONFIG.DH.SETTINGS.menu.Range.Icon,
type: DhRangeMeasurementSettings,
restricted: true
});
game.settings.registerMenu(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance, {
name: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.appearance.title'),

View file

@ -15,6 +15,7 @@
"readline": "^1.3.0",
"pushLDBtoYML": "node ./tools/pushLDBtoYML.mjs",
"pullYMLtoLDB": "node ./tools/pullYMLtoLDB.mjs",
"pullYMLtoLDBBuild": "node ./tools/pullYMLtoLDBBuild.mjs",
"createSymlink": "node ./tools/create-symlink.mjs"
},
"devDependencies": {

View file

@ -60,6 +60,7 @@
},
"attack": {
"name": "Spears",
"img": "icons/weapons/polearms/spear-flared-worn.webp",
"range": "veryClose",
"roll": {
"bonus": 0,

View file

@ -74,6 +74,7 @@
"description": "<p>An undead figure wearing a heavy leather coat, with searching eyes and a casually cruel demeanor.</p>",
"attack": {
"name": "Tear at Flesh",
"img": "icons/skills/melee/strike-slashes-red.webp",
"roll": {
"bonus": 5,
"type": "attack"

View file

@ -74,6 +74,7 @@
"motivesAndTactics": "Move through solid objects, rally troops, rehash old battles",
"attack": {
"name": "Longbow",
"img": "icons/weapons/bows/longbow-recurve-skull-brown.webp",
"damage": {
"parts": [
{

View file

@ -68,6 +68,7 @@
"description": "<p>A dust-covered golden construct with boxy limbs and a huge mace for a hand.</p>",
"attack": {
"name": "Charged Mace",
"img": "icons/weapons/maces/shortmace-ornate-gold.webp",
"range": "veryClose",
"roll": {
"bonus": 3,

View file

@ -26,8 +26,8 @@
],
"uses": {
"value": null,
"max": "",
"recovery": null
"max": "1",
"recovery": "shortRest"
},
"effects": [],
"target": {
@ -58,7 +58,7 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753997061290,
"modifiedTime": 1753997114091,
"modifiedTime": 1754498245294,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_key": "!items!AXqcoxnRoWBbbKpK"

View file

@ -26,8 +26,8 @@
],
"uses": {
"value": null,
"max": "",
"recovery": null
"max": "1",
"recovery": "session"
},
"effects": [],
"target": {
@ -58,7 +58,7 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753994658436,
"modifiedTime": 1753994711690,
"modifiedTime": 1754498186961,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_key": "!items!U6iFjZgLYawlOlQZ"

View file

@ -49,8 +49,8 @@
"suggestedTraits": {
"agility": 1,
"strength": -1,
"finesse": 0,
"instinct": 2,
"finesse": 2,
"instinct": 0,
"presence": 1,
"knowledge": 0
},
@ -76,8 +76,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1754325275832,
"modifiedTime": 1754325686143,
"lastModifiedBy": "LgnbNMLaxandgMQq"
"modifiedTime": 1754500637635,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
},
"_key": "!items!CvHlkHZfpMiCz5uT"
}

View file

@ -6,15 +6,7 @@
"img": "icons/magic/unholy/strike-body-explode-disintegrate.webp",
"system": {
"description": "<p>Once per long rest, you can place a domain card from your loadout into your vault and choose to either:</p><ul><li><p>Gain Hope equal to the level of the card.</p></li><li><p>Enhance a spell that deals damage, gaining a bonus to your damage roll equal to twice the level of the card.</p></li></ul>",
"resource": {
"type": "simple",
"value": 1,
"max": "1",
"icon": "",
"recovery": "longRest",
"diceStates": {},
"dieFaces": "d4"
},
"resource": null,
"actions": {
"YFmqnbMx540su2Ni": {
"type": "effect",
@ -26,17 +18,17 @@
"cost": [
{
"scalable": false,
"key": "P02cbN50LIoD662z",
"key": "hitPoints",
"value": 1,
"keyIsID": true,
"keyIsID": false,
"step": null,
"consumeOnSuccess": false
}
],
"uses": {
"value": null,
"max": "",
"recovery": null,
"max": "1",
"recovery": "longRest",
"consumeOnSuccess": false
},
"effects": [],
@ -67,8 +59,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1754349703843,
"modifiedTime": 1754349703843,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
"modifiedTime": 1754498040342,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_key": "!items!P02cbN50LIoD662z"
}

View file

@ -5,15 +5,7 @@
"img": "icons/tools/instruments/drum-hand-tan.webp",
"system": {
"description": "<p>Once per session, describe how you rally the party and give yourself and each of your allies a Rally Die. At level 1, your Rally Die is a d6. A PC can spend their Rally Die to roll it, adding the result to their action roll, reaction roll, damage roll, or to clear a number of Stress equal to the result. At the end of each session, clear all unspent Rally Dice. At level 5, your Rally Die increases to a d8.</p>",
"resource": {
"type": "simple",
"value": 1,
"max": "1",
"icon": "",
"recovery": "session",
"diceStates": {},
"dieFaces": "d4"
},
"resource": null,
"actions": {
"vI4Fph3y9ygsya9e": {
"type": "effect",
@ -25,15 +17,16 @@
"cost": [
{
"scalable": false,
"key": "Y7waM3ljoRLyk38N",
"key": "hitPoints",
"value": 1,
"keyIsID": true,
"step": null
"keyIsID": false,
"step": null,
"consumeOnSuccess": false
}
],
"uses": {
"value": null,
"max": "",
"max": "1",
"recovery": "session"
},
"effects": [
@ -114,8 +107,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1754174497668,
"modifiedTime": 1754246215191,
"lastModifiedBy": "LgnbNMLaxandgMQq"
"modifiedTime": 1754494820213,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"ownership": {
"default": 0,

View file

@ -5,15 +5,7 @@
"img": "icons/tools/instruments/drum-hand-tan.webp",
"system": {
"description": "<p>Once per session, describe how you rally the party and give yourself and each of your allies a Rally Die. At level 1, your Rally Die is a d6. A PC can spend their Rally Die to roll it, adding the result to their action roll, reaction roll, damage roll, or to clear a number of Stress equal to the result. At the end of each session, clear all unspent Rally Dice. At level 5, your Rally Die increases to a d8.</p>",
"resource": {
"type": "simple",
"value": 1,
"max": "1",
"icon": "",
"recovery": "session",
"diceStates": {},
"dieFaces": "d4"
},
"resource": null,
"actions": {
"Z1KWFrpXOqZWuZD1": {
"type": "effect",
@ -25,15 +17,16 @@
"cost": [
{
"scalable": false,
"key": "oxv0m8AFUQVFKtZ4",
"key": "hitPoints",
"value": 1,
"keyIsID": true,
"step": null
"keyIsID": false,
"step": null,
"consumeOnSuccess": false
}
],
"uses": {
"value": null,
"max": "",
"max": "1",
"recovery": "session"
},
"effects": [
@ -114,8 +107,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1754174499893,
"modifiedTime": 1754246215922,
"lastModifiedBy": "LgnbNMLaxandgMQq"
"modifiedTime": 1754494835723,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"ownership": {
"default": 0,

View file

@ -7,10 +7,11 @@
"system": {
"description": "<p>Choose a number between 1 and 12. When you roll that number on a Duality Die, gain a Hope or clear a Stress.</p><p></p><p>You can change this number when you take a long rest.</p>",
"resource": {
"type": "simple",
"type": "diceValue",
"value": 1,
"max": "",
"icon": "fa-solid fa-hashtag"
"max": "1",
"icon": "fa-solid fa-hashtag",
"dieFaces": "d12"
},
"actions": {
"RkqPzF1bdWzPPMml": {
@ -100,7 +101,7 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1754254942995,
"modifiedTime": 1754255067467,
"modifiedTime": 1754498121727,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_key": "!items!6YsfFjmCGuFYVhT4"

View file

@ -30,8 +30,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1754010247432,
"modifiedTime": 1754010247432,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
"modifiedTime": 1754498464092,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"sort": 0,
"ownership": {

View file

@ -66,7 +66,7 @@
}
],
"roll": {
"type": null,
"type": "spellcast",
"trait": null,
"difficulty": null,
"bonus": null,
@ -154,8 +154,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753922784434,
"modifiedTime": 1754253433766,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
"modifiedTime": 1754475145346,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_id": "5EP2Lgf7ojfrc0Is",
"sort": 3400000,

View file

@ -20,9 +20,9 @@
"cost": [],
"uses": {
"value": null,
"max": "",
"recovery": null,
"consumeOnSuccess": false
"max": "1",
"recovery": "longRest",
"consumeOnSuccess": true
},
"damage": {
"parts": [],
@ -128,8 +128,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753922784437,
"modifiedTime": 1754254161910,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
"modifiedTime": 1754501480068,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_id": "R8NDiJXJWmC48WSr",
"sort": 3400000,

View file

@ -9,12 +9,31 @@
"recallCost": 3,
"level": 9,
"type": "spell",
"resource": {
"type": "simple",
"value": 1,
"recovery": "longRest",
"resource": null,
"actions": {
"RKEceNKiQirYwN45": {
"type": "effect",
"_id": "RKEceNKiQirYwN45",
"systemPath": "actions",
"description": "<p class=\"Body-Foundation\">Once per long rest, this card can mimic the features of another domain card of level 8 or lower in another players loadout. <strong>Spend Hope equal to half the cards level</strong> to gain access to the feature. It lasts until your next rest or they place the card in their vault.</p>",
"chatDisplay": true,
"actionType": "action",
"cost": [],
"uses": {
"value": null,
"max": "1",
"icon": ""
"recovery": "longRest",
"consumeOnSuccess": false
},
"effects": [],
"target": {
"type": "any",
"amount": null
},
"name": "Mimic",
"img": "icons/magic/perception/hand-eye-black.webp",
"range": ""
}
}
},
"flags": {},
@ -26,8 +45,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753922784439,
"modifiedTime": 1754329311656,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
"modifiedTime": 1754499898585,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_id": "3A7LZ1xmDEMGa165",
"sort": 3400000,

View file

@ -29,9 +29,9 @@
],
"uses": {
"value": null,
"max": "",
"recovery": null,
"consumeOnSuccess": false
"max": "1",
"recovery": "shortRest",
"consumeOnSuccess": true
},
"damage": {
"parts": [
@ -99,13 +99,7 @@
"range": "veryFar"
}
},
"resource": {
"type": "simple",
"value": 1,
"recovery": "shortRest",
"max": "1",
"icon": ""
}
"resource": null
},
"flags": {},
"_stats": {
@ -116,8 +110,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753922784449,
"modifiedTime": 1754254262215,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
"modifiedTime": 1754501560924,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_id": "C0qLOwSSvZ6PG3Ws",
"sort": 3400000,

View file

@ -31,7 +31,12 @@
"type": "any",
"amount": null
},
"effects": [],
"effects": [
{
"_id": "EYG5dLImk6GkmfRd",
"onSave": false
}
],
"roll": {
"type": "spellcast",
"trait": null,
@ -127,7 +132,7 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753922784451,
"modifiedTime": 1754340901029,
"modifiedTime": 1754500747453,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
},
"_id": "a8lFiKX1o8T924ze",
@ -135,6 +140,10 @@
"effects": [
{
"name": "Enraptured",
"img": "icons/magic/control/hypnosis-mesmerism-eye.webp",
"origin": "Compendium.daggerheart.domains.Item.a8lFiKX1o8T924ze",
"transfer": false,
"_id": "EYG5dLImk6GkmfRd",
"type": "base",
"system": {
"rangeDependence": {
@ -144,8 +153,6 @@
"range": "melee"
}
},
"_id": "FOQ2yqrpl2TPVxvt",
"img": "icons/magic/control/hypnosis-mesmerism-eye.webp",
"changes": [],
"disabled": false,
"duration": {
@ -157,10 +164,8 @@
"startRound": null,
"startTurn": null
},
"description": "<p><span style=\"color:rgb(239, 230, 216);font-family:Montserrat, sans-serif;font-size:14px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;orphans:2;text-align:start;text-indent:0px;text-transform:none;widows:2;word-spacing:0px;-webkit-text-stroke-width:0px;white-space:normal;background-color:rgba(24, 22, 46, 0.376);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;display:inline !important;float:none\">While </span><span style=\"box-sizing:border-box;scrollbar-width:thin;scrollbar-color:rgb(93, 20, 43) rgba(0, 0, 0, 0);color:rgb(239, 230, 216);font-family:Montserrat, sans-serif;font-size:14px;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;orphans:2;text-align:start;text-indent:0px;text-transform:none;widows:2;word-spacing:0px;-webkit-text-stroke-width:0px;white-space:normal;background-color:rgba(24, 22, 46, 0.376);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial\">Enraptured</span><span style=\"color:rgb(239, 230, 216);font-family:Montserrat, sans-serif;font-size:14px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;orphans:2;text-align:start;text-indent:0px;text-transform:none;widows:2;word-spacing:0px;-webkit-text-stroke-width:0px;white-space:normal;background-color:rgba(24, 22, 46, 0.376);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;display:inline !important;float:none\">, a targets attention is fixed on you, narrowing their field of view and drowning out any sound but your voice.</span></p>",
"origin": null,
"description": "<p>While <em>Enraptured</em>, a targets attention is fixed on you, narrowing their field of view and drowning out any sound but your voice.</p>",
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
@ -171,11 +176,11 @@
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1754106690796,
"modifiedTime": 1754340886910,
"createdTime": 1754500747406,
"modifiedTime": 1754500810477,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
},
"_key": "!items.effects!a8lFiKX1o8T924ze.FOQ2yqrpl2TPVxvt"
"_key": "!items.effects!a8lFiKX1o8T924ze.EYG5dLImk6GkmfRd"
}
],
"ownership": {

View file

@ -4,7 +4,7 @@
"type": "domainCard",
"folder": "7Cs44YADBTmmtCw6",
"system": {
"description": "<p class=\"Body-Foundation\">Make a <strong>Spellcast Roll</strong> against all adversaries within Far range. <strong>Mark any number of Stress</strong> to make shards of arcana rain down from above. Targets you succeed against take <strong>1d20+2</strong> magic damage for each Stress marked.</p>",
"description": "<p class=\"Body-Foundation\">Make a <strong>Spellcast Roll</strong> against all adversaries within Far range. <strong>Mark any number of Stress</strong> to make shards of arcana rain down from above. Targets you succeed against take <strong>1d20+2</strong> magic damage for each Stress marked.</p><p>@Template[type:emanation|range:f]</p>",
"domain": "arcana",
"recallCost": 1,
"level": 10,
@ -14,7 +14,7 @@
"type": "attack",
"_id": "xJfXJDVsBayGaqkr",
"systemPath": "actions",
"description": "<p>Make a <strong>Spellcast Roll</strong> against all adversaries within Far range. <strong>Mark any number of Stress</strong> to make shards of arcana rain down from above. Targets you succeed against take <strong>1d20+2</strong> magic damage for each Stress marked.</p>",
"description": "<p class=\"Body-Foundation\">Make a <strong>Spellcast Roll</strong> against all adversaries within Far range. <strong>Mark any number of Stress</strong> to make shards of arcana rain down from above. Targets you succeed against take <strong>1d20+2</strong> magic damage for each Stress marked.</p><p>@Template[type:emanation|range:f]</p>",
"chatDisplay": true,
"actionType": "action",
"cost": [
@ -103,8 +103,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753922784452,
"modifiedTime": 1754254370187,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
"modifiedTime": 1754501517016,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_id": "hZJp9mdkMnqKDROe",
"sort": 3400000,

View file

@ -28,8 +28,8 @@
],
"uses": {
"value": null,
"max": "",
"recovery": null
"max": "1",
"recovery": "longRest"
},
"effects": [
{
@ -56,7 +56,7 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753922784460,
"modifiedTime": 1754242182536,
"modifiedTime": 1754498928489,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_id": "SgvjJfMyubZowPxS",

View file

@ -173,7 +173,8 @@
"value": 1,
"recovery": "longRest",
"max": "1",
"icon": ""
"icon": "",
"progression": "decreasing"
}
},
"flags": {},
@ -185,8 +186,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753922784466,
"modifiedTime": 1754338717920,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
"modifiedTime": 1754499077474,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_id": "GlRm1Dxlc0Z1b04o",
"sort": 3400000,

View file

@ -14,7 +14,8 @@
"value": 0,
"recovery": "longRest",
"max": "@system.traits.presence.value",
"icon": ""
"icon": "",
"progression": "decreasing"
},
"actions": {
"5sGMd6m6Ltahit4h": {
@ -253,8 +254,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753922784473,
"modifiedTime": 1754340989544,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
"modifiedTime": 1754499693699,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_id": "cWu1o82ZF7GvnbXc",
"sort": 3400000,

View file

@ -4,7 +4,7 @@
"type": "domainCard",
"folder": "7O1tTswJMNdPgLsx",
"system": {
"description": "<p class=\"Body-Foundation\">Make a <strong>Spellcast Roll</strong> against all targets within Far range. Targets you succeed against become temporarily <em>Enraptured</em>. While <em>Enraptured</em>, a targets attention is fixed on you, narrowing their field of view and drowning out any sound but your voice. <strong>Mark a Stress</strong> to force all <em>Enraptured</em> targets to mark a Stress, ending this spell.</p>",
"description": "<p class=\"Body-Foundation\">Make a <strong>Spellcast Roll</strong> against all targets within Far range. Targets you succeed against become temporarily <em>Enraptured</em>. While <em>Enraptured</em>, a targets attention is fixed on you, narrowing their field of view and drowning out any sound but your voice. <strong>Mark a Stress</strong> to force all <em>Enraptured</em> targets to mark a Stress, ending this spell.</p><p>@Template[type:emanation|range:f]</p>",
"domain": "grace",
"recallCost": 3,
"level": 8,
@ -14,7 +14,7 @@
"type": "attack",
"_id": "r5eA3tAH7EplOQCP",
"systemPath": "actions",
"description": "<p>Make a <strong>Spellcast Roll</strong> against all targets within Far range. Targets you succeed against become temporarily <em>Enraptured</em>. While <em>Enraptured</em>, a targets attention is fixed on you, narrowing their field of view and drowning out any sound but your voice.</p>",
"description": "<p class=\"Body-Foundation\">Make a <strong>Spellcast Roll</strong> against all targets within Far range. Targets you succeed against become temporarily <em>Enraptured</em>. While <em>Enraptured</em>, a targets attention is fixed on you, narrowing their field of view and drowning out any sound but your voice. <strong>Mark a Stress</strong> to force all <em>Enraptured</em> targets to mark a Stress, ending this spell.</p><p>@Template[type:emanation|range:f]</p>",
"chatDisplay": true,
"actionType": "action",
"cost": [],
@ -132,8 +132,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753922784481,
"modifiedTime": 1754342040215,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
"modifiedTime": 1754499825008,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_id": "ubpixIgZrJXKyM3b",
"sort": 3400000,

View file

@ -338,9 +338,10 @@
"resource": {
"type": "simple",
"value": 1,
"recovery": "longRest",
"progression": "decreasing",
"max": "1",
"icon": ""
"icon": "fa-solid fa-hands-praying",
"recovery": "longRest"
}
},
"flags": {},
@ -352,8 +353,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753922784482,
"modifiedTime": 1754269394280,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
"modifiedTime": 1754498631054,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_id": "TGjR4vJVNbQRV8zr",
"sort": 3400000,

View file

@ -4,7 +4,7 @@
"type": "domainCard",
"folder": "2rqOUxEglhhPKk2j",
"system": {
"description": "<p class=\"Body-Foundation\">Once per long rest, choose any targets within Very Close range to perceive you as a nightmarish horror. The targets must succeed on a Reaction Roll (16) or become temporarily <em>Horrified</em>. While <em>Horrified</em>, theyre <em>Vulnerable</em>. Steal a number of Fear from the GM equal to the number of targets that are <em>Horrified</em> (up to the number of Fear in the GMs pool). Roll a number of <strong>d6s</strong> equal to the number of stolen Fear and deal the total damage to each <em>Horrified</em> target. Discard the stolen Fear.</p>",
"description": "<p class=\"Body-Foundation\">Once per long rest, choose any targets within Very Close range to perceive you as a nightmarish horror. The targets must succeed on a Reaction Roll (16) or become temporarily <em>Horrified</em>. While <em>Horrified</em>, theyre <em>Vulnerable</em>. Steal a number of Fear from the GM equal to the number of targets that are <em>Horrified</em> (up to the number of Fear in the GMs pool). Roll a number of <strong>d6s</strong> equal to the number of stolen Fear and deal the total damage to each <em>Horrified</em> target. Discard the stolen Fear.</p><p>@Template[type:emanation|range:vc]</p>",
"domain": "midnight",
"recallCost": 2,
"level": 9,
@ -14,14 +14,14 @@
"type": "attack",
"_id": "e4A6GQERsn08IBby",
"systemPath": "actions",
"description": "<p>Once per long rest, choose any targets within Very Close range to perceive you as a nightmarish horror. The targets must succeed on a Reaction Roll (16) or become temporarily <em>Horrified</em>. While <em>Horrified</em>, theyre <em>Vulnerable</em>. Steal a number of Fear from the GM equal to the number of targets that are <em>Horrified</em> (up to the number of Fear in the GMs pool). Roll a number of <strong>d6s</strong> equal to the number of stolen Fear and deal the total damage to each <em>Horrified</em> target. Discard the stolen Fear.</p><p></p>",
"description": "<p class=\"Body-Foundation\">Once per long rest, choose any targets within Very Close range to perceive you as a nightmarish horror. The targets must succeed on a Reaction Roll (16) or become temporarily <em>Horrified</em>. While <em>Horrified</em>, theyre <em>Vulnerable</em>. Steal a number of Fear from the GM equal to the number of targets that are <em>Horrified</em> (up to the number of Fear in the GMs pool). Roll a number of <strong>d6s</strong> equal to the number of stolen Fear and deal the total damage to each <em>Horrified</em> target. Discard the stolen Fear.</p><p>@Template[type:emanation|range:vc]</p>",
"chatDisplay": true,
"actionType": "action",
"cost": [],
"uses": {
"value": null,
"max": "",
"recovery": null
"max": "1",
"recovery": "longRest"
},
"damage": {
"parts": [],
@ -121,12 +121,7 @@
"range": ""
}
},
"resource": {
"type": "simple",
"value": 0,
"max": "",
"icon": ""
}
"resource": null
},
"flags": {},
"_stats": {
@ -137,8 +132,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753922784487,
"modifiedTime": 1754331219352,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
"modifiedTime": 1754499654051,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_id": "zcldCuqOg3dphUVI",
"sort": 3400000,

View file

@ -21,7 +21,8 @@
"uses": {
"value": null,
"max": "1",
"recovery": "longRest"
"recovery": "longRest",
"consumeOnSuccess": true
},
"damage": {
"parts": [],
@ -67,8 +68,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753922784493,
"modifiedTime": 1754340285152,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
"modifiedTime": 1754499238543,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_id": "9a6xP5pxhVvdugk9",
"sort": 3400000,

View file

@ -19,7 +19,7 @@
"actionType": "action",
"cost": [],
"uses": {
"value": 1,
"value": 0,
"max": "1",
"recovery": "shortRest",
"consumeOnSuccess": false
@ -44,8 +44,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753922784499,
"modifiedTime": 1754269219077,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
"modifiedTime": 1754498645559,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_id": "iYNVTB7uAD1FTCZu",
"sort": 3400000,

View file

@ -4,7 +4,7 @@
"type": "domainCard",
"folder": "qY4Zqc1Ch6p317uK",
"system": {
"description": "<p class=\"Body-Foundation\">Make a <strong>Spellcast Roll (15)</strong>. Once per rest on a success, create a temporary barrier of protective energy around you at Very Close range. You and all allies within the barrier when this spell is cast clear <strong>1d4</strong> Hit Points. While the barrier is up, you and all allies within have resistance to physical damage from outside the barrier.</p><p class=\"Body-Foundation\">When you move, the barrier follows you.</p>",
"description": "<p class=\"Body-Foundation\">Make a <strong>Spellcast Roll (15)</strong>. Once per rest on a success, create a temporary barrier of protective energy around you at Very Close range. You and all allies within the barrier when this spell is cast clear <strong>1d4</strong> Hit Points. While the barrier is up, you and all allies within have resistance to physical damage from outside the barrier.</p><p class=\"Body-Foundation\">When you move, the barrier follows you.</p><p>@Template[type:emanation|range:vc]</p>",
"domain": "sage",
"recallCost": 1,
"level": 8,
@ -14,7 +14,7 @@
"type": "healing",
"_id": "XdAwXl2uWNinInFe",
"systemPath": "actions",
"description": "<p class=\"Body-Foundation\">Make a <strong>Spellcast Roll (15)</strong>. Once per rest on a success, create a temporary barrier of protective energy around you at Very Close range. You and all allies within the barrier when this spell is cast clear <strong>1d4</strong> Hit Points. While the barrier is up, you and all allies within have resistance to physical damage from outside the barrier.</p><p class=\"Body-Foundation\">When you move, the barrier follows you.</p>",
"description": "<p class=\"Body-Foundation\">Make a <strong>Spellcast Roll (15)</strong>. Once per rest on a success, create a temporary barrier of protective energy around you at Very Close range. You and all allies within the barrier when this spell is cast clear <strong>1d4</strong> Hit Points. While the barrier is up, you and all allies within have resistance to physical damage from outside the barrier.</p><p class=\"Body-Foundation\">When you move, the barrier follows you.</p><p>@Template[type:emanation|range:vc]</p>",
"chatDisplay": true,
"actionType": "action",
"cost": [],
@ -22,7 +22,7 @@
"value": null,
"max": "1",
"recovery": "shortRest",
"consumeOnSuccess": false
"consumeOnSuccess": true
},
"damage": {
"parts": [
@ -93,8 +93,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753922784502,
"modifiedTime": 1754339870857,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
"modifiedTime": 1754499308449,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_id": "HtWx5IIemCoorMj2",
"sort": 3400000,

View file

@ -14,7 +14,8 @@
"value": 0,
"recovery": "longRest",
"max": "@cast",
"icon": ""
"icon": "",
"progression": "decreasing"
},
"actions": {
"udmHKUtCDClxeB4h": {
@ -214,8 +215,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753922784502,
"modifiedTime": 1754269768509,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
"modifiedTime": 1754498742091,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_id": "wUQFsRtww18naYaq",
"sort": 3400000,

View file

@ -4,7 +4,7 @@
"type": "domainCard",
"folder": "Abn46nCQst6kpGeA",
"system": {
"description": "<p class=\"Body-Foundation\">Make a <strong>Spellcast Roll</strong> against all adversaries within Very Close range. Targets you succeed against are temporarily <em>Restrained</em> as their shadow binds them in place.</p>",
"description": "<p class=\"Body-Foundation\">Make a <strong>Spellcast Roll</strong> against all adversaries within Very Close range. Targets you succeed against are temporarily <em>Restrained</em> as their shadow binds them in place.</p><p>@Template[type:emanation|range:vc]</p>",
"domain": "midnight",
"recallCost": 0,
"level": 2,
@ -14,7 +14,7 @@
"type": "attack",
"_id": "Llr9uIDUCrfsiZNn",
"systemPath": "actions",
"description": "<p><span style=\"color: rgb(239, 230, 216); font-family: Montserrat, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgba(24, 22, 46, 0.376); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none\">Make a </span><span class=\"tooltip-convert\" style=\"box-sizing: border-box; scrollbar-width: thin; scrollbar-color: rgb(93, 20, 43) rgba(0, 0, 0, 0); color: rgb(239, 230, 216); font-family: Montserrat, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgba(24, 22, 46, 0.376); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial\"><strong style=\"box-sizing: border-box; scrollbar-width: thin; scrollbar-color: rgb(93, 20, 43) rgba(0, 0, 0, 0);\">Spellcast Roll</strong></span><span style=\"color: rgb(239, 230, 216); font-family: Montserrat, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgba(24, 22, 46, 0.376); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none\"> against all adversaries within </span><span class=\"tooltip-convert\" style=\"box-sizing: border-box; scrollbar-width: thin; scrollbar-color: rgb(93, 20, 43) rgba(0, 0, 0, 0); color: rgb(239, 230, 216); font-family: Montserrat, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgba(24, 22, 46, 0.376); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial\">Very Close</span><span style=\"color: rgb(239, 230, 216); font-family: Montserrat, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgba(24, 22, 46, 0.376); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none\"> range. Targets you succeed against are temporarily </span><span class=\"tooltip-convert\" style=\"box-sizing: border-box; scrollbar-width: thin; scrollbar-color: rgb(93, 20, 43) rgba(0, 0, 0, 0); color: rgb(239, 230, 216); font-family: Montserrat, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgba(24, 22, 46, 0.376); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial\"><em style=\"box-sizing: border-box; scrollbar-width: thin; scrollbar-color: rgb(93, 20, 43) rgba(0, 0, 0, 0);\">Restrained</em> </span><span style=\"color: rgb(239, 230, 216); font-family: Montserrat, sans-serif; font-size: 14px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; white-space: normal; background-color: rgba(24, 22, 46, 0.376); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; display: inline !important; float: none\">as their shadow binds them in place.</span></p>",
"description": "<p class=\"Body-Foundation\">Make a <strong>Spellcast Roll</strong> against all adversaries within Very Close range. Targets you succeed against are temporarily <em>Restrained</em> as their shadow binds them in place.</p><p>@Template[type:emanation|range:vc]</p>",
"chatDisplay": true,
"actionType": "action",
"cost": [],
@ -72,8 +72,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753922784512,
"modifiedTime": 1754330643864,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
"modifiedTime": 1754499502570,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_id": "kguhWlidhxe2GbT0",
"sort": 3400000,

View file

@ -9,7 +9,31 @@
"recallCost": 0,
"level": 6,
"type": "spell",
"actions": {}
"actions": {
"MjSx44ovuKBGVKGs": {
"type": "effect",
"_id": "MjSx44ovuKBGVKGs",
"systemPath": "actions",
"description": "<p class=\"Body-Foundation\">Once per rest, take on the Stress from a willing creature within Melee range. The target describes what intimate knowledge or emotions telepathically leak from their mind in this moment between you. Transfer any number of their marked Stress to you, then gain a Hope for each Stress transferred.</p>",
"chatDisplay": true,
"actionType": "action",
"cost": [],
"uses": {
"value": null,
"max": "1",
"recovery": "shortRest",
"consumeOnSuccess": false
},
"effects": [],
"target": {
"type": "any",
"amount": null
},
"name": "Take Stress",
"img": "systems/daggerheart/assets/icons/domains/domain-card/grace.png",
"range": ""
}
}
},
"flags": {},
"_stats": {
@ -20,8 +44,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753922784515,
"modifiedTime": 1754327488946,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
"modifiedTime": 1754499760780,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_id": "8nRle10pw1HO8QVu",
"sort": 3400000,

View file

@ -29,8 +29,8 @@
],
"uses": {
"value": null,
"max": "",
"recovery": null,
"max": "1",
"recovery": "shortRest",
"consumeOnSuccess": false
},
"effects": [
@ -58,8 +58,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753922784519,
"modifiedTime": 1754269704377,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
"modifiedTime": 1754498725946,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_id": "U1uWJE94HZVudujz",
"sort": 3400000,

View file

@ -10,9 +10,9 @@
"level": 9,
"type": "ability",
"actions": {
"B4vyaTibK0GiRBCW": {
"type": "effect",
"_id": "B4vyaTibK0GiRBCW",
"yjEcSlzsWGX79gpB": {
"type": "attack",
"_id": "yjEcSlzsWGX79gpB",
"systemPath": "actions",
"description": "<p class=\"Body-Foundation\"><strong>Spend a Hope</strong> and make an attack against all adversaries within your weapons range. Once per long rest, on a success against any targets, add up the damage dealt, then redistribute that damage however you wish between the targets you succeeded against. When you deal damage to a target, roll an additional damage die and add its result to the damage you deal to that target.</p>",
"chatDisplay": true,
@ -29,15 +29,39 @@
],
"uses": {
"value": null,
"max": "",
"recovery": null,
"consumeOnSuccess": false
"max": "1",
"recovery": "longRest",
"consumeOnSuccess": true
},
"damage": {
"parts": [],
"includeBase": false
},
"effects": [],
"target": {
"type": "any",
"amount": null
},
"effects": [],
"roll": {
"type": "attack",
"trait": null,
"difficulty": null,
"bonus": null,
"advState": "neutral",
"diceRolling": {
"multiplier": "prof",
"flatMultiplier": 1,
"dice": "d6",
"compare": null,
"treshold": null
},
"useDefault": false
},
"save": {
"trait": null,
"difficulty": null,
"damageMod": "none"
},
"name": "Spend Hope",
"img": "icons/skills/melee/strike-sword-steel-yellow.webp",
"range": ""
@ -53,7 +77,7 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753922784522,
"modifiedTime": 1754252510860,
"modifiedTime": 1754501075258,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_id": "TYKfM3H9vBXyWiH4",

View file

@ -11,10 +11,11 @@
"type": "ability",
"resource": {
"type": "simple",
"value": 1,
"max": "",
"value": 0,
"max": "max(@system.traits.knowledge.value, 1)",
"icon": "fa-solid fa-bullseye",
"recovery": "longRest"
"recovery": "longRest",
"progression": "decreasing"
},
"actions": {
"jTC0GbsBpGmaQLi7": {
@ -60,7 +61,7 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753922784523,
"modifiedTime": 1754249661976,
"modifiedTime": 1754501630846,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_id": "5b1awkgTmMp3FVrm",

View file

@ -108,16 +108,17 @@
"cost": [
{
"scalable": false,
"key": "n0P3VS1WfxvmXbB6",
"key": "hitPoints",
"value": 1,
"keyIsID": true,
"step": null
"keyIsID": false,
"step": null,
"consumeOnSuccess": false
}
],
"uses": {
"value": null,
"max": "",
"recovery": null
"max": "1",
"recovery": "shortRest"
},
"effects": [],
"target": {
@ -129,13 +130,7 @@
"range": ""
}
},
"resource": {
"type": "simple",
"value": 1,
"recovery": "shortRest",
"max": "1",
"icon": ""
}
"resource": null
},
"flags": {},
"_stats": {
@ -146,8 +141,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753922784534,
"modifiedTime": 1754338673637,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
"modifiedTime": 1754499113867,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_id": "n0P3VS1WfxvmXbB6",
"sort": 3400000,

View file

@ -14,7 +14,8 @@
"value": 0,
"max": "@cast",
"icon": "",
"recovery": "session"
"recovery": "session",
"progression": "decreasing"
},
"actions": {
"MWvrKuwejWcQm7N1": {
@ -143,8 +144,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753922784537,
"modifiedTime": 1754253370819,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
"modifiedTime": 1754501257508,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_id": "o62i0QdbUDIiAhSq",
"sort": 3400000,

View file

@ -20,7 +20,7 @@
"type": "effect",
"_id": "nYu6LRNVDKfWUJhx",
"systemPath": "actions",
"description": "",
"description": "<p class=\"Body-Foundation\">Once per long rest, <strong>mark a Stress</strong> to channel the natural world around you and enhance yourself. Describe how your appearance changes, then place a <strong>d6</strong> on this card with the 1 value facing up.</p><p class=\"Body-Foundation\">While the Wild Surge Die is active, you add its value to every action roll you make. After you add its value to a roll, increase the Wild Surge Dies value by one. When the dies value would exceed 6 or you take a rest, this form drops and you must <strong>mark an additional Stress</strong>.</p>",
"chatDisplay": true,
"actionType": "action",
"cost": [
@ -34,8 +34,8 @@
],
"uses": {
"value": null,
"max": "",
"recovery": null
"max": "1",
"recovery": "longRest"
},
"effects": [],
"target": {
@ -57,8 +57,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753922784547,
"modifiedTime": 1754340095871,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
"modifiedTime": 1754499199811,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_id": "DjnKlZQYaWdQGKcK",
"sort": 3400000,

View file

@ -4,7 +4,7 @@
"type": "domainCard",
"folder": "OwsbTSWzKq2WJmQN",
"system": {
"description": "<p class=\"Body-Foundation\">Make a <strong>Spellcast Roll (16)</strong>. 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 <strong>d6</strong> on this card with the 1 value facing up. When an ally in this zone takes damage, they reduce it by the dies value. You then increase the dies value by one. When the dies value would exceed 6, this effect ends.</p><p></p><p><span style=\"color:oklab(0.952331 0.000418991 -0.00125992);font-family:'gg mono', 'Source Code Pro', Consolas, 'Andale Mono WT', 'Andale Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Liberation Mono', 'Nimbus Mono L', Monaco, 'Courier New', Courier, monospace;font-size:13.6px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;orphans:2;text-align:left;text-indent:0px;text-transform:none;widows:2;word-spacing:0px;-webkit-text-stroke-width:0px;white-space:pre-wrap;background-color:oklab(0.57738 0.0140701 -0.208587 / 0.0784314);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;display:inline !important;float:none\">@Template[type:emanation|range:vc]</span></p>",
"description": "<p class=\"Body-Foundation\">Make a <strong>Spellcast Roll (16)</strong>. 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 <strong>d6</strong> on this card with the 1 value facing up. When an ally in this zone takes damage, they reduce it by the dies value. You then increase the dies value by one. When the dies value would exceed 6, this effect ends.</p><p><span style=\"color:oklab(0.952331 0.000418991 -0.00125992);font-family:'gg mono', 'Source Code Pro', Consolas, 'Andale Mono WT', 'Andale Mono', 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono', 'Bitstream Vera Sans Mono', 'Liberation Mono', 'Nimbus Mono L', Monaco, 'Courier New', Courier, monospace;font-size:13.6px;font-style:normal;font-variant-ligatures:normal;font-variant-caps:normal;font-weight:400;letter-spacing:normal;orphans:2;text-align:left;text-indent:0px;text-transform:none;widows:2;word-spacing:0px;-webkit-text-stroke-width:0px;white-space:pre-wrap;background-color:oklab(0.57738 0.0140701 -0.208587 / 0.0784314);text-decoration-thickness:initial;text-decoration-style:initial;text-decoration-color:initial;display:inline !important;float:none\">@Template[type:emanation|range:vc]</span></p>",
"domain": "splendor",
"recallCost": 2,
"level": 6,
@ -58,13 +58,7 @@
"range": "far"
}
},
"resource": {
"type": "simple",
"value": 0,
"max": "6",
"icon": "",
"recovery": "longRest"
}
"resource": null
},
"flags": {},
"_stats": {
@ -75,8 +69,8 @@
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753922784549,
"modifiedTime": 1754269795161,
"lastModifiedBy": "Q9NoTaEarn3VMS6Z"
"modifiedTime": 1754498786877,
"lastModifiedBy": "MQSznptE5yLT7kj8"
},
"_id": "lOZaRb4fCVgQsWB5",
"sort": 3400000,

View file

@ -6,7 +6,7 @@
"sorting": "a",
"_id": "pPzU9WOQNv3ckO1w",
"description": "",
"sort": 1000000,
"sort": 2000000,
"flags": {},
"_stats": {
"compendiumSource": null,
@ -15,8 +15,8 @@
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "YNJ4HgHtFrTI89mx",
"modifiedTime": 1752681545540
"lastModifiedBy": "MQSznptE5yLT7kj8",
"modifiedTime": 1754499337594
},
"_key": "!folders!pPzU9WOQNv3ckO1w"
}

View file

@ -6,7 +6,7 @@
"sorting": "a",
"_id": "EJoXzO85rG5EiZsh",
"description": "",
"sort": 100000,
"sort": 1100000,
"flags": {},
"_stats": {
"compendiumSource": null,
@ -15,8 +15,8 @@
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "YNJ4HgHtFrTI89mx",
"modifiedTime": 1752681545540
"lastModifiedBy": "MQSznptE5yLT7kj8",
"modifiedTime": 1754499330683
},
"_key": "!folders!EJoXzO85rG5EiZsh"
}

View file

@ -6,7 +6,7 @@
"sorting": "a",
"_id": "xZrCYAd05ayNu1yW",
"description": "",
"sort": 200000,
"sort": 1200000,
"flags": {},
"_stats": {
"compendiumSource": null,
@ -15,8 +15,8 @@
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "YNJ4HgHtFrTI89mx",
"modifiedTime": 1752681545540
"lastModifiedBy": "MQSznptE5yLT7kj8",
"modifiedTime": 1754499331503
},
"_key": "!folders!xZrCYAd05ayNu1yW"
}

View file

@ -6,7 +6,7 @@
"sorting": "a",
"_id": "uXGugK72AffddFdH",
"description": "",
"sort": 300000,
"sort": 1300000,
"flags": {},
"_stats": {
"compendiumSource": null,
@ -15,8 +15,8 @@
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "YNJ4HgHtFrTI89mx",
"modifiedTime": 1752681545540
"lastModifiedBy": "MQSznptE5yLT7kj8",
"modifiedTime": 1754499332151
},
"_key": "!folders!uXGugK72AffddFdH"
}

View file

@ -6,7 +6,7 @@
"sorting": "a",
"_id": "BJIiOIWAQUz5zuqo",
"description": "",
"sort": 400000,
"sort": 1400000,
"flags": {},
"_stats": {
"compendiumSource": null,
@ -15,8 +15,8 @@
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "YNJ4HgHtFrTI89mx",
"modifiedTime": 1752681545540
"lastModifiedBy": "MQSznptE5yLT7kj8",
"modifiedTime": 1754499332813
},
"_key": "!folders!BJIiOIWAQUz5zuqo"
}

View file

@ -6,7 +6,7 @@
"sorting": "a",
"_id": "ZZHIbaynhzVArA1p",
"description": "",
"sort": 500000,
"sort": 1500000,
"flags": {},
"_stats": {
"compendiumSource": null,
@ -15,8 +15,8 @@
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "YNJ4HgHtFrTI89mx",
"modifiedTime": 1752681545540
"lastModifiedBy": "MQSznptE5yLT7kj8",
"modifiedTime": 1754499333460
},
"_key": "!folders!ZZHIbaynhzVArA1p"
}

View file

@ -6,7 +6,7 @@
"sorting": "a",
"_id": "u5Lq2kfC8LlDAGDC",
"description": "",
"sort": 550000,
"sort": 1600000,
"flags": {},
"_stats": {
"compendiumSource": null,
@ -15,8 +15,8 @@
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "YNJ4HgHtFrTI89mx",
"modifiedTime": 1752681547474
"lastModifiedBy": "MQSznptE5yLT7kj8",
"modifiedTime": 1754499335115
},
"_key": "!folders!u5Lq2kfC8LlDAGDC"
}

View file

@ -6,7 +6,7 @@
"sorting": "a",
"_id": "gEVGjjPrjqxxZkb5",
"description": "",
"sort": 575000,
"sort": 1700000,
"flags": {},
"_stats": {
"compendiumSource": null,
@ -15,8 +15,8 @@
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "YNJ4HgHtFrTI89mx",
"modifiedTime": 1752681548701
"lastModifiedBy": "MQSznptE5yLT7kj8",
"modifiedTime": 1754499335821
},
"_key": "!folders!gEVGjjPrjqxxZkb5"
}

View file

@ -6,7 +6,7 @@
"sorting": "a",
"_id": "qY4Zqc1Ch6p317uK",
"description": "",
"sort": 587500,
"sort": 1800000,
"flags": {},
"_stats": {
"compendiumSource": null,
@ -15,8 +15,8 @@
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "YNJ4HgHtFrTI89mx",
"modifiedTime": 1752681549490
"lastModifiedBy": "MQSznptE5yLT7kj8",
"modifiedTime": 1754499336356
},
"_key": "!folders!qY4Zqc1Ch6p317uK"
}

Some files were not shown because too many files have changed in this diff Show more