153 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			153 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 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: 3, max: 8 }),
 | |
| 
 | |
|             moves: cappedArrayField(moveField(), 5),
 | |
|             customMove: moveField(),
 | |
| 
 | |
|             experienceTriggers: cappedArrayField(markableField(), 5),
 | |
|             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) {
 | |
|         // New stress cap
 | |
|         if(source.stress) {
 | |
|             source.stress = Math.min(source.stress, 8);
 | |
|         }
 | |
| 
 | |
|         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) {
 | |
|         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);
 | |
|     }
 | |
| } | 
