mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-15 13:11:08 +01:00
Fixed data handling in the LevelUp view
This commit is contained in:
parent
3cc8800950
commit
cc0766fc20
8 changed files with 231 additions and 412 deletions
|
|
@ -10,7 +10,7 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
||||||
this.levelTiers = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.LevelTiers);
|
this.levelTiers = game.settings.get(SYSTEM.id, SYSTEM.SETTINGS.gameSettings.LevelTiers);
|
||||||
|
|
||||||
const playerLevelupData = actor.system.levelData;
|
const playerLevelupData = actor.system.levelData;
|
||||||
this.levelup = new DhLevelup(DhLevelup.initializeData(this.levelTiers, playerLevelupData));
|
this.levelup = new DhLevelup(DhLevelup.initializeData(this.levelTiers, playerLevelupData, actor.system.level));
|
||||||
}
|
}
|
||||||
|
|
||||||
get title() {
|
get title() {
|
||||||
|
|
@ -23,11 +23,8 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
||||||
window: {
|
window: {
|
||||||
resizable: true
|
resizable: true
|
||||||
},
|
},
|
||||||
actions: {},
|
actions: {
|
||||||
form: {
|
save: this.save
|
||||||
handler: this.updateForm,
|
|
||||||
submitOnChange: true,
|
|
||||||
closeOnSubmit: false
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -45,380 +42,57 @@ export default class DhlevelUp extends HandlebarsApplicationMixin(ApplicationV2)
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
static async updateForm(event, _, formData) {
|
_attachPartListeners(partId, htmlElement, options) {
|
||||||
await this.document.update(formData.object);
|
super._attachPartListeners(partId, htmlElement, options);
|
||||||
|
$(htmlElement).find('.selection-checkbox').on('change', this.selectionClick.bind(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
async selectionClick(event) {
|
||||||
|
const button = event.currentTarget;
|
||||||
|
|
||||||
|
if (!button.checked) {
|
||||||
|
await this.levelup.updateSource({
|
||||||
|
[`tiers.${button.dataset.tier}.levels.${button.dataset.level}.optionSelections.${button.dataset.option}.-=${button.dataset.checkboxNr}`]:
|
||||||
|
null
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const levelSelections = this.levelup.levelSelections;
|
||||||
|
if (levelSelections.total >= this.levelup.maxSelections) {
|
||||||
|
// Notification?
|
||||||
|
this.render();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const tier = this.levelup.tiers[button.dataset.tier];
|
||||||
|
const tierLevels = Object.keys(tier.levels).map(level => Number(level));
|
||||||
|
const lowestLevelChoice = Object.keys(levelSelections.selections).reduce((currentLowest, key) => {
|
||||||
|
const level = Number(key);
|
||||||
|
if (tierLevels.includes(level)) {
|
||||||
|
if (!currentLowest || level < currentLowest) return level;
|
||||||
|
}
|
||||||
|
|
||||||
|
return currentLowest;
|
||||||
|
}, null);
|
||||||
|
|
||||||
|
if (!lowestLevelChoice) {
|
||||||
|
// Notification?
|
||||||
|
this.render();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.levelup.updateSource({
|
||||||
|
[`tiers.${button.dataset.tier}.levels.${lowestLevelChoice}.optionSelections.${button.dataset.option}.${button.dataset.checkboxNr}`]: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
this.render();
|
this.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static async save() {
|
||||||
|
await this.actor.update({
|
||||||
|
'system.levelData.selections': [{ tier: 1, level: 2, type: 'hitPoint', checkboxNr: 1 }]
|
||||||
|
});
|
||||||
|
|
||||||
|
this.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// import SelectDialog from '../dialogs/selectDialog.mjs';
|
|
||||||
// import { getTier } from '../helpers/utils.mjs';
|
|
||||||
// import DhpMulticlassDialog from './multiclassDialog.mjs';
|
|
||||||
|
|
||||||
// const { HandlebarsApplicationMixin, ApplicationV2 } = foundry.applications.api;
|
|
||||||
|
|
||||||
// export default class DhpLevelup extends HandlebarsApplicationMixin(ApplicationV2) {
|
|
||||||
// constructor(actor) {
|
|
||||||
// super({});
|
|
||||||
|
|
||||||
// this.actor = actor;
|
|
||||||
// this.data = foundry.utils.deepClone(actor.system.levelData);
|
|
||||||
// this.activeLevel = actor.system.levelData.currentLevel + 1;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// get title() {
|
|
||||||
// return `${this.actor.name} - Level Up`;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// static DEFAULT_OPTIONS = {
|
|
||||||
// classes: ['daggerheart', 'views', 'levelup'],
|
|
||||||
// position: { width: 1200, height: 'auto' },
|
|
||||||
// window: {
|
|
||||||
// resizable: true
|
|
||||||
// },
|
|
||||||
|
|
||||||
// actions: {
|
|
||||||
// toggleBox: this.toggleBox,
|
|
||||||
// advanceLevel: this.advanceLevel,
|
|
||||||
// finishLevelup: this.finishLevelup
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// static PARTS = {
|
|
||||||
// form: {
|
|
||||||
// id: 'levelup',
|
|
||||||
// template: 'systems/daggerheart/templates/views/levelup.hbs'
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
// async _prepareContext(_options) {
|
|
||||||
// let selectedChoices = 0,
|
|
||||||
// multiclassing = {},
|
|
||||||
// subclassing = {};
|
|
||||||
// const leveledTiers = Object.keys(this.data.levelups).reduce(
|
|
||||||
// (acc, levelKey) => {
|
|
||||||
// const levelData = this.data.levelups[levelKey];
|
|
||||||
// ['tier1', 'tier2', 'tier3'].forEach(tierKey => {
|
|
||||||
// let tierUpdate = {};
|
|
||||||
// const tierData = levelData[tierKey];
|
|
||||||
// if (tierData) {
|
|
||||||
// tierUpdate = Object.keys(tierData).reduce((acc, propertyKey) => {
|
|
||||||
// const values = tierData[propertyKey];
|
|
||||||
// const level = Number.parseInt(levelKey);
|
|
||||||
|
|
||||||
// acc[propertyKey] = Object.values(values).map(value => {
|
|
||||||
// if (value && level === this.activeLevel) selectedChoices++;
|
|
||||||
// if (propertyKey === 'multiclass') multiclassing[levelKey] = true;
|
|
||||||
// if (propertyKey === 'subclass') subclassing[tierKey] = true;
|
|
||||||
|
|
||||||
// return { level: level, value: value };
|
|
||||||
// });
|
|
||||||
|
|
||||||
// return acc;
|
|
||||||
// }, {});
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Object.keys(tierUpdate).forEach(propertyKey => {
|
|
||||||
// const property = tierUpdate[propertyKey];
|
|
||||||
// const propertyValues = foundry.utils.getProperty(acc, `${tierKey}.${propertyKey}`) ?? [];
|
|
||||||
// foundry.utils.setProperty(acc, `${tierKey}.${propertyKey}`, [...propertyValues, ...property]);
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
|
|
||||||
// return acc;
|
|
||||||
// },
|
|
||||||
// { tier1: {}, tier2: {}, tier3: {} }
|
|
||||||
// );
|
|
||||||
|
|
||||||
// const activeTier = getTier(this.activeLevel);
|
|
||||||
// const data = Object.keys(SYSTEM.ACTOR.levelupData).reduce((acc, tierKey) => {
|
|
||||||
// const tier = SYSTEM.ACTOR.levelupData[tierKey];
|
|
||||||
// acc[tierKey] = {
|
|
||||||
// label: game.i18n.localize(tier.label),
|
|
||||||
// info: game.i18n.localize(tier.info),
|
|
||||||
// pretext: game.i18n.localize(tier.pretext),
|
|
||||||
// postext: game.i18n.localize(tier.posttext),
|
|
||||||
// active: tierKey <= activeTier,
|
|
||||||
// choices: Object.keys(tier.choices).reduce((acc, propertyKey) => {
|
|
||||||
// const property = tier.choices[propertyKey];
|
|
||||||
// acc[propertyKey] = { description: property.description, cost: property.cost ?? 1, values: [] };
|
|
||||||
// for (var i = 0; i < property.maxChoices; i++) {
|
|
||||||
// const leveledValue = leveledTiers[tierKey][propertyKey]?.[i];
|
|
||||||
// const subclassLock =
|
|
||||||
// propertyKey === 'subclass' &&
|
|
||||||
// Object.keys(multiclassing).find(x => getTier(Number.parseInt(x)) === tierKey);
|
|
||||||
// const subclassMulticlassLock = propertyKey === 'multiclass' && subclassing[tierKey];
|
|
||||||
// const multiclassLock =
|
|
||||||
// propertyKey === 'multiclass' &&
|
|
||||||
// Object.keys(multiclassing).length > 0 &&
|
|
||||||
// !(
|
|
||||||
// leveledValue &&
|
|
||||||
// Object.keys(multiclassing).find(x => Number.parseInt(x) === leveledValue.level)
|
|
||||||
// );
|
|
||||||
// const locked =
|
|
||||||
// (leveledValue && leveledValue.level !== this.activeLevel) ||
|
|
||||||
// subclassLock ||
|
|
||||||
// subclassMulticlassLock ||
|
|
||||||
// multiclassLock;
|
|
||||||
// const disabled =
|
|
||||||
// tierKey > activeTier ||
|
|
||||||
// (selectedChoices === 2 && !(leveledValue && leveledValue.level === this.activeLevel)) ||
|
|
||||||
// locked;
|
|
||||||
|
|
||||||
// acc[propertyKey].values.push({
|
|
||||||
// selected: leveledValue?.value !== undefined,
|
|
||||||
// path: `levelups.${this.activeLevel}.${tierKey}.${propertyKey}.${i}`,
|
|
||||||
// description: game.i18n.localize(property.description),
|
|
||||||
// disabled: disabled,
|
|
||||||
// locked: locked
|
|
||||||
// });
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return acc;
|
|
||||||
// }, {})
|
|
||||||
// };
|
|
||||||
|
|
||||||
// return acc;
|
|
||||||
// }, {});
|
|
||||||
|
|
||||||
// return {
|
|
||||||
// data: data,
|
|
||||||
// activeLevel: this.activeLevel,
|
|
||||||
// changedLevel: this.actor.system.levelData.changedLevel,
|
|
||||||
// completedSelection: selectedChoices === 2
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// static async toggleBox(_, button) {
|
|
||||||
// const path = button.dataset.path;
|
|
||||||
// if (foundry.utils.getProperty(this.data, path)) {
|
|
||||||
// const pathParts = path.split('.');
|
|
||||||
// const arrayPart = pathParts.slice(0, pathParts.length - 1).join('.');
|
|
||||||
// let array = foundry.utils.getProperty(this.data, arrayPart);
|
|
||||||
// if (button.dataset.levelAttribute === 'multiclass') {
|
|
||||||
// array = [];
|
|
||||||
// } else {
|
|
||||||
// delete array[Number.parseInt(pathParts[pathParts.length - 1])];
|
|
||||||
// }
|
|
||||||
// foundry.utils.setProperty(this.data, arrayPart, array);
|
|
||||||
// } else {
|
|
||||||
// const updates = [{ path: path, value: { level: this.activeLevel } }];
|
|
||||||
// const levelChoices = SYSTEM.ACTOR.levelChoices[button.dataset.levelAttribute];
|
|
||||||
// if (button.dataset.levelAttribute === 'subclass') {
|
|
||||||
// if (!this.actor.system.multiclassSubclass) {
|
|
||||||
// updates[0].value.value = {
|
|
||||||
// multiclass: false,
|
|
||||||
// feature: this.actor.system.subclass.system.specializationFeature.unlocked
|
|
||||||
// ? 'mastery'
|
|
||||||
// : 'specialization'
|
|
||||||
// };
|
|
||||||
// } else {
|
|
||||||
// const choices = [
|
|
||||||
// { name: this.actor.system.subclass.name, value: this.actor.system.subclass.uuid },
|
|
||||||
// {
|
|
||||||
// name: this.actor.system.multiclassSubclass.name,
|
|
||||||
// value: this.actor.system.multiclassSubclass.uuid
|
|
||||||
// }
|
|
||||||
// ];
|
|
||||||
// const indexes = await SelectDialog.selectItem({
|
|
||||||
// actor: this.actor,
|
|
||||||
// choices: choices,
|
|
||||||
// title: levelChoices.title,
|
|
||||||
// nrChoices: 1
|
|
||||||
// });
|
|
||||||
// if (indexes.length === 0) {
|
|
||||||
// this.render();
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// const multiclassSubclass = choices[indexes[0]].name === this.actor.system.multiclassSubclass.name;
|
|
||||||
// updates[0].value.value = {
|
|
||||||
// multiclass: multiclassSubclass,
|
|
||||||
// feature: this.actor.system.multiclassSubclass.system.specializationFeature.unlocked
|
|
||||||
// ? 'mastery'
|
|
||||||
// : 'specialization'
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
// } else if (button.dataset.levelAttribute === 'multiclass') {
|
|
||||||
// const multiclassAwait = new Promise(resolve => {
|
|
||||||
// new DhpMulticlassDialog(this.actor.name, this.actor.system.class, resolve).render(true);
|
|
||||||
// });
|
|
||||||
// const multiclassData = await multiclassAwait;
|
|
||||||
// if (!multiclassData) {
|
|
||||||
// this.render();
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const pathParts = path.split('.');
|
|
||||||
// const arrayPart = pathParts.slice(0, pathParts.length - 1).join('.');
|
|
||||||
// updates[0] = {
|
|
||||||
// path: [arrayPart, '0'].join('.'),
|
|
||||||
// value: {
|
|
||||||
// level: this.activeLevel,
|
|
||||||
// value: {
|
|
||||||
// class: multiclassData.class,
|
|
||||||
// subclass: multiclassData.subclass,
|
|
||||||
// domain: multiclassData.domain,
|
|
||||||
// level: this.activeLevel
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// updates[1] = {
|
|
||||||
// path: [arrayPart, '1'].join('.'),
|
|
||||||
// value: {
|
|
||||||
// level: this.activeLevel,
|
|
||||||
// value: {
|
|
||||||
// class: multiclassData.class,
|
|
||||||
// subclass: multiclassData.subclass,
|
|
||||||
// domain: multiclassData.domain,
|
|
||||||
// level: this.activeLevel
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
// } else {
|
|
||||||
// if (levelChoices.choices.length > 0) {
|
|
||||||
// if (typeof levelChoices.choices === 'string') {
|
|
||||||
// const choices = foundry.utils
|
|
||||||
// .getProperty(this.actor, levelChoices.choices)
|
|
||||||
// .map(x => ({ name: x.description, value: x.id }));
|
|
||||||
// const indexes = await SelectDialog.selectItem({
|
|
||||||
// actor: this.actor,
|
|
||||||
// choices: choices,
|
|
||||||
// title: levelChoices.title,
|
|
||||||
// nrChoices: levelChoices.nrChoices
|
|
||||||
// });
|
|
||||||
// if (indexes.length === 0) {
|
|
||||||
// this.render();
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// updates[0].value.value = choices
|
|
||||||
// .filter((_, index) => indexes.includes(index))
|
|
||||||
// .map(x => x.value);
|
|
||||||
// } else {
|
|
||||||
// const indexes = await SelectDialog.selectItem({
|
|
||||||
// actor: this.actor,
|
|
||||||
// choices: levelChoices.choices,
|
|
||||||
// title: levelChoices.title,
|
|
||||||
// nrChoices: levelChoices.nrChoices
|
|
||||||
// });
|
|
||||||
// if (indexes.length === 0) {
|
|
||||||
// this.render();
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// updates[0].value.value = levelChoices.choices[indexes[0]].path;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const update = updates.reduce((acc, x) => {
|
|
||||||
// acc[x.path] = x.value;
|
|
||||||
|
|
||||||
// return acc;
|
|
||||||
// }, {});
|
|
||||||
|
|
||||||
// this.data = foundry.utils.mergeObject(this.data, update);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// this.render();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// static advanceLevel() {
|
|
||||||
// this.activeLevel += 1;
|
|
||||||
// this.render();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// static async finishLevelup() {
|
|
||||||
// this.data.currentLevel = this.data.changedLevel;
|
|
||||||
// let multiclass = null;
|
|
||||||
// for (var level in this.data.levelups) {
|
|
||||||
// for (var tier in this.data.levelups[level]) {
|
|
||||||
// for (var category in this.data.levelups[level][tier]) {
|
|
||||||
// for (var value in this.data.levelups[level][tier][category]) {
|
|
||||||
// if (category === 'multiclass') {
|
|
||||||
// multiclass = this.data.levelups[level][tier][category][value].value;
|
|
||||||
// this.data.levelups[level][tier][category][value] = true;
|
|
||||||
// } else {
|
|
||||||
// this.data.levelups[level][tier][category][value] =
|
|
||||||
// this.data.levelups[level][tier][category][value].value ?? true;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const tiersMoved =
|
|
||||||
// getTier(this.actor.system.levelData.changedLevel, true) -
|
|
||||||
// getTier(this.actor.system.levelData.currentLevel, true);
|
|
||||||
// const experiences = Array.from(Array(tiersMoved), (_, index) => ({
|
|
||||||
// id: foundry.utils.randomID(),
|
|
||||||
// level: this.actor.system.experiences.length + index * 3,
|
|
||||||
// description: '',
|
|
||||||
// value: 1
|
|
||||||
// }));
|
|
||||||
|
|
||||||
// await this.actor.update(
|
|
||||||
// {
|
|
||||||
// system: {
|
|
||||||
// levelData: this.data,
|
|
||||||
// experiences: [...this.actor.system.experiences, ...experiences]
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// { diff: false }
|
|
||||||
// );
|
|
||||||
|
|
||||||
// if (!this.actor.multiclass && multiclass) {
|
|
||||||
// const multiclassClass = (await fromUuid(multiclass.class.uuid)).toObject();
|
|
||||||
// multiclassClass.system.domains = [multiclass.domain.id];
|
|
||||||
// multiclassClass.system.multiclass = multiclass.level;
|
|
||||||
|
|
||||||
// const multiclassFeatures = [];
|
|
||||||
// for (var i = 0; i < multiclassClass.system.features.length; i++) {
|
|
||||||
// const feature = (await fromUuid(multiclassClass.system.features[i].uuid)).toObject();
|
|
||||||
// feature.system.multiclass = multiclass.level;
|
|
||||||
// multiclassFeatures.push(feature);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// const multiclassSubclass = (await fromUuid(multiclass.subclass.uuid)).toObject();
|
|
||||||
// multiclassSubclass.system.multiclass = multiclass.level;
|
|
||||||
|
|
||||||
// const multiclassSubclassFeatures = {};
|
|
||||||
// const features = [
|
|
||||||
// multiclassSubclass.system.foundationFeature,
|
|
||||||
// multiclassSubclass.system.specializationFeature,
|
|
||||||
// multiclassSubclass.system.masteryFeature
|
|
||||||
// ];
|
|
||||||
// for (var i = 0; i < features.length; i++) {
|
|
||||||
// const path = i === 0 ? 'foundationFeature' : i === 1 ? 'specializationFeature' : 'masteryFeature';
|
|
||||||
// const feature = features[i];
|
|
||||||
// for (var ability of feature.abilities) {
|
|
||||||
// const data = (await fromUuid(ability.uuid)).toObject();
|
|
||||||
// if (i > 0) data.system.disabled = true;
|
|
||||||
// data.system.multiclass = multiclass.level;
|
|
||||||
// if (!multiclassSubclassFeatures[path]) multiclassSubclassFeatures[path] = [data];
|
|
||||||
// else multiclassSubclassFeatures[path].push(data);
|
|
||||||
// // data.uuid = feature.uuid;
|
|
||||||
|
|
||||||
// // const abilityData = await this._onDropItemCreate(data);
|
|
||||||
// // ability.uuid = abilityData[0].uuid;
|
|
||||||
|
|
||||||
// // createdItems.push(abilityData);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// for (let subclassFeaturesKey in multiclassSubclassFeatures) {
|
|
||||||
// const values = multiclassSubclassFeatures[subclassFeaturesKey];
|
|
||||||
// const abilityResults = await this.actor.createEmbeddedDocuments('Item', values);
|
|
||||||
// for (var i = 0; i < abilityResults.length; i++) {
|
|
||||||
// multiclassSubclass.system[subclassFeaturesKey].abilities[i].uuid = abilityResults[i].uuid;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// await this.actor.createEmbeddedDocuments('Item', [
|
|
||||||
// multiclassClass,
|
|
||||||
// ...multiclassFeatures,
|
|
||||||
// multiclassSubclass
|
|
||||||
// ]);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// this.close();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,16 @@ export class DhLevelTiers extends foundry.abstract.DataModel {
|
||||||
tiers: new fields.TypedObjectField(new fields.EmbeddedDataField(DhLevelTier))
|
tiers: new fields.TypedObjectField(new fields.EmbeddedDataField(DhLevelTier))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get availableChoicesPerLevel() {
|
||||||
|
return Object.values(this.tiers).reduce((acc, tier) => {
|
||||||
|
for (var level = tier.levels.start; level < tier.levels.end + 1; level++) {
|
||||||
|
acc[level] = tier.availableOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DhLevelTier extends foundry.abstract.DataModel {
|
class DhLevelTier extends foundry.abstract.DataModel {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,28 @@
|
||||||
import { LevelOptionType } from './levelTier.mjs';
|
import { LevelOptionType } from './levelTier.mjs';
|
||||||
|
|
||||||
export class DhLevelup extends foundry.abstract.DataModel {
|
export class DhLevelup extends foundry.abstract.DataModel {
|
||||||
static initializeData(levelTierData, levelChoices) {
|
static initializeData(levelTierData, pcLevelData) {
|
||||||
|
const availableChoicesPerLevel = levelTierData.availableChoicesPerLevel;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tiers: Object.keys(levelTierData.tiers).reduce((acc, key) => {
|
tiers: Object.keys(levelTierData.tiers).reduce((acc, key) => {
|
||||||
acc[key] = DhLevelupTier.initializeData(levelTierData.tiers[key]);
|
acc[key] = DhLevelupTier.initializeData(
|
||||||
|
levelTierData.tiers[key],
|
||||||
|
pcLevelData.selections.filter(x => x.tier === key),
|
||||||
|
pcLevelData.level.changed
|
||||||
|
);
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {})
|
}, {}),
|
||||||
|
maxSelections: [...Array(pcLevelData.level.changed).keys()].reduce((acc, index) => {
|
||||||
|
const level = index + 1;
|
||||||
|
const availableChoices = availableChoicesPerLevel[level];
|
||||||
|
if (level > 1 && availableChoices) {
|
||||||
|
acc += availableChoices;
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, 0)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -15,33 +30,73 @@ export class DhLevelup extends foundry.abstract.DataModel {
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tiers: new fields.TypedObjectField(new fields.EmbeddedDataField(DhLevelupTier))
|
tiers: new fields.TypedObjectField(new fields.EmbeddedDataField(DhLevelupTier)),
|
||||||
|
maxSelections: new fields.NumberField({ required: true, integer: true })
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get totalSelections() {
|
get levelSelections() {
|
||||||
return Object.values(this.tiers).reduce((acc, tier) => acc + tier.nrSelections, 0);
|
return Object.values(this.tiers).reduce(
|
||||||
|
(acc, tier) => {
|
||||||
|
acc.total += tier.selections.total;
|
||||||
|
for (var key in tier.selections.selections) {
|
||||||
|
const nrSelections = tier.selections.selections[key];
|
||||||
|
|
||||||
|
if (acc.selections[key]) acc.selections[key] += nrSelections;
|
||||||
|
else acc.selections[key] = nrSelections;
|
||||||
|
}
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
},
|
||||||
|
{ total: 0, selections: {} }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
get playerData() {
|
||||||
|
return Object.keys(this.tiers).flatMap(tierKey => {
|
||||||
|
const tier = this.tiers[tierKey];
|
||||||
|
return Object.keys(tier.levels).flatMap(levelKey => {
|
||||||
|
const level = tier.levels[levelKey];
|
||||||
|
return Object.keys(level.optionSelections).flatMap(optionSelectionKey => {
|
||||||
|
const selection = level.optionSelections[optionSelectionKey];
|
||||||
|
const optionSelect = tier.options[optionSelectionKey];
|
||||||
|
|
||||||
|
return Object.keys(selection).map(checkboxNr => ({
|
||||||
|
tier: Number(tierKey),
|
||||||
|
level: Number(levelKey),
|
||||||
|
optionKey: optionSelectionKey,
|
||||||
|
type: optionSelect.type,
|
||||||
|
checkboxNr: Number(checkboxNr),
|
||||||
|
value: optionSelect.value,
|
||||||
|
amount: optionSelect.amount
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DhLevelupTier extends foundry.abstract.DataModel {
|
class DhLevelupTier extends foundry.abstract.DataModel {
|
||||||
static initializeData(levelTier, levelChoices) {
|
static initializeData(levelTier, pcLevelData, pcLevel) {
|
||||||
const levels = {};
|
const levels = {};
|
||||||
const levelEndCap = levelTier.levels.end + 1;
|
const levelEndCap = levelTier.levels.end + 1;
|
||||||
for (var level = levelTier.levels.start; level < levelEndCap; level++) {
|
for (var level = levelTier.levels.start; level < levelEndCap; level++) {
|
||||||
levels[level] = DhLevelupLevel.initializeData(levelTier.availableOptions, levelTier.options);
|
levels[level] = DhLevelupLevel.initializeData(
|
||||||
|
levelTier.availableOptions,
|
||||||
|
pcLevelData.filter(x => x.level === level)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
tier: levelTier.tier,
|
tier: levelTier.tier,
|
||||||
name: levelTier.name,
|
name: levelTier.name,
|
||||||
|
active: pcLevel >= levelTier.levels.start,
|
||||||
options: Object.keys(levelTier.options).reduce((acc, key) => {
|
options: Object.keys(levelTier.options).reduce((acc, key) => {
|
||||||
acc[key] = levelTier.options[key];
|
acc[key] = levelTier.options[key];
|
||||||
|
|
||||||
return acc;
|
return acc;
|
||||||
}, {}),
|
}, {}),
|
||||||
levels: levels,
|
levels: levels
|
||||||
maxSelections: levelTier.availableOptions * (levelEndCap - levelTier.levels.start)
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -51,14 +106,22 @@ class DhLevelupTier extends foundry.abstract.DataModel {
|
||||||
return {
|
return {
|
||||||
tier: new fields.NumberField({ required: true, integer: true }),
|
tier: new fields.NumberField({ required: true, integer: true }),
|
||||||
name: new fields.StringField({ required: true }),
|
name: new fields.StringField({ required: true }),
|
||||||
|
active: new fields.BooleanField({ required: true, initial: true }),
|
||||||
options: new fields.TypedObjectField(new fields.EmbeddedDataField(DhLevelupTierOption)),
|
options: new fields.TypedObjectField(new fields.EmbeddedDataField(DhLevelupTierOption)),
|
||||||
levels: new fields.TypedObjectField(new fields.EmbeddedDataField(DhLevelupLevel)),
|
levels: new fields.TypedObjectField(new fields.EmbeddedDataField(DhLevelupLevel))
|
||||||
maxSelections: new fields.NumberField({ required: true, integer: true })
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get nrSelections() {
|
get selections() {
|
||||||
return Object.values(this.levels).reduce((acc, level) => acc + level.nrSelections, 0);
|
const allSelections = Object.keys(this.levels).reduce((acc, key) => {
|
||||||
|
acc[key] = this.levels[key].nrSelections;
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
return {
|
||||||
|
selections: allSelections,
|
||||||
|
total: Object.values(allSelections).reduce((acc, nr) => acc + nr, 0)
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Data to render all options in a Tier from */
|
/* Data to render all options in a Tier from */
|
||||||
|
|
@ -69,14 +132,17 @@ class DhLevelupTier extends foundry.abstract.DataModel {
|
||||||
label: game.i18n.localize(option.label),
|
label: game.i18n.localize(option.label),
|
||||||
checkboxes: [...Array(option.checkboxQuantity).keys()].map(checkboxNr => {
|
checkboxes: [...Array(option.checkboxQuantity).keys()].map(checkboxNr => {
|
||||||
const levelId = Object.keys(this.levels).find(levelKey => {
|
const levelId = Object.keys(this.levels).find(levelKey => {
|
||||||
Object.values(this.levels[levelKey].optionSelections).some(nr => nr === checkboxNr);
|
const optionSelect = this.levels[levelKey].optionSelections;
|
||||||
|
return Object.keys(optionSelect)
|
||||||
|
.filter(key => key === optionKey)
|
||||||
|
.some(optionKey => optionSelect[optionKey][checkboxNr]);
|
||||||
});
|
});
|
||||||
return {
|
return {
|
||||||
...option,
|
...option,
|
||||||
tier: this.tier,
|
tier: this.tier,
|
||||||
level: levelId,
|
level: levelId,
|
||||||
selected: Boolean(levelId),
|
selected: Boolean(levelId),
|
||||||
optionkey: optionKey,
|
optionKey: optionKey,
|
||||||
checkboxNr: checkboxNr
|
checkboxNr: checkboxNr
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
@ -101,10 +167,15 @@ class DhLevelupTierOption extends foundry.abstract.DataModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
class DhLevelupLevel extends foundry.abstract.DataModel {
|
class DhLevelupLevel extends foundry.abstract.DataModel {
|
||||||
static initializeData(maxSelections, levelOptions, levelChoices) {
|
static initializeData(maxSelections, levelData) {
|
||||||
return {
|
return {
|
||||||
maxSelections: maxSelections,
|
maxSelections: maxSelections,
|
||||||
optionSelections: {} // collate levelOption and levelChoices,
|
optionSelections: levelData.reduce((acc, data) => {
|
||||||
|
if (!acc[data.optionkey]) acc[data.optionKey] = {};
|
||||||
|
acc[data.optionKey][data.checkboxNr] = true;
|
||||||
|
|
||||||
|
return acc;
|
||||||
|
}, {})
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -114,14 +185,15 @@ class DhLevelupLevel extends foundry.abstract.DataModel {
|
||||||
return {
|
return {
|
||||||
maxSelections: new fields.NumberField({ required: true, integer: true }),
|
maxSelections: new fields.NumberField({ required: true, integer: true }),
|
||||||
optionSelections: new fields.TypedObjectField(
|
optionSelections: new fields.TypedObjectField(
|
||||||
new fields.SchemaField({
|
new fields.TypedObjectField(new fields.BooleanField({ required: true, initial: true }))
|
||||||
checkboxNr: new fields.NumberField({ required: true, integer: true })
|
|
||||||
})
|
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
get nrSelections() {
|
get nrSelections() {
|
||||||
return this.optionSelections.length;
|
return Object.keys(this.optionSelections).reduce(
|
||||||
|
(acc, optionKey) => acc + Object.keys(this.optionSelections[optionKey]).length,
|
||||||
|
0
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { getPathValue, getTier } from '../helpers/utils.mjs';
|
import { getPathValue, getTier } from '../helpers/utils.mjs';
|
||||||
|
import { LevelOptionType } from './levelTier.mjs';
|
||||||
|
|
||||||
const fields = foundry.data.fields;
|
const fields = foundry.data.fields;
|
||||||
|
|
||||||
|
|
@ -97,8 +98,8 @@ export default class DhpPC extends foundry.abstract.TypeDataModel {
|
||||||
armorMarks: new fields.SchemaField({
|
armorMarks: new fields.SchemaField({
|
||||||
max: new fields.NumberField({ initial: 6, integer: true }),
|
max: new fields.NumberField({ initial: 6, integer: true }),
|
||||||
value: new fields.NumberField({ initial: 0, integer: true })
|
value: new fields.NumberField({ initial: 0, integer: true })
|
||||||
})
|
}),
|
||||||
// levelUpData: new fields.TypeDataModel(DhpLevelUpData),
|
levelData: new fields.EmbeddedDataField(DhPCLevelData)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -509,3 +510,29 @@ export default class DhpPC extends foundry.abstract.TypeDataModel {
|
||||||
else return 0;
|
else return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DhPCLevelData extends foundry.abstract.DataModel {
|
||||||
|
static defineSchema() {
|
||||||
|
return {
|
||||||
|
level: new fields.SchemaField({
|
||||||
|
current: new fields.NumberField({ required: true, integer: true, initial: 1 }),
|
||||||
|
changed: new fields.NumberField({ required: true, integer: true, initial: 1 })
|
||||||
|
}),
|
||||||
|
selections: new fields.ArrayField(
|
||||||
|
new fields.SchemaField({
|
||||||
|
tier: new fields.NumberField({ required: true, integer: true }),
|
||||||
|
level: new fields.NumberField({ required: true, integer: true }),
|
||||||
|
optionKey: new fields.StringField({ required: true }),
|
||||||
|
type: new fields.StringField({ required: true, choices: LevelOptionType }),
|
||||||
|
checkboxNr: new fields.NumberField({ required: true, integer: true }),
|
||||||
|
value: new fields.NumberField({ integer: true }),
|
||||||
|
amount: new fields.NumberField({ integer: true })
|
||||||
|
})
|
||||||
|
)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
get canLevelUp() {
|
||||||
|
return this.level.current < this.level.updated;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2797,18 +2797,33 @@ div.daggerheart.views.multiclass {
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||||
}
|
}
|
||||||
|
.daggerheart.levelup .tiers-container .tier-container.inactive {
|
||||||
|
opacity: 0.4;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
.daggerheart.levelup .tiers-container .tier-container legend {
|
.daggerheart.levelup .tiers-container .tier-container legend {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 0 12px;
|
||||||
}
|
}
|
||||||
.daggerheart.levelup .tiers-container .tier-container .checkbox-group-container {
|
.daggerheart.levelup .tiers-container .tier-container .checkbox-group-container {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 3fr 1fr;
|
grid-template-columns: 1fr 2.3fr;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
}
|
}
|
||||||
.daggerheart.levelup .tiers-container .tier-container .checkbox-group-container .checkboxes-container {
|
.daggerheart.levelup .tiers-container .tier-container .checkbox-group-container .checkboxes-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
|
justify-content: end;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .tiers-container .tier-container .checkbox-group-container .checkbox-group-label {
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
.daggerheart.levelup .levelup-footer {
|
||||||
|
display: flex;
|
||||||
}
|
}
|
||||||
.application.sheet.daggerheart.dh-style.feature .item-sheet-header {
|
.application.sheet.daggerheart.dh-style.feature .item-sheet-header {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
|
||||||
|
|
@ -13,14 +13,22 @@
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
background-image: url('../assets/parchments/dh-parchment-dark.png');
|
||||||
|
|
||||||
|
&.inactive {
|
||||||
|
opacity: 0.4;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
legend {
|
legend {
|
||||||
margin-left: auto;
|
margin-left: auto;
|
||||||
margin-right: auto;
|
margin-right: auto;
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: bold;
|
||||||
|
padding: 0 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.checkbox-group-container {
|
.checkbox-group-container {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 3fr 1fr;
|
grid-template-columns: 1fr 2.3fr;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
|
|
||||||
.checkbox-group-container-title {
|
.checkbox-group-container-title {
|
||||||
|
|
@ -29,8 +37,18 @@
|
||||||
.checkboxes-container {
|
.checkboxes-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 4px;
|
gap: 4px;
|
||||||
|
justify-content: end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-group-label {
|
||||||
|
font-size: 14px;
|
||||||
|
font-style: italic;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.levelup-footer {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -43,8 +43,8 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="level-container {{#if document.system.canLevelUp}}levelup{{/if}}">
|
<div class="level-container {{#if document.system.canLevelUp}}levelup{{/if}}">
|
||||||
<div class="level-value-container">
|
<div class="level-value-container">
|
||||||
<input class="level-value {{#if document.system.canLevelUp}}levelup{{/if}}" name="system.levelData.changedLevel" value="{{document.system.levelData.changedLevel}}" type="text" data-dtype="Number" />
|
<input class="level-value {{#if document.system.levelData.canLevelUp}}levelup{{/if}}" name="system.levelData.level.changed" value="{{document.system.levelData.level.changed}}" type="text" data-dtype="Number" />
|
||||||
{{#if document.system.canLevelUp}}<div class="levelup-marker {{#if (gte document.system.levels.current 10)}}double-digit{{/if}}">*</div>{{/if}}
|
{{#if document.system.levelData.canLevelUp}}<div class="levelup-marker">*</div>{{/if}}
|
||||||
</div>
|
</div>
|
||||||
<img src="systems/daggerheart/assets/AttributeShield.svg" />
|
<img src="systems/daggerheart/assets/AttributeShield.svg" />
|
||||||
<div class="level-title {{#if document.system.canLevelUp}}levelup{{/if}}">{{localize "DAGGERHEART.Sheets.PC.Level"}}</div>
|
<div class="level-title {{#if document.system.canLevelUp}}levelup{{/if}}">{{localize "DAGGERHEART.Sheets.PC.Level"}}</div>
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,25 @@
|
||||||
<div>
|
<div>
|
||||||
<div class="tiers-container">
|
<div class="tiers-container">
|
||||||
{{#each this.levelup.tiers as |tier key|}}
|
{{#each this.levelup.tiers as |tier key|}}
|
||||||
<fieldset class="tier-container">
|
<fieldset class="tier-container {{#if (not tier.active)}}inactive{{/if}}">
|
||||||
<legend>{{tier.name}}</legend>
|
<legend>{{tier.name}}</legend>
|
||||||
|
|
||||||
{{#each tier.tierCheckboxGroups}}
|
{{#each tier.tierCheckboxGroups}}
|
||||||
<div class="checkbox-group-container">
|
<div class="checkbox-group-container">
|
||||||
{{this.label}}
|
<div class="checkboxes-container">
|
||||||
<div class="checkboxex-container">
|
|
||||||
{{#each this.checkboxes}}
|
{{#each this.checkboxes}}
|
||||||
<input type="checkbox" {{checked this.selected}} />
|
<input type="checkbox" class="selection-checkbox" data-tier="{{this.tier}}" data-level="{{this.level}}" data-option="{{this.optionKey}}" data-checkbox-nr="{{this.checkboxNr}}" {{checked this.selected}} />
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="checkbox-group-label">{{this.label}}</div>
|
||||||
</div>
|
</div>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</fieldset>
|
</fieldset>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</div>
|
</div>
|
||||||
|
<footer class="levelup-footer">
|
||||||
|
<button data-action="save">{{localize "Finish Levelup"}}</button>
|
||||||
|
</footer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{!-- <div class="flex-col">
|
{{!-- <div class="flex-col">
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue