Merge branch 'main' into feature/chat-message-styles

This commit is contained in:
WBHarry 2025-07-28 20:54:02 +02:00
commit 094e0740dd
270 changed files with 13798 additions and 982 deletions

View file

@ -19,7 +19,6 @@ import {
} from './module/systemRegistration/_module.mjs'; } from './module/systemRegistration/_module.mjs';
import { placeables } from './module/canvas/_module.mjs'; import { placeables } from './module/canvas/_module.mjs';
import { registerRollDiceHooks } from './module/dice/dhRoll.mjs'; import { registerRollDiceHooks } from './module/dice/dhRoll.mjs';
import { registerDHActorHooks } from './module/documents/actor.mjs';
import './node_modules/@yaireo/tagify/dist/tagify.css'; import './node_modules/@yaireo/tagify/dist/tagify.css';
Hooks.once('init', () => { Hooks.once('init', () => {
@ -80,8 +79,8 @@ Hooks.once('init', () => {
Items.registerSheet(SYSTEM.id, applications.sheets.items.Subclass, { types: ['subclass'], makeDefault: true }); Items.registerSheet(SYSTEM.id, applications.sheets.items.Subclass, { types: ['subclass'], makeDefault: true });
Items.registerSheet(SYSTEM.id, applications.sheets.items.Feature, { types: ['feature'], makeDefault: true }); Items.registerSheet(SYSTEM.id, applications.sheets.items.Feature, { types: ['feature'], makeDefault: true });
Items.registerSheet(SYSTEM.id, applications.sheets.items.DomainCard, { types: ['domainCard'], makeDefault: true }); Items.registerSheet(SYSTEM.id, applications.sheets.items.DomainCard, { types: ['domainCard'], makeDefault: true });
Items.registerSheet(SYSTEM.id, applications.sheets.items.Miscellaneous, { Items.registerSheet(SYSTEM.id, applications.sheets.items.Loot, {
types: ['miscellaneous'], types: ['loot'],
makeDefault: true makeDefault: true
}); });
Items.registerSheet(SYSTEM.id, applications.sheets.items.Consumable, { types: ['consumable'], makeDefault: true }); Items.registerSheet(SYSTEM.id, applications.sheets.items.Consumable, { types: ['consumable'], makeDefault: true });
@ -169,7 +168,7 @@ Hooks.on('ready', () => {
registerCountdownHooks(); registerCountdownHooks();
socketRegistration.registerSocketHooks(); socketRegistration.registerSocketHooks();
registerRollDiceHooks(); registerRollDiceHooks();
registerDHActorHooks(); socketRegistration.registerUserQueries();
}); });
Hooks.once('dicesoniceready', () => {}); Hooks.once('dicesoniceready', () => {});
@ -198,13 +197,13 @@ Hooks.on('chatMessage', (_, message) => {
const traitValue = rollCommand.trait?.toLowerCase(); const traitValue = rollCommand.trait?.toLowerCase();
const advantage = rollCommand.advantage const advantage = rollCommand.advantage
? CONFIG.DH.ACTIONS.advandtageState.advantage.value ? CONFIG.DH.ACTIONS.advantageState.advantage.value
: rollCommand.disadvantage : rollCommand.disadvantage
? CONFIG.DH.ACTIONS.advandtageState.disadvantage.value ? CONFIG.DH.ACTIONS.advantageState.disadvantage.value
: undefined; : undefined;
const difficulty = rollCommand.difficulty; const difficulty = rollCommand.difficulty;
const target = getCommandTarget(); const target = getCommandTarget({ allowNull: true });
const title = traitValue const title = traitValue
? game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', { ? game.i18n.format('DAGGERHEART.UI.Chat.dualityRoll.abilityCheckTitle', {
ability: game.i18n.localize(SYSTEM.ACTOR.abilities[traitValue].label) ability: game.i18n.localize(SYSTEM.ACTOR.abilities[traitValue].label)

View file

@ -8,7 +8,7 @@
"feature": "Feature", "feature": "Feature",
"domainCard": "Domain Card", "domainCard": "Domain Card",
"consumable": "Consumable", "consumable": "Consumable",
"miscellaneous": "Miscellaneous", "loot": "Loot",
"weapon": "Weapon", "weapon": "Weapon",
"armor": "Armor", "armor": "Armor",
"beastform": "Beastform" "beastform": "Beastform"
@ -28,25 +28,6 @@
}, },
"DAGGERHEART": { "DAGGERHEART": {
"ACTIONS": { "ACTIONS": {
"Config": {
"beastform": {
"exact": "Beastform Max Tier",
"exactHint": "The Character's Tier is used if empty",
"label": "Beastform"
}
},
"Settings": {
"attackBonus": "Attack Bonus",
"attackName": "Attack Name",
"includeBase": { "label": "Include Item Damage" },
"multiplier": "Multiplier",
"resultBased": {
"label": "Formula based on Hope/Fear result."
},
"applyTo": {
"label": "Targeted Resource"
}
},
"TYPES": { "TYPES": {
"attack": { "attack": {
"name": "Attack", "name": "Attack",
@ -76,6 +57,35 @@
"name": "Summon", "name": "Summon",
"tooltip": "Create tokens in the scene." "tooltip": "Create tokens in the scene."
} }
},
"Config": {
"beastform": {
"exact": "Beastform Max Tier",
"exactHint": "The Character's Tier is used if empty",
"label": "Beastform"
},
"displayInChat": "Display in chat"
},
"RollField": {
"diceRolling": {
"compare": "Should be",
"dice": "Dice Type",
"flatMultiplier": "Flat Multiplier",
"multiplier": "Dice Number",
"threshold": "Threshold"
}
},
"Settings": {
"attackBonus": "Attack Bonus",
"attackName": "Attack Name",
"includeBase": { "label": "Include Item Damage" },
"multiplier": "Multiplier",
"resultBased": {
"label": "Formula based on Hope/Fear result."
},
"applyTo": {
"label": "Targeted Resource"
}
} }
}, },
"ACTORS": { "ACTORS": {
@ -283,6 +293,7 @@
"usedMarks": "Used Marks" "usedMarks": "Used Marks"
}, },
"DeathMove": { "DeathMove": {
"selectMove": "Select Move",
"takeMove": "Take Death Move", "takeMove": "Take Death Move",
"title": "{actor} - Death Move" "title": "{actor} - Death Move"
}, },
@ -442,6 +453,9 @@
"title": "Ownership Selection - {name}", "title": "Ownership Selection - {name}",
"default": "Default Ownership" "default": "Default Ownership"
}, },
"ReactionRoll": {
"title": "Reaction Roll: {trait}"
},
"ResourceDice": { "ResourceDice": {
"title": "{name} Resource", "title": "{name} Resource",
"rerollDice": "Reroll Dice" "rerollDice": "Reroll Dice"
@ -703,7 +717,7 @@
"abbreviation": "HO" "abbreviation": "HO"
}, },
"armorStack": { "armorStack": {
"name": "Armor Stack", "name": "Armor Slot",
"abbreviation": "AS" "abbreviation": "AS"
}, },
"fear": { "fear": {
@ -1019,6 +1033,9 @@
}, },
"damageRoll": { "damageRoll": {
"name": "Damage Roll" "name": "Damage Roll"
},
"healingRoll": {
"name": "Healing Roll"
} }
}, },
"Duration": { "Duration": {
@ -1107,10 +1124,30 @@
}, },
"DamageResistance": { "DamageResistance": {
"none": "None", "none": "None",
"resistance": "Resistance", "physicalResistance": {
"immunity": "Immunity", "label": "Damage Resistance: Physical",
"physicalReduction": "Physical Damage Reduction", "hint": "Physical Damage is halved if this is set to 1"
"magicalReduction": "Magical Damage Reduction" },
"magicalResistance": {
"label": "Damage Resistance: Magical",
"hint": "Magical Damage is halved if this is set to 1"
},
"physicalImmunity": {
"label": "Damage Immunity: Physical",
"hint": "Immune to Physical Damage if this is set to 1"
},
"magicalImmunity": {
"label": "Damage Immunity: Magical",
"hint": "Immune to Magical Damage if this is set to 1"
},
"physicalReduction": {
"label": "Damage Reduction: Physical",
"hint": "Physical Damage is reduced by the amount set here"
},
"magicalReduction": {
"label": "Damage Reduction: Magical",
"hint": "Magical Damage is reduced by the amount set here"
}
}, },
"DamageThresholds": { "DamageThresholds": {
"title": "Damage Thresholds", "title": "Damage Thresholds",
@ -1243,7 +1280,14 @@
}, },
"attack": { "attack": {
"damage": { "damage": {
"value": { "label": "Base Attack: Damage" } "dice": {
"label": "Base Attack: Damage Dice Index",
"hint": "Index for the damage dice used on the basic attack. 0=d4, 1=d6, 2=d8, 3=d10, 4=d12, 5=d20"
},
"bonus": { "label": "Base Attack: Damage Bonus" }
},
"roll": {
"trait": { "label": "Base Attack: Trait" }
} }
} }
}, },
@ -1562,7 +1606,11 @@
"foreground": "Foreground", "foreground": "Foreground",
"background": "Background", "background": "Background",
"outline": "Outline", "outline": "Outline",
"edge": "Edge" "edge": "Edge",
"texture": "Texture",
"colorset": "Theme",
"material": "Material",
"system": "Dice Preset"
} }
}, },
"variantRules": { "variantRules": {
@ -1590,6 +1638,10 @@
"hint": "test" "hint": "test"
} }
} }
},
"ResetSettings": {
"resetConfirmationTitle": "Reset Settings",
"resetConfirmationText": "Are you sure you want to reset the {settings}?"
} }
}, },
"UI": { "UI": {
@ -1627,8 +1679,9 @@
"subclassFeatureTitle": "Subclass Feature" "subclassFeatureTitle": "Subclass Feature"
}, },
"healingRoll": { "healingRoll": {
"title": "Heal - {healing}", "title": "Heal - {damage}",
"heal": "Heal" "heal": "Heal",
"applyHealing": "Apply Healing"
}, },
"reroll": { "reroll": {
"confirmTitle": "Reroll Dice", "confirmTitle": "Reroll Dice",

View file

@ -123,13 +123,15 @@ export default class BeastformDialog extends HandlebarsApplicationMixin(Applicat
); );
const compendiumBeastforms = await game.packs.get(`daggerheart.beastforms`)?.getDocuments(); const compendiumBeastforms = await game.packs.get(`daggerheart.beastforms`)?.getDocuments();
const beastformTiers = [...(compendiumBeastforms ? compendiumBeastforms : []), ...game.items].reduce( const beastformTiers = [...game.items, ...(compendiumBeastforms ? compendiumBeastforms : [])].reduce(
(acc, x) => { (acc, x) => {
const tier = CONFIG.DH.GENERAL.tiers[x.system.tier]; const tier = CONFIG.DH.GENERAL.tiers[x.system.tier];
if (x.type !== 'beastform' || tier.id > this.configData.tierLimit) return acc; if (x.type !== 'beastform' || tier.id > this.configData.tierLimit) return acc;
if (!acc[tier.id]) acc[tier.id] = { label: game.i18n.localize(tier.label), values: {} }; if (!acc[tier.id]) acc[tier.id] = { label: game.i18n.localize(tier.label), values: {} };
if (Object.values(acc[tier.id].values).find(existing => existing.value.name === x.name)) return acc;
acc[tier.id].values[x.uuid] = { acc[tier.id].values[x.uuid] = {
selected: this.selected?.uuid == x.uuid, selected: this.selected?.uuid == x.uuid,
value: x, value: x,

View file

@ -91,9 +91,10 @@ export default class D20RollDialog extends HandlebarsApplicationMixin(Applicatio
context.roll = this.roll; context.roll = this.roll;
context.rollType = this.roll?.constructor.name; context.rollType = this.roll?.constructor.name;
context.rallyDie = this.roll.rallyChoices; context.rallyDie = this.roll.rallyChoices;
context.experiences = Object.keys(this.config.data.experiences).map(id => ({ const experiences = this.config.data?.experiences || {};
context.experiences = Object.keys(experiences).map(id => ({
id, id,
...this.config.data.experiences[id] ...experiences[id]
})); }));
context.selectedExperiences = this.config.experiences; context.selectedExperiences = this.config.experiences;
context.advantage = this.config.roll?.advantage; context.advantage = this.config.roll?.advantage;

View file

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

View file

@ -1,6 +1,6 @@
import { damageKeyToNumber, getDamageLabel } from '../../helpers/utils.mjs'; import { damageKeyToNumber, getDamageLabel } from '../../helpers/utils.mjs';
const { DialogV2, ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
export default class DamageReductionDialog extends HandlebarsApplicationMixin(ApplicationV2) { export default class DamageReductionDialog extends HandlebarsApplicationMixin(ApplicationV2) {
constructor(resolve, reject, actor, damage, damageType) { constructor(resolve, reject, actor, damage, damageType) {
@ -53,10 +53,6 @@ export default class DamageReductionDialog extends HandlebarsApplicationMixin(Ap
); );
} }
get title() {
return game.i18n.localize('DAGGERHEART.APPLICATIONS.DamageReduction.title');
}
static DEFAULT_OPTIONS = { static DEFAULT_OPTIONS = {
tag: 'form', tag: 'form',
classes: ['daggerheart', 'views', 'damage-reduction'], classes: ['daggerheart', 'views', 'damage-reduction'],

View file

@ -1,3 +1,5 @@
// TO DELETE ?
const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api; const { ApplicationV2, HandlebarsApplicationMixin } = foundry.applications.api;
export default class DamageSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) { export default class DamageSelectionDialog extends HandlebarsApplicationMixin(ApplicationV2) {

View file

@ -13,8 +13,9 @@ export default class DhpDeathMove extends HandlebarsApplicationMixin(Application
} }
static DEFAULT_OPTIONS = { static DEFAULT_OPTIONS = {
classes: ['daggerheart', 'views', 'death-move'], classes: ['daggerheart', 'dh-style', 'dialog', 'views', 'death-move'],
position: { width: 800, height: 'auto' }, position: { width: 'auto', height: 'auto' },
window: { icon: 'fa-solid fa-skull' },
actions: { actions: {
selectMove: this.selectMove, selectMove: this.selectMove,
takeMove: this.takeMove takeMove: this.takeMove
@ -32,6 +33,7 @@ export default class DhpDeathMove extends HandlebarsApplicationMixin(Application
const context = await super._prepareContext(_options); const context = await super._prepareContext(_options);
context.selectedMove = this.selectedMove; context.selectedMove = this.selectedMove;
context.options = CONFIG.DH.GENERAL.deathMoves; context.options = CONFIG.DH.GENERAL.deathMoves;
context.title = game.i18n.localize('DAGGERHEART.APPLICATIONS.DeathMove.takeMove');
return context; return context;
} }

View file

@ -66,12 +66,9 @@ export default class DHTokenHUD extends foundry.applications.hud.TokenHUD {
if (!status) continue; if (!status) continue;
if (status._id) { if (status._id) {
if (status._id !== effect.id) continue; if (status._id !== effect.id) continue;
} else {
if (effect.statuses.size !== 1) continue;
} }
status.isActive = true; status.isActive = true;
if (effect.getFlag('core', 'overlay')) status.isOverlay = true; if (effect.getFlag('core', 'overlay')) status.isOverlay = true;
break;
} }
} }

View file

@ -43,7 +43,7 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
{ id: 'hope', label: 'DAGGERHEART.GENERAL.hope' }, { id: 'hope', label: 'DAGGERHEART.GENERAL.hope' },
{ id: 'fear', label: 'DAGGERHEART.GENERAL.fear' }, { id: 'fear', label: 'DAGGERHEART.GENERAL.fear' },
{ id: 'advantage', label: 'DAGGERHEART.GENERAL.Advantage.full' }, { id: 'advantage', label: 'DAGGERHEART.GENERAL.Advantage.full' },
{ id: 'disadvantage', label: 'DAGGERHEART.GENERAL.Advantage.full' } { id: 'disadvantage', label: 'DAGGERHEART.GENERAL.Disadvantage.full' }
], ],
initial: 'hope' initial: 'hope'
} }
@ -59,8 +59,20 @@ export default class DHAppearanceSettings extends HandlebarsApplicationMixin(App
const context = await super._prepareContext(_options); const context = await super._prepareContext(_options);
context.settingFields = this.settings; context.settingFields = this.settings;
context.diceSoNiceTextures = game.dice3d?.exports?.TEXTURELIST ?? {}; context.showDiceSoNice = game.modules.get('dice-so-nice')?.active;
context.diceSoNiceColorsets = game.dice3d?.exports?.COLORSETS ?? {}; if (game.dice3d) {
context.diceSoNiceTextures = game.dice3d.exports.TEXTURELIST;
context.diceSoNiceColorsets = game.dice3d.exports.COLORSETS;
context.diceSoNiceMaterials = Object.keys(game.dice3d.DiceFactory.material_options).map(key => ({
key: key,
name: `DICESONICE.Material${key.capitalize()}`
}));
context.diceSoNiceSystems = [];
for (const [key, system] of game.dice3d.DiceFactory.systems.entries()) {
context.diceSoNiceSystems.push({ key, name: system.name });
}
}
context.diceTab = { context.diceTab = {
key: this.tabGroups.diceSoNice, key: this.tabGroups.diceSoNice,
source: this.settings._source.diceSoNice[this.tabGroups.diceSoNice], source: this.settings._source.diceSoNice[this.tabGroups.diceSoNice],

View file

@ -136,10 +136,14 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
...move, ...move,
name: game.i18n.localize(move.name), name: game.i18n.localize(move.name),
description: game.i18n.localize(move.description), description: game.i18n.localize(move.description),
actions: move.actions.map(action => ({ actions: move.actions.reduce((acc, key) => {
...action, const action = move.actions[key];
name: game.i18n.localize(action.name) acc[key] = {
})) ...action,
name: game.i18n.localize(action.name)
};
return acc;
}, {})
}; };
return acc; return acc;
@ -165,8 +169,18 @@ export default class DhHomebrewSettings extends HandlebarsApplicationMixin(Appli
} }
static async reset() { static async reset() {
const confirmed = await foundry.applications.api.DialogV2.confirm({
window: {
title: game.i18n.format('DAGGERHEART.SETTINGS.ResetSettings.resetConfirmationTitle')
},
content: game.i18n.format('DAGGERHEART.SETTINGS.ResetSettings.resetConfirmationText', {
settings: game.i18n.localize('DAGGERHEART.SETTINGS.Menu.homebrew.name')
})
});
if (!confirmed) return;
const resetSettings = new DhHomebrew(); const resetSettings = new DhHomebrew();
let localizedSettings = this.localizeObject(resetSettings); let localizedSettings = this.localizeObject(resetSettings.toObject());
this.settings.updateSource(localizedSettings); this.settings.updateSource(localizedSettings);
this.render(); this.render();
} }

View file

@ -110,6 +110,7 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
context.costOptions = this.getCostOptions(); context.costOptions = this.getCostOptions();
context.disableOption = this.disableOption.bind(this); context.disableOption = this.disableOption.bind(this);
context.isNPC = this.action.actor?.isNPC; context.isNPC = this.action.actor?.isNPC;
context.baseSaveDifficulty = this.action.actor?.baseSaveDifficulty;
context.hasRoll = this.action.hasRoll; context.hasRoll = this.action.hasRoll;
const settingsTiers = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers; const settingsTiers = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.LevelTiers).tiers;
@ -117,7 +118,6 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
{ key: 1, label: game.i18n.localize('DAGGERHEART.GENERAL.Tiers.1') }, { key: 1, label: game.i18n.localize('DAGGERHEART.GENERAL.Tiers.1') },
...Object.values(settingsTiers).map(x => ({ key: x.tier, label: x.name })) ...Object.values(settingsTiers).map(x => ({ key: x.tier, label: x.name }))
]; ];
return context; return context;
} }

View file

@ -99,7 +99,17 @@ export default class DhActiveEffectConfig extends foundry.applications.sheets.Ac
async _preparePartContext(partId, context) { async _preparePartContext(partId, context) {
const partContext = await super._preparePartContext(partId, context); const partContext = await super._preparePartContext(partId, context);
switch (partId) { switch (partId) {
case 'changes': case 'details':
const useGeneric = game.settings.get(
CONFIG.DH.id,
CONFIG.DH.SETTINGS.gameSettings.appearance
).showGenericStatusEffects;
if (!useGeneric) {
partContext.statuses = Object.values(CONFIG.DH.GENERAL.conditions).map(status => ({
value: status.id,
label: game.i18n.localize(status.name)
}));
}
break; break;
} }

View file

@ -75,7 +75,7 @@ export default class DHEnvironmentSettings extends DHBaseActorSettings {
* @returns * @returns
*/ */
static async #deleteAdversary(_event, target) { static async #deleteAdversary(_event, target) {
const doc = getDocFromElement(target); const doc = await getDocFromElement(target);
const { category } = target.dataset; const { category } = target.dataset;
const path = `system.potentialAdversaries.${category}.adversaries`; const path = `system.potentialAdversaries.${category}.adversaries`;

View file

@ -4,7 +4,7 @@ import { abilities } from '../../../config/actorConfig.mjs';
import DhCharacterlevelUp from '../../levelup/characterLevelup.mjs'; import DhCharacterlevelUp from '../../levelup/characterLevelup.mjs';
import DhCharacterCreation from '../../characterCreation/characterCreation.mjs'; import DhCharacterCreation from '../../characterCreation/characterCreation.mjs';
import FilterMenu from '../../ux/filter-menu.mjs'; import FilterMenu from '../../ux/filter-menu.mjs';
import { getDocFromElement, itemAbleRollParse } from '../../../helpers/utils.mjs'; import { getDocFromElement, getDocFromElementSync } from '../../../helpers/utils.mjs';
/**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */ /**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */
@ -55,7 +55,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
}, },
{ {
handler: CharacterSheet.#getItemContextOptions, handler: CharacterSheet.#getItemContextOptions,
selector: '[data-item-uuid][data-type="consumable"], [data-item-uuid][data-type="miscellaneous"]', selector: '[data-item-uuid][data-type="consumable"], [data-item-uuid][data-type="loot"]',
options: { options: {
parentClassHooks: false, parentClassHooks: false,
fixed: true fixed: true
@ -258,19 +258,27 @@ export default class CharacterSheet extends DHBaseActorSheet {
{ {
name: 'toLoadout', name: 'toLoadout',
icon: 'fa-solid fa-arrow-up', icon: 'fa-solid fa-arrow-up',
condition: target => getDocFromElement(target).system.inVault, condition: target => {
callback: target => { const doc = getDocFromElementSync(target);
const doc = getDocFromElement(target), return doc && system.inVault;
actorLoadout = doc.actor.system.loadoutSlot; },
if(actorLoadout.available) return doc.update({ 'system.inVault': false }); callback: async target => {
ui.notifications.warn(game.i18n.format('DAGGERHEART.UI.Notifications.loadoutMaxReached', { max: actorLoadout.max })) const doc = await getDocFromElement(target);
const actorLoadout = doc.actor.system.loadoutSlot;
if (actorLoadout.available) return doc.update({ 'system.inVault': false });
ui.notifications.warn(
game.i18n.format('DAGGERHEART.UI.Notifications.loadoutMaxReached', { max: actorLoadout.max })
);
} }
}, },
{ {
name: 'toVault', name: 'toVault',
icon: 'fa-solid fa-arrow-down', icon: 'fa-solid fa-arrow-down',
condition: target => !getDocFromElement(target).system.inVault, condition: target => {
callback: target => getDocFromElement(target).update({ 'system.inVault': true }) const doc = getDocFromElementSync(target);
return doc && !doc.system.inVault;
},
callback: async target => (await getDocFromElement(target)).update({ 'system.inVault': true })
} }
].map(option => ({ ].map(option => ({
...option, ...option,
@ -292,13 +300,19 @@ export default class CharacterSheet extends DHBaseActorSheet {
{ {
name: 'equip', name: 'equip',
icon: 'fa-solid fa-hands', icon: 'fa-solid fa-hands',
condition: target => !getDocFromElement(target).system.equipped, condition: target => {
const doc = getDocFromElementSync(target);
return doc && !doc.system.equipped;
},
callback: (target, event) => CharacterSheet.#toggleEquipItem.call(this, event, target) callback: (target, event) => CharacterSheet.#toggleEquipItem.call(this, event, target)
}, },
{ {
name: 'unequip', name: 'unequip',
icon: 'fa-solid fa-hands', icon: 'fa-solid fa-hands',
condition: target => getDocFromElement(target).system.equipped, condition: target => {
const doc = getDocFromElementSync(target);
return doc && doc.system.equipped;
},
callback: (target, event) => CharacterSheet.#toggleEquipItem.call(this, event, target) callback: (target, event) => CharacterSheet.#toggleEquipItem.call(this, event, target)
} }
].map(option => ({ ].map(option => ({
@ -311,7 +325,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
} }
/** /**
* Get the set of ContextMenu options for Consumable and Miscellaneous. * Get the set of ContextMenu options for Consumable and Loot.
* @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance * @returns {import('@client/applications/ux/context-menu.mjs').ContextMenuEntry[]} - The Array of context options passed to the ContextMenu instance
* @this {CharacterSheet} * @this {CharacterSheet}
* @protected * @protected
@ -407,11 +421,11 @@ export default class CharacterSheet extends DHBaseActorSheet {
* @param {HTMLElement} html The container to filter items from. * @param {HTMLElement} html The container to filter items from.
* @protected * @protected
*/ */
_onSearchFilterInventory(event, query, rgx, html) { async _onSearchFilterInventory(_event, query, rgx, html) {
this.#filteredItems.inventory.search.clear(); this.#filteredItems.inventory.search.clear();
for (const li of html.querySelectorAll('.inventory-item')) { for (const li of html.querySelectorAll('.inventory-item')) {
const item = getDocFromElement(li); const item = await getDocFromElement(li);
const matchesSearch = !query || foundry.applications.ux.SearchFilter.testQuery(rgx, item.name); const matchesSearch = !query || foundry.applications.ux.SearchFilter.testQuery(rgx, item.name);
if (matchesSearch) this.#filteredItems.inventory.search.add(item.id); if (matchesSearch) this.#filteredItems.inventory.search.add(item.id);
const { menu } = this.#filteredItems.inventory; const { menu } = this.#filteredItems.inventory;
@ -427,11 +441,11 @@ export default class CharacterSheet extends DHBaseActorSheet {
* @param {HTMLElement} html The container to filter items from. * @param {HTMLElement} html The container to filter items from.
* @protected * @protected
*/ */
_onSearchFilterCard(event, query, rgx, html) { async _onSearchFilterCard(_event, query, rgx, html) {
this.#filteredItems.loadout.search.clear(); this.#filteredItems.loadout.search.clear();
for (const li of html.querySelectorAll('.items-list .inventory-item, .card-list .card-item')) { for (const li of html.querySelectorAll('.items-list .inventory-item, .card-list .card-item')) {
const item = getDocFromElement(li); const item = await getDocFromElement(li);
const matchesSearch = !query || foundry.applications.ux.SearchFilter.testQuery(rgx, item.name); const matchesSearch = !query || foundry.applications.ux.SearchFilter.testQuery(rgx, item.name);
if (matchesSearch) this.#filteredItems.loadout.search.add(item.id); if (matchesSearch) this.#filteredItems.loadout.search.add(item.id);
const { menu } = this.#filteredItems.loadout; const { menu } = this.#filteredItems.loadout;
@ -478,11 +492,11 @@ export default class CharacterSheet extends DHBaseActorSheet {
* @param {HTMLElement} html * @param {HTMLElement} html
* @param {import('../ux/filter-menu.mjs').FilterItem[]} filters * @param {import('../ux/filter-menu.mjs').FilterItem[]} filters
*/ */
_onMenuFilterInventory(event, html, filters) { async _onMenuFilterInventory(_event, html, filters) {
this.#filteredItems.inventory.menu.clear(); this.#filteredItems.inventory.menu.clear();
for (const li of html.querySelectorAll('.inventory-item')) { for (const li of html.querySelectorAll('.inventory-item')) {
const item = getDocFromElement(li); const item = await getDocFromElement(li);
const matchesMenu = const matchesMenu =
filters.length === 0 || filters.some(f => foundry.applications.ux.SearchFilter.evaluateFilter(item, f)); filters.length === 0 || filters.some(f => foundry.applications.ux.SearchFilter.evaluateFilter(item, f));
@ -499,11 +513,11 @@ export default class CharacterSheet extends DHBaseActorSheet {
* @param {HTMLElement} html * @param {HTMLElement} html
* @param {import('../ux/filter-menu.mjs').FilterItem[]} filters * @param {import('../ux/filter-menu.mjs').FilterItem[]} filters
*/ */
_onMenuFilterLoadout(event, html, filters) { async _onMenuFilterLoadout(_event, html, filters) {
this.#filteredItems.loadout.menu.clear(); this.#filteredItems.loadout.menu.clear();
for (const li of html.querySelectorAll('.items-list .inventory-item, .card-list .card-item')) { for (const li of html.querySelectorAll('.items-list .inventory-item, .card-list .card-item')) {
const item = getDocFromElement(li); const item = await getDocFromElement(li);
const matchesMenu = const matchesMenu =
filters.length === 0 || filters.some(f => foundry.applications.ux.SearchFilter.evaluateFilter(item, f)); filters.length === 0 || filters.some(f => foundry.applications.ux.SearchFilter.evaluateFilter(item, f));
@ -519,7 +533,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
/* -------------------------------------------- */ /* -------------------------------------------- */
async updateItemResource(event) { async updateItemResource(event) {
const item = getDocFromElement(event.currentTarget); const item = await getDocFromElement(event.currentTarget);
if (!item) return; if (!item) return;
const max = event.currentTarget.max ? Number(event.currentTarget.max) : null; const max = event.currentTarget.max ? Number(event.currentTarget.max) : null;
@ -529,7 +543,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
} }
async updateItemQuantity(event) { async updateItemQuantity(event) {
const item = getDocFromElement(event.currentTarget); const item = await getDocFromElement(event.currentTarget);
if (!item) return; if (!item) return;
await item.update({ 'system.quantity': event.currentTarget.value }); await item.update({ 'system.quantity': event.currentTarget.value });
@ -609,7 +623,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
* @type {ApplicationClickAction} * @type {ApplicationClickAction}
*/ */
static async #toggleEquipItem(_event, button) { static async #toggleEquipItem(_event, button) {
const item = getDocFromElement(button); const item = await getDocFromElement(button);
if (!item) return; if (!item) return;
if (item.system.equipped) { if (item.system.equipped) {
await item.update({ 'system.equipped': false }); await item.update({ 'system.equipped': false });
@ -664,7 +678,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
* @type {ApplicationClickAction} * @type {ApplicationClickAction}
*/ */
static async #toggleVault(_event, button) { static async #toggleVault(_event, button) {
const doc = getDocFromElement(button); const doc = await getDocFromElement(button);
await doc?.update({ 'system.inVault': !doc.system.inVault }); await doc?.update({ 'system.inVault': !doc.system.inVault });
} }
@ -673,7 +687,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
* @type {ApplicationClickAction} * @type {ApplicationClickAction}
*/ */
static async #toggleResourceDice(event, target) { static async #toggleResourceDice(event, target) {
const item = getDocFromElement(target); const item = await getDocFromElement(target);
const { dice } = event.target.closest('.item-resource').dataset; const { dice } = event.target.closest('.item-resource').dataset;
const diceState = item.system.resource.diceStates[dice]; const diceState = item.system.resource.diceStates[dice];
@ -688,7 +702,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
* @type {ApplicationClickAction} * @type {ApplicationClickAction}
*/ */
static async #handleResourceDice(_, target) { static async #handleResourceDice(_, target) {
const item = getDocFromElement(target); const item = await getDocFromElement(target);
if (!item) return; if (!item) return;
const rollValues = await game.system.api.applications.dialogs.ResourceDiceDialog.create(item, this.document); const rollValues = await game.system.api.applications.dialogs.ResourceDiceDialog.create(item, this.document);
@ -709,7 +723,7 @@ export default class CharacterSheet extends DHBaseActorSheet {
} }
async _onDragStart(event) { async _onDragStart(event) {
const item = getDocFromElement(event.target); const item = await getDocFromElement(event.target);
const dragData = { const dragData = {
type: item.documentName, type: item.documentName,

View file

@ -1,6 +1,5 @@
const { HandlebarsApplicationMixin } = foundry.applications.api; const { HandlebarsApplicationMixin } = foundry.applications.api;
import { getDocFromElement, tagifyElement } from '../../../helpers/utils.mjs'; import { getDocFromElement, getDocFromElementSync, tagifyElement } from '../../../helpers/utils.mjs';
import DHActionConfig from '../../sheets-configs/action-config.mjs';
/** /**
* @typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction * @typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction
@ -124,7 +123,14 @@ export default function DHApplicationMixin(Base) {
/**@inheritdoc */ /**@inheritdoc */
async _onFirstRender(context, options) { async _onFirstRender(context, options) {
await super._onFirstRender(context, options); await super._onFirstRender(context, options);
this.relatedDocs.filter(doc => doc).map(doc => (doc.apps[this.id] = this));
const docs = [];
for (var docData of this.relatedDocs) {
const doc = await foundry.utils.fromUuid(docData.uuid);
docs.push(doc);
}
docs.filter(doc => doc).map(doc => (doc.apps[this.id] = this));
if (!!this.options.contextMenus.length) this._createContextMenus(); if (!!this.options.contextMenus.length) this._createContextMenus();
} }
@ -259,14 +265,20 @@ export default function DHApplicationMixin(Base) {
{ {
name: 'disableEffect', name: 'disableEffect',
icon: 'fa-solid fa-lightbulb', icon: 'fa-solid fa-lightbulb',
condition: target => !getDocFromElement(target).disabled, condition: target => {
callback: target => getDocFromElement(target).update({ disabled: true }) const doc = getDocFromElementSync(target);
return doc && !doc.disabled;
},
callback: async target => (await getDocFromElement(target)).update({ disabled: true })
}, },
{ {
name: 'enableEffect', name: 'enableEffect',
icon: 'fa-regular fa-lightbulb', icon: 'fa-regular fa-lightbulb',
condition: target => getDocFromElement(target).disabled, condition: target => {
callback: target => getDocFromElement(target).update({ disabled: false }) const doc = getDocFromElementSync(target);
return doc && doc.disabled;
},
callback: async target => (await getDocFromElement(target)).update({ disabled: false })
} }
].map(option => ({ ].map(option => ({
...option, ...option,
@ -299,10 +311,10 @@ export default function DHApplicationMixin(Base) {
name: 'CONTROLS.CommonEdit', name: 'CONTROLS.CommonEdit',
icon: 'fa-solid fa-pen-to-square', icon: 'fa-solid fa-pen-to-square',
condition: target => { condition: target => {
const doc = getDocFromElement(target); const doc = getDocFromElementSync(target);
return !doc.hasOwnProperty('systemPath') || doc.inCollection; return !doc || !doc.hasOwnProperty('systemPath') || doc.inCollection;
}, },
callback: target => getDocFromElement(target).sheet.render({ force: true }) callback: async target => (await getDocFromElement(target)).sheet.render({ force: true })
} }
]; ];
@ -311,25 +323,25 @@ export default function DHApplicationMixin(Base) {
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.useItem', name: 'DAGGERHEART.APPLICATIONS.ContextMenu.useItem',
icon: 'fa-solid fa-burst', icon: 'fa-solid fa-burst',
condition: target => { condition: target => {
const doc = getDocFromElement(target); const doc = getDocFromElementSync(target);
return !(doc.type === 'domainCard' && doc.system.inVault) return doc && !(doc.type === 'domainCard' && doc.system.inVault);
}, },
callback: (target, event) => getDocFromElement(target).use(event) callback: async (target, event) => (await getDocFromElement(target)).use(event)
}); });
if (toChat) if (toChat)
options.unshift({ options.unshift({
name: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat', name: 'DAGGERHEART.APPLICATIONS.ContextMenu.sendToChat',
icon: 'fa-solid fa-message', icon: 'fa-solid fa-message',
callback: target => getDocFromElement(target).toChat(this.document.id) callback: async target => (await getDocFromElement(target)).toChat(this.document.id)
}); });
if (deletable) if (deletable)
options.push({ options.push({
name: 'CONTROLS.CommonDelete', name: 'CONTROLS.CommonDelete',
icon: 'fa-solid fa-trash', icon: 'fa-solid fa-trash',
callback: (target, event) => { callback: async (target, event) => {
const doc = getDocFromElement(target); const doc = await getDocFromElement(target);
if (event.shiftKey) return doc.delete(); if (event.shiftKey) return doc.delete();
else return doc.deleteDialog(); else return doc.deleteDialog();
} }
@ -371,7 +383,7 @@ export default function DHApplicationMixin(Base) {
if (!actionId && !itemUuid) return; if (!actionId && !itemUuid) return;
const doc = itemUuid const doc = itemUuid
? getDocFromElement(extensibleElement) ? await getDocFromElement(extensibleElement)
: this.document.system.attack?.id === actionId : this.document.system.attack?.id === actionId
? this.document.system.attack ? this.document.system.attack
: this.document.system.actions?.get(actionId); : this.document.system.actions?.get(actionId);
@ -429,8 +441,8 @@ export default function DHApplicationMixin(Base) {
* Renders an embedded document. * Renders an embedded document.
* @type {ApplicationClickAction} * @type {ApplicationClickAction}
*/ */
static #editDoc(_event, target) { static async #editDoc(_event, target) {
const doc = getDocFromElement(target); const doc = await getDocFromElement(target);
if (doc) return doc.sheet.render({ force: true }); if (doc) return doc.sheet.render({ force: true });
} }
@ -439,7 +451,7 @@ export default function DHApplicationMixin(Base) {
* @type {ApplicationClickAction} * @type {ApplicationClickAction}
*/ */
static async #deleteDoc(event, target) { static async #deleteDoc(event, target) {
const doc = getDocFromElement(target); const doc = await getDocFromElement(target);
if (doc) { if (doc) {
if (event.shiftKey) return doc.delete(); if (event.shiftKey) return doc.delete();
else return await doc.deleteDialog(); else return await doc.deleteDialog();
@ -451,7 +463,7 @@ export default function DHApplicationMixin(Base) {
* @type {ApplicationClickAction} * @type {ApplicationClickAction}
*/ */
static async #toChat(_event, target) { static async #toChat(_event, target) {
let doc = getDocFromElement(target); let doc = await getDocFromElement(target);
return doc.toChat(this.document.id); return doc.toChat(this.document.id);
} }
@ -460,7 +472,7 @@ export default function DHApplicationMixin(Base) {
* @type {ApplicationClickAction} * @type {ApplicationClickAction}
*/ */
static async #useItem(event, target) { static async #useItem(event, target) {
let doc = getDocFromElement(target); let doc = await getDocFromElement(target);
await doc.use(event); await doc.use(event);
} }
@ -469,7 +481,7 @@ export default function DHApplicationMixin(Base) {
* @type {ApplicationClickAction} * @type {ApplicationClickAction}
*/ */
static async #toggleEffect(_, target) { static async #toggleEffect(_, target) {
const doc = getDocFromElement(target); const doc = await getDocFromElement(target);
await doc.update({ disabled: !doc.disabled }); await doc.update({ disabled: !doc.disabled });
} }
@ -492,7 +504,7 @@ export default function DHApplicationMixin(Base) {
const t = extensible?.classList.toggle('extended'); const t = extensible?.classList.toggle('extended');
const descriptionElement = extensible?.querySelector('.invetory-description'); const descriptionElement = extensible?.querySelector('.invetory-description');
if (t && !!descriptionElement) this.#prepareInventoryDescription(extensible, descriptionElement); if (t && !!descriptionElement) await this.#prepareInventoryDescription(extensible, descriptionElement);
} }
} }

View file

@ -117,7 +117,7 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
name: 'CONTROLS.CommonDelete', name: 'CONTROLS.CommonDelete',
icon: '<i class="fa-solid fa-trash"></i>', icon: '<i class="fa-solid fa-trash"></i>',
callback: async target => { callback: async target => {
const feature = getDocFromElement(target); const feature = await getDocFromElement(target);
if (!feature) return; if (!feature) return;
const confirmed = await foundry.applications.api.DialogV2.confirm({ const confirmed = await foundry.applications.api.DialogV2.confirm({
window: { window: {
@ -168,7 +168,7 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
*/ */
static async #deleteFeature(_, element) { static async #deleteFeature(_, element) {
const target = element.closest('[data-item-uuid]'); const target = element.closest('[data-item-uuid]');
const feature = getDocFromElement(target); const feature = await getDocFromElement(target);
if (!feature) return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing')); if (!feature) return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing'));
await this.document.update({ await this.document.update({
'system.features': this.document.system.features 'system.features': this.document.system.features
@ -249,12 +249,20 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
const target = event.target.closest('fieldset.drop-section'); const target = event.target.closest('fieldset.drop-section');
const item = await fromUuid(data.uuid); const item = await fromUuid(data.uuid);
if (item?.type === 'feature') { if (item?.type === 'feature') {
await this.document.update({ if (target.dataset.type) {
'system.features': [...this.document.system.features, { type: target.dataset.type, item }].map(x => ({ await this.document.update({
...x, 'system.features': [...this.document.system.features, { type: target.dataset.type, item }].map(
item: x.item?.uuid x => ({
})) ...x,
}); item: x.item?.uuid
})
)
});
} else {
await this.document.update({
'system.features': [...this.document.system.features, item].map(x => x.uuid)
});
}
} }
} }
} }

View file

@ -6,6 +6,6 @@ export { default as Community } from './community.mjs';
export { default as Consumable } from './consumable.mjs'; export { default as Consumable } from './consumable.mjs';
export { default as DomainCard } from './domainCard.mjs'; export { default as DomainCard } from './domainCard.mjs';
export { default as Feature } from './feature.mjs'; export { default as Feature } from './feature.mjs';
export { default as Miscellaneous } from './miscellaneous.mjs'; export { default as Loot } from './loot.mjs';
export { default as Subclass } from './subclass.mjs'; export { default as Subclass } from './subclass.mjs';
export { default as Weapon } from './weapon.mjs'; export { default as Weapon } from './weapon.mjs';

View file

@ -81,6 +81,7 @@ export default class BeastformSheet extends DHBaseItemSheet {
case 'effects': case 'effects':
context.effects.actives = context.effects.actives.map(effect => { context.effects.actives = context.effects.actives.map(effect => {
const data = effect.toObject(); const data = effect.toObject();
data.uuid = effect.uuid;
data.id = effect.id; data.id = effect.id;
if (effect.type === 'beastform') data.mandatory = true; if (effect.type === 'beastform') data.mandatory = true;

View file

@ -132,14 +132,14 @@ export default class ClassSheet extends DHBaseItemSheet {
}); });
} }
} else if (target.classList.contains('choice-a-section')) { } else if (target.classList.contains('choice-a-section')) {
if (item.type === 'miscellaneous' || item.type === 'consumable') { if (item.type === 'loot' || item.type === 'consumable') {
const filteredChoiceA = this.document.system.inventory.choiceA; const filteredChoiceA = this.document.system.inventory.choiceA;
if (filteredChoiceA.length < 2) if (filteredChoiceA.length < 2)
await this.document.update({ await this.document.update({
'system.inventory.choiceA': [...filteredChoiceA.map(x => x.uuid), item.uuid] 'system.inventory.choiceA': [...filteredChoiceA.map(x => x.uuid), item.uuid]
}); });
} }
} else if (item.type === 'miscellaneous') { } else if (item.type === 'loot') {
if (target.classList.contains('take-section')) { if (target.classList.contains('take-section')) {
const filteredTake = this.document.system.inventory.take.filter(x => x); const filteredTake = this.document.system.inventory.take.filter(x => x);
if (filteredTake.length < 3) if (filteredTake.length < 3)

View file

@ -1,15 +1,15 @@
import DHBaseItemSheet from '../api/base-item.mjs'; import DHBaseItemSheet from '../api/base-item.mjs';
export default class MiscellaneousSheet extends DHBaseItemSheet { export default class LootSheet extends DHBaseItemSheet {
/**@inheritdoc */ /**@inheritdoc */
static DEFAULT_OPTIONS = { static DEFAULT_OPTIONS = {
classes: ['miscellaneous'], classes: ['loot'],
position: { width: 550 } position: { width: 550 }
}; };
/**@override */ /**@override */
static PARTS = { static PARTS = {
header: { template: 'systems/daggerheart/templates/sheets/items/miscellaneous/header.hbs' }, header: { template: 'systems/daggerheart/templates/sheets/items/loot/header.hbs' },
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
description: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-description.hbs' }, description: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-description.hbs' },
actions: { actions: {
@ -17,7 +17,7 @@ export default class MiscellaneousSheet extends DHBaseItemSheet {
scrollable: ['.actions'] scrollable: ['.actions']
}, },
settings: { settings: {
template: 'systems/daggerheart/templates/sheets/items/miscellaneous/settings.hbs', template: 'systems/daggerheart/templates/sheets/items/loot/settings.hbs',
scrollable: ['.settings'] scrollable: ['.settings']
}, },
effects: { effects: {

View file

@ -1,3 +1,5 @@
import { emitAsGM, GMUpdateEvent } from "../../systemRegistration/socket.mjs";
export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLog { export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLog {
constructor(options) { constructor(options) {
super(options); super(options);
@ -17,9 +19,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
html.querySelectorAll('.duality-action-damage').forEach(element => html.querySelectorAll('.duality-action-damage').forEach(element =>
element.addEventListener('click', event => this.onRollDamage(event, data.message)) element.addEventListener('click', event => this.onRollDamage(event, data.message))
); );
html.querySelectorAll('.duality-action-healing').forEach(element =>
element.addEventListener('click', event => this.onRollHealing(event, data.message))
);
html.querySelectorAll('.target-save-container').forEach(element => html.querySelectorAll('.target-save-container').forEach(element =>
element.addEventListener('click', event => this.onRollSave(event, data.message)) element.addEventListener('click', event => this.onRollSave(event, data.message))
); );
@ -66,8 +65,8 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
super.close(options); super.close(options);
} }
async getActor(id) { async getActor(uuid) {
return await fromUuid(id); return await foundry.utils.fromUuid(uuid);
} }
getAction(actor, itemId, actionId) { getAction(actor, itemId, actionId) {
@ -92,17 +91,6 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
} }
} }
async onRollHealing(event, message) {
event.stopPropagation();
const actor = await this.getActor(message.system.source.actor);
if (!actor || !game.user.isGM) return true;
if (message.system.source.item && message.system.source.action) {
const action = this.getAction(actor, message.system.source.item, message.system.source.action);
if (!action || !action?.rollHealing) return;
await action.rollHealing(event, message);
}
}
async onRollSave(event, message) { async onRollSave(event, message) {
event.stopPropagation(); event.stopPropagation();
const actor = await this.getActor(message.system.source.actor), const actor = await this.getActor(message.system.source.actor),
@ -112,17 +100,41 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
if (message.system.source.item && message.system.source.action) { if (message.system.source.item && message.system.source.action) {
const action = this.getAction(actor, message.system.source.item, message.system.source.action); const action = this.getAction(actor, message.system.source.item, message.system.source.action);
if (!action || !action?.hasSave) return; if (!action || !action?.hasSave) return;
action.rollSave(token, event, message); action.rollSave(token.actor, event, message).then(result => emitAsGM(
GMUpdateEvent.UpdateSaveMessage,
action.updateSaveMessage.bind(action, result, message, token.id),
{
action: action.uuid,
message: message._id,
token: token.id,
result
}
));
} }
} }
onRollAllSave(event, _message) { async onRollAllSave(event, message) {
event.stopPropagation(); event.stopPropagation();
if(!game.user.isGM) return;
const targets = event.target.parentElement.querySelectorAll( const targets = event.target.parentElement.querySelectorAll(
'.target-section > [data-token] .target-save-container' '.target-section > [data-token] .target-save-container'
); );
targets.forEach(el => { const actor = await this.getActor(message.system.source.actor),
el.dispatchEvent(new PointerEvent('click', { shiftKey: true })); action = this.getAction(actor, message.system.source.item, message.system.source.action);
targets.forEach(async el => {
const tokenId = el.closest('[data-token]')?.dataset.token,
token = game.canvas.tokens.get(tokenId);
if(!token.actor) return;
if(game.user === token.actor.owner)
el.dispatchEvent(new PointerEvent('click', { shiftKey: true }));
else {
token.actor.owner.query('reactionRoll', {
actionId: action.uuid,
actorId: token.actor.uuid,
event,
message
}).then(result => action.updateSaveMessage(result, message, token.id));
}
}); });
} }
@ -160,7 +172,7 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
return { return {
isHit, isHit,
targets: isHit targets: isHit
? message.system.targets.filter(t => t.hit === true).map(target => game.canvas.tokens.get(target.id)) ? message.system.targets.filter(t => t.hit === true).map(target => game.canvas.tokens.documentCollection.find(t => t.actor.uuid === target.actorId))
: Array.from(game.user.targets) : Array.from(game.user.targets)
}; };
} }
@ -222,19 +234,10 @@ export default class DhpChatLog extends foundry.applications.sidebar.tabs.ChatLo
}); });
} }
target.actor.takeDamage(damages); if(message.system.hasHealing)
} target.actor.takeHealing(damages);
} else
target.actor.takeDamage(damages);
async onHealing(event, message) {
event.stopPropagation();
const targets = Array.from(game.user.targets);
if (targets.length === 0)
return ui.notifications.info(game.i18n.localize('DAGGERHEART.UI.Notifications.noTargetsSelected'));
for (var target of targets) {
target.actor.takeHealing(message.system.roll);
} }
} }

View file

@ -10,8 +10,28 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
this.effects.overlay = null; this.effects.overlay = null;
// Categorize effects // Categorize effects
const activeEffects = this.actor ? Array.from(this.actor.effects).filter(x => !x.disabled) : []; const statusMap = new Map(foundry.CONFIG.statusEffects.map(status => [status.id, status]));
const overlayEffect = activeEffects.findLast(e => e.img && e.getFlag('core', 'overlay')); const activeEffects = (this.actor ? this.actor.effects.filter(x => !x.disabled) : []).reduce((acc, effect) => {
acc.push(effect);
const currentStatusActiveEffects = acc.filter(
x => x.statuses.size === 1 && x.name === game.i18n.localize(statusMap.get(x.statuses.first()).name)
);
for (var status of effect.statuses) {
if (!currentStatusActiveEffects.find(x => x.statuses.includes(status))) {
const statusData = statusMap.get(status);
acc.push({
name: game.i18n.localize(statusData.name),
statuses: [status],
img: statusData.icon,
tint: effect.tint
});
}
}
return acc;
}, []);
const overlayEffect = activeEffects.findLast(e => e.img && e.getFlag?.('core', 'overlay'));
// Draw effects // Draw effects
const promises = []; const promises = [];

View file

@ -108,7 +108,7 @@ export const diceCompare = {
} }
}; };
export const advandtageState = { export const advantageState = {
disadvantage: { disadvantage: {
label: 'DAGGERHEART.GENERAL.Disadvantage.full', label: 'DAGGERHEART.GENERAL.Disadvantage.full',
value: -1 value: -1

View file

@ -141,9 +141,11 @@ export const defaultRestOptions = {
actions: { actions: {
tendToWounds: { tendToWounds: {
type: 'healing', type: 'healing',
systemPath: 'restMoves.shortRest.moves.tendToWounds.actions',
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.tendToWounds.name'), name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.tendToWounds.name'),
img: 'icons/magic/life/cross-worn-green.webp', img: 'icons/magic/life/cross-worn-green.webp',
actionType: 'action', actionType: 'action',
chatDisplay: false,
healing: { healing: {
applyTo: healingTypes.hitPoints.id, applyTo: healingTypes.hitPoints.id,
value: { value: {
@ -165,9 +167,11 @@ export const defaultRestOptions = {
actions: { actions: {
clearStress: { clearStress: {
type: 'healing', type: 'healing',
systemPath: 'restMoves.shortRest.moves.clearStress.actions',
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.clearStress.name'), name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.clearStress.name'),
img: 'icons/magic/perception/eye-ringed-green.webp', img: 'icons/magic/perception/eye-ringed-green.webp',
actionType: 'action', actionType: 'action',
chatDisplay: false,
healing: { healing: {
applyTo: healingTypes.stress.id, applyTo: healingTypes.stress.id,
value: { value: {
@ -189,9 +193,11 @@ export const defaultRestOptions = {
actions: { actions: {
repairArmor: { repairArmor: {
type: 'healing', type: 'healing',
systemPath: 'restMoves.shortRest.moves.repairArmor.actions',
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.repairArmor.name'), name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.shortRest.repairArmor.name'),
img: 'icons/skills/trades/smithing-anvil-silver-red.webp', img: 'icons/skills/trades/smithing-anvil-silver-red.webp',
actionType: 'action', actionType: 'action',
chatDisplay: false,
healing: { healing: {
applyTo: healingTypes.armorStack.id, applyTo: healingTypes.armorStack.id,
value: { value: {
@ -223,9 +229,11 @@ export const defaultRestOptions = {
actions: { actions: {
tendToWounds: { tendToWounds: {
type: 'healing', type: 'healing',
systemPath: 'restMoves.longRest.moves.tendToWounds.actions',
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.tendToWounds.name'), name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.tendToWounds.name'),
img: 'icons/magic/life/cross-worn-green.webp', img: 'icons/magic/life/cross-worn-green.webp',
actionType: 'action', actionType: 'action',
chatDisplay: false,
healing: { healing: {
applyTo: healingTypes.hitPoints.id, applyTo: healingTypes.hitPoints.id,
value: { value: {
@ -247,9 +255,11 @@ export const defaultRestOptions = {
actions: { actions: {
clearStress: { clearStress: {
type: 'healing', type: 'healing',
systemPath: 'restMoves.longRest.moves.clearStress.actions',
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.clearStress.name'), name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.clearStress.name'),
img: 'icons/magic/perception/eye-ringed-green.webp', img: 'icons/magic/perception/eye-ringed-green.webp',
actionType: 'action', actionType: 'action',
chatDisplay: false,
healing: { healing: {
applyTo: healingTypes.stress.id, applyTo: healingTypes.stress.id,
value: { value: {
@ -271,9 +281,11 @@ export const defaultRestOptions = {
actions: { actions: {
repairArmor: { repairArmor: {
type: 'healing', type: 'healing',
systemPath: 'restMoves.longRest.moves.repairArmor.actions',
name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.repairArmor.name'), name: game.i18n.localize('DAGGERHEART.APPLICATIONS.Downtime.longRest.repairArmor.name'),
img: 'icons/skills/trades/smithing-anvil-silver-red.webp', img: 'icons/skills/trades/smithing-anvil-silver-red.webp',
actionType: 'action', actionType: 'action',
chatDisplay: false,
healing: { healing: {
applyTo: healingTypes.armorStack.id, applyTo: healingTypes.armorStack.id,
value: { value: {
@ -310,18 +322,21 @@ export const deathMoves = {
id: 'avoidDeath', id: 'avoidDeath',
name: 'DAGGERHEART.CONFIG.DeathMoves.avoidDeath.name', name: 'DAGGERHEART.CONFIG.DeathMoves.avoidDeath.name',
img: 'icons/magic/time/hourglass-yellow-green.webp', img: 'icons/magic/time/hourglass-yellow-green.webp',
icon: 'fa-person-running',
description: 'DAGGERHEART.CONFIG.DeathMoves.avoidDeath.description' description: 'DAGGERHEART.CONFIG.DeathMoves.avoidDeath.description'
}, },
riskItAll: { riskItAll: {
id: 'riskItAll', id: 'riskItAll',
name: 'DAGGERHEART.CONFIG.DeathMoves.riskItAll.name', name: 'DAGGERHEART.CONFIG.DeathMoves.riskItAll.name',
img: 'icons/sundries/gaming/dice-pair-white-green.webp', img: 'icons/sundries/gaming/dice-pair-white-green.webp',
icon: 'fa-dice',
description: 'DAGGERHEART.CONFIG.DeathMoves.riskItAll.description' description: 'DAGGERHEART.CONFIG.DeathMoves.riskItAll.description'
}, },
blazeOfGlory: { blazeOfGlory: {
id: 'blazeOfGlory', id: 'blazeOfGlory',
name: 'DAGGERHEART.CONFIG.DeathMoves.blazeOfGlory.name', name: 'DAGGERHEART.CONFIG.DeathMoves.blazeOfGlory.name',
img: 'icons/magic/life/heart-cross-strong-flame-purple-orange.webp', img: 'icons/magic/life/heart-cross-strong-flame-purple-orange.webp',
icon: 'fa-burst',
description: 'DAGGERHEART.CONFIG.DeathMoves.blazeOfGlory.description' description: 'DAGGERHEART.CONFIG.DeathMoves.blazeOfGlory.description'
} }
}; };
@ -369,42 +384,28 @@ export const diceSetNumbers = {
flat: 'Flat' flat: 'Flat'
}; };
export const getDiceSoNicePresets = () => { export const getDiceSoNicePresets = async (hopeFaces, fearFaces, advantageFaces = 'd6', disadvantageFaces = 'd6') => {
const { diceSoNice } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance); const { diceSoNice } = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance);
const getPreset = async (type, faces) => {
const system = game.dice3d.DiceFactory.systems.get(type.system).dice.get(faces);
if (!system.modelLoaded) {
await system.loadModel(game.dice3d.DiceFactory.loaderGLTF);
}
return {
modelFile: system.modelFile,
appearance: {
...system.appearance,
...type
}
};
};
return { return {
hope: { hope: await getPreset(diceSoNice.hope, hopeFaces),
...diceSoNice.hope, fear: await getPreset(diceSoNice.fear, fearFaces),
colorset: 'inspired', advantage: await getPreset(diceSoNice.advantage, advantageFaces),
texture: 'bloodmoon', disadvantage: await getPreset(diceSoNice.disadvantage, disadvantageFaces)
material: 'metal',
font: 'Arial Black',
system: 'standard'
},
fear: {
...diceSoNice.fear,
colorset: 'bloodmoon',
texture: 'bloodmoon',
material: 'metal',
font: 'Arial Black',
system: 'standard'
},
advantage: {
...diceSoNice.advantage,
colorset: 'bloodmoon',
texture: 'bloodmoon',
material: 'metal',
font: 'Arial Black',
system: 'standard'
},
disadvantage: {
...diceSoNice.disadvantage,
colorset: 'bloodmoon',
texture: 'bloodmoon',
material: 'metal',
font: 'Arial Black',
system: 'standard'
}
}; };
}; };
@ -441,7 +442,7 @@ export const abilityCosts = {
}, },
armor: { armor: {
id: 'armor', id: 'armor',
label: 'Armor Stack', label: 'Armor Slot',
group: 'TYPES.Actor.character' group: 'TYPES.Actor.character'
}, },
fear: { fear: {

View file

@ -1301,9 +1301,9 @@ export const featureTypes = {
id: 'consumable', id: 'consumable',
label: 'TYPES.Item.consumable' label: 'TYPES.Item.consumable'
}, },
miscellaneous: { loot: {
id: 'miscellaneous', id: 'loot',
label: 'TYPES.Item.miscellaneous' label: 'TYPES.Item.loot'
}, },
beastform: { beastform: {
if: 'beastform', if: 'beastform',

View file

@ -27,7 +27,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
name: new fields.StringField({ initial: undefined }), name: new fields.StringField({ initial: undefined }),
description: new fields.HTMLField(), description: new fields.HTMLField(),
img: new fields.FilePathField({ initial: undefined, categories: ['IMAGE'], base64: false }), img: new fields.FilePathField({ initial: undefined, categories: ['IMAGE'], base64: false }),
chatDisplay: new fields.BooleanField({ initial: true, label: 'Display in chat' }), chatDisplay: new fields.BooleanField({ initial: true, label: 'DAGGERHEART.ACTIONS.Config.displayInChat' }),
actionType: new fields.StringField({ actionType: new fields.StringField({
choices: CONFIG.DH.ITEM.actionTypes, choices: CONFIG.DH.ITEM.actionTypes,
initial: 'action', initial: 'action',
@ -164,12 +164,13 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
title: this.item.name, title: this.item.name,
source: { source: {
item: this.item._id, item: this.item._id,
action: this._id action: this._id,
actor: this.actor.uuid
}, },
dialog: {}, dialog: {},
type: this.type, type: this.type,
hasDamage: !!this.damage?.parts?.length, hasDamage: this.damage?.parts?.length && this.type !== 'healing',
hasHealing: !!this.healing, hasHealing: this.damage?.parts?.length && this.type === 'healing',
hasEffect: !!this.effects?.length, hasEffect: !!this.effects?.length,
hasSave: this.hasSave, hasSave: this.hasSave,
selectedRollMode: game.settings.get('core', 'rollMode'), selectedRollMode: game.settings.get('core', 'rollMode'),
@ -191,7 +192,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
difficulty: this.roll?.difficulty, difficulty: this.roll?.difficulty,
formula: this.roll.getFormula(), formula: this.roll.getFormula(),
bonus: this.roll.bonus, bonus: this.roll.bonus,
advantage: CONFIG.DH.ACTIONS.advandtageState[this.roll.advState].value advantage: CONFIG.DH.ACTIONS.advantageState[this.roll.advState].value
}; };
if (this.roll?.type === 'diceSet') roll.lite = true; if (this.roll?.type === 'diceSet') roll.lite = true;
@ -256,6 +257,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
/* EFFECTS */ /* EFFECTS */
async applyEffects(event, data, targets) { async applyEffects(event, data, targets) {
targets ??= data.system.targets; targets ??= data.system.targets;
const force = true; /* Where should this come from? */
if (!this.effects?.length || !targets.length) return; if (!this.effects?.length || !targets.length) return;
let effects = this.effects; let effects = this.effects;
targets.forEach(async token => { targets.forEach(async token => {
@ -297,27 +299,39 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
/* EFFECTS */ /* EFFECTS */
/* SAVE */ /* SAVE */
async rollSave(target, event, message) { async rollSave(actor, event, message) {
if (!target?.actor) return; if (!actor) return;
return target.actor return actor
.diceRoll({ .diceRoll({
event, event,
title: 'Roll Save', title: 'Roll Save',
roll: { roll: {
trait: this.save.trait, trait: this.save.trait,
difficulty: this.save.difficulty, difficulty: this.save.difficulty ?? this.actor?.baseSaveDifficulty,
type: 'reaction' type: 'reaction'
}, },
data: target.actor.getRollData() data: actor.getRollData()
})
.then(async result => {
if (result)
this.updateChatMessage(message, target.id, {
result: result.roll.total,
success: result.roll.success
});
}); });
} }
updateSaveMessage(result, message, targetId) {
const updateMsg = this.updateChatMessage.bind(this, message, targetId, {
result: result.roll.total,
success: result.roll.success
});
if (game.modules.get('dice-so-nice')?.active)
game.dice3d.waitFor3DAnimationByMessageID(result.message.id ?? result.message._id).then(() => updateMsg());
else updateMsg();
}
static rollSaveQuery({ actionId, actorId, event, message }) {
return new Promise(async (resolve, reject) => {
const actor = await fromUuid(actorId),
action = await fromUuid(actionId);
if (!actor || !actor?.isOwner) reject();
action.rollSave(actor, event, message).then(result => resolve(result));
});
}
/* SAVE */ /* SAVE */
async updateChatMessage(message, targetId, changes, chain = true) { async updateChatMessage(message, targetId, changes, chain = true) {
@ -331,7 +345,7 @@ export default class DHBaseAction extends ActionMixin(foundry.abstract.DataModel
if (chain) { if (chain) {
if (message.system.source.message) if (message.system.source.message)
this.updateChatMessage(ui.chat.collection.get(message.system.source.message), targetId, changes, false); this.updateChatMessage(ui.chat.collection.get(message.system.source.message), targetId, changes, false);
const relatedChatMessages = ui.chat.collection.filter(c => c.system.source.message === message._id); const relatedChatMessages = ui.chat.collection.filter(c => c.system.source?.message === message._id);
relatedChatMessages.forEach(c => { relatedChatMessages.forEach(c => {
this.updateChatMessage(c, targetId, changes, false); this.updateChatMessage(c, targetId, changes, false);
}); });

View file

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

View file

@ -47,11 +47,12 @@ export default class DHDamageAction extends DHBaseAction {
formulas = this.formatFormulas(formulas, systemData); formulas = this.formatFormulas(formulas, systemData);
const config = { const config = {
title: game.i18n.format('DAGGERHEART.UI.Chat.damageRoll.title', { damage: game.i18n.localize(this.name) }), title: game.i18n.format(`DAGGERHEART.UI.Chat.${ this.type === 'healing' ? 'healing' : 'damage'}Roll.title`, { damage: game.i18n.localize(this.name) }),
roll: formulas, roll: formulas,
targets: systemData.targets?.filter(t => t.hit) ?? data.targets, targets: systemData.targets?.filter(t => t.hit) ?? data.targets,
hasSave: this.hasSave, hasSave: this.hasSave,
isCritical: systemData.roll?.isCritical ?? false, isCritical: systemData.roll?.isCritical ?? false,
isHealing: this.type === 'healing',
source: systemData.source, source: systemData.source,
data: this.getRollData(), data: this.getRollData(),
event event

View file

@ -1,7 +1,14 @@
import DHBaseAction from './baseAction.mjs'; import DHBaseAction from './baseAction.mjs';
import DHDamageAction from './damageAction.mjs';
export default class DHHealingAction extends DHBaseAction { export default class DHHealingAction extends DHDamageAction {
static extraSchemas = [...super.extraSchemas, 'target', 'effects', 'healing', 'roll']; static extraSchemas = [...super.extraSchemas, 'roll'];
static getRollType(parent) {
return 'spellcast';
}
/* static extraSchemas = [...super.extraSchemas, 'target', 'effects', 'healing', 'roll'];
static getRollType(parent) { static getRollType(parent) {
return 'spellcast'; return 'spellcast';
@ -44,5 +51,5 @@ export default class DHHealingAction extends DHBaseAction {
get modifiers() { get modifiers() {
return []; return [];
} } */
} }

View file

@ -62,6 +62,7 @@ export default class DhpAdversary extends BaseDataActor {
img: 'icons/skills/melee/blood-slash-foam-red.webp', img: 'icons/skills/melee/blood-slash-foam-red.webp',
_id: foundry.utils.randomID(), _id: foundry.utils.randomID(),
systemPath: 'attack', systemPath: 'attack',
chatDisplay: false,
type: 'attack', type: 'attack',
range: 'melee', range: 'melee',
target: { target: {

View file

@ -1,10 +1,25 @@
import DHBaseActorSettings from '../../applications/sheets/api/actor-setting.mjs'; import DHBaseActorSettings from '../../applications/sheets/api/actor-setting.mjs';
const resistanceField = reductionLabel => const resistanceField = (resistanceLabel, immunityLabel, reductionLabel) =>
new foundry.data.fields.SchemaField({ new foundry.data.fields.SchemaField({
resistance: new foundry.data.fields.BooleanField({ initial: false }), resistance: new foundry.data.fields.BooleanField({
immunity: new foundry.data.fields.BooleanField({ initial: false }), initial: false,
reduction: new foundry.data.fields.NumberField({ integer: true, initial: 0, label: reductionLabel }) label: `${resistanceLabel}.label`,
hint: `${resistanceLabel}.hint`,
isAttributeChoice: true
}),
immunity: new foundry.data.fields.BooleanField({
initial: false,
label: `${immunityLabel}.label`,
hint: `${immunityLabel}.hint`,
isAttributeChoice: true
}),
reduction: new foundry.data.fields.NumberField({
integer: true,
initial: 0,
label: `${reductionLabel}.label`,
hint: `${reductionLabel}.hint`
})
}); });
/** /**
@ -40,8 +55,16 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel {
if (this.metadata.isNPC) schema.description = new fields.HTMLField({ required: true, nullable: true }); if (this.metadata.isNPC) schema.description = new fields.HTMLField({ required: true, nullable: true });
if (this.metadata.hasResistances) if (this.metadata.hasResistances)
schema.resistance = new fields.SchemaField({ schema.resistance = new fields.SchemaField({
physical: resistanceField('DAGGERHEART.GENERAL.DamageResistance.physicalReduction'), physical: resistanceField(
magical: resistanceField('DAGGERHEART.GENERAL.DamageResistance.magicalReduction') 'DAGGERHEART.GENERAL.DamageResistance.physicalResistance',
'DAGGERHEART.GENERAL.DamageResistance.physicalImmunity',
'DAGGERHEART.GENERAL.DamageResistance.physicalReduction'
),
magical: resistanceField(
'DAGGERHEART.GENERAL.DamageResistance.magicalResistance',
'DAGGERHEART.GENERAL.DamageResistance.magicalImmunity',
'DAGGERHEART.GENERAL.DamageResistance.magicalReduction'
)
}); });
return schema; return schema;
} }

View file

@ -45,12 +45,12 @@ export default class DhCharacter extends BaseDataActor {
severe: new fields.NumberField({ severe: new fields.NumberField({
integer: true, integer: true,
initial: 0, initial: 0,
label: 'DAGGERHEART.GENERAL.DamageThresholds.majorThreshold' label: 'DAGGERHEART.GENERAL.DamageThresholds.severeThreshold'
}), }),
major: new fields.NumberField({ major: new fields.NumberField({
integer: true, integer: true,
initial: 0, initial: 0,
label: 'DAGGERHEART.GENERAL.DamageThresholds.severeThreshold' label: 'DAGGERHEART.GENERAL.DamageThresholds.majorThreshold'
}) })
}), }),
experiences: new fields.TypedObjectField( experiences: new fields.TypedObjectField(
@ -94,6 +94,7 @@ export default class DhCharacter extends BaseDataActor {
img: 'icons/skills/melee/unarmed-punch-fist-yellow-red.webp', img: 'icons/skills/melee/unarmed-punch-fist-yellow-red.webp',
_id: foundry.utils.randomID(), _id: foundry.utils.randomID(),
systemPath: 'attack', systemPath: 'attack',
chatDisplay: false,
type: 'attack', type: 'attack',
range: 'melee', range: 'melee',
target: { target: {
@ -111,7 +112,7 @@ export default class DhCharacter extends BaseDataActor {
value: { value: {
custom: { custom: {
enabled: true, enabled: true,
formula: '@system.rules.attack.damage.value' formula: '@profd4'
} }
} }
} }
@ -203,7 +204,7 @@ export default class DhCharacter extends BaseDataActor {
}) })
}) })
}), }),
maxLoadout : new fields.NumberField({ maxLoadout: new fields.NumberField({
integer: true, integer: true,
initial: 0, initial: 0,
label: 'DAGGERHEART.GENERAL.Bonuses.maxLoadout.label' label: 'DAGGERHEART.GENERAL.Bonuses.maxLoadout.label'
@ -243,10 +244,28 @@ export default class DhCharacter extends BaseDataActor {
}), }),
attack: new fields.SchemaField({ attack: new fields.SchemaField({
damage: new fields.SchemaField({ damage: new fields.SchemaField({
value: new fields.StringField({ diceIndex: new fields.NumberField({
integer: true,
min: 0,
max: 5,
initial: 0,
label: 'DAGGERHEART.GENERAL.Rules.attack.damage.dice.label',
hint: 'DAGGERHEART.GENERAL.Rules.attack.damage.dice.hint'
}),
bonus: new fields.NumberField({
required: true, required: true,
initial: '@profd4', initial: 0,
label: 'DAGGERHEART.GENERAL.Rules.attack.damage.value.label' min: 0,
label: 'DAGGERHEART.GENERAL.Rules.attack.damage.bonus.label'
})
}),
roll: new fields.SchemaField({
trait: new fields.StringField({
required: true,
choices: CONFIG.DH.ACTOR.abilities,
nullable: true,
initial: null,
label: 'DAGGERHEART.GENERAL.Rules.attack.roll.trait.label'
}) })
}) })
}), }),
@ -328,13 +347,15 @@ export default class DhCharacter extends BaseDataActor {
get loadoutSlot() { get loadoutSlot() {
const loadoutCount = this.domainCards.loadout?.length ?? 0, const loadoutCount = this.domainCards.loadout?.length ?? 0,
max = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxLoadout + this.bonuses.maxLoadout; max =
game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxLoadout +
this.bonuses.maxLoadout;
return { return {
current: loadoutCount, current: loadoutCount,
available: Math.max(max - loadoutCount, 0), available: Math.max(max - loadoutCount, 0),
max max
} };
} }
get armor() { get armor() {
@ -450,6 +471,12 @@ export default class DhCharacter extends BaseDataActor {
}; };
} }
get basicAttackDamageDice() {
const diceTypes = Object.keys(CONFIG.DH.GENERAL.diceTypes);
const attackDiceIndex = Math.max(Math.min(this.rules.attack.damage.diceIndex, 5), 0);
return diceTypes[attackDiceIndex];
}
static async unequipBeforeEquip(itemToEquip) { static async unequipBeforeEquip(itemToEquip) {
const primary = this.primaryWeapon, const primary = this.primaryWeapon,
secondary = this.secondaryWeapon; secondary = this.secondaryWeapon;
@ -534,12 +561,17 @@ export default class DhCharacter extends BaseDataActor {
prepareDerivedData() { prepareDerivedData() {
const baseHope = this.resources.hope.value + (this.companion?.system?.resources?.hope ?? 0); const baseHope = this.resources.hope.value + (this.companion?.system?.resources?.hope ?? 0);
this.resources.hope.value = Math.min(baseHope, this.resources.hope.max); this.resources.hope.value = Math.min(baseHope, this.resources.hope.max);
this.attack.roll.trait = this.rules.attack.roll.trait ?? this.attack.roll.trait;
this.attack.damage.parts[0].value.custom.formula = `@prof${this.basicAttackDamageDice}${this.rules.attack.damage.bonus ? ` + ${this.rules.attack.damage.bonus}` : ''}`;
} }
getRollData() { getRollData() {
const data = super.getRollData(); const data = super.getRollData();
return { return {
...data, ...data,
basicAttackDamageDice: this.basicAttackDamageDice,
tier: this.tier, tier: this.tier,
level: this.levelData.level.current level: this.levelData.level.current
}; };

View file

@ -53,6 +53,7 @@ export default class DhCompanion extends BaseDataActor {
img: 'icons/creatures/claws/claw-bear-paw-swipe-brown.webp', img: 'icons/creatures/claws/claw-bear-paw-swipe-brown.webp',
_id: foundry.utils.randomID(), _id: foundry.utils.randomID(),
systemPath: 'attack', systemPath: 'attack',
chatDisplay: false,
type: 'attack', type: 'attack',
range: 'melee', range: 'melee',
target: { target: {

View file

@ -21,6 +21,7 @@ export default class DHDamageRoll extends foundry.abstract.TypeDataModel {
), ),
targetSelection: new fields.BooleanField({ initial: true }), targetSelection: new fields.BooleanField({ initial: true }),
hasSave: new fields.BooleanField({ initial: false }), hasSave: new fields.BooleanField({ initial: false }),
isHealing: new fields.BooleanField({ initial: false }),
onSave: new fields.StringField(), onSave: new fields.StringField(),
source: new fields.SchemaField({ source: new fields.SchemaField({
actor: new fields.StringField(), actor: new fields.StringField(),

View file

@ -2,8 +2,11 @@ import { DHDamageData } from './damageField.mjs';
const fields = foundry.data.fields; const fields = foundry.data.fields;
export default class HealingField extends fields.EmbeddedDataField { export default class HealingField extends fields.SchemaField {
constructor(options, context = {}) { constructor(options, context = {}) {
super(DHDamageData, options, context); const healingFields = {
parts: new fields.ArrayField(new fields.EmbeddedDataField(DHDamageData))
};
super(healingFields, options, context);
} }
} }

View file

@ -8,25 +8,38 @@ export class DHActionRollData extends foundry.abstract.DataModel {
trait: new fields.StringField({ nullable: true, initial: null, choices: CONFIG.DH.ACTOR.abilities }), trait: new fields.StringField({ nullable: true, initial: null, choices: CONFIG.DH.ACTOR.abilities }),
difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }), difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }),
bonus: new fields.NumberField({ nullable: true, initial: null, integer: true }), bonus: new fields.NumberField({ nullable: true, initial: null, integer: true }),
advState: new fields.StringField({ choices: CONFIG.DH.ACTIONS.advandtageState, initial: 'neutral' }), advState: new fields.StringField({
choices: CONFIG.DH.ACTIONS.advantageState,
initial: 'neutral'
}),
diceRolling: new fields.SchemaField({ diceRolling: new fields.SchemaField({
multiplier: new fields.StringField({ multiplier: new fields.StringField({
choices: CONFIG.DH.GENERAL.diceSetNumbers, choices: CONFIG.DH.GENERAL.diceSetNumbers,
initial: 'prof', initial: 'prof',
label: 'Dice Number' label: 'DAGGERHEART.ACTIONS.RollField.diceRolling.multiplier'
}),
flatMultiplier: new fields.NumberField({
nullable: true,
initial: 1,
label: 'DAGGERHEART.ACTIONS.RollField.diceRolling.flatMultiplier'
}), }),
flatMultiplier: new fields.NumberField({ nullable: true, initial: 1, label: 'Flat Multiplier' }),
dice: new fields.StringField({ dice: new fields.StringField({
choices: CONFIG.DH.GENERAL.diceTypes, choices: CONFIG.DH.GENERAL.diceTypes,
initial: 'd6', initial: CONFIG.DH.GENERAL.diceTypes.d6,
label: 'Dice Type' label: 'DAGGERHEART.ACTIONS.RollField.diceRolling.dice'
}), }),
compare: new fields.StringField({ compare: new fields.StringField({
choices: CONFIG.DH.ACTIONS.diceCompare, choices: CONFIG.DH.ACTIONS.diceCompare,
initial: 'above', nullable: true,
label: 'Should be' initial: null,
label: 'DAGGERHEART.ACTIONS.RollField.diceRolling.compare'
}), }),
treshold: new fields.NumberField({ initial: 1, integer: true, min: 1, label: 'Treshold' }) treshold: new fields.NumberField({
integer: true,
nullable: true,
initial: null,
label: 'DAGGERHEART.ACTIONS.RollField.diceRolling.threshold'
})
}), }),
useDefault: new fields.BooleanField({ initial: false }) useDefault: new fields.BooleanField({ initial: false })
}; };
@ -41,7 +54,11 @@ export class DHActionRollData extends foundry.abstract.DataModel {
this.diceRolling.multiplier === 'flat' this.diceRolling.multiplier === 'flat'
? this.diceRolling.flatMultiplier ? this.diceRolling.flatMultiplier
: `@${this.diceRolling.multiplier}`; : `@${this.diceRolling.multiplier}`;
formula = `${multiplier}${this.diceRolling.dice}cs${CONFIG.DH.ACTIONS.diceCompare[this.diceRolling.compare].operator}${this.diceRolling.treshold}`; if (this.diceRolling.compare && this.diceRolling.threshold) {
formula = `${multiplier}${this.diceRolling.dice}cs${CONFIG.DH.ACTIONS.diceCompare[this.diceRolling.compare].operator}${this.diceRolling.treshold}`;
} else {
formula = `${multiplier}${this.diceRolling.dice}`;
}
break; break;
default: default:
formula = ''; formula = '';

View file

@ -8,7 +8,7 @@ export default class SaveField extends fields.SchemaField {
initial: null, initial: null,
choices: CONFIG.DH.ACTOR.abilities choices: CONFIG.DH.ACTOR.abilities
}), }),
difficulty: new fields.NumberField({ nullable: true, initial: 10, integer: true, min: 0 }), difficulty: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }),
damageMod: new fields.StringField({ damageMod: new fields.StringField({
initial: CONFIG.DH.ACTIONS.damageOnSave.none.id, initial: CONFIG.DH.ACTIONS.damageOnSave.none.id,
choices: CONFIG.DH.ACTIONS.damageOnSave choices: CONFIG.DH.ACTIONS.damageOnSave

View file

@ -6,8 +6,7 @@ export default class TargetField extends fields.SchemaField {
type: new fields.StringField({ type: new fields.StringField({
choices: CONFIG.DH.ACTIONS.targetTypes, choices: CONFIG.DH.ACTIONS.targetTypes,
initial: CONFIG.DH.ACTIONS.targetTypes.any.id, initial: CONFIG.DH.ACTIONS.targetTypes.any.id,
nullable: true, nullable: true
initial: null
}), }),
amount: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 }) amount: new fields.NumberField({ nullable: true, initial: null, integer: true, min: 0 })
}; };
@ -18,11 +17,13 @@ export default class TargetField extends fields.SchemaField {
if (!this.target?.type) return []; if (!this.target?.type) return [];
let targets; let targets;
if (this.target?.type === CONFIG.DH.ACTIONS.targetTypes.self.id) if (this.target?.type === CONFIG.DH.ACTIONS.targetTypes.self.id)
targets = TargetField.formatTarget.call(this, this.actor.token ?? this.actor.prototypeToken); targets = [this.actor.token ?? this.actor.prototypeToken];
targets = Array.from(game.user.targets); else {
if (this.target.type !== CONFIG.DH.ACTIONS.targetTypes.any.id) { targets = Array.from(game.user.targets);
targets = targets.filter(t => TargetField.isTargetFriendly.call(this, t)); if (this.target.type !== CONFIG.DH.ACTIONS.targetTypes.any.id) {
if (this.target.amount && targets.length > this.target.amount) targets = []; targets = targets.filter(t => TargetField.isTargetFriendly.call(this, t));
if (this.target.amount && targets.length > this.target.amount) targets = [];
}
} }
config.targets = targets.map(t => TargetField.formatTarget.call(this, t)); config.targets = targets.map(t => TargetField.formatTarget.call(this, t));
const hasTargets = TargetField.checkTargets.call(this, this.target.amount, config.targets); const hasTargets = TargetField.checkTargets.call(this, this.target.amount, config.targets);

View file

@ -175,7 +175,10 @@ export function ActionMixin(Base) {
classes: ['daggerheart', 'dh-style'], classes: ['daggerheart', 'dh-style'],
content: await foundry.applications.handlebars.renderTemplate( content: await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/actionTypes/actionType.hbs', 'systems/daggerheart/templates/actionTypes/actionType.hbs',
{ types: CONFIG.DH.ACTIONS.actionTypes } {
types: CONFIG.DH.ACTIONS.actionTypes,
itemName: parent.parent?.name
}
), ),
ok: { ok: {
label: game.i18n.format('DOCUMENT.Create', { label: game.i18n.format('DOCUMENT.Create', {
@ -197,7 +200,7 @@ export function ActionMixin(Base) {
} }
); );
const created = await parent.parent.update({ [`system.actions.${action.id}`]: action.toObject() }); const created = await parent.parent.update({ [`system.actions.${action.id}`]: action.toObject() });
const newAction = parent.actions.get(action.id); const newAction = created.system.actions.get(action.id);
if (!newAction) return null; if (!newAction) return null;
if (renderSheet) newAction.sheet.render({ force: true }); if (renderSheet) newAction.sheet.render({ force: true });
return newAction; return newAction;

View file

@ -35,7 +35,13 @@ export default class FormulaField extends foundry.data.fields.StringField {
/** @inheritDoc */ /** @inheritDoc */
_validateType(value) { _validateType(value) {
const roll = new Roll(value.replace(/@([a-z.0-9_-]+)/gi, '1')); /* A bit suss, but seems to work */
let roll = null;
try {
roll = new Roll(value.replace(/@([a-z.0-9_-]+)/gi, '1'));
} catch (_) {
roll = new Roll(value.replace(/@([a-z.0-9_-]+)/gi, 'd6'));
}
roll.evaluateSync({ strict: false }); roll.evaluateSync({ strict: false });
if (this.options.deterministic && !roll.isDeterministic) if (this.options.deterministic && !roll.isDeterministic)
throw new Error(`must not contain dice terms: ${value}`); throw new Error(`must not contain dice terms: ${value}`);

View file

@ -6,7 +6,7 @@ import DHCommunity from './community.mjs';
import DHConsumable from './consumable.mjs'; import DHConsumable from './consumable.mjs';
import DHDomainCard from './domainCard.mjs'; import DHDomainCard from './domainCard.mjs';
import DHFeature from './feature.mjs'; import DHFeature from './feature.mjs';
import DHMiscellaneous from './miscellaneous.mjs'; import DHLoot from './loot.mjs';
import DHSubclass from './subclass.mjs'; import DHSubclass from './subclass.mjs';
import DHWeapon from './weapon.mjs'; import DHWeapon from './weapon.mjs';
import DHBeastform from './beastform.mjs'; import DHBeastform from './beastform.mjs';
@ -20,7 +20,7 @@ export {
DHConsumable, DHConsumable,
DHDomainCard, DHDomainCard,
DHFeature, DHFeature,
DHMiscellaneous, DHLoot,
DHSubclass, DHSubclass,
DHWeapon, DHWeapon,
DHBeastform DHBeastform
@ -35,7 +35,7 @@ export const config = {
consumable: DHConsumable, consumable: DHConsumable,
domainCard: DHDomainCard, domainCard: DHDomainCard,
feature: DHFeature, feature: DHFeature,
miscellaneous: DHMiscellaneous, loot: DHLoot,
subclass: DHSubclass, subclass: DHSubclass,
weapon: DHWeapon, weapon: DHWeapon,
beastform: DHBeastform beastform: DHBeastform

View file

@ -1,5 +1,4 @@
import AttachableItem from './attachableItem.mjs'; import AttachableItem from './attachableItem.mjs';
import { ActionsField } from '../fields/actionField.mjs';
import { armorFeatures } from '../../config/itemConfig.mjs'; import { armorFeatures } from '../../config/itemConfig.mjs';
export default class DHArmor extends AttachableItem { export default class DHArmor extends AttachableItem {

View file

@ -94,10 +94,13 @@ export default class DHBeastform extends BaseDataItem {
return false; return false;
} }
const features = await this.parent.parent.createEmbeddedDocuments( const beastformFeatures = [];
'Item', for (let featureData of this.features) {
this.features.map(x => x.toObject()) const feature = await foundry.utils.fromUuid(featureData.uuid);
); beastformFeatures.push(feature.toObject());
}
const features = await this.parent.parent.createEmbeddedDocuments('Item', beastformFeatures);
const extraEffects = await this.parent.parent.createEmbeddedDocuments( const extraEffects = await this.parent.parent.createEmbeddedDocuments(
'ActiveEffect', 'ActiveEffect',
@ -152,12 +155,14 @@ export default class DHBeastform extends BaseDataItem {
_onCreate(_data, _options, userId) { _onCreate(_data, _options, userId) {
if (userId !== game.user.id) return; if (userId !== game.user.id) return;
this.parent.createEmbeddedDocuments('ActiveEffect', [ if (!this.parent.effects.find(x => x.type === 'beastform')) {
{ this.parent.createEmbeddedDocuments('ActiveEffect', [
type: 'beastform', {
name: game.i18n.localize('DAGGERHEART.ITEMS.Beastform.beastformEffect'), type: 'beastform',
img: 'icons/creatures/abilities/paw-print-pair-purple.webp' name: game.i18n.localize('DAGGERHEART.ITEMS.Beastform.beastformEffect'),
} img: 'icons/creatures/abilities/paw-print-pair-purple.webp'
]); }
]);
}
} }
} }

View file

@ -1,12 +1,11 @@
import BaseDataItem from './base.mjs'; import BaseDataItem from './base.mjs';
import { ActionField } from '../fields/actionField.mjs';
export default class DHMiscellaneous extends BaseDataItem { export default class DHLoot extends BaseDataItem {
/** @inheritDoc */ /** @inheritDoc */
static get metadata() { static get metadata() {
return foundry.utils.mergeObject(super.metadata, { return foundry.utils.mergeObject(super.metadata, {
label: 'TYPES.Item.miscellaneous', label: 'TYPES.Item.loot',
type: 'miscellaneous', type: 'loot',
hasDescription: true, hasDescription: true,
isQuantifiable: true, isQuantifiable: true,
isInventoryItem: true, isInventoryItem: true,

View file

@ -63,6 +63,19 @@ export default class DHWeapon extends AttachableItem {
] ]
} }
} }
}),
rules: new fields.SchemaField({
attack: new fields.SchemaField({
roll: new fields.SchemaField({
trait: new fields.StringField({
required: true,
choices: CONFIG.DH.ACTOR.abilities,
nullable: true,
initial: null,
label: 'DAGGERHEART.GENERAL.Rules.attack.roll.trait.label'
})
})
})
}) })
}; };
} }
@ -77,6 +90,10 @@ export default class DHWeapon extends AttachableItem {
); );
} }
prepareDerivedData() {
this.attack.roll.trait = this.rules.attack.roll.trait ?? this.attack.roll.trait;
}
async _preUpdate(changes, options, user) { async _preUpdate(changes, options, user) {
const allowed = await super._preUpdate(changes, options, user); const allowed = await super._preUpdate(changes, options, user);
if (allowed === false) return false; if (allowed === false) return false;

View file

@ -21,25 +21,41 @@ export default class DhAppearance extends foundry.abstract.DataModel {
foreground: new fields.ColorField({ required: true, initial: '#ffffff' }), foreground: new fields.ColorField({ required: true, initial: '#ffffff' }),
background: new fields.ColorField({ required: true, initial: '#ffe760' }), background: new fields.ColorField({ required: true, initial: '#ffe760' }),
outline: new fields.ColorField({ required: true, initial: '#000000' }), outline: new fields.ColorField({ required: true, initial: '#000000' }),
edge: new fields.ColorField({ required: true, initial: '#ffffff' }) edge: new fields.ColorField({ required: true, initial: '#ffffff' }),
texture: new fields.StringField({ initial: 'astralsea' }),
colorset: new fields.StringField({ initial: 'inspired' }),
material: new fields.StringField({ initial: 'metal' }),
system: new fields.StringField({ initial: 'standard' })
}), }),
fear: new fields.SchemaField({ fear: new fields.SchemaField({
foreground: new fields.ColorField({ required: true, initial: '#000000' }), foreground: new fields.ColorField({ required: true, initial: '#000000' }),
background: new fields.ColorField({ required: true, initial: '#0032b1' }), background: new fields.ColorField({ required: true, initial: '#0032b1' }),
outline: new fields.ColorField({ required: true, initial: '#ffffff' }), outline: new fields.ColorField({ required: true, initial: '#ffffff' }),
edge: new fields.ColorField({ required: true, initial: '#000000' }) edge: new fields.ColorField({ required: true, initial: '#000000' }),
texture: new fields.StringField({ initial: 'astralsea' }),
colorset: new fields.StringField({ initial: 'inspired' }),
material: new fields.StringField({ initial: 'metal' }),
system: new fields.StringField({ initial: 'standard' })
}), }),
advantage: new fields.SchemaField({ advantage: new fields.SchemaField({
foreground: new fields.ColorField({ required: true, initial: '#ffffff' }), foreground: new fields.ColorField({ required: true, initial: '#ffffff' }),
background: new fields.ColorField({ required: true, initial: '#008000' }), background: new fields.ColorField({ required: true, initial: '#008000' }),
outline: new fields.ColorField({ required: true, initial: '#000000' }), outline: new fields.ColorField({ required: true, initial: '#000000' }),
edge: new fields.ColorField({ required: true, initial: '#ffffff' }) edge: new fields.ColorField({ required: true, initial: '#ffffff' }),
texture: new fields.StringField({ initial: 'astralsea' }),
colorset: new fields.StringField({ initial: 'inspired' }),
material: new fields.StringField({ initial: 'metal' }),
system: new fields.StringField({ initial: 'standard' })
}), }),
disadvantage: new fields.SchemaField({ disadvantage: new fields.SchemaField({
foreground: new fields.ColorField({ required: true, initial: '#000000' }), foreground: new fields.ColorField({ required: true, initial: '#000000' }),
background: new fields.ColorField({ required: true, initial: '#b30000' }), background: new fields.ColorField({ required: true, initial: '#b30000' }),
outline: new fields.ColorField({ required: true, initial: '#ffffff' }), outline: new fields.ColorField({ required: true, initial: '#ffffff' }),
edge: new fields.ColorField({ required: true, initial: '#000000' }) edge: new fields.ColorField({ required: true, initial: '#000000' }),
texture: new fields.StringField({ initial: 'astralsea' }),
colorset: new fields.StringField({ initial: 'inspired' }),
material: new fields.StringField({ initial: 'metal' }),
system: new fields.StringField({ initial: 'standard' })
}) })
}), }),
showGenericStatusEffects: new fields.BooleanField({ showGenericStatusEffects: new fields.BooleanField({

View file

@ -1,5 +1,4 @@
import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs'; import D20RollDialog from '../applications/dialogs/d20RollDialog.mjs';
import { getDiceSoNicePresets } from '../config/generalConfig.mjs';
import DHRoll from './dhRoll.mjs'; import DHRoll from './dhRoll.mjs';
export default class D20Roll extends DHRoll { export default class D20Roll extends DHRoll {

View file

@ -12,11 +12,16 @@ export default class DamageRoll extends DHRoll {
static async buildEvaluate(roll, config = {}, message = {}) { static async buildEvaluate(roll, config = {}, message = {}) {
if (config.evaluate !== false) { if (config.evaluate !== false) {
if (config.dialog.configure === false) roll.constructFormula(config); // if (config.dialog.configure === false) roll.constructFormula(config);
for (const roll of config.roll) await roll.roll.evaluate(); for (const roll of config.roll) await roll.roll.evaluate();
} }
roll._evaluated = true; roll._evaluated = true;
const parts = config.roll.map(r => this.postEvaluate(r)); const parts = [];
for (let r of config.roll) {
const part = this.postEvaluate(r);
parts.push(part);
}
config.roll = this.unifyDamageRoll(parts); config.roll = this.unifyDamageRoll(parts);
} }
@ -78,18 +83,20 @@ export default class DamageRoll extends DHRoll {
applyBaseBonus(part) { applyBaseBonus(part) {
const modifiers = [], const modifiers = [],
type = this.options.messageType ?? 'damage', type = this.options.messageType ?? (this.options.isHealing ? 'healing' : 'damage'),
options = part ?? this.options; options = part ?? this.options;
modifiers.push(...this.getBonus(`${type}`, `${type.capitalize()} Bonus`)); modifiers.push(...this.getBonus(`${type}`, `${type.capitalize()} Bonus`));
options.damageTypes?.forEach(t => { if(!this.options.isHealing) {
modifiers.push(...this.getBonus(`${type}.${t}`, `${t.capitalize()} ${type.capitalize()} Bonus`)); options.damageTypes?.forEach(t => {
}); modifiers.push(...this.getBonus(`${type}.${t}`, `${t.capitalize()} ${type.capitalize()} Bonus`));
const weapons = ['primaryWeapon', 'secondaryWeapon']; });
weapons.forEach(w => { const weapons = ['primaryWeapon', 'secondaryWeapon'];
if (this.options.source.item && this.options.source.item === this.data[w]?.id) weapons.forEach(w => {
modifiers.push(...this.getBonus(`${type}.${w}`, 'Weapon Bonus')); if (this.options.source.item && this.options.source.item === this.data[w]?.id)
}); modifiers.push(...this.getBonus(`${type}.${w}`, 'Weapon Bonus'));
});
}
return modifiers; return modifiers;
} }

View file

@ -166,6 +166,18 @@ export default class DualityRoll extends D20Roll {
return modifiers; return modifiers;
} }
static async buildEvaluate(roll, config = {}, message = {}) {
await super.buildEvaluate(roll, config, message);
await setDiceSoNiceForDualityRoll(
roll,
config.roll.advantage.type,
config.roll.hope.dice,
config.roll.fear.dice,
config.roll.advantage.dice
);
}
static postEvaluate(roll, config = {}) { static postEvaluate(roll, config = {}) {
const data = super.postEvaluate(roll, config); const data = super.postEvaluate(roll, config);
@ -198,8 +210,6 @@ export default class DualityRoll extends D20Roll {
if (roll._rallyIndex && roll.data?.parent) if (roll._rallyIndex && roll.data?.parent)
roll.data.parent.deleteEmbeddedDocuments('ActiveEffect', [roll._rallyIndex]); roll.data.parent.deleteEmbeddedDocuments('ActiveEffect', [roll._rallyIndex]);
setDiceSoNiceForDualityRoll(roll, data.advantage.type);
return data; return data;
} }
@ -220,10 +230,10 @@ export default class DualityRoll extends D20Roll {
options: { appearance: {} } options: { appearance: {} }
}; };
const diceSoNicePresets = getDiceSoNicePresets(); const diceSoNicePresets = await getDiceSoNicePresets(`d${term._faces}`, `d${term._faces}`);
const type = target.dataset.type; const type = target.dataset.type;
if (diceSoNicePresets[type]) { if (diceSoNicePresets[type]) {
diceSoNiceRoll.dice[0].options = { appearance: diceSoNicePresets[type] }; diceSoNiceRoll.dice[0].options = diceSoNicePresets[type];
} }
await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true); await game.dice3d.showForRoll(diceSoNiceRoll, game.user, true);

View file

@ -41,6 +41,14 @@ export default class DhActiveEffect extends ActiveEffect {
}); });
} }
get localizedStatuses() {
const statusMap = new Map(foundry.CONFIG.statusEffects.map(status => [status.id, status.name]));
return this.statuses.map(x => ({
key: x,
name: game.i18n.localize(statusMap.get(x))
}));
}
async _preCreate(data, options, user) { async _preCreate(data, options, user) {
const update = {}; const update = {};
if (!data.img) { if (!data.img) {
@ -55,7 +63,8 @@ export default class DhActiveEffect extends ActiveEffect {
} }
static applyField(model, change, field) { static applyField(model, change, field) {
change.value = this.effectSafeEval(itemAbleRollParse(change.value, model, change.effect.parent)); const evalValue = this.effectSafeEval(itemAbleRollParse(change.value, model, change.effect.parent));
change.value = evalValue ?? change.value;
super.applyField(model, change, field); super.applyField(model, change, field);
} }

View file

@ -1,5 +1,4 @@
import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs'; import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs';
import DamageReductionDialog from '../applications/dialogs/damageReductionDialog.mjs';
import { LevelOptionType } from '../data/levelTier.mjs'; import { LevelOptionType } from '../data/levelTier.mjs';
import DHFeature from '../data/item/feature.mjs'; import DHFeature from '../data/item/feature.mjs';
import { damageKeyToNumber } from '../helpers/utils.mjs'; import { damageKeyToNumber } from '../helpers/utils.mjs';
@ -385,6 +384,45 @@ export default class DhpActor extends Actor {
return CONFIG.Dice.daggerheart[['character', 'companion'].includes(this.type) ? 'DualityRoll' : 'D20Roll']; return CONFIG.Dice.daggerheart[['character', 'companion'].includes(this.type) ? 'DualityRoll' : 'D20Roll'];
} }
get baseSaveDifficulty() {
return this.system.difficulty ?? 10;
}
/** @inheritDoc */
async toggleStatusEffect(statusId, { active, overlay = false } = {}) {
const status = CONFIG.statusEffects.find(e => e.id === statusId);
if (!status) throw new Error(`Invalid status ID "${statusId}" provided to Actor#toggleStatusEffect`);
const existing = [];
// Find the effect with the static _id of the status effect
if (status._id) {
const effect = this.effects.get(status._id);
if (effect) existing.push(effect.id);
}
// If no static _id, find all effects that have this status
else {
for (const effect of this.effects) {
if (effect.statuses.has(status.id)) existing.push(effect.id);
}
}
// Remove the existing effects unless the status effect is forced active
if (existing.length) {
if (active) return true;
await this.deleteEmbeddedDocuments('ActiveEffect', existing);
return false;
}
// Create a new effect unless the status effect is forced inactive
if (!active && active !== undefined) return;
const ActiveEffect = getDocumentClass('ActiveEffect');
const effect = await ActiveEffect.fromStatusEffect(statusId);
if (overlay) effect.updateSource({ 'flags.core.overlay': true });
return ActiveEffect.implementation.create(effect, { parent: this, keepId: true });
}
getRollData() { getRollData() {
const rollData = super.getRollData(); const rollData = super.getRollData();
rollData.system = this.system.getRollData(); rollData.system = this.system.getRollData();
@ -444,10 +482,14 @@ export default class DhpActor extends Actor {
this.#canReduceDamage(hpDamage.value, hpDamage.damageTypes) this.#canReduceDamage(hpDamage.value, hpDamage.damageTypes)
) { ) {
const armorStackResult = await this.owner.query('armorStack', { const armorStackResult = await this.owner.query('armorStack', {
actorId: this.uuid, actorId: this.uuid,
damage: hpDamage.value, damage: hpDamage.value,
type: [...hpDamage.damageTypes] type: [...hpDamage.damageTypes]
}); },
{
timeout: 30000
}
);
if (armorStackResult) { if (armorStackResult) {
const { modifiedDamage, armorSpent, stressSpent } = armorStackResult; const { modifiedDamage, armorSpent, stressSpent } = armorStackResult;
updates.find(u => u.key === 'hitPoints').value = modifiedDamage; updates.find(u => u.key === 'hitPoints').value = modifiedDamage;
@ -467,7 +509,7 @@ export default class DhpActor extends Actor {
await this.modifyResource(updates); await this.modifyResource(updates);
if (Hooks.call(`${CONFIG.DH.id}.postTakeDamage`, this, damages) === false) return null; if (Hooks.call(`${CONFIG.DH.id}.postTakeDamage`, this, updates) === false) return null;
} }
calculateDamage(baseDamage, type) { calculateDamage(baseDamage, type) {
@ -494,14 +536,28 @@ export default class DhpActor extends Actor {
return reduction === Infinity ? 0 : reduction; return reduction === Infinity ? 0 : reduction;
} }
async takeHealing(resources) { async takeHealing(healings) {
const updates = Object.entries(resources).map(([key, value]) => ({ if (Hooks.call(`${CONFIG.DH.id}.preTakeHealing`, this, healings) === false) return null;
key: key,
value: !(key === 'fear' || this.system?.resources?.[key]?.isReversed === false) const updates = [];
? value.total * -1 Object.entries(healings).forEach(([key, healing]) => {
: value.total healing.parts.forEach(part => {
})); const update = updates.find(u => u.key === key);
if (update) update.value += part.total;
else updates.push({ value: part.total, key });
});
});
updates.forEach(
u =>
(u.value = !(u.key === 'fear' || this.system?.resources?.[u.key]?.isReversed === false)
? u.value * -1
: u.value)
);
await this.modifyResource(updates); await this.modifyResource(updates);
if (Hooks.call(`${CONFIG.DH.id}.postTakeHealing`, this, updates) === false) return null;
} }
async modifyResource(resources) { async modifyResource(resources) {
@ -543,6 +599,7 @@ export default class DhpActor extends Actor {
} }
} }
}); });
Object.keys(updates).forEach(async key => { Object.keys(updates).forEach(async key => {
const u = updates[key]; const u = updates[key];
if (key === 'items') { if (key === 'items') {
@ -584,7 +641,3 @@ export default class DhpActor extends Actor {
}); });
} }
} }
export const registerDHActorHooks = () => {
CONFIG.queries.armorStack = DamageReductionDialog.armorStackQuery;
};

View file

@ -52,6 +52,7 @@ export default class DHToken extends TokenDocument {
for (const [name, field] of Object.entries(schema.fields)) { for (const [name, field] of Object.entries(schema.fields)) {
const p = _path.concat([name]); const p = _path.concat([name]);
if (field instanceof foundry.data.fields.NumberField) attributes.value.push(p); if (field instanceof foundry.data.fields.NumberField) attributes.value.push(p);
if (field instanceof foundry.data.fields.BooleanField && field.options.isAttributeChoice) attributes.value.push(p);
if (field instanceof foundry.data.fields.StringField) attributes.value.push(p); if (field instanceof foundry.data.fields.StringField) attributes.value.push(p);
if (field instanceof foundry.data.fields.ArrayField) attributes.value.push(p); if (field instanceof foundry.data.fields.ArrayField) attributes.value.push(p);
const isSchema = field instanceof foundry.data.fields.SchemaField; const isSchema = field instanceof foundry.data.fields.SchemaField;

View file

@ -87,6 +87,26 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti
this.tooltip.innerHTML = html; this.tooltip.innerHTML = html;
} }
} }
const deathMove = element.dataset.tooltip?.startsWith('#deathMove#');
if (deathMove) {
const name = element.dataset.deathName;
const img = element.dataset.deathImg;
const description = element.dataset.deathDescription;
html = await foundry.applications.handlebars.renderTemplate(
`systems/daggerheart/templates/ui/tooltip/death-move.hbs`,
{
move: { name: name, img: img, description: description }
}
);
this.tooltip.innerHTML = html;
options.direction = this._determineItemTooltipDirection(
element,
this.constructor.TOOLTIP_DIRECTIONS.RIGHT
);
}
} }
super.activate(element, { ...options, html: html }); super.activate(element, { ...options, html: html });

View file

@ -22,14 +22,14 @@ function getDualityMessage(roll) {
: game.i18n.localize('DAGGERHEART.GENERAL.duality'); : game.i18n.localize('DAGGERHEART.GENERAL.duality');
const advantage = roll.advantage const advantage = roll.advantage
? CONFIG.DH.ACTIONS.advandtageState.advantage.value ? CONFIG.DH.ACTIONS.advantageState.advantage.value
: roll.disadvantage : roll.disadvantage
? CONFIG.DH.ACTIONS.advandtageState.disadvantage.value ? CONFIG.DH.ACTIONS.advantageState.disadvantage.value
: undefined; : undefined;
const advantageLabel = const advantageLabel =
advantage === CONFIG.DH.ACTIONS.advandtageState.advantage.value advantage === CONFIG.DH.ACTIONS.advantageState.advantage.value
? 'Advantage' ? 'Advantage'
: advantage === CONFIG.DH.ACTIONS.advandtageState.disadvantage.value : advantage === CONFIG.DH.ACTIONS.advantageState.disadvantage.value
? 'Disadvantage' ? 'Disadvantage'
: undefined; : undefined;
@ -58,7 +58,7 @@ function getDualityMessage(roll) {
export const renderDualityButton = async event => { export const renderDualityButton = async event => {
const button = event.currentTarget, const button = event.currentTarget,
traitValue = button.dataset.trait?.toLowerCase(), traitValue = button.dataset.trait?.toLowerCase(),
target = getCommandTarget(), target = getCommandTarget({ allowNull: true }),
difficulty = button.dataset.difficulty, difficulty = button.dataset.difficulty,
advantage = button.dataset.advantage ? Number(button.dataset.advantage) : undefined; advantage = button.dataset.advantage ? Number(button.dataset.advantage) : undefined;
@ -80,13 +80,11 @@ export const enrichedDualityRoll = async (
{ traitValue, target, difficulty, title, label, actionType, advantage }, { traitValue, target, difficulty, title, label, actionType, advantage },
event event
) => { ) => {
if (!target) return;
const config = { const config = {
event: event ?? {}, event: event ?? {},
title: title, title: title,
roll: { roll: {
modifier: traitValue ? target.system.traits[traitValue].value : null, trait: traitValue && target ? traitValue : null,
label: label, label: label,
difficulty: difficulty, difficulty: difficulty,
advantage, advantage,
@ -96,5 +94,13 @@ export const enrichedDualityRoll = async (
template: 'systems/daggerheart/templates/ui/chat/duality-roll.hbs' template: 'systems/daggerheart/templates/ui/chat/duality-roll.hbs'
} }
}; };
await target.diceRoll(config);
if (target) {
await target.diceRoll(config);
} else {
// For no target, call DualityRoll directly with basic data
config.data = { experiences: {}, traits: {} };
config.source = { actor: null };
await CONFIG.Dice.daggerheart.DualityRoll.build(config);
}
}; };

View file

@ -31,35 +31,38 @@ export function rollCommandToJSON(text) {
return Object.keys(result).length > 0 ? result : null; return Object.keys(result).length > 0 ? result : null;
} }
export const getCommandTarget = () => { export const getCommandTarget = (options = {}) => {
const { allowNull = false } = options;
let target = game.canvas.tokens.controlled.length > 0 ? game.canvas.tokens.controlled[0].actor : null; let target = game.canvas.tokens.controlled.length > 0 ? game.canvas.tokens.controlled[0].actor : null;
if (!game.user.isGM) { if (!game.user.isGM) {
target = game.user.character; target = game.user.character;
if (!target) { if (!target && !allowNull) {
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noAssignedPlayerCharacter')); ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noAssignedPlayerCharacter'));
return null; return null;
} }
} }
if (!target) { if (!target && !allowNull) {
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noSelectedToken')); ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.noSelectedToken'));
return null; return null;
} }
if (target.type !== 'character') { if (target && target.type !== 'character') {
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.onlyUseableByPC')); if (!allowNull) {
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.onlyUseableByPC'));
}
return null; return null;
} }
return target; return target;
}; };
export const setDiceSoNiceForDualityRoll = (rollResult, advantageState) => { export const setDiceSoNiceForDualityRoll = async (rollResult, advantageState, hopeFaces, fearFaces, advantageFaces) => {
const diceSoNicePresets = getDiceSoNicePresets(); if (!game.modules.get('dice-so-nice')?.active) return;
rollResult.dice[0].options = { appearance: diceSoNicePresets.hope }; const diceSoNicePresets = await getDiceSoNicePresets(hopeFaces, fearFaces, advantageFaces, advantageFaces);
rollResult.dice[1].options = { appearance: diceSoNicePresets.fear }; //diceSoNicePresets.fear; rollResult.dice[0].options = diceSoNicePresets.hope;
rollResult.dice[1].options = diceSoNicePresets.fear;
if (rollResult.dice[2] && advantageState) { if (rollResult.dice[2] && advantageState) {
rollResult.dice[2].options = { rollResult.dice[2].options =
appearance: advantageState === 1 ? diceSoNicePresets.advantage : diceSoNicePresets.disadvantage advantageState === 1 ? diceSoNicePresets.advantage : diceSoNicePresets.disadvantage;
};
} }
}; };
@ -236,9 +239,25 @@ export const updateActorTokens = async (actor, update) => {
* @param {HTMLElement} element - The DOM element to start the search from. * @param {HTMLElement} element - The DOM element to start the search from.
* @returns {foundry.abstract.Document|null} The resolved document, or null if not found or invalid. * @returns {foundry.abstract.Document|null} The resolved document, or null if not found or invalid.
*/ */
export function getDocFromElement(element) { export async function getDocFromElement(element) {
const target = element.closest('[data-item-uuid]'); const target = element.closest('[data-item-uuid]');
return foundry.utils.fromUuidSync(target.dataset.itemUuid) ?? null; return (await foundry.utils.fromUuid(target.dataset.itemUuid)) ?? null;
}
/**
* Retrieves a Foundry document associated with the nearest ancestor element
* that has a `data-item-uuid` attribute.
* @param {HTMLElement} element - The DOM element to start the search from.
* @returns {foundry.abstract.Document|null} The resolved document, or null if not found, invalid
* or in embedded compendium collection.
*/
export function getDocFromElementSync(element) {
const target = element.closest('[data-item-uuid]');
try {
return foundry.utils.fromUuidSync(target.dataset.itemUuid) ?? null;
} catch (_) {
return null;
}
} }
/** /**
@ -256,14 +275,14 @@ export function addLinkedItemsDiff(changedItems, currentItems, options) {
newItems newItems
.difference(prevItems) .difference(prevItems)
.map(item => item?.item ?? item) .map(item => item?.item ?? item)
.filter(x => (typeof x === 'object' ? x.item : x)) .filter(x => (typeof x === 'object' ? x?.item : x))
); );
options.toUnlink = Array.from( options.toUnlink = Array.from(
prevItems prevItems
.difference(newItems) .difference(newItems)
.map(item => item?.item?.uuid ?? item?.uuid ?? item) .map(item => item?.item?.uuid ?? item?.uuid ?? item)
.filter(x => (typeof x === 'object' ? x.item : x)) .filter(x => (typeof x === 'object' ? x?.item : x))
); );
} }
} }

View file

@ -1,3 +1,5 @@
import DamageReductionDialog from '../applications/dialogs/damageReductionDialog.mjs';
export function handleSocketEvent({ action = null, data = {} } = {}) { export function handleSocketEvent({ action = null, data = {} } = {}) {
switch (action) { switch (action) {
case socketEvent.GMUpdate: case socketEvent.GMUpdate:
@ -21,7 +23,8 @@ export const socketEvent = {
export const GMUpdateEvent = { export const GMUpdateEvent = {
UpdateDocument: 'DhGMUpdateDocument', UpdateDocument: 'DhGMUpdateDocument',
UpdateSetting: 'DhGMUpdateSetting', UpdateSetting: 'DhGMUpdateSetting',
UpdateFear: 'DhGMUpdateFear' UpdateFear: 'DhGMUpdateFear',
UpdateSaveMessage: 'DhGMUpdateSaveMessage'
}; };
export const RefreshType = { export const RefreshType = {
@ -53,8 +56,12 @@ export const registerSocketHooks = () => {
) )
) )
); );
/* Hooks.callAll(socketEvent.DhpFearUpdate); break;
await game.socket.emit(`system.${CONFIG.DH.id}`, { action: socketEvent.DhpFearUpdate }); */ case GMUpdateEvent.UpdateSaveMessage:
const action = await fromUuid(data.update.action),
message = game.messages.get(data.update.message);
if(!action || !message) return;
action.updateSaveMessage(data.update.result, message, data.update.token);
break; break;
} }
@ -69,6 +76,11 @@ export const registerSocketHooks = () => {
}); });
}; };
export const registerUserQueries = () => {
CONFIG.queries.armorStack = DamageReductionDialog.armorStackQuery;
CONFIG.queries.reactionRoll = game.system.api.models.actions.actionsTypes.base.rollSaveQuery;
}
export const emitAsGM = async (eventName, callback, update, uuid = null) => { export const emitAsGM = async (eventName, callback, update, uuid = null) => {
if (!game.user.isGM) { if (!game.user.isGM) {
return await game.socket.emit(`system.${CONFIG.DH.id}`, { return await game.socket.emit(`system.${CONFIG.DH.id}`, {

View file

@ -1,11 +1,11 @@
{ {
"name": "Agile Scout", "name": "Agile Scout",
"type": "beastform", "type": "beastform",
"img": "icons/creatures/mammals/goat-horned-blue.webp", "img": "icons/creatures/mammals/rodent-rat-diseaed-gray.webp",
"system": { "system": {
"beastformType": "normal", "beastformType": "normal",
"tier": 1, "tier": 1,
"tokenImg": "icons/creatures/mammals/goat-horned-blue.webp", "tokenImg": "icons/svg/mystery-man.svg",
"tokenRingImg": "icons/svg/mystery-man.svg", "tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": { "tokenSize": {
"height": null, "height": null,
@ -13,22 +13,23 @@
}, },
"mainTrait": "agility", "mainTrait": "agility",
"advantageOn": { "advantageOn": {
"tXlf8FvWrgGqfaJu": { "mtep5GS5Ruj6ZXk1": {
"value": "deceit" "value": "Deceive"
}, },
"lp1gv9iNUCpC2Fli": { "HtQHzK88eiFGP1sW": {
"value": "locate" "value": "Locate"
}, },
"GxDMKUpOeDHzyhAT": { "pq2OXeTmvgRVWTnM": {
"value": "sneak" "value": "Sneak"
} }
}, },
"features": [ "features": [
"Compendium.daggerheart.beastforms.Item.sef9mwD2eRLZ64oV", "Compendium.daggerheart.beastforms.Item.xLS5YT1B6yeCiNTg",
"Compendium.daggerheart.beastforms.Item.9ryNrYWjNtOT6DXN" "Compendium.daggerheart.beastforms.Item.QFg1hNCEoKVDd9Zo"
], ],
"evolved": { "evolved": {
"mainTraitBonus": 0 "mainTraitBonus": 0,
"maximumTier": 1
}, },
"hybrid": { "hybrid": {
"beastformOptions": 2, "beastformOptions": 2,
@ -42,7 +43,7 @@
"type": "beastform", "type": "beastform",
"name": "Beastform Transformation", "name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp", "img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "m098fyKkAjTFZ6UJ", "_id": "ehfx2SUKMiM6f5Pd",
"system": { "system": {
"characterTokenData": { "characterTokenData": {
"tokenImg": null, "tokenImg": null,
@ -65,6 +66,18 @@
"mode": 2, "mode": 2,
"value": "2", "value": "2",
"priority": null "priority": null
},
{
"key": "system.rules.attack.damage.diceIndex",
"mode": 5,
"value": "0",
"priority": null
},
{
"key": "system.rules.attack.roll.trait",
"mode": 5,
"value": "agility",
"priority": null
} }
], ],
"disabled": false, "disabled": false,
@ -91,56 +104,16 @@
"coreVersion": "13.346", "coreVersion": "13.346",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "0.0.1",
"lastModifiedBy": null "lastModifiedBy": "FecEtPuoQh6MpjQ0",
"modifiedTime": 1753637028054
}, },
"_key": "!items.effects!6tr99y6wHaJJYy3J.m098fyKkAjTFZ6UJ" "_key": "!items.effects!a9UoCwtrbgKk02mK.ehfx2SUKMiM6f5Pd"
},
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "5mi9ku2R4paP579i",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [],
"disabled": false,
"duration": {
"startTime": null,
"combat": null
},
"description": "",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1752976985351,
"modifiedTime": 1752976985351,
"lastModifiedBy": "k0gmQFlvrPvlTtbh"
},
"_key": "!items.effects!6tr99y6wHaJJYy3J.5mi9ku2R4paP579i"
} }
], ],
"folder": null, "folder": "nhnRdZgSSrE9myIX",
"ownership": { "ownership": {
"default": 0, "default": 0,
"k0gmQFlvrPvlTtbh": 3 "FecEtPuoQh6MpjQ0": 3
}, },
"flags": {}, "flags": {},
"_stats": { "_stats": {
@ -150,11 +123,11 @@
"coreVersion": "13.346", "coreVersion": "13.346",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "0.0.1",
"createdTime": 1752976985346, "createdTime": 1753570913893,
"modifiedTime": 1752976987362, "modifiedTime": 1753575463479,
"lastModifiedBy": "k0gmQFlvrPvlTtbh" "lastModifiedBy": "FecEtPuoQh6MpjQ0"
}, },
"_id": "6tr99y6wHaJJYy3J", "_id": "a9UoCwtrbgKk02mK",
"sort": 100000, "sort": 500000,
"_key": "!items!6tr99y6wHaJJYy3J" "_key": "!items!a9UoCwtrbgKk02mK"
} }

View file

@ -0,0 +1,138 @@
{
"name": "Aquatic Predator",
"type": "beastform",
"img": "icons/creatures/fish/fish-marlin-swordfight-blue.webp",
"system": {
"beastformType": "normal",
"tier": 3,
"tokenImg": "icons/svg/mystery-man.svg",
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {
"height": null,
"width": null
},
"mainTrait": "agility",
"advantageOn": {
"1AcQrnvX9BKAWb0Y": {
"value": "Attack"
},
"BY5ht3SqyKw0dBr1": {
"value": "Swim"
},
"4OW2JewIhcKpwzVE": {
"value": "Track"
}
},
"features": [
"Compendium.daggerheart.beastforms.Item.kQWWx9P3fCyGSVOI",
"Compendium.daggerheart.beastforms.Item.jYUBi7yLHap5ljpa"
],
"evolved": {
"mainTraitBonus": 0
},
"hybrid": {
"beastformOptions": 2,
"advantages": 2,
"features": 2
},
"examples": "Dolphin, Orca, Shark, etc."
},
"effects": [
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "7OTbmhQNtKhRAl59",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [
{
"key": "system.traits.agility.value",
"mode": 2,
"value": "2",
"priority": null
},
{
"key": "system.evasion",
"mode": 2,
"value": "4",
"priority": null
},
{
"key": "system.rules.attack.damage.diceIndex",
"mode": 5,
"value": "3",
"priority": null
},
{
"key": "system.rules.attack.damage.bonus",
"mode": 5,
"value": "6",
"priority": null
},
{
"key": "system.rules.attack.roll.trait",
"mode": 5,
"value": "agility",
"priority": null
}
],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "FecEtPuoQh6MpjQ0",
"modifiedTime": 1753636850470
},
"_key": "!items.effects!ItBVeCl2u5uetgy7.7OTbmhQNtKhRAl59"
}
],
"folder": "9uPPuDAUXrVYquTw",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753626985883,
"modifiedTime": 1753626995174,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "ItBVeCl2u5uetgy7",
"sort": 0,
"_key": "!items!ItBVeCl2u5uetgy7"
}

View file

@ -0,0 +1,132 @@
{
"name": "Aquatic Scout",
"type": "beastform",
"img": "icons/creatures/tentacles/tentacles-octopus-black-pink.webp",
"system": {
"beastformType": "normal",
"tier": 1,
"tokenImg": "icons/svg/mystery-man.svg",
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {
"height": null,
"width": null
},
"mainTrait": "agility",
"advantageOn": {
"OG0TnlIP9lKye1e9": {
"value": "Navigate"
},
"q3yvEjEGqK6o4AYw": {
"value": "Sneak"
},
"RWK8rni8s7nYVP7I": {
"value": "Swim"
}
},
"features": [
"Compendium.daggerheart.beastforms.Item.kQWWx9P3fCyGSVOI",
"Compendium.daggerheart.beastforms.Item.QFg1hNCEoKVDd9Zo"
],
"evolved": {
"mainTraitBonus": 0
},
"hybrid": {
"beastformOptions": 2,
"advantages": 2,
"features": 2
},
"examples": "Eel, Fish, Octopus, etc."
},
"effects": [
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "TsXyfEOCk0ma5tp9",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [
{
"key": "system.traits.agility.value",
"mode": 2,
"value": "1",
"priority": null
},
{
"key": "system.evasion",
"mode": 2,
"value": "2",
"priority": null
},
{
"key": "system.rules.attack.damage.diceIndex",
"mode": 5,
"value": "0",
"priority": null
},
{
"key": "system.rules.attack.roll.trait",
"mode": 5,
"value": "agility",
"priority": null
}
],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "FecEtPuoQh6MpjQ0",
"modifiedTime": 1753637058698
},
"_key": "!items.effects!qqzdFCxyYupWZK23.TsXyfEOCk0ma5tp9"
}
],
"folder": "nhnRdZgSSrE9myIX",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753575463467,
"modifiedTime": 1753575469111,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "qqzdFCxyYupWZK23",
"sort": 200000,
"_key": "!items!qqzdFCxyYupWZK23"
}

View file

@ -0,0 +1,138 @@
{
"name": "Armored Sentry",
"type": "beastform",
"img": "icons/creatures/reptiles/turtle-shell-glowing-green.webp",
"system": {
"beastformType": "normal",
"tier": 2,
"tokenImg": "icons/svg/mystery-man.svg",
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {
"height": null,
"width": null
},
"mainTrait": "strength",
"advantageOn": {
"mX0DbTDuWAIpAGYq": {
"value": "Armadillo"
},
"0VGGQOhVOoNpZfdJ": {
"value": "Pangolin"
},
"6v6bkfKevJrn3YHf": {
"value": "Turtle"
}
},
"features": [
"Compendium.daggerheart.beastforms.Item.nDQZdIF2epKlhauX",
"Compendium.daggerheart.beastforms.Item.jp5KpPRBFBOIs46Q"
],
"evolved": {
"mainTraitBonus": 0
},
"hybrid": {
"beastformOptions": 2,
"advantages": 2,
"features": 2
},
"examples": "Armadillo, Pangolin, Turtle, etc."
},
"effects": [
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "hd0uVl6ZZeyPJn9O",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [
{
"key": "system.traits.strength.value",
"mode": 2,
"value": "1",
"priority": null
},
{
"key": "system.evasion",
"mode": 2,
"value": "1",
"priority": null
},
{
"key": "system.rules.attack.damage.diceIndex",
"mode": 5,
"value": "2",
"priority": null
},
{
"key": "system.rules.attack.damage.bonus",
"mode": 5,
"value": "2",
"priority": null
},
{
"key": "system.rules.attack.roll.trait",
"mode": 5,
"value": "strength",
"priority": null
}
],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "FecEtPuoQh6MpjQ0",
"modifiedTime": 1753636932760
},
"_key": "!items.effects!8pUHJv3BYdjA4Qdf.hd0uVl6ZZeyPJn9O"
}
],
"folder": "Rd30i5G7Pg0HtEUT",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753580987168,
"modifiedTime": 1753617739186,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "8pUHJv3BYdjA4Qdf",
"sort": 100000,
"_key": "!items!8pUHJv3BYdjA4Qdf"
}

View file

@ -0,0 +1,141 @@
{
"name": "Epic Aquatic Beast",
"type": "beastform",
"img": "icons/creatures/fish/squid-kraken-teal.webp",
"system": {
"beastformType": "normal",
"tier": 4,
"tokenImg": "icons/svg/mystery-man.svg",
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {
"height": null,
"width": null
},
"mainTrait": "agility",
"advantageOn": {
"DzjyAaegBTZSxhT4": {
"value": "Locate"
},
"rRDzTGkLNCbStmih": {
"value": "Protect"
},
"0k26pAQLTjfEfviV": {
"value": "Scare"
},
"zkEFnEqpAf4lqy5W": {
"value": "Track"
}
},
"features": [
"Compendium.daggerheart.beastforms.Item.tGDdEH40wyOCsFmH",
"Compendium.daggerheart.beastforms.Item.vEAQ4cfsoPmOv2Gg"
],
"evolved": {
"mainTraitBonus": 0
},
"hybrid": {
"beastformOptions": 2,
"advantages": 2,
"features": 2
},
"examples": "Giant Squid, Whale, etc."
},
"effects": [
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "zYUexUr2e3k4kzhw",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [
{
"key": "system.traits.agility.value",
"mode": 2,
"value": "3",
"priority": null
},
{
"key": "system.evasion",
"mode": 2,
"value": "3",
"priority": null
},
{
"key": "system.rules.attack.damage.diceIndex",
"mode": 5,
"value": "3",
"priority": null
},
{
"key": "system.rules.attack.damage.bonus",
"mode": 5,
"value": "10",
"priority": null
},
{
"key": "system.rules.attack.roll.trait",
"mode": 5,
"value": "agility",
"priority": null
}
],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "FecEtPuoQh6MpjQ0",
"modifiedTime": 1753636776763
},
"_key": "!items.effects!wT4xbF99I55yjKZV.zYUexUr2e3k4kzhw"
}
],
"folder": "UQEr5SAm2Z1Aih1S",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753628697986,
"modifiedTime": 1753628714911,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "wT4xbF99I55yjKZV",
"sort": 0,
"_key": "!items!wT4xbF99I55yjKZV"
}

View file

@ -0,0 +1,138 @@
{
"name": "Great Predator",
"type": "beastform",
"img": "icons/creatures/mammals/wolf-shadow-black.webp",
"system": {
"beastformType": "normal",
"tier": 3,
"tokenImg": "icons/svg/mystery-man.svg",
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {
"height": null,
"width": null
},
"mainTrait": "strength",
"advantageOn": {
"WvQYbcASMk0IwESy": {
"value": "Attack"
},
"Hzr7zL547nyrZyZK": {
"value": "Sneak"
},
"3wDuhdlzs3gOJ9l9": {
"value": "Sprint"
}
},
"features": [
"Compendium.daggerheart.beastforms.Item.EVOJTskJYf4rpuga",
"Compendium.daggerheart.beastforms.Item.jYUBi7yLHap5ljpa"
],
"evolved": {
"mainTraitBonus": 0
},
"hybrid": {
"beastformOptions": 2,
"advantages": 2,
"features": 2
},
"examples": "Dire Wolf, Velociraptor, Sabertooth Tiger, etc."
},
"effects": [
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "TFzvHClwIAQUFxyP",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [
{
"key": "system.traits.strength.value",
"mode": 2,
"value": "2",
"priority": null
},
{
"key": "system.evasion",
"mode": 2,
"value": "2",
"priority": null
},
{
"key": "system.rules.attack.damage.diceIndex",
"mode": 5,
"value": "4",
"priority": null
},
{
"key": "system.rules.attack.damage.bonus",
"mode": 5,
"value": "8",
"priority": null
},
{
"key": "system.rules.attack.roll.trait",
"mode": 5,
"value": "strength",
"priority": null
}
],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "FecEtPuoQh6MpjQ0",
"modifiedTime": 1753636866636
},
"_key": "!items.effects!afbMt4Ld6nY3mw0N.TFzvHClwIAQUFxyP"
}
],
"folder": "9uPPuDAUXrVYquTw",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753625648103,
"modifiedTime": 1753626865950,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "afbMt4Ld6nY3mw0N",
"sort": 100000,
"_key": "!items!afbMt4Ld6nY3mw0N"
}

View file

@ -0,0 +1,138 @@
{
"name": "Great Winged Beast",
"type": "beastform",
"img": "icons/creatures/birds/corvid-flying-wings-purple.webp",
"system": {
"beastformType": "normal",
"tier": 3,
"tokenImg": "icons/svg/mystery-man.svg",
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {
"height": null,
"width": null
},
"mainTrait": "finesse",
"advantageOn": {
"9J8GmsoqjBiXJLKE": {
"value": "Deceive"
},
"MkD8mmiirYPqs0oA": {
"value": "Distract"
},
"qzud50ZkqNJgYhRH": {
"value": "Locate"
}
},
"features": [
"Compendium.daggerheart.beastforms.Item.FNKQlWQcArSorMPK",
"Compendium.daggerheart.beastforms.Item.EVOJTskJYf4rpuga"
],
"evolved": {
"mainTraitBonus": 0
},
"hybrid": {
"beastformOptions": 2,
"advantages": 2,
"features": 2
},
"examples": "Giant Eagle, Falcon, etc."
},
"effects": [
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "qGSm1QpICpxRZxLn",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [
{
"key": "system.traits.finesse.value",
"mode": 2,
"value": "2",
"priority": null
},
{
"key": "system.evasion",
"mode": 2,
"value": "3",
"priority": null
},
{
"key": "system.rules.attack.damage.diceIndex",
"mode": 5,
"value": "2",
"priority": null
},
{
"key": "system.rules.attack.damage.bonus",
"mode": 5,
"value": "6",
"priority": null
},
{
"key": "system.rules.attack.roll.trait",
"mode": 5,
"value": "finesse",
"priority": null
}
],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "FecEtPuoQh6MpjQ0",
"modifiedTime": 1753636883399
},
"_key": "!items.effects!b4BMnTbJ3iPPidSb.qGSm1QpICpxRZxLn"
}
],
"folder": "9uPPuDAUXrVYquTw",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753626865938,
"modifiedTime": 1753626874515,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "b4BMnTbJ3iPPidSb",
"sort": 200000,
"_key": "!items!b4BMnTbJ3iPPidSb"
}

View file

@ -0,0 +1,132 @@
{
"name": "Household Friend",
"type": "beastform",
"img": "icons/creatures/mammals/rabbit-movement-glowing-green.webp",
"system": {
"beastformType": "normal",
"tier": 1,
"tokenImg": "icons/svg/mystery-man.svg",
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {
"height": null,
"width": null
},
"mainTrait": "instinct",
"advantageOn": {
"CXfEZDZ96ajdn5aU": {
"value": "Climb"
},
"IojJaTrMIkT4xFSN": {
"value": "Locate"
},
"IwwGzrAig49bAy8y": {
"value": "Protect"
}
},
"features": [
"Compendium.daggerheart.beastforms.Item.jhWSC5bNZyYUAA5Q",
"Compendium.daggerheart.beastforms.Item.QFg1hNCEoKVDd9Zo"
],
"evolved": {
"mainTraitBonus": 0
},
"hybrid": {
"beastformOptions": 2,
"advantages": 2,
"features": 2
},
"examples": "Cat, Dog, Rabbit, etc."
},
"effects": [
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "CzMAMg2q5gL15JrZ",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [
{
"key": "system.traits.instinct.value",
"mode": 2,
"value": "1",
"priority": null
},
{
"key": "system.evasion",
"mode": 2,
"value": "2",
"priority": null
},
{
"key": "system.rules.attack.damage.diceIndex",
"mode": 5,
"value": "1",
"priority": null
},
{
"key": "system.rules.attack.roll.trait",
"mode": 5,
"value": "instinct",
"priority": null
}
],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "FecEtPuoQh6MpjQ0",
"modifiedTime": 1753637069907
},
"_key": "!items.effects!iDmOtiHJJ80AIAVT.CzMAMg2q5gL15JrZ"
}
],
"folder": "nhnRdZgSSrE9myIX",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753573035973,
"modifiedTime": 1753575463479,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "iDmOtiHJJ80AIAVT",
"sort": 100000,
"_key": "!items!iDmOtiHJJ80AIAVT"
}

View file

@ -1,166 +0,0 @@
{
"name": "Household Friend",
"type": "beastform",
"img": "icons/creatures/mammals/cat-hunched-glowing-red.webp",
"system": {
"beastformType": "normal",
"tier": 1,
"tokenImg": "icons/creatures/mammals/cat-hunched-glowing-red.webp",
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {
"height": null,
"width": null
},
"mainTrait": "instinct",
"advantageOn": {
"u0mzlWihDHITgh1x": {
"value": "climb"
},
"m53oFXA2SA5jAjWc": {
"value": "locate"
},
"XLPn5Egg9mIuLyVP": {
"value": "protect"
}
},
"features": [
"Compendium.daggerheart.beastforms.Item.0tlnxIxlIw2hl1UE",
"Compendium.daggerheart.beastforms.Item.9ryNrYWjNtOT6DXN"
],
"evolved": {
"mainTraitBonus": 0
},
"hybrid": {
"beastformOptions": 2,
"advantages": 2,
"features": 2
},
"examples": "Cat, Dog, Rabbit, etc."
},
"effects": [
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "d25tcdgssnDvekKR",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [
{
"key": "system.rules.attack.trait",
"mode": 5,
"value": "instinct",
"priority": null
},
{
"key": "system.rules.attack.range",
"mode": 5,
"value": "melee",
"priority": null
},
{
"key": "system.rules.attack.damage.value",
"mode": 5,
"value": "1d8",
"priority": null
}
],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": null
},
"_key": "!items.effects!uxBugKULjn7O1KQc.d25tcdgssnDvekKR"
},
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "idqGh9sm1zBLME1O",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [],
"disabled": false,
"duration": {
"startTime": null,
"combat": null
},
"description": "",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1752976986453,
"modifiedTime": 1752976986453,
"lastModifiedBy": "k0gmQFlvrPvlTtbh"
},
"_key": "!items.effects!uxBugKULjn7O1KQc.idqGh9sm1zBLME1O"
}
],
"folder": null,
"ownership": {
"default": 0,
"k0gmQFlvrPvlTtbh": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1752976986449,
"modifiedTime": 1752976987362,
"lastModifiedBy": "k0gmQFlvrPvlTtbh"
},
"_id": "uxBugKULjn7O1KQc",
"sort": 300000,
"_key": "!items!uxBugKULjn7O1KQc"
}

View file

@ -1,11 +1,11 @@
{ {
"name": "Legendary Beast", "name": "Legendary Beast",
"type": "beastform", "type": "beastform",
"img": "icons/creatures/magical/spirit-undead-horned-blue.webp", "img": "icons/creatures/magical/humanoid-giant-forest-blue.webp",
"system": { "system": {
"beastformType": "evolved", "beastformType": "evolved",
"tier": 3, "tier": 3,
"tokenImg": "icons/creatures/magical/spirit-undead-horned-blue.webp", "tokenImg": "icons/svg/mystery-man.svg",
"tokenRingImg": "icons/svg/mystery-man.svg", "tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": { "tokenSize": {
"height": null, "height": null,
@ -30,7 +30,7 @@
"type": "beastform", "type": "beastform",
"name": "Beastform Transformation", "name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp", "img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "cKAoI5JqYOtGBscd", "_id": "cL4kH57pf2nGIxaK",
"system": { "system": {
"characterTokenData": { "characterTokenData": {
"tokenImg": null, "tokenImg": null,
@ -48,12 +48,6 @@
"value": "6", "value": "6",
"priority": null "priority": null
}, },
{
"key": "system.bonuses.damage.magical.bonus",
"mode": 2,
"value": "6",
"priority": null
},
{ {
"key": "system.evasion", "key": "system.evasion",
"mode": 2, "mode": 2,
@ -71,7 +65,7 @@
"startRound": null, "startRound": null,
"startTurn": null "startTurn": null
}, },
"description": "<p>Pick a Tier 1 Beastform option and become a larger, more powerful version of that creature. While youre in this form, you retain all traits and features from the original form and gain the following bonuses:</p><ul><li><p>- A +6 bonus to damage rolls</p></li><li><p>- A +1 bonus to the trait used by this form</p></li><li><p>- A +2 bonus to evasion</p></li></ul>", "description": "<p>Pick a Tier 1 Beastform option and become a larger, more powerful version of that creature. While youre in this form, you retain all traits and features from the original form and gain the following bonuses:</p><ul><li>A +6 bonus to the damage rolls</li><li>A +1 bonus to the trait used by this form</li><li>A +2 bonus to evasion</li></ul>",
"origin": null, "origin": null,
"tint": "#ffffff", "tint": "#ffffff",
"transfer": true, "transfer": true,
@ -85,56 +79,16 @@
"coreVersion": "13.346", "coreVersion": "13.346",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "0.0.1",
"lastModifiedBy": null "lastModifiedBy": "FecEtPuoQh6MpjQ0",
"modifiedTime": 1753629435612
}, },
"_key": "!items.effects!mERwC7aMDoIKfZTf.cKAoI5JqYOtGBscd" "_key": "!items.effects!mqP6z4Wg4K3oDAom.cL4kH57pf2nGIxaK"
},
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "LltLvTqjhk9RseV8",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [],
"disabled": false,
"duration": {
"startTime": null,
"combat": null
},
"description": "",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1752976989252,
"modifiedTime": 1752976989252,
"lastModifiedBy": "k0gmQFlvrPvlTtbh"
},
"_key": "!items.effects!mERwC7aMDoIKfZTf.LltLvTqjhk9RseV8"
} }
], ],
"folder": null, "folder": "9uPPuDAUXrVYquTw",
"ownership": { "ownership": {
"default": 0, "default": 0,
"k0gmQFlvrPvlTtbh": 3 "FecEtPuoQh6MpjQ0": 3
}, },
"flags": {}, "flags": {},
"_stats": { "_stats": {
@ -144,11 +98,11 @@
"coreVersion": "13.346", "coreVersion": "13.346",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "0.0.1",
"createdTime": 1752976989245, "createdTime": 1753627165434,
"modifiedTime": 1752976989245, "modifiedTime": 1753627165434,
"lastModifiedBy": "k0gmQFlvrPvlTtbh" "lastModifiedBy": "FecEtPuoQh6MpjQ0"
}, },
"_id": "mERwC7aMDoIKfZTf", "_id": "mqP6z4Wg4K3oDAom",
"sort": 0, "sort": 0,
"_key": "!items!mERwC7aMDoIKfZTf" "_key": "!items!mqP6z4Wg4K3oDAom"
} }

View file

@ -0,0 +1,126 @@
{
"name": "Legendary Hybrid",
"type": "beastform",
"img": "icons/creatures/magical/humanoid-silhouette-glowing-pink.webp",
"system": {
"beastformType": "hybrid",
"tier": 3,
"tokenImg": "icons/svg/mystery-man.svg",
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {
"height": null,
"width": null
},
"mainTrait": "strength",
"advantageOn": {},
"features": [],
"evolved": {
"mainTraitBonus": 0
},
"hybrid": {
"beastformOptions": 2,
"advantages": 4,
"features": 2,
"maximumTier": 2
},
"examples": ""
},
"effects": [
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "RbZKFWATkfLZm67j",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [
{
"key": "system.traits.strength.value",
"mode": 2,
"value": "2",
"priority": null
},
{
"key": "system.evasion",
"mode": 2,
"value": "3",
"priority": null
},
{
"key": "system.rules.attack.damage.diceIndex",
"mode": 5,
"value": "3",
"priority": null
},
{
"key": "system.rules.attack.damage.bonus",
"mode": 5,
"value": "8",
"priority": null
},
{
"key": "system.rules.attack.roll.trait",
"mode": 5,
"value": "strength",
"priority": null
}
],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "<p>To transform into this creature, <strong>mark an additional Stress</strong>. Choose any two Beastform options from Tiers 12. Choose a total of four advantages and two features from those options.</p>",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "FecEtPuoQh6MpjQ0",
"modifiedTime": 1753636904176
},
"_key": "!items.effects!rRUtgcUjimlpPhnn.RbZKFWATkfLZm67j"
}
],
"folder": "9uPPuDAUXrVYquTw",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753627303526,
"modifiedTime": 1753627303526,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "rRUtgcUjimlpPhnn",
"sort": 0,
"_key": "!items!rRUtgcUjimlpPhnn"
}

View file

@ -0,0 +1,142 @@
{
"name": "Massive Behemoth",
"type": "beastform",
"img": "icons/creatures/mammals/beast-horned-scaled-glowing-orange.webp",
"system": {
"beastformType": "normal",
"tier": 4,
"tokenImg": "icons/svg/mystery-man.svg",
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {
"height": null,
"width": null
},
"mainTrait": "strength",
"advantageOn": {
"4dxvlv6F3kjurgJg": {
"value": "Locate"
},
"WiskYFviMDpdKcR6": {
"value": "Protect"
},
"AoFiYKVbPxGalK8Y": {
"value": "Scare"
},
"TMzWo667tWr355c9": {
"value": "Sprint"
}
},
"features": [
"Compendium.daggerheart.beastforms.Item.EVOJTskJYf4rpuga",
"Compendium.daggerheart.beastforms.Item.DfBXO8jTchwFG8dZ",
"Compendium.daggerheart.beastforms.Item.ODudjX88Te4vDP57"
],
"evolved": {
"mainTraitBonus": 0
},
"hybrid": {
"beastformOptions": 2,
"advantages": 2,
"features": 2
},
"examples": "Elephant, Mammoth, Rhinoceros, etc."
},
"effects": [
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "RR6ySJU5zaC7ws6A",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [
{
"key": "system.traits.strength.value",
"mode": 2,
"value": "3",
"priority": null
},
{
"key": "system.evasion",
"mode": 2,
"value": "1",
"priority": null
},
{
"key": "system.rules.attack.damage.diceIndex",
"mode": 5,
"value": "4",
"priority": null
},
{
"key": "system.rules.attack.damage.bonus",
"mode": 5,
"value": "12",
"priority": null
},
{
"key": "system.rules.attack.roll.trait",
"mode": 5,
"value": "strength",
"priority": null
}
],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "FecEtPuoQh6MpjQ0",
"modifiedTime": 1753636791070
},
"_key": "!items.effects!qjwMzPn33aKZACkv.RR6ySJU5zaC7ws6A"
}
],
"folder": "UQEr5SAm2Z1Aih1S",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753627711166,
"modifiedTime": 1753631381561,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "qjwMzPn33aKZACkv",
"sort": 100000,
"_key": "!items!qjwMzPn33aKZACkv"
}

View file

@ -0,0 +1,138 @@
{
"name": "Mighty Lizard",
"type": "beastform",
"img": "icons/creatures/reptiles/lizard-iguana-green.webp",
"system": {
"beastformType": "normal",
"tier": 3,
"tokenImg": "icons/svg/mystery-man.svg",
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {
"height": null,
"width": null
},
"mainTrait": "instinct",
"advantageOn": {
"f5tUBKOfwDQKPrYP": {
"value": "Attack"
},
"XS1zEu9IJ6ec0mZ0": {
"value": "Sneak"
},
"K0hUzxErOc7t0myx": {
"value": "Track"
}
},
"features": [
"Compendium.daggerheart.beastforms.Item.StabkQ3BzWRZa8Tz",
"Compendium.daggerheart.beastforms.Item.Ky3rZD3sJMXYZOBC"
],
"evolved": {
"mainTraitBonus": 0
},
"hybrid": {
"beastformOptions": 2,
"advantages": 2,
"features": 2
},
"examples": "Alligator, Crocodile, Gila Monster, etc."
},
"effects": [
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "d13EzuP8Co04Jk8K",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [
{
"key": "system.traits.instinct.value",
"mode": 2,
"value": "2",
"priority": null
},
{
"key": "system.evasion",
"mode": 2,
"value": "1",
"priority": null
},
{
"key": "system.rules.attack.damage.diceIndex",
"mode": 5,
"value": "3",
"priority": null
},
{
"key": "system.rules.attack.damage.bonus",
"mode": 5,
"value": "7",
"priority": null
},
{
"key": "system.rules.attack.roll.trait",
"mode": 5,
"value": "instinct",
"priority": null
}
],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "FecEtPuoQh6MpjQ0",
"modifiedTime": 1753636918241
},
"_key": "!items.effects!94tvcC3D5Kp4lzuN.d13EzuP8Co04Jk8K"
}
],
"folder": "9uPPuDAUXrVYquTw",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753626720443,
"modifiedTime": 1753626865950,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "94tvcC3D5Kp4lzuN",
"sort": 300000,
"_key": "!items!94tvcC3D5Kp4lzuN"
}

View file

@ -1,11 +1,11 @@
{ {
"name": "Mighty Strider", "name": "Mighty Strider",
"type": "beastform", "type": "beastform",
"img": "icons/creatures/mammals/bull-horned-blue.webp", "img": "icons/environment/creatures/horse-tan.webp",
"system": { "system": {
"beastformType": "normal", "beastformType": "normal",
"tier": 2, "tier": 2,
"tokenImg": "icons/creatures/mammals/bull-horned-blue.webp", "tokenImg": "icons/svg/mystery-man.svg",
"tokenRingImg": "icons/svg/mystery-man.svg", "tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": { "tokenSize": {
"height": null, "height": null,
@ -13,19 +13,19 @@
}, },
"mainTrait": "agility", "mainTrait": "agility",
"advantageOn": { "advantageOn": {
"Xxr01TwSerOS8qsd": { "Az0LCAgV6NQMddlz": {
"value": "leap" "value": "Leap"
}, },
"cASut9AUij2Uf4zm": { "6Povd6wfN4USSgbL": {
"value": "navigate" "value": "Navigate"
}, },
"XJie4FhCSwUCg9uN": { "wnHlu70U2vvDKNIo": {
"value": "sprint" "value": "Sprint"
} }
}, },
"features": [ "features": [
"Compendium.daggerheart.beastforms.Item.YSolAjtv6Sfnai98", "Compendium.daggerheart.beastforms.Item.EVOJTskJYf4rpuga",
"Compendium.daggerheart.beastforms.Item.P6tWFIZzXWyekw6r" "Compendium.daggerheart.beastforms.Item.A0lgd6eVEfX6oqSB"
], ],
"evolved": { "evolved": {
"mainTraitBonus": 0 "mainTraitBonus": 0
@ -42,7 +42,7 @@
"type": "beastform", "type": "beastform",
"name": "Beastform Transformation", "name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp", "img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "1KdhXARm6rg2fg22", "_id": "IWg2cWUdvucUOLAL",
"system": { "system": {
"characterTokenData": { "characterTokenData": {
"tokenImg": null, "tokenImg": null,
@ -55,22 +55,34 @@
}, },
"changes": [ "changes": [
{ {
"key": "system.rules.attack.damage.value", "key": "system.traits.agility.value",
"mode": 5, "mode": 2,
"value": "d8 + 1", "value": "1",
"priority": null "priority": null
}, },
{ {
"key": "system.rules.attack.trait", "key": "system.evasion",
"mode": 2,
"value": "2",
"priority": null
},
{
"key": "system.rules.attack.damage.diceIndex",
"mode": 5,
"value": "2",
"priority": null
},
{
"key": "system.rules.attack.damage.bonus",
"mode": 5,
"value": "1",
"priority": null
},
{
"key": "system.rules.attack.roll.trait",
"mode": 5, "mode": 5,
"value": "agility", "value": "agility",
"priority": null "priority": null
},
{
"key": "system.rules.attack.range",
"mode": 5,
"value": "melee",
"priority": null
} }
], ],
"disabled": false, "disabled": false,
@ -97,56 +109,16 @@
"coreVersion": "13.346", "coreVersion": "13.346",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "0.0.1",
"lastModifiedBy": null "lastModifiedBy": "FecEtPuoQh6MpjQ0",
"modifiedTime": 1753636945655
}, },
"_key": "!items.effects!LF68kGAcOTZQ81GB.1KdhXARm6rg2fg22" "_key": "!items.effects!zRLjqKx4Rn2TjivL.IWg2cWUdvucUOLAL"
},
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "ngEREmS8hzNyshak",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [],
"disabled": false,
"duration": {
"startTime": null,
"combat": null
},
"description": "",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1752976987358,
"modifiedTime": 1752976987358,
"lastModifiedBy": "k0gmQFlvrPvlTtbh"
},
"_key": "!items.effects!LF68kGAcOTZQ81GB.ngEREmS8hzNyshak"
} }
], ],
"folder": null, "folder": "Rd30i5G7Pg0HtEUT",
"ownership": { "ownership": {
"default": 0, "default": 0,
"k0gmQFlvrPvlTtbh": 3 "FecEtPuoQh6MpjQ0": 3
}, },
"flags": {}, "flags": {},
"_stats": { "_stats": {
@ -156,11 +128,11 @@
"coreVersion": "13.346", "coreVersion": "13.346",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "0.0.1",
"createdTime": 1752976987354, "createdTime": 1753617739175,
"modifiedTime": 1752976987362, "modifiedTime": 1753617745460,
"lastModifiedBy": "k0gmQFlvrPvlTtbh" "lastModifiedBy": "FecEtPuoQh6MpjQ0"
}, },
"_id": "LF68kGAcOTZQ81GB", "_id": "zRLjqKx4Rn2TjivL",
"sort": 200000, "sort": 200000,
"_key": "!items!LF68kGAcOTZQ81GB" "_key": "!items!zRLjqKx4Rn2TjivL"
} }

View file

@ -0,0 +1,141 @@
{
"name": "Mythic Aerial Hunter",
"type": "beastform",
"img": "icons/creatures/reptiles/dragon-winged-blue.webp",
"system": {
"beastformType": "normal",
"tier": 4,
"tokenImg": "icons/svg/mystery-man.svg",
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {
"height": null,
"width": null
},
"mainTrait": "finesse",
"advantageOn": {
"wqIBmWSIbwwJdB07": {
"value": "Attack"
},
"wUjIM4ohI1309Qmy": {
"value": "Deceive"
},
"7YTz0VSytRCmpn3Y": {
"value": "Locate"
},
"YyyzVE5FOevDKvAP": {
"value": "Navigate"
}
},
"features": [
"Compendium.daggerheart.beastforms.Item.EVOJTskJYf4rpuga",
"Compendium.daggerheart.beastforms.Item.QQtQ77tos8ijTHag"
],
"evolved": {
"mainTraitBonus": 0
},
"hybrid": {
"beastformOptions": 2,
"advantages": 2,
"features": 2
},
"examples": "Dragon, Pterodactyl, Roc, Wyvern, etc."
},
"effects": [
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "hnKnaOaswWJdaYmf",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [
{
"key": "system.traits.finesse.value",
"mode": 2,
"value": "3",
"priority": null
},
{
"key": "system.evasion",
"mode": 2,
"value": "4",
"priority": null
},
{
"key": "system.rules.attack.damage.diceIndex",
"mode": 5,
"value": "3",
"priority": null
},
{
"key": "system.rules.attack.damage.bonus",
"mode": 5,
"value": "11",
"priority": null
},
{
"key": "system.rules.attack.roll.trait",
"mode": 5,
"value": "finesse",
"priority": null
}
],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "FecEtPuoQh6MpjQ0",
"modifiedTime": 1753636808135
},
"_key": "!items.effects!jV6EuEacyQlHW4SN.hnKnaOaswWJdaYmf"
}
],
"folder": "UQEr5SAm2Z1Aih1S",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753628382723,
"modifiedTime": 1753628401450,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "jV6EuEacyQlHW4SN",
"sort": 200000,
"_key": "!items!jV6EuEacyQlHW4SN"
}

View file

@ -0,0 +1,114 @@
{
"name": "Mythic Beast",
"type": "beastform",
"img": "icons/creatures/eyes/lizard-single-slit-pink.webp",
"system": {
"beastformType": "evolved",
"tier": 4,
"tokenImg": "icons/svg/mystery-man.svg",
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {
"height": null,
"width": null
},
"mainTrait": "agility",
"advantageOn": {},
"features": [],
"evolved": {
"mainTraitBonus": 2,
"maximumTier": 2
},
"hybrid": {
"beastformOptions": 2,
"advantages": 2,
"features": 2
},
"examples": ""
},
"effects": [
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "vhwbPQ2YT64qD1t1",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [
{
"key": "system.bonuses.damage.physical.bonus",
"mode": 2,
"value": "9",
"priority": null
},
{
"key": "system.evasion",
"mode": 2,
"value": "3",
"priority": null
},
{
"key": "system.rules.attack.damage.diceIndex",
"mode": 2,
"value": "1",
"priority": 60
}
],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "<p>Pick a Tier 1 or Tier 2 Beastform option and become a larger, more powerful version of that creature. While youre in this form, you retain all traits and features from the original form and gain the the following bonuses:</p><ul><li>A +9 bonus to damage rolls</li><li>A +2 bonus to the trait used by this form</li><li>A +3 bonus to Evasion</li><li>Your damage die increases by one size (d6 becomes d8, d8 becomes d10 etc.)</li></ul>",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "FecEtPuoQh6MpjQ0",
"modifiedTime": 1753636223126
},
"_key": "!items.effects!kObobka52JdpWBSu.vhwbPQ2YT64qD1t1"
}
],
"folder": "UQEr5SAm2Z1Aih1S",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753628844905,
"modifiedTime": 1753628844905,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "kObobka52JdpWBSu",
"sort": 0,
"_key": "!items!kObobka52JdpWBSu"
}

View file

@ -1,11 +1,11 @@
{ {
"name": "Mythic Hybrid", "name": "Mythic Hybrid",
"type": "beastform", "type": "beastform",
"img": "icons/creatures/magical/humanoid-silhouette-glowing-pink.webp", "img": "icons/creatures/magical/spirit-undead-horned-blue.webp",
"system": { "system": {
"beastformType": "hybrid", "beastformType": "hybrid",
"tier": 4, "tier": 4,
"tokenImg": "icons/creatures/magical/humanoid-silhouette-glowing-pink.webp", "tokenImg": "icons/svg/mystery-man.svg",
"tokenRingImg": "icons/svg/mystery-man.svg", "tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": { "tokenSize": {
"height": null, "height": null,
@ -30,7 +30,7 @@
"type": "beastform", "type": "beastform",
"name": "Beastform Transformation", "name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp", "img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "dvqS0a0ur8xM2swY", "_id": "HutJQ9HGtr1Eoibp",
"system": { "system": {
"characterTokenData": { "characterTokenData": {
"tokenImg": null, "tokenImg": null,
@ -53,6 +53,24 @@
"mode": 2, "mode": 2,
"value": "2", "value": "2",
"priority": null "priority": null
},
{
"key": "system.rules.attack.damage.diceIndex",
"mode": 5,
"value": "4",
"priority": null
},
{
"key": "system.rules.attack.damage.bonus",
"mode": 5,
"value": "10",
"priority": null
},
{
"key": "system.rules.attack.roll.trait",
"mode": 5,
"value": "strength",
"priority": null
} }
], ],
"disabled": false, "disabled": false,
@ -65,7 +83,7 @@
"startRound": null, "startRound": null,
"startTurn": null "startTurn": null
}, },
"description": "<p>To transform into this creature, mark 2 additional Stress. Choose any three Beastform options from Tiers 13. Choose a total of five advantages and three features from those options.</p>", "description": "<p>To transform into this creature, <strong>mark 2 additional Stress</strong>. Choose any three Beastform options from Tiers 13. Choose a total of five advantages and three features from those options.</p>",
"origin": null, "origin": null,
"tint": "#ffffff", "tint": "#ffffff",
"transfer": true, "transfer": true,
@ -79,56 +97,16 @@
"coreVersion": "13.346", "coreVersion": "13.346",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "0.0.1",
"lastModifiedBy": null "lastModifiedBy": "FecEtPuoQh6MpjQ0",
"modifiedTime": 1753636821848
}, },
"_key": "!items.effects!VI1DyowECDCDdsC1.dvqS0a0ur8xM2swY" "_key": "!items.effects!WAbxCf2An8qmxyJ1.HutJQ9HGtr1Eoibp"
},
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "xSMy4BO5PuqASEKW",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [],
"disabled": false,
"duration": {
"startTime": null,
"combat": null
},
"description": "",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1752976990470,
"modifiedTime": 1752976990470,
"lastModifiedBy": "k0gmQFlvrPvlTtbh"
},
"_key": "!items.effects!VI1DyowECDCDdsC1.xSMy4BO5PuqASEKW"
} }
], ],
"folder": null, "folder": "UQEr5SAm2Z1Aih1S",
"ownership": { "ownership": {
"default": 0, "default": 0,
"k0gmQFlvrPvlTtbh": 3 "FecEtPuoQh6MpjQ0": 3
}, },
"flags": {}, "flags": {},
"_stats": { "_stats": {
@ -138,11 +116,11 @@
"coreVersion": "13.346", "coreVersion": "13.346",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "0.0.1",
"createdTime": 1752976990466, "createdTime": 1753628965658,
"modifiedTime": 1752976990466, "modifiedTime": 1753628965658,
"lastModifiedBy": "k0gmQFlvrPvlTtbh" "lastModifiedBy": "FecEtPuoQh6MpjQ0"
}, },
"_id": "VI1DyowECDCDdsC1", "_id": "WAbxCf2An8qmxyJ1",
"sort": 0, "sort": 0,
"_key": "!items!VI1DyowECDCDdsC1" "_key": "!items!WAbxCf2An8qmxyJ1"
} }

View file

@ -0,0 +1,132 @@
{
"name": "Nimble Grazer",
"type": "beastform",
"img": "icons/creatures/mammals/deer-antlers-glowing-blue.webp",
"system": {
"beastformType": "normal",
"tier": 1,
"tokenImg": "icons/svg/mystery-man.svg",
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {
"height": null,
"width": null
},
"mainTrait": "agility",
"advantageOn": {
"pZh7UYwWgdddKX77": {
"value": "Leap"
},
"MVO36LtrjuSU4cAv": {
"value": "Sneak"
},
"P9W2JIx0FJgQf6b6": {
"value": "Sprint"
}
},
"features": [
"Compendium.daggerheart.beastforms.Item.a7Qvmm14nx9BCysA",
"Compendium.daggerheart.beastforms.Item.QFg1hNCEoKVDd9Zo"
],
"evolved": {
"mainTraitBonus": 0
},
"hybrid": {
"beastformOptions": 2,
"advantages": 2,
"features": 2
},
"examples": "Deer, Gazelle, Goat, etc."
},
"effects": [
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "lIbJFb5XQ98Eaauu",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [
{
"key": "system.traits.agility.value",
"mode": 2,
"value": "1",
"priority": null
},
{
"key": "system.evasion",
"mode": 2,
"value": "3",
"priority": null
},
{
"key": "system.rules.attack.damage.diceIndex",
"mode": 5,
"value": "1",
"priority": null
},
{
"key": "system.rules.attack.roll.trait",
"mode": 5,
"value": "agility",
"priority": null
}
],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "FecEtPuoQh6MpjQ0",
"modifiedTime": 1753637082422
},
"_key": "!items.effects!CItO8yX6amQaqyk7.lIbJFb5XQ98Eaauu"
}
],
"folder": "nhnRdZgSSrE9myIX",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753574930310,
"modifiedTime": 1753575463479,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "CItO8yX6amQaqyk7",
"sort": 300000,
"_key": "!items!CItO8yX6amQaqyk7"
}

View file

@ -0,0 +1,138 @@
{
"name": "Pack Predator",
"type": "beastform",
"img": "icons/creatures/mammals/wolf-howl-moon-forest-blue.webp",
"system": {
"beastformType": "normal",
"tier": 1,
"tokenImg": "icons/svg/mystery-man.svg",
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {
"height": null,
"width": null
},
"mainTrait": "strength",
"advantageOn": {
"WwCjWpa7n6KcP3kY": {
"value": "Attack"
},
"oNzBOc2bkcb7CrQk": {
"value": "Sprint"
},
"wtz5CPetdNzXTyBA": {
"value": "Track"
}
},
"features": [
"Compendium.daggerheart.beastforms.Item.8u0HkK3WgtU9lWYs",
"Compendium.daggerheart.beastforms.Item.d3q8lfeiEMyTjusT"
],
"evolved": {
"mainTraitBonus": 0
},
"hybrid": {
"beastformOptions": 2,
"advantages": 2,
"features": 2
},
"examples": "Coyote, Hyena, Wolf, etc."
},
"effects": [
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "amuFS9LlEnzZnaIN",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [
{
"key": "system.traits.strength.value",
"mode": 2,
"value": "2",
"priority": null
},
{
"key": "system.evasion",
"mode": 2,
"value": "1",
"priority": null
},
{
"key": "system.rules.attack.damage.diceIndex",
"mode": 5,
"value": "2",
"priority": null
},
{
"key": "system.rules.attack.damage.bonus",
"mode": 5,
"value": "2",
"priority": null
},
{
"key": "system.rules.attack.roll.trait",
"mode": 5,
"value": "strength",
"priority": null
}
],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "FecEtPuoQh6MpjQ0",
"modifiedTime": 1753637098077
},
"_key": "!items.effects!YLisKYYhAGca50WM.amuFS9LlEnzZnaIN"
}
],
"folder": "nhnRdZgSSrE9myIX",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753575274807,
"modifiedTime": 1753575463479,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "YLisKYYhAGca50WM",
"sort": 400000,
"_key": "!items!YLisKYYhAGca50WM"
}

View file

@ -0,0 +1,138 @@
{
"name": "Pouncing Predator",
"type": "beastform",
"img": "icons/creatures/abilities/cougar-roar-rush-orange.webp",
"system": {
"beastformType": "normal",
"tier": 2,
"tokenImg": "icons/svg/mystery-man.svg",
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {
"height": null,
"width": null
},
"mainTrait": "instinct",
"advantageOn": {
"q5bjThxdJ1JAuX2r": {
"value": "Attack"
},
"paOxE0DUkqKYVbss": {
"value": "Climb"
},
"nDnJf1tiImA6ryyy": {
"value": "Sneak"
}
},
"features": [
"Compendium.daggerheart.beastforms.Item.GhHsSHOa509cwCvr",
"Compendium.daggerheart.beastforms.Item.0ey4kM9ssj2otHvb"
],
"evolved": {
"mainTraitBonus": 0
},
"hybrid": {
"beastformOptions": 2,
"advantages": 2,
"features": 2
},
"examples": "Cheetah, Lion, Panther, etc."
},
"effects": [
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "GSmW6nJ3kyIoEK2H",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [
{
"key": "system.traits.instinct.value",
"mode": 2,
"value": "1",
"priority": null
},
{
"key": "system.evasion",
"mode": 2,
"value": "3",
"priority": null
},
{
"key": "system.rules.attack.damage.diceIndex",
"mode": 5,
"value": "2",
"priority": null
},
{
"key": "system.rules.attack.damage.bonus",
"mode": 5,
"value": "6",
"priority": null
},
{
"key": "system.rules.attack.roll.trait",
"mode": 5,
"value": "instinct",
"priority": null
}
],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "FecEtPuoQh6MpjQ0",
"modifiedTime": 1753636957124
},
"_key": "!items.effects!33oFSZ1PwFqInHPe.GSmW6nJ3kyIoEK2H"
}
],
"folder": "Rd30i5G7Pg0HtEUT",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753621789186,
"modifiedTime": 1753621803375,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "33oFSZ1PwFqInHPe",
"sort": 0,
"_key": "!items!33oFSZ1PwFqInHPe"
}

View file

@ -0,0 +1,138 @@
{
"name": "Powerful Beast",
"type": "beastform",
"img": "icons/creatures/abilities/bear-roar-bite-brown-green.webp",
"system": {
"beastformType": "normal",
"tier": 2,
"tokenImg": "icons/svg/mystery-man.svg",
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {
"height": null,
"width": null
},
"mainTrait": "strength",
"advantageOn": {
"LhmE20iV5HBExx0A": {
"value": "Navigate"
},
"EP4KQJfmXWGomsVC": {
"value": "Protect"
},
"lMMv1eTry3N6rZCx": {
"value": "Scare"
}
},
"features": [
"Compendium.daggerheart.beastforms.Item.8upqfcZvi7b5hRLE",
"Compendium.daggerheart.beastforms.Item.ZYbdXaWVj2zdcmaK"
],
"evolved": {
"mainTraitBonus": 0
},
"hybrid": {
"beastformOptions": 2,
"advantages": 2,
"features": 2
},
"examples": "Bear, Bull, Moose, etc."
},
"effects": [
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "AZGTvqzFVHa4wS1a",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [
{
"key": "system.traits.strength.value",
"mode": 2,
"value": "1",
"priority": null
},
{
"key": "system.evasion",
"mode": 2,
"value": "3",
"priority": null
},
{
"key": "system.rules.attack.damage.diceIndex",
"mode": 5,
"value": "3",
"priority": null
},
{
"key": "system.rules.attack.damage.bonus",
"mode": 5,
"value": "4",
"priority": null
},
{
"key": "system.rules.attack.roll.trait",
"mode": 5,
"value": "strength",
"priority": null
}
],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "FecEtPuoQh6MpjQ0",
"modifiedTime": 1753636973034
},
"_key": "!items.effects!m8BVTuJI1wCvzTcf.AZGTvqzFVHa4wS1a"
}
],
"folder": "Rd30i5G7Pg0HtEUT",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753582598510,
"modifiedTime": 1753617739186,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "m8BVTuJI1wCvzTcf",
"sort": 300000,
"_key": "!items!m8BVTuJI1wCvzTcf"
}

View file

@ -0,0 +1,138 @@
{
"name": "Stalking Arachnid",
"type": "beastform",
"img": "icons/creatures/invertebrates/spider-mandibles-brown.webp",
"system": {
"beastformType": "normal",
"tier": 1,
"tokenImg": "icons/svg/mystery-man.svg",
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {
"height": null,
"width": null
},
"mainTrait": "finesse",
"advantageOn": {
"udIR54Etg9sx2XbM": {
"value": "Attack"
},
"6On5GDzLxq6bao36": {
"value": "Climb"
},
"IZjCaeEmcRBzGeDn": {
"value": "Sneak"
}
},
"features": [
"Compendium.daggerheart.beastforms.Item.2KlTnfzO03vneVS8",
"Compendium.daggerheart.beastforms.Item.D73fS1iM4SZPFimu"
],
"evolved": {
"mainTraitBonus": 0
},
"hybrid": {
"beastformOptions": 2,
"advantages": 2,
"features": 2
},
"examples": "Tarantula, Wolf Spider, etc."
},
"effects": [
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "YkZX2yc2X0QDFPBG",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [
{
"key": "system.traits.finesse.value",
"mode": 2,
"value": "1",
"priority": null
},
{
"key": "system.evasion",
"mode": 2,
"value": "2",
"priority": null
},
{
"key": "system.rules.attack.damage.diceIndex",
"mode": 5,
"value": "1",
"priority": null
},
{
"key": "system.rules.attack.damage.bonus",
"mode": 5,
"value": "1",
"priority": null
},
{
"key": "system.rules.attack.roll.trait",
"mode": 5,
"value": "finesse",
"priority": null
}
],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "FecEtPuoQh6MpjQ0",
"modifiedTime": 1753637113589
},
"_key": "!items.effects!A4TVRY0D5r9EiVwA.YkZX2yc2X0QDFPBG"
}
],
"folder": "nhnRdZgSSrE9myIX",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753576016472,
"modifiedTime": 1753576046773,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "A4TVRY0D5r9EiVwA",
"sort": 0,
"_key": "!items!A4TVRY0D5r9EiVwA"
}

View file

@ -0,0 +1,138 @@
{
"name": "Striking Serpent",
"type": "beastform",
"img": "icons/creatures/reptiles/serpent-horned-green.webp",
"system": {
"beastformType": "normal",
"tier": 2,
"tokenImg": "icons/svg/mystery-man.svg",
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {
"height": null,
"width": null
},
"mainTrait": "finesse",
"advantageOn": {
"5bQks8eptInw0g9i": {
"value": "Climb"
},
"j0mztP3MfVkA8KVL": {
"value": "Deceive"
},
"yuIbmhAqfJmtzE6x": {
"value": "Sprint"
}
},
"features": [
"Compendium.daggerheart.beastforms.Item.uW3853pViM9VAfHb",
"Compendium.daggerheart.beastforms.Item.cTlqpQZPy5TvdDAT"
],
"evolved": {
"mainTraitBonus": 0
},
"hybrid": {
"beastformOptions": 2,
"advantages": 2,
"features": 2
},
"examples": "Cobra, Rattlesnake, Viper, etc."
},
"effects": [
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "qGhLojWa5430zRd1",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [
{
"key": "system.traits.finesse.value",
"mode": 2,
"value": "1",
"priority": null
},
{
"key": "system.evasion",
"mode": 2,
"value": "2",
"priority": null
},
{
"key": "system.rules.attack.damage.diceIndex",
"mode": 5,
"value": "2",
"priority": null
},
{
"key": "system.rules.attack.damage.bonus",
"mode": 5,
"value": "4",
"priority": null
},
{
"key": "system.rules.attack.roll.trait",
"mode": 5,
"value": "finesse",
"priority": null
}
],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "FecEtPuoQh6MpjQ0",
"modifiedTime": 1753635875178
},
"_key": "!items.effects!1XrZWGDttBAAUxR1.qGhLojWa5430zRd1"
}
],
"folder": "Rd30i5G7Pg0HtEUT",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753621251793,
"modifiedTime": 1753621266619,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "1XrZWGDttBAAUxR1",
"sort": 0,
"_key": "!items!1XrZWGDttBAAUxR1"
}

View file

@ -0,0 +1,141 @@
{
"name": "Terrible Lizard",
"type": "beastform",
"img": "icons/creatures/abilities/dragon-breath-purple.webp",
"system": {
"beastformType": "normal",
"tier": 4,
"tokenImg": "icons/svg/mystery-man.svg",
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {
"height": null,
"width": null
},
"mainTrait": "strength",
"advantageOn": {
"onvcDZ0vtg88AjUn": {
"value": "Attack"
},
"poo5ToKvpeI1Kvr3": {
"value": "Deceive"
},
"oPA8ogGHNDvxrJps": {
"value": "Scare"
},
"BIdGziN8wJX0iaHQ": {
"value": "Track"
}
},
"features": [
"Compendium.daggerheart.beastforms.Item.HJbQcKWcFZ9NoFxs",
"Compendium.daggerheart.beastforms.Item.9QkZSeuEKgXtlpHc"
],
"evolved": {
"mainTraitBonus": 0
},
"hybrid": {
"beastformOptions": 2,
"advantages": 2,
"features": 2
},
"examples": "Brachiosaurus, Tyrannosaurus, etc."
},
"effects": [
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "qPtjZIkRVm68TGHN",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [
{
"key": "system.traits.strength.value",
"mode": 2,
"value": "3",
"priority": null
},
{
"key": "system.evasion",
"mode": 2,
"value": "2",
"priority": null
},
{
"key": "system.rules.attack.damage.diceIndex",
"mode": 5,
"value": "4",
"priority": null
},
{
"key": "system.rules.attack.damage.bonus",
"mode": 5,
"value": "10",
"priority": null
},
{
"key": "system.rules.attack.roll.trait",
"mode": 5,
"value": "strength",
"priority": null
}
],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "FecEtPuoQh6MpjQ0",
"modifiedTime": 1753636836057
},
"_key": "!items.effects!5BABxRe2XVrYTj8N.qPtjZIkRVm68TGHN"
}
],
"folder": "UQEr5SAm2Z1Aih1S",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753628213224,
"modifiedTime": 1753628382733,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "5BABxRe2XVrYTj8N",
"sort": 300000,
"_key": "!items!5BABxRe2XVrYTj8N"
}

View file

@ -0,0 +1,138 @@
{
"name": "Winged Beast",
"type": "beastform",
"img": "icons/creatures/birds/raptor-owl-flying-moon.webp",
"system": {
"beastformType": "normal",
"tier": 2,
"tokenImg": "icons/svg/mystery-man.svg",
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {
"height": null,
"width": null
},
"mainTrait": "finesse",
"advantageOn": {
"DgZ77yPVJqNq7rxY": {
"value": "Deceive"
},
"HCH9ogsxcP0r3fU2": {
"value": "Locate"
},
"WST3bOmxoIeSHYqX": {
"value": "Scare"
}
},
"features": [
"Compendium.daggerheart.beastforms.Item.FNKQlWQcArSorMPK",
"Compendium.daggerheart.beastforms.Item.xVgmXhj2YgeqS1KK"
],
"evolved": {
"mainTraitBonus": 0
},
"hybrid": {
"beastformOptions": 2,
"advantages": 2,
"features": 2
},
"examples": "Hawk, Owl, Raven, etc."
},
"effects": [
{
"type": "beastform",
"name": "Beastform Transformation",
"img": "icons/creatures/abilities/paw-print-pair-purple.webp",
"_id": "Ln3atrxiqtPA0Wi6",
"system": {
"characterTokenData": {
"tokenImg": null,
"tokenRingImg": "icons/svg/mystery-man.svg",
"tokenSize": {}
},
"advantageOn": [],
"featureIds": [],
"effectIds": []
},
"changes": [
{
"key": "system.traits.finesse.value",
"mode": 2,
"value": "1",
"priority": null
},
{
"key": "system.evasion",
"mode": 2,
"value": "3",
"priority": null
},
{
"key": "system.rules.attack.damage.diceIndex",
"mode": 5,
"value": "0",
"priority": null
},
{
"key": "system.rules.attack.damage.bonus",
"mode": 5,
"value": "2",
"priority": null
},
{
"key": "system.rules.attack.roll.trait",
"mode": 5,
"value": "finesse",
"priority": null
}
],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": "FecEtPuoQh6MpjQ0",
"modifiedTime": 1753637037576
},
"_key": "!items.effects!mZ4Wlqtss2FlNNvL.Ln3atrxiqtPA0Wi6"
}
],
"folder": "Rd30i5G7Pg0HtEUT",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753624952844,
"modifiedTime": 1753624972889,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "mZ4Wlqtss2FlNNvL",
"sort": 0,
"_key": "!items!mZ4Wlqtss2FlNNvL"
}

View file

@ -0,0 +1,65 @@
{
"name": "Agile",
"type": "feature",
"img": "icons/skills/movement/arrow-upward-yellow.webp",
"system": {
"description": "<p>Your movement is silent, and you can <strong>spend a Hope</strong> to move up to Far range without rolling.</p>",
"resource": null,
"actions": {
"4yQ56hSL5LBkzrV6": {
"type": "effect",
"_id": "4yQ56hSL5LBkzrV6",
"systemPath": "actions",
"description": "",
"chatDisplay": true,
"actionType": "action",
"cost": [
{
"keyIsID": false,
"key": "hope",
"value": 1,
"scalable": false,
"step": null
}
],
"uses": {
"value": null,
"max": null,
"recovery": null
},
"effects": [],
"target": {
"type": null,
"amount": null
},
"name": "Spend Hope",
"img": "icons/skills/movement/arrow-upward-yellow.webp",
"range": ""
}
},
"originItemType": null,
"subType": null,
"originId": null
},
"effects": [],
"folder": "uU8bIoZvXge0rLaU",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753569752255,
"modifiedTime": 1753628206133,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "xLS5YT1B6yeCiNTg",
"sort": 2700000,
"_key": "!items!xLS5YT1B6yeCiNTg"
}

View file

@ -0,0 +1,34 @@
{
"name": "Aquatic",
"type": "feature",
"img": "icons/magic/water/bubbles-air-water-light.webp",
"system": {
"description": "<p>You can breathe and move naturally underwater.</p>",
"resource": null,
"actions": {},
"originItemType": null,
"subType": null,
"originId": null
},
"effects": [],
"folder": "uU8bIoZvXge0rLaU",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753575456927,
"modifiedTime": 1753628206133,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "kQWWx9P3fCyGSVOI",
"sort": 2100000,
"_key": "!items!kQWWx9P3fCyGSVOI"
}

View file

@ -0,0 +1,155 @@
{
"name": "Armored Shell",
"type": "feature",
"img": "icons/creatures/reptiles/turtle-shell-glowing-green.webp",
"system": {
"description": "<p>Your hardened exterior gives you resistance to physical damage. Additionally, <strong>mark an Armor Slot</strong> to retract into your shell. While in your shell, physical damage is reduced by a number equal to your Armor Score (after applying resistance), but you cant perform other actions without leaving this form.</p>",
"resource": null,
"actions": {
"15XSCpCAZ2JLspmN": {
"type": "effect",
"_id": "15XSCpCAZ2JLspmN",
"systemPath": "actions",
"description": "<p><strong>Mark an Armor Slot</strong> to retract into your shell. While in your shell, physical damage is reduced by a number equal to your Armor Score (after applying resistance), but you cant perform other actions without leaving this form.</p>",
"chatDisplay": true,
"actionType": "action",
"cost": [
{
"scalable": false,
"key": "armor",
"value": 1,
"keyIsID": false,
"step": null
}
],
"uses": {
"value": null,
"max": null,
"recovery": null
},
"effects": [
{
"_id": "OY3lEB3vuDKNLzxv",
"onSave": false
}
],
"target": {
"type": "self",
"amount": null
},
"name": "Retract",
"img": "icons/creatures/reptiles/turtle-shell-glowing-green.webp",
"range": ""
}
},
"originItemType": null,
"subType": null,
"originId": null
},
"effects": [
{
"name": "Armored Shell",
"type": "base",
"_id": "XFmEC0kDQJNrGbtr",
"img": "icons/creatures/reptiles/turtle-shell-glowing-green.webp",
"system": {},
"changes": [
{
"key": "system.resistance.physical.resistance",
"mode": 5,
"value": "1",
"priority": null
}
],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "<p>Your hardened exterior gives you resistance to physical damage.</p>",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": null
},
"_key": "!items.effects!nDQZdIF2epKlhauX.XFmEC0kDQJNrGbtr"
},
{
"name": "Retracted",
"img": "icons/creatures/reptiles/turtle-shell-glowing-green.webp",
"origin": "Item.WqNiQYeaRVGPWpm0",
"transfer": false,
"_id": "OY3lEB3vuDKNLzxv",
"type": "base",
"system": {},
"changes": [
{
"key": "system.resistance.physical.reduction",
"mode": 2,
"value": "@system.armorScore",
"priority": null
}
],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "<p>While in your shell, physical damage is reduced by a number equal to your Armor Score (after applying resistance), but you cant perform other actions without leaving this form.</p>",
"tint": "#ffffff",
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": null
},
"_key": "!items.effects!nDQZdIF2epKlhauX.OY3lEB3vuDKNLzxv"
}
],
"folder": "uU8bIoZvXge0rLaU",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753580983699,
"modifiedTime": 1753628206133,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "nDQZdIF2epKlhauX",
"sort": 2200000,
"_key": "!items!nDQZdIF2epKlhauX"
}

View file

@ -0,0 +1,57 @@
{
"name": "Bird's-Eye View",
"type": "feature",
"img": "icons/creatures/eyes/humanoid-single-red-brown.webp",
"system": {
"description": "<p>You can fly at will. Once per rest while you are airborne, you can ask the GM a question about the scene below you without needing to roll. The first time a character makes a roll to act on this information, they gain advantage on the roll.</p>",
"resource": null,
"actions": {
"JRCE5826Yhh09aaI": {
"type": "effect",
"_id": "JRCE5826Yhh09aaI",
"systemPath": "actions",
"description": "<p>Once per rest while you are airborne, you can ask the GM a question about the scene below you without needing to roll. The first time a character makes a roll to act on this information, they gain advantage on the roll.</p>",
"chatDisplay": true,
"actionType": "action",
"cost": [],
"uses": {
"value": 0,
"max": 1,
"recovery": "shortRest"
},
"effects": [],
"target": {
"type": null,
"amount": null
},
"name": "Ask Question",
"img": "icons/creatures/eyes/humanoid-single-red-brown.webp",
"range": ""
}
},
"originItemType": null,
"subType": null,
"originId": null
},
"effects": [],
"folder": "uU8bIoZvXge0rLaU",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753624947561,
"modifiedTime": 1753628206133,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "FNKQlWQcArSorMPK",
"sort": 1400000,
"_key": "!items!FNKQlWQcArSorMPK"
}

View file

@ -0,0 +1,87 @@
{
"name": "Cannonball",
"type": "feature",
"img": "icons/skills/movement/ball-spinning-blue.webp",
"system": {
"description": "<p><strong>Mark a Stress</strong> to allow an ally to throw or launch you at an adversary. To do so, the ally makes an attack roll using Agility or Strength (their choice) against a target within Close range. On a success, the adversary takes <strong>d12+2</strong> physical damage using the throwers Proficiency. You can <strong>spend a Hope</strong> to target an additional adversary within Very Close range of the first. The second target takes half the damage dealt to the first target.</p>",
"resource": null,
"actions": {
"5Vxm8LyBHE1GYbsv": {
"type": "effect",
"_id": "5Vxm8LyBHE1GYbsv",
"systemPath": "actions",
"description": "<p><strong>Mark a Stress</strong> to allow an ally to throw or launch you at an adversary. To do so, the ally makes an attack roll using Agility or Strength (their choice) against a target within Close range. On a success, the adversary takes <strong>d12+2</strong> physical damage using the throwers Proficiency.</p>",
"chatDisplay": true,
"actionType": "action",
"cost": [],
"uses": {
"value": null,
"max": null,
"recovery": null
},
"effects": [],
"target": {
"type": "any",
"amount": 1
},
"name": "Cannonball",
"img": "icons/skills/movement/ball-spinning-blue.webp",
"range": "close"
},
"QCQzArBrajYi2D2I": {
"type": "effect",
"_id": "QCQzArBrajYi2D2I",
"systemPath": "actions",
"description": "<p>You can <strong>spend a Hope</strong> to target an additional adversary within Very Close range of the first. The second target takes half the damage dealt to the first target.</p>",
"chatDisplay": true,
"actionType": "action",
"cost": [
{
"keyIsID": false,
"key": "hope",
"value": 1,
"scalable": false,
"step": null
}
],
"uses": {
"value": null,
"max": null,
"recovery": null
},
"effects": [],
"target": {
"type": null,
"amount": null
},
"name": "Spend Hope",
"img": "icons/magic/defensive/shield-barrier-deflect-teal.webp",
"range": "veryClose"
}
},
"originItemType": null,
"subType": null,
"originId": null
},
"effects": [],
"folder": "uU8bIoZvXge0rLaU",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753580984811,
"modifiedTime": 1753628206133,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "jp5KpPRBFBOIs46Q",
"sort": 1900000,
"_key": "!items!jp5KpPRBFBOIs46Q"
}

View file

@ -1,20 +1,20 @@
{ {
"type": "feature",
"name": "Carrier", "name": "Carrier",
"img": "icons/creatures/abilities/bull-head-horns-glowing.webp", "type": "feature",
"img": "icons/environment/people/cavalry-heavy.webp",
"system": { "system": {
"description": "<p>You can carry up to two willing allies with you when you move.</p>", "description": "<p>You can carry up to two willing allies with you when you move.</p>",
"resource": null, "resource": null,
"actions": {},
"originItemType": null, "originItemType": null,
"subType": null, "subType": null,
"originId": null, "originId": null
"actions": []
}, },
"effects": [], "effects": [],
"folder": "uU8bIoZvXge0rLaU", "folder": "uU8bIoZvXge0rLaU",
"ownership": { "ownership": {
"default": 0, "default": 0,
"k0gmQFlvrPvlTtbh": 3 "FecEtPuoQh6MpjQ0": 3
}, },
"flags": {}, "flags": {},
"_stats": { "_stats": {
@ -24,11 +24,11 @@
"coreVersion": "13.346", "coreVersion": "13.346",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "0.0.1",
"createdTime": 1752976866309, "createdTime": 1753617736331,
"modifiedTime": 1752976907469, "modifiedTime": 1753628206133,
"lastModifiedBy": "k0gmQFlvrPvlTtbh" "lastModifiedBy": "FecEtPuoQh6MpjQ0"
}, },
"_id": "YSolAjtv6Sfnai98", "_id": "EVOJTskJYf4rpuga",
"sort": 200000, "sort": 900000,
"_key": "!items!YSolAjtv6Sfnai98" "_key": "!items!EVOJTskJYf4rpuga"
} }

View file

@ -1,20 +1,20 @@
{ {
"type": "feature",
"name": "Companion", "name": "Companion",
"img": "icons/magic/life/heart-hand-gold-green-light.webp", "type": "feature",
"img": "icons/magic/life/heart-glowing-red.webp",
"system": { "system": {
"description": "<p>When you Help an Ally, you can roll a d8 as your advantage die.</p>", "description": "<p>When you Help an Ally, you can roll a <strong>d8</strong> as your advantage die.</p>",
"resource": null, "resource": null,
"actions": {},
"originItemType": null, "originItemType": null,
"subType": null, "subType": null,
"originId": null, "originId": null
"actions": []
}, },
"effects": [], "effects": [],
"folder": "uU8bIoZvXge0rLaU", "folder": "uU8bIoZvXge0rLaU",
"ownership": { "ownership": {
"default": 0, "default": 0,
"k0gmQFlvrPvlTtbh": 3 "FecEtPuoQh6MpjQ0": 3
}, },
"flags": {}, "flags": {},
"_stats": { "_stats": {
@ -24,11 +24,11 @@
"coreVersion": "13.346", "coreVersion": "13.346",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "0.0.1",
"createdTime": 1752976848672, "createdTime": 1753572888764,
"modifiedTime": 1752976908157, "modifiedTime": 1753628206133,
"lastModifiedBy": "k0gmQFlvrPvlTtbh" "lastModifiedBy": "FecEtPuoQh6MpjQ0"
}, },
"_id": "0tlnxIxlIw2hl1UE", "_id": "jhWSC5bNZyYUAA5Q",
"sort": 0, "sort": 2600000,
"_key": "!items!0tlnxIxlIw2hl1UE" "_key": "!items!jhWSC5bNZyYUAA5Q"
} }

View file

@ -0,0 +1,34 @@
{
"name": "Deadly Raptor",
"type": "feature",
"img": "icons/creatures/claws/claw-talons-glowing-orange.webp",
"system": {
"description": "<p>You can fly at will and move up to Far range as part of your action. When you move in a straight line into Melee range of a target from at least Close range and make an attack against that target in the same action, you can reroll all damage dice that rolled a result lower than your Proficiency.</p>",
"resource": null,
"actions": {},
"originItemType": null,
"subType": null,
"originId": null
},
"effects": [],
"folder": "uU8bIoZvXge0rLaU",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753628380597,
"modifiedTime": 1753628380597,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "QQtQ77tos8ijTHag",
"sort": 0,
"_key": "!items!QQtQ77tos8ijTHag"
}

View file

@ -0,0 +1,157 @@
{
"name": "Demolish",
"type": "feature",
"img": "icons/magic/earth/barrier-stone-brown-green.webp",
"system": {
"description": "<p><strong>Spend a Hope</strong> to move up to Far range in a straight line and make an attack against all targets within Melee range of the line. Targets you succeed against take <strong>d8+10</strong> physical damage using your Proficiency and are temporarily <em>Vulnerable</em>.</p><p>@Template[type:ray|range:f]</p>",
"resource": null,
"actions": {
"5SXMT39vrZoK7GBM": {
"type": "attack",
"_id": "5SXMT39vrZoK7GBM",
"systemPath": "actions",
"description": "<p>Spend a Hope to move up to Far range in a straight line and make an attack against all targets within Melee range of the line. Targets you succeed against take d8+10 physical damage using your Proficiency and are temporarily Vulnerable.</p>",
"chatDisplay": true,
"actionType": "action",
"cost": [
{
"keyIsID": false,
"key": "hope",
"value": 1,
"scalable": false,
"step": null
}
],
"uses": {
"value": null,
"max": null,
"recovery": null
},
"damage": {
"parts": [
{
"resultBased": false,
"value": {
"custom": {
"enabled": false
},
"multiplier": "prof",
"dice": "d8",
"bonus": 10,
"flatMultiplier": 1
},
"applyTo": "hitPoints",
"type": [],
"base": false,
"valueAlt": {
"multiplier": "prof",
"flatMultiplier": 1,
"dice": "d6",
"bonus": null,
"custom": {
"enabled": false
}
}
}
],
"includeBase": false
},
"target": {
"type": "any",
"amount": null
},
"effects": [
{
"_id": "FXdFgEgqVl5gIWJS",
"onSave": false
}
],
"roll": {
"type": "attack",
"trait": "strength",
"difficulty": null,
"bonus": null,
"advState": "neutral",
"diceRolling": {
"multiplier": "prof",
"flatMultiplier": 1,
"dice": "d6",
"compare": null,
"treshold": null
},
"useDefault": false
},
"save": {
"trait": null,
"difficulty": 10,
"damageMod": "none"
},
"name": "Attack",
"img": "icons/magic/earth/barrier-stone-brown-green.webp",
"range": "far"
}
},
"originItemType": null,
"subType": null,
"originId": null
},
"effects": [
{
"name": "Demolished",
"img": "icons/skills/wounds/injury-pain-body-orange.webp",
"origin": "Item.PkLs4PFO0HiCNVRH",
"transfer": false,
"_id": "FXdFgEgqVl5gIWJS",
"type": "base",
"system": {},
"changes": [],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "",
"tint": "#ffffff",
"statuses": [
"vulnerable"
],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": null
},
"_key": "!items.effects!DfBXO8jTchwFG8dZ.FXdFgEgqVl5gIWJS"
}
],
"folder": "uU8bIoZvXge0rLaU",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753627699848,
"modifiedTime": 1753628206133,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "DfBXO8jTchwFG8dZ",
"sort": 100000,
"_key": "!items!DfBXO8jTchwFG8dZ"
}

View file

@ -0,0 +1,65 @@
{
"name": "Devastating Strikes",
"type": "feature",
"img": "icons/skills/melee/blood-slash-foam-red.webp",
"system": {
"description": "<p>When you deal Severe damage to a target within Melee range, you can <strong>mark a Stress</strong> to force them to mark an additional Hit Point.</p>",
"resource": null,
"actions": {
"Il2C9zALU6a1gGrg": {
"type": "effect",
"_id": "Il2C9zALU6a1gGrg",
"systemPath": "actions",
"description": "<p>When you deal Severe damage to a target within Melee range, you can <strong>mark a Stress</strong> to force them to mark an additional Hit Point.</p>",
"chatDisplay": true,
"actionType": "action",
"cost": [
{
"scalable": false,
"key": "stress",
"value": 1,
"keyIsID": false,
"step": null
}
],
"uses": {
"value": null,
"max": null,
"recovery": null
},
"effects": [],
"target": {
"type": null,
"amount": null
},
"name": "Mark Stress",
"img": "icons/skills/melee/blood-slash-foam-red.webp",
"range": ""
}
},
"originItemType": null,
"subType": null,
"originId": null
},
"effects": [],
"folder": "uU8bIoZvXge0rLaU",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753628206110,
"modifiedTime": 1753628206133,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "HJbQcKWcFZ9NoFxs",
"sort": 200000,
"_key": "!items!HJbQcKWcFZ9NoFxs"
}

View file

@ -0,0 +1,89 @@
{
"name": "Elusive Prey",
"type": "feature",
"img": "icons/skills/movement/arrows-up-trio-red.webp",
"system": {
"description": "<p>When an attack roll against you would succeed, you can <strong>mark a Stress</strong> and roll a <strong>d4</strong>. Add the result to your Evasion against this attack.</p>",
"resource": null,
"actions": {
"9lvrqQKTEB3NRwvM": {
"type": "attack",
"_id": "9lvrqQKTEB3NRwvM",
"systemPath": "actions",
"description": "",
"chatDisplay": true,
"actionType": "action",
"cost": [
{
"scalable": false,
"key": "stress",
"value": 1,
"keyIsID": false,
"step": null
}
],
"uses": {
"value": null,
"max": null,
"recovery": null
},
"damage": {
"parts": [],
"includeBase": false
},
"target": {
"type": null,
"amount": null
},
"effects": [],
"roll": {
"type": "diceSet",
"trait": null,
"difficulty": null,
"bonus": null,
"advState": "neutral",
"diceRolling": {
"multiplier": "flat",
"flatMultiplier": 1,
"dice": "d4",
"compare": null,
"treshold": null
},
"useDefault": false
},
"save": {
"trait": null,
"difficulty": 10,
"damageMod": "none"
},
"name": "Mark Stress",
"img": "icons/skills/movement/arrows-up-trio-red.webp",
"range": ""
}
},
"originItemType": null,
"subType": null,
"originId": null
},
"effects": [],
"folder": "uU8bIoZvXge0rLaU",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753574925665,
"modifiedTime": 1753628206133,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "a7Qvmm14nx9BCysA",
"sort": 2300000,
"_key": "!items!a7Qvmm14nx9BCysA"
}

View file

@ -1,34 +0,0 @@
{
"type": "feature",
"name": "Evolved",
"img": "icons/creatures/abilities/dragon-breath-purple.webp",
"system": {
"description": "<p>Pick a Tier 1 Beastform option and become a larger, more powerful version of that creature. While youre in this form, you retain all traits and features from the original form and gain the following bonuses:</p><ul><li>A +6 bonus to damage rolls</li><li>A +1 bonus to the trait used by this form</li><li>A +2 bonus to Evasion</li></ul>",
"resource": null,
"originItemType": null,
"subType": null,
"originId": null,
"actions": []
},
"effects": [],
"folder": "uU8bIoZvXge0rLaU",
"ownership": {
"default": 0,
"k0gmQFlvrPvlTtbh": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1752976852417,
"modifiedTime": 1752976908700,
"lastModifiedBy": "k0gmQFlvrPvlTtbh"
},
"_id": "MG21w4u5wXSGZ5WB",
"sort": 50000,
"_key": "!items!MG21w4u5wXSGZ5WB"
}

View file

@ -0,0 +1,65 @@
{
"name": "Fleet",
"type": "feature",
"img": "icons/skills/movement/arrow-upward-yellow.webp",
"system": {
"description": "<p><strong>Spend a Hope</strong> to move up to Far range without rolling.</p>",
"resource": null,
"actions": {
"pn5p2ocVvVPzDyAX": {
"type": "effect",
"_id": "pn5p2ocVvVPzDyAX",
"systemPath": "actions",
"description": "<p><strong>Spend a Hope</strong> to move up to Far range without rolling.</p>",
"chatDisplay": true,
"actionType": "action",
"cost": [
{
"keyIsID": false,
"key": "hope",
"value": 1,
"scalable": false,
"step": null
}
],
"uses": {
"value": null,
"max": null,
"recovery": null
},
"effects": [],
"target": {
"type": null,
"amount": null
},
"name": "Move",
"img": "icons/skills/movement/arrow-upward-yellow.webp",
"range": ""
}
},
"originItemType": null,
"subType": null,
"originId": null
},
"effects": [],
"folder": "uU8bIoZvXge0rLaU",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753621784810,
"modifiedTime": 1753628206133,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "GhHsSHOa509cwCvr",
"sort": 1000000,
"_key": "!items!GhHsSHOa509cwCvr"
}

View file

@ -1,20 +1,20 @@
{ {
"type": "feature",
"name": "Fragile", "name": "Fragile",
"img": "icons/magic/life/heart-broken-red.webp", "type": "feature",
"img": "icons/magic/life/heart-pink.webp",
"system": { "system": {
"description": "<p>When you take Major or greater damage, you drop out of Beastform.</p>", "description": "<p>When you take Major or greater damage, you drop out of Beastform.</p>",
"resource": null, "resource": null,
"actions": {},
"originItemType": null, "originItemType": null,
"subType": null, "subType": null,
"originId": null, "originId": null
"actions": []
}, },
"effects": [], "effects": [],
"folder": "uU8bIoZvXge0rLaU", "folder": "uU8bIoZvXge0rLaU",
"ownership": { "ownership": {
"default": 0, "default": 0,
"k0gmQFlvrPvlTtbh": 3 "FecEtPuoQh6MpjQ0": 3
}, },
"flags": {}, "flags": {},
"_stats": { "_stats": {
@ -24,11 +24,11 @@
"coreVersion": "13.346", "coreVersion": "13.346",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "0.0.1",
"createdTime": 1752976855944, "createdTime": 1753569754067,
"modifiedTime": 1752976909267, "modifiedTime": 1753628206133,
"lastModifiedBy": "k0gmQFlvrPvlTtbh" "lastModifiedBy": "FecEtPuoQh6MpjQ0"
}, },
"_id": "9ryNrYWjNtOT6DXN", "_id": "QFg1hNCEoKVDd9Zo",
"sort": 150000, "sort": 2500000,
"_key": "!items!9ryNrYWjNtOT6DXN" "_key": "!items!QFg1hNCEoKVDd9Zo"
} }

View file

@ -0,0 +1,110 @@
{
"name": "Hobbling Strike",
"type": "feature",
"img": "icons/skills/wounds/bone-broken-knee-beam.webp",
"system": {
"description": "<p>When you succeed on an attack against a target within Melee range, you can <strong>mark a Stress</strong> to make the target temporarily <em>Vulnerable</em>.</p>",
"resource": null,
"actions": {
"p8DESOMGA6dLwEMz": {
"type": "effect",
"_id": "p8DESOMGA6dLwEMz",
"systemPath": "actions",
"description": "",
"chatDisplay": true,
"actionType": "action",
"cost": [
{
"scalable": false,
"key": "stress",
"value": 1,
"keyIsID": false,
"step": null
}
],
"uses": {
"value": null,
"max": null,
"recovery": null
},
"effects": [
{
"_id": "2kKkV9zhfvqA2vlt",
"onSave": false
}
],
"target": {
"type": null,
"amount": null
},
"name": "Mark Stress",
"img": "icons/skills/wounds/bone-broken-knee-beam.webp",
"range": ""
}
},
"originItemType": null,
"subType": null,
"originId": null
},
"effects": [
{
"name": "Hobbling Strike",
"img": "icons/skills/wounds/bone-broken-knee-beam.webp",
"origin": "Compendium.daggerheart.beastforms.Item.8u0HkK3WgtU9lWYs",
"transfer": false,
"_id": "2kKkV9zhfvqA2vlt",
"type": "base",
"system": {},
"changes": [],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "",
"tint": "#ffffff",
"statuses": [
"vulnerable"
],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753578616579,
"modifiedTime": 1753578640444,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_key": "!items.effects!8u0HkK3WgtU9lWYs.2kKkV9zhfvqA2vlt"
}
],
"folder": "uU8bIoZvXge0rLaU",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753575250590,
"modifiedTime": 1753628206133,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "8u0HkK3WgtU9lWYs",
"sort": 2400000,
"_key": "!items!8u0HkK3WgtU9lWYs"
}

View file

@ -0,0 +1,83 @@
{
"name": "Hollow Bones",
"type": "feature",
"img": "icons/skills/wounds/anatomy-bone-joint.webp",
"system": {
"description": "<p>You gain a 2 penalty to your damage thresholds.</p>",
"resource": null,
"actions": {},
"originItemType": null,
"subType": null,
"originId": null
},
"effects": [
{
"name": "Hollow Bones",
"type": "base",
"_id": "RM8wHu5GIF6zfF7V",
"img": "icons/skills/wounds/anatomy-bone-joint.webp",
"system": {},
"changes": [
{
"key": "system.damageThresholds.major",
"mode": 2,
"value": "-2",
"priority": null
},
{
"key": "system.damageThresholds.severe",
"mode": 2,
"value": "-2",
"priority": null
}
],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "<p>You gain a 2 penalty to your damage thresholds.</p>",
"origin": null,
"tint": "#ffffff",
"transfer": true,
"statuses": [],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": null
},
"_key": "!items.effects!xVgmXhj2YgeqS1KK.RM8wHu5GIF6zfF7V"
}
],
"folder": "uU8bIoZvXge0rLaU",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753624948910,
"modifiedTime": 1753628206133,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "xVgmXhj2YgeqS1KK",
"sort": 1500000,
"_key": "!items!xVgmXhj2YgeqS1KK"
}

View file

@ -1,20 +1,20 @@
{ {
"name": "Massive Stride",
"type": "feature", "type": "feature",
"name": "Agile",
"img": "icons/skills/movement/arrow-upward-blue.webp", "img": "icons/skills/movement/arrow-upward-blue.webp",
"system": { "system": {
"description": "<p>Your movement is silent, and you can spend a Hope to move up to Far range without rolling.</p>", "description": "<p>You can move up to Far range without rolling. You ignore rough terrain (at the GMs discretion) due to your size.</p>",
"resource": null, "resource": null,
"actions": {},
"originItemType": null, "originItemType": null,
"subType": null, "subType": null,
"originId": null, "originId": null
"actions": []
}, },
"effects": [], "effects": [],
"folder": "uU8bIoZvXge0rLaU", "folder": "uU8bIoZvXge0rLaU",
"ownership": { "ownership": {
"default": 0, "default": 0,
"k0gmQFlvrPvlTtbh": 3 "FecEtPuoQh6MpjQ0": 3
}, },
"flags": {}, "flags": {},
"_stats": { "_stats": {
@ -24,11 +24,11 @@
"coreVersion": "13.346", "coreVersion": "13.346",
"systemId": "daggerheart", "systemId": "daggerheart",
"systemVersion": "0.0.1", "systemVersion": "0.0.1",
"createdTime": 1752976877948, "createdTime": 1753628207886,
"modifiedTime": 1752976906072, "modifiedTime": 1753628207886,
"lastModifiedBy": "k0gmQFlvrPvlTtbh" "lastModifiedBy": "FecEtPuoQh6MpjQ0"
}, },
"_id": "sef9mwD2eRLZ64oV", "_id": "9QkZSeuEKgXtlpHc",
"sort": 100000, "sort": 0,
"_key": "!items!sef9mwD2eRLZ64oV" "_key": "!items!9QkZSeuEKgXtlpHc"
} }

View file

@ -0,0 +1,100 @@
{
"name": "Ocean Master",
"type": "feature",
"img": "icons/creatures/fish/fish-bioluminous-blue.webp",
"system": {
"description": "<p>You can breathe and move naturally underwater. When you succeed on an attack against a target within Melee range, you can temporarily <em>Restrain</em> them.</p>",
"resource": null,
"actions": {
"ge2koBRj4snhIzDA": {
"type": "effect",
"_id": "ge2koBRj4snhIzDA",
"systemPath": "actions",
"description": "<p>When you succeed on an attack against a target within Melee range, you can temporarily <em>Restrain</em> them.</p>",
"chatDisplay": true,
"actionType": "action",
"cost": [],
"uses": {
"value": null,
"max": null,
"recovery": null
},
"effects": [
{
"_id": "6GBczj8REkDmgX2Q",
"onSave": false
}
],
"target": {
"type": null,
"amount": null
},
"name": "Restrain",
"img": "icons/creatures/fish/fish-bioluminous-blue.webp",
"range": ""
}
},
"originItemType": null,
"subType": null,
"originId": null
},
"effects": [
{
"name": "Ocean Master",
"img": "icons/creatures/fish/fish-bioluminous-blue.webp",
"origin": "Item.5UbOIcuLE5Z3ViAm",
"transfer": false,
"_id": "6GBczj8REkDmgX2Q",
"type": "base",
"system": {},
"changes": [],
"disabled": false,
"duration": {
"startTime": null,
"combat": null,
"seconds": null,
"rounds": null,
"turns": null,
"startRound": null,
"startTurn": null
},
"description": "",
"tint": "#ffffff",
"statuses": [
"restrained"
],
"sort": 0,
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"lastModifiedBy": null
},
"_key": "!items.effects!tGDdEH40wyOCsFmH.6GBczj8REkDmgX2Q"
}
],
"folder": "uU8bIoZvXge0rLaU",
"ownership": {
"default": 0,
"FecEtPuoQh6MpjQ0": 3
},
"flags": {},
"_stats": {
"compendiumSource": null,
"duplicateSource": null,
"exportSource": null,
"coreVersion": "13.346",
"systemId": "daggerheart",
"systemVersion": "0.0.1",
"createdTime": 1753628691739,
"modifiedTime": 1753628691739,
"lastModifiedBy": "FecEtPuoQh6MpjQ0"
},
"_id": "tGDdEH40wyOCsFmH",
"sort": 0,
"_key": "!items!tGDdEH40wyOCsFmH"
}

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