const { HTMLField, SchemaField, NumberField, StringField, BooleanField, FilePathField, ArrayField } = foundry.data.fields; import { getBossMutation, nullStorylineKey, storylineKeys } from './boss.mjs'; import { nullPlaybookKey, playbookKeys, lookupPlaybook, getPlaybookMutation } from './playbooks.mjs'; const textField = () => new StringField({ required: true, blank: true }); const promptField = () => new SchemaField({ question: textField(), answer: textField() }); const markableField = () => new SchemaField({ marked: new BooleanField({ required: true, initial: false }), description: textField(), }); const arrayFieldValidator = (maxSize) => { return (val, opts) => { return !(val?.length > maxSize); }; }; const cappedArrayField = (innerField, maxSize, defaultValue = undefined) => new ArrayField(innerField, { required: true, nullable: false, initial: defaultValue ? Array(maxSize).fill(defaultValue) : [], validate: arrayFieldValidator(maxSize), }); const moveField = () => new SchemaField({ marked: new BooleanField({ required: true, initial: false }), name: textField(), description: textField(), hasWriteIn: new BooleanField({ required: true, initial: false }), writein: textField(), }); const nullPlaybook = lookupPlaybook(nullPlaybookKey); export class HenchDataModel extends foundry.abstract.TypeDataModel { static defineSchema() { return { look: textField(), details: cappedArrayField(promptField(), 2), fixedInclinations: cappedArrayField(textField(), 2), customInclination: textField(), missionPlanning: cappedArrayField(textField(), 3), gearLimit: new NumberField({ required: true, integer: true, min: 3, initial: 3, max: 5 }), fixedGear: cappedArrayField(markableField(), 9), customGear: markableField(), harm: new SchemaField({ levelOne: cappedArrayField(markableField(), 2, { marked: false, description: "" }), levelTwo: cappedArrayField(markableField(), 2, { marked: false, description: "" }), levelThree: cappedArrayField(markableField(), 1, { marked: false, description: "" }), levelFour: cappedArrayField(markableField(), 1, { marked: false, description: "" }), }), stress: new NumberField({ required: true, integer: true, min: 0, initial: 0, max: 12 }), moves: cappedArrayField(moveField(), 5), customMove: moveField(), experienceTriggers: cappedArrayField(markableField(), 4), experience: new NumberField({ required: true, integer: true, min: 0, initial: 0, max: 5 }), baseAdvancements: cappedArrayField(markableField(), 5), exAdvancements: cappedArrayField(markableField(), 3), playbook: new StringField({ required: true, blank: false, initial: nullPlaybookKey, options: playbookKeys }), }; } static migrateData(source) { // No migrations yet - base case. return super.migrateData(source); } /** @override */ async _preCreate(data, options, user) { await super._preCreate(data, options, user); const addPlaybookInfoMutation = getPlaybookMutation(data.playbook); return this.updateSource(addPlaybookInfoMutation); } get dead() { return !!this.harm.levelFour.marked; } get hasPlaybookSelected() { return this.playbook !== nullPlaybookKey; } get inclinations() { const base = this.fixedInclinations ?? []; return [ ...base, this.customInclination ]; } get gear() { const base = this.fixedGear ?? []; return [ ...base, this.customGear, ]; } } export class BossDataModel extends foundry.abstract.TypeDataModel { static defineSchema() { return { look: textField(), details: cappedArrayField(promptField(), 4), storyline: new StringField({ required: true, blank: false, initial: nullStorylineKey, options: storylineKeys}), heat: new NumberField({ required: true, integer: true, min: 0, initial: 0, max: 18}), experienceTriggers: cappedArrayField(markableField(), 4), experience: new NumberField({ required: true, integer: true, min: 0, initial: 0, max: 5 }), moves: cappedArrayField(moveField(), 13), }; } static migrateData(source) { // no migrations yet return super.migrateData(source); } /** @override */ async _preCreate(data, options, user) { await super._preCreate(data, options, user); const initMutation = getBossMutation(); return this.updateSource(initMutation); } get tier() { const divisor = 18 / 3; return Math.ceil(this.heat / divisor); } }