Fixed relinking of Features from items created on Character

This commit is contained in:
WBHarry 2025-07-31 18:03:33 +02:00
parent 0cf9e1d17c
commit 3dc4daffc3
7 changed files with 96 additions and 51 deletions

View file

@ -2221,6 +2221,7 @@
"tooHighLevel": "You cannot raise the character level past the maximum", "tooHighLevel": "You cannot raise the character level past the maximum",
"tooLowLevel": "You cannot lower the character level below starting level", "tooLowLevel": "You cannot lower the character level below starting level",
"subclassNotInClass": "This subclass does not belong to your selected class.", "subclassNotInClass": "This subclass does not belong to your selected class.",
"subclassNotInMulticlass": "This subclass does not belong to your selected multiclass.",
"missingClass": "You don't have a class selected yet.", "missingClass": "You don't have a class selected yet.",
"missingMulticlass": "Missing multiclass", "missingMulticlass": "Missing multiclass",
"wrongDomain": "The card isn't from one of your class domains.", "wrongDomain": "The card isn't from one of your class domains.",

View file

@ -41,7 +41,7 @@ export default class MulticlassChoiceDialog extends HandlebarsApplicationMixin(A
description: game.i18n.localize(domain.description), description: game.i18n.localize(domain.description),
src: domain.src, src: domain.src,
selected: value === this.selectedDomain, selected: value === this.selectedDomain,
disabled: this.actor.system.domains.includes(domain) disabled: this.actor.system.domains.includes(value)
}; };
}); });
context.multiclassDisabled = !this.selectedDomain; context.multiclassDisabled = !this.selectedDomain;

View file

@ -407,11 +407,16 @@ export default class DhCharacter extends BaseDataActor {
} else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.subclass.id) { } else if (item.system.originItemType === CONFIG.DH.ITEM.featureTypes.subclass.id) {
if (this.class.subclass) { if (this.class.subclass) {
const subclassState = this.class.subclass.system.featureState; const subclassState = this.class.subclass.system.featureState;
const subType = item.system.subType; const subclass =
item.system.identifier === 'multiclass' ? this.multiclass.subclass : this.class.subclass;
const featureType = subclass
? (subclass.system.features.find(x => x.item?.uuid === item.uuid)?.type ?? null)
: null;
if ( if (
subType === CONFIG.DH.ITEM.featureSubTypes.foundation || featureType === CONFIG.DH.ITEM.featureSubTypes.foundation ||
(subType === CONFIG.DH.ITEM.featureSubTypes.specialization && subclassState >= 2) || (featureType === CONFIG.DH.ITEM.featureSubTypes.specialization && subclassState >= 2) ||
(subType === CONFIG.DH.ITEM.featureSubTypes.mastery && subclassState >= 3) (featureType === CONFIG.DH.ITEM.featureSubTypes.mastery && subclassState >= 3)
) { ) {
subclassFeatures.push(item); subclassFeatures.push(item);
} }

View file

@ -125,18 +125,36 @@ export default class BaseDataItem extends foundry.abstract.TypeDataModel {
} }
if (this.actor && this.actor.type === 'character' && this.features) { if (this.actor && this.actor.type === 'character' && this.features) {
const featureUpdates = {};
for (let f of this.features) { for (let f of this.features) {
const feature = f.item ?? f; const feature = f.item ?? f;
const createData = foundry.utils.mergeObject(feature.toObject(), { const createData = foundry.utils.mergeObject(
system: { feature.toObject(),
originItemType: this.parent.type, {
originId: data._id, system: {
identifier: feature.identifier, originItemType: this.parent.type,
subType: feature.item ? feature.type : undefined originId: data._id,
} identifier: this.isMulticlass ? 'multiclass' : null,
}, { inplace: false }); subType: feature.item ? feature.type : undefined
await this.actor.createEmbeddedDocuments('Item', [createData]); }
},
{ inplace: false }
);
const [doc] = await this.actor.createEmbeddedDocuments('Item', [createData]);
if (!featureUpdates.features)
featureUpdates.features = this.features.map(x => (x.item ? { ...x, item: x.item.uuid } : x.uuid));
if (f.item) {
const existingFeature = featureUpdates.features.find(x => x.item === f.item.uuid);
existingFeature.item = doc.uuid;
} else {
const replaceIndex = featureUpdates.features.findIndex(x => x === f.uuid);
featureUpdates.features.splice(replaceIndex, 1, doc.uuid);
}
} }
await this.updateSource(featureUpdates);
} }
} }

