This commit is contained in:
WBHarry 2025-07-20 19:54:53 +02:00
parent 28efef7951
commit 9cdfd7a27a
11 changed files with 151 additions and 69 deletions

View file

@ -1603,13 +1603,7 @@
"attackIsMissing": "Attack is missing", "attackIsMissing": "Attack is missing",
"unownedActionMacro": "Cannot make a Use macro for an Action not on your character", "unownedActionMacro": "Cannot make a Use macro for an Action not on your character",
"unownedAttackMacro": "Cannot make a Use macro for an Attack that doesn't belong to one of your characters", "unownedAttackMacro": "Cannot make a Use macro for an Attack that doesn't belong to one of your characters",
"featureNotHope": "This feature is used as something else than a Hope feature and cannot be used here.", "featureAlreadyLinked": "{name} is already linked to {origin}. Remove it from there if you want to link it elsewhere."
"featureNotClass": "This feature is used as something else than a Class feature and cannot be used here.",
"featureNotPrimary": "This feature is used as something else than a Primary feature and cannot be used here.",
"featureNotSecondary": "This feature is used as something else than a Secondary feature and cannot be used here.",
"featureNotFoundation": "This feature is used as something else than a Foundation feature and cannot be used here.",
"featureNotSpecialization": "This feature is used as something else than a Specialization feature and cannot be used here.",
"featureNotMastery": "This feature is used as something else than a Mastery feature and cannot be used here."
}, },
"Tooltip": { "Tooltip": {
"disableEffect": "Disable Effect", "disableEffect": "Disable Effect",

View file

@ -506,7 +506,12 @@ export default function DHApplicationMixin(Base) {
if (doc) return doc.sheet.render({ force: true }); if (doc) return doc.sheet.render({ force: true });
// TODO: REDO this // TODO: REDO this
const { actionId } = target.closest('[data-action-id]').dataset; const actionNode = target.closest('[data-action-id]');
if (!actionNode) {
return ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureIsMissing'));
}
const { actionId } = actionNode.dataset;
const { actions, attack } = this.document.system; const { actions, attack } = this.document.system;
const action = attack?.id === actionId ? attack : actions?.find(a => a.id === actionId); const action = attack?.id === actionId ? attack : actions?.find(a => a.id === actionId);
new DHActionConfig(action).render({ force: true }); new DHActionConfig(action).render({ force: true });

View file

@ -290,6 +290,16 @@ export default class DHBaseItemSheet extends DHApplicationMixin(ItemSheetV2) {
const item = await fromUuid(data.uuid); const item = await fromUuid(data.uuid);
if (item?.type === 'feature') { if (item?.type === 'feature') {
if (item.system.originId) {
const origin = await foundry.utils.fromUuid(item.system.originId);
return ui.notifications.warn(
game.i18n.format('DAGGERHEART.UI.Notifications.featureAlreadyLinked', {
name: item.name,
origin: origin.name
})
);
}
await item.update({ await item.update({
system: { system: {
originItemType: CONFIG.DH.ITEM.featureTypes[this.document.type].id, originItemType: CONFIG.DH.ITEM.featureTypes[this.document.type].id,

View file

@ -50,13 +50,17 @@ export default class AncestrySheet extends DHHeritageSheet {
const item = await fromUuid(data.uuid); const item = await fromUuid(data.uuid);
if (item?.type === 'feature') { if (item?.type === 'feature') {
const subType = event.target.closest('.primary-feature') ? 'primary' : 'secondary'; if (item.system.originId) {
if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes[subType]) { const origin = await foundry.utils.fromUuid(item.system.originId);
const error = subType === 'primary' ? 'featureNotPrimary' : 'featureNotSecondary'; return ui.notifications.warn(
ui.notifications.warn(game.i18n.localize(`DAGGERHEART.UI.Notifications.${error}`)); game.i18n.format('DAGGERHEART.UI.Notifications.featureAlreadyLinked', {
return; name: item.name,
origin: origin.name
})
);
} }
const subType = event.target.closest('.primary-feature') ? 'primary' : 'secondary';
await item.update({ await item.update({
system: { system: {
subType: subType, subType: subType,

View file

@ -83,16 +83,38 @@ export default class ClassSheet extends DHBaseItemSheet {
const item = await fromUuid(data.uuid); const item = await fromUuid(data.uuid);
const target = event.target.closest('fieldset.drop-section'); const target = event.target.closest('fieldset.drop-section');
if (item.type === 'subclass') { if (item.type === 'subclass') {
if (item.system.originId) {
const origin = await foundry.utils.fromUuid(item.system.originId);
return ui.notifications.warn(
game.i18n.format('DAGGERHEART.UI.Notifications.featureAlreadyLinked', {
name: item.name,
origin: origin.name
})
);
}
await item.update({
system: {
originItemType: CONFIG.DH.ITEM.featureTypes[this.document.type].id,
originId: this.document.uuid
}
});
await this.document.update({ await this.document.update({
'system.subclasses': [...this.document.system.subclasses.map(x => x.uuid), item.uuid] 'system.subclasses': [...this.document.system.subclasses.map(x => x.uuid), item.uuid]
}); });
} else if (item.type === 'feature') { } else if (item.type === 'feature') {
if (target.classList.contains('hope-feature')) { if (item.system.originId) {
if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes.hope) { const origin = await foundry.utils.fromUuid(item.system.originId);
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureNotHope')); return ui.notifications.warn(
return; game.i18n.format('DAGGERHEART.UI.Notifications.featureAlreadyLinked', {
} name: item.name,
origin: origin.name
})
);
}
if (target.classList.contains('hope-feature')) {
await item.update({ await item.update({
system: { system: {
subType: CONFIG.DH.ITEM.featureSubTypes.hope, subType: CONFIG.DH.ITEM.featureSubTypes.hope,
@ -104,11 +126,6 @@ export default class ClassSheet extends DHBaseItemSheet {
'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid] 'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid]
}); });
} else if (target.classList.contains('class-feature')) { } else if (target.classList.contains('class-feature')) {
if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes.class) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureNotClass'));
return;
}
await item.update({ await item.update({
system: { system: {
subType: CONFIG.DH.ITEM.featureSubTypes.class, subType: CONFIG.DH.ITEM.featureSubTypes.class,
@ -151,18 +168,20 @@ export default class ClassSheet extends DHBaseItemSheet {
} }
} else if (item.type === 'miscellaneous') { } else if (item.type === 'miscellaneous') {
if (target.classList.contains('take-section')) { if (target.classList.contains('take-section')) {
if (this.document.system.inventory.take.length < 3) if (this.document.system.inventory.take.length < 3) {
await this.document.update({ await this.document.update({
'system.inventory.take': [...this.document.system.inventory.take.map(x => x.uuid), item.uuid] 'system.inventory.take': [...this.document.system.inventory.take.map(x => x.uuid), item.uuid]
}); });
}
} else if (target.classList.contains('choice-b-section')) { } else if (target.classList.contains('choice-b-section')) {
if (this.document.system.inventory.choiceB.length < 2) if (this.document.system.inventory.choiceB.length < 2) {
await this.document.update({ await this.document.update({
'system.inventory.choiceB': [ 'system.inventory.choiceB': [
...this.document.system.inventory.choiceB.map(x => x.uuid), ...this.document.system.inventory.choiceB.map(x => x.uuid),
item.uuid item.uuid
] ]
}); });
}
} }
} }
} }
@ -179,7 +198,19 @@ export default class ClassSheet extends DHBaseItemSheet {
static async #removeItemFromCollection(_event, element) { static async #removeItemFromCollection(_event, element) {
const { uuid, target } = element.dataset; const { uuid, target } = element.dataset;
const prop = foundry.utils.getProperty(this.document.system, target); const prop = foundry.utils.getProperty(this.document.system, target);
await this.document.update({ [`system.${target}`]: prop.filter(i => i.uuid !== uuid) }); const item = await foundry.utils.fromUuid(uuid);
if (item) {
await item.update({
system: {
originItemType: null,
originId: null,
subType: null
}
});
}
await this.document.update({ [`system.${target}`]: prop.filter(i => i && i.uuid !== uuid) });
} }
/** /**

View file

@ -62,12 +62,17 @@ export default class SubclassSheet extends DHBaseItemSheet {
const item = await fromUuid(data.uuid); const item = await fromUuid(data.uuid);
const target = event.target.closest('fieldset.drop-section'); const target = event.target.closest('fieldset.drop-section');
if (item.type === 'feature') { if (item.type === 'feature') {
if (target.dataset.type === 'foundation') { if (item.system.originId) {
if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes.foundation) { const origin = await foundry.utils.fromUuid(item.system.originId);
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureNotFoundation')); return ui.notifications.warn(
return; game.i18n.format('DAGGERHEART.UI.Notifications.featureAlreadyLinked', {
} name: item.name,
origin: origin.name
})
);
}
if (target.dataset.type === 'foundation') {
await item.update({ await item.update({
system: { system: {
subType: CONFIG.DH.ITEM.featureSubTypes.foundation, subType: CONFIG.DH.ITEM.featureSubTypes.foundation,
@ -79,11 +84,6 @@ export default class SubclassSheet extends DHBaseItemSheet {
'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid] 'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid]
}); });
} else if (target.dataset.type === 'specialization') { } else if (target.dataset.type === 'specialization') {
if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes.specialization) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureNotSpecialization'));
return;
}
await item.update({ await item.update({
system: { system: {
subType: CONFIG.DH.ITEM.featureSubTypes.specialization, subType: CONFIG.DH.ITEM.featureSubTypes.specialization,
@ -95,11 +95,6 @@ export default class SubclassSheet extends DHBaseItemSheet {
'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid] 'system.features': [...this.document.system.features.map(x => x.uuid), item.uuid]
}); });
} else if (target.dataset.type === 'mastery') { } else if (target.dataset.type === 'mastery') {
if (item.system.subType && item.system.subType !== CONFIG.DH.ITEM.featureSubTypes.mastery) {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.featureNotMastery'));
return;
}
await item.update({ await item.update({
system: { system: {
subType: CONFIG.DH.ITEM.featureSubTypes.mastery, subType: CONFIG.DH.ITEM.featureSubTypes.mastery,

View file

@ -21,7 +21,8 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
hasDescription: false, hasDescription: false,
hasResource: false, hasResource: false,
isQuantifiable: false, isQuantifiable: false,
isInventoryItem: false isInventoryItem: false,
possibleItemLink: false
}; };
} }
@ -69,6 +70,20 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
if (this.metadata.isQuantifiable) if (this.metadata.isQuantifiable)
schema.quantity = new fields.NumberField({ integer: true, initial: 1, min: 0, required: true }); schema.quantity = new fields.NumberField({ integer: true, initial: 1, min: 0, required: true });
if (this.metadata.possibleItemLink) {
schema.originItemType = new fields.StringField({
choices: CONFIG.DH.ITEM.featureTypes,
nullable: true,
initial: null
});
schema.originId = new fields.StringField({ nullable: true, initial: null });
schema.subType = new fields.StringField({
choices: CONFIG.DH.ITEM.featureSubTypes,
nullable: true,
initial: null
});
}
return schema; return schema;
} }
@ -137,22 +152,43 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
async _preDelete() { async _preDelete() {
if (this.originId) { if (this.originId) {
if (this.actor && this.actor.type === 'character') { if (this.parent.type === 'feature') {
const items = this.actor.items.filter(item => item.system.originId === this.parent.id); if (this.actor && this.actor.type === 'character') {
if (items.length > 0) const items = this.actor.items.filter(item => item.system.originId === this.parent.id);
await this.actor.deleteEmbeddedDocuments( if (items.length > 0)
'Item', await this.actor.deleteEmbeddedDocuments(
items.map(x => x.id) 'Item',
); items.map(x => x.id)
} else { );
const linkedItem = await foundry.utils.fromUuid(this.originId); } else {
if (linkedItem) { const linkedItem = await foundry.utils.fromUuid(this.originId);
await linkedItem.update({ if (linkedItem) {
'system.features': linkedItem.system.features await linkedItem.update({
.filter(x => x.uuid !== this.parent.uuid) 'system.features': linkedItem.system.features
.map(x => x.uuid) .filter(x => x.uuid !== this.parent.uuid)
}); .map(x => x.uuid)
});
}
} }
} else if (this.parent.type === 'subclass') {
const linkedItem = await foundry.utils.fromUuid(this.originId);
await linkedItem.update({
'system.subclasses': linkedItem.system.subclasses
.filter(x => x.uuid !== this.parent.uuid)
.map(x => x.uuid)
});
}
}
if (this.features?.length) {
for (var feature of this.features) {
await feature.update({
system: {
originItemType: null,
originId: null,
subType: null
}
});
} }
} }
} }

View file

@ -86,6 +86,18 @@ export default class DHClass extends BaseDataItem {
} }
} }
async _preDelete() {
for (var subclass of this.subclasses) {
await subclass.update({
system: {
originItemType: null,
originId: null,
subType: null
}
});
}
}
_onDelete(options, userId) { _onDelete(options, userId) {
super._onDelete(options, userId); super._onDelete(options, userId);

View file

@ -8,7 +8,8 @@ export default class DHFeature extends BaseDataItem {
label: 'TYPES.Item.feature', label: 'TYPES.Item.feature',
type: 'feature', type: 'feature',
hasDescription: true, hasDescription: true,
hasResource: true hasResource: true,
possibleItemLink: true
}); });
} }
@ -17,13 +18,6 @@ export default class DHFeature extends BaseDataItem {
const fields = foundry.data.fields; const fields = foundry.data.fields;
return { return {
...super.defineSchema(), ...super.defineSchema(),
originItemType: new fields.StringField({
choices: CONFIG.DH.ITEM.featureTypes,
nullable: true,
initial: null
}),
originId: new fields.StringField({ nullable: true, initial: null }),
subType: new fields.StringField({ choices: CONFIG.DH.ITEM.featureSubTypes, nullable: true, initial: null }),
actions: new fields.ArrayField(new ActionField()) actions: new fields.ArrayField(new ActionField())
}; };
} }

View file

@ -7,7 +7,8 @@ export default class DHSubclass extends BaseDataItem {
return foundry.utils.mergeObject(super.metadata, { return foundry.utils.mergeObject(super.metadata, {
label: 'TYPES.Item.subclass', label: 'TYPES.Item.subclass',
type: 'subclass', type: 'subclass',
hasDescription: true hasDescription: true,
possibleItemLink: true
}); });
} }

View file

@ -90,7 +90,7 @@
<img class="image" src="{{this.img}}" /> <img class="image" src="{{this.img}}" />
<span>{{this.name}}</span> <span>{{this.name}}</span>
<div class="controls"> <div class="controls">
<i data-action="removeItemFromCollection" data-target="invetory.take" data-uuid="{{this.uuid}}" class="fa-solid fa-trash icon-button"></i> <i data-action="removeItemFromCollection" data-target="inventory.take" data-uuid="{{this.uuid}}" class="fa-solid fa-trash icon-button"></i>
</div> </div>
</div> </div>
{{/each}} {{/each}}
@ -105,7 +105,7 @@
<img class="image" src="{{this.img}}" /> <img class="image" src="{{this.img}}" />
<span>{{this.name}}</span> <span>{{this.name}}</span>
<div class="controls"> <div class="controls">
<i data-action="removeItemFromCollection" data-target="invetory.choiceA" data-uuid="{{this.uuid}}" class="fa-solid fa-trash icon-button"></i> <i data-action="removeItemFromCollection" data-target="inventory.choiceA" data-uuid="{{this.uuid}}" class="fa-solid fa-trash icon-button"></i>
</div> </div>
</div> </div>
{{/each}} {{/each}}
@ -120,7 +120,7 @@
<img class="image" src="{{this.img}}" /> <img class="image" src="{{this.img}}" />
<span>{{this.name}}</span> <span>{{this.name}}</span>
<div class="controls"> <div class="controls">
<i data-action="removeItemFromCollection" data-target="invetory.choiceB" data-uuid="{{this.uuid}}" class="fa-solid fa-trash icon-button"></i> <i data-action="removeItemFromCollection" data-target="inventory.choiceB" data-uuid="{{this.uuid}}" class="fa-solid fa-trash icon-button"></i>
</div> </div>
</div> </div>
{{/each}} {{/each}}