Skip to content

Commit

Permalink
Font of Magic Macro
Browse files Browse the repository at this point in the history
  • Loading branch information
MrPrimate committed Jun 16, 2024
1 parent d288dde commit e818e71
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 77 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Next Up

- Simple Macros: Spell Refueling Ring infusion macro.
- Simple Macros: Spell Refueling Ring infusion, and Font of Magic macro.

# 5.2.15

Expand Down
163 changes: 87 additions & 76 deletions macros/feats/fontOfMagic.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,86 +2,14 @@
// required modules: midi-qol
// number of points required to regain an nth level spell slot; {slot-level : point-cost}.

// midi changes to skip config dialog and not consume usage

Hooks.once("dnd5e.preUseItem", (item, config, options) => {
options.configureDialog = false;
return true;
});
Hooks.once("dnd5e.preItemUsageConsumption", (item, config, options) => {
config.consumeUsage = false;
return true;
});
// End of midi changes to macro

const conversion_map = {
"1": 2,
"2": 3,
"3": 5,
"4": 6,
"5": 7
};

const style = `
<style>
.font-of-magic .dialog-buttons {
flex-direction: column;
gap: 5px;
}
</style>`;

const sorceryPointsItem = actor.items.find((i) => i.name === "Sorcery Points");
const { value: spvalue, max: spmax } = sorceryPointsItem.system.uses;
const spells = foundry.utils.duplicate(actor.system.spells);

// array of spell levels for converting points to slots.
const valid_levels_with_spent_spell_slots = Object.entries(spells).filter(([key, { value, max }]) => {
const cost = conversion_map[key.at(-1)];
if (!cost || cost > spvalue) return false;
return (max > 0 && value < max);
});
// array of spell levels for converting slots to points.
const spell_levels_with_available_slots = Object.entries(spells).filter(([key, { value, max }]) => {
return (value > 0 && max > 0);
});

const is_missing_points = spvalue < spmax;
const is_missing_slots = valid_levels_with_spent_spell_slots.length > 0;

// has unspent spell slots.
const has_available_spell_slots = spell_levels_with_available_slots.length > 0;
// has sp equal to or higher than the minimum required.
const has_available_sorcery_points = spvalue >= Math.min(...Object.values(conversion_map));

const can_convert_slot_to_points = has_available_spell_slots && is_missing_points;
const can_convert_points_to_slot = has_available_sorcery_points && is_missing_slots;
if (!can_convert_points_to_slot && !can_convert_slot_to_points) {
ui.notifications.warn("You have no options available.");
return;
}

// set up available buttons.
const buttons = {};
if (can_convert_slot_to_points) buttons["slot_to_point"] = {
icon: "<i class='fa-solid fa-arrow-left'></i> <br>",
label: "Convert a spell slot to sorcery points",
callback: slot_to_points
};
if (can_convert_points_to_slot) buttons["point_to_slot"] = {
icon: "<i class='fa-solid fa-arrow-right'></i> <br>",
label: "Convert sorcery points to a spell slot",
callback: points_to_slot
};
new Dialog({ title: "Font of Magic", buttons }).render(true);