View file

@ -41,9 +41,6 @@ export default class DHSubclass extends BaseDataItem {
} }
async _preCreate(data, options, user) { async _preCreate(data, options, user) {
const allowed = await super._preCreate(data, options, user);
if (allowed === false) return;
if (this.actor?.type === 'character') { if (this.actor?.type === 'character') {
if (this.actor.system.class.subclass) { if (this.actor.system.class.subclass) {
if (this.actor.system.multiclass.subclass) { if (this.actor.system.multiclass.subclass) {
@ -55,6 +52,17 @@ export default class DHSubclass extends BaseDataItem {
return false; return false;
} }
if (
this.actor.system.multiclass.value.system.subclasses.every(
x => x.uuid !== (data.uuid ?? `Item.${data._id}`)
)
) {
ui.notifications.error(
game.i18n.localize('DAGGERHEART.UI.Notifications.subclassNotInMulticlass')
);
return false;
}
await this.updateSource({ isMulticlass: true }); await this.updateSource({ isMulticlass: true });
} }
} else { } else {
@ -62,8 +70,19 @@ export default class DHSubclass extends BaseDataItem {
ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.missingClass')); ui.notifications.warn(game.i18n.localize('DAGGERHEART.UI.Notifications.missingClass'));
return false; return false;
} }
if (
this.actor.system.class.value.system.subclasses.every(
x => x.uuid !== (data.uuid ?? `Item.${data._id}`)
)
) {
ui.notifications.error(game.i18n.localize('DAGGERHEART.UI.Notifications.subclassNotInClass'));
return false;
}
} }
} }
const allowed = await super._preCreate(data, options, user);
if (allowed === false) return;
} }
_onCreate(data, options, userId) { _onCreate(data, options, userId) {

View file

@ -6,7 +6,7 @@
<div class="multiclass-domains-container"> <div class="multiclass-domains-container">
{{#each domainChoices as | choice |}} {{#each domainChoices as | choice |}}
<div class="domain-choice-container"> <div class="domain-choice-container">
<button data-action="selectDomain" data-domain="{{choice.value}}" {{#if choice.selected}}class="selected"{{/if}}> <button data-action="selectDomain" data-domain="{{choice.value}}" {{#if choice.selected}}class="selected"{{/if}} {{disabled choice.disabled}}>
<label>{{choice.label}}</label> <label>{{choice.label}}</label>
<img src="{{choice.src}}" /> <img src="{{choice.src}}" />
</button> </button>

View file

@ -23,38 +23,40 @@
</fieldset> </fieldset>
</div> </div>
<fieldset> {{#unless (eq document.parent.type 'character')}}
<legend>{{localize "TYPES.Item.subclass"}}</legend> <fieldset>
<div class="feature-list"> <legend>{{localize "TYPES.Item.subclass"}}</legend>
{{#each source.system.subclasses as |subclass index|}} <div class="feature-list">
<li class='feature-item'> {{#each source.system.subclasses as |subclass index|}}
<div class='feature-line'> <li class='feature-item'>
<img class='image' src='{{subclass.img}}' /> <div class='feature-line'>
<h4> <img class='image' src='{{subclass.img}}' />
{{subclass.name}} <h4>
</h4> {{subclass.name}}
<div class='controls'> </h4>
<a <div class='controls'>
class='effect-control' <a
data-action='editDoc' class='effect-control'
data-item-uuid={{subclass.uuid}} data-action='editDoc'
data-tooltip='{{localize "DAGGERHEART.UI.Tooltip.openItemWorld"}}' data-item-uuid={{subclass.uuid}}
> data-tooltip='{{localize "DAGGERHEART.UI.Tooltip.openItemWorld"}}'
<i class="fa-solid fa-globe"></i> >
</a> <i class="fa-solid fa-globe"></i>
<a </a>
class='effect-control' <a
data-action='removeItemFromCollection' class='effect-control'
data-target="subclasses" data-action='removeItemFromCollection'
data-uuid={{subclass.uuid}} data-target="subclasses"
data-tooltip='{{localize "CONTROLS.CommonDelete"}}' data-uuid={{subclass.uuid}}
> data-tooltip='{{localize "CONTROLS.CommonDelete"}}'
<i class='fas fa-trash'></i> >
</a> <i class='fas fa-trash'></i>
</a>
</div>
</div> </div>
</div> </li>
</li> {{/each}}
{{/each}} </div>
</div> </fieldset>
</fieldset> {{/unless}}
</div> </div>