From 436d3354630a6385e5b07332f759f8f424a993da Mon Sep 17 00:00:00 2001 From: Jeff Hitchcock Date: Tue, 18 Oct 2022 13:53:36 -0700 Subject: [PATCH] [#1401] Update ItemChoice using new Advancement data models --- lang/en.json | 1 + .../advancement/item-choice-config.mjs | 4 +-- .../advancement/item-choice-flow.mjs | 16 +++++------ module/data/advancement/_module.mjs | 1 + module/data/advancement/item-choice.mjs | 27 +++++++++++++++++++ module/documents/advancement/item-choice.mjs | 20 +++++--------- templates/advancement/item-choice-config.hbs | 20 +++++++------- templates/advancement/item-choice-flow.hbs | 6 ++--- 8 files changed, 59 insertions(+), 36 deletions(-) create mode 100644 module/data/advancement/item-choice.mjs diff --git a/lang/en.json b/lang/en.json index 8e0e06f7ad..932662fca4 100644 --- a/lang/en.json +++ b/lang/en.json @@ -124,6 +124,7 @@ "DND5E.AdvancementDeleteConfirmationMessage": "Deleting this item will also delete all advancement choices made for it. These changes will be removed from your character as long as the checkbox below is checked.", "DND5E.AdvancementDeleteConfirmationTitle": "Confirm Deletion", "DND5E.AdvancementFlowDropAreaHint": "Drop an Item here to choose it.", +"DND5E.AdvancementHint": "Hint", "DND5E.AdvancementHitPointsTitle": "Hit Points", "DND5E.AdvancementHitPointsHint": "Track the player's hit points for each level in the class.", "DND5E.AdvancementHitPointsAverage": "Take Average", diff --git a/module/applications/advancement/item-choice-config.mjs b/module/applications/advancement/item-choice-config.mjs index 1572836365..e8ffbe5a39 100644 --- a/module/applications/advancement/item-choice-config.mjs +++ b/module/applications/advancement/item-choice-config.mjs @@ -23,7 +23,7 @@ export default class ItemChoiceConfig extends AdvancementConfig { for ( const type of this.advancement.constructor.VALID_TYPES ) { data.validTypes[type] = game.i18n.localize(`ITEM.Type${type.capitalize()}`); } - data.showSpellConfig = this.advancement.data.configuration.type === "spell"; + data.showSpellConfig = this.advancement.configuration.type === "spell"; return data; } @@ -34,7 +34,7 @@ export default class ItemChoiceConfig extends AdvancementConfig { if ( configuration.choices ) configuration.choices = this.constructor._cleanedObject(configuration.choices); // Ensure items are still valid if type restriction or spell restriction are changed - configuration.pool ??= this.advancement.data.configuration.pool; + configuration.pool ??= this.advancement.configuration.pool; configuration.pool = await configuration.pool.reduce(async (pool, uuid) => { const item = await fromUuid(uuid); if ( this.advancement._verifyItemType(item, { diff --git a/module/applications/advancement/item-choice-flow.mjs b/module/applications/advancement/item-choice-flow.mjs index 8db8bcf1f3..564f6cc83a 100644 --- a/module/applications/advancement/item-choice-flow.mjs +++ b/module/applications/advancement/item-choice-flow.mjs @@ -40,9 +40,9 @@ export default class ItemChoiceFlow extends AdvancementFlow { // Prepare initial data this.selected ??= new Set( this.retainedData?.items.map(i => foundry.utils.getProperty(i, "flags.dnd5e.sourceId")) - ?? Object.values(this.advancement.data.value[this.level] ?? {}) + ?? Object.values(this.advancement.value[this.level] ?? {}) ); - this.pool ??= await Promise.all(this.advancement.data.configuration.pool.map(fromUuid)); + this.pool ??= await Promise.all(this.advancement.configuration.pool.map(fromUuid)); this.dropped ??= await (this.retainedData?.items ?? []).reduce(async (arrP, data) => { const arr = await arrP; const uuid = foundry.utils.getProperty(data, "flags.dnd5e.sourceId"); @@ -54,12 +54,12 @@ export default class ItemChoiceFlow extends AdvancementFlow { return arr; }, []); - const max = this.advancement.data.configuration.choices[this.level]; + const max = this.advancement.configuration.choices[this.level]; const choices = { max, current: this.selected.size, full: this.selected.size >= max }; const previousLevels = {}; const previouslySelected = new Set(); - for ( const [level, data] of Object.entries(this.advancement.data.value) ) { + for ( const [level, data] of Object.entries(this.advancement.value) ) { if ( level > this.level ) continue; previousLevels[level] = await Promise.all(Object.values(data).map(fromUuid)); Object.values(data).forEach(uuid => previouslySelected.add(uuid)); @@ -127,7 +127,7 @@ export default class ItemChoiceFlow extends AdvancementFlow { /** @inheritdoc */ async _onDrop(event) { - if ( this.selected.size >= this.advancement.data.configuration.choices[this.level] ) return false; + if ( this.selected.size >= this.advancement.configuration.choices[this.level] ) return false; // Try to extract the data let data; @@ -150,14 +150,14 @@ export default class ItemChoiceFlow extends AdvancementFlow { if ( this.selected.has(item.uuid) ) return false; // Check to ensure the dropped item hasn't been selected at a lower level - for ( const [level, data] of Object.entries(this.advancement.data.value) ) { + for ( const [level, data] of Object.entries(this.advancement.value) ) { if ( level >= this.level ) continue; if ( Object.values(data).includes(item.uuid) ) return; } // If spell level is restricted to available level, ensure the spell is of the appropriate level - const spellLevel = this.advancement.data.configuration.spell?.level; - if ( (this.advancement.data.configuration.type === "spell") && spellLevel === "available" ) { + const spellLevel = this.advancement.configuration.spell?.level; + if ( (this.advancement.configuration.type === "spell") && spellLevel === "available" ) { const maxSlot = this._maxSpellSlotLevel(); if ( item.system.level > maxSlot ) return ui.notifications.error(game.i18n.format( "DND5E.AdvancementItemChoiceSpellLevelAvailableWarning", { level: CONFIG.DND5E.spellLevels[maxSlot] } diff --git a/module/data/advancement/_module.mjs b/module/data/advancement/_module.mjs index 8d42742bda..ae75e937bd 100644 --- a/module/data/advancement/_module.mjs +++ b/module/data/advancement/_module.mjs @@ -1,5 +1,6 @@ export {default as BaseAdvancement} from "./base-advancement.mjs"; export {default as SpellConfigurationData} from "./spell-config.mjs"; +export {default as ItemChoiceConfigurationData} from "./item-choice.mjs"; export {default as ItemGrantConfigurationData} from "./item-grant.mjs"; export * as scaleValue from "./scale-value.mjs"; diff --git a/module/data/advancement/item-choice.mjs b/module/data/advancement/item-choice.mjs new file mode 100644 index 0000000000..6f0bbe5846 --- /dev/null +++ b/module/data/advancement/item-choice.mjs @@ -0,0 +1,27 @@ +import { MappingField } from "../fields.mjs"; +import SpellConfigurationData from "./spell-config.mjs"; + +export default class ItemChoiceConfigurationData extends foundry.abstract.DataModel { + static defineSchema() { + return { + hint: new foundry.data.fields.StringField({label: "DND5E.AdvancementHint"}), + choices: new MappingField(new foundry.data.fields.NumberField(), { + required: true, hint: "DND5E.AdvancementItemChoiceLevelsHint" + }), + allowDrops: new foundry.data.fields.BooleanField({ + required: true, initial: true, label: "DND5E.AdvancementConfigureAllowDrops", + hint: "DND5E.AdvancementConfigureAllowDropsHint" + }), + type: new foundry.data.fields.StringField({ + required: true, blank: false, nullable: true, initial: null, + label: "DND5E.AdvancementItemChoiceType", hint: "DND5E.AdvancementItemChoiceTypeHint" + }), + pool: new foundry.data.fields.ArrayField(new foundry.data.fields.StringField(), { + required: true, label: "DOCUMENT.Items" + }), + spell: new foundry.data.fields.EmbeddedDataField(SpellConfigurationData, { + required: true, nullable: true, initial: null + }) + }; + } +} diff --git a/module/documents/advancement/item-choice.mjs b/module/documents/advancement/item-choice.mjs index 986bdc925b..ccd6d8878d 100644 --- a/module/documents/advancement/item-choice.mjs +++ b/module/documents/advancement/item-choice.mjs @@ -1,6 +1,7 @@ import Advancement from "./advancement.mjs"; import ItemChoiceConfig from "../../applications/advancement/item-choice-config.mjs"; import ItemChoiceFlow from "../../applications/advancement/item-choice-flow.mjs"; +import ItemChoiceConfigurationData from "../../data/advancement/item-choice.mjs"; /** * Advancement that presents the player with a choice of multiple items that they can take. Keeps track of which @@ -11,15 +12,8 @@ export default class ItemChoiceAdvancement extends Advancement { /** @inheritdoc */ static get metadata() { return foundry.utils.mergeObject(super.metadata, { - defaults: { - configuration: { - hint: "", - choices: {}, - allowDrops: true, - type: null, - pool: [], - spell: null - } + dataModels: { + configuration: ItemChoiceConfigurationData }, order: 50, icon: "systems/dnd5e/icons/svg/item-choice.svg", @@ -90,7 +84,7 @@ export default class ItemChoiceAdvancement extends Advancement { async apply(level, data, retainedData={}) { const items = []; const updates = {}; - const spellChanges = this.data.configuration.spell ? this._prepareSpellChanges(this.data.configuration.spell) : {}; + const spellChanges = this.configuration.spell?.spellChanges ?? {}; for ( const [uuid, selected] of Object.entries(data) ) { if ( !selected ) continue; @@ -130,7 +124,7 @@ export default class ItemChoiceAdvancement extends Advancement { /** @inheritdoc */ reverse(level) { const items = []; - for ( const id of Object.keys(this.data.value[level] ?? {}) ) { + for ( const id of Object.keys(this.value[level] ?? {}) ) { const item = this.actor.items.get(id); if ( item ) items.push(item.toObject()); this.actor.items.delete(id); @@ -152,8 +146,8 @@ export default class ItemChoiceAdvancement extends Advancement { * @throws An error if the item is invalid and warn is `true`. */ _verifyItemType(item, { restriction, spellLevel, error=true }={}) { - restriction ??= this.data.configuration.type; - spellLevel ??= this.data.configuration.spell?.level; + restriction ??= this.configuration.type; + spellLevel ??= this.configuration.spell?.level; // Type restriction is set and the item type does not match the selected type if ( restriction && (restriction !== item.type) ) { diff --git a/templates/advancement/item-choice-config.hbs b/templates/advancement/item-choice-config.hbs index 4280954ab4..20056ef868 100644 --- a/templates/advancement/item-choice-config.hbs +++ b/templates/advancement/item-choice-config.hbs @@ -2,14 +2,14 @@
{{> "dnd5e.advancement-controls"}}
- - + +
- +

{{localize "DND5E.AdvancementConfigureAllowDropsHint"}}

@@ -17,8 +17,8 @@
- + {{selectOptions validTypes selected=configuration.type blank=(localize "DND5E.AdvancementItemChoiceTypeAny")}}
@@ -29,8 +29,8 @@
- + {{#select configuration.spell.level}} {{#each CONFIG.spellLevels as |label key|}} @@ -49,7 +49,7 @@
  1. {{localize "DOCUMENT.Items"}}

    1. - {{#each data.configuration.pool}} + {{#each configuration.pool}}
    2. {{{dnd5e-linkForUuid this}}}
      @@ -72,8 +72,8 @@
      - {{numberInput (lookup ../data.configuration.choices level) placeholder="0" - name=(concat "data.configuration.choices." level) min=1 step=1}} + {{numberInput (lookup ../configuration.choices level) placeholder="0" + name=(concat "configuration.choices." level) min=1 step=1}}
      {{/each}} diff --git a/templates/advancement/item-choice-flow.hbs b/templates/advancement/item-choice-flow.hbs index 1b734edca3..4ea914d9fb 100644 --- a/templates/advancement/item-choice-flow.hbs +++ b/templates/advancement/item-choice-flow.hbs @@ -3,8 +3,8 @@

      {{{title}}}

      - {{#if advancement.data.configuration.hint}} -

      {{advancement.data.configuration.hint}}

      + {{#if advancement.configuration.hint}} +

      {{advancement.configuration.hint}}

      {{/if}} {{#each previousLevels}} @@ -40,7 +40,7 @@
      {{/each}} - {{#if advancement.data.configuration.allowDrops}} + {{#if advancement.configuration.allowDrops}}

      {{localize "DND5E.AdvancementFlowDropAreaHint"}}

      {{/if}}