Hotfix 1.0.2 (#916)

* Updated the background image for the system

* Fixed so Weapon/Armor features are added again

* Fixed so fear is available as a resource to be deducted by actions (#757)

* Changed to use the config labels and src

* Updated Weapons

* Fixed so the decrease button of simple fear tracker is not visible when not hovered

* Fixed so armor preUpdate doesn't fail if no system changes are made

* Updated .gitignore and author details (#777)

* Add author details and name mapping for chrisryan10 (#773)

Co-authored-by: Chris Ryan <chrisr@blackhole>

* Add build to ignore for my linux dev (#775)

Co-authored-by: Chris Ryan <chrisr@blackhole>

---------

Co-authored-by: Chris Ryan <chrisr@blackhole>

* Corrected sneak attack active effect (#780)

* Fixed a spelling error (#779)

* Fix bardic rally showing in damage dialog when it should not (#783)

* update spelling (#786)

* Translating inventory descriptions (#782)

* updated credits for 1.0.1 release (#797)

* updated credits for 1.0.1 release

* further updated artwork credits

* Chagned handlebarhelper rollparsed to be more defensive (#794)

* Added missing scene refreshType (#790)

* Remove ability use buttons for not owned abilities (#795)

* [Fix] PrayerDice Fixed (#799)

* Fixed prayer dice, and wheelchair images

* Fixed -settings data sources

* Dragging features from one adversary to another (#788)

* [Fix] Levelup Fixes (#787)

* Fixed crash on experience selection. Fixed subclass error on multiclassing

* Fixed so multiclasses do not gain the hope feature for the class

* Fixed so Class/Subclass features are properly deleted on delevel

* Removed automatic deletion of features on delevel when not using levelup auto

* Fixed so custom domains can be selected in levelup when multiclassing

* Changed so encounter countdowns is a button (#804)

* Fixed so that dropping on class/subclass...creates the item on the character (#803)

* [BUG] - Importing All Adversaries/Environments (#814)

Fixes #774

Co-authored-by: Joaquin Pereyra <joaquinpereyra98@users.noreply.github.com>

* Bug/671 reaction roll chat title (#809)

* Update Reaction Roll Chat Message Title

* Removed console log

---------

Co-authored-by: WBHarry <williambjrklund@gmail.com>

* Improve Trait tooltip display (#817)

Fixes #806

Co-authored-by: Joaquin Pereyra <joaquinpereyra98@users.noreply.github.com>

* [BUG] - Combat Tracker d12 logo not found (#812)

Fixes #764

Co-authored-by: Joaquin Pereyra <joaquinpereyra98@users.noreply.github.com>

* Compendium Browser (#821)

* Corrected timbending description localization (#816)

* [Fix] Compendium Item (#810)

* Corrected Emberwoven Armor

* Fixed subclass regression

* Fixed so character's with wildcard images don't break beastform (#815)

* Fix roll result based duality damage (#822)

* Impproved Adversary Sheet Data Display (#751)

* Impproved Adversary Sheet Data Display
Fixes #604

* FIX: formula lables for attacks and weapons

---------

Co-authored-by: Joaquin Pereyra <joaquinpereyra98@users.noreply.github.com>

* Fixed so exp increases can be selected normally (#835)

* Update localization of rollSelection.hbs (#841)

The Fear label lacked a localization path

* renamed .md files (#834)

* Removed the unintended icons that came from merge conflicts (#838)

* Sheet image position (#861)

* Remove extra result text from reaction chat message (#860)

* Remove extra result text from reaction chat message

* Remove log

* [Fix] 850 - Downtime Refreshes (#859)

* Fixed the filtering of refreshable features

* Raised version

* [Fix] 691 - CharacterCreation Improvement  (#863)

* Removed main creation tab. Added equipment to remaining tab

* Fixed MixedAncestry name setting

* Fixed tab after first creation

* Fix/857 generic roll buttons (#866)

* Action Macro working again

* Remove buttons from generic roll for non-gm

* Update sidebar.less Armor Slots label padding, for translations (#872)

Equalizes the vertical and horizontal padding on the Armor Slots status label, to allow for more (much needed) room for the upcoming French translation (and others in the future).

Doesn't change anything for the display in English.

* Hid item attachments for now (#876)

* Fixed so effects supposed to use item data use the model directly, since items have no rolldata (#883)

* Made sure the beastform user isn't moved onto other duplicate actors. Fixed scrolling text duplication (#882)

* Change the critical damage max calculation (#890)

* Change the critical damage max calculation to avoid setting the rolled portion to maximum all the time.

* Change to the more neater code

* Restored package.json

---------

Co-authored-by: Chris Ryan <chrisr@blackhole>
Co-authored-by: WBHarry <williambjrklund@gmail.com>

* Fix movement (#885)

* Changed so companion can level up on its own (#879)

* Fix the missing ancestries in the SRD (#888)

Co-authored-by: Chris Ryan <chrisr@blackhole>

* Removed the old, now unused companion levelup button (#893)

* Fixed so custom downtime moves will display their descriptions in the tooltip (#894)

* Made coinflip icons in chat round (#895)

* Fixed undefined case (#898)

* Fix/877 hope update order (#896)

* Action Macro working again

* k

* Fix Hope gain on Duality Roll

* [Feature] Descriptions enhancements (#887)

* add style to hover items and add start setting to features be expanded by default

* REFACTOR:
now prepare description onRender and simply the other methods

* add setting to extend description from items and add molilo contacts in system.json

---------

Co-authored-by: Joaquin Pereyra <joaquinpereyra98@users.noreply.github.com>

* Fixed traits container styling in light-mode (#899)

* [BUG] - Movement Button selector not showing current mode (#912)

Fixes #910

Co-authored-by: Joaquin Pereyra <joaquinpereyra98@users.noreply.github.com>

* Fix/864 chat targeting (#911)

* Fix targeting

* Fix chat target selection

* Uncomment chatDisplay

* Removed unessecary arg

* [Feature] 648 - Mark Defeated Actors (#914)

* Improved death marking styling

* Added automation for defeated status

* Fixed so the tracker recognises and sets the correct defeated statuses depending on type

* Fixed so missing statuses doesn't cause crashes

* Increased companion sheet width by 40 pixels

* Added missing inheritDoc

* Removed fas

* FIX: use img instead of icons con statusEffectConfigns (#917)

Authored-by: Joaquin Pereyra <joaquinpereyra98@users.noreply.github.com>

---------

Co-authored-by: Chris Ryan <73275196+chrisryan10@users.noreply.github.com>
Co-authored-by: Chris Ryan <chrisr@blackhole>
Co-authored-by: Dapoulp <74197441+Dapoulp@users.noreply.github.com>
Co-authored-by: IrkTheImp <41175833+IrkTheImp@users.noreply.github.com>
Co-authored-by: CPTN_Cosmo <cptncosmo@gmail.com>
Co-authored-by: Josh Q. <jshqntnr13@gmail.com>
Co-authored-by: joaquinpereyra98 <24190917+joaquinpereyra98@users.noreply.github.com>
Co-authored-by: Joaquin Pereyra <joaquinpereyra98@users.noreply.github.com>
Co-authored-by: SunnySunSun <snysun@pm.me>
Co-authored-by: Murilo Brito <91566541+moliloo@users.noreply.github.com>
This commit is contained in:
WBHarry 2025-08-13 21:53:20 +02:00 committed by GitHub
parent 67d3b6c0e3
commit 5e56c7ec2e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
208 changed files with 1450 additions and 895 deletions

View file

View file

@ -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 isnt 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."
}
}
}

View file

@ -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,44 +194,11 @@ 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);
return context;
}
async _preparePartContext(partId, context) {
switch (partId) {
case 'footer':
context.isLastTab = this.tabGroups.setup === 'domainCards' || this.tabGroups.primary !== 'setup';
switch (this.tabGroups.setup) {
case null:
case 'ancestry':
context.nextDisabled = this.setup.visibility === 1;
break;
case 'community':
context.nextDisabled = this.setup.visibility === 2;
break;
case 'class':
context.nextDisabled = this.setup.visibility === 3;
break;
case 'traits':
context.nextDisabled = this.setup.visibility === 4;
break;
case 'experience':
context.nextDisabled = this.setup.visibility === 5;
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 }));
@ -353,7 +240,9 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
};
context.mixedAncestry = Number(this.setup.mixedAncestry);
context.ancestryName = this.setup.ancestryName;
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' };
@ -370,6 +259,36 @@ export default class DhCharacterCreation extends HandlebarsApplicationMixin(Appl
}, {});
context.visibility = this.setup.visibility;
return context;
}
async _preparePartContext(partId, context) {
switch (partId) {
case 'footer':
context.isLastTab = this.tabGroups.setup === 'equipment';
switch (this.tabGroups.setup) {
case null:
case 'ancestry':
context.nextDisabled = this.setup.visibility === 1;
break;
case 'community':
context.nextDisabled = this.setup.visibility === 2;
break;
case 'class':
context.nextDisabled = this.setup.visibility === 3;
break;
case 'traits':
context.nextDisabled = this.setup.visibility === 4;
break;
case 'experience':
context.nextDisabled = this.setup.visibility === 5;
break;
case 'domainCards':
context.nextDisabled = this.setup.visibility === 6;
break;
}
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()),

View file

@ -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,

View file

@ -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,

View file

@ -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];

View file

@ -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'
}
};

View file

@ -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 });
}
}

View file

@ -639,14 +639,20 @@ export default class CharacterSheet extends DHBaseActorSheet {
})
});
setTimeout(() => {
this.consumeResource(result?.costs);
}, 50);
}
// 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 {

View file

@ -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 });
}
}

View file

@ -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,24 +419,28 @@ 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<void>}
*/
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);
//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);
const isAction = !!actionId;
// Enrich the description and attach it;
const isAction = doc.documentName === 'Action';
descriptionElement.innerHTML = await foundry.applications.ux.TextEditor.implementation.enrichHTML(
description,
{
@ -431,14 +450,45 @@ export default function DHApplicationMixin(Base) {
}
);
}
}
/* -------------------------------------------- */
/* Extend Descriptions by Settings */
/* -------------------------------------------- */
/**
* Extend inventory description when enabled in settings.
* @returns {Promise<void>}
*/
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);
}
}
}
/* -------------------------------------------- */
/* Application Clicks Actions */
/* -------------------------------------------- */
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');
}
}

View file

@ -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'
}

View file

@ -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: {

View file

@ -15,11 +15,12 @@ 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);
if (statusData) {
acc.push({
name: game.i18n.localize(statusData.name),
statuses: [status],
@ -28,6 +29,7 @@ export default class DhTokenPlaceable extends foundry.canvas.placeables.Token {
});
}
}
}
return acc;
}, []);

View file

@ -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: {

View file

@ -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;
}
}

View file

@ -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);

View file

@ -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) {

View file

@ -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
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';
}
: null;
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;

View file

@ -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 });

View file

@ -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(

View file

@ -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;
}
}

View file

@ -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';

View file

@ -0,0 +1,7 @@
const fields = foundry.data.fields;
export default class MacroField extends fields.DocumentUUIDField {
constructor(context = {}) {
super({ type: "Macro" }, context);
}
}

View file

@ -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;

View file

@ -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;
}

View file

@ -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;
}
}

View file

@ -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 });
}
}

View file

@ -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: {

View file

@ -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,

View file

@ -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'
})
};
}

View file

@ -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'
})
})
};
}

