diff --git a/coc.md b/COC.md similarity index 100% rename from coc.md rename to COC.md diff --git a/contributing.md b/CONTRIBUTING.md similarity index 100% rename from contributing.md rename to CONTRIBUTING.md diff --git a/lang/en.json b/lang/en.json index e5f09217..f1785185 100755 --- a/lang/en.json +++ b/lang/en.json @@ -189,7 +189,11 @@ "title": "Multiclass Subclass", "text": "Do you want to add this subclass as your multiclass subclass?" }, - "cannotRemoveCoreExperience": "You are using Levelup Auto. You cannot remove an experience given to you by the rule progression." + "cannotRemoveCoreExperience": "You are using Levelup Auto. You cannot remove an experience given to you by the rule progression.", + "companionLevelup": { + "confirmTitle": "Companion Levelup", + "confirmText": "Would you like to level up your companion {name} by {levelChange} levels at this time? (You can do it manually later)" + } }, "Companion": { "FIELDS": { @@ -233,13 +237,14 @@ }, "APPLICATIONS": { "CharacterCreation": { - "setupTabs": { + "tabs": { "ancestry": "Ancestry", "community": "Community", "class": "Class", "experience": "Experience", "traits": "Traits", - "domainCards": "Domain Cards" + "domainCards": "Domain Cards", + "equipment": "Equipment" }, "ancestryNamePlaceholder": "Your ancestry's name", "buttonTitle": "Character Setup", @@ -816,6 +821,10 @@ "name": "Dead", "description": "The character is dead" }, + "defeated": { + "name": "Defeated", + "description": "This adversary is defeated." + }, "hidden": { "name": "Hidden", "description": "While Hidden, attacks cannot be made directly targeting them nd any rolls against them are at disadvantage.\nWhen a Hidden creature moves or attacks, they are no longer Hidden. However, if a creature is Hidden when they begin making an attack, the roll has advantage; the Hidden condition isn’t cleared until after the attack is resolved." @@ -1865,7 +1874,8 @@ "tier3": "Tier 3", "tier4": "tier 4", "domains": "Domains", - "downtime": "Downtime" + "downtime": "Downtime", + "rules": "Rules" }, "Tiers": { "singular": "Tier", @@ -1934,6 +1944,7 @@ "itemResource": "Item Resource", "label": "Label", "level": "Level", + "levelShort": "Lv", "levelUp": "Level Up", "loadout": "Loadout", "max": "Max", @@ -2081,7 +2092,12 @@ "FIELDS": { "displayFear": { "label": "Fear Display" }, "dualityColorScheme": { "label": "Chat Style" }, - "showGenericStatusEffects": { "label": "Show Foundry Status Effects" } + "showGenericStatusEffects": { "label": "Show Foundry Status Effects" }, + "expandedTitle": "Auto-expand Descriptions", + "extendCharacterDescriptions": { "label": "Characters" }, + "extendAdversaryDescriptions": { "label": "Adversaries" }, + "extendEnvironmentDescriptions": { "label": "Environments" }, + "extendItemDescriptions": { "label": "Items" } }, "fearDisplay": { "token": "Tokens", @@ -2099,6 +2115,13 @@ "label": "Damage Reduction Rules Default", "hint": "Wether using armor and reductions has rules on by default" }, + "defeated": { + "enabled": { "label": "Enabled" }, + "overlay": { "label": "Overlay Effect" }, + "characterDefault": { "label": "Character Default Defeated Status" }, + "adversaryDefault": { "label": "Adversary Default Defeated Status" }, + "companionDefault": { "label": "Companion Default Defeated Status" } + }, "hopeFear": { "label": "Hope & Fear", "gm": { "label": "GM" }, @@ -2130,6 +2153,9 @@ "label": "Players Can Manually Edit Character Settings", "hint": "Players are allowed to access the manual Character Settings and change their statistics beyond the rules." } + }, + "defeated": { + "title": "Defeated Handling" } }, "Homebrew": { @@ -2369,7 +2395,8 @@ "rulesOn": "Rules On", "rulesOff": "Rules Off", "remainingUses": "Uses refresh on {type}", - "rightClickExtand": "Right-Click to extand" + "rightClickExtand": "Right-Click to extand", + "companionPartnerLevelBlock": "The companion needs an assigned partner to level up." } } } diff --git a/module/applications/characterCreation/characterCreation.mjs b/module/applications/characterCreation/characterCreation.mjs index 1cadd6d4..693f6c5a 100644 --- a/module/applications/characterCreation/characterCreation.mjs +++ b/module/applications/characterCreation/characterCreation.mjs @@ -13,7 +13,10 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl this.setup = { traits: this.character.system.traits, - ancestryName: '', + ancestryName: { + primary: '', + secondary: '' + }, mixedAncestry: false, primaryAncestry: this.character.system.ancestry ?? {}, secondaryAncestry: {}, @@ -83,131 +86,70 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl static PARTS = { tabs: { template: 'systems/daggerheart/templates/characterCreation/tabs.hbs' }, - setup: { template: 'systems/daggerheart/templates/characterCreation/tabs/setup.hbs' }, - ancestry: { template: 'systems/daggerheart/templates/characterCreation/setupTabs/ancestry.hbs' }, - community: { template: 'systems/daggerheart/templates/characterCreation/setupTabs/community.hbs' }, - class: { template: 'systems/daggerheart/templates/characterCreation/setupTabs/class.hbs' }, - traits: { template: 'systems/daggerheart/templates/characterCreation/setupTabs/traits.hbs' }, - experience: { template: 'systems/daggerheart/templates/characterCreation/setupTabs/experience.hbs' }, - domainCards: { template: 'systems/daggerheart/templates/characterCreation/setupTabs/domainCards.hbs' }, - equipment: { template: 'systems/daggerheart/templates/characterCreation/tabs/equipment.hbs' }, - // story: { template: 'systems/daggerheart/templates/characterCreation/tabs/story.hbs' }, + ancestry: { template: 'systems/daggerheart/templates/characterCreation/tabs/ancestry.hbs' }, + community: { template: 'systems/daggerheart/templates/characterCreation/tabs/community.hbs' }, + class: { template: 'systems/daggerheart/templates/characterCreation/tabs/class.hbs' }, + traits: { template: 'systems/daggerheart/templates/characterCreation/tabs/traits.hbs' }, + experience: { template: 'systems/daggerheart/templates/characterCreation/tabs/experience.hbs' }, + domainCards: { template: 'systems/daggerheart/templates/characterCreation/tabs/domainCards.hbs' }, + equipment: { template: 'systems/daggerheart/templates/characterCreation/equipment.hbs' }, + // story: { template: 'systems/daggerheart/templates/characterCreation/story.hbs' }, footer: { template: 'systems/daggerheart/templates/characterCreation/footer.hbs' } }; static TABS = { - setup: { - active: true, - cssClass: '', - group: 'primary', - id: 'setup', - label: 'DAGGERHEART.GENERAL.Tabs.setup' - }, - equipment: { - active: false, - cssClass: '', - group: 'primary', - id: 'equipment', - label: 'DAGGERHEART.GENERAL.Tabs.equipment', - optional: true - } - // story: { - // active: false, - // cssClass: '', - // group: 'primary', - // id: 'story', - // label: 'DAGGERHEART.GENERAL.Tabs.story', - // optional: true - // } - }; - - static SETUPTABS = { ancestry: { active: true, cssClass: '', group: 'setup', id: 'ancestry', - label: 'DAGGERHEART.APPLICATIONS.CharacterCreation.setupTabs.ancestry' + label: 'DAGGERHEART.APPLICATIONS.CharacterCreation.tabs.ancestry' }, community: { active: false, cssClass: '', group: 'setup', id: 'community', - label: 'DAGGERHEART.APPLICATIONS.CharacterCreation.setupTabs.community' + label: 'DAGGERHEART.APPLICATIONS.CharacterCreation.tabs.community' }, class: { active: false, cssClass: '', group: 'setup', id: 'class', - label: 'DAGGERHEART.APPLICATIONS.CharacterCreation.setupTabs.class' + label: 'DAGGERHEART.APPLICATIONS.CharacterCreation.tabs.class' }, traits: { active: false, cssClass: '', group: 'setup', id: 'traits', - label: 'DAGGERHEART.APPLICATIONS.CharacterCreation.setupTabs.traits' + label: 'DAGGERHEART.APPLICATIONS.CharacterCreation.tabs.traits' }, experience: { active: false, cssClass: '', group: 'setup', id: 'experience', - label: 'DAGGERHEART.APPLICATIONS.CharacterCreation.setupTabs.experience' + label: 'DAGGERHEART.APPLICATIONS.CharacterCreation.tabs.experience' }, domainCards: { active: false, cssClass: '', group: 'setup', id: 'domainCards', - label: 'DAGGERHEART.APPLICATIONS.CharacterCreation.setupTabs.domainCards' + label: 'DAGGERHEART.APPLICATIONS.CharacterCreation.tabs.domainCards' + }, + equipment: { + active: false, + cssClass: '', + group: 'setup', + id: 'equipment', + label: 'DAGGERHEART.APPLICATIONS.CharacterCreation.tabs.equipment' } }; _getTabs(tabs) { - for (const v of Object.values(tabs)) { - v.active = this.tabGroups[v.group] ? this.tabGroups[v.group] === v.id : v.active; - v.cssClass = v.active ? 'active' : ''; - - switch (v.id) { - case 'setup': - const ancestryFinished = this.setup.primaryAncestry.uuid; - const communityFinished = this.setup.community.uuid; - const classFinished = this.setup.class.uuid && this.setup.subclass.uuid; - const traitsFinished = Object.values(this.setup.traits).every(x => x.value !== null); - const experiencesFinished = Object.values(this.setup.experiences).every(x => x.name); - const domainCardsFinished = Object.values(this.setup.domainCards).every(x => x.uuid); - v.finished = - ancestryFinished && - communityFinished && - classFinished && - traitsFinished && - experiencesFinished && - domainCardsFinished; - break; - case 'equipment': - const armorFinished = this.equipment.armor?.uuid; - const primaryFinished = this.equipment.primaryWeapon?.uuid; - const secondaryFinished = - this.equipment.secondaryWeapon?.uuid || - (primaryFinished && this.equipment.primaryWeapon.system.burden == burden.twoHanded.value); - const choiceAFinished = this.equipment.inventory.choiceA?.uuid; - const choiceBFinished = this.equipment.inventory.choiceB?.uuid; - - v.finished = - armorFinished && primaryFinished && secondaryFinished && choiceAFinished && choiceBFinished; - } - } - - tabs.equipment.cssClass = tabs.setup.finished ? tabs.equipment.cssClass : 'disabled'; - // tabs.story.cssClass = tabs.setup.finished ? tabs.story.cssClass : 'disabled'; - - return tabs; - } - - _getSetupTabs(tabs) { for (const v of Object.values(tabs)) { v.active = this.tabGroups[v.group] ? this.tabGroups[v.group] === v.id @@ -232,37 +174,15 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl case 'domainCards': v.disabled = this.setup.visibility < 6; break; + case 'equipment': + v.disabled = this.setup.visibility < 7; + break; } } return tabs; } - changeTab(tab, group, options) { - super.changeTab(tab, group, options); - - if (group === 'primary') { - for (var listTab of Object.keys(this.constructor.TABS)) { - const marker = options.navElement.querySelector(`a[data-action="tab"].${listTab} .finish-marker`); - if (listTab === tab) { - marker.classList.add('active'); - } else { - marker.classList.remove('active'); - } - } - - if (tab === 'equipment') { - this.tabGroups.setup = null; - this.element.querySelector('section[data-group="setup"].active')?.classList?.remove?.('active'); - } else { - this.tabGroups.setup = 'domainCards'; - this.element - .querySelector('section[data-group="setup"][data-tab="domainCards"]') - ?.classList?.add?.('active'); - } - } - } - _attachPartListeners(partId, htmlElement, options) { super._attachPartListeners(partId, htmlElement, options); @@ -274,14 +194,71 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl }); } - async _preFirstRender(_context, _options) { - this.tabGroups.primary = 'setup'; - this.tabGroups.setup = 'ancestry'; - } - async _prepareContext(_options) { + this.tabGroups.setup = this.tabGroups.setup ?? 'ancestry'; const context = await super._prepareContext(_options); + context.tabs = this._getTabs(this.constructor.TABS); + const availableTraitModifiers = game.settings + .get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew) + .traitArray.map(trait => ({ key: trait, name: trait })); + for (let trait of Object.values(this.setup.traits).filter(x => x.value !== null)) { + const index = availableTraitModifiers.findIndex(x => x.key === trait.value); + if (index !== -1) { + availableTraitModifiers.splice(index, 1); + } + } + + context.suggestedTraits = this.setup.class.system + ? Object.keys(this.setup.class.system.characterGuide.suggestedTraits).map(traitKey => { + const trait = this.setup.class.system.characterGuide.suggestedTraits[traitKey]; + return `${game.i18n.localize(`DAGGERHEART.CONFIG.Traits.${traitKey}.short`)} ${trait > 0 ? `+${trait}` : trait}`; + }) + : []; + context.traits = { + values: Object.keys(this.setup.traits).map(traitKey => { + const trait = this.setup.traits[traitKey]; + const options = [...availableTraitModifiers]; + if (trait.value !== null && !options.some(x => x.key === trait.value)) + options.push({ key: trait.value, name: trait.value }); + + return { + ...trait, + key: traitKey, + name: game.i18n.localize(abilities[traitKey].label), + options: options + }; + }) + }; + context.traits.nrTotal = Object.keys(context.traits.values).length; + context.traits.nrSelected = this.getNrSelectedTrait(); + + context.experience = { + values: this.setup.experiences, + nrTotal: Object.keys(this.setup.experiences).length, + nrSelected: Object.values(this.setup.experiences).reduce((acc, exp) => acc + (exp.name ? 1 : 0), 0) + }; + + context.mixedAncestry = Number(this.setup.mixedAncestry); + + const { primary, secondary, overwrite } = this.setup.ancestryName; + context.ancestryName = overwrite ?? (primary && secondary ? `${primary}/${secondary}` : primary); + context.primaryAncestry = { ...this.setup.primaryAncestry, compendium: 'ancestries' }; + context.secondaryAncestry = { ...this.setup.secondaryAncestry, compendium: 'ancestries' }; + context.community = { ...this.setup.community, compendium: 'communities' }; + context.class = { ...this.setup.class, compendium: 'classes' }; + context.subclass = { ...this.setup.subclass, compendium: 'subclasses' }; + + const allDomainData = CONFIG.DH.DOMAIN.allDomains(); + context.classDomains = context.class.uuid + ? context.class.system.domains.map(key => game.i18n.localize(allDomainData[key].label)) + : []; + context.domainCards = Object.keys(this.setup.domainCards).reduce((acc, x) => { + acc[x] = { ...this.setup.domainCards[x], compendium: 'domains' }; + return acc; + }, {}); + + context.visibility = this.setup.visibility; return context; } @@ -289,7 +266,7 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl async _preparePartContext(partId, context) { switch (partId) { case 'footer': - context.isLastTab = this.tabGroups.setup === 'domainCards' || this.tabGroups.primary !== 'setup'; + context.isLastTab = this.tabGroups.setup === 'equipment'; switch (this.tabGroups.setup) { case null: case 'ancestry': @@ -307,69 +284,11 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl case 'experience': context.nextDisabled = this.setup.visibility === 5; break; + case 'domainCards': + context.nextDisabled = this.setup.visibility === 6; + break; } - break; - case 'setup': - context.setupTabs = this._getSetupTabs(this.constructor.SETUPTABS); - const availableTraitModifiers = game.settings - .get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew) - .traitArray.map(trait => ({ key: trait, name: trait })); - for (let trait of Object.values(this.setup.traits).filter(x => x.value !== null)) { - const index = availableTraitModifiers.findIndex(x => x.key === trait.value); - if (index !== -1) { - availableTraitModifiers.splice(index, 1); - } - } - - context.suggestedTraits = this.setup.class.system - ? Object.keys(this.setup.class.system.characterGuide.suggestedTraits).map(traitKey => { - const trait = this.setup.class.system.characterGuide.suggestedTraits[traitKey]; - return `${game.i18n.localize(`DAGGERHEART.CONFIG.Traits.${traitKey}.short`)} ${trait > 0 ? `+${trait}` : trait}`; - }) - : []; - context.traits = { - values: Object.keys(this.setup.traits).map(traitKey => { - const trait = this.setup.traits[traitKey]; - const options = [...availableTraitModifiers]; - if (trait.value !== null && !options.some(x => x.key === trait.value)) - options.push({ key: trait.value, name: trait.value }); - - return { - ...trait, - key: traitKey, - name: game.i18n.localize(abilities[traitKey].label), - options: options - }; - }) - }; - context.traits.nrTotal = Object.keys(context.traits.values).length; - context.traits.nrSelected = this.getNrSelectedTrait(); - - context.experience = { - values: this.setup.experiences, - nrTotal: Object.keys(this.setup.experiences).length, - nrSelected: Object.values(this.setup.experiences).reduce((acc, exp) => acc + (exp.name ? 1 : 0), 0) - }; - - context.mixedAncestry = Number(this.setup.mixedAncestry); - context.ancestryName = this.setup.ancestryName; - context.primaryAncestry = { ...this.setup.primaryAncestry, compendium: 'ancestries' }; - context.secondaryAncestry = { ...this.setup.secondaryAncestry, compendium: 'ancestries' }; - context.community = { ...this.setup.community, compendium: 'communities' }; - context.class = { ...this.setup.class, compendium: 'classes' }; - context.subclass = { ...this.setup.subclass, compendium: 'subclasses' }; - - const allDomainData = CONFIG.DH.DOMAIN.allDomains(); - context.classDomains = context.class.uuid - ? context.class.system.domains.map(key => game.i18n.localize(allDomainData[key].label)) - : []; - context.domainCards = Object.keys(this.setup.domainCards).reduce((acc, x) => { - acc[x] = { ...this.setup.domainCards[x], compendium: 'domains' }; - return acc; - }, {}); - - context.visibility = this.setup.visibility; break; case 'equipment': const suggestions = await this.getEquipmentSuggestions( @@ -438,8 +357,10 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl getUpdateVisibility() { switch (this.setup.visibility) { + case 7: + return 7; case 6: - return 6; + return Object.values(this.setup.domainCards).every(x => x.uuid) ? 7 : 6; case 5: return Object.values(this.setup.experiences).every(x => x.name) ? 6 : 5; case 4: @@ -505,7 +426,7 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl const presets = { compendium: 'daggerheart', - folder: equipment.includes(type) ? "equipments" : type, + folder: equipment.includes(type) ? 'equipments' : type, render: { noFolder: true } @@ -565,6 +486,9 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl case 6: this.tabGroups.setup = 'domainCards'; break; + case 7: + this.tabGroups.setup = 'equipment'; + break; } this.render(); @@ -576,9 +500,10 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl ? this.setup.secondaryAncestry.system.secondaryFeature : this.setup.primaryAncestry.system.secondaryFeature; + const { primary, secondary, overwrite } = this.setup.ancestryName; const ancestry = { ...this.setup.primaryAncestry, - name: this.setup.ancestryName ?? this.setup.primaryAncestry.name, + name: overwrite ?? (primary && secondary ? `${primary}/${secondary}` : primary), system: { ...this.setup.primaryAncestry.system, features: [ @@ -650,13 +575,14 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl const data = foundry.applications.ux.TextEditor.implementation.getDragEventData(event); const item = await foundry.utils.fromUuid(data.uuid); if (item.type === 'ancestry' && event.target.closest('.primary-ancestry-card')) { - this.setup.ancestryName = item.name; + this.setup.ancestryName.primary = item.name; this.setup.primaryAncestry = { ...item, effects: Array.from(item.effects).map(x => x.toObject()), uuid: item.uuid }; } else if (item.type === 'ancestry' && event.target.closest('.secondary-ancestry-card')) { + this.setup.ancestryName.secondary = item.name; this.setup.secondaryAncestry = { ...item, effects: Array.from(item.effects).map(x => x.toObject()), diff --git a/module/applications/dialogs/downtime.mjs b/module/applications/dialogs/downtime.mjs index 10708d02..b0f51c4e 100644 --- a/module/applications/dialogs/downtime.mjs +++ b/module/applications/dialogs/downtime.mjs @@ -95,8 +95,9 @@ export default class DhpDowntime extends HandlebarsApplicationMixin(ApplicationV if (x.system.actions) { const recoverable = x.system.actions.reduce((acc, action) => { if ( - (action.uses.recovery && (action.uses.recovery === 'longRest') === !this.shortrest) || - action.uses.recovery === 'shortRest' + action.uses.recovery && + ((action.uses.recovery === 'longRest' && !this.shortrest) || + action.uses.recovery === 'shortRest') ) { acc.push({ title: x.name, diff --git a/module/applications/levelup/characterLevelup.mjs b/module/applications/levelup/characterLevelup.mjs index 0ae136c4..623f0308 100644 --- a/module/applications/levelup/characterLevelup.mjs +++ b/module/applications/levelup/characterLevelup.mjs @@ -49,12 +49,7 @@ export default class DhCharacterLevelUp extends LevelUpBase { const experienceIncreases = Object.values(advancementChoices.experience ?? {}); const experienceIncreaseValues = experienceIncreases .filter(exp => exp.data.length > 0) - .flatMap(exp => - exp.data.map(data => { - const experience = Object.keys(this.actor.system.experiences)[data]; - return this.actor.system.experiences[experience].name; - }) - ); + .flatMap(exp => exp.data); context.experienceIncreases = { values: experienceIncreaseValues, active: experienceIncreases.length > 0, diff --git a/module/applications/levelup/companionLevelup.mjs b/module/applications/levelup/companionLevelup.mjs index c94d7d2b..4b8f9b47 100644 --- a/module/applications/levelup/companionLevelup.mjs +++ b/module/applications/levelup/companionLevelup.mjs @@ -37,12 +37,7 @@ export default class DhCompanionLevelUp extends BaseLevelUp { const experienceIncreases = Object.values(advancementChoices.experience ?? {}); const experienceIncreaseValues = experienceIncreases .filter(exp => exp.data.length > 0) - .flatMap(exp => - exp.data.map(data => { - const experience = Object.keys(this.actor.system.experiences)[data]; - return this.actor.system.experiences[experience].name; - }) - ); + .flatMap(exp => exp.data); context.experienceIncreases = { values: experienceIncreaseValues, active: experienceIncreases.length > 0, @@ -72,6 +67,28 @@ export default class DhCompanionLevelUp extends BaseLevelUp { const levelKeys = Object.keys(this.levelup.levels); const actorDamageDice = this.actor.system.attack.damage.parts[0].value.dice; const actorRange = this.actor.system.attack.range; + + let achievementExperiences = []; + for (var levelKey of levelKeys) { + const level = this.levelup.levels[levelKey]; + if (Number(levelKey) < this.levelup.startLevel) continue; + + achievementExperiences = level.achievements.experiences + ? Object.values(level.achievements.experiences).reduce((acc, experience) => { + if (experience.name) acc.push(experience); + return acc; + }, []) + : []; + } + context.achievements = { + experiences: { + values: achievementExperiences, + shown: achievementExperiences.length > 0 + } + }; + + context.achievements = context.achievements.experiences.shown ? context.achievements : undefined; + const advancement = {}; for (var levelKey of levelKeys) { const level = this.levelup.levels[levelKey]; diff --git a/module/applications/settings/automationSettings.mjs b/module/applications/settings/automationSettings.mjs index 489bae02..0157e016 100644 --- a/module/applications/settings/automationSettings.mjs +++ b/module/applications/settings/automationSettings.mjs @@ -31,8 +31,19 @@ export default class DhAutomationSettings extends HandlebarsApplicationMixin(App }; static PARTS = { + tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' }, + header: { template: 'systems/daggerheart/templates/settings/automation-settings/header.hbs' }, + general: { template: 'systems/daggerheart/templates/settings/automation-settings/general.hbs' }, + rules: { template: 'systems/daggerheart/templates/settings/automation-settings/rules.hbs' }, + footer: { template: 'systems/daggerheart/templates/settings/automation-settings/footer.hbs' } + }; + + /** @inheritdoc */ + static TABS = { main: { - template: 'systems/daggerheart/templates/settings/automation-settings.hbs' + tabs: [{ id: 'general' }, { id: 'rules' }], + initial: 'general', + labelPrefix: 'DAGGERHEART.GENERAL.Tabs' } }; diff --git a/module/applications/sheets-configs/companion-settings.mjs b/module/applications/sheets-configs/companion-settings.mjs index 6791c4c7..8aa21479 100644 --- a/module/applications/sheets-configs/companion-settings.mjs +++ b/module/applications/sheets-configs/companion-settings.mjs @@ -1,5 +1,4 @@ import { GMUpdateEvent, socketEvent } from '../../systemRegistration/socket.mjs'; -import DhCompanionlevelUp from '../levelup/companionLevelup.mjs'; import DHBaseActorSettings from '../sheets/api/actor-setting.mjs'; /**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */ @@ -11,8 +10,7 @@ export default class DHCompanionSettings extends DHBaseActorSettings { position: { width: 455, height: 'auto' }, actions: { addExperience: DHCompanionSettings.#addExperience, - removeExperience: DHCompanionSettings.#removeExperience, - levelUp: DHCompanionSettings.#levelUp + removeExperience: DHCompanionSettings.#removeExperience } }; @@ -121,12 +119,4 @@ export default class DHCompanionSettings extends DHBaseActorSettings { await this.actor.update({ [`system.experiences.-=${target.dataset.experience}`]: null }); } - - /** - * Opens the companion level-up dialog for the associated actor. - * @type {ApplicationClickAction} - */ - static async #levelUp() { - new DhCompanionlevelUp(this.actor).render({ force: true }); - } } diff --git a/module/applications/sheets/actors/character.mjs b/module/applications/sheets/actors/character.mjs index ae597270..481c745e 100644 --- a/module/applications/sheets/actors/character.mjs +++ b/module/applications/sheets/actors/character.mjs @@ -638,15 +638,21 @@ export default class CharacterSheet extends DHBaseActorSheet { ability: abilityLabel }) }); - - setTimeout(() => { - this.consumeResource(result?.costs); - }, 50); + + this.consumeResource(result?.costs); } + // Remove when Action Refactor part #2 done async consumeResource(costs) { if (!costs?.length) return; - const usefulResources = foundry.utils.deepClone(this.actor.system.resources); + const usefulResources = { + ...foundry.utils.deepClone(this.actor.system.resources), + fear: { + value: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Resources.Fear), + max: game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).maxFear, + reversed: false + } + }; const resources = game.system.api.fields.ActionFields.CostField.getRealCosts(costs).map(c => { const resource = usefulResources[c.key]; return { diff --git a/module/applications/sheets/actors/companion.mjs b/module/applications/sheets/actors/companion.mjs index 87cfda27..1105131d 100644 --- a/module/applications/sheets/actors/companion.mjs +++ b/module/applications/sheets/actors/companion.mjs @@ -1,3 +1,4 @@ +import DhCompanionLevelUp from '../../levelup/companionLevelup.mjs'; import DHBaseActorSheet from '../api/base-actor.mjs'; /**@typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */ @@ -5,8 +6,10 @@ import DHBaseActorSheet from '../api/base-actor.mjs'; export default class DhCompanionSheet extends DHBaseActorSheet { static DEFAULT_OPTIONS = { classes: ['actor', 'companion'], - position: { width: 300 }, - actions: {} + position: { width: 340 }, + actions: { + levelManagement: DhCompanionSheet.#levelManagement + } }; static PARTS = { @@ -25,4 +28,25 @@ export default class DhCompanionSheet extends DHBaseActorSheet { labelPrefix: 'DAGGERHEART.GENERAL.Tabs' } }; + + /** @inheritDoc */ + async _onRender(context, options) { + await super._onRender(context, options); + + this.element + .querySelector('.level-value') + ?.addEventListener('change', event => this.document.updateLevel(Number(event.currentTarget.value))); + } + + /* -------------------------------------------- */ + /* Application Clicks Actions */ + /* -------------------------------------------- */ + + /** + * Opens the companions level management window. + * @type {ApplicationClickAction} + */ + static #levelManagement() { + new DhCompanionLevelUp(this.document).render({ force: true }); + } } diff --git a/module/applications/sheets/api/application-mixin.mjs b/module/applications/sheets/api/application-mixin.mjs index 18a5ac91..83dc1581 100644 --- a/module/applications/sheets/api/application-mixin.mjs +++ b/module/applications/sheets/api/application-mixin.mjs @@ -2,6 +2,23 @@ const { HandlebarsApplicationMixin } = foundry.applications.api; import { getDocFromElement, getDocFromElementSync, tagifyElement } from '../../../helpers/utils.mjs'; import { ItemBrowser } from '../../ui/itemBrowser.mjs'; +const typeSettingsMap = { + character: 'extendCharacterDescriptions', + adversary: 'extendAdversaryDescriptions', + environment: 'extendEnvironmentDescriptions', + ancestry: 'extendItemDescriptions', + community: 'extendItemDescriptions', + class: 'extendItemDescriptions', + subclass: 'extendItemDescriptions', + feature: 'extendItemDescriptions', + domainCard: 'extendItemDescriptions', + loot: 'extendItemDescriptions', + consumable: 'extendItemDescriptions', + weapon: 'extendItemDescriptions', + armor: 'extendItemDescriptions', + beastform: 'extendItemDescriptions' +}; + /** * @typedef {import('@client/applications/_types.mjs').ApplicationClickAction} ApplicationClickAction */ @@ -137,6 +154,8 @@ export default function DHApplicationMixin(Base) { docs.filter(doc => doc).forEach(doc => (doc.apps[this.id] = this)); if (!!this.options.contextMenus.length) this._createContextMenus(); + + this.#autoExtendDescriptions(context); } /** @inheritDoc */ @@ -149,6 +168,7 @@ export default function DHApplicationMixin(Base) { async _onRender(context, options) { await super._onRender(context, options); this._createTagifyElements(this.options.tagifyConfigs); + await this.#prepareInventoryDescription(context); } /* -------------------------------------------- */ @@ -162,13 +182,7 @@ export default function DHApplicationMixin(Base) { const { actionId, itemUuid } = el.parentElement.dataset; const selector = `${actionId ? `[data-action-id="${actionId}"]` : `[data-item-uuid="${itemUuid}"]`} .extensible`; const newExtensible = newElement.querySelector(selector); - - if (!newExtensible) continue; - newExtensible.classList.add('extended'); - const descriptionElement = newExtensible.querySelector('.invetory-description'); - if (descriptionElement) { - this.#prepareInventoryDescription(newExtensible, descriptionElement); - } + newExtensible?.classList.add('extended'); } } @@ -395,6 +409,7 @@ export default function DHApplicationMixin(Base) { context.source = this.document; context.fields = this.document.schema.fields; context.systemFields = this.document.system.schema.fields; + context.settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.appearance); return context; } @@ -404,32 +419,69 @@ export default function DHApplicationMixin(Base) { /** * Prepares and enriches an inventory item or action description for display. - * @param {HTMLElement} extensibleElement - The parent element containing the description. - * @param {HTMLElement} descriptionElement - The element where the enriched description will be rendered. * @returns {Promise} */ - async #prepareInventoryDescription(extensibleElement, descriptionElement) { - const parent = extensibleElement.closest('[data-item-uuid], [data-action-id]'); - const { actionId, itemUuid } = parent?.dataset || {}; - if (!actionId && !itemUuid) return; + async #prepareInventoryDescription(context) { + // Get all inventory item elements with a data-item-uuid attribute + const inventoryItems = this.element.querySelectorAll('.inventory-item[data-item-uuid]'); + for (const el of inventoryItems) { + // Get the doc uuid from the element + const { itemUuid } = el?.dataset || {}; + if (!itemUuid) continue; - const doc = itemUuid - ? await getDocFromElement(extensibleElement) - : this.document.system.attack?.id === actionId - ? this.document.system.attack - : this.document.system.actions?.get(actionId); - if (!doc) return; + //get doc by uuid + const doc = await fromUuid(itemUuid); - const description = game.i18n.localize(doc.system?.description ?? doc.description); - const isAction = !!actionId; - descriptionElement.innerHTML = await foundry.applications.ux.TextEditor.implementation.enrichHTML( - description, - { - relativeTo: isAction ? doc.parent : doc, - rollData: doc.getRollData?.(), - secrets: isAction ? doc.parent.isOwner : doc.isOwner + //get inventory-item description element + const descriptionElement = el.querySelector('.invetory-description'); + if (!doc || !descriptionElement) continue; + + // localize the description (idk if it's still necessary) + const description = game.i18n.localize(doc.system?.description ?? doc.description); + + // Enrich the description and attach it; + const isAction = doc.documentName === 'Action'; + descriptionElement.innerHTML = await foundry.applications.ux.TextEditor.implementation.enrichHTML( + description, + { + relativeTo: isAction ? doc.parent : doc, + rollData: doc.getRollData?.(), + secrets: isAction ? doc.parent.isOwner : doc.isOwner + } + ); + } + } + + /* -------------------------------------------- */ + /* Extend Descriptions by Settings */ + /* -------------------------------------------- */ + + /** + * Extend inventory description when enabled in settings. + * @returns {Promise} + */ + async #autoExtendDescriptions(context) { + const inventoryItems = this.element.querySelectorAll('.inventory-item[data-item-uuid]'); + for (const el of inventoryItems) { + // Get the doc uuid from the element + const { itemUuid } = el?.dataset || {}; + if (!itemUuid) continue; + + //get doc by uuid + const doc = await fromUuid(itemUuid); + + //check the type of the document + const actorType = + doc?.type === 'adversary' && context.document?.type === 'environment' + ? typeSettingsMap[doc?.type] + : doc.actor?.type; + + // If the actor type is defined and the setting is enabled, extend the description + if (typeSettingsMap[actorType]) { + const settingKey = typeSettingsMap[actorType]; + if (context.settings[settingKey]) this.#activeExtended(el); } - ); + } } /* -------------------------------------------- */ @@ -437,8 +489,6 @@ export default function DHApplicationMixin(Base) { /* -------------------------------------------- */ static async #addNewItem(event, target) { - const { type } = target.dataset; - const createChoice = await foundry.applications.api.DialogV2.wait({ classes: ['dh-style', 'two-big-buttons'], buttons: [ @@ -606,10 +656,12 @@ export default function DHApplicationMixin(Base) { static async #toggleExtended(_, target) { const container = target.closest('.inventory-item'); const extensible = container?.querySelector('.extensible'); - const t = extensible?.classList.toggle('extended'); + extensible?.classList.toggle('extended'); + } - const descriptionElement = extensible?.querySelector('.invetory-description'); - if (t && !!descriptionElement) await this.#prepareInventoryDescription(extensible, descriptionElement); + async #activeExtended(element) { + const extensible = element?.querySelector('.extensible'); + extensible?.classList.add('extended'); } } diff --git a/module/applications/sheets/api/item-attachment-sheet.mjs b/module/applications/sheets/api/item-attachment-sheet.mjs index e9c3994c..73c39923 100644 --- a/module/applications/sheets/api/item-attachment-sheet.mjs +++ b/module/applications/sheets/api/item-attachment-sheet.mjs @@ -24,7 +24,7 @@ export default function ItemAttachmentSheet(Base) { ...super.TABS, primary: { ...super.TABS?.primary, - tabs: [...(super.TABS?.primary?.tabs || []), { id: 'attachments' }], + tabs: [...(super.TABS?.primary?.tabs || []) /*{ id: 'attachments' }*/], // Disabled until fixed initial: super.TABS?.primary?.initial || 'description', labelPrefix: super.TABS?.primary?.labelPrefix || 'DAGGERHEART.GENERAL.Tabs' } diff --git a/module/applications/ui/combatTracker.mjs b/module/applications/ui/combatTracker.mjs index d3d8eb66..69c43b61 100644 --- a/module/applications/ui/combatTracker.mjs +++ b/module/applications/ui/combatTracker.mjs @@ -65,6 +65,57 @@ export default class DhCombatTracker extends foundry.applications.sidebar.tabs.C ]; } + getDefeatedId(combatant) { + if (!combatant.actor) return CONFIG.specialStatusEffects.DEFEATED; + + const settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).defeated; + return settings[`${combatant.actor.type}Default`]; + } + + /** @inheritdoc */ + async _onToggleDefeatedStatus(combatant) { + const isDefeated = !combatant.isDefeated; + await combatant.update({ defeated: isDefeated }); + await combatant.actor?.toggleStatusEffect(this.getDefeatedId(combatant), { overlay: true, active: isDefeated }); + } + + /** @inheritdoc */ + async _prepareTurnContext(combat, combatant, index) { + const { id, name, isOwner, isDefeated, hidden, initiative, permission } = combatant; + const resource = permission >= CONST.DOCUMENT_OWNERSHIP_LEVELS.OBSERVER ? combatant.resource : null; + const hasDecimals = Number.isFinite(initiative) && !Number.isInteger(initiative); + const turn = { + hasDecimals, + hidden, + id, + isDefeated, + initiative, + isOwner, + name, + resource, + active: index === combat.turn, + canPing: combatant.sceneId === canvas.scene?.id && game.user.hasPermission('PING_CANVAS'), + img: await this._getCombatantThumbnail(combatant) + }; + + turn.css = [turn.active ? 'active' : null, hidden ? 'hide' : null, isDefeated ? 'defeated' : null].filterJoin( + ' ' + ); + + const defeatedId = this.getDefeatedId(combatant); + const effects = []; + for (const effect of combatant.actor?.temporaryEffects ?? []) { + if (effect.statuses.has(defeatedId)) turn.isDefeated = true; + else if (effect.img) effects.push({ img: effect.img, name: effect.name }); + } + turn.effects = { + icons: effects, + tooltip: this._formatEffectsTooltip(effects) + }; + + return turn; + } + async setCombatantSpotlight(combatantId) { const update = { system: { diff --git a/module/canvas/placeables/token.mjs b/module/canvas/placeables/token.mjs index e6179b10..09b3b192 100644 --- a/module/canvas/placeables/token.mjs +++ b/module/canvas/placeables/token.mjs @@ -15,17 +15,19 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token { acc.push(effect); const currentStatusActiveEffects = acc.filter( - x => x.statuses.size === 1 && x.name === game.i18n.localize(statusMap.get(x.statuses.first()).name) + 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.has(status))) { const statusData = statusMap.get(status); - acc.push({ - name: game.i18n.localize(statusData.name), - statuses: [status], - img: statusData.icon, - tint: effect.tint - }); + if (statusData) { + acc.push({ + name: game.i18n.localize(statusData.name), + statuses: [status], + img: statusData.icon, + tint: effect.tint + }); + } } } diff --git a/module/config/generalConfig.mjs b/module/config/generalConfig.mjs index ee0b6671..34ca6009 100644 --- a/module/config/generalConfig.mjs +++ b/module/config/generalConfig.mjs @@ -164,39 +164,49 @@ export const healingTypes = { } }; -export const conditions = { - vulnerable: { - id: 'vulnerable', - name: 'DAGGERHEART.CONFIG.Condition.vulnerable.name', - icon: 'icons/magic/control/silhouette-fall-slip-prone.webp', - description: 'DAGGERHEART.CONFIG.Condition.vulnerable.description' - }, - hidden: { - id: 'hidden', - name: 'DAGGERHEART.CONFIG.Condition.hidden.name', - icon: 'icons/magic/perception/silhouette-stealth-shadow.webp', - description: 'DAGGERHEART.CONFIG.Condition.hidden.description' - }, - restrained: { - id: 'restrained', - name: 'DAGGERHEART.CONFIG.Condition.restrained.name', - icon: 'icons/magic/control/debuff-chains-shackle-movement-red.webp', - description: 'DAGGERHEART.CONFIG.Condition.restrained.description' +export const defeatedConditions = { + defeated: { + id: 'defeated', + name: 'DAGGERHEART.CONFIG.Condition.defeated.name', + img: 'icons/magic/control/fear-fright-mask-orange.webp', + description: 'DAGGERHEART.CONFIG.Condition.defeated.description' }, unconscious: { id: 'unconscious', name: 'DAGGERHEART.CONFIG.Condition.unconscious.name', - icon: 'icons/magic/control/sleep-bubble-purple.webp', + img: 'icons/magic/control/sleep-bubble-purple.webp', description: 'DAGGERHEART.CONFIG.Condition.unconscious.description' }, dead: { id: 'dead', name: 'DAGGERHEART.CONFIG.Condition.dead.name', - icon: 'icons/magic/death/grave-tombstone-glow-teal.webp', + img: 'icons/magic/death/grave-tombstone-glow-teal.webp', description: 'DAGGERHEART.CONFIG.Condition.dead.description' } }; +export const conditions = { + vulnerable: { + id: 'vulnerable', + name: 'DAGGERHEART.CONFIG.Condition.vulnerable.name', + img: 'icons/magic/control/silhouette-fall-slip-prone.webp', + description: 'DAGGERHEART.CONFIG.Condition.vulnerable.description' + }, + hidden: { + id: 'hidden', + name: 'DAGGERHEART.CONFIG.Condition.hidden.name', + img: 'icons/magic/perception/silhouette-stealth-shadow.webp', + description: 'DAGGERHEART.CONFIG.Condition.hidden.description' + }, + restrained: { + id: 'restrained', + name: 'DAGGERHEART.CONFIG.Condition.restrained.name', + img: 'icons/magic/control/debuff-chains-shackle-movement-red.webp', + description: 'DAGGERHEART.CONFIG.Condition.restrained.description' + }, + ...defeatedConditions +}; + export const defaultRestOptions = { shortRest: () => ({ tendToWounds: { diff --git a/module/data/action/attackAction.mjs b/module/data/action/attackAction.mjs index 1ba5f91e..62463d15 100644 --- a/module/data/action/attackAction.mjs +++ b/module/data/action/attackAction.mjs @@ -42,4 +42,32 @@ export default class DHAttackAction extends DHDamageAction { return result; } + + /** + * Generate a localized label array for this item subtype. + * @returns {(string | { value: string, icons: string[] })[]} An array of localized strings and damage label objects. + */ + _getLabels() { + const labels = []; + const { roll, range, damage } = this; + + if (roll.trait) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.Traits.${roll.trait}.short`)) + if (range) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.Range.${range}.short`)); + + for (const { value, type } of damage.parts) { + const str = Roll.replaceFormulaData(value.getFormula(), this.actor?.getRollData() ?? {}); + + const icons = Array.from(type) + .map(t => CONFIG.DH.GENERAL.damageTypes[t]?.icon) + .filter(Boolean); + + if (icons.length === 0) { + labels.push(str); + } else { + labels.push({ value: str, icons }); + } + } + + return labels; + } } diff --git a/module/data/action/macroAction.mjs b/module/data/action/macroAction.mjs index c4a08df7..58b8eba5 100644 --- a/module/data/action/macroAction.mjs +++ b/module/data/action/macroAction.mjs @@ -1,19 +1,13 @@ import DHBaseAction from './baseAction.mjs'; export default class DHMacroAction extends DHBaseAction { - static defineSchema() { - const fields = foundry.data.fields; - return { - ...super.defineSchema(), - documentUUID: new fields.DocumentUUIDField({ type: 'Macro' }) - }; - } + static extraSchemas = [...super.extraSchemas, 'macro']; async trigger(event, ...args) { - const fixUUID = !this.documentUUID.includes('Macro.') ? `Macro.${this.documentUUID}` : this.documentUUID, + const fixUUID = !this.macro.includes('Macro.') ? `Macro.${this.macro}` : this.macro, macro = await fromUuid(fixUUID); try { - if (!macro) throw new Error(`No macro found for the UUID: ${this.documentUUID}.`); + if (!macro) throw new Error(`No macro found for the UUID: ${this.macro}.`); macro.execute(); } catch (error) { ui.notifications.error(error); diff --git a/module/data/actor/base.mjs b/module/data/actor/base.mjs index a32ac9dd..5b225228 100644 --- a/module/data/actor/base.mjs +++ b/module/data/actor/base.mjs @@ -106,6 +106,28 @@ export default class BaseDataActor extends foundry.abstract.TypeDataModel { }, []); options.scrollingTextData = textData; } + + if (changes.system?.resources) { + const defeatedSettings = game.settings.get( + CONFIG.DH.id, + CONFIG.DH.SETTINGS.gameSettings.Automation + ).defeated; + const typeForDefeated = ['character', 'adversary', 'companion'].find(x => x === this.parent.type); + if (defeatedSettings.enabled && typeForDefeated) { + const resource = typeForDefeated === 'companion' ? 'stress' : 'hitPoints'; + if (changes.system.resources[resource]) { + const becameMax = changes.system.resources[resource].value === this.resources[resource].max; + const wasMax = + this.resources[resource].value === this.resources[resource].max && + this.resources[resource].value !== changes.system.resources[resource].value; + if (becameMax) { + this.parent.toggleDefeated(true); + } else if (wasMax) { + this.parent.toggleDefeated(false); + } + } + } + } } _onUpdate(changes, options, userId) { diff --git a/module/data/actor/character.mjs b/module/data/actor/character.mjs index c5e250a4..71b734cd 100644 --- a/module/data/actor/character.mjs +++ b/module/data/actor/character.mjs @@ -103,7 +103,7 @@ export default class DhCharacter extends BaseDataActor { }), attack: new ActionField({ initial: { - name: 'Attack', + name: 'Unarmed Attack', img: 'icons/skills/melee/unarmed-punch-fist-yellow-red.webp', _id: foundry.utils.randomID(), systemPath: 'attack', @@ -394,19 +394,22 @@ export default class DhCharacter extends BaseDataActor { return this.parent.effects.find(x => x.type === 'beastform'); } + /** + * Gets the unarmed attackwhen no primary or secondary weapon is equipped. + * Returns `null` if either weapon is equipped. + * If the actor is in beastform, overrides the attack's name and image. + * + * @returns {DHAttackAction|null} + */ get usedUnarmed() { - const primaryWeaponEquipped = this.primaryWeapon?.system?.equipped; - const secondaryWeaponEquipped = this.secondaryWeapon?.system?.equipped; - return !primaryWeaponEquipped && !secondaryWeaponEquipped - ? { - ...this.attack, - uuid: this.attack.uuid, - id: this.attack.id, - name: this.activeBeastform ? 'DAGGERHEART.ITEMS.Beastform.attackName' : this.attack.name, - img: this.activeBeastform ? 'icons/creatures/claws/claw-straight-brown.webp' : this.attack.img, - actor: this.parent - } - : null; + if (this.primaryWeapon?.system?.equipped || this.secondaryWeapon?.system?.equipped) return null; + + const attack = foundry.utils.deepClone(this.attack); + if (this.activeBeastform) { + attack.name = 'DAGGERHEART.ITEMS.Beastform.attackName'; + attack.img = 'icons/creatures/claws/claw-straight-brown.webp'; + } + return attack; } get sheetLists() { @@ -599,7 +602,20 @@ export default class DhCharacter extends BaseDataActor { } prepareDerivedData() { - const baseHope = this.resources.hope.value + (this.companion?.system?.resources?.hope ?? 0); + let baseHope = this.resources.hope.value; + if (this.companion) { + for (let levelKey in this.companion.system.levelData.levelups) { + const level = this.companion.system.levelData.levelups[levelKey]; + for (let selection of level.selections) { + switch (selection.type) { + case 'hope': + this.resources.hope.max += selection.value; + break; + } + } + } + } + this.resources.hope.value = Math.min(baseHope, this.resources.hope.max); this.attack.roll.trait = this.rules.attack.roll.trait ?? this.attack.roll.trait; diff --git a/module/data/actor/companion.mjs b/module/data/actor/companion.mjs index 7a11f1d1..48572460 100644 --- a/module/data/actor/companion.mjs +++ b/module/data/actor/companion.mjs @@ -40,12 +40,14 @@ export default class DhCompanion extends BaseDataActor { experiences: new fields.TypedObjectField( new fields.SchemaField({ name: new fields.StringField({}), - value: new fields.NumberField({ integer: true, initial: 0 }) + value: new fields.NumberField({ integer: true, initial: 0 }), + description: new fields.StringField(), + core: new fields.BooleanField({ initial: false }) }), { initial: { - experience1: { value: 2 }, - experience2: { value: 2 } + experience1: { value: 2, core: true }, + experience2: { value: 2, core: true } } } ), @@ -134,6 +136,23 @@ export default class DhCompanion extends BaseDataActor { } } + async _preUpdate(changes, options, userId) { + const allowed = await super._preUpdate(changes, options, userId); + if (allowed === false) return; + + /* The first two experiences are always marked as core */ + if (changes.system?.experiences && Object.keys(this.experiences).length < 2) { + const experiences = new Set(Object.keys(this.experiences)); + const changeExperiences = new Set(Object.keys(changes.system.experiences)); + const newExperiences = Array.from(changeExperiences.difference(experiences)); + + for (var i = 0; i < Math.min(newExperiences.length, 2 - experiences.size); i++) { + const experience = newExperiences[i]; + changes.system.experiences[experience].core = true; + } + } + } + async _preDelete() { if (this.partner) { await this.partner.update({ 'system.companion': null }); diff --git a/module/data/chat-message/adversaryRoll.mjs b/module/data/chat-message/adversaryRoll.mjs index 337acf5b..1447acb8 100644 --- a/module/data/chat-message/adversaryRoll.mjs +++ b/module/data/chat-message/adversaryRoll.mjs @@ -110,6 +110,7 @@ export default class DHActorRoll extends foundry.abstract.TypeDataModel { if (this.hasTarget) { this.hasHitTarget = this.targets.filter(t => t.hit === true).length > 0; this.currentTargets = this.getTargetList(); + // this.registerTargetHook(); if (this.targetMode === true && this.hasRoll) { this.targetShort = this.targets.reduce( diff --git a/module/data/combatant.mjs b/module/data/combatant.mjs index cae5d08f..bb54c798 100644 --- a/module/data/combatant.mjs +++ b/module/data/combatant.mjs @@ -8,4 +8,10 @@ export default class DhCombatant extends foundry.abstract.TypeDataModel { actionTokens: new fields.NumberField({ required: true, integer: true, initial: 3 }) }; } + + get isDefeated() { + const { unconscious, defeated, dead } = CONFIG.DH.GENERAL.conditions; + const defeatedConditions = new Set([unconscious.id, defeated.id, dead.id]); + return this.defeated || this.actor?.statuses.intersection(defeatedConditions)?.size; + } } diff --git a/module/data/fields/action/_module.mjs b/module/data/fields/action/_module.mjs index 192341e7..e6caa963 100644 --- a/module/data/fields/action/_module.mjs +++ b/module/data/fields/action/_module.mjs @@ -8,3 +8,4 @@ export { default as BeastformField } from './beastformField.mjs'; export { default as DamageField } from './damageField.mjs'; export { default as HealingField } from './healingField.mjs'; export { default as RollField } from './rollField.mjs'; +export { default as MacroField } from './macroField.mjs'; diff --git a/module/data/fields/action/macroField.mjs b/module/data/fields/action/macroField.mjs new file mode 100644 index 00000000..62da0da0 --- /dev/null +++ b/module/data/fields/action/macroField.mjs @@ -0,0 +1,7 @@ +const fields = foundry.data.fields; + +export default class MacroField extends fields.DocumentUUIDField { + constructor(context = {}) { + super({ type: "Macro" }, context); + } +} diff --git a/module/data/fields/actionField.mjs b/module/data/fields/actionField.mjs index 8d865562..dc567da9 100644 --- a/module/data/fields/actionField.mjs +++ b/module/data/fields/actionField.mjs @@ -84,7 +84,7 @@ export class ActionField extends foundry.data.fields.ObjectField { getModel(value) { return ( game.system.api.models.actions.actionsTypes[value.type] ?? - game.system.api.models.actions.actionsTypes.attack + null ); } @@ -93,7 +93,6 @@ export class ActionField extends foundry.data.fields.ObjectField { /** @override */ _cleanType(value, options) { if (!(typeof value === 'object')) value = {}; - const cls = this.getModel(value); if (cls) return cls.cleanData(value, options); return value; diff --git a/module/data/item/armor.mjs b/module/data/item/armor.mjs index a8c3dcec..7f70d3f7 100644 --- a/module/data/item/armor.mjs +++ b/module/data/item/armor.mjs @@ -144,7 +144,8 @@ export default class DHArmor extends AttachableItem { * @returns {(string | { value: string, icons: string[] })[]} An array of localized strings and damage label objects. */ _getLabels() { - const labels = [`${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseScore')}: ${this.baseScore}`]; + const labels = []; + if(this.baseScore) labels.push(`${game.i18n.localize('DAGGERHEART.ITEMS.Armor.baseScore')}: ${this.baseScore}`) return labels; } diff --git a/module/data/item/domainCard.mjs b/module/data/item/domainCard.mjs index 7705adb1..5c471ca1 100644 --- a/module/data/item/domainCard.mjs +++ b/module/data/item/domainCard.mjs @@ -77,6 +77,7 @@ export default class DHDomainCard extends BaseDataItem { const tags = [ game.i18n.localize(`DAGGERHEART.CONFIG.DomainCardTypes.${this.type}`), this.domainLabel, + `${game.i18n.localize('DAGGERHEART.GENERAL.levelShort')}: ${this.level}`, `${game.i18n.localize('DAGGERHEART.ITEMS.DomainCard.recallCost')}: ${this.recallCost}` ]; @@ -88,15 +89,16 @@ export default class DHDomainCard extends BaseDataItem { * @returns {(string | { value: string, icons: string[] })[]} An array of localized strings and damage label objects. */ _getLabels() { - const labels = [ - game.i18n.localize(`DAGGERHEART.CONFIG.DomainCardTypes.${this.type}`), - this.domainLabel, - { + const labels = []; + + if (this.type) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.DomainCardTypes.${this.type}`)); + if (this.domainLabel) labels.push(this.domainLabel); + if (this.recallCost) { + labels.push({ value: `${this.recallCost}`, //converts the number to a string icons: ['fa-bolt'] - } - ]; - + }); + } return labels; } } diff --git a/module/data/item/weapon.mjs b/module/data/item/weapon.mjs index 60a17e3d..66025cc5 100644 --- a/module/data/item/weapon.mjs +++ b/module/data/item/weapon.mjs @@ -1,5 +1,5 @@ import AttachableItem from './attachableItem.mjs'; -import { ActionsField, ActionField } from '../fields/actionField.mjs'; +import { ActionField } from '../fields/actionField.mjs'; export default class DHWeapon extends AttachableItem { /** @inheritDoc */ @@ -18,12 +18,23 @@ export default class DHWeapon extends AttachableItem { const fields = foundry.data.fields; return { ...super.defineSchema(), - tier: new fields.NumberField({ required: true, integer: true, initial: 1, min: 1, label: "DAGGERHEART.GENERAL.Tiers.singular" }), + tier: new fields.NumberField({ + required: true, + integer: true, + initial: 1, + min: 1, + label: 'DAGGERHEART.GENERAL.Tiers.singular' + }), equipped: new fields.BooleanField({ initial: false }), //SETTINGS - secondary: new fields.BooleanField({ initial: false, label: "DAGGERHEART.ITEMS.Weapon.secondaryWeapon" }), - burden: new fields.StringField({ required: true, choices: CONFIG.DH.GENERAL.burden, initial: 'oneHanded', label: "DAGGERHEART.GENERAL.burden" }), + secondary: new fields.BooleanField({ initial: false, label: 'DAGGERHEART.ITEMS.Weapon.secondaryWeapon' }), + burden: new fields.StringField({ + required: true, + choices: CONFIG.DH.GENERAL.burden, + initial: 'oneHanded', + label: 'DAGGERHEART.GENERAL.burden' + }), weaponFeatures: new fields.ArrayField( new fields.SchemaField({ value: new fields.StringField({ @@ -209,26 +220,23 @@ export default class DHWeapon extends AttachableItem { * @returns {(string | { value: string, icons: string[] })[]} An array of localized strings and damage label objects. */ _getLabels() { + const labels = []; const { roll, range, damage } = this.attack; - const labels = [ - game.i18n.localize(`DAGGERHEART.CONFIG.Traits.${roll.trait}.short`), - game.i18n.localize(`DAGGERHEART.CONFIG.Range.${range}.short`) - ]; + if (roll.trait) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.Traits.${roll.trait}.short`)); + if (range) labels.push(game.i18n.localize(`DAGGERHEART.CONFIG.Range.${range}.short`)); for (const { value, type } of damage.parts) { - const str = [value.dice]; - if (value.bonus) str.push(value.bonus.signedString()); + const str = Roll.replaceFormulaData(value.getFormula(), this.actor?.getRollData() ?? {}); const icons = Array.from(type) .map(t => CONFIG.DH.GENERAL.damageTypes[t]?.icon) .filter(Boolean); - const labelValue = str.join(''); if (icons.length === 0) { - labels.push(labelValue); + labels.push(str); } else { - labels.push({ value: labelValue, icons }); + labels.push({ value: str, icons }); } } diff --git a/module/data/levelTier.mjs b/module/data/levelTier.mjs index e9e8d47b..e42cfc54 100644 --- a/module/data/levelTier.mjs +++ b/module/data/levelTier.mjs @@ -404,7 +404,27 @@ export const defaultCompanionTier = { start: 2, end: 10 }, - initialAchievements: {}, + initialAchievements: { + experience: { + nr: 1, + modifier: 2 + } + }, + /* Improved this. Quick solution for companions */ + extraAchievements: { + 5: { + experience: { + nr: 1, + modifier: 2 + } + }, + 8: { + experience: { + nr: 1, + modifier: 2 + } + } + }, availableOptions: 1, domainCardByLevel: 0, options: { diff --git a/module/data/levelup.mjs b/module/data/levelup.mjs index 0f248f45..665b3264 100644 --- a/module/data/levelup.mjs +++ b/module/data/levelup.mjs @@ -26,6 +26,7 @@ export class DhLevelup extends foundry.abstract.DataModel { return acc; }, {}) : {}; + const domainCards = [...Array(tier.domainCardByLevel).keys()].reduce((acc, _) => { const id = foundry.utils.randomID(); acc[id] = { uuid: null, itemUuid: null, level: i }; @@ -42,6 +43,20 @@ export class DhLevelup extends foundry.abstract.DataModel { belongingLevels.push(i); } + /* Improve. Temporary handling for Companion new experiences */ + Object.keys(tier.extraAchievements ?? {}).forEach(key => { + const level = Number(key); + if (level >= startLevel && level <= endLevel) { + const levelExtras = tier.extraAchievements[level]; + if (levelExtras.experience) { + levels[level].achievements.experiences[foundry.utils.randomID()] = { + name: '', + modifier: levelExtras.experience.modifier + }; + } + } + }); + tiers[key] = { name: tier.name, belongingLevels: belongingLevels, diff --git a/module/data/settings/Appearance.mjs b/module/data/settings/Appearance.mjs index e215f956..ac980346 100644 --- a/module/data/settings/Appearance.mjs +++ b/module/data/settings/Appearance.mjs @@ -55,6 +55,22 @@ export default class DhAppearance extends foundry.abstract.DataModel { showGenericStatusEffects: new fields.BooleanField({ initial: true, label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.showGenericStatusEffects.label' + }), + extendCharacterDescriptions: new fields.BooleanField({ + initial: false, + label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.extendCharacterDescriptions.label' + }), + extendAdversaryDescriptions: new fields.BooleanField({ + initial: false, + label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.extendAdversaryDescriptions.label' + }), + extendEnvironmentDescriptions: new fields.BooleanField({ + initial: false, + label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.extendEnvironmentDescriptions.label' + }), + extendItemDescriptions: new fields.BooleanField({ + initial: false, + label: 'DAGGERHEART.SETTINGS.Appearance.FIELDS.extendItemDescriptions.label' }) }; } diff --git a/module/data/settings/Automation.mjs b/module/data/settings/Automation.mjs index facaec17..e1d63669 100644 --- a/module/data/settings/Automation.mjs +++ b/module/data/settings/Automation.mjs @@ -50,6 +50,36 @@ export default class DhAutomation extends foundry.abstract.DataModel { required: true, initial: false, label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.playerCanEditSheet.label' + }), + defeated: new fields.SchemaField({ + enabled: new fields.BooleanField({ + required: true, + initial: false, + label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.defeated.enabled.label' + }), + overlay: new fields.BooleanField({ + required: true, + initial: true, + label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.defeated.overlay.label' + }), + characterDefault: new fields.StringField({ + required: true, + choices: CONFIG.DH.GENERAL.defeatedConditions, + initial: CONFIG.DH.GENERAL.defeatedConditions.unconscious.id, + label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.defeated.characterDefault.label' + }), + adversaryDefault: new fields.StringField({ + required: true, + choices: CONFIG.DH.GENERAL.defeatedConditions, + initial: CONFIG.DH.GENERAL.defeatedConditions.defeated.id, + label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.defeated.adversaryDefault.label' + }), + companionDefault: new fields.StringField({ + required: true, + choices: CONFIG.DH.GENERAL.defeatedConditions, + initial: CONFIG.DH.GENERAL.defeatedConditions.defeated.id, + label: 'DAGGERHEART.SETTINGS.Automation.FIELDS.defeated.companionDefault.label' + }) }) }; } diff --git a/module/dice/damageRoll.mjs b/module/dice/damageRoll.mjs index 8a72d86e..0d64329e 100644 --- a/module/dice/damageRoll.mjs +++ b/module/dice/damageRoll.mjs @@ -137,9 +137,10 @@ export default class DamageRoll extends DHRoll { } if (config.isCritical && part.applyTo === CONFIG.DH.GENERAL.healingTypes.hitPoints.id) { - const tmpRoll = Roll.fromTerms(part.roll.terms)._evaluateSync({ maximize: true }), - criticalBonus = tmpRoll.total - this.constructor.calculateTotalModifiers(tmpRoll); - part.roll.terms.push(...this.formatModifier(criticalBonus)); + const total = part.roll.dice.reduce((acc, term) => acc + term._faces*term._number, 0); + if (total > 0) { + part.roll.terms.push(...this.formatModifier(total)); + } } /* To Remove When Reaction System */ diff --git a/module/dice/dhRoll.mjs b/module/dice/dhRoll.mjs index 126a7944..88d40c32 100644 --- a/module/dice/dhRoll.mjs +++ b/module/dice/dhRoll.mjs @@ -222,26 +222,27 @@ export const registerRollDiceHooks = () => { ) return; - const actor = await fromUuid(config.source.actor), - updates = []; + const actor = await fromUuid(config.source.actor); + let updates = []; if (!actor) return; - if (config.roll.isCritical || config.roll.result.duality === 1) updates.push({ key: 'hope', value: 1 }); - if (config.roll.isCritical) updates.push({ key: 'stress', value: -1 }); - if (config.roll.result.duality === -1) updates.push({ key: 'fear', value: 1 }); + if (config.roll.isCritical || config.roll.result.duality === 1) + updates.push({ key: 'hope', value: 1, total: -1, enabled: true }); + if (config.roll.isCritical) updates.push({ key: 'stress', value: -1, total: 1, enabled: true }); + if (config.roll.result.duality === -1) updates.push({ key: 'fear', value: 1, total: -1, enabled: true }); if (config.rerolledRoll) { if (config.rerolledRoll.isCritical || config.rerolledRoll.result.duality === 1) - updates.push({ key: 'hope', value: -1 }); - if (config.rerolledRoll.isCritical) updates.push({ key: 'stress', value: 1 }); - if (config.rerolledRoll.result.duality === -1) updates.push({ key: 'fear', value: -1 }); + updates.push({ key: 'hope', value: -1, total: 1, enabled: true }); + if (config.rerolledRoll.isCritical) updates.push({ key: 'stress', value: 1, total: -1, enabled: true }); + if (config.rerolledRoll.result.duality === -1) + updates.push({ key: 'fear', value: -1, total: 1, enabled: true }); } if (updates.length) { const target = actor.system.partner ?? actor; - if (!['dead', 'unconscious'].some(x => actor.statuses.has(x))) { - setTimeout(() => { - target.modifyResource(updates); - }, 50); + if (!['dead', 'defeated', 'unconscious'].some(x => actor.statuses.has(x))) { + if (config.rerolledRoll) target.modifyResource(updates); + else config.costs = [...(config.costs ?? []), ...updates]; } } @@ -254,5 +255,7 @@ export const registerRollDiceHooks = () => { const currentCombatant = game.combat.combatants.get(game.combat.current?.combatantId); if (currentCombatant?.actorId == actor.id) ui.combat.setCombatantSpotlight(currentCombatant.id); } + + return; }); }; diff --git a/module/documents/activeEffect.mjs b/module/documents/activeEffect.mjs index bf535b78..f46cc9db 100644 --- a/module/documents/activeEffect.mjs +++ b/module/documents/activeEffect.mjs @@ -118,7 +118,7 @@ export default class DhActiveEffect extends foundry.documents.ActiveEffect { for (const statusId of this.statuses) { const status = CONFIG.statusEffects.find(s => s.id === statusId); - tags.push(game.i18n.localize(status.name)); + if (status) tags.push(game.i18n.localize(status.name)); } return tags; diff --git a/module/documents/actor.mjs b/module/documents/actor.mjs index c75db559..6b01c058 100644 --- a/module/documents/actor.mjs +++ b/module/documents/actor.mjs @@ -2,6 +2,7 @@ import { emitAsGM, GMUpdateEvent } from '../systemRegistration/socket.mjs'; import { LevelOptionType } from '../data/levelTier.mjs'; import DHFeature from '../data/item/feature.mjs'; import { damageKeyToNumber } from '../helpers/utils.mjs'; +import DhCompanionLevelUp from '../applications/levelup/companionLevelup.mjs'; export default class DhpActor extends Actor { /** @@ -142,9 +143,6 @@ export default class DhpActor extends Actor { }, {}) }); this.update(getUpdate()); - if (this.system.companion) { - this.system.companion.update(getUpdate()); - } } if (subclassFeatureState.class) { @@ -195,10 +193,6 @@ export default class DhpActor extends Actor { } }); this.sheet.render(); - - if (this.system.companion) { - this.system.companion.updateLevel(newLevel); - } } } @@ -219,16 +213,6 @@ export default class DhpActor extends Actor { core: true } }); - - if (this.system.companion) { - await this.system.companion.update({ - [`system.experiences.${experienceKey}`]: { - name: '', - value: experience.modifier, - core: true - } - }); - } } } @@ -405,6 +389,7 @@ export default class DhpActor extends Actor { }; } + const levelChange = this.system.levelData.level.changed - this.system.levelData.level.current; await this.update({ system: { levelData: { @@ -417,8 +402,21 @@ export default class DhpActor extends Actor { }); this.sheet.render(); - if (this.system.companion) { - this.system.companion.updateLevel(this.system.levelData.level.changed); + if (this.system.companion && !this.system.companion.system.levelData.canLevelUp) { + const confirmed = await foundry.applications.api.DialogV2.confirm({ + window: { + title: game.i18n.localize('DAGGERHEART.ACTORS.Character.companionLevelup.confirmTitle') + }, + content: game.i18n.format('DAGGERHEART.ACTORS.Character.companionLevelup.confirmText', { + name: this.system.companion.name, + levelChange: levelChange + }) + }); + + if (!confirmed) return; + + await this.system.companion.updateLevel(this.system.companion.system.levelData.level.current + levelChange); + new DhCompanionLevelUp(this.system.companion).render({ force: true }); } } @@ -720,4 +718,21 @@ export default class DhpActor extends Actor { value: 1 }); } + + async toggleDefeated(defeatedState) { + const settings = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Automation).defeated; + const { unconscious, defeated, dead } = CONFIG.DH.GENERAL.conditions; + const defeatedConditions = new Set([unconscious.id, defeated.id, dead.id]); + if (!defeatedState) { + for (let defeatedId of defeatedConditions) { + await this.toggleStatusEffect(defeatedId, { overlay: settings.overlay, active: defeatedState }); + } + } else { + const noDefeatedConditions = this.statuses.intersection(defeatedConditions).size === 0; + if (noDefeatedConditions) { + const condition = settings[`${this.type}Default`]; + await this.toggleStatusEffect(condition, { overlay: settings.overlay, active: defeatedState }); + } + } + } } diff --git a/module/documents/chatMessage.mjs b/module/documents/chatMessage.mjs index 2b10e251..baf4ca17 100644 --- a/module/documents/chatMessage.mjs +++ b/module/documents/chatMessage.mjs @@ -1,6 +1,5 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { targetHook = null; - targetSelection = null; async renderHTML() { const actor = game.actors.get(this.speaker.actor); @@ -24,7 +23,7 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { /** @inheritDoc */ prepareData() { - if (this.isAuthor && this.targetSelection === null) this.targetSelection = this.system.targets?.length > 0; + if (this.isAuthor && this.targetSelection === undefined) this.targetSelection = this.system.targets?.length > 0; super.prepareData(); } @@ -70,9 +69,13 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage { } } - if(!game.user.isGM && !this.isAuthor && !this.speakerActor?.isOwner) { - const buttons = html.querySelectorAll(".ability-card-footer > .ability-use-button"); - buttons.forEach(b => b.remove()); + if(!game.user.isGM) { + const applyButtons = html.querySelector(".apply-buttons"); + applyButtons?.remove(); + if(!this.isAuthor && !this.speakerActor?.isOwner) { + const buttons = html.querySelectorAll(".ability-card-footer > .ability-use-button"); + buttons.forEach(b => b.remove()); + } } } diff --git a/module/documents/token.mjs b/module/documents/token.mjs index becf80b8..6d080f31 100644 --- a/module/documents/token.mjs +++ b/module/documents/token.mjs @@ -72,4 +72,8 @@ export default class DHToken extends TokenDocument { } return attributes; } + + _shouldRecordMovementHistory() { + return false; + } } diff --git a/module/documents/tooltipManager.mjs b/module/documents/tooltipManager.mjs index ec6c8d9e..95621441 100644 --- a/module/documents/tooltipManager.mjs +++ b/module/documents/tooltipManager.mjs @@ -49,11 +49,11 @@ export default class DhTooltipManager extends foundry.helpers.interaction.Toolti const longRest = element.dataset.tooltip?.startsWith('#longRest#'); if (shortRest || longRest) { const key = element.dataset.tooltip.slice(shortRest ? 11 : 10); - const downtimeOptions = shortRest - ? CONFIG.DH.GENERAL.defaultRestOptions.shortRest() - : CONFIG.DH.GENERAL.defaultRestOptions.longRest(); - const move = downtimeOptions[key]; + const moves = game.settings.get(CONFIG.DH.id, CONFIG.DH.SETTINGS.gameSettings.Homebrew).restMoves[ + element.dataset.restType + ].moves; + const move = moves[key]; const description = await TextEditor.enrichHTML(move.description); html = await foundry.applications.handlebars.renderTemplate( `systems/daggerheart/templates/ui/tooltip/downtime.hbs`, diff --git a/module/helpers/utils.mjs b/module/helpers/utils.mjs index 63507782..439f1780 100644 --- a/module/helpers/utils.mjs +++ b/module/helpers/utils.mjs @@ -251,13 +251,15 @@ export const adjustRange = (rangeVal, decrease) => { }; export const updateActorTokens = async (actor, update) => { - await actor.prototypeToken.update(update); + await actor.prototypeToken.update({ ...update }); /* Update the tokens in all scenes belonging to Actor */ for (let token of actor.getDependentTokens()) { const tokenActor = token.baseActor ?? token.actor; if (tokenActor?.id === actor.id) { - await token.update(update); + await token.update({ + ...update + }); } } }; @@ -341,7 +343,7 @@ export const itemAbleRollParse = (value, actor, item) => { const model = isItemTarget ? item : actor; try { - return Roll.replaceFormulaData(slicedValue, model?.getRollData?.() ?? model); + return Roll.replaceFormulaData(slicedValue, isItemTarget || !model?.getRollData ? model : model.getRollData()); } catch (_) { return ''; } @@ -370,7 +372,7 @@ export function getScrollTextData(resources, resource, key) { export function createScrollText(actor, optionsData) { if (actor && optionsData?.length) { - actor.getDependentTokens().forEach(token => { + actor.getActiveTokens().forEach(token => { optionsData.forEach(data => { const { text, ...options } = data; canvas.interface.createScrollingText(token.getCenterPoint(), data.text, { diff --git a/module/systemRegistration/handlebars.mjs b/module/systemRegistration/handlebars.mjs index 48a99735..ff741b91 100644 --- a/module/systemRegistration/handlebars.mjs +++ b/module/systemRegistration/handlebars.mjs @@ -15,9 +15,8 @@ export const preloadHandlebarsTemplates = async function () { 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs', 'systems/daggerheart/templates/ui/combatTracker/combatTrackerSection.hbs', 'systems/daggerheart/templates/actionTypes/damage.hbs', - 'systems/daggerheart/templates/actionTypes/healing.hbs', 'systems/daggerheart/templates/actionTypes/resource.hbs', - 'systems/daggerheart/templates/actionTypes/uuid.hbs', + 'systems/daggerheart/templates/actionTypes/macro.hbs', 'systems/daggerheart/templates/actionTypes/uses.hbs', 'systems/daggerheart/templates/actionTypes/roll.hbs', 'systems/daggerheart/templates/actionTypes/save.hbs', diff --git a/src/packs/adversaries/adversary_Acid_Burrower_89yAh30vaNQOALlz.json b/src/packs/adversaries/adversary_Acid_Burrower_89yAh30vaNQOALlz.json index fe6001f2..fa7d4807 100644 --- a/src/packs/adversaries/adversary_Acid_Burrower_89yAh30vaNQOALlz.json +++ b/src/packs/adversaries/adversary_Acid_Burrower_89yAh30vaNQOALlz.json @@ -122,7 +122,7 @@ }, "_id": "TCKVaVweyJzhEArX", "systemPath": "actions", - "type": "", + "type": "attack", "description": "", "img": "icons/creatures/claws/claw-curved-jagged-yellow.webp", "chatDisplay": true, @@ -708,4 +708,4 @@ "_id": "89yAh30vaNQOALlz", "sort": 500000, "_key": "!actors!89yAh30vaNQOALlz" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Adult_Flickerfly_G7jiltRjgvVhZewm.json b/src/packs/adversaries/adversary_Adult_Flickerfly_G7jiltRjgvVhZewm.json index 3f6b218d..5053b21b 100644 --- a/src/packs/adversaries/adversary_Adult_Flickerfly_G7jiltRjgvVhZewm.json +++ b/src/packs/adversaries/adversary_Adult_Flickerfly_G7jiltRjgvVhZewm.json @@ -103,7 +103,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -768,4 +769,4 @@ ], "effects": [], "_key": "!actors!G7jiltRjgvVhZewm" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Apprentice_Assassin_vNIbYQ4YSzNf0WPE.json b/src/packs/adversaries/adversary_Apprentice_Assassin_vNIbYQ4YSzNf0WPE.json index 75504f87..8901186e 100644 --- a/src/packs/adversaries/adversary_Apprentice_Assassin_vNIbYQ4YSzNf0WPE.json +++ b/src/packs/adversaries/adversary_Apprentice_Assassin_vNIbYQ4YSzNf0WPE.json @@ -102,7 +102,8 @@ } ] }, - "img": "icons/weapons/daggers/dagger-bone-black.webp" + "img": "icons/weapons/daggers/dagger-bone-black.webp", + "type": "attack" } }, "flags": {}, @@ -319,4 +320,4 @@ ], "effects": [], "_key": "!actors!vNIbYQ4YSzNf0WPE" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Arch_Necromancer_WPEOIGfclNJxWb87.json b/src/packs/adversaries/adversary_Arch_Necromancer_WPEOIGfclNJxWb87.json index 431735e1..8ca8dbb2 100644 --- a/src/packs/adversaries/adversary_Arch_Necromancer_WPEOIGfclNJxWb87.json +++ b/src/packs/adversaries/adversary_Arch_Necromancer_WPEOIGfclNJxWb87.json @@ -114,7 +114,8 @@ } ] }, - "img": "icons/magic/unholy/beam-ringed-impact-purple.webp" + "img": "icons/magic/unholy/beam-ringed-impact-purple.webp", + "type": "attack" } }, "flags": {}, @@ -817,4 +818,4 @@ ], "effects": [], "_key": "!actors!WPEOIGfclNJxWb87" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Archer_Guard_JRhrrEg5UroURiAD.json b/src/packs/adversaries/adversary_Archer_Guard_JRhrrEg5UroURiAD.json index 48d5e1b7..ecc969ad 100644 --- a/src/packs/adversaries/adversary_Archer_Guard_JRhrrEg5UroURiAD.json +++ b/src/packs/adversaries/adversary_Archer_Guard_JRhrrEg5UroURiAD.json @@ -108,7 +108,8 @@ } ] }, - "img": "icons/weapons/bows/longbow-recurve-leather-brown.webp" + "img": "icons/weapons/bows/longbow-recurve-leather-brown.webp", + "type": "attack" } }, "flags": {}, @@ -393,4 +394,4 @@ ], "effects": [], "_key": "!actors!JRhrrEg5UroURiAD" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Archer_Squadron_0ts6CGd93lLqGZI5.json b/src/packs/adversaries/adversary_Archer_Squadron_0ts6CGd93lLqGZI5.json index 6d96be34..19385537 100644 --- a/src/packs/adversaries/adversary_Archer_Squadron_0ts6CGd93lLqGZI5.json +++ b/src/packs/adversaries/adversary_Archer_Squadron_0ts6CGd93lLqGZI5.json @@ -103,7 +103,8 @@ "roll": { "bonus": 0, "type": "attack" - } + }, + "type": "attack" } }, "flags": {}, @@ -514,4 +515,4 @@ } ], "_key": "!actors!0ts6CGd93lLqGZI5" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Assassin_Poisoner_h5RuhzGL17dW5FBT.json b/src/packs/adversaries/adversary_Assassin_Poisoner_h5RuhzGL17dW5FBT.json index d0796f0a..f0e15718 100644 --- a/src/packs/adversaries/adversary_Assassin_Poisoner_h5RuhzGL17dW5FBT.json +++ b/src/packs/adversaries/adversary_Assassin_Poisoner_h5RuhzGL17dW5FBT.json @@ -109,7 +109,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -528,4 +529,4 @@ ], "effects": [], "_key": "!actors!h5RuhzGL17dW5FBT" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Battle_Box_dgH3fW9FTYLaIDvS.json b/src/packs/adversaries/adversary_Battle_Box_dgH3fW9FTYLaIDvS.json index 11dd2789..df793a9d 100644 --- a/src/packs/adversaries/adversary_Battle_Box_dgH3fW9FTYLaIDvS.json +++ b/src/packs/adversaries/adversary_Battle_Box_dgH3fW9FTYLaIDvS.json @@ -109,7 +109,8 @@ } ] }, - "range": "" + "range": "", + "type": "attack" } }, "flags": {}, @@ -1284,4 +1285,4 @@ ], "effects": [], "_key": "!actors!dgH3fW9FTYLaIDvS" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Bear_71qKDLKO3CsrNkdy.json b/src/packs/adversaries/adversary_Bear_71qKDLKO3CsrNkdy.json index a8e610a3..ede81984 100644 --- a/src/packs/adversaries/adversary_Bear_71qKDLKO3CsrNkdy.json +++ b/src/packs/adversaries/adversary_Bear_71qKDLKO3CsrNkdy.json @@ -111,7 +111,8 @@ } ] }, - "img": "icons/creatures/claws/claw-straight-brown.webp" + "img": "icons/creatures/claws/claw-straight-brown.webp", + "type": "attack" } }, "flags": {}, @@ -467,4 +468,4 @@ ], "effects": [], "_key": "!actors!71qKDLKO3CsrNkdy" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Bladed_Guard_B4LZcGuBAHzyVdzy.json b/src/packs/adversaries/adversary_Bladed_Guard_B4LZcGuBAHzyVdzy.json index 0be3490d..640cd10e 100644 --- a/src/packs/adversaries/adversary_Bladed_Guard_B4LZcGuBAHzyVdzy.json +++ b/src/packs/adversaries/adversary_Bladed_Guard_B4LZcGuBAHzyVdzy.json @@ -107,7 +107,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -441,4 +442,4 @@ ], "effects": [], "_key": "!actors!B4LZcGuBAHzyVdzy" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Brawny_Zombie_2UeZ0tEe7AzgSJNd.json b/src/packs/adversaries/adversary_Brawny_Zombie_2UeZ0tEe7AzgSJNd.json index fe7e037e..d76c1b93 100644 --- a/src/packs/adversaries/adversary_Brawny_Zombie_2UeZ0tEe7AzgSJNd.json +++ b/src/packs/adversaries/adversary_Brawny_Zombie_2UeZ0tEe7AzgSJNd.json @@ -112,7 +112,8 @@ } ] }, - "img": "icons/skills/melee/unarmed-punch-fist-yellow-red.webp" + "img": "icons/skills/melee/unarmed-punch-fist-yellow-red.webp", + "type": "attack" } }, "flags": {}, @@ -526,4 +527,4 @@ ], "effects": [], "_key": "!actors!2UeZ0tEe7AzgSJNd" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Cave_Ogre_8Zkqk1jU09nKL2fy.json b/src/packs/adversaries/adversary_Cave_Ogre_8Zkqk1jU09nKL2fy.json index 18f1b756..fefc7847 100644 --- a/src/packs/adversaries/adversary_Cave_Ogre_8Zkqk1jU09nKL2fy.json +++ b/src/packs/adversaries/adversary_Cave_Ogre_8Zkqk1jU09nKL2fy.json @@ -108,7 +108,8 @@ ] }, "name": "Club", - "img": "icons/weapons/clubs/club-banded-barbed-black.webp" + "img": "icons/weapons/clubs/club-banded-barbed-black.webp", + "type": "attack" } }, "flags": {}, @@ -598,4 +599,4 @@ ], "effects": [], "_key": "!actors!8Zkqk1jU09nKL2fy" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Chaos_Skull_jDmHqGvzg5wjgmxE.json b/src/packs/adversaries/adversary_Chaos_Skull_jDmHqGvzg5wjgmxE.json index 0e8a87be..e43a2164 100644 --- a/src/packs/adversaries/adversary_Chaos_Skull_jDmHqGvzg5wjgmxE.json +++ b/src/packs/adversaries/adversary_Chaos_Skull_jDmHqGvzg5wjgmxE.json @@ -103,7 +103,8 @@ } ] }, - "img": "icons/magic/light/beam-rays-magenta.webp" + "img": "icons/magic/light/beam-rays-magenta.webp", + "type": "attack" } }, "flags": {}, @@ -561,4 +562,4 @@ ], "effects": [], "_key": "!actors!jDmHqGvzg5wjgmxE" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Conscript_99TqczuQipBmaB8i.json b/src/packs/adversaries/adversary_Conscript_99TqczuQipBmaB8i.json index 88ca68b5..5f3dcf2b 100644 --- a/src/packs/adversaries/adversary_Conscript_99TqczuQipBmaB8i.json +++ b/src/packs/adversaries/adversary_Conscript_99TqczuQipBmaB8i.json @@ -96,7 +96,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -313,4 +314,4 @@ ], "effects": [], "_key": "!actors!99TqczuQipBmaB8i" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Construct_uOP5oT9QzXPlnf3p.json b/src/packs/adversaries/adversary_Construct_uOP5oT9QzXPlnf3p.json index a1440de4..39cd2ea8 100644 --- a/src/packs/adversaries/adversary_Construct_uOP5oT9QzXPlnf3p.json +++ b/src/packs/adversaries/adversary_Construct_uOP5oT9QzXPlnf3p.json @@ -102,7 +102,8 @@ ] }, "name": "Fist Slam", - "img": "icons/skills/melee/unarmed-punch-fist-yellow-red.webp" + "img": "icons/skills/melee/unarmed-punch-fist-yellow-red.webp", + "type": "attack" } }, "flags": {}, @@ -631,4 +632,4 @@ ], "effects": [], "_key": "!actors!uOP5oT9QzXPlnf3p" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Courtesan_ZxWaWPdzFIUPNC62.json b/src/packs/adversaries/adversary_Courtesan_ZxWaWPdzFIUPNC62.json index a0d5bb64..e10ad1a4 100644 --- a/src/packs/adversaries/adversary_Courtesan_ZxWaWPdzFIUPNC62.json +++ b/src/packs/adversaries/adversary_Courtesan_ZxWaWPdzFIUPNC62.json @@ -113,7 +113,8 @@ } ] }, - "img": "icons/weapons/daggers/dagger-straight-cracked.webp" + "img": "icons/weapons/daggers/dagger-straight-cracked.webp", + "type": "attack" } }, "flags": {}, @@ -379,4 +380,4 @@ ], "effects": [], "_key": "!actors!ZxWaWPdzFIUPNC62" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Courtier_CBBuEXAlLKFMJdjg.json b/src/packs/adversaries/adversary_Courtier_CBBuEXAlLKFMJdjg.json index df0103bf..e2c80bec 100644 --- a/src/packs/adversaries/adversary_Courtier_CBBuEXAlLKFMJdjg.json +++ b/src/packs/adversaries/adversary_Courtier_CBBuEXAlLKFMJdjg.json @@ -107,7 +107,8 @@ } ] }, - "img": "icons/weapons/daggers/dagger-twin-green.webp" + "img": "icons/weapons/daggers/dagger-twin-green.webp", + "type": "attack" } }, "flags": {}, @@ -459,4 +460,4 @@ ], "effects": [], "_key": "!actors!CBBuEXAlLKFMJdjg" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Cult_Adept_0NxCSugvKQ4W8OYZ.json b/src/packs/adversaries/adversary_Cult_Adept_0NxCSugvKQ4W8OYZ.json index ed58519c..7ad6e172 100644 --- a/src/packs/adversaries/adversary_Cult_Adept_0NxCSugvKQ4W8OYZ.json +++ b/src/packs/adversaries/adversary_Cult_Adept_0NxCSugvKQ4W8OYZ.json @@ -114,7 +114,8 @@ ] }, "range": "far", - "img": "icons/weapons/staves/staff-ornate-purple.webp" + "img": "icons/weapons/staves/staff-ornate-purple.webp", + "type": "attack" } }, "flags": {}, @@ -723,4 +724,4 @@ ], "effects": [], "_key": "!actors!0NxCSugvKQ4W8OYZ" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Cult_Fang_tyBOpLfigAhI9bU3.json b/src/packs/adversaries/adversary_Cult_Fang_tyBOpLfigAhI9bU3.json index 8632f74f..c012548c 100644 --- a/src/packs/adversaries/adversary_Cult_Fang_tyBOpLfigAhI9bU3.json +++ b/src/packs/adversaries/adversary_Cult_Fang_tyBOpLfigAhI9bU3.json @@ -103,7 +103,8 @@ } ] }, - "range": "" + "range": "", + "type": "attack" } }, "flags": {}, @@ -453,4 +454,4 @@ ], "effects": [], "_key": "!actors!tyBOpLfigAhI9bU3" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Cult_Initiate_zx99sOGTXicP4SSD.json b/src/packs/adversaries/adversary_Cult_Initiate_zx99sOGTXicP4SSD.json index 26c9f4bf..438c51e2 100644 --- a/src/packs/adversaries/adversary_Cult_Initiate_zx99sOGTXicP4SSD.json +++ b/src/packs/adversaries/adversary_Cult_Initiate_zx99sOGTXicP4SSD.json @@ -95,7 +95,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -312,4 +313,4 @@ ], "effects": [], "_key": "!actors!zx99sOGTXicP4SSD" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Deeproot_Defender_9x2xY9zwc3xzbXo5.json b/src/packs/adversaries/adversary_Deeproot_Defender_9x2xY9zwc3xzbXo5.json index 292f5161..3b409300 100644 --- a/src/packs/adversaries/adversary_Deeproot_Defender_9x2xY9zwc3xzbXo5.json +++ b/src/packs/adversaries/adversary_Deeproot_Defender_9x2xY9zwc3xzbXo5.json @@ -108,7 +108,8 @@ } ] }, - "img": "icons/magic/nature/root-vines-grow-brown.webp" + "img": "icons/magic/nature/root-vines-grow-brown.webp", + "type": "attack" } }, "flags": {}, @@ -473,4 +474,4 @@ ], "effects": [], "_key": "!actors!9x2xY9zwc3xzbXo5" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Demon_of_Avarice_pnyjIGxxvurcWmTv.json b/src/packs/adversaries/adversary_Demon_of_Avarice_pnyjIGxxvurcWmTv.json index f4f74da4..ff35a02d 100644 --- a/src/packs/adversaries/adversary_Demon_of_Avarice_pnyjIGxxvurcWmTv.json +++ b/src/packs/adversaries/adversary_Demon_of_Avarice_pnyjIGxxvurcWmTv.json @@ -108,7 +108,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -384,4 +385,4 @@ ], "effects": [], "_key": "!actors!pnyjIGxxvurcWmTv" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Demon_of_Despair_kE4dfhqmIQpNd44e.json b/src/packs/adversaries/adversary_Demon_of_Despair_kE4dfhqmIQpNd44e.json index 970e598b..ee46d67f 100644 --- a/src/packs/adversaries/adversary_Demon_of_Despair_kE4dfhqmIQpNd44e.json +++ b/src/packs/adversaries/adversary_Demon_of_Despair_kE4dfhqmIQpNd44e.json @@ -108,7 +108,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -502,4 +503,4 @@ ], "effects": [], "_key": "!actors!kE4dfhqmIQpNd44e" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Demon_of_Hubris_2VN3BftageoTTIzu.json b/src/packs/adversaries/adversary_Demon_of_Hubris_2VN3BftageoTTIzu.json index cd34ae80..6ed4f966 100644 --- a/src/packs/adversaries/adversary_Demon_of_Hubris_2VN3BftageoTTIzu.json +++ b/src/packs/adversaries/adversary_Demon_of_Hubris_2VN3BftageoTTIzu.json @@ -109,7 +109,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -654,4 +655,4 @@ ], "effects": [], "_key": "!actors!2VN3BftageoTTIzu" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Demon_of_Jealousy_SxSOkM4bcVOFyjbo.json b/src/packs/adversaries/adversary_Demon_of_Jealousy_SxSOkM4bcVOFyjbo.json index 0808e36d..12613133 100644 --- a/src/packs/adversaries/adversary_Demon_of_Jealousy_SxSOkM4bcVOFyjbo.json +++ b/src/packs/adversaries/adversary_Demon_of_Jealousy_SxSOkM4bcVOFyjbo.json @@ -109,7 +109,8 @@ } ] }, - "img": "icons/magic/symbols/rune-sigil-rough-white-teal.webp" + "img": "icons/magic/symbols/rune-sigil-rough-white-teal.webp", + "type": "attack" } }, "flags": {}, @@ -439,4 +440,4 @@ ], "effects": [], "_key": "!actors!SxSOkM4bcVOFyjbo" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Demon_of_Wrath_5lphJAgzoqZI3VoG.json b/src/packs/adversaries/adversary_Demon_of_Wrath_5lphJAgzoqZI3VoG.json index 1ce98d4f..36ebdd09 100644 --- a/src/packs/adversaries/adversary_Demon_of_Wrath_5lphJAgzoqZI3VoG.json +++ b/src/packs/adversaries/adversary_Demon_of_Wrath_5lphJAgzoqZI3VoG.json @@ -109,7 +109,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -474,4 +475,4 @@ ], "effects": [], "_key": "!actors!5lphJAgzoqZI3VoG" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Demonic_Hound_Pack_NoRZ1PqB8N5wcIw0.json b/src/packs/adversaries/adversary_Demonic_Hound_Pack_NoRZ1PqB8N5wcIw0.json index 77d624ab..9c57035f 100644 --- a/src/packs/adversaries/adversary_Demonic_Hound_Pack_NoRZ1PqB8N5wcIw0.json +++ b/src/packs/adversaries/adversary_Demonic_Hound_Pack_NoRZ1PqB8N5wcIw0.json @@ -109,7 +109,8 @@ "bonus": 0, "type": "attack" }, - "range": "" + "range": "", + "type": "attack" } }, "flags": {}, @@ -421,4 +422,4 @@ } ], "_key": "!actors!NoRZ1PqB8N5wcIw0" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Dire_Bat_tBWHW00epmMnkawe.json b/src/packs/adversaries/adversary_Dire_Bat_tBWHW00epmMnkawe.json index 4140d3c1..864e068a 100644 --- a/src/packs/adversaries/adversary_Dire_Bat_tBWHW00epmMnkawe.json +++ b/src/packs/adversaries/adversary_Dire_Bat_tBWHW00epmMnkawe.json @@ -107,7 +107,8 @@ } ] }, - "img": "icons/creatures/claws/claw-hooked-curved.webp" + "img": "icons/creatures/claws/claw-hooked-curved.webp", + "type": "attack" } }, "flags": {}, @@ -512,4 +513,4 @@ ], "effects": [], "_key": "!actors!tBWHW00epmMnkawe" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Dire_Wolf_wNzeuQLfLUMvgHlQ.json b/src/packs/adversaries/adversary_Dire_Wolf_wNzeuQLfLUMvgHlQ.json index 03aa65be..f08e6c7a 100644 --- a/src/packs/adversaries/adversary_Dire_Wolf_wNzeuQLfLUMvgHlQ.json +++ b/src/packs/adversaries/adversary_Dire_Wolf_wNzeuQLfLUMvgHlQ.json @@ -107,7 +107,8 @@ } ] }, - "img": "icons/creatures/claws/claw-straight-brown.webp" + "img": "icons/creatures/claws/claw-straight-brown.webp", + "type": "attack" } }, "flags": {}, @@ -499,4 +500,4 @@ ], "effects": [], "_key": "!actors!wNzeuQLfLUMvgHlQ" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Dryad_wR7cFKrHvRzbzhBT.json b/src/packs/adversaries/adversary_Dryad_wR7cFKrHvRzbzhBT.json index b3443c13..5b06992f 100644 --- a/src/packs/adversaries/adversary_Dryad_wR7cFKrHvRzbzhBT.json +++ b/src/packs/adversaries/adversary_Dryad_wR7cFKrHvRzbzhBT.json @@ -109,7 +109,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -568,4 +569,4 @@ ], "effects": [], "_key": "!actors!wR7cFKrHvRzbzhBT" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Electric_Eels_TLzY1nDw0Bu9Ud40.json b/src/packs/adversaries/adversary_Electric_Eels_TLzY1nDw0Bu9Ud40.json index 7563ea50..97f52a19 100644 --- a/src/packs/adversaries/adversary_Electric_Eels_TLzY1nDw0Bu9Ud40.json +++ b/src/packs/adversaries/adversary_Electric_Eels_TLzY1nDw0Bu9Ud40.json @@ -103,7 +103,8 @@ "bonus": 0, "type": "attack" }, - "range": "" + "range": "", + "type": "attack" } }, "flags": {}, @@ -411,4 +412,4 @@ } ], "_key": "!actors!TLzY1nDw0Bu9Ud40" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Elemental_Spark_P7h54ZePFPHpYwvB.json b/src/packs/adversaries/adversary_Elemental_Spark_P7h54ZePFPHpYwvB.json index 72fc9f3e..fe819d62 100644 --- a/src/packs/adversaries/adversary_Elemental_Spark_P7h54ZePFPHpYwvB.json +++ b/src/packs/adversaries/adversary_Elemental_Spark_P7h54ZePFPHpYwvB.json @@ -96,7 +96,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -313,4 +314,4 @@ ], "effects": [], "_key": "!actors!P7h54ZePFPHpYwvB" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Elite_Soldier_bfhVWMBUh61b9J6n.json b/src/packs/adversaries/adversary_Elite_Soldier_bfhVWMBUh61b9J6n.json index 3159db6f..12f85b54 100644 --- a/src/packs/adversaries/adversary_Elite_Soldier_bfhVWMBUh61b9J6n.json +++ b/src/packs/adversaries/adversary_Elite_Soldier_bfhVWMBUh61b9J6n.json @@ -87,7 +87,7 @@ "img": "icons/weapons/polearms/spear-flared-steel.webp", "_id": "jmrgFi8AUL6LTbtU", "systemPath": "actions", - "type": "", + "type": "attack", "description": "", "chatDisplay": true, "actionType": "action", @@ -435,4 +435,4 @@ "_id": "bfhVWMBUh61b9J6n", "sort": 0, "_key": "!actors!bfhVWMBUh61b9J6n" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Failed_Experiment_ChwwVqowFw8hJQwT.json b/src/packs/adversaries/adversary_Failed_Experiment_ChwwVqowFw8hJQwT.json index 1fefc985..ab005879 100644 --- a/src/packs/adversaries/adversary_Failed_Experiment_ChwwVqowFw8hJQwT.json +++ b/src/packs/adversaries/adversary_Failed_Experiment_ChwwVqowFw8hJQwT.json @@ -109,7 +109,8 @@ ] }, "img": "icons/creatures/claws/claw-hooked-barbed.webp", - "range": "" + "range": "", + "type": "attack" } }, "flags": {}, @@ -411,4 +412,4 @@ ], "effects": [], "_key": "!actors!ChwwVqowFw8hJQwT" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Fallen_Shock_Troop_OsLG2BjaEdTZUJU9.json b/src/packs/adversaries/adversary_Fallen_Shock_Troop_OsLG2BjaEdTZUJU9.json index 1f7e282f..f5c0c384 100644 --- a/src/packs/adversaries/adversary_Fallen_Shock_Troop_OsLG2BjaEdTZUJU9.json +++ b/src/packs/adversaries/adversary_Fallen_Shock_Troop_OsLG2BjaEdTZUJU9.json @@ -96,7 +96,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -399,4 +400,4 @@ ], "effects": [], "_key": "!actors!OsLG2BjaEdTZUJU9" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Fallen_Sorcerer_PELRry1vqjBzSAlr.json b/src/packs/adversaries/adversary_Fallen_Sorcerer_PELRry1vqjBzSAlr.json index e3b3eee5..4aa8dcbc 100644 --- a/src/packs/adversaries/adversary_Fallen_Sorcerer_PELRry1vqjBzSAlr.json +++ b/src/packs/adversaries/adversary_Fallen_Sorcerer_PELRry1vqjBzSAlr.json @@ -109,7 +109,8 @@ } ] }, - "img": "icons/weapons/staves/staff-animal-skull-bull.webp" + "img": "icons/weapons/staves/staff-animal-skull-bull.webp", + "type": "attack" } }, "flags": {}, @@ -646,4 +647,4 @@ ], "effects": [], "_key": "!actors!PELRry1vqjBzSAlr" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Giant_Beastmaster_8VZIgU12cB3cvlyH.json b/src/packs/adversaries/adversary_Giant_Beastmaster_8VZIgU12cB3cvlyH.json index ba9c998b..f5aabf99 100644 --- a/src/packs/adversaries/adversary_Giant_Beastmaster_8VZIgU12cB3cvlyH.json +++ b/src/packs/adversaries/adversary_Giant_Beastmaster_8VZIgU12cB3cvlyH.json @@ -109,7 +109,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -452,4 +453,4 @@ ], "effects": [], "_key": "!actors!8VZIgU12cB3cvlyH" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Giant_Brawler_YnObCleGjPT7yqEc.json b/src/packs/adversaries/adversary_Giant_Brawler_YnObCleGjPT7yqEc.json index 39903650..9aa10ebf 100644 --- a/src/packs/adversaries/adversary_Giant_Brawler_YnObCleGjPT7yqEc.json +++ b/src/packs/adversaries/adversary_Giant_Brawler_YnObCleGjPT7yqEc.json @@ -109,7 +109,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -484,4 +485,4 @@ ], "effects": [], "_key": "!actors!YnObCleGjPT7yqEc" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Giant_Eagle_OMQ0v6PE8s1mSU0K.json b/src/packs/adversaries/adversary_Giant_Eagle_OMQ0v6PE8s1mSU0K.json index e1f3b968..fe48629f 100644 --- a/src/packs/adversaries/adversary_Giant_Eagle_OMQ0v6PE8s1mSU0K.json +++ b/src/packs/adversaries/adversary_Giant_Eagle_OMQ0v6PE8s1mSU0K.json @@ -87,7 +87,7 @@ "img": "icons/creatures/claws/claw-talons-glowing-orange.webp", "_id": "W2KpXQNCg6Nnorbz", "systemPath": "actions", - "type": "", + "type": "attack", "description": "", "chatDisplay": true, "actionType": "action", @@ -745,4 +745,4 @@ "_id": "OMQ0v6PE8s1mSU0K", "sort": 900000, "_key": "!actors!OMQ0v6PE8s1mSU0K" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Giant_Mosquitoes_IIWV4ysJPFPnTP7W.json b/src/packs/adversaries/adversary_Giant_Mosquitoes_IIWV4ysJPFPnTP7W.json index 374aedf9..c48b1b70 100644 --- a/src/packs/adversaries/adversary_Giant_Mosquitoes_IIWV4ysJPFPnTP7W.json +++ b/src/packs/adversaries/adversary_Giant_Mosquitoes_IIWV4ysJPFPnTP7W.json @@ -108,7 +108,8 @@ "bonus": -2, "type": "attack" }, - "range": "" + "range": "", + "type": "attack" } }, "flags": {}, @@ -453,4 +454,4 @@ } ], "_key": "!actors!IIWV4ysJPFPnTP7W" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Giant_Rat_4PfLnaCrOcMdb4dK.json b/src/packs/adversaries/adversary_Giant_Rat_4PfLnaCrOcMdb4dK.json index 10242ab6..a8a31ec7 100644 --- a/src/packs/adversaries/adversary_Giant_Rat_4PfLnaCrOcMdb4dK.json +++ b/src/packs/adversaries/adversary_Giant_Rat_4PfLnaCrOcMdb4dK.json @@ -100,7 +100,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -343,4 +344,4 @@ ], "effects": [], "_key": "!actors!4PfLnaCrOcMdb4dK" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Giant_Recruit_5s8wSvpyC5rxY5aD.json b/src/packs/adversaries/adversary_Giant_Recruit_5s8wSvpyC5rxY5aD.json index d57f9742..e20a3ec2 100644 --- a/src/packs/adversaries/adversary_Giant_Recruit_5s8wSvpyC5rxY5aD.json +++ b/src/packs/adversaries/adversary_Giant_Recruit_5s8wSvpyC5rxY5aD.json @@ -96,7 +96,8 @@ "roll": { "bonus": 1, "type": "attack" - } + }, + "type": "attack" } }, "flags": {}, @@ -313,4 +314,4 @@ ], "effects": [], "_key": "!actors!5s8wSvpyC5rxY5aD" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Giant_Scorpion_fmfntuJ8mHRCAktP.json b/src/packs/adversaries/adversary_Giant_Scorpion_fmfntuJ8mHRCAktP.json index 9cf88069..3a0501e0 100644 --- a/src/packs/adversaries/adversary_Giant_Scorpion_fmfntuJ8mHRCAktP.json +++ b/src/packs/adversaries/adversary_Giant_Scorpion_fmfntuJ8mHRCAktP.json @@ -107,7 +107,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -535,4 +536,4 @@ ], "effects": [], "_key": "!actors!fmfntuJ8mHRCAktP" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Glass_Snake_8KWVLWXFhlY2kYx0.json b/src/packs/adversaries/adversary_Glass_Snake_8KWVLWXFhlY2kYx0.json index e7b03a0f..acd44c34 100644 --- a/src/packs/adversaries/adversary_Glass_Snake_8KWVLWXFhlY2kYx0.json +++ b/src/packs/adversaries/adversary_Glass_Snake_8KWVLWXFhlY2kYx0.json @@ -103,7 +103,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -658,4 +659,4 @@ ], "effects": [], "_key": "!actors!8KWVLWXFhlY2kYx0" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Gorgon_8mJYMpbLTb8qIOrr.json b/src/packs/adversaries/adversary_Gorgon_8mJYMpbLTb8qIOrr.json index 22e68ade..eb169bc2 100644 --- a/src/packs/adversaries/adversary_Gorgon_8mJYMpbLTb8qIOrr.json +++ b/src/packs/adversaries/adversary_Gorgon_8mJYMpbLTb8qIOrr.json @@ -109,7 +109,8 @@ } ] }, - "img": "icons/weapons/bows/shortbow-recurve-yellow.webp" + "img": "icons/weapons/bows/shortbow-recurve-yellow.webp", + "type": "attack" } }, "flags": {}, @@ -621,4 +622,4 @@ ], "effects": [], "_key": "!actors!8mJYMpbLTb8qIOrr" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Greater_Earth_Elemental_dsfB3YhoL5SudvS2.json b/src/packs/adversaries/adversary_Greater_Earth_Elemental_dsfB3YhoL5SudvS2.json index d270ccf8..445264af 100644 --- a/src/packs/adversaries/adversary_Greater_Earth_Elemental_dsfB3YhoL5SudvS2.json +++ b/src/packs/adversaries/adversary_Greater_Earth_Elemental_dsfB3YhoL5SudvS2.json @@ -103,7 +103,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -632,4 +633,4 @@ ], "effects": [], "_key": "!actors!dsfB3YhoL5SudvS2" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Greater_Water_Elemental_xIICT6tEdnA7dKDV.json b/src/packs/adversaries/adversary_Greater_Water_Elemental_xIICT6tEdnA7dKDV.json index 00139c3b..15e0a61d 100644 --- a/src/packs/adversaries/adversary_Greater_Water_Elemental_xIICT6tEdnA7dKDV.json +++ b/src/packs/adversaries/adversary_Greater_Water_Elemental_xIICT6tEdnA7dKDV.json @@ -103,7 +103,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -600,4 +601,4 @@ ], "effects": [], "_key": "!actors!xIICT6tEdnA7dKDV" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Green_Ooze_SHXedd9zZPVfUgUa.json b/src/packs/adversaries/adversary_Green_Ooze_SHXedd9zZPVfUgUa.json index 90fd4089..f41153ee 100644 --- a/src/packs/adversaries/adversary_Green_Ooze_SHXedd9zZPVfUgUa.json +++ b/src/packs/adversaries/adversary_Green_Ooze_SHXedd9zZPVfUgUa.json @@ -107,7 +107,8 @@ } ] }, - "img": "icons/creatures/slimes/slime-movement-dripping-pseudopods-green.webp" + "img": "icons/creatures/slimes/slime-movement-dripping-pseudopods-green.webp", + "type": "attack" } }, "flags": {}, @@ -601,4 +602,4 @@ ], "effects": [], "_key": "!actors!SHXedd9zZPVfUgUa" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Hallowed_Archer_kabueAo6BALApWqp.json b/src/packs/adversaries/adversary_Hallowed_Archer_kabueAo6BALApWqp.json index b28228b1..e1e0fbbd 100644 --- a/src/packs/adversaries/adversary_Hallowed_Archer_kabueAo6BALApWqp.json +++ b/src/packs/adversaries/adversary_Hallowed_Archer_kabueAo6BALApWqp.json @@ -103,7 +103,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -371,4 +372,4 @@ ], "effects": [], "_key": "!actors!kabueAo6BALApWqp" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Hallowed_Soldier_VENwg7xEFcYObjmT.json b/src/packs/adversaries/adversary_Hallowed_Soldier_VENwg7xEFcYObjmT.json index b2afbb74..6b8e339e 100644 --- a/src/packs/adversaries/adversary_Hallowed_Soldier_VENwg7xEFcYObjmT.json +++ b/src/packs/adversaries/adversary_Hallowed_Soldier_VENwg7xEFcYObjmT.json @@ -95,7 +95,8 @@ } ] }, - "img": "icons/skills/melee/sword-shield-stylized-white.webp" + "img": "icons/skills/melee/sword-shield-stylized-white.webp", + "type": "attack" } }, "flags": {}, @@ -376,4 +377,4 @@ ], "effects": [], "_key": "!actors!VENwg7xEFcYObjmT" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Harrier_uRtghKE9mHlII4rs.json b/src/packs/adversaries/adversary_Harrier_uRtghKE9mHlII4rs.json index 5885267b..d7579b0f 100644 --- a/src/packs/adversaries/adversary_Harrier_uRtghKE9mHlII4rs.json +++ b/src/packs/adversaries/adversary_Harrier_uRtghKE9mHlII4rs.json @@ -108,7 +108,8 @@ } ] }, - "img": "icons/weapons/polearms/spear-hooked-rounded.webp" + "img": "icons/weapons/polearms/spear-hooked-rounded.webp", + "type": "attack" } }, "flags": {}, @@ -378,4 +379,4 @@ ], "effects": [], "_key": "!actors!uRtghKE9mHlII4rs" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Head_Guard_mK3A5FTx6k8iPU3F.json b/src/packs/adversaries/adversary_Head_Guard_mK3A5FTx6k8iPU3F.json index 994c66bc..c3ac6946 100644 --- a/src/packs/adversaries/adversary_Head_Guard_mK3A5FTx6k8iPU3F.json +++ b/src/packs/adversaries/adversary_Head_Guard_mK3A5FTx6k8iPU3F.json @@ -111,7 +111,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -388,4 +389,4 @@ ], "effects": [], "_key": "!actors!mK3A5FTx6k8iPU3F" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Head_Vampire_i2UNbRvgyoSs07M6.json b/src/packs/adversaries/adversary_Head_Vampire_i2UNbRvgyoSs07M6.json index a84941f0..7a5e7ce4 100644 --- a/src/packs/adversaries/adversary_Head_Vampire_i2UNbRvgyoSs07M6.json +++ b/src/packs/adversaries/adversary_Head_Vampire_i2UNbRvgyoSs07M6.json @@ -108,7 +108,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -665,4 +666,4 @@ ], "effects": [], "_key": "!actors!i2UNbRvgyoSs07M6" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_High_Seraph_r1mbfSSwKWdcFdAU.json b/src/packs/adversaries/adversary_High_Seraph_r1mbfSSwKWdcFdAU.json index 03c047ba..9130b331 100644 --- a/src/packs/adversaries/adversary_High_Seraph_r1mbfSSwKWdcFdAU.json +++ b/src/packs/adversaries/adversary_High_Seraph_r1mbfSSwKWdcFdAU.json @@ -109,7 +109,8 @@ } ] }, - "img": "icons/skills/melee/strike-blade-hooked-orange-blue.webp" + "img": "icons/skills/melee/strike-blade-hooked-orange-blue.webp", + "type": "attack" } }, "flags": {}, @@ -611,4 +612,4 @@ ], "effects": [], "_key": "!actors!r1mbfSSwKWdcFdAU" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Huge_Green_Ooze_6hbqmxDXFOzZJDk4.json b/src/packs/adversaries/adversary_Huge_Green_Ooze_6hbqmxDXFOzZJDk4.json index 3e0ca4ba..65dd49c1 100644 --- a/src/packs/adversaries/adversary_Huge_Green_Ooze_6hbqmxDXFOzZJDk4.json +++ b/src/packs/adversaries/adversary_Huge_Green_Ooze_6hbqmxDXFOzZJDk4.json @@ -108,7 +108,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -572,4 +573,4 @@ ], "effects": [], "_key": "!actors!6hbqmxDXFOzZJDk4" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Hydra_MI126iMOOobQ1Obn.json b/src/packs/adversaries/adversary_Hydra_MI126iMOOobQ1Obn.json index d5828a9b..40eccf51 100644 --- a/src/packs/adversaries/adversary_Hydra_MI126iMOOobQ1Obn.json +++ b/src/packs/adversaries/adversary_Hydra_MI126iMOOobQ1Obn.json @@ -103,7 +103,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -602,4 +603,4 @@ ], "effects": [], "_key": "!actors!MI126iMOOobQ1Obn" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Jagged_Knife_Bandit_5Lh1T0zaT8Pkr2U2.json b/src/packs/adversaries/adversary_Jagged_Knife_Bandit_5Lh1T0zaT8Pkr2U2.json index e4a2fd1f..5c54c939 100644 --- a/src/packs/adversaries/adversary_Jagged_Knife_Bandit_5Lh1T0zaT8Pkr2U2.json +++ b/src/packs/adversaries/adversary_Jagged_Knife_Bandit_5Lh1T0zaT8Pkr2U2.json @@ -107,7 +107,8 @@ } ] }, - "img": "icons/weapons/daggers/dagger-twin-green.webp" + "img": "icons/weapons/daggers/dagger-twin-green.webp", + "type": "attack" } }, "flags": {}, @@ -349,4 +350,4 @@ ], "effects": [], "_key": "!actors!5Lh1T0zaT8Pkr2U2" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Jagged_Knife_Hexer_MbBPIOxaxXYNApXz.json b/src/packs/adversaries/adversary_Jagged_Knife_Hexer_MbBPIOxaxXYNApXz.json index 909d6c4e..fdfe873d 100644 --- a/src/packs/adversaries/adversary_Jagged_Knife_Hexer_MbBPIOxaxXYNApXz.json +++ b/src/packs/adversaries/adversary_Jagged_Knife_Hexer_MbBPIOxaxXYNApXz.json @@ -108,7 +108,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -443,4 +444,4 @@ ], "effects": [], "_key": "!actors!MbBPIOxaxXYNApXz" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Jagged_Knife_Kneebreaker_CBKixLH3yhivZZuL.json b/src/packs/adversaries/adversary_Jagged_Knife_Kneebreaker_CBKixLH3yhivZZuL.json index aef9e1be..53b5bf30 100644 --- a/src/packs/adversaries/adversary_Jagged_Knife_Kneebreaker_CBKixLH3yhivZZuL.json +++ b/src/packs/adversaries/adversary_Jagged_Knife_Kneebreaker_CBKixLH3yhivZZuL.json @@ -111,7 +111,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -399,4 +400,4 @@ ], "effects": [], "_key": "!actors!CBKixLH3yhivZZuL" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Jagged_Knife_Lackey_C0OMQqV7pN6t7ouR.json b/src/packs/adversaries/adversary_Jagged_Knife_Lackey_C0OMQqV7pN6t7ouR.json index 5b25f674..a1a7b6df 100644 --- a/src/packs/adversaries/adversary_Jagged_Knife_Lackey_C0OMQqV7pN6t7ouR.json +++ b/src/packs/adversaries/adversary_Jagged_Knife_Lackey_C0OMQqV7pN6t7ouR.json @@ -92,7 +92,8 @@ "base": false } ] - } + }, + "type": "attack" }, "resources": { "hitPoints": { @@ -319,4 +320,4 @@ ], "effects": [], "_key": "!actors!C0OMQqV7pN6t7ouR" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Jagged_Knife_Lieutenant_aTljstqteGoLpCBq.json b/src/packs/adversaries/adversary_Jagged_Knife_Lieutenant_aTljstqteGoLpCBq.json index 52c0417c..27ca677f 100644 --- a/src/packs/adversaries/adversary_Jagged_Knife_Lieutenant_aTljstqteGoLpCBq.json +++ b/src/packs/adversaries/adversary_Jagged_Knife_Lieutenant_aTljstqteGoLpCBq.json @@ -108,7 +108,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -502,4 +503,4 @@ ], "effects": [], "_key": "!actors!aTljstqteGoLpCBq" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Jagged_Knife_Shadow_XF4tYTq9nPJAy2ox.json b/src/packs/adversaries/adversary_Jagged_Knife_Shadow_XF4tYTq9nPJAy2ox.json index 4fb8d790..e930fe15 100644 --- a/src/packs/adversaries/adversary_Jagged_Knife_Shadow_XF4tYTq9nPJAy2ox.json +++ b/src/packs/adversaries/adversary_Jagged_Knife_Shadow_XF4tYTq9nPJAy2ox.json @@ -107,7 +107,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -416,4 +417,4 @@ ], "effects": [], "_key": "!actors!XF4tYTq9nPJAy2ox" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Jagged_Knife_Sniper_1zuyof1XuIfi3aMG.json b/src/packs/adversaries/adversary_Jagged_Knife_Sniper_1zuyof1XuIfi3aMG.json index f44feb6c..2fc0e526 100644 --- a/src/packs/adversaries/adversary_Jagged_Knife_Sniper_1zuyof1XuIfi3aMG.json +++ b/src/packs/adversaries/adversary_Jagged_Knife_Sniper_1zuyof1XuIfi3aMG.json @@ -108,7 +108,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -336,4 +337,4 @@ ], "effects": [], "_key": "!actors!1zuyof1XuIfi3aMG" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Juvenile_Flickerfly_MYXmTx2FHcIjdfYZ.json b/src/packs/adversaries/adversary_Juvenile_Flickerfly_MYXmTx2FHcIjdfYZ.json index 06e829cc..163a9dd5 100644 --- a/src/packs/adversaries/adversary_Juvenile_Flickerfly_MYXmTx2FHcIjdfYZ.json +++ b/src/packs/adversaries/adversary_Juvenile_Flickerfly_MYXmTx2FHcIjdfYZ.json @@ -103,7 +103,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -604,4 +605,4 @@ ], "effects": [], "_key": "!actors!MYXmTx2FHcIjdfYZ" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Knight_of_the_Realm_7ai2opemrclQe3VF.json b/src/packs/adversaries/adversary_Knight_of_the_Realm_7ai2opemrclQe3VF.json index cd1d75a1..86dc57ff 100644 --- a/src/packs/adversaries/adversary_Knight_of_the_Realm_7ai2opemrclQe3VF.json +++ b/src/packs/adversaries/adversary_Knight_of_the_Realm_7ai2opemrclQe3VF.json @@ -119,7 +119,8 @@ } ] }, - "range": "" + "range": "", + "type": "attack" } }, "flags": {}, @@ -603,4 +604,4 @@ ], "effects": [], "_key": "!actors!7ai2opemrclQe3VF" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Kraken_4nqv3ZwJGjnmic8j.json b/src/packs/adversaries/adversary_Kraken_4nqv3ZwJGjnmic8j.json index 1300e54a..abb19062 100644 --- a/src/packs/adversaries/adversary_Kraken_4nqv3ZwJGjnmic8j.json +++ b/src/packs/adversaries/adversary_Kraken_4nqv3ZwJGjnmic8j.json @@ -109,7 +109,8 @@ } ] }, - "img": "icons/creatures/tentacles/tentacles-octopus-black-pink.webp" + "img": "icons/creatures/tentacles/tentacles-octopus-black-pink.webp", + "type": "attack" } }, "flags": {}, @@ -568,4 +569,4 @@ ], "effects": [], "_key": "!actors!4nqv3ZwJGjnmic8j" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Masked_Thief_niBpVU7yeo5ccskE.json b/src/packs/adversaries/adversary_Masked_Thief_niBpVU7yeo5ccskE.json index c0a25712..15a3dddd 100644 --- a/src/packs/adversaries/adversary_Masked_Thief_niBpVU7yeo5ccskE.json +++ b/src/packs/adversaries/adversary_Masked_Thief_niBpVU7yeo5ccskE.json @@ -109,7 +109,8 @@ } ] }, - "range": "" + "range": "", + "type": "attack" } }, "flags": {}, @@ -467,4 +468,4 @@ ], "effects": [], "_key": "!actors!niBpVU7yeo5ccskE" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Master_Assassin_dNta0cUzr96xcFhf.json b/src/packs/adversaries/adversary_Master_Assassin_dNta0cUzr96xcFhf.json index c8143250..4d10ed98 100644 --- a/src/packs/adversaries/adversary_Master_Assassin_dNta0cUzr96xcFhf.json +++ b/src/packs/adversaries/adversary_Master_Assassin_dNta0cUzr96xcFhf.json @@ -114,7 +114,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -428,4 +429,4 @@ ], "effects": [], "_key": "!actors!dNta0cUzr96xcFhf" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Merchant_Al3w2CgjfdT3p9ma.json b/src/packs/adversaries/adversary_Merchant_Al3w2CgjfdT3p9ma.json index fb0a7970..dd31d320 100644 --- a/src/packs/adversaries/adversary_Merchant_Al3w2CgjfdT3p9ma.json +++ b/src/packs/adversaries/adversary_Merchant_Al3w2CgjfdT3p9ma.json @@ -107,7 +107,8 @@ } ] }, - "img": "icons/weapons/clubs/club-baton-blue.webp" + "img": "icons/weapons/clubs/club-baton-blue.webp", + "type": "attack" } }, "flags": {}, @@ -348,4 +349,4 @@ ], "effects": [], "_key": "!actors!Al3w2CgjfdT3p9ma" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Merchant_Baron_Vy02IhGhkJLuezu4.json b/src/packs/adversaries/adversary_Merchant_Baron_Vy02IhGhkJLuezu4.json index f706a7ac..7a43e614 100644 --- a/src/packs/adversaries/adversary_Merchant_Baron_Vy02IhGhkJLuezu4.json +++ b/src/packs/adversaries/adversary_Merchant_Baron_Vy02IhGhkJLuezu4.json @@ -113,7 +113,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -403,4 +404,4 @@ ], "effects": [], "_key": "!actors!Vy02IhGhkJLuezu4" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Minor_Chaos_Elemental_sRn4bqerfARvhgSV.json b/src/packs/adversaries/adversary_Minor_Chaos_Elemental_sRn4bqerfARvhgSV.json index 075e47dc..0afdddcc 100644 --- a/src/packs/adversaries/adversary_Minor_Chaos_Elemental_sRn4bqerfARvhgSV.json +++ b/src/packs/adversaries/adversary_Minor_Chaos_Elemental_sRn4bqerfARvhgSV.json @@ -103,7 +103,8 @@ } ] }, - "range": "close" + "range": "close", + "type": "attack" } }, "flags": {}, @@ -620,4 +621,4 @@ ], "effects": [], "_key": "!actors!sRn4bqerfARvhgSV" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Minor_Demon_3tqCjDwJAQ7JKqMb.json b/src/packs/adversaries/adversary_Minor_Demon_3tqCjDwJAQ7JKqMb.json index 8e274cfe..66037843 100644 --- a/src/packs/adversaries/adversary_Minor_Demon_3tqCjDwJAQ7JKqMb.json +++ b/src/packs/adversaries/adversary_Minor_Demon_3tqCjDwJAQ7JKqMb.json @@ -102,7 +102,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -558,4 +559,4 @@ ], "effects": [], "_key": "!actors!3tqCjDwJAQ7JKqMb" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Minor_Fire_Elemental_DscWkNVoHak6P4hh.json b/src/packs/adversaries/adversary_Minor_Fire_Elemental_DscWkNVoHak6P4hh.json index 2a2b5c92..87f7f688 100644 --- a/src/packs/adversaries/adversary_Minor_Fire_Elemental_DscWkNVoHak6P4hh.json +++ b/src/packs/adversaries/adversary_Minor_Fire_Elemental_DscWkNVoHak6P4hh.json @@ -103,7 +103,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -697,4 +698,4 @@ ], "effects": [], "_key": "!actors!DscWkNVoHak6P4hh" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Minor_Treant_G62k4oSkhkoXEs2D.json b/src/packs/adversaries/adversary_Minor_Treant_G62k4oSkhkoXEs2D.json index 81d7e0e6..70568858 100644 --- a/src/packs/adversaries/adversary_Minor_Treant_G62k4oSkhkoXEs2D.json +++ b/src/packs/adversaries/adversary_Minor_Treant_G62k4oSkhkoXEs2D.json @@ -95,7 +95,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -314,4 +315,4 @@ ], "effects": [], "_key": "!actors!G62k4oSkhkoXEs2D" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Minotaur_Wrecker_rM9qCIYeWg9I0B4l.json b/src/packs/adversaries/adversary_Minotaur_Wrecker_rM9qCIYeWg9I0B4l.json index d0d0dda6..acbbf2cc 100644 --- a/src/packs/adversaries/adversary_Minotaur_Wrecker_rM9qCIYeWg9I0B4l.json +++ b/src/packs/adversaries/adversary_Minotaur_Wrecker_rM9qCIYeWg9I0B4l.json @@ -103,7 +103,8 @@ } ] }, - "img": "icons/weapons/axes/axe-double.webp" + "img": "icons/weapons/axes/axe-double.webp", + "type": "attack" } }, "flags": {}, @@ -561,4 +562,4 @@ ], "effects": [], "_key": "!actors!rM9qCIYeWg9I0B4l" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Monarch_yx0vK2yfNVZKWUUi.json b/src/packs/adversaries/adversary_Monarch_yx0vK2yfNVZKWUUi.json index 9e689a3e..77313b30 100644 --- a/src/packs/adversaries/adversary_Monarch_yx0vK2yfNVZKWUUi.json +++ b/src/packs/adversaries/adversary_Monarch_yx0vK2yfNVZKWUUi.json @@ -113,7 +113,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -394,4 +395,4 @@ ], "effects": [], "_key": "!actors!yx0vK2yfNVZKWUUi" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Mortal_Hunter_mVV7a7KQAORoPMgZ.json b/src/packs/adversaries/adversary_Mortal_Hunter_mVV7a7KQAORoPMgZ.json index 1e94b256..4c870b3e 100644 --- a/src/packs/adversaries/adversary_Mortal_Hunter_mVV7a7KQAORoPMgZ.json +++ b/src/packs/adversaries/adversary_Mortal_Hunter_mVV7a7KQAORoPMgZ.json @@ -109,7 +109,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -646,4 +647,4 @@ ], "effects": [], "_key": "!actors!mVV7a7KQAORoPMgZ" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Oak_Treant_XK78QUfY8c8Go8Uv.json b/src/packs/adversaries/adversary_Oak_Treant_XK78QUfY8c8Go8Uv.json index 3bfaa715..f8792192 100644 --- a/src/packs/adversaries/adversary_Oak_Treant_XK78QUfY8c8Go8Uv.json +++ b/src/packs/adversaries/adversary_Oak_Treant_XK78QUfY8c8Go8Uv.json @@ -64,7 +64,45 @@ } }, "tier": 3, - "description": "

A sturdy animate old-growth tree.

" + "description": "

A sturdy animate old-growth tree.

", + "attack": { + "name": "Attack", + "roll": { + "type": "attack", + "bonus": 2 + }, + "range": "close", + "damage": { + "parts": [ + { + "value": { + "multiplier": "flat", + "flatMultiplier": 3, + "dice": "d8", + "bonus": 2, + "custom": { + "enabled": false + } + }, + "type": ["physical"], + "applyTo": "hitPoints", + "resultBased": false, + "valueAlt": { + "multiplier": "prof", + "flatMultiplier": 1, + "dice": "d6", + "bonus": null, + "custom": { + "enabled": false + } + }, + "base": false + } + ] + }, + "img": "icons/skills/melee/blood-slash-foam-red.webp", + "type": "attack" + } }, "flags": {}, "_stats": { diff --git a/src/packs/adversaries/adversary_Oracle_of_Doom_befIqd5IYKg6eUz2.json b/src/packs/adversaries/adversary_Oracle_of_Doom_befIqd5IYKg6eUz2.json index c4b13076..7ef34fea 100644 --- a/src/packs/adversaries/adversary_Oracle_of_Doom_befIqd5IYKg6eUz2.json +++ b/src/packs/adversaries/adversary_Oracle_of_Doom_befIqd5IYKg6eUz2.json @@ -109,7 +109,8 @@ } ] }, - "img": "icons/magic/symbols/rune-sigil-rough-white-teal.webp" + "img": "icons/magic/symbols/rune-sigil-rough-white-teal.webp", + "type": "attack" } }, "flags": {}, @@ -732,4 +733,4 @@ ], "effects": [], "_key": "!actors!befIqd5IYKg6eUz2" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Outer_Realms_Abomination_A0SeeDzwjvqOsyof.json b/src/packs/adversaries/adversary_Outer_Realms_Abomination_A0SeeDzwjvqOsyof.json index 7f8a96f8..ac3c352c 100644 --- a/src/packs/adversaries/adversary_Outer_Realms_Abomination_A0SeeDzwjvqOsyof.json +++ b/src/packs/adversaries/adversary_Outer_Realms_Abomination_A0SeeDzwjvqOsyof.json @@ -103,7 +103,8 @@ } ] }, - "img": "icons/creatures/tentacles/tentacle-earth-green.webp" + "img": "icons/creatures/tentacles/tentacle-earth-green.webp", + "type": "attack" } }, "flags": {}, @@ -603,4 +604,4 @@ ], "effects": [], "_key": "!actors!A0SeeDzwjvqOsyof" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Outer_Realms_Corrupter_ms6nuOl3NFkhPj1k.json b/src/packs/adversaries/adversary_Outer_Realms_Corrupter_ms6nuOl3NFkhPj1k.json index ba42b6d6..1933cb63 100644 --- a/src/packs/adversaries/adversary_Outer_Realms_Corrupter_ms6nuOl3NFkhPj1k.json +++ b/src/packs/adversaries/adversary_Outer_Realms_Corrupter_ms6nuOl3NFkhPj1k.json @@ -103,7 +103,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -415,4 +416,4 @@ ], "effects": [], "_key": "!actors!ms6nuOl3NFkhPj1k" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Outer_Realms_Thrall_moJhHgKqTKPS2WYS.json b/src/packs/adversaries/adversary_Outer_Realms_Thrall_moJhHgKqTKPS2WYS.json index 377762d0..9c8c546f 100644 --- a/src/packs/adversaries/adversary_Outer_Realms_Thrall_moJhHgKqTKPS2WYS.json +++ b/src/packs/adversaries/adversary_Outer_Realms_Thrall_moJhHgKqTKPS2WYS.json @@ -95,7 +95,8 @@ "bonus": 3, "type": "attack" }, - "img": "icons/creatures/claws/claw-talons-yellow-red.webp" + "img": "icons/creatures/claws/claw-talons-yellow-red.webp", + "type": "attack" } }, "flags": {}, @@ -312,4 +313,4 @@ ], "effects": [], "_key": "!actors!moJhHgKqTKPS2WYS" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Patchwork_Zombie_Hulk_EQTOAOUrkIvS2z88.json b/src/packs/adversaries/adversary_Patchwork_Zombie_Hulk_EQTOAOUrkIvS2z88.json index 4b8c4cdb..7a7414e2 100644 --- a/src/packs/adversaries/adversary_Patchwork_Zombie_Hulk_EQTOAOUrkIvS2z88.json +++ b/src/packs/adversaries/adversary_Patchwork_Zombie_Hulk_EQTOAOUrkIvS2z88.json @@ -112,7 +112,8 @@ } ] }, - "img": "icons/commodities/biological/hand-clawed-blue.webp" + "img": "icons/commodities/biological/hand-clawed-blue.webp", + "type": "attack" } }, "flags": {}, @@ -542,4 +543,4 @@ ], "effects": [], "_key": "!actors!EQTOAOUrkIvS2z88" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Perfected_Zombie_CP6iRfHdyFWniTHY.json b/src/packs/adversaries/adversary_Perfected_Zombie_CP6iRfHdyFWniTHY.json index 59db736c..7766be2a 100644 --- a/src/packs/adversaries/adversary_Perfected_Zombie_CP6iRfHdyFWniTHY.json +++ b/src/packs/adversaries/adversary_Perfected_Zombie_CP6iRfHdyFWniTHY.json @@ -103,7 +103,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -565,4 +566,4 @@ ], "effects": [], "_key": "!actors!CP6iRfHdyFWniTHY" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Petty_Noble_wycLpvebWdUqRhpP.json b/src/packs/adversaries/adversary_Petty_Noble_wycLpvebWdUqRhpP.json index d5af9c61..1ae9d31b 100644 --- a/src/packs/adversaries/adversary_Petty_Noble_wycLpvebWdUqRhpP.json +++ b/src/packs/adversaries/adversary_Petty_Noble_wycLpvebWdUqRhpP.json @@ -107,7 +107,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -415,4 +416,4 @@ ], "effects": [], "_key": "!actors!wycLpvebWdUqRhpP" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Pirate_Captain_OROJbjsqagVh7ECV.json b/src/packs/adversaries/adversary_Pirate_Captain_OROJbjsqagVh7ECV.json index 004f8719..d6bcbfed 100644 --- a/src/packs/adversaries/adversary_Pirate_Captain_OROJbjsqagVh7ECV.json +++ b/src/packs/adversaries/adversary_Pirate_Captain_OROJbjsqagVh7ECV.json @@ -111,7 +111,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -531,4 +532,4 @@ ], "effects": [], "_key": "!actors!OROJbjsqagVh7ECV" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Pirate_Raiders_5YgEajn0wa4i85kC.json b/src/packs/adversaries/adversary_Pirate_Raiders_5YgEajn0wa4i85kC.json index b180927e..cd3d011c 100644 --- a/src/packs/adversaries/adversary_Pirate_Raiders_5YgEajn0wa4i85kC.json +++ b/src/packs/adversaries/adversary_Pirate_Raiders_5YgEajn0wa4i85kC.json @@ -107,7 +107,8 @@ "roll": { "bonus": 1, "type": "attack" - } + }, + "type": "attack" } }, "flags": {}, @@ -388,4 +389,4 @@ } ], "_key": "!actors!5YgEajn0wa4i85kC" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Red_Ooze_9rVlbJVrDNn1x7PS.json b/src/packs/adversaries/adversary_Red_Ooze_9rVlbJVrDNn1x7PS.json index 6dfa21a1..d2293628 100644 --- a/src/packs/adversaries/adversary_Red_Ooze_9rVlbJVrDNn1x7PS.json +++ b/src/packs/adversaries/adversary_Red_Ooze_9rVlbJVrDNn1x7PS.json @@ -107,7 +107,8 @@ } ] }, - "img": "icons/creatures/slimes/slime-movement-dripping-pseudopods-green.webp" + "img": "icons/creatures/slimes/slime-movement-dripping-pseudopods-green.webp", + "type": "attack" } }, "flags": {}, @@ -537,4 +538,4 @@ ], "effects": [], "_key": "!actors!9rVlbJVrDNn1x7PS" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Rotted_Zombie_gP3fWTLzSFnpA8EJ.json b/src/packs/adversaries/adversary_Rotted_Zombie_gP3fWTLzSFnpA8EJ.json index 35ac498e..5edc82f1 100644 --- a/src/packs/adversaries/adversary_Rotted_Zombie_gP3fWTLzSFnpA8EJ.json +++ b/src/packs/adversaries/adversary_Rotted_Zombie_gP3fWTLzSFnpA8EJ.json @@ -87,7 +87,8 @@ } ] }, - "img": "icons/creatures/abilities/mouth-teeth-sharp.webp" + "img": "icons/creatures/abilities/mouth-teeth-sharp.webp", + "type": "attack" }, "resources": { "hitPoints": { @@ -314,4 +315,4 @@ ], "effects": [], "_key": "!actors!gP3fWTLzSFnpA8EJ" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Royal_Advisor_EtLJiTsilPPZvLUX.json b/src/packs/adversaries/adversary_Royal_Advisor_EtLJiTsilPPZvLUX.json index e66a7ba0..1f9e2689 100644 --- a/src/packs/adversaries/adversary_Royal_Advisor_EtLJiTsilPPZvLUX.json +++ b/src/packs/adversaries/adversary_Royal_Advisor_EtLJiTsilPPZvLUX.json @@ -114,7 +114,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -448,4 +449,4 @@ ], "effects": [], "_key": "!actors!EtLJiTsilPPZvLUX" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Secret_Keeper_sLAccjvCWfeedbpI.json b/src/packs/adversaries/adversary_Secret_Keeper_sLAccjvCWfeedbpI.json index fa8d7f07..5cc726e7 100644 --- a/src/packs/adversaries/adversary_Secret_Keeper_sLAccjvCWfeedbpI.json +++ b/src/packs/adversaries/adversary_Secret_Keeper_sLAccjvCWfeedbpI.json @@ -114,7 +114,8 @@ } ] }, - "img": "icons/weapons/staves/staff-ornate-purple.webp" + "img": "icons/weapons/staves/staff-ornate-purple.webp", + "type": "attack" } }, "flags": {}, @@ -573,4 +574,4 @@ ], "effects": [], "_key": "!actors!sLAccjvCWfeedbpI" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Sellsword_bgreCaQ6ap2DVpCr.json b/src/packs/adversaries/adversary_Sellsword_bgreCaQ6ap2DVpCr.json index 4a569ea1..bd7fe87a 100644 --- a/src/packs/adversaries/adversary_Sellsword_bgreCaQ6ap2DVpCr.json +++ b/src/packs/adversaries/adversary_Sellsword_bgreCaQ6ap2DVpCr.json @@ -95,7 +95,8 @@ "bonus": 3, "type": "attack" }, - "img": "icons/weapons/swords/sword-guard.webp" + "img": "icons/weapons/swords/sword-guard.webp", + "type": "attack" } }, "flags": {}, @@ -314,4 +315,4 @@ ], "effects": [], "_key": "!actors!bgreCaQ6ap2DVpCr" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Shambling_Zombie_2nXz4ilAY4xuhKLm.json b/src/packs/adversaries/adversary_Shambling_Zombie_2nXz4ilAY4xuhKLm.json index bbc59263..efd04dce 100644 --- a/src/packs/adversaries/adversary_Shambling_Zombie_2nXz4ilAY4xuhKLm.json +++ b/src/packs/adversaries/adversary_Shambling_Zombie_2nXz4ilAY4xuhKLm.json @@ -102,7 +102,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -343,4 +344,4 @@ ], "effects": [], "_key": "!actors!2nXz4ilAY4xuhKLm" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Shark_YmVAkdNsyuXWTtYp.json b/src/packs/adversaries/adversary_Shark_YmVAkdNsyuXWTtYp.json index 7c81805d..bc97ce5a 100644 --- a/src/packs/adversaries/adversary_Shark_YmVAkdNsyuXWTtYp.json +++ b/src/packs/adversaries/adversary_Shark_YmVAkdNsyuXWTtYp.json @@ -103,7 +103,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -505,4 +506,4 @@ ], "effects": [], "_key": "!actors!YmVAkdNsyuXWTtYp" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Siren_BK4jwyXSRx7IOQiO.json b/src/packs/adversaries/adversary_Siren_BK4jwyXSRx7IOQiO.json index 29faf229..4f855702 100644 --- a/src/packs/adversaries/adversary_Siren_BK4jwyXSRx7IOQiO.json +++ b/src/packs/adversaries/adversary_Siren_BK4jwyXSRx7IOQiO.json @@ -109,7 +109,8 @@ ] }, "img": "icons/creatures/abilities/mouth-teeth-sharp.webp", - "range": "" + "range": "", + "type": "attack" } }, "flags": {}, @@ -476,4 +477,4 @@ ], "effects": [], "_key": "!actors!BK4jwyXSRx7IOQiO" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Skeleton_Archer_7X5q7a6ueeHs5oA9.json b/src/packs/adversaries/adversary_Skeleton_Archer_7X5q7a6ueeHs5oA9.json index cdc360ff..fadf4d46 100644 --- a/src/packs/adversaries/adversary_Skeleton_Archer_7X5q7a6ueeHs5oA9.json +++ b/src/packs/adversaries/adversary_Skeleton_Archer_7X5q7a6ueeHs5oA9.json @@ -103,7 +103,8 @@ } ] }, - "img": "icons/weapons/bows/shortbow-leather.webp" + "img": "icons/weapons/bows/shortbow-leather.webp", + "type": "attack" } }, "flags": {}, @@ -365,4 +366,4 @@ ], "effects": [], "_key": "!actors!7X5q7a6ueeHs5oA9" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Skeleton_Dredge_6l1a3Fazq8BoKIcc.json b/src/packs/adversaries/adversary_Skeleton_Dredge_6l1a3Fazq8BoKIcc.json index f9aa30a1..35be0c10 100644 --- a/src/packs/adversaries/adversary_Skeleton_Dredge_6l1a3Fazq8BoKIcc.json +++ b/src/packs/adversaries/adversary_Skeleton_Dredge_6l1a3Fazq8BoKIcc.json @@ -87,7 +87,8 @@ "base": false } ] - } + }, + "type": "attack" }, "resources": { "hitPoints": { @@ -314,4 +315,4 @@ ], "effects": [], "_key": "!actors!6l1a3Fazq8BoKIcc" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Skeleton_Knight_Q9LaVTyXF9NF12C7.json b/src/packs/adversaries/adversary_Skeleton_Knight_Q9LaVTyXF9NF12C7.json index f564be39..b9a259ea 100644 --- a/src/packs/adversaries/adversary_Skeleton_Knight_Q9LaVTyXF9NF12C7.json +++ b/src/packs/adversaries/adversary_Skeleton_Knight_Q9LaVTyXF9NF12C7.json @@ -102,7 +102,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -558,4 +559,4 @@ ], "effects": [], "_key": "!actors!Q9LaVTyXF9NF12C7" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Skeleton_Warrior_10YIQl0lvCJXZLfX.json b/src/packs/adversaries/adversary_Skeleton_Warrior_10YIQl0lvCJXZLfX.json index 64fcf7e3..885cd007 100644 --- a/src/packs/adversaries/adversary_Skeleton_Warrior_10YIQl0lvCJXZLfX.json +++ b/src/packs/adversaries/adversary_Skeleton_Warrior_10YIQl0lvCJXZLfX.json @@ -102,7 +102,8 @@ } ] }, - "img": "icons/weapons/swords/sword-guard-brass-worn.webp" + "img": "icons/weapons/swords/sword-guard-brass-worn.webp", + "type": "attack" } }, "flags": {}, @@ -389,4 +390,4 @@ ], "effects": [], "_key": "!actors!10YIQl0lvCJXZLfX" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Spectral_Archer_5tCkhnBByUIN5UdG.json b/src/packs/adversaries/adversary_Spectral_Archer_5tCkhnBByUIN5UdG.json index 99cefd01..906fb301 100644 --- a/src/packs/adversaries/adversary_Spectral_Archer_5tCkhnBByUIN5UdG.json +++ b/src/packs/adversaries/adversary_Spectral_Archer_5tCkhnBByUIN5UdG.json @@ -109,7 +109,8 @@ } ] }, - "range": "far" + "range": "far", + "type": "attack" } }, "flags": {}, @@ -452,4 +453,4 @@ ], "effects": [], "_key": "!actors!5tCkhnBByUIN5UdG" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Spectral_Captain_65cSO3EQEh6ZH6Xk.json b/src/packs/adversaries/adversary_Spectral_Captain_65cSO3EQEh6ZH6Xk.json index 318c51a6..2672514d 100644 --- a/src/packs/adversaries/adversary_Spectral_Captain_65cSO3EQEh6ZH6Xk.json +++ b/src/packs/adversaries/adversary_Spectral_Captain_65cSO3EQEh6ZH6Xk.json @@ -109,7 +109,8 @@ "bonus": 3, "type": "attack" }, - "range": "far" + "range": "far", + "type": "attack" } }, "flags": {}, @@ -506,4 +507,4 @@ ], "effects": [], "_key": "!actors!65cSO3EQEh6ZH6Xk" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Spectral_Guardian_UFVGl1osOsJTneLf.json b/src/packs/adversaries/adversary_Spectral_Guardian_UFVGl1osOsJTneLf.json index bcbde73d..8b3ff6a4 100644 --- a/src/packs/adversaries/adversary_Spectral_Guardian_UFVGl1osOsJTneLf.json +++ b/src/packs/adversaries/adversary_Spectral_Guardian_UFVGl1osOsJTneLf.json @@ -109,7 +109,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -485,4 +486,4 @@ ], "effects": [], "_key": "!actors!UFVGl1osOsJTneLf" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Spellblade_ldbWEL7uZs84vyrR.json b/src/packs/adversaries/adversary_Spellblade_ldbWEL7uZs84vyrR.json index 8b3cde3d..be274c9f 100644 --- a/src/packs/adversaries/adversary_Spellblade_ldbWEL7uZs84vyrR.json +++ b/src/packs/adversaries/adversary_Spellblade_ldbWEL7uZs84vyrR.json @@ -108,7 +108,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -477,4 +478,4 @@ ], "effects": [], "_key": "!actors!ldbWEL7uZs84vyrR" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Spy_8zlynOhnVA59KpKT.json b/src/packs/adversaries/adversary_Spy_8zlynOhnVA59KpKT.json index 501cf4d8..127a992a 100644 --- a/src/packs/adversaries/adversary_Spy_8zlynOhnVA59KpKT.json +++ b/src/packs/adversaries/adversary_Spy_8zlynOhnVA59KpKT.json @@ -109,7 +109,8 @@ ] }, "img": "icons/weapons/daggers/dagger-curved-purple.webp", - "range": "" + "range": "", + "type": "attack" } }, "flags": {}, @@ -423,4 +424,4 @@ ], "effects": [], "_key": "!actors!8zlynOhnVA59KpKT" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Stag_Knight_KGVwnLq85ywP9xvB.json b/src/packs/adversaries/adversary_Stag_Knight_KGVwnLq85ywP9xvB.json index 03e9a9f4..40bceb11 100644 --- a/src/packs/adversaries/adversary_Stag_Knight_KGVwnLq85ywP9xvB.json +++ b/src/packs/adversaries/adversary_Stag_Knight_KGVwnLq85ywP9xvB.json @@ -108,7 +108,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -496,4 +497,4 @@ ], "effects": [], "_key": "!actors!KGVwnLq85ywP9xvB" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Stonewraith_3aAS2Qm3R6cgaYfE.json b/src/packs/adversaries/adversary_Stonewraith_3aAS2Qm3R6cgaYfE.json index ed541b43..742d2dcc 100644 --- a/src/packs/adversaries/adversary_Stonewraith_3aAS2Qm3R6cgaYfE.json +++ b/src/packs/adversaries/adversary_Stonewraith_3aAS2Qm3R6cgaYfE.json @@ -109,7 +109,8 @@ } ] }, - "range": "" + "range": "", + "type": "attack" } }, "flags": {}, @@ -575,4 +576,4 @@ ], "effects": [], "_key": "!actors!3aAS2Qm3R6cgaYfE" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Swarm_of_Rats_qNgs3AbLyJrY19nt.json b/src/packs/adversaries/adversary_Swarm_of_Rats_qNgs3AbLyJrY19nt.json index 4f2d0b81..b1f577d2 100644 --- a/src/packs/adversaries/adversary_Swarm_of_Rats_qNgs3AbLyJrY19nt.json +++ b/src/packs/adversaries/adversary_Swarm_of_Rats_qNgs3AbLyJrY19nt.json @@ -102,7 +102,8 @@ "roll": { "bonus": -3, "type": "attack" - } + }, + "type": "attack" } }, "flags": {}, @@ -330,4 +331,4 @@ } ], "_key": "!actors!qNgs3AbLyJrY19nt" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Sylvan_Soldier_VtFBt9XBE0WrGGxP.json b/src/packs/adversaries/adversary_Sylvan_Soldier_VtFBt9XBE0WrGGxP.json index 6c548162..baddaa4d 100644 --- a/src/packs/adversaries/adversary_Sylvan_Soldier_VtFBt9XBE0WrGGxP.json +++ b/src/packs/adversaries/adversary_Sylvan_Soldier_VtFBt9XBE0WrGGxP.json @@ -107,7 +107,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -568,4 +569,4 @@ ], "effects": [], "_key": "!actors!VtFBt9XBE0WrGGxP" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Tiny_Green_Ooze_aLkLFuVoKz2NLoBK.json b/src/packs/adversaries/adversary_Tiny_Green_Ooze_aLkLFuVoKz2NLoBK.json index 861fc6bd..8b0e8254 100644 --- a/src/packs/adversaries/adversary_Tiny_Green_Ooze_aLkLFuVoKz2NLoBK.json +++ b/src/packs/adversaries/adversary_Tiny_Green_Ooze_aLkLFuVoKz2NLoBK.json @@ -86,7 +86,8 @@ } ] }, - "img": "icons/creatures/slimes/slime-movement-dripping-pseudopods-green.webp" + "img": "icons/creatures/slimes/slime-movement-dripping-pseudopods-green.webp", + "type": "attack" }, "damageThresholds": { "major": 4, @@ -305,4 +306,4 @@ ], "effects": [], "_key": "!actors!aLkLFuVoKz2NLoBK" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Tiny_Red_Ooze_1fkLQXVtmILqfJ44.json b/src/packs/adversaries/adversary_Tiny_Red_Ooze_1fkLQXVtmILqfJ44.json index 83399fcd..dc4e18f3 100644 --- a/src/packs/adversaries/adversary_Tiny_Red_Ooze_1fkLQXVtmILqfJ44.json +++ b/src/packs/adversaries/adversary_Tiny_Red_Ooze_1fkLQXVtmILqfJ44.json @@ -86,7 +86,8 @@ "base": false } ] - } + }, + "type": "attack" }, "resources": { "hitPoints": { @@ -306,4 +307,4 @@ ], "effects": [], "_key": "!actors!1fkLQXVtmILqfJ44" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Treant_Sapling_o63nS0k3wHu6EgKP.json b/src/packs/adversaries/adversary_Treant_Sapling_o63nS0k3wHu6EgKP.json index d90df755..d3f3fceb 100644 --- a/src/packs/adversaries/adversary_Treant_Sapling_o63nS0k3wHu6EgKP.json +++ b/src/packs/adversaries/adversary_Treant_Sapling_o63nS0k3wHu6EgKP.json @@ -95,7 +95,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -312,4 +313,4 @@ ], "effects": [], "_key": "!actors!o63nS0k3wHu6EgKP" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Vampire_WWyUp6Mxl1S3KYUG.json b/src/packs/adversaries/adversary_Vampire_WWyUp6Mxl1S3KYUG.json index f6227213..2c300e8a 100644 --- a/src/packs/adversaries/adversary_Vampire_WWyUp6Mxl1S3KYUG.json +++ b/src/packs/adversaries/adversary_Vampire_WWyUp6Mxl1S3KYUG.json @@ -108,7 +108,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -447,4 +448,4 @@ ], "effects": [], "_key": "!actors!WWyUp6Mxl1S3KYUG" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Vault_Guardian_Gaoler_JqYraOqNmmhHk4Yy.json b/src/packs/adversaries/adversary_Vault_Guardian_Gaoler_JqYraOqNmmhHk4Yy.json index 1a2cfa38..74485986 100644 --- a/src/packs/adversaries/adversary_Vault_Guardian_Gaoler_JqYraOqNmmhHk4Yy.json +++ b/src/packs/adversaries/adversary_Vault_Guardian_Gaoler_JqYraOqNmmhHk4Yy.json @@ -103,7 +103,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -396,4 +397,4 @@ ], "effects": [], "_key": "!actors!JqYraOqNmmhHk4Yy" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Vault_Guardian_Sentinel_FVgYb28fhxlVcGwA.json b/src/packs/adversaries/adversary_Vault_Guardian_Sentinel_FVgYb28fhxlVcGwA.json index 743ada61..dbe89528 100644 --- a/src/packs/adversaries/adversary_Vault_Guardian_Sentinel_FVgYb28fhxlVcGwA.json +++ b/src/packs/adversaries/adversary_Vault_Guardian_Sentinel_FVgYb28fhxlVcGwA.json @@ -103,7 +103,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -525,4 +526,4 @@ ], "effects": [], "_key": "!actors!FVgYb28fhxlVcGwA" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Vault_Guardian_Turret_c5hGdvY5UnSjlHws.json b/src/packs/adversaries/adversary_Vault_Guardian_Turret_c5hGdvY5UnSjlHws.json index 63ad9d5c..2e582dfd 100644 --- a/src/packs/adversaries/adversary_Vault_Guardian_Turret_c5hGdvY5UnSjlHws.json +++ b/src/packs/adversaries/adversary_Vault_Guardian_Turret_c5hGdvY5UnSjlHws.json @@ -103,7 +103,8 @@ } ] }, - "img": "icons/commodities/tech/metal-joint.webp" + "img": "icons/commodities/tech/metal-joint.webp", + "type": "attack" } }, "flags": {}, @@ -553,4 +554,4 @@ ], "effects": [], "_key": "!actors!c5hGdvY5UnSjlHws" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_War_Wizard_noDdT0tsN6FXSmC8.json b/src/packs/adversaries/adversary_War_Wizard_noDdT0tsN6FXSmC8.json index 59891951..3f133440 100644 --- a/src/packs/adversaries/adversary_War_Wizard_noDdT0tsN6FXSmC8.json +++ b/src/packs/adversaries/adversary_War_Wizard_noDdT0tsN6FXSmC8.json @@ -114,7 +114,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -684,4 +685,4 @@ ], "effects": [], "_key": "!actors!noDdT0tsN6FXSmC8" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Weaponmaster_ZNbQ2jg35LG4t9eH.json b/src/packs/adversaries/adversary_Weaponmaster_ZNbQ2jg35LG4t9eH.json index af441764..bab53785 100644 --- a/src/packs/adversaries/adversary_Weaponmaster_ZNbQ2jg35LG4t9eH.json +++ b/src/packs/adversaries/adversary_Weaponmaster_ZNbQ2jg35LG4t9eH.json @@ -103,7 +103,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -555,4 +556,4 @@ ], "effects": [], "_key": "!actors!ZNbQ2jg35LG4t9eH" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Young_Dryad_8yUj2Mzvnifhxegm.json b/src/packs/adversaries/adversary_Young_Dryad_8yUj2Mzvnifhxegm.json index c6ff7c34..b3f5b9c6 100644 --- a/src/packs/adversaries/adversary_Young_Dryad_8yUj2Mzvnifhxegm.json +++ b/src/packs/adversaries/adversary_Young_Dryad_8yUj2Mzvnifhxegm.json @@ -107,7 +107,8 @@ "base": false } ] - } + }, + "type": "attack" } }, "flags": {}, @@ -467,4 +468,4 @@ ], "effects": [], "_key": "!actors!8yUj2Mzvnifhxegm" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Young_Ice_Dragon_UGPiPLJsPvMTSKEF.json b/src/packs/adversaries/adversary_Young_Ice_Dragon_UGPiPLJsPvMTSKEF.json index 11289fba..93aa7fc9 100644 --- a/src/packs/adversaries/adversary_Young_Ice_Dragon_UGPiPLJsPvMTSKEF.json +++ b/src/packs/adversaries/adversary_Young_Ice_Dragon_UGPiPLJsPvMTSKEF.json @@ -108,7 +108,8 @@ } ] }, - "img": "icons/creatures/claws/claw-scaled-red.webp" + "img": "icons/creatures/claws/claw-scaled-red.webp", + "type": "attack" } }, "flags": {}, @@ -863,4 +864,4 @@ ], "effects": [], "_key": "!actors!UGPiPLJsPvMTSKEF" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Zombie_Legion_YhJrP7rTBiRdX5Fp.json b/src/packs/adversaries/adversary_Zombie_Legion_YhJrP7rTBiRdX5Fp.json index 1d4fa474..10c3562a 100644 --- a/src/packs/adversaries/adversary_Zombie_Legion_YhJrP7rTBiRdX5Fp.json +++ b/src/packs/adversaries/adversary_Zombie_Legion_YhJrP7rTBiRdX5Fp.json @@ -103,7 +103,8 @@ "type": "attack" }, "range": "close", - "img": "icons/creatures/tentacles/tentacles-octopus-black-pink.webp" + "img": "icons/creatures/tentacles/tentacles-octopus-black-pink.webp", + "type": "attack" } }, "flags": {}, @@ -478,4 +479,4 @@ } ], "_key": "!actors!YhJrP7rTBiRdX5Fp" -} +} \ No newline at end of file diff --git a/src/packs/adversaries/adversary_Zombie_Pack_Nf0v43rtflV56V2T.json b/src/packs/adversaries/adversary_Zombie_Pack_Nf0v43rtflV56V2T.json index 972c4efa..0e469958 100644 --- a/src/packs/adversaries/adversary_Zombie_Pack_Nf0v43rtflV56V2T.json +++ b/src/packs/adversaries/adversary_Zombie_Pack_Nf0v43rtflV56V2T.json @@ -102,7 +102,8 @@ "bonus": -1, "type": "attack" }, - "img": "icons/creatures/abilities/mouth-teeth-sharp.webp" + "img": "icons/creatures/abilities/mouth-teeth-sharp.webp", + "type": "attack" } }, "flags": {}, @@ -361,4 +362,4 @@ } ], "_key": "!actors!Nf0v43rtflV56V2T" -} +} \ No newline at end of file diff --git a/src/packs/journals/journal_Daggerheart_SRD_uNs7ne9VCbbu5dcG.json b/src/packs/journals/journal_Daggerheart_SRD_uNs7ne9VCbbu5dcG.json index 2d655ba5..ece610c0 100644 --- a/src/packs/journals/journal_Daggerheart_SRD_uNs7ne9VCbbu5dcG.json +++ b/src/packs/journals/journal_Daggerheart_SRD_uNs7ne9VCbbu5dcG.json @@ -88,7 +88,7 @@ "image": {}, "text": { "format": 1, - "content": "

DOMAINS

The Daggerheart core set includes 9 Domain Decks, each comprising a collection of cards granting features or special abilities expressing a particular theme.

The 9 Domains are:

ARCANA

Arcana is the domain of innate and instinctual magic. Those who choose this path tap into the raw, enigmatic forces of the realms to manipulate both their own energy and the elements.

Arcana offers wielders a volatile power, but it is incredibly potent when correctly channeled.

The Arcana domain can be accessed by the Druid and Sorcerer classes.

BLADE

Blade is the domain of weapon mastery. Whether by steel, bow, or perhaps a more specialized arm, those who follow this path have the skill to cut short the lives of others. Wielders of Blade dedicate themselves to achieving inexorable power over death.

The Blade domain can be accessed by the Guardian and Warrior classes.

BONE

Bone is the domain of tactics and the body. Practitioners of this domain have an uncanny control over their own physical abilities and an eye for predicting the behaviors of others in combat. Adherents to Bone gain an unparalleled understanding of bodies and their movements.

The Bone domain can be accessed by the Ranger & Warrior classes.

CODEX

Codex is the domain of intensive magical study. Those who seek magical knowledge turn to the equations of power recorded in books, written on scrolls, etched into walls, or tattooed on bodies. Codex offers a commanding and versatile understanding of magic to devotees who pursue knowledge beyond the boundaries of common wisdom.

The Codex domain can be accessed by the Bard and Wizard classes.

GRACE

Grace is the domain of charisma. Through rapturous storytelling, charming spells, or a shroud of lies, those who channel this power define the realities of their adversaries, bending perception to their will. Grace offers its wielders raw magnetism and mastery over language.

The Grace domain can be accessed by the Bard and Rogue classes

MIDNIGHT

Midnight is the domain of shadows and secrecy. Whether by clever tricks, deft magic, or the cloak of night, those who channel these forces practice the art of obscurity and can uncover sequestered treasures. Midnight offers practitioners the power to control and create enigmas.

The Midnight domain can be access by the Rogue and Sorcerer classes.

SAGE

Sage is the domain of the natural world. Those who walk this path tap into the unfettered power of the earth and its creatures to unleash raw magic. Sage grants its adherents the vitality of a blooming flower and the ferocity of a ravenous predator.

The Sage domain can be accessed by the Druid and Ranger classes.

SPLENDOR

Splendor is the domain of life. Through this magic, followers gain the ability to heal and, to an extent, control death. Splendor offers its disciples the magnificent ability to both give and end life.

The Splendor domain can be accessed by the Seraph and Wizard classes.

VALOR

Valor is the domain of protection. Whether through attack or defense, those who choose this discipline channel formidable strength to protect their allies in battle. Valor offers great power to those who raise their shields in defense of others.

The Valor domain can be accessed by the Guardian and Seraph classes.

CLASS DOMAINS

Each class grants access to two domains:

PCs acquire two 1st-level domain cards at character creation and an additional domain card at or below their level each time they level up.

DOMAIN CARDS

Each domain card provides one or more features your PC can utilize during their adventures. Some domain cards provide moves you can make, such as a unique attack or a spell. Others offer passive effects, new downtime or social encounter abilities, or one-time benefits.

DOMAIN CARD ANATOMY

Each domain card includes six elements:

Note: A player can swap domain cards during downtime without paying the domain card’s Recall Cost.

LOADOUT & VAULT

Your loadout is the set of acquired domain cards whose effects your PC can use during play. You can have up to 5 domain cards in your loadout at one time. Once you’ve acquired six or more domain cards, you must choose five to keep in your loadout; the rest are considered to be in your vault. Vault cards are inactive and do not influence play

Note: Your subclass, ancestry, and community cards don’t count toward your loadout or vault and are always active and available.

At the start of a rest, before using downtime moves, you can freely move cards between your loadout and your vault, so long as your loadout doesn’t exceed its five-card maximum.

To move a card from your vault to your loadout at any other time, you must mark a number of Stress equal to the vaulted card’s Recall Cost (located in the top right of the card next to the lightning bolt symbol). If your loadout is already full, you must also move a card from your loadout to your vault to make space, though you can do this at no cost.

When you gain a new domain card at level-up, you can immediately move it into your loadout for free. If your loadout is already full, you must also move a card from your loadout to your vault to make space.

USAGE LIMITS

If a domain card restricts how often it can be used, you can track such limits with whatever method you prefer, such as turning the card sideways, flipping it facedown, or using tokens.

Note: if an effect or ability gives you a number of uses equal to a trait with a modifier of +0 or less, it grants you 0 uses.

CLASSES

A class is a role-based archetype that determines a PC’s:

There are 9 classes in the Daggerheart core materials: Bard, Druid, Guardian, Ranger, Rogue, Seraph, Sorcerer, Warrior, and Wizard.

SUBCLASSES

Each class is divided into two subclasses, each of which further defines and highlights one aspect of its class archetype. Your chosen subclass grants the following:

For some subclasses, the mastery feature unlocks an extraordinary new ability; for others, it’s the impressive culmination of their subclass’s core feature.

The Daggerheart core set includes cards detailing each foundation, specialization, and mastery feature. When you acquire one of these features, take its card for use as a reference during play.

ANCESTRIES

Ancestries represent your character’s lineage, which affects their physical appearance and access to certain special abilities. The following section describes each ancestry in Daggerheart and the characteristics generally shared by members of that ancestry, however, each player decides how much their character aligns with the “standard” or “average” expression of their ancestry.

In Daggerheart, the term “people” is used to refer to all ancestries, as individuals from all lineages possess unique characteristics and cultures, as well as personhood.

Some ancestries are described using the term “humanoid.” This does not imply any genetic relation to “humans,” which is a distinct ancestry within Daggerheart. Instead, it refers to the set of physical characteristics humans will recognize from their own anatomy, such as bipedal movement, upright posture, facial layout, and more. These traits vary by ancestry and individual, though “humanoid” should still provide a useful frame of reference.

The core ruleset includes the following ancestries: Clank, Drakona, Dwarf, Elf, Faerie, Faun, Firbolg, Fungril, Galapa, Giant, Goblin, Halfling, Human, Infernis, Katari, Orc, Ribbet, Simiah, and Mixed Ancestry.

ANCESTRY FEATURES

Each ancestry grants two ancestry features. While some features (such as the ability to fly) are implicitly tied to an ancestry’s anatomy, players determine their characters’ physical form. Work with the GM to re-flavor any implied traits that don’t align with your character concept. If you’d like to make a character who combines more than one ancestry, see “Mixed Ancestry.”

CLANK

Clanks are sentient mechanical beings built from a variety of materials, including metal, wood, and stone. They can resemble humanoids, animals, or even inanimate objects. Like organic beings, their bodies come in a wide array of sizes. Because of their bespoke construction, many clanks have highly specialized physical configurations. Examples include clawed hands for grasping, wheels for movement, or built-in weaponry.

Many clanks embrace body modifications for style as well as function, and members of other ancestries often turn to clank artisans to construct customized mobility aids and physical adornments. Other ancestries can create clanks, even using their own physical characteristics as inspiration, but it’s also common for clanks to build one another. A clank’s lifespan extends as long as they’re able to acquire or craft new parts, making their physical form effectively immortal. That said, their minds are subject to the effects of time, and deteriorate as the magic that powers them loses potency.

DRAKONA

Drakona resemble wingless dragons in humanoid form and possess a powerful elemental breath. All drakona have thick scales that provide excellent natural armor against both attacks and the forces of nature. They are large in size, ranging from 5 feet to 7 feet on average, with long sharp teeth. New teeth grow throughout a Drakona’s approximately 350-year lifespan, so they are never in danger of permanently losing an incisor. Unlike their dragon ancestors, drakona don’t have wings and can’t fly without magical aid. Members of this ancestry pass down the element of their breath through generations, though in rare cases, a drakona’s elemental power will differ from the rest of their family’s.

DWARF

Dwarves are most easily recognized as short humanoids with square frames, dense musculature, and thick hair. Their average height ranges from 4 to 5 ½ feet, and they are often broad in proportion to their stature. Their skin and nails contain a high amount of keratin, making them naturally resilient. This allows dwarves to embed gemstones into their bodies and decorate themselves with tattoos or piercings. Their hair grows thickly—usually on their heads, but some dwarves have thick hair across their bodies as well. Dwarves of all genders can grow facial hair, which they often style in elaborate arrangements. Typically, dwarves live up to 250 years of age, maintaining their muscle mass well into later life.

ELF

Elves are typically tall humanoids with pointed ears and acutely attuned senses. Their ears vary in size and pointed shape, and as they age, the tips begin to droop. While elves come in a wide range of body types, they are all fairly tall, with heights ranging from about 6 to 6 ½ feet. All elves have the ability to drop into a celestial trance, rather than sleep. This allows them to rest effectively in a short amount of time.

Some elves possess what is known as a “mystic form,” which occurs when an elf has dedicated themself to the study or protection of the natural world so deeply that their physical form changes. These characteristics can include celestial freckles, the presence of leaves, vines, or flowers in their hair, eyes that flicker like fire, and more. Sometimes these traits are inherited from parents, but if an elf changes their environment or magical focus, their appearance changes over time. Because elves live for about 350 years, these traits can shift more than once throughout their lifespan.

FAERIE

Faeries are winged humanoid creatures with insectile features. These characteristics cover a broad spectrum from humanoid to insectoid—some possess additional arms, compound eyes, lantern organs, chitinous exoskeletons, or stingers. Because of their close ties to the natural world, they also frequently possess attributes that allow them to blend in with various plants. The average height of a faerie ranges from about 2 feet to 5 feet, but some faeries grow up to 7 feet tall. All faeries possess membranous wings and they each go through a process of metamorphosis. The process and changes differ from faerie to faerie, but during this transformation each individual manifests the unique appearance they will carry throughout the rest of their approximately 50-year lifespan.

FAUN

Fauns resemble humanoid goats with curving horns, square pupils, and cloven hooves. Though their appearances may vary, most fauns have a humanoid torso and a goatlike lower body covered in dense fur. Faun faces can be more caprine or more humanlike, and they have a wide variety of ear and horn shapes. Faun horns range from short with minimal curvature to much larger with a distinct curl. The average faun ranges from 4 feet to 6 ½ feet tall, but their height can change dramatically from one moment to the next based on their stance. The majority of fauns have proportionately long limbs, no matter their size or shape, and are known for their ability to deliver powerful blows with their split hooves. Fauns live for roughly 225 years, and as they age, their appearance can become increasingly goatlike.

FIRBOLG

Firbolgs are bovine humanoids typically recognized by their broad noses and long, drooping ears. Some have faces that are a blend of humanoid and bison, ox, cow, or other bovine creatures. Others, often referred to as minotaurs, have heads that entirely resemble cattle. They are tall and muscular creatures, with heights ranging from around 5 feet to 7 feet, and possess remarkable strength no matter their age. Some firbolgs are known to use this strength to charge their adversaries, an action that is particuarly effective for those who have one of the many varieties of horn styles commonly found in this ancestry. Though their unique characteristics can vary, all firbolgs are covered in fur, which can be muted and earth-toned in color, or come in a variety of pastels, such as soft pinks and blues. On average, firbolgs live for about 150 years.

FUNGRIL

Fungril resemble humanoid mushrooms. They can be either more humanoid or more fungal in appearance, and they come in an assortment of colors, from earth tones to bright reds, yellows, purples, and blues. Fungril display an incredible variety of bodies, faces, and limbs, as there’s no single common shape among them. Even their heights range from a tiny 2 feet tall to a staggering 7 feet tall. While the common lifespan of a fungril is about 300 years, some have been reported to live much longer. They can communicate nonverbally, and many members of this ancestry use a mycelial array to chemically exchange information with other fungril across long distances.

GALAPA

Galapa resemble anthropomorphic turtles with large, domed shells into which they can retract. On average, they range from 4 feet to 6 feet in height, and their head and body shapes can resemble any type of turtle. Galapa come in a variety of earth tones—most often shades of green and brown— and possess unique patterns on their shells. Members of this ancestry can draw their head, arms, and legs into their shell for protection to use it as a natural shield when defensive measures are needed. Some supplement their shell's strength or appearance by attaching armor or carving unique designs, but the process is exceedingly painful. Most galapa move slowly no matter their age, and they can live approximately 150 years.

GIANT

Giants are towering humanoids with broad shoulders, long arms, and one to three eyes. Adult giants range from 6 ½ to 8 ½ feet tall and are naturally muscular, regardless of body type. They are easily recognized by their wide frames and elongated arms and necks. Though they can have up to three eyes, all giants are born with none and remain sightless for their first year of life. Until a giant reaches the age of 10 and their features fully develop, the formation of their eyes may fluctuate. Those with a single eye are commonly known as cyclops. The average giant lifespan is about 75 years.

GOBLIN

Goblins are small humanoids easily recognizable by their large eyes and massive membranous ears. With keen hearing and sharp eyesight, they perceive details both at great distances and in darkness, allowing them to move through less-optimal environments with ease. Their skin and eye colors are incredibly varied, with no one hue, either vibrant or subdued, more dominant than another. A typical goblin stands between 3 feet and 4 feet tall, and each of their ears is about the size of their head. Goblins are known to use ear positions to very specific effect when communicating nonverbally. A goblin’s lifespan is roughly 100 years, and many maintain their keen hearing and sight well into advanced age.

HALFLING

Halflings are small humanoids with large hairy feet and prominent rounded ears. On average, halflings are 3 to 4 feet in height, and their ears, nose, and feet are larger in proportion to the rest of their body. Members of this ancestry live for around 150 years, and a halfling’s appearance is likely to remain youthful even as they progress from adulthood into old age. Halflings are naturally attuned to the magnetic fields of the Mortal Realm, granting them a strong internal compass. They also possess acute senses of hearing and smell, and can often detect those who are familiar to them by the sound of their movements.

HUMAN

Humans are most easily recognized by their dexterous hands, rounded ears, and bodies built for endurance. Their average height ranges from just under 5 feet to about 6 ½ feet. They have a wide variety of builds, with some being quite broad, others lithe, and many inhabiting the spectrum in between. Humans are physically adaptable and adjust to harsh climates with relative ease. In general, humans live to an age of about 100, with their bodies changing dramatically between their youngest and oldest years.

INFERNIS

Infernis are humanoids who possess sharp canine teeth, pointed ears, and horns. They are the descendants of demons from the Circles Below. On average, infernis range in height from 5 feet to 7 feet and are known to have long fingers and pointed nails. Some have long, thin, and smooth tails that end in points, forks, or arrowheads. It’s common for infernis to have two or four horns—though some have crowns of many horns, or only one. These horns can also grow asymmetrically, forming unique, often curving, shapes that infernis enhance with carving and ornamentation. Their skin, hair, and horns come in an assortment of colors that can include soft pastels, stark tones, or vibrant hues, such as rosy scarlet, deep purple, and pitch black.

Infernis possess a “dread visage” that manifests both involuntarily, such as when they experience fear or other strong emotions, or purposefully, such as when they wish to intimidate an adversary. This visage can briefly modify their appearance in a variety of ways, including lengthening their teeth and nails, changing the colors of their eyes, twisting their horns, or enhancing their height. On average, infernis live up to 350 years, with some attributing this lifespan to their demonic lineage.

KATARI

Katari are feline humanoids with retractable claws, vertically slit pupils, and high, triangular ears. They can also have small, pointed canine teeth, soft fur, and long whiskers that assist their perception and navigation. Their ears can swivel nearly 180 degrees to detect sound, adding to their heightened senses. Katari may look more or less feline or humanoid, with catlike attributes in the form of hair, whiskers, and a muzzle. About half of the katari population have tails. Their skin and fur come in a wide range of hues and patterns, including solid colors, calico tones, tabby stripes, and an array of spots, patches, marbling, or bands. Their height ranges from about 3 feet to 6 ½ feet, and they live to around 150 years.

ORC

Orcs are humanoids most easily recognized by their square features and boar-like tusks that protrude from their lower jaw. Tusks come in various sizes, and though they extend from the mouth, they aren’t used for consuming food. Instead, many orcs choose to decorate their tusks with significant ornamentation. Orcs typically live for 125 years, and unless altered, their tusks continue to grow throughout the course of their lives. Their ears are pointed, and their hair and skin typically have green, blue, pink, or gray tones. Orcs tend toward a muscular build, and their average height ranges from 5 feet to 6 ½ feet.

RIBBET

Ribbets resemble anthropomorphic frogs with protruding eyes and webbed hands and feet. They have smooth (though sometimes warty) moist skin and eyes positioned on either side of their head. Some ribbets have hind legs more than twice the length of their torso, while others have short limbs. No matter their size (which ranges from about 3 feet to 4 ½ feet), ribbets primarily move by hopping. All ribbets have webbed appendages, allowing them to swim with ease. Some ribbets possess a natural green-and-brown camouflage, while others are vibrantly colored with bold patterns. No matter their appearance, all ribbets are born from eggs laid in the water, hatch into tadpoles, and after about 6 to 7 years, grow into amphibians that can move around on land. Ribbets live for approximately 100 years.

SIMIAH

Simiah resemble anthropomorphic monkeys and apes with long limbs and prehensile feet. While their appearance reflects all simian creatures, from the largest gorilla to the smallest marmoset, their size does not align with their animal counterparts, and they can be anywhere from 2 to 6 feet tall. All simiah can use their dexterous feet for nonverbal communication, work, and combat. Additionally, some also have prehensile tails that can grasp objects or help with balance during difficult maneuvers. These traits grant members of this ancestry unique agility that aids them in a variety of physical tasks. In particular, simiah are skilled climbers and can easily transition from bipedal movement to knuckle-walking and climbing, and back again. On average, simiah live for about 100 years.

MIXED ANCESTRY

Families within the world of Daggerheart are as unique as the peoples and cultures that inhabit it. Anyone’s appearance and abilities can be shaped by blood, magic, proximity, or a variety of other factors.

If you decide that your character is a descendant of multiple ancestries and you want to mechanically represent that in the game, use the steps below:

  1. Determine Ancestry Combination

    When you choose an ancestry at character creation, write down how your character identifies themself in the Heritage section of your character sheet. For example, if your character is descended from both goblins and orcs, you could use a hybridized term, such as “goblin-orc,” to describe your ancestry, list only the ancestry you more closely identify with (e.g., just “goblin” or just “orc”), or invent a new term, such as “toothling.”

  2. Choose Ancestry Features

    Work with your GM to choose two features from the ancestries in your character’s lineage. You must choose the first feature from one ancestry and the second from another. Write both down on a notecard you can keep with your other cards or next to your character sheet.


For example, if you are making a goblin-orc, you might take the “Surefooted” and “Tusks” features or the “Sturdy” and “Danger Sense” features. You can’t take both the “Surefooted” and “Sturdy” features, because these are both the first features listed on their respective ancestry cards.


Your character's heritage might include more than two ancestries, but you still only choose features from two. You can represent additional ancestries through their appearance or backstory.

COMMUNITIES

Communities represent a key aspect of the culture, class, or environment of origin that has had the most influence over your character’s upbringing.

Your character’s community grants them a community feature. Each community card also lists six adjectives you can use as inspiration to create your character’s personality, their relationship to their peers, their attitude toward their upbringing, or the demeanor with which they interact with the rest of the party.

HIGHBORNE

Being part of a highborne community means you're accustomed to a life of elegance, opulence, and prestige within the upper echelons of society. Traditionally, members of a highborne community possess incredible material wealth. While this can take a variety of forms depending on the community—including gold and other minerals, land, or controlling the means of production—this status always comes with power and influence. Highborne place great value on titles and possessions, and there is little social mobility within their ranks. Members of a highborne community often control the political and economic status of the areas in which they live due to their ability to influence people and the economy with their substantial wealth. The health and safety of the less affluent people who live in these locations often hinges on the ability of this highborne ruling class to prioritize the well-being of their subjects over profit.

Highborne are often amiable, candid, conniving, enterprising, ostentatious, and unflappable.

LOREBORNE

Being part of a loreborne community means you’re from a society that favors strong academic or political prowess. Loreborne communities highly value knowledge, frequently in the form of historical preservation, political advancement, scientific study, skill development, or lore and mythology compilation. Most members of these communities research in institutions built in bastions of civilization, while some eclectic few thrive in gathering information from the natural world. Some may be isolationists, operating in smaller enclaves, schools, or guilds and following their own unique ethos. Others still wield their knowledge on a larger scale, making deft political maneuvers across governmental landscapes.

Loreborne are often direct, eloquent, inquisitive, patient, rhapsodic, and witty.

ORDERBORNE

Being part of an orderborne community means you’re from a collective that focuses on discipline or faith, and you uphold a set of principles that reflect your experience there. Orderborne are frequently some of the most powerful among the surrounding communities. By aligning the members of their society around a common value or goal, such as a god, doctrine, ethos, or even a shared business or trade, the ruling bodies of these enclaves can mobilize larger populations with less effort. While orderborne communities take a variety of forms—some even profoundly pacifistic—perhaps the most feared are those that structure themselves around military prowess. In such a case, it’s not uncommon for orderborne to provide soldiers for hire to other cities or countries.

Orderborne are often ambitious, benevolent, pensive, prudent, sardonic, and stoic.

RIDGEBORNE

Being part of a ridgeborne community means you’ve called the rocky peaks and sharp cliffs of the mountainside home. Those who’ve lived in the mountains often consider themselves hardier than most because they’ve thrived among the most dangerous terrain many continents have to offer. These groups are adept at adaptation, developing unique technologies and equipment to move both people and products across difficult terrain. As such, ridgeborne grow up scrambling and climbing, making them sturdy and strong-willed. Ridgeborne localities appear in a variety of forms—some cities carve out entire cliff faces, others construct castles of stone, and still more live in small homes on windblown peaks. Outside forces often struggle to attack ridgeborne groups, as the small militias and large military forces of the mountains are adept at utilizing their high-ground advantage.

Ridgeborne are often bold, hardy, indomitable, loyal, reserved, and stubborn.

SEABORNE

Being part of a seaborne community means you lived on or near a large body of water. Seaborne communities are built, both physically and culturally, around the specific waters they call home. Some of these groups live along the shore, constructing ports for locals and travelers alike. These harbors function as centers of commerce, tourist attractions, or even just a safe place to lay down one’s head after weeks of travel. Other seaborne live on the water in small boats or large ships, with the idea of “home” comprising a ship and its crew, rather than any one landmass. No matter their exact location, seaborne communities are closely tied to the ocean tides and the creatures who inhabit them. Seaborne learn to fish at a young age, and train from birth to hold their breath and swim in even the most tumultuous waters. Individuals from these groups are highly sought after for their sailing skills, and many become captains of vessels, whether within their own community, working for another, or even at the helm of a powerful naval operation.

Seaborne are often candid, cooperative, exuberant, fierce, resolute, and weathered.

SLYBORNE

Being part of a slyborne community means you come from a group that operates outside the law, including all manner of criminals, grifters, and con artists. Members of slyborne communities are brought together by their disreputable goals and their clever means of achieving them. Many people in these communities have an array of unscrupulous skills: forging, thievery, smuggling, and violence. People of any social class can be slyborne, from those who have garnered vast wealth and influence to those without a coin to their name. To the outside eye, slyborne might appear to be ruffians with no loyalty, but these communities possess some of the strictest codes of honor which, when broken, can result in a terrifying end for the transgressor.

Slyborne are often calculating, clever, formidable, perceptive, shrewd, and tenacious.

UNDERBORNE

Being part of an underborne community means you’re from a subterranean society. Many underborne live right beneath the cities and villages of other collectives, while some live much deeper. These communities range from small family groups in burrows to massive metropolises in caverns of stone. In many locales, underborne are recognized for their incredible boldness and skill that enable great feats of architecture and engineering. Underborne are regularly hired for their bravery, as even the least daring among them has likely encountered formidable belowground beasts, and learning to dispatch such creatures is common practice amongst these societies. Because of the dangers of their environment, many underborne communities develop unique nonverbal languages that prove equally useful on the surface.

Underborne are often composed, elusive, indomitable, innovative, resourceful, and unpretentious.

WANDERBORNE

Being part of a wanderborne community means you’ve lived as a nomad, forgoing a permanent home and experiencing a wide variety of cultures. Unlike many communities that are defined by their locale, wanderborne are defined by their traveling lifestyle. Because of their frequent migration, wanderborne put less value on the accumulation of material possessions in favor of acquiring information, skills, and connections. While some wanderborne are allied by a common ethos, such as a religion or a set of political or economic values, others come together after shared tragedy, such as the loss of their home or land. No matter the reason, the dangers posed by life on the road and the choice to continue down that road together mean that wanderborne are known for their unwavering loyalty.

Wanderborne are often inscrutable, magnanimous, mirthful, reliable, savvy, and unorthodox.

WILDBORNE

Being part of a wildborne community means you lived deep within the forest. Wildborne communities are defined by their dedication to the conservation of their homelands, and many have strong religious or cultural ties to the fauna they live among. This results in unique architectural and technological advancements that favor sustainability over short-term, high-yield results. It is a hallmark of wildborne societies to integrate their villages and cities with the natural environment and avoid disturbing the lives of the plants and animals. While some construct their lodgings high in the branches of trees, others establish their homes on the ground beneath the forest canopy. It’s not uncommon for wildborne to remain reclusive and hidden within their woodland homes.

Wildborne are often hardy, loyal, nurturing, reclusive, sagacious, and vibrant.

" + "content": "

DOMAINS

The Daggerheart core set includes 9 Domain Decks, each comprising a collection of cards granting features or special abilities expressing a particular theme.

The 9 Domains are:

ARCANA

Arcana is the domain of innate and instinctual magic. Those who choose this path tap into the raw, enigmatic forces of the realms to manipulate both their own energy and the elements.

Arcana offers wielders a volatile power, but it is incredibly potent when correctly channeled.

The Arcana domain can be accessed by the Druid and Sorcerer classes.

BLADE

Blade is the domain of weapon mastery. Whether by steel, bow, or perhaps a more specialized arm, those who follow this path have the skill to cut short the lives of others. Wielders of Blade dedicate themselves to achieving inexorable power over death.

The Blade domain can be accessed by the Guardian and Warrior classes.

BONE

Bone is the domain of tactics and the body. Practitioners of this domain have an uncanny control over their own physical abilities and an eye for predicting the behaviors of others in combat. Adherents to Bone gain an unparalleled understanding of bodies and their movements.

The Bone domain can be accessed by the Ranger & Warrior classes.

CODEX

Codex is the domain of intensive magical study. Those who seek magical knowledge turn to the equations of power recorded in books, written on scrolls, etched into walls, or tattooed on bodies. Codex offers a commanding and versatile understanding of magic to devotees who pursue knowledge beyond the boundaries of common wisdom.

The Codex domain can be accessed by the Bard and Wizard classes.

GRACE

Grace is the domain of charisma. Through rapturous storytelling, charming spells, or a shroud of lies, those who channel this power define the realities of their adversaries, bending perception to their will. Grace offers its wielders raw magnetism and mastery over language.

The Grace domain can be accessed by the Bard and Rogue classes

MIDNIGHT

Midnight is the domain of shadows and secrecy. Whether by clever tricks, deft magic, or the cloak of night, those who channel these forces practice the art of obscurity and can uncover sequestered treasures. Midnight offers practitioners the power to control and create enigmas.

The Midnight domain can be access by the Rogue and Sorcerer classes.

SAGE

Sage is the domain of the natural world. Those who walk this path tap into the unfettered power of the earth and its creatures to unleash raw magic. Sage grants its adherents the vitality of a blooming flower and the ferocity of a ravenous predator.

The Sage domain can be accessed by the Druid and Ranger classes.

SPLENDOR

Splendor is the domain of life. Through this magic, followers gain the ability to heal and, to an extent, control death. Splendor offers its disciples the magnificent ability to both give and end life.

The Splendor domain can be accessed by the Seraph and Wizard classes.

VALOR

Valor is the domain of protection. Whether through attack or defense, those who choose this discipline channel formidable strength to protect their allies in battle. Valor offers great power to those who raise their shields in defense of others.

The Valor domain can be accessed by the Guardian and Seraph classes.

CLASS DOMAINS

Each class grants access to two domains:

PCs acquire two 1st-level domain cards at character creation and an additional domain card at or below their level each time they level up.

DOMAIN CARDS

Each domain card provides one or more features your PC can utilize during their adventures. Some domain cards provide moves you can make, such as a unique attack or a spell. Others offer passive effects, new downtime or social encounter abilities, or one-time benefits.

DOMAIN CARD ANATOMY

Each domain card includes six elements:

Note: A player can swap domain cards during downtime without paying the domain card’s Recall Cost.

LOADOUT & VAULT

Your loadout is the set of acquired domain cards whose effects your PC can use during play. You can have up to 5 domain cards in your loadout at one time. Once you’ve acquired six or more domain cards, you must choose five to keep in your loadout; the rest are considered to be in your vault. Vault cards are inactive and do not influence play

Note: Your subclass, ancestry, and community cards don’t count toward your loadout or vault and are always active and available.

At the start of a rest, before using downtime moves, you can freely move cards between your loadout and your vault, so long as your loadout doesn’t exceed its five-card maximum.

To move a card from your vault to your loadout at any other time, you must mark a number of Stress equal to the vaulted card’s Recall Cost (located in the top right of the card next to the lightning bolt symbol). If your loadout is already full, you must also move a card from your loadout to your vault to make space, though you can do this at no cost.

When you gain a new domain card at level-up, you can immediately move it into your loadout for free. If your loadout is already full, you must also move a card from your loadout to your vault to make space.

USAGE LIMITS

If a domain card restricts how often it can be used, you can track such limits with whatever method you prefer, such as turning the card sideways, flipping it facedown, or using tokens.

Note: if an effect or ability gives you a number of uses equal to a trait with a modifier of +0 or less, it grants you 0 uses.

CLASSES

A class is a role-based archetype that determines a PC’s:

There are 9 classes in the Daggerheart core materials: Bard, Druid, Guardian, Ranger, Rogue, Seraph, Sorcerer, Warrior, and Wizard.

SUBCLASSES

Each class is divided into two subclasses, each of which further defines and highlights one aspect of its class archetype. Your chosen subclass grants the following:

For some subclasses, the mastery feature unlocks an extraordinary new ability; for others, it’s the impressive culmination of their subclass’s core feature.

The Daggerheart core set includes cards detailing each foundation, specialization, and mastery feature. When you acquire one of these features, take its card for use as a reference during play.

ANCESTRIES

Ancestries represent your character’s lineage, which affects their physical appearance and access to certain special abilities. The following section describes each ancestry in Daggerheart and the characteristics generally shared by members of that ancestry, however, each player decides how much their character aligns with the “standard” or “average” expression of their ancestry.

In Daggerheart, the term “people” is used to refer to all ancestries, as individuals from all lineages possess unique characteristics and cultures, as well as personhood.

Some ancestries are described using the term “humanoid.” This does not imply any genetic relation to “humans,” which is a distinct ancestry within Daggerheart. Instead, it refers to the set of physical characteristics humans will recognize from their own anatomy, such as bipedal movement, upright posture, facial layout, and more. These traits vary by ancestry and individual, though “humanoid” should still provide a useful frame of reference.

The core ruleset includes the following ancestries: Clank, Drakona, Dwarf, Elf, Faerie, Faun, Firbolg, Fungril, Galapa, Giant, Goblin, Halfling, Human, Infernis, Katari, Orc, Ribbet, Simiah, and Mixed Ancestry.

ANCESTRY FEATURES

Each ancestry grants two ancestry features. While some features (such as the ability to fly) are implicitly tied to an ancestry’s anatomy, players determine their characters’ physical form. Work with the GM to re-flavor any implied traits that don’t align with your character concept. If you’d like to make a character who combines more than one ancestry, see “Mixed Ancestry.”

CLANK

Clanks are sentient mechanical beings built from a variety of materials, including metal, wood, and stone. They can resemble humanoids, animals, or even inanimate objects. Like organic beings, their bodies come in a wide array of sizes. Because of their bespoke construction, many clanks have highly specialized physical configurations. Examples include clawed hands for grasping, wheels for movement, or built-in weaponry.

Many clanks embrace body modifications for style as well as function, and members of other ancestries often turn to clank artisans to construct customized mobility aids and physical adornments. Other ancestries can create clanks, even using their own physical characteristics as inspiration, but it’s also common for clanks to build one another. A clank’s lifespan extends as long as they’re able to acquire or craft new parts, making their physical form effectively immortal. That said, their minds are subject to the effects of time, and deteriorate as the magic that powers them loses potency.

DRAKONA

Drakona resemble wingless dragons in humanoid form and possess a powerful elemental breath. All drakona have thick scales that provide excellent natural armor against both attacks and the forces of nature. They are large in size, ranging from 5 feet to 7 feet on average, with long sharp teeth. New teeth grow throughout a Drakona’s approximately 350-year lifespan, so they are never in danger of permanently losing an incisor. Unlike their dragon ancestors, drakona don’t have wings and can’t fly without magical aid. Members of this ancestry pass down the element of their breath through generations, though in rare cases, a drakona’s elemental power will differ from the rest of their family’s.

DWARF

Dwarves are most easily recognized as short humanoids with square frames, dense musculature, and thick hair. Their average height ranges from 4 to 5 ½ feet, and they are often broad in proportion to their stature. Their skin and nails contain a high amount of keratin, making them naturally resilient. This allows dwarves to embed gemstones into their bodies and decorate themselves with tattoos or piercings. Their hair grows thickly—usually on their heads, but some dwarves have thick hair across their bodies as well. Dwarves of all genders can grow facial hair, which they often style in elaborate arrangements. Typically, dwarves live up to 250 years of age, maintaining their muscle mass well into later life.

ELF

Elves are typically tall humanoids with pointed ears and acutely attuned senses. Their ears vary in size and pointed shape, and as they age, the tips begin to droop. While elves come in a wide range of body types, they are all fairly tall, with heights ranging from about 6 to 6 ½ feet. All elves have the ability to drop into a celestial trance, rather than sleep. This allows them to rest effectively in a short amount of time.

Some elves possess what is known as a “mystic form,” which occurs when an elf has dedicated themself to the study or protection of the natural world so deeply that their physical form changes. These characteristics can include celestial freckles, the presence of leaves, vines, or flowers in their hair, eyes that flicker like fire, and more. Sometimes these traits are inherited from parents, but if an elf changes their environment or magical focus, their appearance changes over time. Because elves live for about 350 years, these traits can shift more than once throughout their lifespan.

FAERIE

Faeries are winged humanoid creatures with insectile features. These characteristics cover a broad spectrum from humanoid to insectoid—some possess additional arms, compound eyes, lantern organs, chitinous exoskeletons, or stingers. Because of their close ties to the natural world, they also frequently possess attributes that allow them to blend in with various plants. The average height of a faerie ranges from about 2 feet to 5 feet, but some faeries grow up to 7 feet tall. All faeries possess membranous wings and they each go through a process of metamorphosis. The process and changes differ from faerie to faerie, but during this transformation each individual manifests the unique appearance they will carry throughout the rest of their approximately 50-year lifespan.

FAUN

Fauns resemble humanoid goats with curving horns, square pupils, and cloven hooves. Though their appearances may vary, most fauns have a humanoid torso and a goatlike lower body covered in dense fur. Faun faces can be more caprine or more humanlike, and they have a wide variety of ear and horn shapes. Faun horns range from short with minimal curvature to much larger with a distinct curl. The average faun ranges from 4 feet to 6 ½ feet tall, but their height can change dramatically from one moment to the next based on their stance. The majority of fauns have proportionately long limbs, no matter their size or shape, and are known for their ability to deliver powerful blows with their split hooves. Fauns live for roughly 225 years, and as they age, their appearance can become increasingly goatlike.

FIRBOLG

Firbolgs are bovine humanoids typically recognized by their broad noses and long, drooping ears. Some have faces that are a blend of humanoid and bison, ox, cow, or other bovine creatures. Others, often referred to as minotaurs, have heads that entirely resemble cattle. They are tall and muscular creatures, with heights ranging from around 5 feet to 7 feet, and possess remarkable strength no matter their age. Some firbolgs are known to use this strength to charge their adversaries, an action that is particuarly effective for those who have one of the many varieties of horn styles commonly found in this ancestry. Though their unique characteristics can vary, all firbolgs are covered in fur, which can be muted and earth-toned in color, or come in a variety of pastels, such as soft pinks and blues. On average, firbolgs live for about 150 years.

FUNGRIL

Fungril resemble humanoid mushrooms. They can be either more humanoid or more fungal in appearance, and they come in an assortment of colors, from earth tones to bright reds, yellows, purples, and blues. Fungril display an incredible variety of bodies, faces, and limbs, as there’s no single common shape among them. Even their heights range from a tiny 2 feet tall to a staggering 7 feet tall. While the common lifespan of a fungril is about 300 years, some have been reported to live much longer. They can communicate nonverbally, and many members of this ancestry use a mycelial array to chemically exchange information with other fungril across long distances.

GALAPA

Galapa resemble anthropomorphic turtles with large, domed shells into which they can retract. On average, they range from 4 feet to 6 feet in height, and their head and body shapes can resemble any type of turtle. Galapa come in a variety of earth tones—most often shades of green and brown— and possess unique patterns on their shells. Members of this ancestry can draw their head, arms, and legs into their shell for protection to use it as a natural shield when defensive measures are needed. Some supplement their shell's strength or appearance by attaching armor or carving unique designs, but the process is exceedingly painful. Most galapa move slowly no matter their age, and they can live approximately 150 years.

GIANT

Giants are towering humanoids with broad shoulders, long arms, and one to three eyes. Adult giants range from 6 ½ to 8 ½ feet tall and are naturally muscular, regardless of body type. They are easily recognized by their wide frames and elongated arms and necks. Though they can have up to three eyes, all giants are born with none and remain sightless for their first year of life. Until a giant reaches the age of 10 and their features fully develop, the formation of their eyes may fluctuate. Those with a single eye are commonly known as cyclops. The average giant lifespan is about 75 years.

GOBLIN

Goblins are small humanoids easily recognizable by their large eyes and massive membranous ears. With keen hearing and sharp eyesight, they perceive details both at great distances and in darkness, allowing them to move through less-optimal environments with ease. Their skin and eye colors are incredibly varied, with no one hue, either vibrant or subdued, more dominant than another. A typical goblin stands between 3 feet and 4 feet tall, and each of their ears is about the size of their head. Goblins are known to use ear positions to very specific effect when communicating nonverbally. A goblin’s lifespan is roughly 100 years, and many maintain their keen hearing and sight well into advanced age.

HALFLING

Halflings are small humanoids with large hairy feet and prominent rounded ears. On average, halflings are 3 to 4 feet in height, and their ears, nose, and feet are larger in proportion to the rest of their body. Members of this ancestry live for around 150 years, and a halfling’s appearance is likely to remain youthful even as they progress from adulthood into old age. Halflings are naturally attuned to the magnetic fields of the Mortal Realm, granting them a strong internal compass. They also possess acute senses of hearing and smell, and can often detect those who are familiar to them by the sound of their movements.

HUMAN

Humans are most easily recognized by their dexterous hands, rounded ears, and bodies built for endurance. Their average height ranges from just under 5 feet to about 6 ½ feet. They have a wide variety of builds, with some being quite broad, others lithe, and many inhabiting the spectrum in between. Humans are physically adaptable and adjust to harsh climates with relative ease. In general, humans live to an age of about 100, with their bodies changing dramatically between their youngest and oldest years.

INFERNIS

Infernis are humanoids who possess sharp canine teeth, pointed ears, and horns. They are the descendants of demons from the Circles Below. On average, infernis range in height from 5 feet to 7 feet and are known to have long fingers and pointed nails. Some have long, thin, and smooth tails that end in points, forks, or arrowheads. It’s common for infernis to have two or four horns—though some have crowns of many horns, or only one. These horns can also grow asymmetrically, forming unique, often curving, shapes that infernis enhance with carving and ornamentation. Their skin, hair, and horns come in an assortment of colors that can include soft pastels, stark tones, or vibrant hues, such as rosy scarlet, deep purple, and pitch black.

Infernis possess a “dread visage” that manifests both involuntarily, such as when they experience fear or other strong emotions, or purposefully, such as when they wish to intimidate an adversary. This visage can briefly modify their appearance in a variety of ways, including lengthening their teeth and nails, changing the colors of their eyes, twisting their horns, or enhancing their height. On average, infernis live up to 350 years, with some attributing this lifespan to their demonic lineage.

KATARI

Katari are feline humanoids with retractable claws, vertically slit pupils, and high, triangular ears. They can also have small, pointed canine teeth, soft fur, and long whiskers that assist their perception and navigation. Their ears can swivel nearly 180 degrees to detect sound, adding to their heightened senses. Katari may look more or less feline or humanoid, with catlike attributes in the form of hair, whiskers, and a muzzle. About half of the katari population have tails. Their skin and fur come in a wide range of hues and patterns, including solid colors, calico tones, tabby stripes, and an array of spots, patches, marbling, or bands. Their height ranges from about 3 feet to 6 ½ feet, and they live to around 150 years.

ORC

Orcs are humanoids most easily recognized by their square features and boar-like tusks that protrude from their lower jaw. Tusks come in various sizes, and though they extend from the mouth, they aren’t used for consuming food. Instead, many orcs choose to decorate their tusks with significant ornamentation. Orcs typically live for 125 years, and unless altered, their tusks continue to grow throughout the course of their lives. Their ears are pointed, and their hair and skin typically have green, blue, pink, or gray tones. Orcs tend toward a muscular build, and their average height ranges from 5 feet to 6 ½ feet.

RIBBET

Ribbets resemble anthropomorphic frogs with protruding eyes and webbed hands and feet. They have smooth (though sometimes warty) moist skin and eyes positioned on either side of their head. Some ribbets have hind legs more than twice the length of their torso, while others have short limbs. No matter their size (which ranges from about 3 feet to 4 ½ feet), ribbets primarily move by hopping. All ribbets have webbed appendages, allowing them to swim with ease. Some ribbets possess a natural green-and-brown camouflage, while others are vibrantly colored with bold patterns. No matter their appearance, all ribbets are born from eggs laid in the water, hatch into tadpoles, and after about 6 to 7 years, grow into amphibians that can move around on land. Ribbets live for approximately 100 years.

SIMIAH

Simiah resemble anthropomorphic monkeys and apes with long limbs and prehensile feet. While their appearance reflects all simian creatures, from the largest gorilla to the smallest marmoset, their size does not align with their animal counterparts, and they can be anywhere from 2 to 6 feet tall. All simiah can use their dexterous feet for nonverbal communication, work, and combat. Additionally, some also have prehensile tails that can grasp objects or help with balance during difficult maneuvers. These traits grant members of this ancestry unique agility that aids them in a variety of physical tasks. In particular, simiah are skilled climbers and can easily transition from bipedal movement to knuckle-walking and climbing, and back again. On average, simiah live for about 100 years.

MIXED ANCESTRY

Families within the world of Daggerheart are as unique as the peoples and cultures that inhabit it. Anyone’s appearance and abilities can be shaped by blood, magic, proximity, or a variety of other factors.

If you decide that your character is a descendant of multiple ancestries and you want to mechanically represent that in the game, use the steps below:

  1. Determine Ancestry Combination

    When you choose an ancestry at character creation, write down how your character identifies themself in the Heritage section of your character sheet. For example, if your character is descended from both goblins and orcs, you could use a hybridized term, such as “goblin-orc,” to describe your ancestry, list only the ancestry you more closely identify with (e.g., just “goblin” or just “orc”), or invent a new term, such as “toothling.”

  2. Choose Ancestry Features

    Work with your GM to choose two features from the ancestries in your character’s lineage. You must choose the first feature from one ancestry and the second from another. Write both down on a notecard you can keep with your other cards or next to your character sheet.


For example, if you are making a goblin-orc, you might take the “Surefooted” and “Tusks” features or the “Sturdy” and “Danger Sense” features. You can’t take both the “Surefooted” and “Sturdy” features, because these are both the first features listed on their respective ancestry cards.


Your character's heritage might include more than two ancestries, but you still only choose features from two. You can represent additional ancestries through their appearance or backstory.

COMMUNITIES

Communities represent a key aspect of the culture, class, or environment of origin that has had the most influence over your character’s upbringing.

Your character’s community grants them a community feature. Each community card also lists six adjectives you can use as inspiration to create your character’s personality, their relationship to their peers, their attitude toward their upbringing, or the demeanor with which they interact with the rest of the party.

HIGHBORNE

Being part of a highborne community means you're accustomed to a life of elegance, opulence, and prestige within the upper echelons of society. Traditionally, members of a highborne community possess incredible material wealth. While this can take a variety of forms depending on the community—including gold and other minerals, land, or controlling the means of production—this status always comes with power and influence. Highborne place great value on titles and possessions, and there is little social mobility within their ranks. Members of a highborne community often control the political and economic status of the areas in which they live due to their ability to influence people and the economy with their substantial wealth. The health and safety of the less affluent people who live in these locations often hinges on the ability of this highborne ruling class to prioritize the well-being of their subjects over profit.

Highborne are often amiable, candid, conniving, enterprising, ostentatious, and unflappable.

LOREBORNE

Being part of a loreborne community means you’re from a society that favors strong academic or political prowess. Loreborne communities highly value knowledge, frequently in the form of historical preservation, political advancement, scientific study, skill development, or lore and mythology compilation. Most members of these communities research in institutions built in bastions of civilization, while some eclectic few thrive in gathering information from the natural world. Some may be isolationists, operating in smaller enclaves, schools, or guilds and following their own unique ethos. Others still wield their knowledge on a larger scale, making deft political maneuvers across governmental landscapes.

Loreborne are often direct, eloquent, inquisitive, patient, rhapsodic, and witty.

ORDERBORNE

Being part of an orderborne community means you’re from a collective that focuses on discipline or faith, and you uphold a set of principles that reflect your experience there. Orderborne are frequently some of the most powerful among the surrounding communities. By aligning the members of their society around a common value or goal, such as a god, doctrine, ethos, or even a shared business or trade, the ruling bodies of these enclaves can mobilize larger populations with less effort. While orderborne communities take a variety of forms—some even profoundly pacifistic—perhaps the most feared are those that structure themselves around military prowess. In such a case, it’s not uncommon for orderborne to provide soldiers for hire to other cities or countries.

Orderborne are often ambitious, benevolent, pensive, prudent, sardonic, and stoic.

RIDGEBORNE

Being part of a ridgeborne community means you’ve called the rocky peaks and sharp cliffs of the mountainside home. Those who’ve lived in the mountains often consider themselves hardier than most because they’ve thrived among the most dangerous terrain many continents have to offer. These groups are adept at adaptation, developing unique technologies and equipment to move both people and products across difficult terrain. As such, ridgeborne grow up scrambling and climbing, making them sturdy and strong-willed. Ridgeborne localities appear in a variety of forms—some cities carve out entire cliff faces, others construct castles of stone, and still more live in small homes on windblown peaks. Outside forces often struggle to attack ridgeborne groups, as the small militias and large military forces of the mountains are adept at utilizing their high-ground advantage.

Ridgeborne are often bold, hardy, indomitable, loyal, reserved, and stubborn.

SEABORNE

Being part of a seaborne community means you lived on or near a large body of water. Seaborne communities are built, both physically and culturally, around the specific waters they call home. Some of these groups live along the shore, constructing ports for locals and travelers alike. These harbors function as centers of commerce, tourist attractions, or even just a safe place to lay down one’s head after weeks of travel. Other seaborne live on the water in small boats or large ships, with the idea of “home” comprising a ship and its crew, rather than any one landmass. No matter their exact location, seaborne communities are closely tied to the ocean tides and the creatures who inhabit them. Seaborne learn to fish at a young age, and train from birth to hold their breath and swim in even the most tumultuous waters. Individuals from these groups are highly sought after for their sailing skills, and many become captains of vessels, whether within their own community, working for another, or even at the helm of a powerful naval operation.

Seaborne are often candid, cooperative, exuberant, fierce, resolute, and weathered.

SLYBORNE

Being part of a slyborne community means you come from a group that operates outside the law, including all manner of criminals, grifters, and con artists. Members of slyborne communities are brought together by their disreputable goals and their clever means of achieving them. Many people in these communities have an array of unscrupulous skills: forging, thievery, smuggling, and violence. People of any social class can be slyborne, from those who have garnered vast wealth and influence to those without a coin to their name. To the outside eye, slyborne might appear to be ruffians with no loyalty, but these communities possess some of the strictest codes of honor which, when broken, can result in a terrifying end for the transgressor.

Slyborne are often calculating, clever, formidable, perceptive, shrewd, and tenacious.

UNDERBORNE

Being part of an underborne community means you’re from a subterranean society. Many underborne live right beneath the cities and villages of other collectives, while some live much deeper. These communities range from small family groups in burrows to massive metropolises in caverns of stone. In many locales, underborne are recognized for their incredible boldness and skill that enable great feats of architecture and engineering. Underborne are regularly hired for their bravery, as even the least daring among them has likely encountered formidable belowground beasts, and learning to dispatch such creatures is common practice amongst these societies. Because of the dangers of their environment, many underborne communities develop unique nonverbal languages that prove equally useful on the surface.

Underborne are often composed, elusive, indomitable, innovative, resourceful, and unpretentious.

WANDERBORNE

Being part of a wanderborne community means you’ve lived as a nomad, forgoing a permanent home and experiencing a wide variety of cultures. Unlike many communities that are defined by their locale, wanderborne are defined by their traveling lifestyle. Because of their frequent migration, wanderborne put less value on the accumulation of material possessions in favor of acquiring information, skills, and connections. While some wanderborne are allied by a common ethos, such as a religion or a set of political or economic values, others come together after shared tragedy, such as the loss of their home or land. No matter the reason, the dangers posed by life on the road and the choice to continue down that road together mean that wanderborne are known for their unwavering loyalty.

Wanderborne are often inscrutable, magnanimous, mirthful, reliable, savvy, and unorthodox.

WILDBORNE

Being part of a wildborne community means you lived deep within the forest. Wildborne communities are defined by their dedication to the conservation of their homelands, and many have strong religious or cultural ties to the fauna they live among. This results in unique architectural and technological advancements that favor sustainability over short-term, high-yield results. It is a hallmark of wildborne societies to integrate their villages and cities with the natural environment and avoid disturbing the lives of the plants and animals. While some construct their lodgings high in the branches of trees, others establish their homes on the ground beneath the forest canopy. It’s not uncommon for wildborne to remain reclusive and hidden within their woodland homes.

Wildborne are often hardy, loyal, nurturing, reclusive, sagacious, and vibrant.

" }, "video": { "controls": true, diff --git a/styles/less/dialog/character-creation/tab-navigation.less b/styles/less/dialog/character-creation/tab-navigation.less index dbf285f6..709e0adc 100644 --- a/styles/less/dialog/character-creation/tab-navigation.less +++ b/styles/less/dialog/character-creation/tab-navigation.less @@ -3,59 +3,16 @@ .daggerheart.dh-style.dialog.character-creation { .tab-navigation { nav { - flex: 1; + border-bottom: 0; + border-top: 0; a { - flex: 1; - text-align: center; - display: flex; - justify-content: center; - position: relative; + font-family: @font-body; + color: light-dark(@dark-blue, @golden); - &.disabled { + &[disabled] { opacity: 0.4; } - - .nav-section-text { - position: relative; - display: flex; - align-items: center; - } - - .finish-marker { - position: absolute; - align-self: center; - top: -8px; - padding: 4px; - border: 1px solid; - border-radius: 50%; - height: 16px; - width: 16px; - font-size: 12px; - display: flex; - align-items: center; - justify-content: center; - background-color: var(--color-cool-4); - content: ''; - - &.active { - background-color: var(--color-warm-2); - } - } - - .descriptor { - position: absolute; - bottom: -8px; - font-size: 12px; - border-radius: 8px; - width: 56px; - text-align: center; - line-height: 1; - border: 1px solid light-dark(@dark-blue, @golden); - border-radius: 6px; - color: light-dark(@beige, @dark); - background-image: url(../assets/parchments/dh-parchment-light.png); - } } } } diff --git a/styles/less/dialog/downtime/downtime-container.less b/styles/less/dialog/downtime/downtime-container.less index 55fb5b70..fe1344cd 100644 --- a/styles/less/dialog/downtime/downtime-container.less +++ b/styles/less/dialog/downtime/downtime-container.less @@ -6,6 +6,12 @@ .downtime-container .activity-container .activity-selected-marker { background-image: url(../assets/parchments/dh-parchment-light.png); } + + .refreshables-container { + .refreshable-container { + background-image: url(../assets/parchments/dh-parchment-light.png); + } + } } .daggerheart.dh-style.views.downtime { diff --git a/styles/less/global/dice.less b/styles/less/global/dice.less index 075fa9b4..aecfdaee 100644 --- a/styles/less/global/dice.less +++ b/styles/less/global/dice.less @@ -9,13 +9,13 @@ &.xxs { height: 1rem; - font-size: .5rem; + font-size: 0.5rem; -webkit-text-stroke: 1px black; } &.xs { height: 1.5rem; - font-size: .75rem; + font-size: 0.75rem; } &.lg { @@ -29,7 +29,7 @@ } &.discarded { - opacity: .5; + opacity: 0.5; } &:before { @@ -40,7 +40,7 @@ z-index: -1; mask: var(--svg-die) no-repeat center; mask-size: contain; - background: linear-gradient(139.01deg, #EFE6D8 3.51%, #372E1F 96.49%); + background: linear-gradient(139.01deg, #efe6d8 3.51%, #372e1f 96.49%); } &.d4:before { @@ -63,18 +63,22 @@ } &.color-hope:before { - background: linear-gradient(139.01deg, #F3C267 3.51%, #4C3407 96.49%); + background: linear-gradient(139.01deg, #f3c267 3.51%, #4c3407 96.49%); } &.color-fear:before { - background: linear-gradient(151.21deg, #352AB2 7.21%, #18162E 92.79%); + background: linear-gradient(151.21deg, #352ab2 7.21%, #18162e 92.79%); } &.color-adv:before { - background: linear-gradient(139.01deg, #40A640 3.51%, #011B01 96.49%); + background: linear-gradient(139.01deg, #40a640 3.51%, #011b01 96.49%); } &.color-dis:before { - background: linear-gradient(139.01deg, #E54E4E 3.51%, #3C0000 82.19%); + background: linear-gradient(139.01deg, #e54e4e 3.51%, #3c0000 82.19%); } -} \ No newline at end of file + + &.coin:before { + border-radius: 50%; + } +} diff --git a/styles/less/global/inventory-item.less b/styles/less/global/inventory-item.less index e79a88c7..c4239d27 100644 --- a/styles/less/global/inventory-item.less +++ b/styles/less/global/inventory-item.less @@ -35,6 +35,37 @@ } } } + + &:hover { + .inventory-item-header .item-label .item-name .expanded-icon { + margin-left: 10px; + display: inline-block; + } + &:has(.inventory-item-content.extensible) { + .inventory-item-header, + .inventory-item-content { + background: light-dark(@dark-blue-40, @golden-40); + } + } + &:has(.inventory-item-content.extended) { + .inventory-item-header .item-label .item-name .expanded-icon { + display: none; + } + } + } + + &:has(.inventory-item-content.extensible) { + .inventory-item-header { + border-radius: 5px 5px 0 0; + } + .inventory-item-content { + border-radius: 0 0 5px 5px; + } + } + + &:not(:has(.inventory-item-content.extensible)) .inventory-item-header { + border-radius: 5px; + } } .inventory-item-header { @@ -42,6 +73,7 @@ align-items: center; gap: 10px; cursor: pointer; + border-radius: 3px; .img-portait { flex: 0 0 40px; @@ -75,6 +107,10 @@ .item-name { font-size: 14px; + + .expanded-icon { + display: none; + } } .item-tags, @@ -118,6 +154,11 @@ justify-content: end; gap: 8px; + a { + width: 15px; + text-align: center; + } + .unequipped { opacity: 0.5; } diff --git a/styles/less/sheets-settings/character-settings/sheet.less b/styles/less/sheets-settings/character-settings/sheet.less index b563f2f4..43103fc7 100644 --- a/styles/less/sheets-settings/character-settings/sheet.less +++ b/styles/less/sheets-settings/character-settings/sheet.less @@ -1,3 +1,16 @@ +.theme-light .application.daggerheart.dh-style.dialog { + .tab.details { + .traits-inner-container .trait-container { + background: url('../assets/svg/trait-shield-light.svg') no-repeat; + + div { + filter: drop-shadow(0 0 3px @beige); + text-shadow: 0 0 3px @beige; + } + } + } +} + .application.daggerheart.dh-style.dialog { .tab.details { .traits-inner-container { diff --git a/styles/less/sheets/actors/adversary/sidebar.less b/styles/less/sheets/actors/adversary/sidebar.less index 4955c175..79927881 100644 --- a/styles/less/sheets/actors/adversary/sidebar.less +++ b/styles/less/sheets/actors/adversary/sidebar.less @@ -9,10 +9,14 @@ } } }, { - &.adversary { + &.sheet.actor.dh-style.adversary { .adversary-sidebar-sheet { background: transparent; } + + .portrait.death-roll .death-roll-btn { + filter: brightness(0) drop-shadow(0 0 3px @dark-blue); + } } }); @@ -27,18 +31,10 @@ border-bottom: 1px solid light-dark(@dark-blue, @golden); cursor: pointer; - img { - height: 235px; - width: 275px; - object-fit: cover; - } - - .death-roll-btn { - display: none; - } - &.death-roll { - filter: grayscale(1); + img { + filter: grayscale(1); + } .death-roll-btn { display: flex; @@ -47,12 +43,24 @@ right: 30%; font-size: 6rem; color: @beige; + filter: grayscale(1) drop-shadow(0 0 3px black); &:hover { text-shadow: 0 0 8px @beige; } } } + + img { + height: 275px; + width: 275px; + object-fit: cover; + object-position: top center; + } + + .death-roll-btn { + display: none; + } } .threshold-section { @@ -67,7 +75,6 @@ align-items: center; width: fit-content; height: 30px; - margin-top: 16px; h4 { font-size: 14px; @@ -108,6 +115,7 @@ .resources-section { display: flex; justify-content: space-evenly; + margin-bottom: 16px; .status-bar { display: flex; diff --git a/styles/less/sheets/actors/character/sidebar.less b/styles/less/sheets/actors/character/sidebar.less index 494a4728..3d8e829a 100644 --- a/styles/less/sheets/actors/character/sidebar.less +++ b/styles/less/sheets/actors/character/sidebar.less @@ -11,11 +11,16 @@ } } }, { - .character-sidebar-sheet { + &.sheet.actor.dh-style.character .character-sidebar-sheet { background: transparent; + .experience-value { background: url('../assets/svg/experience-shield-light.svg') no-repeat; } + + .portrait.death-roll .death-roll-btn { + filter: brightness(0) drop-shadow(0 0 3px @dark-blue); + } } }); @@ -29,18 +34,10 @@ border-bottom: 1px solid light-dark(@dark-blue, @golden); cursor: pointer; - img { - height: 235px; - width: 275px; - object-fit: cover; - } - - .death-roll-btn { - display: none; - } - &.death-roll { - filter: grayscale(1); + img { + filter: grayscale(1); + } .death-roll-btn { display: flex; @@ -49,6 +46,7 @@ right: 30%; font-size: 6rem; color: @beige; + filter: grayscale(1) drop-shadow(0 0 3px black); &:hover { text-shadow: 0 0 8px @beige; @@ -56,6 +54,17 @@ } } + img { + height: 275px; + width: 275px; + object-fit: cover; + object-position: top center; + } + + .death-roll-btn { + display: none; + } + .icons-list { position: absolute; display: flex; @@ -239,7 +248,7 @@ height: 30px; .status-label { - padding: 2px 10px; + padding: 2px 2px; position: relative; top: 30px; height: 22px; diff --git a/styles/less/sheets/actors/companion/header.less b/styles/less/sheets/actors/companion/header.less index b343146f..bc1995aa 100644 --- a/styles/less/sheets/actors/companion/header.less +++ b/styles/less/sheets/actors/companion/header.less @@ -10,8 +10,9 @@ .profile { height: 235px; - width: 100%; + width: 235px; object-fit: cover; + object-position: top center; cursor: pointer; mask-image: linear-gradient(0deg, transparent 0%, black 10%); } @@ -184,9 +185,50 @@ } } - .level-up-label { - font-size: 24px; - padding-top: 8px; + .level-div { + white-space: nowrap; + display: flex; + justify-content: end; + + .label { + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; + + .input-section { + display: flex; + align-items: center; + justify-content: space-between; + } + } + + input { + width: 40px; + padding: 0; + text-align: center; + border: 0; + } + + .level-button { + color: light-dark(@dark, @beige); + font-size: 18px; + line-height: 1; + min-height: unset; + height: min-content; + padding: 4px; + font-family: 'Cinzel', serif; + margin: 0; + font-weight: normal; + border-color: light-dark(@dark-blue, @golden); + background-color: light-dark(transparent, @deep-black); + + &:hover { + background-image: none; + background-color: var(--color-warm-2); + filter: drop-shadow(0 0 3px lightgray); + } + } } } diff --git a/styles/less/sheets/actors/environment/header.less b/styles/less/sheets/actors/environment/header.less index 1276b276..c9d01754 100644 --- a/styles/less/sheets/actors/environment/header.less +++ b/styles/less/sheets/actors/environment/header.less @@ -9,9 +9,10 @@ text-align: center; .profile { - width: 100%; + width: 235px; height: 235px; object-fit: cover; + object-position: top center; mask-image: linear-gradient(0deg, transparent 0%, black 10%); cursor: pointer; } diff --git a/system.json b/system.json index a0c91923..66a4cfdb 100644 --- a/system.json +++ b/system.json @@ -2,7 +2,7 @@ "id": "daggerheart", "title": "Daggerheart", "description": "An unofficial implementation of the Daggerheart system", - "version": "1.0.0", + "version": "1.0.2", "compatibility": { "minimum": "13", "verified": "13.347", @@ -28,7 +28,10 @@ "name": "jacobwojoski" }, { - "name": "moliloo" + "name": "moliloo", + "url": "https://github.com/moliloo", + "email": "molilofl@gmail.com", + "discord": "molilo" }, { "name": "Mysteryusy" diff --git a/templates/actionTypes/healing.hbs b/templates/actionTypes/healing.hbs deleted file mode 100644 index 4b8023d7..00000000 --- a/templates/actionTypes/healing.hbs +++ /dev/null @@ -1,41 +0,0 @@ - -
- - {{localize "DAGGERHEART.GENERAL.healing"}} - - {{#if (and (not @root.isNPC) @root.hasRoll)}} - {{formField fields.resultBased value=source.resultBased name="healing.resultBased" localize=true classes="checkbox"}} - {{/if}} - {{#if (and (not @root.isNPC) @root.hasRoll source.resultBased)}} -
-
- -
{{localize "DAGGERHEART.GENERAL.withThing" thing=(localize "DAGGERHEART.GENERAL.hope")}}
-
- {{> formula fields=fields.value.fields source=source.value target="value"}} -
-
- -
{{localize "DAGGERHEART.GENERAL.withThing" thing=(localize "DAGGERHEART.GENERAL.fear")}}
-
- {{> formula fields=fields.valueAlt.fields source=source.valueAlt target="valueAlt"}} -
-
- {{else}} - {{> formula fields=fields.value.fields source=source.value target="value"}} - {{/if}} - {{formField fields.applyTo value=source.applyTo name="healing.applyTo" localize=true}} -
- -{{#*inline "formula"}} - {{formField fields.custom.fields.enabled value=source.custom.enabled name=(concat "healing." target ".custom.enabled") classes="checkbox"}} - {{#if source.custom.enabled}} - {{formField fields.custom.fields.formula value=source.custom.formula name=(concat "healing." target ".custom.formula") localize=true}} - {{else}} -
- {{formField fields.multiplier value=source.multiplier name=(concat "healing." target ".multiplier") localize=true}} - {{formField fields.dice value=source.dice name=(concat "healing." target ".dice")}} - {{formField fields.bonus value=source.bonus name=(concat "healing." target ".bonus") localize=true}} -
- {{/if}} -{{/inline}} \ No newline at end of file diff --git a/templates/actionTypes/macro.hbs b/templates/actionTypes/macro.hbs new file mode 100644 index 00000000..8c984be8 --- /dev/null +++ b/templates/actionTypes/macro.hbs @@ -0,0 +1,5 @@ + +
+ {{localize "DOCUMENT.Macro"}} + {{formInput fields name="macro" value=source}} +
\ No newline at end of file diff --git a/templates/actionTypes/uuid.hbs b/templates/actionTypes/uuid.hbs deleted file mode 100644 index 140cad78..00000000 --- a/templates/actionTypes/uuid.hbs +++ /dev/null @@ -1,9 +0,0 @@ - -
- -
{{localize "DOCUMENT.Macro"}}
-
-
- {{formInput fields value=source name="documentUUID" placeholder=fields.options.placeholder}} -
-
\ No newline at end of file diff --git a/templates/characterCreation/tabs/equipment.hbs b/templates/characterCreation/equipment.hbs similarity index 100% rename from templates/characterCreation/tabs/equipment.hbs rename to templates/characterCreation/equipment.hbs diff --git a/templates/characterCreation/footer.hbs b/templates/characterCreation/footer.hbs index 47321c7a..95b86cfb 100644 --- a/templates/characterCreation/footer.hbs +++ b/templates/characterCreation/footer.hbs @@ -1,7 +1,7 @@