mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-11 19:25:21 +01:00
277 lines
No EOL
14 KiB
JavaScript
277 lines
No EOL
14 KiB
JavaScript
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 = {
|
|
id: "daggerheart-levelup",
|
|
classes: ["daggerheart", "views", "levelup"],
|
|
position: { width: 1200, height: 'auto' },
|
|
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();
|
|
}
|
|
} |