Fixed sticky previous selections on continous leveling

This commit is contained in:
WBHarry 2025-06-01 02:43:37 +02:00
parent 8d53c8d3f1
commit 4e6b181fed
8 changed files with 179 additions and 55 deletions

View file

@ -786,6 +786,7 @@
"error": {
"domainCardWrongDomain": "You don't have access to that Domain",
"domainCardToHighLevel": "The Domain Card is too high level to be selected",
"domainCardDuplicate": "You already have that domain card!",
"noSelectionsLeft": "Nothing more to select!",
"alreadySelectedClass": "You already have that class!"
}

View file

@ -32,7 +32,8 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
actions: {
save: this.save,
viewCompendium: this.viewCompendium,
selectPreview: this.selectPreview
selectPreview: this.selectPreview,
selectDomain: this.selectDomain
},
form: {
handler: this.updateForm,
@ -100,13 +101,17 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
const traits = Object.values(context.advancementChoices.trait ?? {});
context.traits = {
values: traits.filter(trait => trait.data.length > 0).flatMap(trait => trait.data),
active: traits.length > 0
values: traits.filter(trait => !trait.locked && trait.data.length > 0).flatMap(trait => trait.data),
active: traits.length > 0 && traits.filter(trait => !trait.locked).length > 0
};
const experienceIncreases = Object.values(context.advancementChoices.experience ?? {});
const experienceIncreases = Object.values(context.advancementChoices.experience ?? {}).filter(
x => !x.locked
);
context.experienceIncreases = {
values: experienceIncreases.filter(trait => trait.data.length > 0).flatMap(trait => trait.data),
values: experienceIncreases
.filter(trait => !trait.locked && trait.data.length > 0)
.flatMap(trait => trait.data),
active: experienceIncreases.length > 0
};
@ -127,6 +132,8 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
context.domainCards = [];
for (var domainCard of allDomainCardValues) {
if (domainCard.locked) continue;
const uuid = domainCard.data?.length > 0 ? domainCard.data[0] : domainCard.uuid;
const card = uuid ? await foundry.utils.fromUuid(uuid) : { path: domainCard.path };
context.domainCards.push({
@ -135,7 +142,8 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
'DAGGERHEART.Application.LevelUp.Selections.emptyDomainCardHint',
{ level: domainCard.level }
),
limit: domainCard.level
limit: domainCard.level,
compendium: 'domains'
});
}
@ -165,12 +173,13 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
const multiclasses = Object.values(context.advancementChoices.multiclass ?? {});
if (multiclasses?.[0]) {
const data = multiclasses[0];
const path = `tiers.${data.tier}.levels.${data.level}.optionSelections.${data.optionKey}.${data.checkboxNr}.data`;
const multiclass =
data.data.length > 0 ? await foundry.utils.fromUuid(data.data[0]) : { path: path };
const path = `tiers.${data.tier}.levels.${data.level}.optionSelections.${data.optionKey}.${data.checkboxNr}`;
const multiclass = data.data.length > 0 ? await foundry.utils.fromUuid(data.data[0]) : {};
context.multiclass = {
...(multiclass.toObject?.() ?? multiclass),
uuid: multiclass.uuid,
path: path,
domains:
multiclass?.system?.domains.map(key => {
const domain = domains[key];
@ -179,7 +188,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
return {
...domain,
selected: key === data.secondaryData,
disabled: (key !== data.secondaryData && data.secondaryData) || alreadySelected
disabled: (data.secondaryData && key !== data.secondaryData) || alreadySelected
};
}) ?? [],
compendium: 'classes',
@ -267,7 +276,6 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
tagifyUpdate =
type =>
async (_, { option, removed }) => {
/* Needs to take Amount into account to allow multiple to be stored in the same option. Array structure? */
const updatePath = this.levelup.selectionData.reduce((acc, data) => {
if (data.optionKey === type && removed ? data.data.includes(option) : data.data.length < data.amount) {
return `tiers.${data.tier}.levels.${data.level}.optionSelections.${data.optionKey}.${data.checkboxNr}.data`;
@ -298,7 +306,8 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
async _onDrop(event) {
const data = foundry.applications.ux.TextEditor.getDragEventData(event);
const item = await fromUuid(data.uuid);
if (event.target.parentElement?.classList?.contains('domain-cards')) {
if (event.target.closest('.domain-cards')) {
const target = event.target.closest('.card-preview-container');
if (item.type === 'domainCard') {
if (!this.actor.system.class.system.domains.includes(item.system.domain)) {
// Also needs to check for multiclass adding a new domain
@ -308,17 +317,28 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
return;
}
if (item.system.level > Number(event.target.dataset.limit)) {
if (item.system.level > Number(target.dataset.limit)) {
ui.notifications.error(
game.i18n.localize('DAGGERHEART.Application.LevelUp.notifications.error.domainCardToHighLevel')
);
return;
}
await this.levelup.updateSource({ [event.target.dataset.path]: item.uuid });
if (
Object.values(this.levelup.domainCards).some(x => x.uuid === item.uuid) ||
this.levelup.selectionData.some(x => x.type === 'domainCard' && x.data.includes(item.uuid))
) {
ui.notifications.error(
game.i18n.localize('DAGGERHEART.Application.LevelUp.notifications.error.domainCardDuplicate')
);
return;
}
await this.levelup.updateSource({ [target.dataset.path]: item.uuid });
this.render();
}
} else if (event.target.parentElement?.classList?.contains('multiclass-cards')) {
} else if (event.target.closest('.multiclass-cards')) {
const target = event.target.closest('.card-preview-container');
if (item.type === 'class') {
if (item.name === this.actor.system.class.name) {
ui.notifications.error(
@ -326,7 +346,13 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
);
return;
}
await this.levelup.updateSource({ [event.target.dataset.path]: item.uuid });
await this.levelup.updateSource({
[target.dataset.path]: {
data: item.uuid,
secondaryData: null
}
});
this.render();
}
}
@ -336,7 +362,6 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
event.stopPropagation();
const button = event.currentTarget;
// const advancementSelections = this.getAdvancementSelectionUpdates(button);
if (!button.checked) {
await this.levelup.updateSource({
[`tiers.${button.dataset.tier}.levels.${button.dataset.level}.optionSelections.${button.dataset.option}.-=${button.dataset.checkboxNr}`]:
@ -397,15 +422,20 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
const option = remove
? selectionData.find(x => x.type === 'subclass' && x.data.includes(button.dataset.uuid))
: selectionData.find(x => x.type === 'subclass' && x.data.length === 0);
if (!option) {
return; // Notification?
}
if (!option) return;
const path = `tiers.${option.tier}.levels.${option.level}.optionSelections.${option.optionKey}.${option.checkboxNr}.data`;
await this.levelup.updateSource({ [path]: remove ? [] : button.dataset.uuid });
this.render();
}
static async selectDomain(_, button) {
const option = this.levelup.selectionData.find(x => x.type === 'multiclass');
const path = `tiers.${option.tier}.levels.${option.level}.optionSelections.${option.optionKey}.${option.checkboxNr}.secondaryData`;
await this.levelup.updateSource({ [path]: option.secondaryData ? null : button.dataset.domain });
this.render();
}
static async save() {
await this.actor.levelUp(this.levelup.levelupData);
this.close();

View file

@ -46,7 +46,16 @@ export class DhLevelup extends foundry.abstract.DataModel {
const domainCards = Object.keys(tiers).reduce((acc, tierKey) => {
const tier = tiers[tierKey];
for (var level of tier.belongingLevels) {
if (level <= pcLevelData.level.changed) {
if (level <= pcLevelData.level.current) {
const cardId = foundry.utils.randomID();
acc[cardId] = {
...pcLevelData.levelups[level].domainCards[0],
path: `domainCards.${cardId}.uuid`,
locked: true,
level: level,
tier: tierKey
};
} else if (level <= pcLevelData.level.changed) {
for (var domainCardSlot = 1; domainCardSlot <= tier.domainCardByLevel; domainCardSlot++) {
const cardId = foundry.utils.randomID();
acc[cardId] = {
@ -103,7 +112,8 @@ export class DhLevelup extends foundry.abstract.DataModel {
tier: new fields.NumberField({ required: true, integer: true }),
level: new fields.NumberField({ required: true, integer: true }),
domainCardSlot: new fields.NumberField({ required: true, integer: true }),
path: new fields.StringField({ required: true })
path: new fields.StringField({ required: true }),
locked: new fields.BooleanField({ required: true, initial: false })
})
),
progressionLevels: new fields.ArrayField(new fields.NumberField({ required: true, integer: true }))
@ -181,7 +191,8 @@ export class DhLevelup extends foundry.abstract.DataModel {
value: optionSelect.value,
amount: optionSelect.amount,
data: selectionObj.data,
secondaryData: selectionObj.secondaryData
secondaryData: selectionObj.secondaryData,
locked: selectionObj.locked
};
});
});
@ -191,6 +202,7 @@ export class DhLevelup extends foundry.abstract.DataModel {
get levelupData() {
const leveledSelections = this.selectionData.reduce((acc, data) => {
if (data.type === 'domainCard' && data.locked) return acc;
if (!acc[data.level]) acc[data.level] = [data];
else acc[data.level].push(data);
@ -198,13 +210,15 @@ export class DhLevelup extends foundry.abstract.DataModel {
}, {});
return this.progressionLevels.reduce((acc, level) => {
acc[level] = {
achievements: {
experiences: this.allInitialAchievements[level].newExperiences,
proficiency: this.allInitialAchievements[level].proficiency
},
domainCards: Object.values(this.domainCards).map(card => ({ ...card })),
domainCards: Object.values(this.domainCards).filter(x => !x.locked && x.level === level),
selections: leveledSelections[level]
};
if (this.allInitialAchievements[level]) {
acc[level].achievements = {
experiences: this.allInitialAchievements[level].newExperiences,
proficiency: this.allInitialAchievements[level].proficiency
};
}
return acc;
}, {});
@ -348,8 +362,9 @@ class DhLevelupLevel extends foundry.abstract.DataModel {
optionSelections: levelData.reduce((acc, data) => {
if (!acc[data.optionKey]) acc[data.optionKey] = {};
acc[data.optionKey][data.checkboxNr] = {
...data,
minCost: optionSelections[data.optionKey].minCost,
minCost: optionSelections[data.optionKey].amount,
amount: optionSelections[data.optionKey].amount,
locked: locked
};

View file

@ -447,7 +447,8 @@ class DhPCLevelData extends foundry.abstract.DataModel {
),
domainCards: new fields.ArrayField(
new fields.SchemaField({
uuid: new fields.StringField({ required: true })
uuid: new fields.StringField({ required: true }),
itemUuid: new fields.StringField({ required: true })
})
),
selections: new fields.ArrayField(
@ -459,7 +460,8 @@ class DhPCLevelData extends foundry.abstract.DataModel {
checkboxNr: new fields.NumberField({ required: true, integer: true }),
value: new fields.NumberField({ integer: true }),
amount: new fields.NumberField({ integer: true }),
data: new fields.ArrayField(new fields.StringField({ required: true }))
data: new fields.ArrayField(new fields.StringField({ required: true })),
uuid: new fields.StringField({ required: true })
})
)
})

View file

@ -6,13 +6,16 @@ import { setDiceSoNiceForDualityRoll } from '../helpers/utils.mjs';
export default class DhpActor extends Actor {
async _preCreate(data, options, user) {
if ( (await super._preCreate(data, options, user)) === false ) return false;
if ((await super._preCreate(data, options, user)) === false) return false;
// Configure prototype token settings
const prototypeToken = {};
if ( this.type === "pc" ) Object.assign(prototypeToken, {
sight: { enabled: true }, actorLink: true, disposition: CONST.TOKEN_DISPOSITIONS.FRIENDLY
});
if (this.type === 'pc')
Object.assign(prototypeToken, {
sight: { enabled: true },
actorLink: true,
disposition: CONST.TOKEN_DISPOSITIONS.FRIENDLY
});
this.updateSource({ prototypeToken });
}
@ -37,7 +40,7 @@ export default class DhpActor extends Actor {
);
for (var domainCard of changes.domainCards) {
const uuid = domainCard.uuid ? domainCard.uuid : domainCard.data;
const uuid = domainCard.itemUuid ? domainCard.itemUuid : domainCard.uuid;
const itemCard = await this.items.find(x => x.uuid === uuid);
itemCard.delete();
}
@ -56,6 +59,14 @@ export default class DhpActor extends Actor {
}
}
for (var subclass of changes.subclasses) {
/* Implemented after datamodel rework is in */
}
if (changes.multiclass) {
/* Implemented after datamodel rework is in */
}
const newLevelData = {
level: {
current: newLevel,
@ -98,11 +109,13 @@ export default class DhpActor extends Actor {
multiclass: null,
subclasses: [],
traits: [],
experiences: [],
experiences: {},
experienceIncreases: []
};
for (var level = startLevel + 1; level <= endLevel; level++) {
if (!levelData[level]) continue;
const achievements = levelData[level].achievements;
const selections = levelData[level].selections.reduce((acc, selection) => {
if (!acc[selection.type]) acc[selection.type] = [selection];
@ -122,16 +135,27 @@ export default class DhpActor extends Actor {
: 0;
changedFeatures.proficiency +=
(achievements?.proficiency ?? 0) +
(selections.evasion ? selections.evasion.reduce((acc, evasion) => acc + Number(evasion.value), 0) : 0);
changedFeatures.domainCards = [
...levelData[level].domainCards,
...(selections.domainCard.flatMap(x => x.data.map(data => ({ ...x, data: data }))) ?? [])
];
changedFeatures.traits = selections.trait ? selections.trait.flatMap(x => x.data) : [];
changedFeatures.experiences = achievements?.experiences ? achievements.experiences : {};
changedFeatures.experienceIncreases = selections.experience ?? [];
changedFeatures.subclasses = selections.subclasses ? [] : [];
changedFeatures.multiclass = selections.multiclass ? [] : [];
(selections.proficiency
? selections.proficiency.reduce((acc, proficiency) => acc + Number(proficiency.value), 0)
: 0);
changedFeatures.domainCards.push(
...[
...levelData[level].domainCards,
...(selections.domainCard?.flatMap(x => x.data.map(data => ({ ...x, data: data }))) ?? [])
]
);
changedFeatures.traits.push(...(selections.trait ? selections.trait.flatMap(x => x.data) : []));
changedFeatures.experiences = Object.keys(achievements?.experiences ? achievements.experiences : {}).reduce(
(acc, key) => {
acc[key] = achievements.experiences[key];
return acc;
},
changedFeatures.experiences
);
changedFeatures.experienceIncreases.push(...(selections.experience ?? []));
changedFeatures.subclasses.push(...(selections.subclasses ? [] : []));
changedFeatures.multiclass = selections.multiclass ? selections.multiclass[0] : null;
}
return changedFeatures;
@ -147,12 +171,13 @@ export default class DhpActor extends Actor {
for (var card of changes.domainCards) {
const fromAchievement = Boolean(card.uuid);
const domainCard = await foundry.utils.fromUuid(fromAchievement ? card.uuid : card.data);
const newCard = (await this.createEmbeddedDocuments('Item', [domainCard]))[0];
const createdCards = await this.createEmbeddedDocuments('Item', [domainCard]);
const newCard = createdCards[0];
if (fromAchievement) {
const levelupCard = levelupData[card.level].domainCards.find(
x => x.tier === card.tier && x.level === card.level
);
if (levelupCard) levelupCard.uuid = newCard.uuid;
if (levelupCard) levelupCard.itemUuid = newCard.uuid;
} else {
const levelupCard = levelupData[card.level].selections.find(
x =>
@ -161,7 +186,7 @@ export default class DhpActor extends Actor {
x.optionKey === card.optionKey &&
x.checkboxNr === card.checkboxNr
);
if (levelupCard) levelupCard.data.findSplice(x => x === card.data, newCard.uuid);
if (levelupCard) levelupCard.uuid = newCard.uuid;
}
}
@ -184,6 +209,14 @@ export default class DhpActor extends Actor {
}
}
for (var subclass of changes.subclasses) {
/* Implemented after datamodel rework is in */
}
if (changes.multiclass) {
/* Implemented after datamodel rework is in */
}
await this.update({
system: {
'traits': traitsUpdate,

View file

@ -2837,7 +2837,7 @@ div.daggerheart.views.multiclass {
pointer-events: none;
opacity: 0.4;
}
.daggerheart.levelup .levelup-selections-container .levelup-card-selection .levelup-domains-selection-container .levelup-domain-selection-container div {
.daggerheart.levelup .levelup-selections-container .levelup-card-selection .levelup-domains-selection-container .levelup-domain-selection-container .levelup-domain-label {
position: absolute;
text-align: center;
top: 4px;
@ -2848,6 +2848,24 @@ div.daggerheart.views.multiclass {
.daggerheart.levelup .levelup-selections-container .levelup-card-selection .levelup-domains-selection-container .levelup-domain-selection-container img {
height: 124px;
}
.daggerheart.levelup .levelup-selections-container .levelup-card-selection .levelup-domains-selection-container .levelup-domain-selection-container .levelup-domain-selected {
position: absolute;
height: 54px;
width: 54px;
border-radius: 50%;
border: 2px solid;
font-size: 48px;
display: flex;
align-items: center;
justify-content: center;
background-image: url(../assets/parchments/dh-parchment-light.png);
color: var(--color-dark-5);
top: calc(50% - 29px);
}
.daggerheart.levelup .levelup-selections-container .levelup-card-selection .levelup-domains-selection-container .levelup-domain-selection-container .levelup-domain-selected i {
position: relative;
right: 2px;
}
.daggerheart.levelup .levelup-summary-container .level-achievements-container {
display: flex;
flex-direction: column;

View file

@ -147,7 +147,7 @@
opacity: 0.4;
}
div {
.levelup-domain-label {
position: absolute;
text-align: center;
top: 4px;
@ -159,6 +159,26 @@
img {
height: 124px; // Can it be dynamically sized? Won't follow any window resizing like this.
}
.levelup-domain-selected {
position: absolute;
height: 54px;
width: 54px;
border-radius: 50%;
border: 2px solid;
font-size: 48px;
display: flex;
align-items: center;
justify-content: center;
background-image: url(../assets/parchments/dh-parchment-light.png);
color: var(--color-dark-5);
top: calc(50% - 29px);
i {
position: relative;
right: 2px;
}
}
}
}
}

View file

@ -71,9 +71,14 @@
{{> "systems/daggerheart/templates/components/card-preview.hbs" img=this.multiclass.img name=this.multiclass.name path=this.multiclass.path compendium=this.multiclass.compendium }}
<div class="levelup-domains-selection-container">
{{#each this.multiclass.domains}}
<div class="levelup-domain-selection-container {{#if this.disabled}}disabled{{/if}}">
<div>{{localize this.label}}</div>
<div class="levelup-domain-selection-container {{#if this.disabled}}disabled{{/if}}" {{#if (not this.disabled)}}data-action="selectDomain" data-uuid="{{../multiclass.uuid}}" data-domain="{{this.id}}"{{/if}}>
<div class="levelup-domain-label">{{localize this.label}}</div>
<img src="{{this.src}}" />
{{#if this.selected}}
<div class="levelup-domain-selected">
<i class="fa-solid fa-check"></i>
</div>
{{/if}}
</div>
{{/each}}
</div>