View file

@ -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 */

View file

@ -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;
});
};

View file

@ -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;

View file

@ -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 });
}
}
}
}

View file

@ -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,11 +69,15 @@ export default class DhpChatMessage extends foundry.documents.ChatMessage {
}
}
if(!game.user.isGM && !this.isAuthor && !this.speakerActor?.isOwner) {
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());
}
}
}
addChatListeners(html) {
html.querySelectorAll('.damage-button').forEach(element =>

View file

@ -72,4 +72,8 @@ export default class DHToken extends TokenDocument {
}
return attributes;
}
_shouldRecordMovementHistory() {
return false;
}
}

View file

@ -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`,

View file

@ -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, {

View file

@ -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',

View file

@ -122,7 +122,7 @@
},
"_id": "TCKVaVweyJzhEArX",
"systemPath": "actions",
"type": "",
"type": "attack",
"description": "",
"img": "icons/creatures/claws/claw-curved-jagged-yellow.webp",
"chatDisplay": true,

View file

@ -103,7 +103,8 @@
"base": false
}
]
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -102,7 +102,8 @@
}
]
},
"img": "icons/weapons/daggers/dagger-bone-black.webp"
"img": "icons/weapons/daggers/dagger-bone-black.webp",
"type": "attack"
}
},
"flags": {},

View file

@ -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": {},

View file

@ -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": {},

View file

@ -103,7 +103,8 @@
"roll": {
"bonus": 0,
"type": "attack"
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -109,7 +109,8 @@
"base": false
}
]
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -109,7 +109,8 @@
}
]
},
"range": ""
"range": "",
"type": "attack"
}
},
"flags": {},

View file

@ -111,7 +111,8 @@
}
]
},
"img": "icons/creatures/claws/claw-straight-brown.webp"
"img": "icons/creatures/claws/claw-straight-brown.webp",
"type": "attack"
}
},
"flags": {},

View file

@ -107,7 +107,8 @@
"base": false
}
]
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -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": {},

View file

@ -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": {},

View file

@ -103,7 +103,8 @@
}
]
},
"img": "icons/magic/light/beam-rays-magenta.webp"
"img": "icons/magic/light/beam-rays-magenta.webp",
"type": "attack"
}
},
"flags": {},

View file

@ -96,7 +96,8 @@
"base": false
}
]
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -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": {},

View file

@ -113,7 +113,8 @@
}
]
},
"img": "icons/weapons/daggers/dagger-straight-cracked.webp"
"img": "icons/weapons/daggers/dagger-straight-cracked.webp",
"type": "attack"
}
},
"flags": {},

View file

@ -107,7 +107,8 @@
}
]
},
"img": "icons/weapons/daggers/dagger-twin-green.webp"
"img": "icons/weapons/daggers/dagger-twin-green.webp",
"type": "attack"
}
},
"flags": {},

View file

@ -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": {},

View file

@ -103,7 +103,8 @@
}
]
},
"range": ""
"range": "",
"type": "attack"
}
},
"flags": {},

View file

@ -95,7 +95,8 @@
"base": false
}
]
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -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": {},

View file

@ -108,7 +108,8 @@
"base": false
}
]
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -108,7 +108,8 @@
"base": false
}
]
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -109,7 +109,8 @@
"base": false
}
]
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -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": {},

View file

@ -109,7 +109,8 @@
"base": false
}
]
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -109,7 +109,8 @@
"bonus": 0,
"type": "attack"
},
"range": ""
"range": "",
"type": "attack"
}
},
"flags": {},

View file

@ -107,7 +107,8 @@
}
]
},
"img": "icons/creatures/claws/claw-hooked-curved.webp"
"img": "icons/creatures/claws/claw-hooked-curved.webp",
"type": "attack"
}
},
"flags": {},

View file

@ -107,7 +107,8 @@
}
]
},
"img": "icons/creatures/claws/claw-straight-brown.webp"
"img": "icons/creatures/claws/claw-straight-brown.webp",
"type": "attack"
}
},
"flags": {},

View file

@ -109,7 +109,8 @@
"base": false
}
]
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -103,7 +103,8 @@
"bonus": 0,
"type": "attack"
},
"range": ""
"range": "",
"type": "attack"
}
},
"flags": {},

View file

@ -96,7 +96,8 @@
"base": false
}
]
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -87,7 +87,7 @@
"img": "icons/weapons/polearms/spear-flared-steel.webp",
"_id": "jmrgFi8AUL6LTbtU",
"systemPath": "actions",
"type": "",
"type": "attack",
"description": "",
"chatDisplay": true,
"actionType": "action",

View file

@ -109,7 +109,8 @@
]
},
"img": "icons/creatures/claws/claw-hooked-barbed.webp",
"range": ""
"range": "",
"type": "attack"
}
},
"flags": {},

View file

@ -96,7 +96,8 @@
"base": false
}
]
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -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": {},

View file

@ -109,7 +109,8 @@
"base": false
}
]
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -109,7 +109,8 @@
"base": false
}
]
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -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",

View file

@ -108,7 +108,8 @@
"bonus": -2,
"type": "attack"
},
"range": ""
"range": "",
"type": "attack"
}
},
"flags": {},

View file

@ -100,7 +100,8 @@
"base": false
}
]
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -96,7 +96,8 @@
"roll": {
"bonus": 1,
"type": "attack"
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -107,7 +107,8 @@
"base": false
}
]
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -103,7 +103,8 @@
"base": false
}
]
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -109,7 +109,8 @@
}
]
},
"img": "icons/weapons/bows/shortbow-recurve-yellow.webp"
"img": "icons/weapons/bows/shortbow-recurve-yellow.webp",
"type": "attack"
}
},
"flags": {},

View file

@ -103,7 +103,8 @@
"base": false
}
]
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -103,7 +103,8 @@
"base": false
}
]
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -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": {},

View file

@ -103,7 +103,8 @@
"base": false
}
]
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -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": {},

View file

@ -108,7 +108,8 @@
}
]
},
"img": "icons/weapons/polearms/spear-hooked-rounded.webp"
"img": "icons/weapons/polearms/spear-hooked-rounded.webp",
"type": "attack"
}
},
"flags": {},

View file

@ -111,7 +111,8 @@
"base": false
}
]
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -108,7 +108,8 @@
"base": false
}
]
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -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": {},

View file

@ -108,7 +108,8 @@
"base": false
}
]
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -103,7 +103,8 @@
"base": false
}
]
}
},
"type": "attack"
}
},
"flags": {},

View file

@ -107,7 +107,8 @@
}
]
},
"img": "icons/weapons/daggers/dagger-twin-green.webp"
"img": "icons/weapons/daggers/dagger-twin-green.webp",
"type": "attack"
}
},
"flags": {},

View file

@ -108,7 +108,8 @@
"base": false
}
]
}
},
"type": "attack"
}
},
"flags": {},

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