diff --git a/module.json b/module.json
index 7122e16..c852999 100644
--- a/module.json
+++ b/module.json
@@ -1,56 +1,60 @@
{
- "name": "pf2e-jb2a-macros",
- "title": "PF2e x JB2A Macros Compendium",
- "description": "
A module dedicated to making PF2e shine.
",
- "version": "1.0.0",
- "minimumCoreVersion": "9",
- "compatibleCoreVersion": "9",
- "author": "MrVauxs and Co.",
- "systems": [
- "pf2e"
- ],
- "scripts": [
- "./module/auto-import.js"
- ],
- "esmodules": [],
- "styles": [],
- "url": "https://github.com/MrVauxs/pf2e-jb2a-macros",
- "manifest": "https://github.com/MrVauxs/pf2e-jb2a-macros/releases/latest/download/manifest.json",
- "download": "https://github.com/MrVauxs/pf2e-jb2a-macros/releases/latest/download/module.zip",
- "packs": [
- {
- "label": "PF2e x JB2A Macros",
- "entity": "Macro",
- "name": "Macros",
- "path": "packs/macros.db",
- "system": "pf2e"
- },
- {
- "label": "PF2e x JB2A Actors",
- "entity": "Actor",
- "name": "Actors",
- "path": "packs/actors.db",
- "system": "pf2e"
- }
- ],
- "dependencies": [
- {
- "name": "sequencer",
- "manifest": "https://github.com/fantasycalendar/FoundryVTT-Sequencer/releases/latest/download/module.json"
- },
- {
- "name": "autoanimations",
- "manifest": "https://github.com/otigon/automated-jb2a-animations/releases/latest/download/module.json"
- },
- {
- "name": "warpgate",
- "manifest": "https://github.com/trioderegion/warpgate/releases/latest/download/module.json"
- },
- {
- "name": "soundfxlibrary",
- "manifest": "https://raw.githubusercontent.com/CDeenen/SoundFxLibrary/master/module.json"
- }
- ],
- "languages": [],
- "socket": false
+ "name": "pf2e-jb2a-macros",
+ "title": "PF2e x JB2A Macros Compendium",
+ "description": "A module dedicated to making PF2e shine.
",
+ "version": "1.1.0",
+ "minimumCoreVersion": "9",
+ "compatibleCoreVersion": "9",
+ "author": "MrVauxs and Co.",
+ "systems": [
+ "pf2e"
+ ],
+ "scripts": [
+ "./module/auto-import.js"
+ ],
+ "esmodules": [],
+ "styles": [],
+ "url": "https://github.com/MrVauxs/pf2e-jb2a-macros",
+ "manifest": "https://github.com/MrVauxs/pf2e-jb2a-macros/releases/latest/download/module.json",
+ "download": "https://github.com/MrVauxs/pf2e-jb2a-macros/releases/download/1.1.0/module.zip",
+ "packs": [
+ {
+ "label": "PF2e x JB2A Macros",
+ "entity": "Macro",
+ "name": "Macros",
+ "path": "packs/macros.db",
+ "system": "pf2e"
+ },
+ {
+ "label": "PF2e x JB2A Actors",
+ "entity": "Actor",
+ "name": "Actors",
+ "path": "packs/actors.db",
+ "system": "pf2e"
+ }
+ ],
+ "dependencies": [
+ {
+ "name": "sequencer",
+ "manifest": "https://github.com/fantasycalendar/FoundryVTT-Sequencer/releases/latest/download/module.json"
+ },
+ {
+ "name": "autoanimations",
+ "manifest": "https://github.com/otigon/automated-jb2a-animations/releases/latest/download/module.json"
+ },
+ {
+ "name": "warpgate",
+ "manifest": "https://github.com/trioderegion/warpgate/releases/latest/download/module.json"
+ },
+ {
+ "name": "soundfxlibrary",
+ "manifest": "https://raw.githubusercontent.com/CDeenen/SoundFxLibrary/master/module.json"
+ },
+ {
+ "name": "socketlib",
+ "manifest": "https://raw.githubusercontent.com/manuelVo/foundryvtt-socketlib/master/module.json"
+ }
+ ],
+ "languages": [],
+ "socket": false
}
\ No newline at end of file
diff --git a/module/auto-import.js b/module/auto-import.js
index ab31473..622a2aa 100644
--- a/module/auto-import.js
+++ b/module/auto-import.js
@@ -1,7 +1,11 @@
+let version = 111
+
Hooks.on("init", () => {
game.settings.register("pf2e-jb2a-macros", "imported", {
scope: "world",
- config: false,
+ config: true,
+ name: "Imported Contents",
+ hint: "Whether or not you have imported the contents of the module using the pop-up when you enabled the module.\nDisable to have them imported again automatically.",
type: Boolean,
default: false
});
@@ -21,11 +25,11 @@ async function importAllMacros() {
const module = game.modules.get("pf2e-jb2a-macros");
let scenes = null;
let actors = null;
- for ( let p of module.packs ) {
- const pack = game.packs.get("pf2e-jb2a-macros."+p.name);
+ for (let p of module.packs) {
+ const pack = game.packs.get("pf2e-jb2a-macros." + p.name);
await pack.importAll();
- if ( p.entity === "Macro" ) macros = game.folders.getName(p.label);
- if ( p.entity === "Actor" ) actors = game.folders.getName(p.label);
+ if (p.entity === "Macro") macros = game.folders.getName(p.label);
+ if (p.entity === "Actor") actors = game.folders.getName(p.label);
}
return game.settings.set("pf2e-jb2a-macros", "imported", true);
-}
+}
\ No newline at end of file
diff --git a/packs/macros.db b/packs/macros.db
index a857ef5..45c5602 100644
--- a/packs/macros.db
+++ b/packs/macros.db
@@ -1,5 +1,5 @@
{"name":"Heal","type":"script","author":"ynn7ZMgoRi8oPGYQ","img":"systems/pf2e/icons/spells/heal.webp","scope":"global","command":"//vardef\nlet caster = canvas.tokens.controlled[0]\nlet spellTarget = Array.from(game.user.targets)[0]\n\nlet d = new Dialog({\n title: \"Heal\",\n content: \"How many actions are you using to cast Heal?
\",\n buttons: {\n one: {\n icon: '1',\n label: \"One Action\",\n callback: () => {\n oneActionHeal()\n }\n },\n two: {\n icon: '2',\n label: \"Two Actions\",\n callback: () => {\n twoActionHeal()\n }\n },\n three:{\n icon: `3`,\n label: \"Three Actions\",\n callback: () => {\n threeActionHeal()\n }\n }\n },\n default: \"Two Actions\"\n})\n\nmain()\n\nfunction main(){\n if(caster != undefined){\n d.render(true);\n }else{\n ui.notifications.error(`You must select the token of the caster!`)\n return;\n } \n}\n\n//Defining unique behavior for each kind of effect\n\n\nfunction oneActionHeal(){\n //error checking for multiple targets\n if (game.user.targets.size!= 1){\n ui.notifications.error(\"Select only one target!\")\n return;\n }\n if(canvas.grid.measureDistance(caster,spellTarget) < 7.5){\n console.log(\"Close enough to touch!\")\n // checks whether the target is undead or not\n oneActionHealAnimation()\n }else{\n console.log(`Too far to touch!`); \n ui.notifications.error(`Your target is further than touch distance away! Try again.`);\n return;\n } \n}\n\nfunction twoActionHeal(){\n //error checking for multiple targets\n if (game.user.targets.size!= 1){\n ui.notifications.error(\"Select only one target!\")\n return;\n }\n if(canvas.grid.measureDistance(caster,spellTarget) <= 30){\n console.log(\"Within 30 feet!\")\n // checks whether the target is undead or not\n twoActionHealAnimation()\n }else{\n console.log(`Farther than 30 feet!`); \n ui.notifications.error(`Your target is further than 30 feet away! Try again.`);\n return;\n }\n}\n\nfunction threeActionHeal(){\n //divide up all tokens within range into an array for living and an array for undead\n let tokensOnMap = Array.from(canvas.tokens.placeables)\n let tokensInRange = tokensOnMap.filter(distance => canvas.grid.measureDistance(distance,caster)<=30)\n let tokenTraitsInRange = tokensInRange.map(x => x.actor.traits)\n let undeadTokensInRange = []\n let livingTokensInRange = []\n for(let i=0;i {\n if (!!placementOffsets.length) {\n const offset = placementOffsets.pop();\n console.log('location, offset',location, offset)\n location.x += offset.x ?? 0;\n location.y += offset.y ?? 0;\n }\n}\n\n/* we need to offset the initial placement with pre\n * and subsequent duplicates with post\n */\nconst callbacks = {\n pre: offset,\n post: offset\n}\n\nconst config = {\n interval: -1\n}\n\n/* Helper function to find and remove an array element by value */\nfunction findDelete(source, searchElement){\n const index = source.indexOf(searchElement);\n if (index > -1) {\n source.splice(index, 1);\n return true;\n }\n return false;\n}\n\n/* Spawn in our lights with dimLight already set*/\nconst lightsIds = await warpgate.spawn(\"Dancing Light\", {}, callbacks, {duplicates:4, crosshairs:config});\n\n/* We are already dismissing the current actor, hence\n * this trigger firing. We need to now dismiss\n * the remianing 3 lights.\n */\nconst deleteOthers = async (eventData) => {\n \n /* whoever we are, we are already being deleted */\n findDelete(lightsIds, eventData.actorData.token._id);\n \n for( const id of lightsIds ){\n // @todo dismiss really should take an array of ids\n new Sequence()\n .effect()\n .atLocation(id)\n .file(\"jb2a.template_circle.out_pulse.02.burst\")\n .scaleToObject(2)\n .fadeOut(500)\n .play()\n await warpgate.dismiss(id, eventData.sceneId);\n }\n}\n\n/* we only want our event to trigger when it involves\n * one of our just-spawned lightsIds\n */\nconst condition = (eventData) => {\n return lightsIds.includes(eventData.actorData.token._id)\n}\n\n/* Set up a trigger for when one of our\n * lights is dismissed, all of them\n * get dismissed\n */\nwarpgate.event.trigger(warpgate.EVENT.DISMISS, deleteOthers, condition)","folder":null,"sort":0,"permission":{"default":0,"ynn7ZMgoRi8oPGYQ":3},"flags":{"advanced-macros":{"runAsGM":false},"exportSource":{"world":"hogwarts","system":"pf2e","coreVersion":"9.269","systemVersion":"3.10.2.11602"},"core":{"sourceId":"Macro.1wpIPbvbCvQaueft"}},"_id":"KHYzBiPwMIY6WfYa"}
+{"_id":"KHYzBiPwMIY6WfYa","name":"Dancing Lights","type":"script","author":"ynn7ZMgoRi8oPGYQ","img":"icons/magic/light/explosion-star-glow-yellow.webp","scope":"global","command":"/* get the pixels equivalent to 5 feet */\nconst gridSquare = canvas.scene.data.size/(canvas.scene.data.gridDistance/5);\n\n/* we will be doing this a lot */\nconst twentyFeet = gridSquare * 4;\n\n/* assume click on our desired \"center\" */\nconst startingoffset = {x: -2*gridSquare, y: -2*gridSquare};\n\n/* backwards since thats how pop do */\nconst placementOffsets = [{x:-twentyFeet}, {y:twentyFeet}, {x:twentyFeet},{x: -2*gridSquare, y: -2*gridSquare}];\n\n/* pop the offsets off one at a time and mody 'location' by that much.\n * Remember, this is a culmulative iteration\n */\nconst offset = (location) => {\n new Sequence()\n .effect()\n .atLocation(location)\n .file(\"jb2a.template_circle.out_pulse.02.burst\")\n .scaleToObject(2)\n .fadeOut(500)\n .play()\n if (!!placementOffsets.length) {\n const offset = placementOffsets.pop();\n console.log('location, offset',location, offset)\n location.x += offset.x ?? 0;\n location.y += offset.y ?? 0;\n }\n}\n\n/* we need to offset the initial placement with pre\n * and subsequent duplicates with post\n */\nconst callbacks = {\n pre: offset,\n post: offset\n}\n\nconst config = {\n interval: -1\n}\n\n/* Helper function to find and remove an array element by value */\nfunction findDelete(source, searchElement){\n const index = source.indexOf(searchElement);\n if (index > -1) {\n source.splice(index, 1);\n return true;\n }\n return false;\n}\n\n/* Spawn in our lights with dimLight already set*/\nconst lightsIds = await warpgate.spawn(\"Dancing Light\", {}, callbacks, {duplicates:4, crosshairs:config});\n\n/* We are already dismissing the current actor, hence\n * this trigger firing. We need to now dismiss\n * the remianing 3 lights.\n */\nconst deleteOthers = async (eventData) => {\n \n /* whoever we are, we are already being deleted */\n findDelete(lightsIds, eventData.actorData.token._id);\n \n for( const id of lightsIds ){\n // @todo dismiss really should take an array of ids\n new Sequence()\n .effect()\n .atLocation(id)\n .file(\"jb2a.template_circle.out_pulse.02.burst\")\n .scaleToObject(2)\n .fadeOut(500)\n .play()\n await warpgate.dismiss(id, eventData.sceneId);\n }\n}\n\n/* we only want our event to trigger when it involves\n * one of our just-spawned lightsIds\n */\nconst condition = (eventData) => {\n return lightsIds.includes(eventData.actorData.token._id)\n}\n\n/* Set up a trigger for when one of our\n * lights is dismissed, all of them\n * get dismissed\n */\nwarpgate.event.trigger(warpgate.EVENT.DISMISS, deleteOthers, condition)","folder":null,"sort":0,"permission":{"default":0,"ynn7ZMgoRi8oPGYQ":3},"flags":{"advanced-macros":{"runAsGM":false},"exportSource":{"world":"hogwarts","system":"pf2e","coreVersion":"9.269","systemVersion":"3.10.2.11602"},"core":{"sourceId":"Macro.1wpIPbvbCvQaueft"}}}
{"name":"Harm","type":"script","author":"ynn7ZMgoRi8oPGYQ","img":"systems/pf2e/icons/spells/harm.webp","scope":"global","command":"//vardef\nlet caster = canvas.tokens.controlled[0]\nlet spellTarget = Array.from(game.user.targets)[0]\nlet doNotTargetCaster = false\n\nlet d = new Dialog({\n title: \"Harm\",\n content: \"How many actions are you using to cast Harm?
\",\n buttons: {\n one: {\n icon: '1',\n label: \"One Action\",\n callback: () => {\n oneActionHarm()\n }\n },\n two: {\n icon: '2',\n label: \"Two Actions\",\n callback: () => {\n twoActionHarm()\n }\n },\n three:{\n icon: `3`,\n label: \"Three Actions\",\n callback: () => {\n d2.render(true);\n }\n }\n },\n default: \"Two Actions\"\n})\n\nconsole.log(caster)\nlet d2 = new Dialog({\n title: \"Harm\",\n content: `Do you want to target yourself? ${true ? \"This action will heal you.\" : \"This action will hurt you.\"}
`,\n buttons: {\n one: {\n label: \"Yes\",\n callback: () => {\n doNotTargetCaster = false\n threeActionHarm()\n }\n },\n two: {\n label: \"No\",\n callback: () => {\n doNotTargetCaster = true\n threeActionHarm()\n }\n }\n },\n default: \"No\"\n})\n\nmain()\n\nfunction main(){\n if(caster != undefined){\n d.render(true);\n }else{\n ui.notifications.error(`You must select the token of the caster!`)\n return;\n } \n}\n\n//Defining unique behavior for each kind of effect\n\n\nfunction oneActionHarm(){\n //error checking for multiple targets\n if (game.user.targets.size!= 1){\n ui.notifications.error(\"Select only one target!\")\n return;\n }\n if(canvas.grid.measureDistance(caster,spellTarget) < 7.5){\n console.log(\"Close enough to touch!\")\n // checks whether the target is undead or not\n oneActionHarmAnimation()\n }else{\n console.log(`Too far to touch!`); \n ui.notifications.error(`Your target is further than touch distance away! Try again.`);\n return;\n } \n}\n\nfunction twoActionHarm(){\n //error checking for multiple targets\n if (game.user.targets.size!= 1){\n ui.notifications.error(\"Select only one target!\")\n return;\n }\n if(canvas.grid.measureDistance(caster,spellTarget) <= 30){\n console.log(\"Within 30 feet!\")\n // checks whether the target is undead or not\n twoActionHarmAnimation()\n }else{\n console.log(`Farther than 30 feet!`); \n ui.notifications.error(`Your target is further than 30 feet away! Try again.`);\n return;\n }\n}\n\nfunction threeActionHarm(){\n //divide up all tokens within range into an array for living and an array for undead\n let tokensOnMap = Array.from(canvas.tokens.placeables)\n let tokensInRange = tokensOnMap.filter(distance => canvas.grid.measureDistance(distance,caster)<=30)\n let tokenTraitsInRange = tokensInRange.map(x => x.actor.traits)\n let undeadTokensInRange = []\n let livingTokensInRange = []\n for(let i=0;i s.slug === 'magic-missile') && !token.actor.itemTypes.consumable.some(s => s.data.data.spell?.data?.data?.slug === 'magic-missile') && !token.actor.itemTypes.equipment.some(s => mani.includes(s.slug))) { return ui.notifications.error('You do not have Magic Missile') }if (game.user.targets.ids === undefined || game.user.targets.ids.length === 0) { return ui.notifications.error('At least 1 target is required'); }\n\nconst mmE = token.actor.itemTypes.spellcastingEntry.filter(m => m.spells.some(x => x.slug === 'magic-missile') === true);\n\nconst mmIds = [];\ntoken.actor.itemTypes.spell.forEach(id => {\n\tif(id.slug === 'magic-missile') { mmIds.push(id.id); }\n});\n\nconst mm = [];\nconst formula = `{1d4 + 1}[force]`;\n\nmmE.forEach(e => {\n const spellData = e.getSpellData();\n\t spellData.levels.forEach(sp => {\n if(sp.uses !== undefined && !sp.isCantrip && sp.uses.value < 1) { return; }\n\t sp.active.forEach((spa,index) => {\n\t if(spa === null) { return; }\n if(spa.spell.slug !== \"magic-missile\") { return; }\n if(spa.expended) { return; }\n if(spellData.isFocusPool && !spa.spell.isCantrip && token.actor.data.data.resources.focus.value === 0){ return; }\n let level = `lv${sp.level}`\n const name = spa.spell.name;\n\t const sname = `${name} ${level} (${e.name})`;\n mm.push({name: sname, entryId: spellData.id, level: sp.level, spId: spa.spell.id, slug: spa.spell.slug, DC: e.data.data.statisticData.dc.value, spell: spa.spell, index: index});\n\t });\n\t });\n});\t\n\ntoken.actor.itemTypes.consumable.forEach(s => {\n\tif (!s.data.data.traits.value.includes(\"wand\") && !s.data.data.traits.value.includes(\"scroll\")) { return; }\n\tif (s.data.data.spell.data.data.slug === 'magic-missile') { \n\t\tif (s.data.data.traits.value.includes(\"wand\") && s.data.data.charges.value > 0) {\n\t\t\tmm.push({name: `${s.name}`, level: parseInt(s.slug.substr(11,1)), prepared: false, entryId: s.id , wand: true, scroll: false, spont: false, }) \n\t\t}\n\t\tif (s.data.data.traits.value.includes(\"scroll\")) {\n\t\t\tmm.push({name: `${s.name}`, level: s.data.data.spell.heightenedLevel, prepared: false, entryId: s.id, wand: false, scroll: true, spont: false })\n\t\t}\n\t}\n});\ntoken.actor.itemTypes.equipment.forEach(s => {\n\tif (mani.includes(s.slug)) { \n\t\tmm.push({name: `${s.name}`, level: parseInt(s.slug.substr(26,1)), prepared: false, entryId: s.slug, wand: true, scroll: false, spont: false}); \n\t}\n});\n\nif (token.actor.itemTypes.effect.some(e => e.slug === \"maniEF\")) {\n\tconst effect = token.actor.itemTypes.effect.find(e => e.slug === \"maniEF\");\n\tmm.push({name: `${effect.name}`, level: effect.data.data.level.value, prepared: false, entryId: null, wand: false, scroll: false, spont: false });\n}\n\nif (mm.length === 0) { return ui.notifications.warn(\"You currently have no available means of casting Magic Missile\");}\n\nconst mmdd = [{label: 'Which spell?', type: 'select', options: mm.map(n => n.name)},\n\t {label: 'Number of Actions?', type: 'select', options: [3,2,1]}\n\t ];\n\nif (token.actor.itemTypes.effect.some(e => e.slug === \"maniEF\")) { mmdd.push({label: `Remove Effect Instead?`, type: \"checkbox\"})}\n\nconst mmdiag = await quickDialog({data : mmdd, title : `Magic Missile`});\n\nif (mmdiag[2] === true) { \n\tconst effect = token.actor.itemTypes.effect.find(e => e.slug === \"maniEF\")\n\tawait effect.delete();\n\treturn;\n}\n\nconst mmch = mm.find(n => n.name === mmdiag[0]);\n\nif(mmch.entryId === null) { mmdiag[1] = 1 }\n\nconst multi = parseInt(mmdiag[1]) * Math.floor((1 + mmch.level)/2);\n\nconst targetIds = game.user.targets.ids;\nconst targets = canvas.tokens.placeables.filter(t => targetIds.includes(t.id));\n\nconst tdata = [];\ntargets.forEach(t => {\n\tif(t.actor.hasPlayerOwner) { ui.notifications.info(`${t.name} is most likely an ally`);}\n\ttdata.push({label: t.name, type: 'number', options: [1]});\n});\n\nif (targetIds.length === 1) { tdata[0].options = [multi]; }\n\nconst tdiag = await quickDialog({data : tdata, title : `Distribute ${multi} Missiles`});\n\nlet tot = 0;\nlet i;\nconst fmm = [];\ntdiag.forEach(m => {\n\ttot = tot + m\n\tif( i !== undefined) { i++ }\n\tif( i === undefined) { i = 0}\n\tfmm.push({name: targets[i].name, num: m})\n});\n\nif (tot > multi) { return ui.notifications.warn(`You have entered ${tot - multi} too many missiles. Please try again`)}\nif (tot < multi) { return ui.notifications.warn(`You have entered ${ multi - tot} too few missiles. Please try again`)}\n\nconsole.log(targets)\n\nlet targetNum = 0\nfmm.forEach(a => {\n if(a.num === 0 || a.num === undefined) { return; }\n\tlet dam = token.actor.itemTypes.feat.some(ds => ds.slug === 'dangerous-sorcery') ? formula.repeat(a.num).replace(/]{/g,'] + {') + ` + {${mmch.level}}[status,force]` : formula.repeat(a.num).replace(/]{/g,'] + {');\n\tvar droll = new Roll(dam);\n droll.toMessage({ flavor: `${a.num} Magic Missile(s) targeting ${a.name}
Magic Missile`, speaker: ChatMessage.getSpeaker() });\n \tnew Sequence()\n .effect()\n .file(`jb2a.magic_missile.green`)\n .atLocation(canvas.tokens.controlled[0])\n .stretchTo(targets[targetNum])\n .repeats(a.num,100,300)\n .delay(300,600)\n .play()\n targetNum++\n });\n\nconst s_entry = mmE.find(e => e.id === mmch.entryId);\n\n/* Expend slots */\nif (!mmch.wand && !mmch.scroll) { \n await s_entry.cast(mmch.spell,{slot: mmch.index,level: mmch.level,message: false});\n}\n\n\n/* Wand */\nif (mmch.wand) {\n\tif (mani.includes(mmch.entryId)) {\n\t\tif (token.actor.itemTypes.effect.some(e => e.slug === \"maniEF\")) {\n\t\t\tconst effect = token.actor.itemTypes.effect.find(e => e.slug === \"maniEF\")\n\t\t\tawait effect.delete();\n\t\t}\n\t\tif (!token.actor.itemTypes.effect.some(e => e.slug === \"maniEF\")){\n\t\t\tconst maniEF = {\n \t\t\t\t\"name\": `${mmch.name} Effect`,\n \t\t\t\t\"type\": \"effect\",\n \t\t\t\t\"img\": \"systems/pf2e/icons/equipment/wands/specialty-wands/wand-of-manifold-missiles.webp\",\n \t\t\t\t\"data\": {\n \t\t\t\t\t\"description\": {\n \t\t\t\t\t\t\"value\": `Requirements You used Wand of Manifold Missiles to cast Magic Missile.
\\n
\\nAfter you cast the spell, an additional missile or missiles are released from the wand at the start of each of your turns, as though you cast the 1-action version of magic missile. Choose targets each time. This lasts for 1 minute, until you are no longer wielding the wand, or until you try to activate the wand again.
`\n \t\t\t\t\t},\n \t\t\t\t\t\"source\": {\n\t\t\t\t\t\t\"value\": \"\"\n \t\t\t\t\t},\n \t\t\t\t\t\"traits\": {\n\t\t\t\t\t\t\"value\": [],\n \t\t\t\t\t\t\"rarity\": {\n \t\t\t\t\t\"value\": \"common\"\n\t\t\t\t\t\t},\n \t\t\t\t\t\t\"custom\": \"\"\n \t\t\t\t\t},\n\t\t\t\t\t\"rules\": [],\n \t\t\t\t\t\"slug\": \"maniEF\",\n \t\t\t\t\t\"schema\": {\n \t\t\t\t\t\t\"version\": 0.697,\n\t\t\t\t\t\t\"lastMigration\": null\n \t\t\t\t\t},\n \t\t\t\t\t\"level\": {\n\t\t\t\t\t\t\"value\": mmch.level\n \t\t\t\t\t},\n \t\t\t\t\t\"duration\": {\n\t\t\t\t\t\t\"value\": -1,\n \t\t\t\t\t\t\"unit\": \"unlimited\",\n \t\t\t\t\t\t\"sustained\": false,\n \t\t\t\t\t\t\"expiry\": \"turn-start\"\n \t\t\t\t\t},\n \t\t\t\t\t\"start\": {\n\t\t\t\t\t\t\"value\": 0,\n \t\t\t\t\t\t\"initiative\": null\n \t\t\t\t\t},\n \t\t\t\t\t\"target\": null,\n \t\t\t\t\t\"tokenIcon\": {\n\t\t\t\t\t\t\"show\": true\n\t\t\t\t\t}\n \t\t\t\t},\n \t\t\t\t\"effects\": [],\n\t\t\t\t\"sort\": 0\n\t\t\t};\n\t\t\tawait actor.createEmbeddedDocuments('Item', [maniEF]);\n\t\t}\n\t}\n\telse {\n\t\tconst w = token.actor.itemTypes.consumable.find(id => id.id === mmch.entryId);\n\t\tconst wData = duplicate(w.data);\n\t\twData.data.charges.value --;\n\t\tw.update(wData);\n\t}\n}\n\n/* Scroll */\nif(mmch.scroll){\n\tconst s = token.actor.itemTypes.consumable.find(id => id.id === mmch.entryId);\n\tif (s.data.data.quantity > 1) {\n\t\tconst sData = duplicate(s.data);\n\t\tsData.data.quantity --;\n\t\ts.update(sData);\n\t}\n\telse { await s.delete(); }\n}\n\n\n/* Dialog box */\nasync function quickDialog({data, title = `Quick Dialog`} = {}) {\n\tdata = data instanceof Array ? data : [data];\n\n\treturn await new Promise(async (resolve) => {\n\t let content = `\n \t`;\n\n await new Dialog({\n title, content,\n buttons : {\n Ok : { label : `Ok`, callback : (html) => {\n resolve(Array(data.length).fill().map((e,i)=>{\n let {type} = data[i];\n if(type.toLowerCase() === `select`)\n {\n return html.find(`select#${i}qd`).val();\n }else{\n switch(type.toLowerCase())\n {\n case `text` :\n case `password` :\n case `radio` :\n return html.find(`input#${i}qd`)[0].value;\n case `checkbox` :\n return html.find(`input#${i}qd`)[0].checked;\n case `number` :\n return html.find(`input#${i}qd`)[0].valueAsNumber;\n }\n }\n }));\n }}\n },\n default : 'Ok'\n })._render(true);\n document.getElementById(\"0qd\").focus();\n });\n }","folder":null,"sort":0,"permission":{"default":0,"ynn7ZMgoRi8oPGYQ":3},"flags":{"advanced-macros":{"runAsGM":false},"exportSource":{"world":"hogwarts","system":"pf2e","coreVersion":"9.269","systemVersion":"3.10.2.11602"},"core":{"sourceId":"Macro.OHxEXANJvgNk9uTU"}},"_id":"pXVewDMcOapNzVcO"}