mirror of
https://github.com/Foundryborne/daggerheart.git
synced 2026-01-12 03:31:07 +01:00
276 lines
12 KiB
JavaScript
276 lines
12 KiB
JavaScript
import { chunkify } from '../helpers/utils.mjs';
|
|
import { LevelOptionType } from './levelTier.mjs';
|
|
|
|
export class DhLevelup extends foundry.abstract.DataModel {
|
|
static initializeData(levelTierData, pcLevelData) {
|
|
const startLevel = pcLevelData.level.current + 1;
|
|
const currentLevel = pcLevelData.level.current + 1;
|
|
const endLevel = pcLevelData.level.changed;
|
|
|
|
const tiers = {};
|
|
const levels = {};
|
|
const tierKeys = Object.keys(levelTierData.tiers);
|
|
tierKeys.forEach(key => {
|
|
const tier = levelTierData.tiers[key];
|
|
const belongingLevels = [];
|
|
for (var i = tier.levels.start; i <= tier.levels.end; i++) {
|
|
if (i <= endLevel) {
|
|
const initialAchievements = i === tier.levels.start ? tier.initialAchievements : {};
|
|
const experiences = initialAchievements.experience
|
|
? [...Array(initialAchievements.experience.nr).keys()].reduce((acc, _) => {
|
|
acc[foundry.utils.randomID()] = {
|
|
name: '',
|
|
modifier: initialAchievements.experience.modifier
|
|
};
|
|
return acc;
|
|
}, {})
|
|
: {};
|
|
const domainCards = [...Array(tier.domainCardByLevel).keys()].reduce((acc, _) => {
|
|
const id = foundry.utils.randomID();
|
|
acc[id] = { uuid: null, itemUuid: null, level: i };
|
|
return acc;
|
|
}, {});
|
|
|
|
levels[i] = DhLevelupLevel.initializeData(pcLevelData.levelups[i], tier.availableOptions, {
|
|
...initialAchievements,
|
|
experiences,
|
|
domainCards
|
|
});
|
|
}
|
|
|
|
belongingLevels.push(i);
|
|
}
|
|
|
|
tiers[key] = {
|
|
name: tier.name,
|
|
belongingLevels: belongingLevels,
|
|
options: Object.keys(tier.options).reduce((acc, key) => {
|
|
acc[key] = tier.options[key].toObject();
|
|
return acc;
|
|
}, {})
|
|
};
|
|
});
|
|
|
|
return {
|
|
tiers,
|
|
levels,
|
|
startLevel,
|
|
currentLevel,
|
|
endLevel
|
|
};
|
|
}
|
|
|
|
static defineSchema() {
|
|
const fields = foundry.data.fields;
|
|
|
|
return {
|
|
tiers: new fields.TypedObjectField(
|
|
new fields.SchemaField({
|
|
name: new fields.StringField({ required: true }),
|
|
belongingLevels: new fields.ArrayField(new fields.NumberField({ required: true, integer: true })),
|
|
options: new fields.TypedObjectField(
|
|
new fields.SchemaField({
|
|
label: new fields.StringField({ required: true }),
|
|
checkboxSelections: new fields.NumberField({ required: true, integer: true }),
|
|
minCost: new fields.NumberField({ required: true, integer: true }),
|
|
type: new fields.StringField({ required: true, choices: LevelOptionType }),
|
|
value: new fields.NumberField({ integer: true }),
|
|
amount: new fields.NumberField({ integer: true })
|
|
})
|
|
)
|
|
})
|
|
),
|
|
levels: new fields.TypedObjectField(new fields.EmbeddedDataField(DhLevelupLevel)),
|
|
startLevel: new fields.NumberField({ required: true, integer: true }),
|
|
currentLevel: new fields.NumberField({ required: true, integer: true }),
|
|
endLevel: new fields.NumberField({ required: true, integer: true }),
|
|
multiclass: new fields.SchemaField({
|
|
class: new fields.StringField({ required: true }),
|
|
domain: new fields.StringField(),
|
|
tier: new fields.NumberField({ required: true, integer: true }),
|
|
level: new fields.NumberField({ required: true, integer: true })
|
|
})
|
|
};
|
|
}
|
|
|
|
#levelFinished(levelKey) {
|
|
const allSelectionsMade = this.levels[levelKey].nrSelections.available === 0;
|
|
const allChoicesMade = Object.keys(this.levels[levelKey].choices).every(choiceKey => {
|
|
const choice = this.levels[levelKey].choices[choiceKey];
|
|
return Object.values(choice).every(checkbox => {
|
|
switch (choiceKey) {
|
|
case 'trait':
|
|
case 'experience':
|
|
case 'domainCard':
|
|
case 'subclass':
|
|
return checkbox.amount ? checkbox.data.length === checkbox.amount : checkbox.data.length === 1;
|
|
case 'multiclass':
|
|
const classSelected = checkbox.data.length === 1;
|
|
const domainSelected = checkbox.secondaryData;
|
|
return classSelected && domainSelected;
|
|
default:
|
|
return true;
|
|
}
|
|
});
|
|
});
|
|
const experiencesSelected = !this.levels[levelKey].achievements.experiences
|
|
? true
|
|
: Object.values(this.levels[levelKey].achievements.experiences).every(exp => exp.name);
|
|
const domainCardsSelected = Object.values(this.levels[levelKey].achievements.domainCards)
|
|
.filter(x => x.level <= this.endLevel)
|
|
.every(card => card.uuid);
|
|
const allAchievementsSelected = experiencesSelected && domainCardsSelected;
|
|
|
|
return allSelectionsMade && allChoicesMade && allAchievementsSelected;
|
|
}
|
|
|
|
get currentLevelFinished() {
|
|
return this.#levelFinished(this.currentLevel);
|
|
}
|
|
|
|
get allLevelsFinished() {
|
|
return Object.keys(this.levels)
|
|
.filter(level => Number(level) >= this.startLevel)
|
|
.every(this.#levelFinished.bind(this));
|
|
}
|
|
|
|
get tiersForRendering() {
|
|
const tierKeys = Object.keys(this.tiers);
|
|
const selections = Object.keys(this.levels).reduce(
|
|
(acc, key) => {
|
|
const level = this.levels[key];
|
|
Object.keys(level.choices).forEach(optionKey => {
|
|
const choice = level.choices[optionKey];
|
|
Object.keys(choice).forEach(checkboxNr => {
|
|
const checkbox = choice[checkboxNr];
|
|
if (!acc[checkbox.tier][optionKey]) acc[checkbox.tier][optionKey] = {};
|
|
Object.keys(choice).forEach(checkboxNr => {
|
|
acc[checkbox.tier][optionKey][checkboxNr] = { ...checkbox, level: Number(key) };
|
|
});
|
|
});
|
|
});
|
|
|
|
return acc;
|
|
},
|
|
tierKeys.reduce((acc, key) => {
|
|
acc[key] = {};
|
|
return acc;
|
|
}, {})
|
|
);
|
|
|
|
return tierKeys.map(tierKey => {
|
|
const tier = this.tiers[tierKey];
|
|
return {
|
|
name: tier.name,
|
|
active: this.currentLevel >= Math.min(...tier.belongingLevels),
|
|
groups: Object.keys(tier.options).map(optionKey => {
|
|
const option = tier.options[optionKey];
|
|
|
|
const checkboxes = [...Array(option.checkboxSelections).keys()].flatMap(index => {
|
|
const checkboxNr = index + 1;
|
|
const checkboxData = selections[tierKey]?.[optionKey]?.[checkboxNr];
|
|
const checkbox = { ...option, checkboxNr, tier: tierKey };
|
|
|
|
if (checkboxData) {
|
|
checkbox.level = checkboxData.level;
|
|
checkbox.selected = true;
|
|
checkbox.disabled = checkbox.level !== this.currentLevel;
|
|
}
|
|
|
|
return checkbox;
|
|
});
|
|
return {
|
|
label: game.i18n.localize(option.label),
|
|
checkboxGroups: chunkify(checkboxes, option.minCost, chunkedBoxes => {
|
|
const anySelected = chunkedBoxes.some(x => x.selected);
|
|
const anyDisabled = chunkedBoxes.some(x => x.disabled);
|
|
return {
|
|
multi: option.minCost > 1,
|
|
checkboxes: chunkedBoxes.map(x => ({
|
|
...x,
|
|
selected: anySelected,
|
|
disabled: anyDisabled
|
|
}))
|
|
};
|
|
})
|
|
};
|
|
})
|
|
};
|
|
});
|
|
}
|
|
}
|
|
|
|
export class DhLevelupLevel extends foundry.abstract.DataModel {
|
|
static initializeData(levelData = { selections: [] }, maxSelections, achievements) {
|
|
return {
|
|
maxSelections: maxSelections,
|
|
achievements: {
|
|
experiences: levelData.achievements?.experiences ?? achievements.experiences ?? {},
|
|
domainCards: levelData.achievements?.domainCards
|
|
? levelData.achievements.domainCards.reduce((acc, card, index) => {
|
|
acc[index] = { ...card };
|
|
return acc;
|
|
}, {})
|
|
: (achievements.domainCards ?? {}),
|
|
proficiency: levelData.achievements?.proficiency ?? achievements.proficiency ?? null
|
|
},
|
|
choices: levelData.selections.reduce((acc, data) => {
|
|
if (!acc[data.optionKey]) acc[data.optionKey] = {};
|
|
acc[data.optionKey][data.checkboxNr] = { ...data };
|
|
|
|
return acc;
|
|
}, {})
|
|
};
|
|
}
|
|
|
|
static defineSchema() {
|
|
const fields = foundry.data.fields;
|
|
|
|
return {
|
|
maxSelections: new fields.NumberField({ required: true, integer: true }),
|
|
achievements: new fields.SchemaField({
|
|
experiences: new fields.TypedObjectField(
|
|
new fields.SchemaField({
|
|
name: new fields.StringField({ required: true }),
|
|
modifier: new fields.NumberField({ required: true, integer: true })
|
|
})
|
|
),
|
|
domainCards: new fields.TypedObjectField(
|
|
new fields.SchemaField({
|
|
uuid: new fields.StringField({ required: true, nullable: true, initial: null }),
|
|
itemUuid: new fields.StringField({ required: true }),
|
|
level: new fields.NumberField({ required: true, integer: true })
|
|
})
|
|
),
|
|
proficiency: new fields.NumberField({ integer: true })
|
|
}),
|
|
choices: new fields.TypedObjectField(
|
|
new fields.TypedObjectField(
|
|
new fields.SchemaField({
|
|
tier: new fields.NumberField({ required: true, integer: true }),
|
|
minCost: new fields.NumberField({ required: true, integer: true }),
|
|
amount: new fields.NumberField({ integer: true }),
|
|
value: new fields.StringField(),
|
|
data: new fields.ArrayField(new fields.StringField()),
|
|
secondaryData: new fields.StringField(),
|
|
type: new fields.StringField({ required: true })
|
|
})
|
|
)
|
|
)
|
|
};
|
|
}
|
|
|
|
get nrSelections() {
|
|
const selections = Object.keys(this.choices).reduce((acc, choiceKey) => {
|
|
const choice = this.choices[choiceKey];
|
|
acc += Object.values(choice).reduce((acc, x) => acc + x.minCost, 0);
|
|
|
|
return acc;
|
|
}, 0);
|
|
|
|
return {
|
|
selections: selections,
|
|
available: this.maxSelections - selections
|
|
};
|
|
}
|
|
}
|