Fix conflicts

This commit is contained in:
Dapoolp 2025-06-15 13:39:35 +02:00
commit 4a044db77f
35 changed files with 1148 additions and 290 deletions

View file

@ -106,8 +106,13 @@ Hooks.once('init', () => {
Hooks.on('ready', () => {
ui.resources = new CONFIG.ui.resources();
if(game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.DisplayFear) !== 'hide') ui.resources.render({ force: true });
document.body.classList.toggle('theme-colorful', game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance).dualityColorScheme === DualityRollColor.colorful.value);
if (game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.Resources.DisplayFear) !== 'hide')
ui.resources.render({ force: true });
document.body.classList.toggle(
'theme-colorful',
game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.appearance).dualityColorScheme ===
DualityRollColor.colorful.value
);
});
Hooks.once('dicesoniceready', () => {});
@ -269,6 +274,8 @@ const preloadHandlebarsTemplates = async function () {
'systems/daggerheart/templates/sheets/character/sections/loadout.hbs',
'systems/daggerheart/templates/sheets/character/parts/heritageCard.hbs',
'systems/daggerheart/templates/sheets/character/parts/advancementCard.hbs',
'systems/daggerheart/templates/sheets/items/subclass/parts/subclass-features.hbs',
'systems/daggerheart/templates/sheets/items/subclass/parts/subclass-feature.hbs',
'systems/daggerheart/templates/components/card-preview.hbs',
'systems/daggerheart/templates/views/levelup/parts/selectable-card-preview.hbs',
'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs',

View file

@ -264,6 +264,7 @@
}
},
"Tiers": {
"singular": "Tier",
"tier1": "Tier 1",
"tier2": "Tier 2",
"tier3": "Tier 3",
@ -630,7 +631,7 @@
"WeaponFeature": {
"Barrier": {
"Name": "Barrier",
"Description": "+{armorBonus} to Armor Score; -1 to Evasion"
"Description": "+{armorScore} to Armor Score; -1 to Evasion"
},
"Bonded": {
"Name": "Bonded",
@ -700,6 +701,10 @@
"Name": "Greedy",
"Description": "Spend a handful of gold to gain a +1 bonus to your Proficiency on a damage roll."
},
"Healing": {
"Name": "Healing",
"Description": "During downtime, automatically clear a Hit Point."
},
"Heavy": {
"Name": "Heavy",
"Description": "-1 to Evasion"
@ -724,6 +729,10 @@
"Name": "Locked On",
"Description": "On a successful attack, your next attack against the same target with your primary weapon automatically succeeds."
},
"Lucky": {
"Name": "Lucky",
"Description": "On a failed attack, you can mark a Stress to reroll your attack."
},
"Long": {
"Name": "Long",
"Description": "This weapon's attack targets all adversaries in a line within range."
@ -758,7 +767,7 @@
},
"Protective": {
"Name": "Protective",
"Description": "+{armorBonus} to Armor Score"
"Description": "+{tier} to Armor Score"
},
"Quick": {
"Name": "Quick",
@ -824,7 +833,8 @@
"Input": "Input",
"Dice": "Dice"
},
"Max": "Max"
"Max": "Max",
"NewEffect": "New Effect"
},
"FeatureType": {
"Normal": "Normal",
@ -1144,6 +1154,8 @@
"Appearance": "Appearance",
"settings": "Settings"
},
"HopeFeatures": "Hope Features",
"Class Features": "Class Features",
"Domains": "Domains",
"DamageThresholds": {
"Title": "Damage Thresholds",
@ -1193,8 +1205,9 @@
}
}
},
"Heritage": {
"Title": "Abilities"
"Global": {
"Actions": "Actions",
"Effects": "Effects"
},
"DomainCard": {
"Type": "Type",
@ -1252,7 +1265,9 @@
"Description": "Description",
"SubclassFeature": {
"Description": "Description",
"Abilities": "Abilities"
"Abilities": "Abilities",
"Actions": "Actions",
"Effects": "Effects"
}
},
"Weapon": {

View file

@ -86,11 +86,11 @@ export default class DHActionConfig extends DaggerheartSheet(ApplicationV2) {
static async updateForm(event, _, formData) {
const submitData = this._prepareSubmitData(event, formData),
data = foundry.utils.expandObject(foundry.utils.mergeObject(this.action.toObject(), submitData)),
newActions = this.action.parent.actions.map(x => x.toObject()); // Find better way
newActions = foundry.utils.getProperty(this.action.parent, this.action.systemPath).map(x => x.toObject()); // Find better way
if (!newActions.findSplice(x => x._id === data._id, data)) newActions.push(data);
const updates = await this.action.parent.parent.update({ 'system.actions': newActions });
const updates = await this.action.parent.parent.update({ [`system.${this.action.systemPath}`]: newActions });
if (!updates) return;
this.action = updates.system.actions[this.action.index];
this.action = foundry.utils.getProperty(updates.system, this.action.systemPath)[this.action.index];
this.render();
}

View file

@ -1,88 +1,13 @@
import DaggerheartSheet from '../daggerheart-sheet.mjs';
import DHHeritageSheetV2 from './heritage.mjs';
const { ItemSheetV2 } = foundry.applications.sheets;
export default class AncestrySheet extends DaggerheartSheet(ItemSheetV2) {
export default class AncestrySheet extends DHHeritageSheetV2(ItemSheetV2) {
static DEFAULT_OPTIONS = {
tag: 'form',
classes: ['daggerheart', 'sheet', 'item', 'dh-style', 'ancestry'],
position: { width: 450, height: 700 },
actions: {
editFeature: this.editFeature,
deleteFeature: this.deleteFeature
},
form: {
handler: this.updateForm,
submitOnChange: true,
closeOnSubmit: false
},
dragDrop: [{ dragSelector: null, dropSelector: null }]
classes: ['ancestry']
};
static PARTS = {
header: { template: 'systems/daggerheart/templates/sheets/items/ancestry/header.hbs' },
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
description: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-description.hbs' },
features: {
template: 'systems/daggerheart/templates/sheets/global/tabs/tab-feature-section.hbs',
scrollable: ['.features']
}
...super.PARTS
};
static TABS = {
description: {
active: true,
cssClass: '',
group: 'primary',
id: 'description',
icon: null,
label: 'DAGGERHEART.Sheets.Feature.Tabs.Description'
},
features: {
active: false,
cssClass: '',
group: 'primary',
id: 'features',
icon: null,
label: 'DAGGERHEART.Sheets.Feature.Tabs.Features'
}
};
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
context.document = this.document;
context.tabs = super._getTabs(this.constructor.TABS);
return context;
}
static async updateForm(event, _, formData) {
await this.document.update(formData.object);
this.render();
}
static async editFeature(_, target) {
const feature = await fromUuid(target.dataset.feature);
feature.sheet.render(true);
}
static async deleteFeature(event, target) {
event.preventDefault();
event.stopPropagation();
await this.item.update({
'system.abilities': this.item.system.abilities.filter(x => x.uuid !== target.dataset.feature)
});
}
async _onDrop(event) {
const data = TextEditor.getDragEventData(event);
const item = await fromUuid(data.uuid);
if (item.type === 'feature' && item.system.type === SYSTEM.ITEM.featureTypes.ancestry.id) {
await this.document.update({
'system.abilities': [
...this.document.system.abilities,
{ img: item.img, name: item.name, uuid: item.uuid }
]
});
}
}
}

View file

@ -1,3 +1,5 @@
import { armorFeatures } from '../../../config/itemConfig.mjs';
import { tagifyElement } from '../../../helpers/utils.mjs';
import DHItemSheetV2 from '../item.mjs';
const { ItemSheetV2 } = foundry.applications.sheets;
@ -20,4 +22,28 @@ export default class ArmorSheet extends DHItemSheetV2(ItemSheetV2) {
scrollable: ['.settings']
}
};
async _preparePartContext(partId, context) {
super._preparePartContext(partId, context);
switch (partId) {
case 'settings':
context.features = this.document.system.features.map(x => x.value);
break;
}
return context;
}
_attachPartListeners(partId, htmlElement, options) {
super._attachPartListeners(partId, htmlElement, options);
const featureInput = htmlElement.querySelector('.features-input');
tagifyElement(featureInput, armorFeatures, this.onFeatureSelect.bind(this));
}
async onFeatureSelect(features) {
await this.document.update({ 'system.features': features.map(x => ({ value: x.value })) });
this.render(true);
}
}

View file

@ -1,8 +1,11 @@
import { actionsTypes } from '../../../data/_module.mjs';
import { tagifyElement } from '../../../helpers/utils.mjs';
import DHActionConfig from '../../config/Action.mjs';
import DaggerheartSheet from '../daggerheart-sheet.mjs';
const { ItemSheetV2 } = foundry.applications.sheets;
const { TextEditor } = foundry.applications.ux;
export default class ClassSheet extends DaggerheartSheet(ItemSheetV2) {
static DEFAULT_OPTIONS = {
tag: 'form',
@ -11,8 +14,9 @@ export default class ClassSheet extends DaggerheartSheet(ItemSheetV2) {
actions: {
removeSubclass: this.removeSubclass,
viewSubclass: this.viewSubclass,
deleteFeature: this.deleteFeature,
addFeature: this.addFeature,
editFeature: this.editFeature,
deleteFeature: this.deleteFeature,
removeItem: this.removeItem,
viewItem: this.viewItem,
removePrimaryWeapon: this.removePrimaryWeapon,
@ -151,6 +155,69 @@ export default class ClassSheet extends DaggerheartSheet(ItemSheetV2) {
await this.document.update({ 'system.characterGuide.suggestedArmor': null }, { diff: false });
}
async selectActionType() {
const content = await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/views/actionType.hbs',
{ types: SYSTEM.ACTIONS.actionTypes }
),
title = 'Select Action Type',
type = 'form',
data = {};
return Dialog.prompt({
title,
label: title,
content,
type,
callback: html => {
const form = html[0].querySelector('form'),
fd = new foundry.applications.ux.FormDataExtended(form);
foundry.utils.mergeObject(data, fd.object, { inplace: true });
return data;
},
rejectClose: false
});
}
getActionPath(type) {
return type === 'hope' ? 'hopeFeatures' : 'classFeatures';
}
static async addFeature(_, target) {
const actionPath = this.getActionPath(target.dataset.type);
const actionType = await this.selectActionType();
const cls = actionsTypes[actionType?.type] ?? actionsTypes.attack,
action = new cls(
{
_id: foundry.utils.randomID(),
systemPath: actionPath,
type: actionType.type,
name: game.i18n.localize(SYSTEM.ACTIONS.actionTypes[actionType.type].name),
...cls.getSourceConfig(this.document)
},
{
parent: this.document
}
);
await this.document.update({ [`system.${actionPath}`]: [...this.document.system[actionPath], action] });
}
static async editFeature(_, target) {
const action = this.document.system[this.getActionPath(target.dataset.type)].find(
x => x._id === target.dataset.feature
);
await new DHActionConfig(action).render(true);
}
static async deleteFeature(_, target) {
const actionPath = this.getActionPath(target.dataset.type);
await this.document.update({
[`system.${actionPath}`]: this.document.system[actionPath].filter(
action => action._id !== target.dataset.feature
)
});
}
async _onDrop(event) {
const data = TextEditor.getDragEventData(event);
const item = await fromUuid(data.uuid);
@ -158,10 +225,6 @@ export default class ClassSheet extends DaggerheartSheet(ItemSheetV2) {
await this.document.update({
'system.subclasses': [...this.document.system.subclasses.map(x => x.uuid), item.uuid]
});
} else if (item.type === 'feature') {
await this.document.update({
'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid]
});
} else if (item.type === 'weapon') {
if (event.currentTarget.classList.contains('primary-weapon-section')) {
if (!this.document.system.characterGuide.suggestedPrimaryWeapon && !item.system.secondary)

View file

@ -1,88 +1,13 @@
import DaggerheartSheet from '../daggerheart-sheet.mjs';
import DHHeritageSheetV2 from './heritage.mjs';
const { ItemSheetV2 } = foundry.applications.sheets;
export default class CommunitySheet extends DaggerheartSheet(ItemSheetV2) {
export default class CommunitySheet extends DHHeritageSheetV2(ItemSheetV2) {
static DEFAULT_OPTIONS = {
tag: 'form',
classes: ['daggerheart', 'sheet', 'item', 'dh-style', 'community'],
position: { width: 450, height: 700 },
actions: {
editFeature: this.editFeature,
deleteFeature: this.deleteFeature
},
form: {
handler: this.updateForm,
submitOnChange: true,
closeOnSubmit: false
},
dragDrop: [{ dragSelector: null, dropSelector: null }]
classes: ['community']
};
static PARTS = {
header: { template: 'systems/daggerheart/templates/sheets/items/community/header.hbs' },
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
description: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-description.hbs' },
features: {
template: 'systems/daggerheart/templates/sheets/global/tabs/tab-feature-section.hbs',
scrollable: ['.features']
}
...super.PARTS
};
static TABS = {
description: {
active: true,
cssClass: '',
group: 'primary',
id: 'description',
icon: null,
label: 'DAGGERHEART.Sheets.Feature.Tabs.Description'
},
features: {
active: false,
cssClass: '',
group: 'primary',
id: 'features',
icon: null,
label: 'DAGGERHEART.Sheets.Feature.Tabs.Features'
}
};
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
context.document = this.document;
context.tabs = super._getTabs(this.constructor.TABS);
return context;
}
static async updateForm(event, _, formData) {
await this.document.update(formData.object);
this.render();
}
static async editFeature(_, target) {
const feature = await fromUuid(target.dataset.feature);
feature.sheet.render(true);
}
static async deleteFeature(event, target) {
event.preventDefault();
event.stopPropagation();
await this.item.update({
'system.abilities': this.item.system.abilities.filter(x => x.uuid !== target.dataset.feature)
});
}
async _onDrop(event) {
const data = TextEditor.getDragEventData(event);
const item = await fromUuid(data.uuid);
if (item.type === 'feature' && item.system.type === SYSTEM.ITEM.featureTypes.community.id) {
await this.document.update({
'system.abilities': [
...this.document.system.abilities,
{ img: item.img, name: item.name, uuid: item.uuid }
]
});
}
}
}

View file

@ -0,0 +1,147 @@
import { actionsTypes } from '../../../data/_module.mjs';
import DHActionConfig from '../../config/Action.mjs';
import DHItemMixin from '../item.mjs';
export default function DHHeritageMixin(Base) {
return class DHHeritageSheetV2 extends DHItemMixin(Base) {
static DEFAULT_OPTIONS = {
tag: 'form',
position: { width: 450, height: 700 },
actions: {
addAction: this.addAction,
editAction: this.editAction,
removeAction: this.removeAction,
addEffect: this.addEffect,
editEffect: this.editEffect,
removeEffect: this.removeEffect
},
form: {
handler: this.updateForm,
submitOnChange: true,
closeOnSubmit: false
}
};
static PARTS = {
tabs: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-navigation.hbs' },
description: { template: 'systems/daggerheart/templates/sheets/global/tabs/tab-description.hbs' },
actions: {
template: 'systems/daggerheart/templates/sheets/global/tabs/tab-actions.hbs',
scrollable: ['.actions']
},
effects: {
template: 'systems/daggerheart/templates/sheets/global/tabs/tab-effects.hbs',
scrollable: ['.effects']
}
};
static TABS = {
description: {
active: true,
cssClass: '',
group: 'primary',
id: 'description',
icon: null,
label: 'DAGGERHEART.Sheets.Feature.Tabs.Description'
},
actions: {
active: false,
cssClass: '',
group: 'primary',
id: 'actions',
icon: null,
label: 'DAGGERHEART.Sheets.Feature.Tabs.Actions'
},
effects: {
active: false,
cssClass: '',
group: 'primary',
id: 'effects',
icon: null,
label: 'DAGGERHEART.Sheets.Feature.Tabs.Effects'
}
};
async _prepareContext(_options) {
const context = await super._prepareContext(_options);
context.document = this.document;
context.tabs = super._getTabs(this.constructor.TABS);
return context;
}
static async updateForm(event, _, formData) {
await this.document.update(formData.object);
this.render();
}
async selectActionType() {
const content = await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/views/actionType.hbs',
{ types: SYSTEM.ACTIONS.actionTypes }
),
title = 'Select Action Type',
type = 'form',
data = {};
return Dialog.prompt({
title,
label: title,
content,
type,
callback: html => {
const form = html[0].querySelector('form'),
fd = new foundry.applications.ux.FormDataExtended(form);
foundry.utils.mergeObject(data, fd.object, { inplace: true });
return data;
},
rejectClose: false
});
}
static async addAction() {
const actionType = await this.selectActionType();
const cls = actionsTypes[actionType?.type] ?? actionsTypes.attack,
action = new cls(
{
_id: foundry.utils.randomID(),
type: actionType.type,
name: game.i18n.localize(SYSTEM.ACTIONS.actionTypes[actionType.type].name),
...cls.getSourceConfig(this.document)
},
{
parent: this.document
}
);
await this.document.update({ 'system.actions': [...this.document.system.actions, action] });
}
static async editAction(_, button) {
const action = this.document.system.actions[button.dataset.index];
await new DHActionConfig(action).render(true);
}
static async removeAction(event, button) {
event.stopPropagation();
await this.document.update({
'system.actions': this.document.system.actions.filter(
(_, index) => index !== Number.parseInt(button.dataset.index)
)
});
}
static async addEffect() {
await this.document.createEmbeddedDocuments('ActiveEffect', [
{ name: game.i18n.localize('DAGGERHEART.Feature.NewEffect') }
]);
}
static async editEffect(_, target) {
const effect = this.document.effects.get(target.dataset.effect);
effect.sheet.render(true);
}
static async removeEffect(_, target) {
await this.document.effects.get(target.dataset.effect).delete();
}
};
}

View file

@ -1,28 +1,24 @@
import DaggerheartSheet from '../daggerheart-sheet.mjs';
import { actionsTypes } from '../../../data/_module.mjs';
import DHActionConfig from '../../config/Action.mjs';
import DhpApplicationMixin from '../daggerheart-sheet.mjs';
const { ItemSheetV2 } = foundry.applications.sheets;
const { TextEditor } = foundry.applications.ux;
const { duplicate, getProperty } = foundry.utils;
export default class SubclassSheet extends DaggerheartSheet(ItemSheetV2) {
export default class SubclassSheet extends DhpApplicationMixin(ItemSheetV2) {
static DEFAULT_OPTIONS = {
tag: 'form',
classes: ['daggerheart', 'sheet', 'item', 'dh-style', 'subclass'],
position: { width: 600 },
window: { resizable: false },
actions: {
editAbility: this.editAbility,
deleteFeatureAbility: this.deleteFeatureAbility
addFeature: this.addFeature,
editFeature: this.editFeature,
deleteFeature: this.deleteFeature
},
form: {
handler: this.updateForm,
submitOnChange: true,
closeOnSubmit: false
},
dragDrop: [
{ dragSelector: null, dropSelector: '.foundation-tab' },
{ dragSelector: null, dropSelector: '.specialization-tab' },
{ dragSelector: null, dropSelector: '.mastery-tab' }
]
}
};
static PARTS = {
@ -80,41 +76,99 @@ export default class SubclassSheet extends DaggerheartSheet(ItemSheetV2) {
this.render();
}
static async editAbility(_, button) {
const feature = await fromUuid(button.dataset.ability);
feature.sheet.render(true);
static addFeature(_, target) {
if (target.dataset.type === 'action') this.addAction(target.dataset.level);
else this.addEffect(target.dataset.level);
}
static async deleteFeatureAbility(event, button) {
event.preventDefault();
event.stopPropagation();
const feature = button.dataset.feature;
const newAbilities = this.document.system[`${feature}Feature`].abilities.filter(
x => x.uuid !== button.dataset.ability
);
const path = `system.${feature}Feature.abilities`;
await this.document.update({ [path]: newAbilities });
static async editFeature(_, target) {
if (target.dataset.type === 'action') this.editAction(target.dataset.level, target.dataset.feature);
else this.editEffect(target.dataset.feature);
}
async _onDrop(event) {
event.preventDefault();
const data = TextEditor.getDragEventData(event);
const item = await fromUuid(data.uuid);
if (!(item.type === 'feature' && item.system.type === SYSTEM.ITEM.featureTypes.subclass.id)) return;
static async deleteFeature(_, target) {
if (target.dataset.type === 'action') this.removeAction(target.dataset.level, target.dataset.feature);
else this.removeEffect(target.dataset.level, target.dataset.feature);
}
let featureField;
if (event.currentTarget.classList.contains('foundation-tab')) featureField = 'foundation';
else if (event.currentTarget.classList.contains('specialization-tab')) featureField = 'specialization';
else if (event.currentTarget.classList.contains('mastery-tab')) featureField = 'mastery';
else return;
async #selectActionType() {
const content = await foundry.applications.handlebars.renderTemplate(
'systems/daggerheart/templates/views/actionType.hbs',
{ types: SYSTEM.ACTIONS.actionTypes }
),
title = 'Select Action Type',
type = 'form',
data = {};
return Dialog.prompt({
title,
label: title,
content,
type,
callback: html => {
const form = html[0].querySelector('form'),
fd = new foundry.applications.ux.FormDataExtended(form);
foundry.utils.mergeObject(data, fd.object, { inplace: true });
return data;
},
rejectClose: false
});
}
const path = `system.${featureField}Feature.abilities`;
const abilities = duplicate(getProperty(this.document, path)) || [];
const featureData = { name: item.name, img: item.img, uuid: item.uuid };
abilities.push(featureData);
async addAction(level) {
const actionType = await this.#selectActionType();
const cls = actionsTypes[actionType?.type] ?? actionsTypes.attack,
action = new cls(
{
_id: foundry.utils.randomID(),
systemPath: `${level}.actions`,
type: actionType.type,
name: game.i18n.localize(SYSTEM.ACTIONS.actionTypes[actionType.type].name),
...cls.getSourceConfig(this.document)
},
{
parent: this.document
}
);
await this.document.update({ [`system.${level}.actions`]: [...this.document.system[level].actions, action] });
await new DHActionConfig(
this.document.system[level].actions[this.document.system[level].actions.length - 1]
).render(true);
}
await this.document.update({ [path]: abilities });
async addEffect(level) {
const embeddedItems = await this.document.createEmbeddedDocuments('ActiveEffect', [
{ name: game.i18n.localize('DAGGERHEART.Feature.NewEffect') }
]);
await this.document.update({
[`system.${level}.effects`]: [
...this.document.system[level].effects.map(x => x.uuid),
embeddedItems[0].uuid
]
});
}
async editAction(level, id) {
const action = this.document.system[level].actions.find(x => x._id === id);
await new DHActionConfig(action).render(true);
}
async editEffect(id) {
const effect = this.document.effects.get(id);
effect.sheet.render(true);
}
async removeAction(level, id) {
await this.document.update({
[`system.${level}.actions`]: this.document.system[level].actions.filter(action => action._id !== id)
});
}
async removeEffect(level, id) {
await this.document.effects.get(id).delete();
await this.document.update({
[`system.${level}.effects`]: this.document.system[level].effects
.filter(x => x && x.id !== id)
.map(effect => effect.uuid)
});
}
}

View file

@ -1,3 +1,5 @@
import { weaponFeatures } from '../../../config/itemConfig.mjs';
import { tagifyElement } from '../../../helpers/utils.mjs';
import DHItemSheetV2 from '../item.mjs';
const { ItemSheetV2 } = foundry.applications.sheets;
@ -19,4 +21,28 @@ export default class WeaponSheet extends DHItemSheetV2(ItemSheetV2) {
scrollable: ['.settings']
}
};
async _preparePartContext(partId, context) {
super._preparePartContext(partId, context);
switch (partId) {
case 'settings':
context.features = this.document.system.features.map(x => x.value);
break;
}
return context;
}
_attachPartListeners(partId, htmlElement, options) {
super._attachPartListeners(partId, htmlElement, options);
const featureInput = htmlElement.querySelector('.features-input');
tagifyElement(featureInput, weaponFeatures, this.onFeatureSelect.bind(this));
}
async onFeatureSelect(features) {
await this.document.update({ 'system.features': features.map(x => ({ value: x.value })) });
this.render(true);
}
}

View file

@ -5,15 +5,78 @@ export const armorFeatures = {
},
channeling: {
label: 'DAGGERHEART.ArmorFeature.Channeling.Name',
description: 'DAGGERHEART.ArmorFeature.Channeling.Description'
description: 'DAGGERHEART.ArmorFeature.Channeling.Description',
effects: [
{
changes: [
{
key: 'system.bonuses.spellcast',
mode: 2,
value: '1'
}
]
}
]
},
difficult: {
label: 'DAGGERHEART.ArmorFeature.Difficult.Name',
description: 'DAGGERHEART.ArmorFeature.Difficult.Description'
description: 'DAGGERHEART.ArmorFeature.Difficult.Description',
effects: [
{
changes: [
{
key: 'system.traits.agility.bonus',
mode: 2,
value: '-1'
},
{
key: 'system.traits.strength.bonus',
mode: 2,
value: '-1'
},
{
key: 'system.traits.finesse.bonus',
mode: 2,
value: '-1'
},
{
key: 'system.traits.instinct.bonus',
mode: 2,
value: '-1'
},
{
key: 'system.traits.presence.bonus',
mode: 2,
value: '-1'
},
{
key: 'system.traits.knowledge.bonus',
mode: 2,
value: '-1'
},
{
key: 'system.evasion.bonus',
mode: 2,
value: '-1'
}
]
}
]
},
flexible: {
label: 'DAGGERHEART.ArmorFeature.Flexible.Name',
description: 'DAGGERHEART.ArmorFeature.Flexible.Description'
description: 'DAGGERHEART.ArmorFeature.Flexible.Description',
effects: [
{
changes: [
{
key: 'system.evasion.bonus',
mode: 2,
value: '1'
}
]
}
]
},
fortified: {
label: 'DAGGERHEART.ArmorFeature.Fortified.Name',
@ -21,11 +84,33 @@ export const armorFeatures = {
},
gilded: {
label: 'DAGGERHEART.ArmorFeature.Gilded.Name',
description: 'DAGGERHEART.ArmorFeature.Gilded.Description'
description: 'DAGGERHEART.ArmorFeature.Gilded.Description',
effects: [
{
changes: [
{
key: 'system.traits.presence.bonus',
mode: 2,
value: '1'
}
]
}
]
},
heavy: {
label: 'DAGGERHEART.ArmorFeature.Heavy.Name',
description: 'DAGGERHEART.ArmorFeature.Heavy.Description'
description: 'DAGGERHEART.ArmorFeature.Heavy.Description',
effects: [
{
changes: [
{
key: 'system.evasion.bonus',
mode: 2,
value: '-1'
}
]
}
]
},
hopeful: {
label: 'DAGGERHEART.ArmorFeature.Hopeful.Name',
@ -77,7 +162,23 @@ export const armorFeatures = {
},
veryheavy: {
label: 'DAGGERHEART.ArmorFeature.VeryHeavy.Name',
description: 'DAGGERHEART.ArmorFeature.VeryHeavy.Description'
description: 'DAGGERHEART.ArmorFeature.VeryHeavy.Description',
effects: [
{
changes: [
{
key: 'system.evasion.bonus',
mode: 2,
value: '-2'
},
{
key: 'system.traits.agility.bonus',
mode: 2,
value: '-1'
}
]
}
]
},
warded: {
label: 'DAGGERHEART.ArmorFeature.Warded.Name',
@ -89,13 +190,41 @@ export const weaponFeatures = {
barrier: {
label: 'DAGGERHEART.WeaponFeature.Barrier.Name',
description: 'DAGGERHEART.WeaponFeature.Barrier.Description',
override: {
armorBonus: 1
}
effects: [
{
changes: [
{
key: 'system.bonuses.armorScore',
mode: 2,
value: '@system.tier + 1'
}
]
},
{
changes: [
{
key: 'system.evasion.bonus',
mode: 2,
value: '-1'
}
]
}
]
},
bonded: {
label: 'DAGGERHEART.WeaponFeature.Bonded.Name',
description: 'DAGGERHEART.WeaponFeature.Bonded.Description'
description: 'DAGGERHEART.WeaponFeature.Bonded.Description',
effects: [
{
changes: [
{
key: 'system.bonuses.damage',
mode: 2,
value: 'system.levelData.levels.current'
}
]
}
]
},
bouncing: {
label: 'DAGGERHEART.WeaponFeature.Bouncing.Name',
@ -103,7 +232,27 @@ export const weaponFeatures = {
},
brave: {
label: 'DAGGERHEART.WeaponFeature.Brave.Name',
description: 'DAGGERHEART.WeaponFeature.Brave.Description'
description: 'DAGGERHEART.WeaponFeature.Brave.Description',
effects: [
{
changes: [
{
key: 'system.evasion.bonus',
mode: 2,
value: '-1'
}
]
},
{
changes: [
{
key: 'system.damageThresholds.severe',
mode: 2,
value: '3'
}
]
}
]
},
brutal: {
label: 'DAGGERHEART.WeaponFeature.Brutal.Name',
@ -111,15 +260,55 @@ export const weaponFeatures = {
},
charged: {
label: 'DAGGERHEART.WeaponFeature.Charged.Name',
description: 'DAGGERHEART.WeaponFeature.Charged.Description'
description: 'DAGGERHEART.WeaponFeature.Charged.Description',
actions: [
{
type: 'effect',
name: 'DAGGERHEART.WeaponFeature.Concussive.Name',
img: 'icons/skills/melee/shield-damaged-broken-brown.webp',
actionType: 'action',
cost: [
{
type: 'stress',
value: 1
}
]
// Should add an effect with path system.proficiency.bonus +1
}
]
},
concussive: {
label: 'DAGGERHEART.WeaponFeature.Concussive.Name',
description: 'DAGGERHEART.WeaponFeature.Concussive.Description'
description: 'DAGGERHEART.WeaponFeature.Concussive.Description',
actions: [
{
type: 'resource',
name: 'DAGGERHEART.WeaponFeature.Concussive.Name',
img: 'icons/skills/melee/shield-damaged-broken-brown.webp',
actionType: 'action',
cost: [
{
type: 'hope',
value: 1
}
]
}
]
},
cumbersome: {
label: 'DAGGERHEART.WeaponFeature.Cumbersome.Name',
description: 'DAGGERHEART.WeaponFeature.Cumbersome.Description'
description: 'DAGGERHEART.WeaponFeature.Cumbersome.Description',
effects: [
{
changes: [
{
key: 'system.traits.finesse.bonus',
mode: 2,
value: '-1'
}
]
}
]
},
deadly: {
label: 'DAGGERHEART.WeaponFeature.Deadly.Name',
@ -128,18 +317,64 @@ export const weaponFeatures = {
deflecting: {
label: 'DAGGERHEART.WeaponFeature.Deflecting.Name',
description: 'DAGGERHEART.WeaponFeature.Deflecting.Description'
// actions: [{
// type: 'effect',
// name: 'DAGGERHEART.WeaponFeature.Deflecting.Name',
// img: 'icons/skills/melee/strike-flail-destructive-yellow.webp',
// actionType: 'reaction',
// cost: [{
// type: 'armorSlot', // Needs armorSlot as type
// value: 1
// }],
// }],
},
destructive: {
label: 'DAGGERHEART.WeaponFeature.Destructive.Name',
description: 'DAGGERHEART.WeaponFeature.Destructive.Description'
description: 'DAGGERHEART.WeaponFeature.Destructive.Description',
effects: [
{
changes: [
{
key: 'system.traits.agility.bonus',
mode: 2,
value: '-1'
}
]
}
]
},
devastating: {
label: 'DAGGERHEART.WeaponFeature.Devastating.Name',
description: 'DAGGERHEART.WeaponFeature.Devastating.Description'
description: 'DAGGERHEART.WeaponFeature.Devastating.Description',
actions: [
{
type: 'resource',
name: 'DAGGERHEART.WeaponFeature.Devastating.Name',
img: 'icons/skills/melee/strike-flail-destructive-yellow.webp',
actionType: 'action',
cost: [
{
type: 'stress',
value: 1
}
]
}
]
},
doubleduty: {
label: 'DAGGERHEART.WeaponFeature.DoubleDuty.Name',
description: 'DAGGERHEART.WeaponFeature.DoubleDuty.Description'
description: 'DAGGERHEART.WeaponFeature.DoubleDuty.Description',
effects: [
{
changes: [
{
key: 'system.bonuses.armorScore',
mode: 2,
value: '1'
}
]
}
]
},
doubledup: {
label: 'DAGGERHEART.WeaponFeature.DoubledUp.Name',
@ -155,15 +390,61 @@ export const weaponFeatures = {
},
grappling: {
label: 'DAGGERHEART.WeaponFeature.Grappling.Name',
description: 'DAGGERHEART.WeaponFeature.Grappling.Description'
description: 'DAGGERHEART.WeaponFeature.Grappling.Description',
actions: [
{
type: 'resource',
name: 'DAGGERHEART.WeaponFeature.Grappling.Name',
img: 'icons/magic/control/debuff-chains-ropes-net-white.webp',
actionType: 'action',
cost: [
{
type: 'stress',
value: 1
}
]
}
]
},
greedy: {
label: 'DAGGERHEART.WeaponFeature.Greedy.Name',
description: 'DAGGERHEART.WeaponFeature.Greedy.Description'
},
healing: {
label: 'DAGGERHEART.WeaponFeature.Healing.Name',
description: 'DAGGERHEART.WeaponFeature.Healing.Description',
actions: [
{
type: 'healing',
name: 'DAGGERHEART.WeaponFeature.Healing.Name',
img: 'icons/magic/life/cross-beam-green.webp',
actionType: 'action',
healing: {
type: 'health',
value: {
custom: {
enabled: true,
formula: '1'
}
}
}
}
]
},
heavy: {
label: 'DAGGERHEART.WeaponFeature.Heavy.Name',
description: 'DAGGERHEART.WeaponFeature.Heavy.Description'
description: 'DAGGERHEART.WeaponFeature.Heavy.Description',
effects: [
{
changes: [
{
key: 'system.evasion.bonus',
mode: 2,
value: '-1'
}
]
}
]
},
hooked: {
label: 'DAGGERHEART.WeaponFeature.Hooked.Name',
@ -189,13 +470,56 @@ export const weaponFeatures = {
label: 'DAGGERHEART.WeaponFeature.Long.Name',
description: 'DAGGERHEART.WeaponFeature.Long.Description'
},
lucky: {
label: 'DAGGERHEART.WeaponFeature.Lucky.Name',
description: 'DAGGERHEART.WeaponFeature.Lucky.Description',
actions: [
{
type: 'resource',
name: 'DAGGERHEART.WeaponFeature.Lucky.Name',
img: 'icons/magic/control/buff-luck-fortune-green.webp',
actionType: 'action',
cost: [
{
type: 'stress',
value: 1
}
]
}
]
},
massive: {
label: 'DAGGERHEART.WeaponFeature.Massive.Name',
description: 'DAGGERHEART.WeaponFeature.Massive.Description'
description: 'DAGGERHEART.WeaponFeature.Massive.Description',
effects: [
{
changes: [
{
key: 'system.evasion.bonus',
mode: 2,
value: '-1'
}
]
}
]
},
painful: {
label: 'DAGGERHEART.WeaponFeature.Painful.Name',
description: 'DAGGERHEART.WeaponFeature.Painful.Description'
description: 'DAGGERHEART.WeaponFeature.Painful.Description',
actions: [
{
type: 'resource',
name: 'DAGGERHEART.WeaponFeature.Painful.Name',
img: 'icons/skills/wounds/injury-face-impact-orange.webp',
actionType: 'action',
cost: [
{
type: 'stress',
value: 1
}
]
}
]
},
paired: {
label: 'DAGGERHEART.WeaponFeature.Paired.Name',
@ -223,17 +547,50 @@ export const weaponFeatures = {
protective: {
label: 'DAGGERHEART.WeaponFeature.Protective.Name',
description: 'DAGGERHEART.WeaponFeature.Protective.Description',
override: {
armorBonus: 1
}
effects: [
{
changes: [
{
key: 'system.bonuses.armorScore',
mode: 2,
value: '@system.tier'
}
]
}
]
},
quick: {
label: 'DAGGERHEART.WeaponFeature.Quick.Name',
description: 'DAGGERHEART.WeaponFeature.Quick.Description'
description: 'DAGGERHEART.WeaponFeature.Quick.Description',
actions: [
{
type: 'resource',
name: 'DAGGERHEART.WeaponFeature.Quick.Name',
img: 'icons/skills/movement/arrow-upward-yellow.webp',
actionType: 'action',
cost: [
{
type: 'stress',
value: 1
}
]
}
]
},
reliable: {
label: 'DAGGERHEART.WeaponFeature.Reliable.Name',
description: 'DAGGERHEART.WeaponFeature.Reliable.Description'
description: 'DAGGERHEART.WeaponFeature.Reliable.Description',
effects: [
{
changes: [
{
key: 'system.bonuses.attack',
mode: 2,
value: 1
}
]
}
]
},
reloading: {
label: 'DAGGERHEART.WeaponFeature.Reloading.Name',
@ -265,7 +622,21 @@ export const weaponFeatures = {
},
startling: {
label: 'DAGGERHEART.WeaponFeature.Startling.Name',
description: 'DAGGERHEART.WeaponFeature.Startling.Description'
description: 'DAGGERHEART.WeaponFeature.Startling.Description',
actions: [
{
type: 'resource',
name: 'DAGGERHEART.WeaponFeature.Startling.Name',
img: 'icons/magic/control/fear-fright-mask-orange.webp',
actionType: 'action',
cost: [
{
type: 'stress',
value: 1
}
]
}
]
},
timebending: {
label: 'DAGGERHEART.WeaponFeature.Timebending.Name',

View file

@ -36,6 +36,7 @@ export class DHBaseAction extends foundry.abstract.DataModel {
static defineSchema() {
return {
_id: new fields.DocumentIdField(),
systemPath: new fields.StringField({ required: true, initial: 'actions' }),
type: new fields.StringField({ initial: undefined, readonly: true, required: true }),
name: new fields.StringField({ initial: undefined }),
description: new fields.HTMLField(),
@ -119,7 +120,7 @@ export class DHBaseAction extends foundry.abstract.DataModel {
prepareData() {}
get index() {
return this.parent.actions.indexOf(this);
return foundry.utils.getProperty(this.parent, this.systemPath).indexOf(this);
}
get item() {

View file

@ -23,7 +23,7 @@ export class DHActionDiceData extends foundry.abstract.DataModel {
getFormula(actor) {
return this.custom.enabled
? this.custom.formula
: `${actor.system[this.multiplier].value ?? 1}${this.dice}${this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : ''}`;
: `${actor.system[this.multiplier]?.total ?? 1}${this.dice}${this.bonus ? (this.bonus < 0 ? ` - ${Math.abs(this.bonus)}` : ` + ${this.bonus}`) : ''}`;
}
}

View file

@ -84,7 +84,12 @@ export default class DhCharacter extends BaseDataActor {
value: new ForeignDocumentUUIDField({ type: 'Item', nullable: true }),
subclass: new ForeignDocumentUUIDField({ type: 'Item', nullable: true })
}),
levelData: new fields.EmbeddedDataField(DhPCLevelData)
levelData: new fields.EmbeddedDataField(DhPCLevelData),
bonuses: new fields.SchemaField({
attack: new fields.NumberField({ integer: true, initial: 0 }),
spellcast: new fields.NumberField({ integer: true, initial: 0 }),
armorScore: new fields.NumberField({ integer: true, initial: 0 })
})
};
}

View file

@ -1,12 +1,13 @@
import ActionField from '../fields/actionField.mjs';
import BaseDataItem from './base.mjs';
export default class DHAncestry extends BaseDataItem {
/** @inheritDoc */
static get metadata() {
return foundry.utils.mergeObject(super.metadata, {
label: "TYPES.Item.ancestry",
type: "ancestry",
hasDescription: true,
label: 'TYPES.Item.ancestry',
type: 'ancestry',
hasDescription: true
});
}
@ -15,7 +16,7 @@ export default class DHAncestry extends BaseDataItem {
const fields = foundry.data.fields;
return {
...super.defineSchema(),
//TODO: add features field
actions: new fields.ArrayField(new ActionField())
};
}
}

View file

@ -1,14 +1,15 @@
import BaseDataItem from './base.mjs';
import ActionField from '../fields/actionField.mjs';
import { armorFeatures } from '../../config/itemConfig.mjs';
export default class DHArmor extends BaseDataItem {
/** @inheritDoc */
static get metadata() {
return foundry.utils.mergeObject(super.metadata, {
label: "TYPES.Item.armor",
type: "armor",
label: 'TYPES.Item.armor',
type: 'armor',
hasDescription: true,
isQuantifiable: true,
isQuantifiable: true
});
}
@ -17,9 +18,16 @@ export default class DHArmor extends BaseDataItem {
const fields = foundry.data.fields;
return {
...super.defineSchema(),
tier: new fields.NumberField({ required: true, integer: true, initial: 1, min: 1 }),
equipped: new fields.BooleanField({ initial: false }),
baseScore: new fields.NumberField({ integer: true, initial: 0 }),
feature: new fields.StringField({ choices: SYSTEM.ITEM.armorFeatures, blank: true }),
features: new fields.ArrayField(
new fields.SchemaField({
value: new fields.StringField({ required: true, choices: SYSTEM.ITEM.armorFeatures, blank: true }),
effectIds: new fields.ArrayField(new fields.StringField({ required: true })),
actionIds: new fields.ArrayField(new fields.StringField({ required: true }))
})
),
marks: new fields.SchemaField({
max: new fields.NumberField({ initial: 6, integer: true }),
value: new fields.NumberField({ initial: 0, integer: true })
@ -35,4 +43,47 @@ export default class DHArmor extends BaseDataItem {
get featureInfo() {
return this.feature ? CONFIG.daggerheart.ITEM.armorFeatures[this.feature] : null;
}
async _preUpdate(changes, options, user) {
const allowed = await super._preUpdate(changes, options, user);
if (allowed === false) return false;
if (changes.system.features) {
const removed = this.features.filter(x => !changes.system.features.includes(x));
const added = changes.system.features.filter(x => !this.features.includes(x));
for (var feature of removed) {
for (var effectId of feature.effectIds) {
await this.parent.effects.get(effectId).delete();
}
changes.system.actions = this.actions.filter(x => !feature.actionIds.includes(x._id));
}
for (var feature of added) {
const featureData = armorFeatures[feature.value];
if (featureData.effects?.length > 0) {
const embeddedItems = await this.parent.createEmbeddedDocuments('ActiveEffect', [
{
name: game.i18n.localize(featureData.label),
description: game.i18n.localize(featureData.description),
changes: featureData.effects.flatMap(x => x.changes)
}
]);
feature.effectIds = embeddedItems.map(x => x.id);
}
if (featureData.actions?.length > 0) {
const newActions = featureData.actions.map(action => {
const cls = actionsTypes[action.type];
return new cls(
{ ...action, _id: foundry.utils.randomID(), name: game.i18n.localize(action.name) },
{ parent: this }
);
});
changes.system.actions = [...this.actions, ...newActions];
feature.actionIds = newActions.map(x => x._id);
}
}
}
}
}

View file

@ -1,5 +1,6 @@
import BaseDataItem from './base.mjs';
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
import ActionField from '../fields/actionField.mjs';
export default class DHClass extends BaseDataItem {
/** @inheritDoc */
@ -19,7 +20,8 @@ export default class DHClass extends BaseDataItem {
domains: new fields.ArrayField(new fields.StringField(), { max: 2 }),
classItems: new fields.ArrayField(new ForeignDocumentUUIDField({ type: 'Item' })),
evasion: new fields.NumberField({ initial: 0, integer: true }),
features: new fields.ArrayField(new ForeignDocumentUUIDField({ type: 'Item' })),
hopeFeatures: new foundry.data.fields.ArrayField(new ActionField()),
classFeatures: new foundry.data.fields.ArrayField(new ActionField()),
subclasses: new fields.ArrayField(
new ForeignDocumentUUIDField({ type: 'Item', required: false, nullable: true, initial: undefined })
),
@ -51,6 +53,10 @@ export default class DHClass extends BaseDataItem {
};
}
get hopeFeature() {
return this.hopeFeatures.length > 0 ? this.hopeFeatures[0] : null;
}
async _preCreate(data, options, user) {
const allowed = await super._preCreate(data, options, user);
if (allowed === false) return;

View file

@ -1,22 +1,22 @@
import ActionField from '../fields/actionField.mjs';
import BaseDataItem from './base.mjs';
export default class DHCommunity extends BaseDataItem {
/** @inheritDoc */
static get metadata() {
return foundry.utils.mergeObject(super.metadata, {
label: "TYPES.Item.community",
type: "community",
hasDescription: true,
label: 'TYPES.Item.community',
type: 'community',
hasDescription: true
});
}
/** @inheritDoc */
static defineSchema() {
const fields = foundry.data.fields;
return {
...super.defineSchema(),
//TODO: add features field
actions: new fields.ArrayField(new ActionField())
};
}
}

View file

@ -1,6 +1,15 @@
import ActionField from '../fields/actionField.mjs';
import ForeignDocumentUUIDField from '../fields/foreignDocumentUUIDField.mjs';
import BaseDataItem from './base.mjs';
const featureSchema = () => {
return new foundry.data.fields.SchemaField({
name: new foundry.data.fields.StringField({ required: true }),
effects: new foundry.data.fields.ArrayField(new ForeignDocumentUUIDField({ type: 'ActiveEffect' })),
actions: new foundry.data.fields.ArrayField(new ActionField())
});
};
export default class DHSubclass extends BaseDataItem {
/** @inheritDoc */
static get metadata() {
@ -22,9 +31,9 @@ export default class DHSubclass extends BaseDataItem {
nullable: true,
initial: null
}),
foundationFeature: new ForeignDocumentUUIDField({ type: 'Item' }),
specializationFeature: new ForeignDocumentUUIDField({ type: 'Item' }),
masteryFeature: new ForeignDocumentUUIDField({ type: 'Item' }),
foundationFeature: featureSchema(),
specializationFeature: featureSchema(),
masteryFeature: featureSchema(),
featureState: new fields.NumberField({ required: true, initial: 1, min: 1 }),
isMulticlass: new fields.BooleanField({ initial: false })
};

View file

@ -1,8 +1,8 @@
import BaseDataItem from './base.mjs';
import FormulaField from '../fields/formulaField.mjs';
import PseudoDocumentsField from '../fields/pseudoDocumentsField.mjs';
import BaseFeatureData from '../pseudo-documents/feature/baseFeatureData.mjs';
import ActionField from '../fields/actionField.mjs';
import { weaponFeatures } from '../../config/itemConfig.mjs';
import { actionsTypes } from '../../data/_module.mjs';
export default class DHWeapon extends BaseDataItem {
/** @inheritDoc */
@ -23,6 +23,7 @@ export default class DHWeapon extends BaseDataItem {
const fields = foundry.data.fields;
return {
...super.defineSchema(),
tier: new fields.NumberField({ required: true, integer: true, initial: 1, min: 1 }),
equipped: new fields.BooleanField({ initial: false }),
//SETTINGS
@ -39,14 +40,57 @@ export default class DHWeapon extends BaseDataItem {
initial: 'physical'
})
}),
feature: new fields.StringField({ choices: SYSTEM.ITEM.weaponFeatures, blank: true }),
featureTest: new PseudoDocumentsField(BaseFeatureData, {
required: true,
nullable: true,
max: 1,
validTypes: ['weapon']
}),
features: new fields.ArrayField(
new fields.SchemaField({
value: new fields.StringField({ required: true, choices: SYSTEM.ITEM.weaponFeatures, blank: true }),
effectIds: new fields.ArrayField(new fields.StringField({ required: true })),
actionIds: new fields.ArrayField(new fields.StringField({ required: true }))
})
),
actions: new fields.ArrayField(new ActionField())
};
}
async _preUpdate(changes, options, user) {
const allowed = await super._preUpdate(changes, options, user);
if (allowed === false) return false;
if (changes.system.features) {
const removed = this.features.filter(x => !changes.system.features.includes(x));
const added = changes.system.features.filter(x => !this.features.includes(x));
for (var feature of removed) {
for (var effectId of feature.effectIds) {
await this.parent.effects.get(effectId).delete();
}
changes.system.actions = this.actions.filter(x => !feature.actionIds.includes(x._id));
}
for (var feature of added) {
const featureData = weaponFeatures[feature.value];
if (featureData.effects?.length > 0) {
const embeddedItems = await this.parent.createEmbeddedDocuments('ActiveEffect', [
{
name: game.i18n.localize(featureData.label),
description: game.i18n.localize(featureData.description),
changes: featureData.effects.flatMap(x => x.changes)
}
]);
feature.effectIds = embeddedItems.map(x => x.id);
}
if (featureData.actions?.length > 0) {
const newActions = featureData.actions.map(action => {
const cls = actionsTypes[action.type];
return new cls(
{ ...action, _id: foundry.utils.randomID(), name: game.i18n.localize(action.name) },
{ parent: this }
);
});
changes.system.actions = [...this.actions, ...newActions];
feature.actionIds = newActions.map(x => x._id);
}
}
}
}
}

View file

@ -1,4 +1,12 @@
export default class DhActiveEffect extends ActiveEffect {
get isSuppressed() {
if (['weapon', 'armor'].includes(this.parent.type)) {
return !this.parent.system.equipped;
}
return super.isSuppressed;
}
async _preCreate(data, options, user) {
const update = {};
if (!data.img) {
@ -11,4 +19,9 @@ export default class DhActiveEffect extends ActiveEffect {
await super._preCreate(data, options, user);
}
static applyField(model, change, field) {
change.value = Roll.safeEval(Roll.replaceFormulaData(change.value, change.effect.parent));
super.applyField(model, change, field);
}
}

View file

@ -3415,6 +3415,41 @@ div.daggerheart.views.multiclass {
.sheet.daggerheart.dh-style .tab.actions .actions-list .action-item .controls a {
text-shadow: none;
}
.sheet.daggerheart.dh-style .tab.effects .effects-list {
display: flex;
flex-direction: column;
list-style: none;
padding: 0;
margin: 0;
width: 100%;
gap: 5px;
}
.sheet.daggerheart.dh-style .tab.effects .effects-list .effect-item {
display: grid;
align-items: center;
grid-template-columns: 1fr 4fr 1fr;
cursor: pointer;
}
.sheet.daggerheart.dh-style .tab.effects .effects-list .effect-item h4 {
font-family: 'Montserrat', sans-serif;
font-weight: lighter;
color: #efe6d8;
}
.sheet.daggerheart.dh-style .tab.effects .effects-list .effect-item .image {
height: 40px;
width: 40px;
object-fit: cover;
border-radius: 6px;
border: none;
}
.sheet.daggerheart.dh-style .tab.effects .effects-list .effect-item .controls {
display: flex;
justify-content: center;
gap: 10px;
}
.sheet.daggerheart.dh-style .tab.effects .effects-list .effect-item .controls a {
text-shadow: none;
}
.application.sheet.daggerheart.dh-style .item-sheet-header {
display: flex;
}
@ -3544,7 +3579,6 @@ div.daggerheart.views.multiclass {
}
.sheet.daggerheart.dh-style.item .tab.features {
padding: 0 10px;
max-height: 265px;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: light-dark(#18162e, #f3c267) transparent;

View file

@ -29,6 +29,7 @@
@import './less/global/tab-navigation.less';
@import './less/global/tab-form-footer.less';
@import './less/global/tab-actions.less';
@import './less/global/tab-effects.less';
@import './less/global/item-header.less';
@import './less/global/feature-section.less';

View file

@ -4,7 +4,6 @@
.sheet.daggerheart.dh-style.item {
.tab.features {
padding: 0 10px;
max-height: 265px;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: light-dark(@dark-blue, @golden) transparent;

View file

@ -0,0 +1,46 @@
@import '../utils/colors.less';
@import '../utils/fonts.less';
.sheet.daggerheart.dh-style {
.tab.effects {
.effects-list {
display: flex;
flex-direction: column;
list-style: none;
padding: 0;
margin: 0;
width: 100%;
gap: 5px;
.effect-item {
display: grid;
align-items: center;
grid-template-columns: 1fr 4fr 1fr;
cursor: pointer;
h4 {
font-family: @font-body;
font-weight: lighter;
color: @beige;
}
.image {
height: 40px;
width: 40px;
object-fit: cover;
border-radius: 6px;
border: none;
}
.controls {
display: flex;
justify-content: center;
gap: 10px;
a {
text-shadow: none;
}
}
}
}
}
}

View file

@ -13,7 +13,7 @@
<li class="item inventory-item">
<div class="inventory-row" data-item-id="{{item.uuid}}">
<div class="inventory-item-title-container">
<div data-action="useItem" data-value="{{item.uuid}}" class="inventory-item-title">
<div data-action="viewObject" data-value="{{item.uuid}}" class="inventory-item-title">
<img src="{{item.img}}" />
{{item.name}}
</div>

View file

@ -9,7 +9,8 @@
<a
class='effect-control'
data-action='editFeature'
data-feature='{{feature.uuid}}'
data-feature='{{feature._id}}'
data-type='{{type}}'
data-tooltip='{{localize "DAGGERHEART.Tooltip.openItemWorld"}}'
>
<i class="fa-solid fa-globe"></i>
@ -17,7 +18,8 @@
<a
class='effect-control'
data-action='deleteFeature'
data-feature='{{feature.uuid}}'
data-feature='{{feature._id}}'
data-type='{{type}}'
data-tooltip='{{localize "DAGGERHEART.Tooltip.delete"}}'
>
<i class='fas fa-trash'></i>

View file

@ -4,7 +4,7 @@
data-group='{{tabs.actions.group}}'
>
<fieldset class="one-column">
<legend>{{localize "Actions"}} <a><i class="fa-solid fa-plus icon-button" data-action="addAction"></i></a></legend>
<legend>{{localize "DAGGERHEART.Sheets.Global.Actions"}} <a><i class="fa-solid fa-plus icon-button" data-action="addAction"></i></a></legend>
<div class="actions-list">
{{#each document.system.actions as |action index|}}
<div class="action-item">

View file

@ -0,0 +1,21 @@
<section
class='tab {{tabs.effects.cssClass}} {{tabs.effects.id}}'
data-tab='{{tabs.effects.id}}'
data-group='{{tabs.effects.group}}'
>
<fieldset class="one-column">
<legend>{{localize "DAGGERHEART.Sheets.Global.Effects"}} <a><i class="fa-solid fa-plus icon-button" data-action="addEffect"></i></a></legend>
<div class="effects-list">
{{#each document.effects as |effect|}}
<div class="effect-item">
<img class="image" src="{{effect.img}}" />
<span>{{effect.name}}</span>
<div class="controls">
<a data-action="editEffect" data-effect="{{effect.id}}"><i class="fa-solid fa-pen-to-square"></i></a>
<a data-action="removeEffect" data-effect="{{effect.id}}"><i class="fa-solid fa-trash"></i></a>
</div>
</div>
{{/each}}
</div>
</fieldset>
</section>

View file

@ -6,11 +6,13 @@
<fieldset class="two-columns">
<legend>{{localize tabs.settings.label}}</legend>
<span>{{localize "DAGGERHEART.Tiers.singular"}}</span>
{{formField systemFields.tier value=source.system.tier}}
<span>{{localize "DAGGERHEART.Sheets.Armor.baseScore"}}</span>
{{formField systemFields.baseScore value=source.system.baseScore}}
<span>{{localize "DAGGERHEART.Sheets.Armor.feature"}}</span>
{{formField systemFields.feature value=source.system.feature localize=true blank=""}}
<input type="text" class="features-input" value="{{features}}" />
<span>{{localize "DAGGERHEART.Sheets.Armor.baseThresholds.base"}}</span>
<div class="nest-inputs">

View file

@ -3,15 +3,25 @@
data-tab='{{tabs.features.id}}'
data-group='{{tabs.features.group}}'
>
<div class="two-columns even">
<fieldset>
<legend>{{localize "DAGGERHEART.Sheets.Class.HopeFeatures"}} <a><i class="fa-solid fa-plus icon-button" data-type="hope" data-action="addFeature"></i></a></legend>
<div class="feature-list">
{{#each source.system.hopeFeatures as |feature index|}}
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' type='hope' feature=feature}}
{{/each}}
</div>
</fieldset>
<fieldset>
<legend>{{localize "DAGGERHEART.Sheets.Feature.Tabs.Features"}}</legend>
<div class="feature-list">
{{#each source.system.features as |feature index|}}
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' feature=feature}}
{{/each}}
</div>
</fieldset>
<fieldset>
<legend>{{localize "DAGGERHEART.Sheets.Class.ClassFeatures"}} <a><i class="fa-solid fa-plus icon-button" data-type="class" data-action="addFeature"></i></a></legend>
<div class="feature-list">
{{#each source.system.classFeatures as |feature index|}}
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' type='class' feature=feature}}
{{/each}}
</div>
</fieldset>
</div>
<fieldset>
<legend>{{localize "TYPES.Item.subclass"}}</legend>

View file

@ -5,16 +5,16 @@
>
<fieldset>
<legend>{{localize "DAGGERHEART.Sheets.Subclass.Tabs.Foundation"}}</legend>
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' feature=source.system.foundationFeature}}
{{> 'systems/daggerheart/templates/sheets/items/subclass/parts/subclass-features.hbs' level='foundationFeature' feature=source.system.foundationFeature}}
</fieldset>
<fieldset>
<legend>{{localize "DAGGERHEART.Sheets.Subclass.Tabs.Specialization"}}</legend>
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' feature=source.system.specializationFeature}}
{{> 'systems/daggerheart/templates/sheets/items/subclass/parts/subclass-features.hbs' level='specializationFeature' feature=source.system.specializationFeature}}
</fieldset>
<fieldset>
<legend>{{localize "DAGGERHEART.Sheets.Subclass.Tabs.Mastery"}}</legend>
{{> 'systems/daggerheart/templates/sheets/global/partials/feature-section-item.hbs' feature=source.system.masteryFeature}}
{{> 'systems/daggerheart/templates/sheets/items/subclass/parts/subclass-features.hbs' level='masteryFeature' feature=source.system.masteryFeature}}
</fieldset>
</section>

View file

@ -0,0 +1,30 @@
<li class='feature-item'>
<div class='feature-line'>
<img class='image' src='{{feature.img}}' />
<h4>{{feature.name}}</h4>
{{#unless hideContrals}}
<div class='controls'>
<a
class='effect-control'
data-action='editFeature'
data-type="{{type}}"
data-level="{{level}}"
data-feature='{{id}}'
data-tooltip='{{localize "DAGGERHEART.Tooltip.openItemWorld"}}'
>
<i class="fa-solid fa-globe"></i>
</a>
<a
class='effect-control'
data-action='deleteFeature'
data-type="{{type}}"
data-level="{{level}}"
data-feature='{{id}}'
data-tooltip='{{localize "DAGGERHEART.Tooltip.delete"}}'
>
<i class='fas fa-trash'></i>
</a>
</div>
{{/unless}}
</div>
</li>

View file

@ -0,0 +1,22 @@
<div class="two-columns even">
<fieldset>
<legend>{{localize "DAGGERHEART.Sheets.Subclass.SubclassFeature.Actions"}} <a><i class="fa-solid fa-plus icon-button" data-level="{{level}}" data-type="action" data-action="addFeature"></i></a></legend>
<div class="feature-list">
{{#each feature.actions}}
{{> 'systems/daggerheart/templates/sheets/items/subclass/parts/subclass-feature.hbs' level=../level type="action" id=this._id feature=this}}
{{/each}}
</div>
</fieldset>
<fieldset>
<legend>{{localize "DAGGERHEART.Sheets.Subclass.SubclassFeature.Effects"}} <a><i class="fa-solid fa-plus icon-button" data-level="{{level}}" data-type="effect" data-action="addFeature"></i></a></legend>
<div class="feature-list">
{{#each feature.effects}}
{{> 'systems/daggerheart/templates/sheets/items/subclass/parts/subclass-feature.hbs' level=../level type="effect" id=this.id feature=this}}
{{/each}}
</div>
</fieldset>
</div>

View file

@ -5,6 +5,8 @@
>
<fieldset class="two-columns">
<legend>{{localize tabs.settings.label}}</legend>
<span>{{localize "DAGGERHEART.Tiers.singular"}}</span>
{{formField systemFields.tier value=source.system.tier}}
<span>{{localize "DAGGERHEART.Sheets.Weapon.SecondaryWeapon"}}</span>
{{formField systemFields.secondary value=source.system.secondary}}
<span>{{localize "DAGGERHEART.Sheets.Weapon.Trait"}}</span>
@ -25,6 +27,6 @@
<fieldset class="two-columns">
<legend>{{localize "DAGGERHEART.Sheets.Weapon.Feature"}}</legend>
<span>{{localize "DAGGERHEART.Sheets.Weapon.Feature"}}</span>
{{formField systemFields.feature value=source.system.feature localize=true}}
<input type="text" class="features-input" value="{{features}}" />
</fieldset>
</section>