// Convert spell slot to sorcery points.
async function slot_to_points() {
const level = await new Promise((resolve) => {
// build buttons for each level where value, max > 0.
const slot_to_points_buttons = Object.fromEntries(spell_levels_with_available_slots.map(([key, { value, max }]) => {
const spell_level = key.at(-1);
return [key, { callback: () => {
resolve(spell_level);
resolve(spell_level);
}, label: `
<div class="flexrow">
<span>${CONFIG.DND5E.spellLevels[spell_level]} (${value}/${max})</span>
Expand All @@ -96,7 +24,7 @@ async function slot_to_points() {
<p>Pick a spell slot level to convert one spell slot to sorcery points (<strong>${spvalue}/${spmax}</strong>).
You regain a number of sorcery points equal to the level of the spell slot.</p>`,
close: () => {
resolve(0);
resolve(0);
}
}, {
classes: ["dialog", "font-of-magic"]
Expand Down Expand Up @@ -124,7 +52,7 @@ async function points_to_slot() {
const spell_level = key.at(-1);
const cost = conversion_map[spell_level];
return [key, { callback: () => {
resolve(spell_level);
resolve(spell_level);
}, label: `
<div class="flexrow">
<span>${CONFIG.DND5E.spellLevels[spell_level]} (${value}/${max})</span>
Expand All @@ -137,7 +65,7 @@ async function points_to_slot() {
buttons: points_to_slot_buttons,
content: style + `<p>Pick a spell slot level to regain from sorcery points (<strong>${spvalue}/${spmax}</strong>).</p>`,
close: () => {
resolve(0);
resolve(0);
}
}, {
classes: ["dialog", "font-of-magic"]
Expand All @@ -154,3 +82,86 @@ async function points_to_slot() {
});
}
}

const conversion_map = {
"1": 2,
"2": 3,
"3": 5,
"4": 6,
"5": 7
};

const style = `
<style>
.font-of-magic .dialog-buttons {
flex-direction: column;
gap: 5px;
}
</style>`;

const sorceryPointsItem = actor.items.find((i) => i.name === "Sorcery Points");
const { value: spvalue, max: spmax } = sorceryPointsItem.system.uses;
const spells = foundry.utils.duplicate(actor.system.spells);

// array of spell levels for converting points to slots.
const valid_levels_with_spent_spell_slots = Object.entries(spells).filter(([key, { value, max }]) => {
const cost = conversion_map[key.at(-1)];
if (!cost || cost > spvalue) return false;
return (max > 0 && value < max);
});
// array of spell levels for converting slots to points.
const spell_levels_with_available_slots = Object.entries(spells).filter(([key, { value, max }]) => {
return (value > 0 && max > 0);
});

const is_missing_points = spvalue < spmax;
const is_missing_slots = valid_levels_with_spent_spell_slots.length > 0;

// has unspent spell slots.
const has_available_spell_slots = spell_levels_with_available_slots.length > 0;
// has sp equal to or higher than the minimum required.
const has_available_sorcery_points = spvalue >= Math.min(...Object.values(conversion_map));

const can_convert_slot_to_points = has_available_spell_slots && is_missing_points;
const can_convert_points_to_slot = has_available_sorcery_points && is_missing_slots;
if (!can_convert_points_to_slot && !can_convert_slot_to_points) {
ui.notifications.warn("You have no options available.");
return;
}

// set up available buttons.
const buttons = {};
if (can_convert_slot_to_points) buttons["slot_to_point"] = {
icon: "<i class='fa-solid fa-arrow-left'></i> <br>",
label: "Convert a spell slot to sorcery points",
callback: slot_to_points
};
if (can_convert_points_to_slot) buttons["point_to_slot"] = {
icon: "<i class='fa-solid fa-arrow-right'></i> <br>",
label: "Convert sorcery points to a spell slot",
callback: points_to_slot
};


if (scope && foundry.utils.getProperty(scope, "flags.ddb-importer.ddbMacroFunction")) {
if (!actor || ! item) {
logger.error("No actor or item passed to arcane recovery");
return;
}
new Dialog({ title: "Font of Magic", buttons }).render(true);
} else if (args && args[0] === "on") {
// midi changes to skip config dialog and not consume usage
Hooks.once("dnd5e.preUseItem", (item, config, options) => {
options.configureDialog = false;
return true;
});
Hooks.once("dnd5e.preItemUsageConsumption", (item, config, options) => {
config.consumeUsage = false;
return true;
});
// End of midi changes to macro

new Dialog({ title: "Font of Magic", buttons }).render(true);
}


8 changes: 8 additions & 0 deletions src/effects/DDBSimpleMacro.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@ export default class DDBSimpleMacro {
name: "arcaneRecovery",
label: "Arcane Recovery Macro"
},
"font-of-magic": {
name: "fontOfMagic",
label: "Font of Magic Macro",
},
"convert-sorcery-points": {
name: "fontOfMagic",
label: "Font of Magic Macro",
},
},
"item": {
"spell-refuleing-ring": {
Expand Down

0 comments on commit e818e71

Please sign in to comment.