Skip to content

Commit

Permalink
[foundryvtt#1401] Add ability to configure spells added by ItemChoice…
Browse files Browse the repository at this point in the history
… advancements
  • Loading branch information
arbron committed Dec 29, 2022
1 parent c7e77d6 commit 7a1a9e9
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 11 deletions.
66 changes: 55 additions & 11 deletions module/advancement/types/item-choice.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ export class ItemChoiceAdvancement extends Advancement {
choices: {},
allowDrops: true,
type: null,
pool: []
pool: [],
spell: null
}
},
order: 50,
Expand Down Expand Up @@ -91,17 +92,24 @@ export 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) : {};
for ( const [uuid, selected] of Object.entries(data) ) {
if ( !selected ) continue;
const item = retainedData[uuid] ? new Item.implementation(retainedData[uuid]) : (await fromUuid(uuid))?.clone();
if ( !item ) continue;
item.updateSource({
_id: retainedData[uuid]?._id ?? foundry.utils.randomID(),
"flags.dnd5e.sourceId": uuid,
"flags.dnd5e.advancementOrigin": `${this.item.id}.${this.id}`
});
items.push(item.toObject());
updates[item.id] = uuid;

let itemData = retainedData[uuid];
if ( !itemData ) {
const source = await fromUuid(uuid);
if ( !source ) continue;
itemData = source.clone({
_id: foundry.utils.randomID(),
"flags.dnd5e.sourceId": uuid,
"flags.dnd5e.advancementOrigin": `${this.item.id}.${this.id}`
}, {keepId: true}).toObject();
}
foundry.utils.mergeObject(itemData, spellChanges);

items.push(itemData);
updates[itemData._id] = uuid;
}
this.actor.updateSource({items});
this.updateSource({[`value.${level}`]: updates});
Expand Down Expand Up @@ -161,6 +169,7 @@ export 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";
return data;
}

Expand Down Expand Up @@ -337,7 +346,26 @@ export class ItemChoiceFlow extends AdvancementFlow {
// If the item is already been marked as selected, no need to go further
if ( this.selected.has(item.uuid) ) return false;

// TODO: Check to ensure the dropped item hasn't been selected at a lower level
// 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) ) {
if ( level >= this.level ) continue;
if ( Object.values(data).includes(item.uuid) ) return;
}

// If spell level is restricted, ensure the spell is of the appropriate level
const spellLevel = this.advancement.data.configuration.spell?.level;
if ( type === "spell" && spellLevel ) {
if ( spellLevel === "available") {
const maxSlot = this._maxSpellSlotLevel();
if ( item.system.level > maxSlot ) return ui.notifications.warn(game.i18n.format(
"DND5E.AdvancementItemChoiceSpellLevelAvailableWarning", { level: CONFIG.DND5E.spellLevels[maxSlot] }
));
} else if ( item.system.level !== parseInt(spellLevel) ) {
return ui.notifications.warn(game.i18n.format(
"DND5E.AdvancementItemChoiceSpellLevelSpecificWarning", { level: CONFIG.DND5E.spellLevels[spellLevel] }
));
}
}

// Mark the item as selected
this.selected.add(item.uuid);
Expand All @@ -353,6 +381,22 @@ export class ItemChoiceFlow extends AdvancementFlow {

/* -------------------------------------------- */

/**
* Determine the maximum spell slot level for the actor to which this advancement is being applied.
* @returns {number}
*/
_maxSpellSlotLevel() {
const largestSlot = Object.entries(this.advancement.actor.system.spells).reduce((slot, [key, data]) => {
if ( data.max === 0 ) return slot;
const level = parseInt(key.replace("spell", ""));
if ( !Number.isNaN(level) && level > slot ) return level;
return slot;
}, -1);
return Math.max(this.advancement.actor.system.spells.pact.level, largestSlot);
}

/* -------------------------------------------- */

/** @inheritdoc */
async _updateObject(event, formData) {
const retainedData = this.retainedData?.items.reduce((obj, i) => {
Expand Down
4 changes: 4 additions & 0 deletions templates/advancement/item-choice-config.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@
<p class="hint">{{localize "DND5E.AdvancementItemChoiceTypeHint"}}</p>
</div>

{{#if showSpellConfig}}
{{> "dnd5e.advancement-spell-config"}}
{{/if}}

<div class="drop-target">
<ol class="items-list">
<li class="items-header flexrow"><h3 class="item-name">{{localize "DOCUMENT.Items"}}</h3></li>
Expand Down

0 comments on commit 7a1a9e9

Please sign in to comment.