diff --git a/packs/macros-misc.db b/packs/macros-misc.db index 2abbf24..d2a1ed5 100644 --- a/packs/macros-misc.db +++ b/packs/macros-misc.db @@ -5,7 +5,7 @@ {"name":"Find Selected Wall IDs","type":"script","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","scope":"global","command":"for (const [i, wall] of canvas.walls.controlled.entries()) {\n\tconst text = new PIXI.Text(`${i} - ${wall.id}`);\n\ttext.anchor.set(0.5);\n\ttext.x = (wall.data.c[0] + wall.data.c[2]) / 2;\n\ttext.y = (wall.data.c[1] + wall.data.c[3]) / 2;\n\ttext.name = wall.id;\n\ttext.style = new PIXI.TextStyle({ fill: 0xffffff, dropShadow: true, fontSize: 20 });\n\twall.addChild(text);\n}\n\nconst wallIds = canvas.walls.controlled.map((x) => x.id);\nconst wallsToExport = new Set(wallIds);\n\nnew Dialog({\n\ttitle: \"Selected Wall IDs\",\n\tcontent: `\n${[...wallIds.entries()].map(([i, x]) => `
${i}: ${x}
`).join(\"\")}\n

\n(copied) \n\n

`,\n\tbuttons: { close: { label: \"Close\" } },\n\tclose() {\n\t\tfor (const wall of canvas.walls.placeables) {\n\t\t\tconst child = wall.children.find((w) => w.name === wall.id);\n\t\t\tif (child) wall.removeChild(child);\n\t\t}\n\t},\n\trender(html) {\n\t\thtml.find(`input[type=checkbox]`).on(\"change\", function () {\n\t\t\tconst $this = $(this);\n\t\t\tconst index = +$this.attr(\"data-i\");\n\t\t\tif ($this.prop(\"checked\")) wallsToExport.add(wallIds[index]);\n\t\t\telse wallsToExport.delete(wallIds[index]);\n\t\t});\n\n\t\thtml.find(\".button-copy-text\").on(\"click\", async () => {\n\t\t\tconst toCopy = JSON.stringify(wallIds.filter((w) => wallsToExport.has(w)));\n\t\t\tawait navigator.clipboard.writeText(toCopy);\n\t\t\thtml.find(\".span-copy-text\").css(\"display\", \"unset\");\n\t\t});\n\t},\n}).render(true);","folder":null,"sort":0,"permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"flags":{"core":{"sourceId":"Macro.JpHGLlRLS6ntLfp4"}},"_id":"8Mu55GZEVVid1UOh"} {"_id":"8iEx21HSVOmkbBFE","name":"Combat Tracker AC HP","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{},"scope":"global","command":"// Adds the actor's AC to the combat tracker. Then toggles between HP and AC\nconst a = \"attributes.ac.value\";\nconst b = \"attributes.hp.value\";\n\nif (game.combat.settings.resource == a) {\n game.settings.set('core', 'combatTrackerConfig', {resource: b, skipDefeated: true});\n} else {\n game.settings.set('core', 'combatTrackerConfig', {resource: a, skipDefeated: true});\n}\nui.combat.updateTrackedResources();\n","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} {"name":"Auto Script","type":"script","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","scope":"global","command":"/*\n * Author: tylers1st \n * Version: 0.1\n *\n * Desc: This is a macro designed to automate a set of lines from a journal into a token as chat bubbles\n * Setup: Make a journal with a unique name. Then make the script (be sure to create a new line for each line. For those\n * who would like to know, it's split by \"

\"). The formatting is \n * :\n * : \n * \n * Once you've created the journal and the script using the above formatting, you can either select tokens on the currently viewed scene\n * and actvate the macro, then enter the journal's name, or if you don't select any tokens, you can manually add their names separated by a comma and no spaces\n * \n * After that the only thing left to do is confirm that everything is correct and press \"Confirm\"\n * A small window will show up for each token that was selected and every time you press the button, the next line will be said by the token. \n *\n *\n * If you can't get this to work, first be sure that it is formatted correctly. \n * 1. The token's name in the journal and the token itself must be exactly the same. It is case sensitive\n * 2. In the journal, immediately following the token's name on each line must be a colon\n * 3. If this still doesn't work, I have a discord server https://discord.gg/FXYbq7kjcH for if you have any questions\n */\n\n\n// checking if there are any controlled tokens in the currently viewed scene\nif (canvas.tokens.controlled.length < 1){\n let d = new Dialog({\n title: 'Auto Script Reader',\n content: `\n
\n
\n \n \n
\n
\n \n \n
\n
\n `,\n buttons: {\n cancel: {\n icon: '',\n label: 'Cancel'\n },\n confirm: {\n icon: '',\n label: 'Confirm',\n callback: (html) => {\n let tokenInput = html.find(`[name=\"tokenNameInput\"]`).val();\n let journalName = html.find('[name=\"journalInput\"]').val();\n console.log(tokenInput, journalName);\n tokenInput = tokenInput.split(\",\");\n var spkr; //The object\n for (const tokenNameAtIndex of tokenInput){\n console.log(tokenNameAtIndex.name + \" is current speaker in for loop\");\n spkr = canvas.tokens.objects.children.find(e => e.name === tokenNameAtIndex); // Finds a token on the current screen with the same name as the dialog input\n\n let messageIndex = 0;\n let messageList = game.journal.getName(journalName).data.content.split(`

${spkr.name}:`);\n console.log(`attempted to load \\n\\`game.journal.getName(${journalName}).data.content.split(\\`

${spkr.name}:\\`)`);\n let messageArr = []; //This will be the actual list of messages to send\n let arrTemp; //temporary array delcaration\n\n for(let i = 0; i < messageList.length; i++){ // for each message in the original list, phase out text past the next

\n arrTemp = messageList[i].split(`

`);\n console.log(`Attempted to tokenize phrase \\\"${messageList[i]}\\\" \\n|| -----AND GOT----- ||\\n \\\"${arrTemp}\\\"`);\n messageArr[i] = i == 0 ? arrTemp[1] : arrTemp[0];\n if (i == 0) console.log(`added \\\"${arrTemp[1]}\\\" to message array`);\n else console.log(`added \\\"${arrTemp[0]}\\\" to message array`);\n }\n console.log(`||messageIndex|| = ${messageIndex}\\n||messageArr|| = ${messageArr}`);\n\n tokenUpdaterFunction(spkr,messageArr,messageIndex+1);\n }\n }\n }\n },\n default: 'yes',\n close: () => {\n console.log('Example Dialog Closed');\n },\n }).render(true);\n}\nelse if (canvas.tokens.controlled.length >= 1){\n let d = new Dialog({\n title: 'Auto Script Reader',\n content: `\n
\n
\n \n \n
\n
\n `,\n buttons: {\n cancel: {\n icon: '',\n label: 'Cancel'\n },\n confirm: {\n icon: '',\n label: 'Confirm',\n callback: (html) => {\n let controlledTokens = canvas.tokens.controlled;\n let journalName = html.find('[name=\"journalInput\"]').val();\n console.log(controlledTokens, journalName);\n var spkr;\n\n for (const tokenNameAtIndex of controlledTokens){\n console.log(tokenNameAtIndex.name + \" is current speaker in for loop\");\n spkr = tokenNameAtIndex; // Finds a token on the current screen with dialog input\n\n let messageIndex = 0;\n let messageList = game.journal.getName(journalName).data.content.split(`

${spkr.name}:`);\n console.log(`attempted to load \\n\\`game.journal.getName(${journalName}).data.content.split(\\`

${spkr.name}:\\`)`);\n let messageArr = []; //This will be the actual list of messages to send\n let arrTemp;\n\n for(let i = 0; i < messageList.length; i++){ //for each message in the original list, phase out text past the next

\n arrTemp = messageList[i].split(`

`);\n console.log(`Attempted to tokenize phrase \\\"${messageList[i]}\\\" \\n|| and got ||\\n \\\"${arrTemp}\\\"`);\n messageArr[i] = i == 0 ? arrTemp[1] : arrTemp[0];\n if (i == 0) console.log(`added \\\"${arrTemp[1]}\\\" to message array`);\n else console.log(`added \\\"${arrTemp[0]}\\\" to message array`);\n }\n console.log(`||messageIndex|| = ${messageIndex}\\n ||messageArr|| = ${messageArr}`);\n\n tokenUpdaterFunction(spkr,messageArr,messageIndex+1);\n }\n }\n }\n },\n default: 'yes',\n close: () => {\n console.log('Example Dialog Closed');\n },\n }).render(true);\n}\n/**\n * @param {Object} tokenObject The token you wish to use\n * @param {(string|string[])} messageArray The array of messages tokenObject is mean to speak\n * @param {number} messagIndexParam The messageArray index to start on\n *\n */\nasync function tokenUpdaterFunction(speaker, messageArray,messageIndexParam){\n let tokenUpdater = new Dialog({\n title: `${speaker.name}`,\n content: 'Do you want to continue to the next line?',\n buttons: {\n Next:{\n label: \"

Next

\",\n callback: () => {\n canvas.hud.bubbles.say(speaker, messageArray[messageIndexParam]);\n console.log(`speaker = ${speaker.name} || messageArray[messageIndexParam] = ${messageArray[messageIndexParam+1]}`);\n messageIndexParam++;\n if (messageIndexParam < messageArray.length){\n tokenUpdater.render(true);\n }\n }\n }\n },\n },\n {\n id: 'updater'\n }\n ).render(true);\n}","folder":null,"sort":0,"permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"flags":{"core":{"sourceId":"Macro.43dreAJcceAuyLIg"}},"_id":"BT9KV43ln8Ey0xQE"} -{"name":"Equip Unequip Shield","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{},"scope":"global","command":"/**\n * Equips/unequips an item. Make sure you change the variables at the top (as required).\n * This script will also error check to make sure items exist and tokens are select. \n * Chat and token icon display options can be set as desired.\n * Author: Zapgun\n */\n\nlet itemName = 'Shield'; // <--- Change this to the *exact* item name (capitals count!)\nlet sendToChat = true; // <--- Change to 'true' or 'false' to display a chat message about equipping\nlet displayIcon = true; // <--- Change to 'true' or 'false' to display an effect icon when equipped\nconst effectIconPath = 'icons/svg/shield.svg'; // <--- Add the effect icon you want to appear when equipped\n\nlet toggleResult = false;\n\nif (!actor) {\n ui.notifications.warn('You need to select a token before using this macro!');\n} else {\n\n\tlet myItem = actor.items.find(i => i.name == itemName);\n\tif (myItem != null)\n\t{\n\t\tlet item = actor.getOwnedItem(myItem._id);\n\t\tlet attr = \"data.equipped\";\n\t\tlet equipped = getProperty(item.data, attr);\n\t\tif (sendToChat) {\t\t\t\n\t\t\tif (!equipped) {\n\t\t\t\tchatMessage(actor.name + ' equips their ' + ' ' + itemName+ '');\n\t\t\t} else {\n\t\t\t\tchatMessage(actor.name + ' un-equips their ' + ' ' + itemName + '');\t\t\t\n\t\t\t}\n\t\t}\n\t\titem.update({[attr]: !getProperty(item.data, attr)});\n\t\t\n\t\t// mark/unmark character's token with an effect icon when displayToken is true\n\t\t(async () => { \n\t\t\tif (displayIcon) {\n\t\t\t\ttoggleResult = await token.toggleEffect(effectIconPath);\n\t\t\t\tif (toggleResult == equipped) token.toggleEffect(effectIconPath); \n\t\t\t}\n\t\t})();\n\t\t\n\t} else {\n\t\tui.notifications.warn(\"No item named '\" + itemName + \"' found on character!\");\n\t}\n}\n\nfunction chatMessage(messageContent) {\n\t// create the message\n\tif (messageContent !== '') {\n\t\tlet chatData = {\n\t\t\tuser: game.user._id,\n\t\t\tspeaker: ChatMessage.getSpeaker(),\n\t\t\tcontent: messageContent,\n\t\t};\n\t\tChatMessage.create(chatData, {});\n\t}\n}","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"BqcD9aq88kWU67Uf"} +{"_id":"BqcD9aq88kWU67Uf","name":"Equip Unequip Shield","type":"script","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","scope":"global","command":"/**\n * D&D5e biased, paths may be wrong in other systems!\n * Equips/unequips an item. Make sure you change the variables at the top (as required).\n * This script will also error check to make sure items exist and tokens are select. \n * Chat and token icon display options can be set as desired.\n * Author: Zapgun, Freeze#2689 (fix for v9)\n */\n\nconst itemName = 'Shield'; // <--- Change this to the *exact* item name (capitals count!)\nconst sendToChat = true; // <--- Change to 'true' or 'false' to display a chat message about equipping\nconst displayIcon = true; // <--- Change to 'true' or 'false' to display an effect icon when equipped\nconst effectIconPath = 'icons/svg/shield.svg'; // <--- Add the effect icon you want to appear when equipped\n\nlet toggleResult = false;\n\nif (!actor) {\n\tui.notifications.warn('You need to select a token before using this macro!');\n} else {\n\n\tconst myItem = actor.items.getName(itemName);\n\tif (myItem) {\n\t\tlet item = actor.items.get(myItem.id);\n\t\tlet attr = \"data.equipped\";\n\t\tlet equipped = getProperty(item.data, attr);\n\t\tif (sendToChat) {\n\t\t\tif (!equipped) {\n\t\t\t\tchatMessage(actor.name + ' equips their ' + ' ' + itemName + '');\n\t\t\t} else {\n\t\t\t\tchatMessage(actor.name + ' un-equips their ' + ' ' + itemName + '');\n\t\t\t}\n\t\t}\n\t\titem.update({ [attr]: !getProperty(item.data, attr) });\n\n\t\t// mark/unmark character's token with an effect icon when displayToken is true\n\t\t(async () => {\n\t\t\tif (displayIcon) {\n\t\t\t\ttoggleResult = await token.toggleEffect(effectIconPath, { active: !equipped });\n\t\t\t\t//\tif (toggleResult == equipped) token.toggleEffect(effectIconPath, {active: equipped}); \n\t\t\t}\n\t\t})();\n\n\t} else {\n\t\tui.notifications.warn(\"No item named '\" + itemName + \"' found on character!\");\n\t}\n}\n\nfunction chatMessage(messageContent) {\n\t// create the message\n\tif (messageContent !== '') {\n\t\tlet chatData = {\n\t\t\tuser: game.user.id,\n\t\t\tspeaker: ChatMessage.getSpeaker(),\n\t\t\tcontent: messageContent,\n\t\t};\n\t\tChatMessage.create(chatData, {});\n\t}\n}","folder":null,"sort":0,"permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"flags":{}} {"name":"Announce Round Number","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.V1p6LWoKnv7croAd"}},"scope":"global","command":"let messageContent = `
ROUND ${game.combat.round}

`\nChatMessage.create({content: messageContent});","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"CVbowNiiiwqmADGY"} {"_id":"Cp6zQmvC9KSCEo1s","name":"Toggle Playlist","type":"script","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","scope":"global","command":"// Get all playlists from contents and prepare choices\n\nlet optionsText = game.playlists.reduce((acc, e) => acc += ``, ``);\nlet _applyChanges = false;\n\nlet d = new Dialog({\n title: \"Playlist Toggle\",\n content: `\n
\n
\n \n \n
\n
\n `,\n buttons: {\n one: {\n icon: '',\n label: \"Playlist Toggle\",\n callback: () => _applyChanges = true\n },\n two: {\n icon: '',\n label: \"Cancel\",\n callback: () => _applyChanges = false\n }\n },\n default: \"Cancel\",\n close: html => {\n if (_applyChanges) {\n let _plId = html.find('[name=playlist-selection]')[0].value;\n let _pl = game.playlists.get(_plId);\n if(_pl) {\n if (_pl.playing) {\n // turn off\n _pl.stopAll();\n } else {\n // turn on\n _pl.playAll();\n }\n }\n else {\n ui.notifications.error(`No valid playlist selected.`);\n } \n }\n }\n}).render(true);","folder":null,"sort":0,"permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"flags":{}} {"_id":"GhloAVkE0qJJGyoB","name":"Award Party XP","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{},"scope":"global","command":"(function ()\n{\n function award_xp(type, amount)\n {\n let actors = game.actors.entities.filter(e => e.data.type === 'character' && e.isPC);\n let isShared = type == \"shared\";\n console.log(type + ' ' + amount);\n if (Number.isInteger(amount) && actors.length > 0)\n {\n let totalAmount = isShared ? amount : amount * actors.length;\n let individualAmount = isShared ? Math.floor(amount / actors.length) : amount\n\n let chatContent = `\n\t\t\t${totalAmount} Experience Awarded!\n\t\t\t
${individualAmount} added to:\n\t\t\t`;\n\n actors.forEach(actor =>\n {\n chatContent += `
${actor.name}`;\n actor.update({\n \"data.details.xp.value\": actor.data.data.details.xp.value + individualAmount\n });\n });\n\n let chatData = {\n user: game.user._id,\n speaker: ChatMessage.getSpeaker(),\n content: chatContent,\n type: CONST.CHAT_MESSAGE_TYPES.OTHER\n };\n ChatMessage.create(chatData);\n }\n }\n\n new Dialog({\n title: \"Award Party XP\",\n content: `\n

Select a type and an amount. Individual xp will give or take a set amount to/from each party member, whereas shared will split an amount evenly.

\n
\n
\n \n \n
\n
\n \n \n
\n
\n `,\n buttons: {\n one: {\n icon: '',\n label: \"Confirm\",\n callback: (html) =>\n {\n let type = html.find('[id=xp-type]')[0].value;\n let amount = parseInt(html.find('[id=xp-amount]')[0].value);\n award_xp(type, amount);\n }\n },\n two: {\n icon: '',\n label: \"Cancel\",\n }\n },\n default: \"Cancel\"\n }).render(true);\n})();\n","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} @@ -36,4 +36,3 @@ {"name":"Show Modules","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{},"scope":"global","command":"/**\n * Show Modules - Shows currently installed modules in foundry. Added on behalf of @vance\n */\nlet mods = '';\ngame.modules.forEach(m => {\n let a = m.active ? 'Enabled' : 'Disabled';\n mods = mods.concat(`${m.id}: ${a}\\n`);\n});\n\nlet d = new Dialog({\n title: `Enabled Mods`,\n content: ``,\n buttons: {\n copy: {\n label: `Copy to clipboard`,\n callback: () => {\n $(\"#modslist\").select();\n document.execCommand('copy');\n }\n },\n close: {\n icon: \"\",\n label: `Close`\n },\n },\n default: \"close\",\n close: () => {}\n});\n\nd.render(true);","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"u71xIHwO8uVaLS8o"} {"_id":"wosXzUFEMQLD84so","name":"Ambient Light Quick Edit","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{},"scope":"global","command":"let macroName = \"AmbientLight QuickEditor\"\nlet macroEndLog = \"---------------------------------------------\"\n\nlet i=0;\nlet lights = canvas.lighting.objects.children;\nlet lightSelected = lights[0];\nlet selectOptions = \"\";\nlet lightSelectedAngle = 0;\nlet lightSelectedBright = 0;\nlet lightSelectedDim = 0;\nlet lightSelectedRotation = 0;\nlet lightSelectedTintAlpha = 1;\nlet lightSelectedTintColor = \"\";\n\nconsole.log(\"---------------------------------------------\");\nconsole.log(`${macroName} by PaperPunk`);\nconsole.log(\"---------------------------------------------\");\nconsole.log(`${macroName} | Start`);\n\nconst drawingDetails = {\n author: game.user._id,\n fillAlpha: 0,\n fillColor: \"#808080\",\n fillType: 1,\n fontFamily: \"FontAwesome\",\n fontSize: 24,\n height: 48,\n hidden: false,\n locked: false,\n rotation: 0,\n strokeAlpha: 1,\n strokeColor: \"#000000\",\n strokeWidth: 2,\n text: i,\n textAlpha: 1,\n textColor: \"#ffffff\",\n type: \"r\",\n width: 48,\n //x: 250,\n x: lightSelected.x-24,\n //y: 250\n y: lightSelected.y+25\n};\n\n//let d = Drawing.create(drawingDetails);\n//d.update({\"x\": lights[i].x-24, \"y\": lights[i].y+25, \"text\": i});\n\nfor (i= 0; i< lights.length; i++) {\n selectOptions += ``;\n}\n\nconst htmlLightSelection = `\n
\n

Select your light.

\n
\n \n \n
\n
\n `;\n\nlet dialogSelector = new Dialog({\n title: `${macroName}`,\n content: htmlLightSelection,\n buttons: {\n confirm: {\n icon: \"\",\n label: `Confirm`,\n callback: htmlLightSelection => { \n lightSelected = (htmlLightSelection.find('[name=\"light-selector\"]')[0].value)\n lightSelectedAngle = lights[lightSelected].data.angle;\n lightSelectedBright = lights[lightSelected].data.bright;\n lightSelectedDim = lights[lightSelected].data.dim;\n lightSelectedRotation = lights[lightSelected].data.rotation;\n lightSelectedTintAlpha = lights[lightSelected].data.tintAlpha;\n lightSelectedTintColor = lights[lightSelected].data.tintColor;\n //console.log(`${macroName} | lightSelected = ${lightSelected}`);\n //console.log(`${macroName} | lightSelectedBright = ${lightSelectedBright}`);\n dialogEditor.render(true);\n }\n },\n cancel: {\n icon: \"\",\n label: `Cancel`,\n callback: () => {\n console.log(`${macroName} | Goodbye`);\n console.log(macroEndLog);\n }\n },\n },\n default: \"cancel\",\n //close: () => console.log(\"AmbientLight QuickEditor | Dialog Window Closed\")\n});\n\nlet dialogEditor = new Dialog({\n title: `${macroName}`,\n content: `

Edit your light.

\n

Emission Angle: ${lightSelectedAngle}

\n

Bright light distance: ${lightSelectedBright}

\n

Dim light distance: ${lightSelectedDim}

\n

Rotation CW from down: ${lightSelectedRotation}

\n

Tint Alpha: ${lightSelectedAngle}

\n

Tint Color HexCode: ${lightSelectedAngle}

`,\n buttons: {\n rot5cw: {\n icon: \"\",\n label: `Rotate 5* CW`,\n callback: () => { \n let rot = lights[lightSelected].data.rotation;\n lights[lightSelected].update({\"rotation\":rot+5});\n dialogEditor.render(true);\n }\n },\n rot15cw: {\n icon: \"\",\n label: `Rotate 15* CW`,\n callback: () => { \n let rot = lights[lightSelected].data.rotation;\n lights[lightSelected].update({\"rotation\":rot+15});\n dialogEditor.render(true);\n }\n },\n rot45cw: {\n icon: \"\",\n label: `Rotate 45* CW`,\n callback: () => { \n let rot = lights[lightSelected].data.rotation;\n lights[lightSelected].update({\"rotation\":rot+45});\n dialogEditor.render(true);\n }\n },\n rot5ccw: {\n icon: \"\",\n label: `Rotate 5* CCW`,\n callback: () => { \n let rot = lights[lightSelected].data.rotation;\n lights[lightSelected].update({\"rotation\":rot-5});\n dialogEditor.render(true);\n }\n },\n rot15ccw: {\n icon: \"\",\n label: `Rotate 15* CCW`,\n callback: () => { \n let rot = lights[lightSelected].data.rotation;\n lights[lightSelected].update({\"rotation\":rot-15});\n dialogEditor.render(true);\n }\n },\n rot45ccw: {\n icon: \"\",\n label: `Rotate 45* CCW`,\n callback: () => { \n let rot = lights[lightSelected].data.rotation;\n lights[lightSelected].update({\"rotation\":rot-45});\n dialogEditor.render(true);\n }\n },\n brightup: {\n icon: \"\",\n label: `Increase Bright by 5`,\n callback: () => { \n let bright = lights[lightSelected].data.bright;\n lights[lightSelected].update({\"bright\":bright+5});\n dialogEditor.render(true);\n }\n },\n brightdown: {\n icon: \"\",\n label: `Decrease Bright by 5`,\n callback: () => { \n let bright = lights[lightSelected].data.bright;\n lights[lightSelected].update({\"bright\":bright-5});\n dialogEditor.render(true);\n }\n },\n brightoff: {\n icon: \"\",\n label: `Remove Bright Light`,\n callback: () => { \n lights[lightSelected].update({\"bright\":0});\n dialogEditor.render(true);\n }\n },\n dimup: {\n icon: \"\",\n label: `Increase Dim by 5`,\n callback: () => { \n let dim = lights[lightSelected].data.dim;\n lights[lightSelected].update({\"dim\":dim+5});\n dialogEditor.render(true);\n }\n },\n dimdown: {\n icon: \"\",\n label: `Decrease Dim by 5`,\n callback: () => { \n let dim = lights[lightSelected].data.dim;\n lights[lightSelected].update({\"dim\":dim-5});\n dialogEditor.render(true);\n }\n },\n dimoff: {\n icon: \"\",\n label: `Remove Dim Light`,\n callback: () => { \n lights[lightSelected].update({\"dim\":0});\n dialogEditor.render(true);\n }\n },\n emit15: {\n icon: \"\",\n label: `Emission Angle 15*`,\n callback: () => { \n lights[lightSelected].update({\"angle\":15});\n dialogEditor.render(true);\n }\n },\n emit45: {\n icon: \"\",\n label: `Emission Angle 45*`,\n callback: () => { \n lights[lightSelected].update({\"angle\":45});\n dialogEditor.render(true);\n }\n },\n emit90: {\n icon: \"\",\n label: `Emission Angle 90*`,\n callback: () => { \n lights[lightSelected].update({\"angle\":90});\n dialogEditor.render(true);\n }\n },\n emit180: {\n icon: \"\",\n label: `Emission Angle 180*`,\n callback: () => { \n lights[lightSelected].update({\"angle\":180});\n dialogEditor.render(true);\n }\n },\n emit270: {\n icon: \"\",\n label: `Emission Angle 270*`,\n callback: () => { \n lights[lightSelected].update({\"angle\":270});\n dialogEditor.render(true);\n }\n },\n emit360: {\n icon: \"\",\n label: `Emission Angle 360*`,\n callback: () => { \n lights[lightSelected].update({\"angle\":360});\n dialogEditor.render(true);\n }\n },\n back: {\n icon: \"\",\n label: `Back`,\n callback: () => dialogSelector.render(true)\n },\n close: {\n icon: \"\",\n label: `Close`\n },\n },\n default: \"close\",\n close: () => {\n console.log(`${macroName} | Goodbye`);\n console.log(macroEndLog);\n }\n});\n\ndialogSelector.render(true);\n","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} {"_id":"zsAzgwdGPAeArcIJ","name":"Lock All Doors","type":"script","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","scope":"global","command":"/**\n * Locks all closed doors on the canvas\n * Author: orcnog\n */\n \nawait canvas.walls.updateAll(w => ({ds: w.data.ds === CONST.WALL_DOOR_STATES.CLOSED ? CONST.WALL_DOOR_STATES.LOCKED : CONST.WALL_DOOR_STATES.CLOSED}), w => w.data.door === CONST.WALL_DOOR_TYPES.DOOR && (w.data.ds === CONST.WALL_DOOR_STATES.LOCKED || w.data.ds === CONST.WALL_DOOR_STATES.CLOSED));","folder":null,"sort":0,"permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"flags":{"core":{"sourceId":"Macro.sSGS2FhGOqiTIpRV"}}} -{"_id":"BqcD9aq88kWU67Uf","name":"Equip Unequip Shield","type":"script","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","scope":"global","command":"/**\n * D&D5e biased, paths may be wrong in other systems!\n * Equips/unequips an item. Make sure you change the variables at the top (as required).\n * This script will also error check to make sure items exist and tokens are select. \n * Chat and token icon display options can be set as desired.\n * Author: Zapgun, Freeze#2689 (fix for v9)\n */\n\nconst itemName = 'Shield'; // <--- Change this to the *exact* item name (capitals count!)\nconst sendToChat = true; // <--- Change to 'true' or 'false' to display a chat message about equipping\nconst displayIcon = true; // <--- Change to 'true' or 'false' to display an effect icon when equipped\nconst effectIconPath = 'icons/svg/shield.svg'; // <--- Add the effect icon you want to appear when equipped\n\nlet toggleResult = false;\n\nif (!actor) {\n\tui.notifications.warn('You need to select a token before using this macro!');\n} else {\n\n\tconst myItem = actor.items.getName(itemName);\n\tif (myItem) {\n\t\tlet item = actor.items.get(myItem.id);\n\t\tlet attr = \"data.equipped\";\n\t\tlet equipped = getProperty(item.data, attr);\n\t\tif (sendToChat) {\n\t\t\tif (!equipped) {\n\t\t\t\tchatMessage(actor.name + ' equips their ' + ' ' + itemName + '');\n\t\t\t} else {\n\t\t\t\tchatMessage(actor.name + ' un-equips their ' + ' ' + itemName + '');\n\t\t\t}\n\t\t}\n\t\titem.update({ [attr]: !getProperty(item.data, attr) });\n\n\t\t// mark/unmark character's token with an effect icon when displayToken is true\n\t\t(async () => {\n\t\t\tif (displayIcon) {\n\t\t\t\ttoggleResult = await token.toggleEffect(effectIconPath, { active: !equipped });\n\t\t\t\t//\tif (toggleResult == equipped) token.toggleEffect(effectIconPath, {active: equipped}); \n\t\t\t}\n\t\t})();\n\n\t} else {\n\t\tui.notifications.warn(\"No item named '\" + itemName + \"' found on character!\");\n\t}\n}\n\nfunction chatMessage(messageContent) {\n\t// create the message\n\tif (messageContent !== '') {\n\t\tlet chatData = {\n\t\t\tuser: game.user.id,\n\t\t\tspeaker: ChatMessage.getSpeaker(),\n\t\t\tcontent: messageContent,\n\t\t};\n\t\tChatMessage.create(chatData, {});\n\t}\n}","folder":null,"sort":0,"permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"flags":{}} diff --git a/packs/macros-pf2e.db b/packs/macros-pf2e.db index ee505d7..aaf3b87 100644 --- a/packs/macros-pf2e.db +++ b/packs/macros-pf2e.db @@ -1,6 +1,3 @@ -{"name":"Dueling Parry","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.ZqKopKfAv7kGpSxL"}},"scope":"global","command":"(async () => {\n if (actor) {\n for (let token of canvas.tokens.controlled) {\n if (\n (token.actor.data.data.customModifiers[\"ac\"] || []).some(\n (modifier) => modifier.name === \"Dueling Parry\"\n )\n ) {\n await actor.removeCustomModifier(\"ac\", \"Dueling Parry\");\n if (\n token.data.effects.includes(\n \"https://i.imgur.com/QeRgfwS.png\"\n )\n ) {\n token.toggleEffect(\"https://i.imgur.com/QeRgfwS.png\");\n }\n } else {\n await actor.addCustomModifier(\"ac\", \"Dueling Parry\", +1, \"untyped\");\n if (\n !token.data.effects.includes(\n \"https://i.imgur.com/QeRgfwS.png\"\n )\n ) {\n token.toggleEffect(\"https://i.imgur.com/QeRgfwS.png\");\n }\n }\n }\n } else {\n ui.notifications.warn(\"You must have an actor selected.\");\n }\n})();","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"0GEgRnG5EZoSXYsV"} -{"name":"Gozreh Sickness","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.tfrML8ONkV6fPY25"}},"scope":"global","command":"(async () => {\n if (actor) {\n for ( let token of canvas.tokens.controlled ) {\n let messageContent = '';\n if ((token.actor.data.data.customModifiers['attack'] || []).some(modifier => modifier.name === 'Gozreh Sickness')) {\n await token.actor.removeCustomModifier('attack', 'Gozreh Sickness');\n await token.actor.removeCustomModifier('damage', 'Gozreh Sickness');\n await actor.removeCustomModifier(\"ac\", \"Gozreh Sickness\");\n await actor.removeCustomModifier(\"fortitude\", \"Gozreh Sickness\");\n\n if (token.data.effects.includes(\"systems/pf2e/icons/spells/daze.jpg\")) {\n await token.toggleEffect(\"systems/pf2e/icons/spells/daze.jpg\")\n }\n\n messageContent = 'Is no longer Sickened by Gozreh!'\n } else {\n await token.actor.addCustomModifier('attack', 'Gozreh Sickness', -1, 'status');\n await token.actor.addCustomModifier('damage', 'Gozreh Sickness', -1, 'status');\n await actor.addCustomModifier(\"ac\", \"Gozreh Sickness\", -1, \"status\");\n await actor.addCustomModifier(\"fortitude\", \"Gozreh Sickness\", -1, \"status\");\n\n if (!token.data.effects.includes(\"systems/pf2e/icons/spells/daze.jpg\")) {\n await token.toggleEffect(\"systems/pf2e/icons/spells/daze.jpg\")\n }\n\n messageContent = 'Is Sickened by Gozreh!'\n };\n // create the message \n\n if (messageContent !== '') {\n let chatData = {\n user: game.user._id,\n speaker: ChatMessage.getSpeaker(),\n content: messageContent,\n };\n\n await ChatMessage.create(chatData, {});\n }\n }\n } else {\n ui.notifications.warn(\"You must have an actor selected.\");\n }\n})();","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"5qF7D6bCiY0Msq1k"} -{"name":"Double Slice Giff","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.60bqoEqWVJvfzWZr"}},"scope":"global","command":"//Created by u/Griff4218\n//----------------Variable Declaration---------------------\nvar user = token;\nvar targetSelected = false;\nvar targetArray = Array.from(game.user.targets);\nvar target;\n\nvar map = 2\nvar attackMod1 = 0\nvar attackMod2 = 0;\nvar attack1Crit = false;\nvar attack1Hit = false\nvar attack2Crit = false;\nvar attack2Hit = false\nvar attack1CritMiss = false;\nvar attack2CritMiss = false;\nvar selectedWeapon1Action;\nvar selectedWeapon2Action;\nvar weapons = [];\n\nvar targetAC = 0;\n\n//-------------------execution-----------------------------\nif(!user){ //if no token is selected, show an error\n ui.notifications.error(\"No token selected, please select the token that will use this ability\");\n}else{\n //if no target selected show a info notification\n if(targetArray[0]){\n target = targetArray[0].actor.data;\n targetSelected = true;\n }else{\n ui.notifications.info(\"Tip: You can target another creature to automatically compare your attacks to its AC\");\n }\n\n //check if the actor has any weapons that meet the requirements\n weapons = GetEligableItems(user.actor.data.items);\n if(weapons.length < 2 ){\n ui.notifications.error(\"This actor is not holding enough weapons. Make sure to equip your weapons in your inventory\");\n return 0;\n }\n\n\tif(weapons[0]){\n\t\tif(CheckFeat()){\n \n\t\t\tSelectWeapon();\n\t\t}\n\t}else{\n\t\tui.notifications.error(\"This actor has no weapons which meet the requirements for this ability\");\n\t}\n}\n\n//------------------Functions begin------------------------\nfunction toChat(content){\n let chatData = {\n user: game.user.id,\n content,\n speaker: ChatMessage.getSpeaker(),\n }\n ChatMessage.create(chatData, {})\n \n \n}\n\n//Determines if the Actor selected as the user has the requisite feat to use the ability, returns true if it does and false if it does not\nfunction CheckFeat() {\n\tlet items = user.actor.data.items;\n\tlet hasFeat = false;\n\n\tfor(let i = 0; i < items.length; i++){\n\t\tif(items[i].name === \"Double Slice\"){\n\t\t\t\thasFeat = true;\n\t\t\t\treturn true;\n\t\t}\n\t}\n\t\n\tif(!hasFeat){\n\t\tui.notifications.error('This Creature does not have the Feat required to use this ability');\n\t\treturn false;\n\t}\n}\n\n//Creates and returns an array of all items that can be used for Double Slice (Equipped 1 handed weapons)\nfunction GetEligableItems(inv){\n\tlet EligableItems = [];\n\tlet f = 0;\n\t\n\tfor(let i = 0; i < inv.length; i++){\n\t\t\n\t\tif(inv[i].data.equipped && inv[i].data.equipped.value && inv[i].data.hands && (inv[i].data.hands.value === \"\" || inv[i].data.hands.value === \"1\")){\n\t\t\tEligableItems[f] = inv[i];\n\t\t\tf++;\n\t\t}\n\t}\n\t\n\treturn EligableItems;\n}\n\n//if the user has more than one elligable weapon, this funtion displays a dialoge to allow them to select which one to use\nfunction SelectWeapon(){\n let options = '';\n \n //automatically propegates the selection menu for the dialouge with the available weapons\n for(let i = 0; i < weapons.length; i++){\n options += \"\";\n }\n \n //Dialouge control\n let applyChanges = false;\n new Dialog({\n title: \"Weapon Selection\",\n content: `\n
Please select the order you want to use your weapons in
\n
\n
\n
\n \n \n
\n
\n \n \n
\n
\n `,\n buttons: {\n yes: {\n icon: \"\",\n label: `Select Weapon`,\n callback: () => applyChanges = true\n },\n no: {\n icon: \"\",\n label: `Cancel`\n },\n },\n default: \"yes\",\n close: html => {\n if (applyChanges) {\n let first = html.find('[name=\"Weapon-List1\"]')[0].value || 0;\n let second = html.find('[name=\"Weapon-List2\"]')[0].value || 0;\n prepareWeapons(weapons[first], weapons[second]);\n }\n }\n }).render(true);\n \n}\n\nfunction prepareWeapons(selectedWeapon1, selectedWeapon2){\n\n //Double Slice only allows for 2 different weapons. If the same weapon is chosen for both attacks, show an error\n if(selectedWeapon1._id === selectedWeapon2._id){\n ui.notifications.error(\"You must use two different weapons for these strikes\");\n return;\n }\n\n //Assign the actions for each of the weapons\n user.actor.data.data.actions.forEach(element => {\n if(element.item === selectedWeapon1._id){\n selectedWeapon1Action = element;\n }else if(element.item === selectedWeapon2._id){\n selectedWeapon2Action = element;\n }\n });\n\n\n //Determine if the second weapon has the Agile trait\n selectedWeapon2.data.traits.value.forEach(element => {\n if(element === \"agile\"){\n map = 0;\n }\n });\n \n //Assign attack modifiers\n attackMod1 = selectedWeapon1Action.totalModifier;\n attackMod2 = selectedWeapon2Action.totalModifier;\n\n //assign target ac\n if(targetSelected){\n targetAC = target.data.attributes.ac.value;\n }\n\n RollAttacks()\n\n}\n\nfunction handleCrits(roll){\n if(roll === 1){\n return -10;\n }else if(roll === 20){\n return 10;\n }\n\n return 0;\n} \n\nfunction RollAttacks(){\n let message = '';\n let name = user.actor.data.name;\n \n\tconst roll1 = new Roll('d20').roll().total;\n\tconst roll2 = new Roll('d20').roll().total;\n\t\n\tconst crit1 = handleCrits(roll1)\n\tconst crit2 = handleCrits(roll2)\n \n //A lot of If statements to change messages displayed based on the results of each attack roll\n\tif(targetAC === 0){ //targetAC is set to 0 by default, if it stays 0 we assume that no target is selected and do not compare to any AC\n\t\tmessage = name + ' Rolls a ' + (roll1 + attackMod1);\n\t\t\tif(crit1 === 10){\n\t\t\t\tmessage += '[Natural 20]';\n\t\t\t}else if(crit1 === -10){\n\t\t\t\tmessage += '[Natural 1]';\n\t\t\t}else{\n\t\t\t\tmessage += \"[\" + roll1 + \"+\" + attackMod1 + \"]\";\n\t\t\t}\t\n\t\tmessage += ' on their first attack and a ' + (roll2 + attackMod2 - map);\n\t\t\tif(crit2 === 10){\n\t\t\t\tmessage += '[Natural 20]';\n\t\t\t}else if(crit2 === -10){\n\t\t\t\tmessage += '[Natural 1]';\n\t\t\t}else{\n\t\t\t\tmessage += \"[\" + roll2 + \"+\" + attackMod2 + \"-\" + map + \"]\";\n\t\t\t}\n message += ' on their scond attack.';\n\n toChat(message);\n\n selectedWeapon1Action.damage();\n selectedWeapon2Action.damage();\n\n\t}else{\n\t\tif(roll1 + crit1 + attackMod1 >= targetAC+10){//Different messages display on a Crit, Hit, and Miss for each attack, and the damage rolls are set accordingly\n\t\t\tmessage += (name + ' Crits on the First attack with a ' + (roll1 + attackMod1));\n attack1Crit = true;\n\t\t}else if(roll1 + crit1 + attackMod1 >= targetAC){\n\t\t\tmessage += (name + ' Hits on the First attack with a ' + (roll1 + attackMod1));\n attack1Hit = true;\n }else if(roll1 + crit1 + attackMod1 <= targetAC-10){\n\t\t\tmessage += (name + ' Critically misses on the First attack with a ' + (roll1 + attackMod1));\n attack1CritMiss = true;\n\t\t}else{\n\t\t\tmessage += (name + ' Misses the First attack with a ' + (roll1 + attackMod1));\n\t\t}\n\t\tif(crit1 === 10){\n\t\t\tmessage += '[Natural 20]';\n\t\t}else if(crit1 === -10){\n\t\t\tmessage += '[Natural 1]';\n\t\t}else{\n\t\t\tmessage += \"[\" + roll1 + \"+\" + attackMod1 + \"]\";\n\t\t}\t\n\t\t\n\t\tif(roll2 + crit2 + attackMod2 - map >= targetAC+10){\n\t\t\tmessage += (' and Crits on the Second attack with a ' + (roll2 + attackMod2 - map));\n\t\t\tattack2Crit = true\n\t\t\t\n\t\t}else if(roll2 + crit2 + attackMod2 - map >= targetAC){\n\t\t\tmessage += (' and Hits on the Second attack with a ' + (roll2 + attackMod2 - map));\n attack2Hit = true;\n }else if(roll2 + crit2 + attackMod2 - map <= targetAC-10){\n\t\t\tmessage += (name + ' Critically misses on the Second attack with a ' + (roll2 + attackMod2 - map));\n attack2CritMiss = true;\n\t\t}else{\n\t\t\tmessage += (' and Misses the Second attack with a ' + (roll2 + attackMod2 - map));\n\n\t\t}\n\t\tif(crit2 === 10){\n\t\t\tmessage += '[Natural 20]';\n\t\t}else if(crit2 === -10){\n\t\t\tmessage += '[Natural 1]';\n\t\t}else{\n\t\t\tmessage += \"[\" + roll2 + \"+\" + attackMod2 + \"-\" + map + \"]\";\n }\t\n\n //display message and Rolls\n toChat(message);\n if(attack1Crit){\n selectedWeapon1Action.critical();\n }else if(attack1Hit){\n selectedWeapon1Action.damage();\n }\n\n if(attack2Crit){\n selectedWeapon2Action.critical();\n }else if(attack2Hit){\n selectedWeapon2Action.damage();\n }\n\n //if both attacks miss, go to dual onslaught\n if(!attack1Crit && !attack1Hit && !attack2Crit && !attack2Hit){\n dualOnslaught();\n }\n\t}\n\t\n}\n\nfunction dualOnslaught(){\n let items = user.actor.data.items;\n\tlet hasFeat = false;\n\n //check if user has the Dual Onslaught feat\n\tfor(let i = 0; i < items.length; i++){\n\t\tif(items[i].name === \"Dual Onslaught\"){\n\t\t\t\thasFeat = true;\n\t\t\t\tbreak;\n\t\t}\n }\n \n if(hasFeat){\n let i = 0;\n let options = \"\";\n\n //A weapon cannot be used if its attack was a critical miss. Add attacks that missed, but not critically so, to the list of attacks to choose\n if(!attack1CritMiss){\n options += \"\";\n i++;\n }\n if(!attack2CritMiss){\n options += \"\";\n i++;\n }\n\n if(i===0){\n return;\n }\n \n //Display a dialog for the user to pick the attack to apply the hit from\n let applyChanges = false;\n new Dialog({\n title: \"Dual Onslaught\",\n content: `\n
You've missed both Attacks! Select one of them to apply the effects of. Attacks where you critically miss cannot be used.
\n
\n
\n
\n \n \n
\n
\n `,\n buttons: {\n yes: {\n icon: \"\",\n label: `Select Weapon`,\n callback: () => applyChanges = true\n },\n no: {\n icon: \"\",\n label: `Cancel`\n },\n },\n default: \"yes\",\n close: html => {\n if (applyChanges) {\n let choice = html.find('[name=\"Weapon-List1\"]')[0].value || 0;\n\n if(choice === \"first\"){\n selectedWeapon1Action.damage();\n }else{\n selectedWeapon2Action.damage();\n }\n }\n }\n }).render(true);\n }\n}","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"6UmG5QdhAKHqcxIw"} {"name":"Strike Damage","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.ZTsdXtAhtr4ZF5nO"}},"scope":"global","command":"//Script for macro that rolls the damage of a \"strike\"\n//Must select a character with the associated \"strike\" for macro to work\n//Replace the weapon 'Throwing Knife' with the name of the strike you want to attack with\n //ex 'Fist' or 'Dagger\nlet weapon = 'Throwing Knife';\nlet bonusdice = '';\n(actor.data.data.actions ?? []).filter(action => action.type === 'strike').find(strike => strike.name === weapon)?.damage(event, [bonusdice]);","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"Aiqt0tV5jpFwpU2U"} {"name":"Apply Spell Effect","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.pza93dBmwvhYvMBr"}},"scope":"global","command":"//allows bulk adding and removing of Spell Effects to tokens\n\nlet applyChanges = false;\nconst compendiumName=\"pf2e.spell-effects\";\nconst effectCompendium = game.packs.get(compendiumName);\nlet effectList=[];\n\nasync function getEffectList(){\n if(effectList.length<=0){\n let effects = await effectCompendium.getContent();\n for(let count=0;count'+ list[count].Name + '';\n }\n return optionlist;\n}\n\nasync function applyEffect(effectid)\n{\n let effectItem=effectList[effectid];\n \n const item = await fromUuid(effectItem.UUID);\n for (const token of canvas.tokens.controlled) {\n let existing = token.actor.items.filter(i => i.type === item.type).find(e => e.name === item.name);\n if (existing) {\n await token.actor.deleteOwnedItem(existing._id);\n } else {\n let owneditemdata = await token.actor.createOwnedItem(item);\n owneditemdata.data.start.value=game.time.worldTime;\n }\n }\n}\n\nnew Dialog({\n title: `Apply Effect`,\n content: `\n
\n
\n \n \n
\n
\n `,\n buttons: {\n yes: {\n icon: \"\",\n label: `Apply Changes`,\n callback: () => applyChanges = true\n },\n no: {\n icon: \"\",\n label: `Cancel Changes`\n },\n },\n default: \"yes\",\n close: html => {\n if (applyChanges) {\n let effectid = html.find('[name=\"effectChoice\"]')[0].value;\n //console.log(effectItem);\n applyEffect(effectid);\n\n }\n }\n}).render(true);","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"AwVUlYzC4Nwgp1xv"} {"name":"Strike Attack","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.fRwIQcsqEvcSsN4n"}},"scope":"global","command":"//Script for macro that rolls the attack of a \"strike\"\n//Must select a character with the associated \"strike\" for macro to work\n//Replace the weapon 'Throwing Knife' with the name of the strike you want to attack with\n //ex 'Fist' or 'Dagger'\nlet weapon = 'Throwing Knife';\n(actor.data.data.actions ?? []).filter(action => action.type === 'strike').find(strike => strike.name === weapon)?.attack(event);","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"BbUHj4WFU3S3CY1k"} @@ -12,45 +9,18 @@ {"name":"Action: Conceal Object","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.NSeemFaopyQqAQdM"}},"scope":"global","command":"if (canvas.tokens.controlled == 0) {\n ui.notifications.warn(\"You must select a token.\");\n return\n}\n\nactor.data.data.skills.ste.roll(event);\n\n////////////// To chat message data /////////////////\n\nlet toChat = (content) => {\n let chatData = {\n user: game.user.id,\n content,\n speaker: ChatMessage.getSpeaker(),\n }\n\n ChatMessage.create(chatData, {})\n}\n\n//////////// To chat message data //////////////////\n\n\n toChat(`

Conceal an Object

\n
\n You hide a small object on your person (such as a weapon of light Bulk). When you try to sneak a concealed object past someone who might notice it, the GM rolls your Stealth check and compares it to this passive observer's Perception DC. \n
You can also conceal an object somewhere other than your person, such as among undergrowth or in a secret compartment within a piece of furniture. In this case, characters Seeking in an area compare their Perception check results to your Stealth DC to determine whether they find the object.\n
\n \n
\n

✔️ Success!

\n
\n The object remains undetected.\n
\n \n
\n

❌ Fail!

\n
\n The searcher finds the object.\n
\n \n
`)","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"IkwD2h1rSEmeW2jD"} {"name":"Action: Steal","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.yLyyTCsuG0dkihwZ"}},"scope":"global","command":"if (canvas.tokens.controlled == 0) {\n ui.notifications.warn(\"You must select a token.\");\n return\n}\n\nactor.data.data.skills.thi.roll(event);\n\n////////////// To chat message data /////////////////\n\nlet toChat = (content) => {\n let chatData = {\n user: game.user.id,\n content,\n speaker: ChatMessage.getSpeaker(),\n }\n\n ChatMessage.create(chatData, {})\n}\n\n//////////// To chat message data //////////////////\n\n\n toChat(`

Steal

\n
\n You try to take a small object from another creature without being noticed. Typically, you can Steal only an object of negligible Bulk, and you automatically fail if the creature who has the object is in combat or on guard.
\n\n
Attempt a Thievery check to determine if you successfully Steal the object. The DC to Steal is usually the Perception DC of the creature wearing the object. This assumes the object is worn but not closely guarded (like a loosely carried pouch filled with coins, or an object within such a pouch). If the object is in a pocket or similarly protected, you take a -5 penalty to your Thievery check. The GM might increase the DC of your check if the nature of the object makes it harder to steal (such as a very small item in a large pack, or a sheet of parchment mixed in with other documents).
\n\n
You might also need to compare your Thievery check result against the Perception DCs of observers other than the person wearing the object. The GM may increase the Perception DCs of these observers if they're distracted.\n
\n \n
\n

✔️ Success!

\n
\n You steal the item without the bearer noticing, or an observer doesn't see you take or attempt to take the item.\n
\n \n
\n

❌ Fail!

\n
\n The item's bearer notices your attempt before you can take the object, or an observer sees you take or attempt to take the item. The GM determines the response of any creature that notices your theft.\n
\n \n
`)","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"InmgtomV2qj8x76y"} {"name":"Alchemist Bomb","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.BTkziTCQnwTlgxDj"}},"scope":"global","command":"let rollBombDmg = (bomb, dmgRollType, stickyBombUsed, isCritical) => {\n let bonuses = {\n }\n let rollString;\n let chatMsg;\n if (isCritical) chatMsg = `Critical Damage Roll: ${bomb.name}`;\n else chatMsg = ` Damage Roll: ${bomb.name}`;\n chatMsg = `${chatMsg}\n
\n \n ${bomb.type}\n \n \n ${dmgRollType}\n \n `;\n\n let optainedFeats = checkFeats();\n\n switch (dmgRollType) {\n case 'Base':\n rollString = bomb.base;\n if(bomb.condition){\n chatMsg = `\n ${chatMsg}\n \n ${bomb.condition}\n \n
\n `;\n } else {chatMsg = `${chatMsg}
`};\n break;\n\n case 'Persistent':\n // Check for Alchemist Feat Sticky Bomb and if prerequisites are met (Quick Alchemy used and bomb level is atleast 2 lower than alchemy level)\n if (optainedFeats.includes('Sticky Bomb') && stickyBombUsed){\n let stickydmg = bomb.splash;\n // check for Alchemist Feat \"Calculated Splash\"\n if (optainedFeats.includes('Calculated Splash')) {\n // Splash damage of bombs equals int modifier\n stickydmg = `${actor.data.data.abilities.int.mod}`;\n if (optainedFeats.includes('Expanded Splash')) stickydmg = parseInt(bomb.splash) + actor.data.data.abilities.int.mod;\n }\n bonuses = {...bonuses, sticky: stickydmg};\n rollString = `${bomb.persistent} + @sticky`;\n chatMsg = `\n ${chatMsg}\n \n Sticky\n \n
\n `;\n } else {\n rollString = bomb.persistent;\n chatMsg = `${chatMsg}
`;\n }\n if (stickyBombUsed && !optainedFeats.includes('Sticky Bomb')) ui.notifications.warn('You don\\'t have the Feat \"Sticky Bomb\"!');\n break;\n\n case 'Splash':\n // check for Alchemist Feat \"Calculated Splash\"\n if (optainedFeats.includes('Calculated Splash')) {\n // Splash damage of bombs equals int modifier\n rollString = `${actor.data.data.abilities.int.mod}`;\n //rollString = '6';\n if (optainedFeats.includes('Expanded Splash')){\n let splashdmg = parseInt(bomb.splash) + actor.data.data.abilities.int.mod\n rollString = `${splashdmg}`;\n }\n } else {\n rollString = bomb.splash;\n }\n chatMsg = `${chatMsg}
`;\n break;\n default:\n }\n\n // Goblin Feat \"Burn It!\"\n if (bomb.type === 'Fire' && optainedFeats.includes('Burn It!')) {\n // + floor(itemlvl/4) to all damage types\n let damageBonus = Math.max(Math.floor(bomb.level/4), 1);\n if (!bonuses.status) {\n bonuses = {...bonuses, status: damageBonus};\n rollString = rollString + ' + @status';\n } else if (bonuses.status < damageBonus) {\n bonuses.status = damageBonus;\n }\n }\n\n\n // simulate the roll with all aplicable boni\n\n\n if(isCritical && (dmgRollType === 'Base' || dmgRollType === 'Persistent')){\n rollString = `${rollString} + ${rollString}`;\n }\n let roll = new Roll(rollString, bonuses);\n roll.roll();\n\n let chatData = {\n user: game.user._id,\n speaker: ChatMessage.getSpeaker(),\n flavor: chatMsg,\n type: CONST.CHAT_MESSAGE_TYPES.ROLL,\n roll,\n };\n ChatMessage.create(chatData, {});\n\n}\n\nlet checkFeats = () => {\n // look up, if the character has specific feats\n let featsToLookUp = ['Burn It!', 'Calculated Splash', 'Expanded Splash', 'Sticky Bomb'];\n let optainedFeats = [];\n\n // checks for every item in featsToLookUp if the selected Actor has this feat and if so, pushes the item to optainedFeats\n featsToLookUp.forEach(\n (item, index) => {\n if (actor.items.find(i => i.name === item) != null) {\n optainedFeats.push(item);\n }\n }\n );\n return optainedFeats;\n}\n\nlet getBombInfo = (selectedBomb, selectedQuality) => {\n // default values for bombs.\n let bombName;\n let bombPersistent, bombSplashDmg, bombBaseDmg, dmgType;\n let condition, level;\n\n if (selectedQuality) bombName = `${selectedBomb} (${selectedQuality})`;\n else bombName = `${selectedBomb}`;\n\n // sets the default item level depending on the quality of the bomb\n // lesser = 1, moderate = 3, greater = 11, major = 17\n // bombs that differ from these values need those values set in the cases\n switch (selectedQuality) {\n case 'Lesser':\n level = 1;\n break;\n case 'Moderate':\n level = 3;\n break;\n case 'Greater':\n level = 11;\n break;\n case 'Major':\n level = 17;\n break;\n default:\n }\n\n // change the vars according to the selected bomb\n // template for new bomb:\n // case 'bombName':\n // switch (selectedQuality) {\n // case 'Lesser':\n // break;\n // case 'Moderate':\n // break;\n // case 'Greater':\n // break;\n // case 'Major':\n // break;\n // default:\n // }\n // break;\n switch (selectedBomb) {\n // Acid Flask\n case 'Acid Flask':\n switch (selectedQuality) {\n case 'Lesser':\n bombPersistent = '1d6';\n bombSplashDmg = '1';\n break;\n case 'Moderate':\n bombPersistent = '2d6';\n bombSplashDmg = '2';\n break;\n case 'Greater':\n bombPersistent = '3d6';\n bombSplashDmg = '3';\n break;\n case 'Major':\n bombPersistent = '4d6';\n bombSplashDmg = '4';\n break;\n default:\n bombPersistent = '1d6';\n bombSplashDmg = '1';\n }\n // Bomb traits independent of quality\n bombBaseDmg = '1';\n dmgType = 'Acid';\n break;\n\n // Alchemist's Fire\n case \"Alchemist\\'s Fire\":\n switch (selectedQuality) {\n case 'Lesser':\n bombPersistent = '1';\n bombBaseDmg = '1d8';\n bombSplashDmg = '1';\n break;\n case 'Moderate':\n bombPersistent = '2';\n bombBaseDmg = '2d8';\n bombSplashDmg = '2';\n break;\n case 'Greater':\n bombPersistent = '3';\n bombBaseDmg = '3d8';\n bombSplashDmg = '3';\n break;\n case 'Major':\n bombPersistent = '4';\n bombBaseDmg = '4d8';\n bombSplashDmg = '4';\n break;\n default:\n bombPersistent = '1';\n bombBaseDmg = '1d8';\n bombSplashDmg = '1';\n }\n // Bomb traits independent of quality\n dmgType = 'Fire';\n break;\n\n // Bottled Lightning\n case \"Bottled Lightning\":\n switch (selectedQuality) {\n case 'Lesser':\n bombBaseDmg = '1d6';\n bombSplashDmg = '1';\n break;\n case 'Moderate':\n bombBaseDmg = '2d6';\n bombSplashDmg = '2';\n break;\n case 'Greater':\n bombBaseDmg = '3d6';\n bombSplashDmg = '3';\n break;\n case 'Major':\n bombBaseDmg = '4d6';\n bombSplashDmg = '4';\n break;\n default:\n bombBaseDmg = '1d6';\n bombSplashDmg = '1';\n }\n // Bomb traits independent of quality\n dmgType = 'Electricity';\n condition = 'flat-footed';\n break;\n\n // Blight Bomb\n case \"Blight Bomb\":\n switch (selectedQuality) {\n case 'Lesser':\n bombPersistent = '1d4';\n bombBaseDmg = '1d6';\n bombSplashDmg = '1';\n break;\n case 'Moderate':\n bombPersistent = '2d4';\n bombBaseDmg = '2d6';\n bombSplashDmg = '2';\n break;\n case 'Greater':\n bombPersistent = '3d4';\n bombBaseDmg = '3d6';\n bombSplashDmg = '3';\n break;\n case 'Major':\n bombPersistent = '4d4';\n bombBaseDmg = '4d6';\n bombSplashDmg = '4';\n break;\n default:\n bombPersistent = '1d4';\n bombBaseDmg = '1d6';\n bombSplashDmg = '1';\n }\n // Bomb traits independent of quality\n dmgType = 'Poison'\n break;\n\n // Dread Ampoule\n case 'Dread Ampoule':\n switch (selectedQuality) {\n case 'Lesser':\n bombBaseDmg = '1d6';\n bombSplashDmg = '1';\n break;\n case 'Moderate':\n bombBaseDmg = '2d6';\n bombSplashDmg = '2';\n break;\n case 'Greater':\n bombBaseDmg = '3d6';\n bombSplashDmg = '3';\n break;\n case 'Major':\n bombBaseDmg = '4d6';\n bombSplashDmg = '4';\n break;\n default:\n bombBaseDmg = '1d6';\n bombSplashDmg = '1';\n }\n // Bomb traits independent of quality\n condition = 'frightened 1';\n dmgType = 'Mental';\n break;\n\n // Frost Vial\n case 'Frost Vial':\n switch (selectedQuality) {\n case 'Lesser':\n bombBaseDmg = '1d6';\n bombSplashDmg = '1';\n condition = '-5ft Speed';\n break;\n case 'Moderate':\n bombBaseDmg = '2d6';\n bombSplashDmg = '2';\n condition = '-10ft Speed';\n break;\n case 'Greater':\n bombBaseDmg = '3d6';\n bombSplashDmg = '3';\n condition = '-10ft Speed';\n break;\n case 'Major':\n bombBaseDmg = '4d6';\n bombSplashDmg = '4';\n condition = '-15ft Speed';\n break;\n default:\n bombBaseDmg = '1d6';\n bombSplashDmg = '1';\n condition = '-5ft Speed';\n }\n // Bomb traits independent of quality\n dmgType = 'Cold';\n break;\n\n // Ghost Charge\n case 'Ghost Charge':\n switch (selectedQuality) {\n case 'Lesser':\n bombBaseDmg = '1d8';\n bombSplashDmg = '1';\n condition = 'enfeebled 1';\n break;\n case 'Moderate':\n bombBaseDmg = '2d8';\n bombSplashDmg = '2';\n condition = 'enfeebled 1';\n break;\n case 'Greater':\n bombBaseDmg = '3d8';\n bombSplashDmg = '3';\n condition = 'enfeebled 2';\n break;\n case 'Major':\n bombBaseDmg = '4d8';\n bombSplashDmg = '4';\n condition = 'enfeebled 2';\n break;\n default:\n bombBaseDmg = '1d8';\n bombSplashDmg = '1';\n condition = 'enfeebled 1';\n }\n // Bomb traits independent of quality\n dmgType = 'Positive';\n break;\n\n // Peshpine Grenade\n case 'Peshpine Grenade':\n switch (selectedQuality) {\n case 'Lesser':\n bombBaseDmg = '1d6';\n bombSplashDmg = '1';\n condition = 'stupefied 1';\n break;\n case 'Moderate':\n bombBaseDmg = '2d6';\n bombSplashDmg = '2';\n condition = 'stupefied 1';\n break;\n case 'Greater':\n bombBaseDmg = '3d6';\n bombSplashDmg = '3';\n condition = 'stupefied 2';\n break;\n case 'Major':\n bombBaseDmg = '4d6';\n bombSplashDmg = '4';\n condition = 'stupefied 3';\n break;\n default:\n bombBaseDmg = '1d6';\n bombSplashDmg = '1';\n condition = 'stupefied 1';\n }\n // Bomb traits independent of quality\n dmgType = 'Piercing';\n break;\n\n // Tanglefoot Bag\n // Tanglefoot Bag is unique in that it doesn't deal any damage.\n // For now that is aquired by rolling 0 dmg\n case 'Tanglefoot Bag':\n switch (selectedQuality) {\n case 'Lesser':\n condition = '-10ft Speed';\n break;\n case 'Moderate':\n condition = '-15ft Speed';\n break;\n case 'Greater':\n condition = '-15ft Speed';\n break;\n case 'Major':\n condition = '-20ft Speed';\n break;\n default:\n condition = '-10ft Speed';\n }\n // Bomb traits independent of quality\n // on crit: immobilized 1\n bombBaseDmg = '0';\n break;\n\n // Thunderstone\n case 'Thunderstone':\n switch (selectedQuality) {\n case 'Lesser':\n bombBaseDmg = '1d4';\n bombSplashDmg = '1';\n condition = 'Fort DC 17';\n break;\n case 'Moderate':\n bombBaseDmg = '2d4';\n bombSplashDmg = '2';\n condition = 'Fort DC 20';\n break;\n case 'Greater':\n bombBaseDmg = '3d4';\n bombSplashDmg = '3';\n condition = 'Fort DC 28';\n break;\n case 'Major':\n bombBaseDmg = '4d4';\n bombSplashDmg = '4';\n condition = 'Fort DC 38';\n break;\n default:\n bombBaseDmg = '1d4';\n bombSplashDmg = '1';\n condition = 'Fort DC 17';\n }\n // Bomb traits independent of quality\n condition = `${condition}: deafened`;\n dmgType = 'Sonic';\n break;\n\n // Dwarven Daisy\n case 'Dwarven Daisy':\n switch (selectedQuality) {\n case 'Lesser':\n bombBaseDmg = '1d6';\n bombSplashDmg = '1';\n condition = 'Fort DC 16';\n break;\n case 'Moderate':\n bombBaseDmg = '1d6';\n bombSplashDmg = '1';\n condition = 'Fort DC 16';\n break;\n default:\n bombBaseDmg = '1d6';\n bombSplashDmg = '1';\n condition = 'Fort DC 16';\n ui.notifications.warn('The selected Bomb isn\\'t avaiable in the selected Quality. Assuming Lesser!');\n }\n // Bomb traits independent of quality\n dmgType = 'Fire';\n condition = `${condition}: dazzled 1`;\n break;\n\n // Crystal Shards\n case 'Crystal Shards':\n switch (selectedQuality) {\n case 'Moderate':\n bombBaseDmg = '2d4';\n bombSplashDmg = '4';\n level = 4;\n break;\n case 'Greater':\n bombBaseDmg = '3d4';\n bombSplashDmg = '5';\n level = 12;\n break;\n case 'Major':\n bombBaseDmg = '4d4';\n bombSplashDmg = '6';\n level = 18;\n break;\n default:\n bombBaseDmg = '2d4';\n bombSplashDmg = '4';\n level = 4;\n ui.notifications.warn('The selected Bomb isn\\'t avaiable in the selected Quality. Assuming Moderate!');\n }\n // Bomb traits independent of quality\n condition = 'caltrops';\n dmgType = 'Piercing'\n break;\n\n default:\n break;\n }\n\n // only persistent and condition should be left undefined\n return {\n name: bombName,\n base: bombBaseDmg,\n splash: bombSplashDmg,\n type: dmgType,\n persistent: bombPersistent,\n condition: condition,\n level: level,\n };\n}\n\nlet applyChanges = false;\nlet rollAll = false;\nif (actor) {\n if(actor.data.type === 'character') {\n\n new Dialog({\n title: `Alchemical Bombs`,\n content: `\n
\n Select a Bomb and a Quality.\n

\n For Bombs that dont have different qualities only select the bomb.\n
\n
\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n
\n \n `,\n //buttoms\n buttons: {\n yes: {\n //icon: \"\",\n label: `Roll Damage`,\n callback: () => {applyChanges = true; rollAll = true},\n },\n maybe: {\n //icon: \"\",\n label: `Roll Persistent`,\n callback: () => applyChanges = true\n },\n no: {\n //icon: \"\",\n label: `Cancel`,\n callback: () => applyChanges = false\n },\n },\n default: \"no\",\n // what happens when you close the dialog\n close: html => {\n if (applyChanges) {\n //for ( let token of canvas.tokens.controlled ) {\n //let selectedBomb = html.find('[name=\"bomb-used\"]')[0].value || \"none\";\n let selectedBombIndex = html.find('[name=\"bomb-used\"]')[0].options.selectedIndex;\n let bombName = html.find('[name=\"bomb-used\"]')[0][selectedBombIndex].label || \"none\";\n\n\n\n //let selectedQuality = html.find('[name=\"bomb-quality\"]')[0].value || \"none\";\n let selectedQualityIndex = html.find('[name=\"bomb-quality\"]')[0].options.selectedIndex;\n var bombQuality = html.find('[name=\"bomb-quality\"]')[0][selectedQualityIndex].label || \"none\";\n\n let bomb = getBombInfo(bombName, bombQuality);\n\n //}\n\n\n\n // roll all three types of dmg and output it to chat\n if (rollAll) {\n // base damage\n rollBombDmg(bomb, 'Base', stickyBomb.checked, critical.checked);\n // splash damage\n if (bomb.splash) rollBombDmg(bomb, 'Splash', stickyBomb.checked, critical.checked);\n // persistent damage, if aplicable\n if (bomb.persistent) rollBombDmg(bomb, 'Persistent', stickyBomb.checked, critical.checked);\n else if (stickyBomb.checked) {\n bomb.persistent = '0';\n rollBombDmg(bomb, 'Persistent', stickyBomb.checked, critical.checked);\n }\n // only roll the persistent dmg and output it to chat\n } else if (bomb.persistent){\n rollBombDmg(bomb, 'Persistent', stickyBomb.checked, critical.checked);\n } else ui.notifications.warn('Select a Bomb that deals persistent damage!');\n\n }\n }\n }).render(true);\n } else if (actor.data.type !== 'character') ui.notifications.warn(\"You must select a playable character!\");\n} else ui.notifications.warn(\"You must select a playable character!\");;","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"JchrKec14Z6ATirE"} -{"name":"Dueling Cape","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.snpX75KWwaceAdos"}},"scope":"global","command":"(async () => {\n if (actor) {\n for (let token of canvas.tokens.controlled) {\n if (\n (token.actor.data.data.customModifiers[\"ac\"] || []).some(\n (modifier) => modifier.name === \"Dueling Cape\"\n )\n ) {\n await actor.removeCustomModifier(\"ac\", \"Dueling Cape\");\n if (\n token.data.effects.includes(\n \"systems/pf2e/icons/equipment/adventuring-gear/dueling-cape.jpg\"\n )\n ) {\n token.toggleEffect(\"systems/pf2e/icons/equipment/adventuring-gear/dueling-cape.jpg\");\n }\n } else {\n await actor.addCustomModifier(\"ac\", \"Dueling Cape\", 1, \"circumstance\");\n if (\n !token.data.effects.includes(\n \"systems/pf2e/icons/spells/fire-shield.jpg\"\n )\n ) {\n token.toggleEffect(\"systems/pf2e/icons/equipment/adventuring-gear/dueling-cape.jpg\");\n }\n }\n }\n } else {\n ui.notifications.warn(\"You must have an actor selected.\");\n }\n})();","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"NyeSPiR2vhfDZP08"} {"name":"Blood Magic Proc","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.Af4NvsNyephxWmUj"}},"scope":"global","command":"(async () => {\n if (actor) {\n for (let token of canvas.tokens.controlled) {\n let messageContent = '';\n if (\n (token.actor.data.data.customModifiers[\"ac\"] || []).some(\n (modifier) => modifier.name === \"Blood Magic\"\n )\n ) {\n await actor.removeCustomModifier(\"ac\", \"Blood Magic\"); \n if (\n token.data.effects.includes(\n \"https://i.imgur.com/Jtub936.png\"\n )\n ) {\n token.toggleEffect(\"https://i.imgur.com/Jtub936.png\");\n }\n messageContent = 'The scales begin to fade away...'\n } else { \n await actor.addCustomModifier(\"ac\", \"Blood Magic\", +1, \"untyped\");\n if (\n !token.data.effects.includes(\n \"https://i.imgur.com/Jtub936.png\"\n )\n ) {\n token.toggleEffect(\"https://i.imgur.com/Jtub936.png\");\n }\nmessageContent = 'Draconic scales strengthen your defenses.'\n };\n // create the message \n\n if (messageContent !== '') {\n let chatData = {\n user: game.user._id,\n speaker: ChatMessage.getSpeaker(),\n content: messageContent,\n };\n\n await ChatMessage.create(chatData, {});\n }\n }\n } else {\n ui.notifications.warn(\"You must have an actor selected.\");\n }\n})();","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"OwukeAHEC6rqj4rz"} {"name":"Persistent Damage Check","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.UB0Oge2Ga302EBuT"}},"scope":"global","command":"let applyChanges = false;\n let clearDamage = false\n let template = `\n
\n
\n \n \n
\n
\n \n
\n
\n
\n `;\n\n new Dialog({\n title: \"Persistent Damage\",\n content: `` + template,\n buttons: {\n ok: {\n icon: \"\",\n label: \"Apply\",\n callback: () => applyChanges = true\n },\n cancel: {\n icon: \"\",\n label: \"Cancel\",\n },\n clear: {\n icon: \"\",\n label: \"Clear\",\n callback: () => clearDamage = true\n },\n },\ndefault: \"yes\",\n close: html => {\n if (applyChanges) {\n\t\t\t\t\t\t\n\t\tif (damage_type.value == \"bleeding\"){\n\t\t addAlerts(document.getElementById(\"damage\").value, \"bleeding\");\n\t\t token.toggleEffect(\"Homebrew/04.%20Icons/02.%20Conditions/damage/bleeding.png\");\n\t\t}\n\t\tif (damage_type.value == \"fire\"){\n\t\t\ttoken.toggleEffect(\"Homebrew/04.%20Icons/02.%20Conditions/damage/fire.png\");\n\t\t\taddAlerts(document.getElementById(\"damage\").value, \"fire\");\n\t\t}\n\t\tif (damage_type.value == \"acid\"){\n\t\t\ttoken.toggleEffect(\"Homebrew/04.%20Icons/02.%20Conditions/damage/acid.png\");\n\t\t\taddAlerts(document.getElementById(\"damage\").value, \"acid\");\n\t\t}\n\t\tif (damage_type.value == \"cold\"){\n\t\t\ttoken.toggleEffect(\"Homebrew/04.%20Icons/02.%20Conditions/damage/cold.png\");\n\t\t\taddAlerts(document.getElementById(\"damage\").value, \"cold\");\n\t\t}\n\t\tif (damage_type.value == \"electricity\"){\n\t\t\ttoken.toggleEffect(\"Homebrew/04.%20Icons/02.%20Conditions/damage/electricity.png\");\n\t\t\taddAlerts(document.getElementById(\"damage\").value, \"electricity\");\n\t\t}\n\t\tif (damage_type.value == \"mental\"){\n\t\t\ttoken.toggleEffect(\"Homebrew/04.%20Icons/02.%20Conditions/damage/mental.png\");\n\t\t\taddAlerts(document.getElementById(\"damage\").value, \"mental\");\n\t\t}\n\t\tif (damage_type.value == \"poison\"){\n\t\t\ttoken.toggleEffect(\"Homebrew/04.%20Icons/02.%20Conditions/damage/poison.png\");\n\t\t\taddAlerts(document.getElementById(\"damage\").value, \"poison\");\n\t\t}\n\t\t\n\t}\n\tif (clearDamage) {\n\t if (damage_type.value == \"bleeding\"){\n\t if (token.data.effects.includes(\"Homebrew/04.%20Icons/02.%20Conditions/damage/bleeding.png\")) {\n\t token.toggleEffect(\"Homebrew/04.%20Icons/02.%20Conditions/damage/bleeding.png\");\n\t deleteAlert(\"bleeding\",`${token.name}`)\n\t }\n\t }\n\t if (damage_type.value == \"fire\"){\n\t\t\tif (token.data.effects.includes(\"Homebrew/04.%20Icons/02.%20Conditions/damage/fire.png\")) {\n\t\t\t token.toggleEffect(\"Homebrew/04.%20Icons/02.%20Conditions/damage/fire.png\");\n\t\t\t deleteAlert(\"fire\",`${token.name}`)\n\t\t\t}\n\t\t}\n\t\tif (damage_type.value == \"acid\"){\n\t if (token.data.effects.includes(\"Homebrew/04.%20Icons/02.%20Conditions/damage/acid.png\")) {\n\t\t\t token.toggleEffect(\"Homebrew/04.%20Icons/02.%20Conditions/damage/acid.png\");\n\t\t\t deleteAlert(\"acid\",`${token.name}`)\n\t }\n\t\t}\n\t\tif (damage_type.value == \"cold\"){\n\t if (token.data.effects.includes(\"Homebrew/04.%20Icons/02.%20Conditions/damage/cold.png\")) {\n\t\t\t token.toggleEffect(\"Homebrew/04.%20Icons/02.%20Conditions/damage/cold.png\");\n\t\t\t deleteAlert(\"cold\",`${token.name}`)\n\t }\n\t\t}\n\t\tif (damage_type.value == \"electricity\"){\n\t\t if (token.data.effects.includes(\"Homebrew/04.%20Icons/02.%20Conditions/damage/electricity.png\")) {\n\t \t token.toggleEffect(\"Homebrew/04.%20Icons/02.%20Conditions/damage/electricity.png\");\n\t\t\t deleteAlert(\"electricity\",`${token.name}`)\n\t\t }\n\t\t}\n\t\tif (damage_type.value == \"mental\"){\n\t\t if (token.data.effects.includes(\"Homebrew/04.%20Icons/02.%20Conditions/damage/mental.png\")) {\n\t\t token.toggleEffect(\"Homebrew/04.%20Icons/02.%20Conditions/damage/mental.png\");\n\t\t\t deleteAlert(\"mental\",`${token.name}`)\n\t\t }\n\t\t}\n\t\tif (damage_type.value == \"poison\"){\n\t\t if (token.data.effects.includes(\"Homebrew/04.%20Icons/02.%20Conditions/damage/poison.png\")) {\n\t\t token.toggleEffect(\"Homebrew/04.%20Icons/02.%20Conditions/damage/poison.png\");\n\t\t\t deleteAlert(\"poison\",`${token.name}`)\n\t\t }\n\t\t}\n\t \n\t}\n}\n\n },{\n id: 'pf2-template-creator'\n }).render(true);\n\n\nfunction addAlerts(damage, type) {\n if (!game.combat) {\n ui.notifications.warn(\"No combat currently active.\")\n return\n} \n \n canvas.tokens.controlled.forEach(t => {\n addAlert(damage, type, t);\n });\n}\n\nfunction addAlert(damage, type, t) {\n const text = t.data.name + \" takes [[/r \" + damage + \"#\"+type+\" damage]] persistent \" + type + \" damage.
Roll DC 15 Flat check, [[/r 1d20 #flat check vs DC 15]]\";\n const text2 = t.data.name + \" is taking persistent \" + type + \" damage.\";\n const combatData = game.combat.data;\n const alertData = {\n combatId: combatData._id,\n createdRound: combatData.round,\n turnId: game.combat.getCombatantByToken(t.data._id)._id,\n round: 0,\n repeating: { frequency: 1, expire: null, expireAbsolute: false, },\n roundAbsolute: false,\n message: text,\n args: type + t.data.name, \n userId: game.userId,\n endOfTurn: true,\n recipientIds: []\n }\n TurnAlert.create(alertData);\n let chatData = {\n user: game.user._id,\n speaker: ChatMessage.getSpeaker(),\n content: text2\n }; ChatMessage.create(chatData, {chatBubble: true});\n}\n\nfunction deleteAlert(type, token){\n if (!game.combat?.data?.flags?.turnAlert?.alerts) {\n ui.notifications.warn(\"There are no alerts on the currently active combat.\");\n return\n} else { \n let alert = TurnAlert.find(c => c.args === `${type}` + `${token}`);\n TurnAlert.delete(alert.combatId, alert.id);\n}\n}","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"PFgqI3RzrIM6PMGC"} {"name":"Spell Attack","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.nudnGSKuk9IvukkG"}},"scope":"global","command":"//Spell Attack: This rolls the spell attack for any \"Spontaneous Primal Spells\". \n //Replace the name with whatever the name of your Spellcasting Entry is in your spellbook to roll that spell attack.\n\nlet name = 'Spontaneous Primal Spells';\n\nlet modifier = (actor.data.items).find(item => item.name\n == name).data.spelldc.value;\n\nconst roll = new Roll('1d20+' + modifier);\n\nroll.roll();\n\nroll.toMessage({ flavor: \"Spell Attack\", speaker: ChatMessage.getSpeaker({actor: actor})});","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"QNeyuuTprAFeibtw"} -{"name":"Animal Rage","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.bmPWLDlpQbpzUeBM"}},"scope":"global","command":"(async () => {\n if (actor) {\n for (let token of canvas.tokens.controlled) {\n if (\n (token.actor.data.data.customModifiers[\"ac\"] || []).some(\n (modifier) => modifier.name === \"Animal Rage\"\n )\n ) {\n await actor.removeCustomModifier(\"ac\", \"Animal Rage\");\n /// Remove the line below if you do not wish for your character to lose all temp hp when toggled \"off\".\n await actor.update({ \"data.attributes.hp.temp\": 0 });\n /// Remove the line above if you do not wish for your character to lose all temp hp when toggled \"off\".\n if (\n token.data.effects.includes(\n \"systems/pf2e/icons/features/classes/rage.jpg\"\n )\n ) {\n token.toggleEffect(\"systems/pf2e/icons/features/classes/rage.jpg\");\n }\n } else {\n const tmpHP =\n token.actor.data.data.details.level.value +\n token.actor.data.data.abilities.con.mod;\n if (token.actor.data.data.attributes.hp.temp < tmpHP) {\n await actor.update({ \"data.attributes.hp.temp\": tmpHP });\n }\n await actor.addCustomModifier(\"ac\", \"Animal Rage\", -1, \"untyped\");\n if (\n !token.data.effects.includes(\n \"systems/pf2e/icons/features/classes/rage.jpg\"\n )\n ) {\n token.toggleEffect(\"systems/pf2e/icons/features/classes/rage.jpg\");\n }\n }\n }\n } else {\n ui.notifications.warn(\"You must have an actor selected.\");\n }\n})();","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"RMhZ3WdSToRNV5cU"} {"_id":"SrJN3RvSnkrSh2Rj","name":"Vision Configuration","type":"script","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","scope":"global","command":"// credit to this repo/user: https://github.com/Sky-Captain-13/foundry/blob/master/scriptMacros/tokenVision.js\nlet applyChanges = false;\nnew Dialog({\n title: `Token Vision Configuration`,\n content: `\n
\n
\n \n \n
\n
\n \n \n
\n
\n `,\n buttons: {\n yes: {\n icon: \"\",\n label: `Apply Changes`,\n callback: () => applyChanges = true\n },\n no: {\n icon: \"\",\n label: `Cancel Changes`\n },\n },\n default: \"yes\",\n close: html => {\n if (applyChanges) {\n for ( let token of canvas.tokens.controlled ) {\n let visionType = html.find('[name=\"vision-type\"]')[0].value || \"none\";\n let lightSource = html.find('[name=\"light-source\"]')[0].value || \"none\";\n let dimSight = 0;\n let brightSight = 0;\n let dimLight = 0;\n let brightLight = 0;\n let lightAngle = 360;\n let lockRotation = token.data.lockRotation;\n // Get Vision Type Values\n switch (visionType) {\n case \"dim0\":\n dimSight = 0;\n brightSight = 0;\n break;\n case \"dim30\":\n dimSight = 30;\n brightSight = 0;\n break;\n case \"dim60\":\n dimSight = 60;\n brightSight = 0;\n break;\n case \"dim90\":\n dimSight = 90;\n brightSight = 0;\n break;\n case \"dim120\":\n dimSight = 120;\n brightSight = 0;\n break;\n case \"dim150\":\n dimSight = 150;\n brightSight = 0;\n break;\n case \"dim180\":\n dimSight = 180;\n brightSight = 0;\n break;\n case \"bright120\":\n dimSight = 0;\n brightSight= 120;\n break;\n case \"nochange\":\n default:\n dimSight = token.data.dimSight;\n brightSight = token.data.brightSight;\n }\n // Get Light Source Values\n switch (lightSource) {\n case \"none\":\n dimLight = 0;\n brightLight = 0;\n break;\n case \"candle\":\n dimLight = 10;\n brightLight = 5;\n break;\n case \"lamp\":\n dimLight = 45;\n brightLight = 15;\n break;\n case \"bullseye\":\n dimLight = 120;\n brightLight = 60;\n lockRotation = false;\n lightAngle = 52.5;\n break;\n case \"hooded-dim\":\n dimLight = 5;\n brightLight = 0;\n break;\n case \"hooded-bright\":\n dimLight = 60;\n brightLight = 30;\n break;\n case \"light\":\n dimLight = 40;\n brightLight = 20;\n break;\n case \"torch\":\n dimLight = 40;\n brightLight = 20;\n break;\n case \"nochange\":\n default:\n dimLight = token.data.dimLight;\n brightLight = token.data.brightLight;\n lightAngle = token.data.lightAngle;\n lockRotation = token.data.lockRotation;\n }\n // Update Token\n console.log(token);\n token.document.update({light:{bright: brightLight, dim: dimLight, angle: lightAngle}});\n }\n }\n }\n}).render(true);","folder":null,"sort":0,"permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"flags":{"core":{"sourceId":"Macro.j36GpxX1gHlgU92E"}}} {"name":"Hunted Shot","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.coXc0B18sVhgtEf0"}},"scope":"global","command":"//Created by u/Griff4218\n\n//------------------Functions begin------------------------\nlet toChat = (content, rollString) => {\n let chatData = {\n user: game.user.id,\n content,\n speaker: ChatMessage.getSpeaker(),\n }\n ChatMessage.create(chatData, {})\n if (rollString) {\n let roll = new Roll(rollString).roll();\n chatData = {\n ...chatData,\n flavor: \"Hunted Shot Damage\",\n type: CONST.CHAT_MESSAGE_TYPES.ROLL,\n roll\n }\n ChatMessage.create(chatData, {})\n }\n \n}\n\nconst handleCrits = (roll) => roll === 1 ? -10 : (roll === 20 ? 10 : 0);\n\nlet RollAttacks = (args) => {\n\tlet {targetAC, bonus, name, weapon} = args;\n\t\n\tvar message = '';\n\tvar damage = '';\n\t\n\tconst roll1 = new Roll('d20').roll().total;\n\tconst roll2 = new Roll('d20').roll().total;\n\t\n\tconst crit1 = handleCrits(roll1)\n\tconst crit2 = handleCrits(roll2)\n\n\tif(map === \"\"){\n\t\tmap = 5;\n\n\t\tweapon.data.traits.value.forEach(element => {\n\t\t\tif(element === \"agile\"){\n\t\t\t\tmap = 4;\n\t\t\t}\n\t\t});\n\t}\n\t\n\t//A lot of If statements to change messages displayed based on the results of each attack roll\n\tif(targetAC === 0){ //targetAC is set to 0 by default, if it stays 0 we assume that no target is selected and do not compare to any AC\n\t\tmessage = name + ' Rolls a ' + (roll1 + bonus);\n\t\t\tif(crit1 === 10){\n\t\t\t\tmessage += '[Natural 20]';\n\t\t\t}else if(crit1 === -10){\n\t\t\t\tmessage += '[Natural 1]';\n\t\t\t}else{\n\t\t\t\tmessage += \"[\" + roll1 + \"+\" + bonus + \"]\";\n\t\t\t}\t\n\t\tmessage += ' on their first attack and a ' + (roll2 + bonus - map);\n\t\t\tif(crit2 === 10){\n\t\t\t\tmessage += '[Natural 20]';\n\t\t\t}else if(crit2 === -10){\n\t\t\t\tmessage += '[Natural 1]';\n\t\t\t}else{\n\t\t\t\tmessage += \"[\" + roll2 + \"+\" + bonus + \"-\" + map + \"]\";\n\t\t\t}\n\t\tmessage += ' on their scond attack.';\n\t\tdamage += weaponDamage + \"+\" + weaponDamage;\n\t}else{\n\t\tif(roll1 + crit1 + bonus >= targetAC+10){//Different messages display on a Crit, Hit, and Miss for each attack, and the damage rolls are set accordingly\n\t\t\tmessage += (name + ' Crits on the First attack with a ' + (roll1 + bonus));\n\t\t\tdamage += weaponDamage+'*2';\n\t\t\tweaponTraits.forEach(element => {\n\t\t\t\tif(element.includes(\"deadly\")){\n\t\t\t\t\tvar deadly = element.split('-');\n\t\t\t\t\tdamage += \"+\" + deadlyDamage + deadly[1];\n\t\t\t\t}\n\t\t\t});\n\t\t}else if(roll1 + crit1 + bonus >= targetAC){\n\t\t\tmessage += (name + ' Hits on the First attack with a ' + (roll1 + bonus));\n\t\t\tdamage += weaponDamage;\n\t\t}else{\n\t\t\tmessage += (name + ' Misses the First attack with a ' + (roll1 + bonus));\n\t\t\tdamage += '0';\n\t\t}\n\t\tif(crit1 === 10){\n\t\t\tmessage += '[Natural 20]';\n\t\t}else if(crit1 === -10){\n\t\t\tmessage += '[Natural 1]';\n\t\t}else{\n\t\t\tmessage += \"[\" + roll1 + \"+\" + bonus + \"]\";\n\t\t}\t\n\t\t\n\t\tif(roll2 + crit2 + bonus - map >= targetAC+10){\n\t\t\tmessage += (' and Crits on the Second attack with a ' + (roll2 + bonus - map));\n\t\t\tdamage += '+'+weaponDamage+'*2';\n\t\t\tweaponTraits.forEach(element => {\n\t\t\t\tif(element.includes(\"deadly\")){\n\t\t\t\t\tvar deadly = element.split('-');\n\t\t\t\t\tdamage += \"+\" + deadlyDamage + deadly[1];\n\t\t\t\t}\n\t\t\t});\n\t\t}else if(roll2 + crit2 + bonus - map >= targetAC){\n\t\t\tmessage += (' and Hits on the Second attack with a ' + (roll2 + bonus - map));\n\t\t\tdamage += \"+\" + weaponDamage;\n\t\t}else{\n\t\t\tmessage += (' and Misses the Second attack with a ' + (roll2 + bonus - map));\n\t\t\tdamage += '+0';\n\t\t}\n\t\tif(crit2 === 10){\n\t\t\tmessage += '[Natural 20]';\n\t\t}else if(crit2 === -10){\n\t\t\tmessage += '[Natural 1]';\n\t\t}else{\n\t\t\tmessage += \"[\" + roll2 + \"+\" + bonus + \"-\" + map + \"]\";\n\t\t}\t\n\t}\n\t\n\t//display message and Rolls\n\ttoChat(message, damage);\n}\n\n//Determines if the Actor selected as the user has the requisite feat to use the ability, returns true if it does and false if it does not\nfunction CheckFeat() {\n\tvar items = token.actor.data.items;\n\tvar hasFeat = false;\n\n\tfor(var i = 0; i < items.length; i++){\n\t\tif(items[i].name === \"Hunted Shot\"){\n\t\t\t\thasFeat = true;\n\t\t\t\treturn true;\n\t\t}\n\t}\n\t\n\tif(!hasFeat){\n\t\tui.notifications.error('This Creature does not have the Feat required to use this ability');\n\t\treturn false;\n\t}\n}\n\n//Creates and returns an array of all items that can be used for Hunted Shot (Ranged weapons with Reload 0)\nfunction GetEligableItems(inv){\n\tvar EligableItems = [];\n\tvar f = 0;\n\t\n\tfor(var i = 0; i < inv.length; i++){\n\t\t\n\t\tif(inv[i].data.range && inv[i].data.range.value > 10 && inv[i].data.reload && (inv[i].data.reload.value === \"\" || inv[i].data.reload.value === \"0\")){\n\t\t\tEligableItems[f] = inv[i];\n\t\t\tf++;\n\t\t}\n\t}\n\t\n\treturn EligableItems;\n}\n\n//Actually executes most of the script\n//In charge of Creating a dialouge for the user to select a weapon to use, setting attack variables to what they should be based on the selected weapon, and calling the RollAttacks function\n//Should probably clean up this entire function but eh\nfunction SelectWeapon(){\n\t\n\t//if there is more than 1 item that is viable to be used, create a dialouge for the user to select which one to use\n\tif(weapons.length > 1){\n\t\tvar options = '';\n\t\t\n\t\t//automatically propegates the selection menu for the dialouge with the available weapons\n\t\tfor(var i = 0; i < weapons.length; i++){\n\t\t\toptions += \"\";\n\t\t}\n\t\t\n\t\t//Dialouge control\n\t\tlet applyChanges = false;\n\t\tnew Dialog({\n\t\t title: \"Weapon Selection\",\n\t\t content: `\n\t\t\t
Please select the weapon you are using for these attacks
\n\t\t\t
\n\t\t\t
\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\n\t\t\t
\n\t\t\t
\n\t\t\t`,\n\t\t buttons: {\n\t\t\tyes: {\n\t\t\t icon: \"\",\n\t\t\t label: `Select Weapon`,\n\t\t\t callback: () => applyChanges = true\n\t\t\t},\n\t\t\tno: {\n\t\t\t icon: \"\",\n\t\t\t label: `Cancel`\n\t\t\t},\n\t\t },\n\t\t default: \"yes\",\n\t\t close: html => {\n\t\t\tif (applyChanges) {\n\t\t\t for ( let token of canvas.tokens.controlled ) {\n\n\t\t\t\t//begin spaghetti code\n\t\t\t\t//set the selected weapon to that chosen by the dialouge\n\t\t\t\tvar sel = html.find('[name=\"Weapon-List\"]')[0].value || 0;\n\t\t\t\tvar selectedWeapon = weapons[sel];\n\n\t\t\t\tfor(var i = 0; i < selectedWeapon.data.traits.value.length; i++){\n\t\t\t\t\t\t\t\t\tweaponTraits[i] = selectedWeapon.data.traits.value[i];\n\t\t\t\t\t\t\t\t}\n\t\t\t\t//set the number of dice for the attack roll equal to the number set in the items data sheet\n\t\t\t\tvar diceNumber = selectedWeapon.data.damage.dice;\n\t\t\t\tif(selectedWeapon.data.strikingRune.value != \"\"){\n\t\t\t\t\t//modify the number of dice based on the weapons striking rune\n\t\t\t\t\tswitch(selectedWeapon.data.strikingRune.value){\n\t\t\t\t\t\tcase \"striking\": diceNumber += 1; deadlyDamage = 1;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"greaterStriking\": diceNumber += 2; deadlyDamage = 2;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase \"majorStriking\": diceNumber += 3; deadlyDamage = 3;\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\t//set the weaponDamage string equal to the diceNumber plus the die size, format is \"XdY\" where X is the number of dice and Y is the size of the die such as d6\n\t\t\t\tweaponDamage = diceNumber + selectedWeapon.data.damage.die;\n\t\t\t\tweaponTraits.forEach(element => {\n\t\t\t\t\tif(element === \"propulsive\"){\n\t\t\t\t\t\tweaponDamage += \"+\";\n\t\t\t\t\t\tif(token.actor.data.data.abilities.str.mod > 0){\n\t\t\t\t\t\t\tweaponDamage += Math.ceil(token.actor.data.data.abilities.str.mod/2);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\t//set the Multiple Attack Penalty equal to that found on the weapons data sheet\n\t\t\t\tmap = selectedWeapon.data.MAP.value;\n\t\t\t\t//determines the weapons attack modifier\n\t\t\t\tAttackMod = 0;\n\t\t\t\tif(selectedWeapon.data.ability.value === \"dex\"){\n\t\t\t\t\tAttackMod += token.actor.data.data.abilities.dex.mod;\n\t\t\t\t}else{\n\t\t\t\t\tAttackMod += token.actor.data.data.abilities.str.mod;\n\t\t\t\t}\n\t\t\t\tif(selectedWeapon.data.weaponType.value === \"martial\"){//Note: if your character has a differnt prficiency in specific weapon groups (like a fighter would) this script will not accurately calculate your proficiency\n\t\t\t\t\tAttackMod += token.actor.data.data.martial.martial.value;\n\t\t\t\t}else{\n\t\t\t\t\tAttackMod += token.actor.data.data.martial.simple.value;\n\t\t\t\t}\n\t\t\t\tif(selectedWeapon.data.potencyRune.value != \"\"){\n\t\t\t\t\tAttackMod += parseInt(selectedWeapon.data.potencyRune.value);\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tfor(var i = 0; i < selectedWeapon.data.traits.value.length; i++){\n\t\t\t\t\tweaponTraits[i] = selectedWeapon.data.traits.value[i];\n\t\t\t\t}\n\t\t\t\t//\n\t\t\t\tif(targetSelected){\n\t\t\t\t\ttargetAC = target.data.attributes.ac.value;\n\t\t\t\t}\n\t\t\t\tconsole.log(selectedWeapon)\n\t\t\t\tRollAttacks({targetAC: targetAC, bonus: AttackMod, name: token.actor.data.name, weapon: selectedWeapon});\n\n\t\t\t }\n\t\t\t}\n\t\t }\n\t\t}).render(true);\n\t}else{\n\n\t\t//same as the similar above section should probably make this a function\n\t\tvar selectedWeapon = weapons[0];\n\n\t\tvar diceNumber = selectedWeapon.data.damage.dice;\n\t\tif(selectedWeapon.data.strikingRune.value != \"\"){\n\n\t\t\tswitch(selectedWeapon.data.strikingRune.value){\n\t\t\t\tcase \"striking\": diceNumber += 1; \n\t\t\t\t\tbreak;\n\t\t\t\tcase \"greaterStriking\": diceNumber += 2;\n\t\t\t\t\tbreak;\n\t\t\t\tcase \"majorStriking\": diceNumber += 3; \n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\n\t\tweaponDamage = diceNumber + selectedWeapon.data.damage.die;\n\t\tmap = selectedWeapon.data.MAP.value;\n\t\tAttackMod = 0;\n\t\tif(selectedWeapon.data.ability.value === \"dex\"){\n\t\t\tAttackMod += token.actor.data.data.abilities.dex.mod;\n\t\t}else{\n\t\t\tAttackMod += token.actor.data.data.abilities.str.mod;\n\t\t}\n\t\tif(selectedWeapon.data.weaponType.value === \"martial\"){//Note: if your character has a differnt prficiency in specific weapon groups (like a fighter would) this script will not accurately calculate your proficiency\n\t\t\tAttackMod += token.actor.data.data.martial.martial.value;\n\t\t}else{\n\t\t\tAttackMod += token.actor.data.data.martial.simple.value;\n\t\t}\n\t\tif(selectedWeapon.data.potencyRune.value != \"\"){\n\t\t\tAttackMod += parseInt(selectedWeapon.data.potencyRune.value);\n\t\t}\n\t\t\t\t\n\t\tif(targetSelected){\n\t\t\ttargetAC = target.data.attributes.ac.value;\n\t\t}\n\t\tconsole.log(selectedWeapon)\n\t\t//Check if the user has the feat\n\t\tRollAttacks({targetAC: targetAC, bonus: AttackMod, name: token.actor.data.name, weapon: selectedWeapon});\n\t}\n}\n//-----------------Functions End----------------\n\n//if no token is selected, show an error\nif(!token){\n\tui.notifications.error(\"No token selected, please select the token that will use this ability\");\n}else{\n\t\n\t//Variable Declaration\n\tvar weaponDamage = '1d4'\n\tvar map = 5\n\tvar AttackMod = 0\n\tvar weaponTraits = [];\n\tvar deadlyDamage = 1;\n\t\n\tvar targetAC = 0;\n\t\n\tvar targetSelected = false;\n\tvar targetArray = Array.from(game.user.targets);\n\t\n\t//if no target selected show a info notification\n\tif(targetArray[0]){\n\t\tvar target = targetArray[0].actor.data;\n\t\ttargetSelected = true;\n\t}else{\n\t\tui.notifications.info(\"Tip: You can target another creature to automatically compare your attacks to its AC\");\n\t}\n\n\tvar weapons = GetEligableItems(token.actor.data.items);\n\t//check if the actor has any weapons that meet the requirements\n\tif(weapons[0]){\n\t\tif(CheckFeat()){\n\t\t\tSelectWeapon();\n\t\t}\n\t}else{\n\t\tui.notifications.error(\"This actor has no weapons which meet the requirements for this ability\");\n\t}\n}","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"Tg3LI0vO2htGscT2"} {"name":"Print Spell DC","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.3fcBbpvGrXEeesoo"}},"scope":"global","command":"//Spell DC: This will print to the chat the spell DC. \n //Replace the name with your character's Spellcasting Entry.\n\nlet name = 'Spontaneous Primal Spells';\n\nlet dc= (actor.data.items).find(item => item.name\n == name).data.spelldc.dc;\n\nlet string = 'DC is ' + dc;\n\nChatMessage.create({content: string, speaker: ChatMessage.getSpeaker({actor: actor})});","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"Txaxyuz3Qxm32Mvs"} -{"name":"Fury Instinct Rage","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.IYF41W576eV2slFl"}},"scope":"global","command":"const RAGE_DAMAGE = 2; // increase for giant instinct or higher levels\n\n(async () => {\n if (actor) {\n for (let token of canvas.tokens.controlled) {\n if (\n (token.actor.data.data.customModifiers[\"ac\"] || []).some(\n (modifier) => modifier.name === \"Rage\"\n )\n ) {\n await actor.removeCustomModifier(\"ac\", \"Rage\");\n await actor.removeCustomModifier(\"damage\", \"Rage\");\n /// Remove the line below if you do not wish for your character to lose all temp hp when toggled \"off\".\n await actor.update({ \"data.attributes.hp.temp\": 0 });\n /// Remove the line above if you do not wish for your character to lose all temp hp when toggled \"off\".\n await actor.update({ \"data.attributes.speed.value\": 25 }); \n if (\n token.data.effects.includes(\n \"https://i.imgur.com/VFOwIY0.png\"\n )\n ) {\n token.toggleEffect(\"https://i.imgur.com/VFOwIY0.png\");\n }\n } else {\n const tmpHP =\n token.actor.data.data.details.level.value +\n token.actor.data.data.abilities.con.mod;\n if (token.actor.data.data.attributes.hp.temp < tmpHP) {\n await actor.update({ \"data.attributes.hp.temp\": tmpHP });\n }\n await actor.addCustomModifier(\"ac\", \"Rage\", -1, \"untyped\");\n await actor.update({ \"data.attributes.speed.value\": 35 }); \n await actor.addCustomModifier(\"damage\", \"Rage\", RAGE_DAMAGE, \"status\");\n if (\n !token.data.effects.includes(\n \"https://i.imgur.com/VFOwIY0.png\"\n )\n ) {\n token.toggleEffect(\"https://i.imgur.com/VFOwIY0.png\");\n }\n }\n }\n } else {\n ui.notifications.warn(\"You must have an actor selected.\");\n }\n})();","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"UYnLO4PslykOyhl9"} -{"_id":"VgRiPGG3EPKIwY6M","name":"Double Slice","type":"script","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","scope":"global","command":"// Heavily Refactored by willvk, based on macro by Drental, u/Griff4218 \n\n// ------------------ damage output ------------------\nconst DoubleSliceDamage = (roll, strike, dos) => {\n\tdos.value = roll.data.degreeOfSuccess;\n\n if(roll.data.degreeOfSuccess === 2) {\n strike.damage({event: event});\n }\n if(roll.data.degreeOfSuccess === 3) {\n strike.critical({event: event});\n }\n\tpusher(dos);\n}\n\n// ------------------- result q ------------------------\nfunction pusher(dos){\n\tresultQ.push(dos);\n\tif (resultQ.length == 2) {\n\t\tresulter();\n\t}\n}\t\n\t\n// ------------------- check for followups -------------\nfunction resulter(){\n\n\tif(CheckFeat('dual-onslaught', false)) {\n\t\tif (Math.max(resultQ[0].value, resultQ[1].value) === 1) {\n\t\t\tgetContent('dual-onslaught');\n\t\t}\n\t}\n\tif(CheckFeat('flensing-slice', false)) {\n\t\tif (resultQ[0].value > 1 && resultQ[1].value > 1) {\n\t\t\tgetContent('flensing-slice');\n\t\t}\n\t}\n}\n\n// ------------------ hide the filthy chat card ---------\nfunction getContent(slug){\n\tlet stuff = token.actor.items.find(i => i.data.data.slug === slug && i.type === 'feat').data\n\tlet content = \n\t`
\n\t

${stuff.name}

${stuff.data.description.value}`\n\n\tlet chatData = {\n\t\tuser: game.user.id,\n\t\tcontent,\n\t\tspeaker: ChatMessage.getSpeaker(),\n\t}\n\tChatMessage.create(chatData, {})\n}\n\t\n// ------------------ hit calculation ------------------\nasync function DoubleSliceStrike(weapon1, weapon2) {\n let targetAC = 0;\n var strike1 = actor.data.data.actions.filter(a => a.type === 'strike').find(b => b.item.data._id === weapon1.data._id);\n var strike2 = actor.data.data.actions.filter(a => a.type === 'strike').find(b => b.item.data._id === weapon2.data._id);\n if(targetSelected) {\n targetAC = target.data.attributes.ac.value;\n }\n var useAgile = (strike1.traits.find(i => i.name === 'agile') || strike2.traits.find(i => i.name === 'agile'));\n\n var options = actor.getRollOptions(['all','attack']);\n\n const dc = {value: targetAC};\n\n var dosFirst = {value: 0};\n var dosSecond = {value: 0};\n\n if (useAgile) {\n if (targetSelected) {\n strike1.attack({event: event, options: options, callback: (roll) => {DoubleSliceDamage(roll, strike1, dosFirst)}, dc: dc});\n strike2.attack({event: event, options: options, callback: (roll) => {DoubleSliceDamage(roll, strike2, dosSecond)}, dc: dc});\n } else {\n strike1.attack({event: event, options: options});\n strike2.attack({event: event, options: options});\n }\n } else {\n\t\t// first strike normal\n\t\tif (targetSelected) {\n\t\t\tstrike1.attack({event: event, options: options, callback: (roll) => {DoubleSliceDamage(roll, strike1, dosFirst)}, dc: dc});\n\t\t} else {\n\t\t\tstrike1.attack({event: event, options: options});\n\t\t}\n\t\t// second strike with the negative modifier\n\t\tawait actor.addCustomModifier(\n\t\t\t\"attack\",\n\t\t\t\"Double Slice\",\n\t\t\t-2,\n\t\t\t\"untyped\"\n\t\t);\n\t\tstrike3 = actor.data.data.actions.filter(a => a.type === 'strike').find(b => b.item.data._id === weapon2.data._id)\n\t\tif (targetSelected) {\n\t\t\tstrike3.attack({event: event, options: options, callback: (roll) => {DoubleSliceDamage(roll, strike3, dosSecond)}, dc: dc});\n\t\t} else {\n\t\t\tstrike3.attack({event: event, options: options});\n\t\t}\n\t\tawait token.actor.removeCustomModifier('attack', 'double-slice');\n\t}\n}\n\n// ------------------ sanity check ------------------\nconst DoubleSliceCheck = ($html) => {\n var sel1 = parseInt($html.find('[name=\"Weapon-ListA\"]')[0].value) || 0;\n var sel2 = parseInt($html.find('[name=\"Weapon-ListB\"]')[0].value) || 0;\n if (sel1 !== sel2) {\n DoubleSliceStrike(weapons[sel1], weapons[sel2]);\n }\n}\n\n// ------------------ dialog function ------------------\n\n//Query all user choices\n//In charge of creating a dialog for the user to select the weapons to use\nfunction DoubleSliceUI() {\n //if there is more than 2 item that is viable to be used, create a dialouge for the user to select which ones to use\n if(weapons.length > 2){\n var options = '';\n \n // create selection entries for weapon selection\n weapons.forEach((value,key,map) => {\n options += \"\";\n });\n \n //Dialouge control\n let applyChanges = false;\n const dialogEditor = new Dialog({\n title: \"Weapon Selection\",\n content: `\n
Please select the weapons you are using for these attacks
\n
\n
\n
\n \n \n
\n
\n \n \n
\n
\n `,\n buttons: {\n yes: {\n icon: \"\",\n label: `Select Weapon`,\n callback: DoubleSliceCheck\n },\n no: {\n icon: \"\",\n label: `Cancel`\n },\n },\n default: \"yes\",\n })\n dialogEditor.render(true);\n }else{\n // it's exactly two weapons here\n DoubleSliceStrike(weapons[\"0\"], weapons[\"1\"]);\n }\n}\n\n// ------------------ helper functions ------------------\n\n// Determines if the Actor selected as the user has the requisite feat to use the ability, returns true if it does and false if it does not\nfunction CheckFeat(slug, required) {\n\n if(token.actor.items.find(i => i.data.data.slug === slug && i.type === 'feat')){\n return true;\n } else if (required) {\n ui.notifications.error('This Creature does not have the Feat required to use this ability');\n return false;\n }\n return false;\n}\n\n// Creates and returns an array of all items that can be used for Double Slice\nfunction GetWeapons(){\n let weapons\n weapons = actor.items.filter(i => i.type === 'weapon').filter(i => i.handsHeld === 1);\n return weapons;\n}\n\n// ------------------ execution ------------------\n\n//if no token is selected, show an error\nif(!token){\n ui.notifications.error(\"No token selected, please select the token that will use this ability\");\n}else{\n \n //Variable Declaration\n \n var targetSelected = false;\n var targetArray = Array.from(game.user.targets);\n\tvar resultQ = [];\n \n //if no target selected show a info notification\n if(targetArray[0]){\n var target = targetArray[0].actor.data;\n targetSelected = true;\n }else{\n ui.notifications.info(\"Tip: You can target another creature to automatically compare your attacks to its AC\");\n }\n\n var weapons = GetWeapons();\n //check if the actor has two melee weapons equipped\n if(weapons[1]){\n if(CheckFeat('double-slice', true)){\n DoubleSliceUI();\n }\n }else{\n ui.notifications.error(\"This actor has no weapons which meet the requirements for this ability\");\n }\n}","folder":null,"sort":0,"permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"flags":{"core":{"sourceId":"Macro.jJcpndozj4xNGJVF"}}} {"name":"Consume Bolt","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.nw7UyrE3hBq5zTcA"}},"scope":"global","command":"if (!actor) {\n ui.notifications.warn(\"You must select yourself.\");\n}\n\nlet updates = [];\nlet consumed = \"\";\n// Use Bolts\n(async () => {\nlet item = actor.items.find(i=> i.name===\"Bolts\");\n\nif(item === null) return\n\nif (item.data.data.quantity.value < 1) {\n\n ui.notifications.warn(`${game.user.name} not enough ${name} remaining`);\n} else {\n\n updates.push({\"_id\": item._id, \"data.quantity.value\": item.data.data.quantity.value - 1});\nconsumed += `${item.data.data.quantity.value - 1} arrows left
`;\n}\n\nif (updates.length > 0) {\n actor.updateEmbeddedEntity(\"OwnedItem\", updates);\n}\nChatMessage.create({\n user: game.user._id,\nspeaker: { actor: actor, alias: actor.name },\n content: consumed,\n type: CONST.CHAT_MESSAGE_TYPES.OTHER\n});\n })();","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"Yns0FUCSbfIu1xiZ"} {"name":"Repair Item","type":"script","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","scope":"global","command":"/**\n * Author: github.com/elizeuangelo\n */\n\nfunction checkDegreeOfSuccess(check, dc) {\n\t// Returns the degree of success of a check, according to PF2e rules\n\tconst diff = check - dc;\n\tlet res = 0;\n\tif (diff > -10) res++;\n\tif (diff > -1) res++;\n\tif (diff > 9) res++;\n\treturn res;\n}\nfunction rollRepair(dc, assurance) {\n\t// Rolls the repair test and returns the result\n\tif (assurance) {\n\t\tconst prof = actor.data.data.skills.cra.modifiers.find((mod) => mod.type === 'proficiency');\n\t\tconst total = 10 + prof.modifier;\n\t\treturn { total, data: { degreeOfSuccess: checkDegreeOfSuccess(total, dc) } };\n\t}\n\tconst options = actor.getRollOptions(['all', 'skill-check', 'craft']);\n\treturn new Promise((resolve) => {\n\t\tactor.data.data.skills.cra.roll({\n\t\t\tdc: { value: dc },\n\t\t\tevent,\n\t\t\toptions,\n\t\t\tcallback: (roll) => resolve(roll),\n\t\t});\n\t\tsetTimeout(() => resolve(undefined), 30000);\n\t});\n}\nfunction getRollTooltip(roll) {\n\t// Returns a message's roll tooltip\n\treturn `\n ${roll.total}`;\n}\nfunction note(msg) {\n\t// Returns a note styled message\n\treturn `

${msg}

`;\n}\n// If no token is selected, notify and return\nif (!token) {\n\tui.notifications.warn('No token is selected');\n\treturn;\n}\nlet repairKits = false,\n\treparableItems = [],\n\tassurance = false;\n// Gathers all the actor items and feats only once\nactor.items.forEach((i) => {\n\tif (i.data.data.hp?.value && i.data.data.hp.value < i.data.data.maxHp?.value) {\n\t\treparableItems.push(i);\n\t}\n\tif (/repair kit/i.exec(i.name)) {\n\t\trepairKits = true;\n\t}\n\tif (i.type === 'feat' && i.slug === 'assurance-crafting') {\n\t\tassurance = true;\n\t}\n});\n// If there are no Repair Kits in inventory, notify and return\nif (!repairKits) {\n\tui.notifications.warn('There are no repair kits on the selected token');\n\treturn;\n}\n// Sort the Reparable Items by descending order according to the damage (max - current hp)\nreparableItems = reparableItems.sort((a, b) => b.data.data.maxHp.value - b.data.data.hp.value - (a.data.data.maxHp.value - a.data.data.hp.value));\n// Adds a Generic Item Option\nreparableItems.push({ name: 'Any Item', type: 'generic', data: { data: { hp: { value: NaN }, maxHp: { value: NaN } } } });\nconst heal = 5 * (1 + actor.data.data.skills.cra.rank);\nconst itemLevels = [];\nfor (let i = 0; i < 21; i++) {\n\titemLevels.push(~~((i + 1) * 1.33) + 13);\n}\n// Creates the dropdown menus selection\nconst itemNames = reparableItems.map((i) => i.name + ` (${i.data.data.hp.value || '??'}/${i.data.data.maxHp.value || '??'})`);\nconst html = `\n
\n
\n Select the repair kit you are using, the item you repairing and a target DC.\n Destroyed items (0 HP) cant be repaired and are not shown in the list.\n
\n
\n

A success repairs ${heal} HP (${heal * 2} on a critical).

\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n`;\nconst dialog = new Dialog(\n\t{\n\t\ttitle: 'Repair Item',\n\t\tcontent: Handlebars.compile(html)({ itemLevels, itemNames, assurance: !assurance }),\n\t\tbuttons: {\n\t\t\tyes: {\n\t\t\t\ticon: ``,\n\t\t\t\tlabel: 'Repair',\n\t\t\t\tcallback: async (html) => {\n\t\t\t\t\t// First get the data\n\t\t\t\t\tconst item = reparableItems[html.querySelector('#item').value];\n\t\t\t\t\tconst dcLevel = html.querySelector('#dc-level').value;\n\t\t\t\t\tconst modifier = html.querySelector('#modifier').value;\n\t\t\t\t\tconst assurance = html.querySelector('#assurance').checked;\n\t\t\t\t\tconst dc = itemLevels[dcLevel] + +modifier;\n\t\t\t\t\t// Roll\n\t\t\t\t\tconst result = await rollRepair(dc, assurance);\n\t\t\t\t\tif (!result) return;\n\t\t\t\t\tconst success = result.data.degreeOfSuccess;\n\t\t\t\t\tconst successText = {\n\t\t\t\t\t\t0: 'Critical Failure',\n\t\t\t\t\t\t1: 'Failure',\n\t\t\t\t\t\t2: 'Success',\n\t\t\t\t\t\t3: 'Critical Success',\n\t\t\t\t\t};\n\t\t\t\t\t// Header\n\t\t\t\t\tconst content = [];\n\t\t\t\t\tconst tags = [`DC ${dc}`, `Item Level ${dcLevel}`, `${item.name} (${item.data.data.hp.value}/${item.data.data.maxHp.value})`];\n\t\t\t\t\tif (item.data.data.hardness?.value) tags.push(`Hardness ${item.data.data.hardness.value}`);\n\t\t\t\t\tif (assurance) tags.push(`Assurance ${result.total}`);\n\t\t\t\t\tcontent.push(\n\t\t\t\t\t\t`F ${\n\t\t\t\t\t\t\tassurance ? 'Assured ' : ''\n\t\t\t\t\t\t}Repair

(${successText[success]})

${tags\n\t\t\t\t\t\t\t.map((b) => `${b}`)\n\t\t\t\t\t\t\t.join('')}

`\n\t\t\t\t\t);\n\t\t\t\t\t// Proceed the results\n\t\t\t\t\tlet newHp;\n\t\t\t\t\tif (!success) {\n\t\t\t\t\t\t// If it is a critical failure\n\t\t\t\t\t\tconst dmgRoll = Roll.create(`2d6 - ${item.data.data.hardness?.value || 0}`).roll();\n\t\t\t\t\t\tnewHp = Math.max(item.data.data.hp.value - dmgRoll.total, 0);\n\t\t\t\t\t\tconst rollTooltip = getRollTooltip(dmgRoll);\n\t\t\t\t\t\tif (dmgRoll.total > 0) {\n\t\t\t\t\t\t\t// If the item is damaged\n\t\t\t\t\t\t\tcontent.push(\n\t\t\t\t\t\t\t\tnote(\n\t\t\t\t\t\t\t\t\t`You fail miserably to repair ${item.name} (${newHp}/${item.data.data.maxHp.value}) and you accidentaly damage it for ${rollTooltip} damage.`\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t\tif (newHp === 0) {\n\t\t\t\t\t\t\t\tcontent.push(note(`${item.name} is destroyed.`));\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// If damage is 0\n\t\t\t\t\t\t\tcontent.push(\n\t\t\t\t\t\t\t\tnote(\n\t\t\t\t\t\t\t\t\t`You fail miserably to repair ${item.name} (${newHp}/${item.data.data.maxHp.value}) but the item resists taking damage ${rollTooltip}.`\n\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (success === 1) {\n\t\t\t\t\t\t// If its a failure\n\t\t\t\t\t\tcontent.push(note(`You fail to repair ${item.name} (${item.data.data.hp.value}/${item.data.data.maxHp.value}).`));\n\t\t\t\t\t} else {\n\t\t\t\t\t\t// If its a success or critical success\n\t\t\t\t\t\tconst repair = heal * (success - 1);\n\t\t\t\t\t\tnewHp = Math.min(item.data.data.hp.value + repair, item.data.data.maxHp.value);\n\t\t\t\t\t\tcontent.push(\n\t\t\t\t\t\t\tnote(`You repair ${item.name} for +${repair} HP (${newHp}/${item.data.data.maxHp.value}).`)\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tif (success === 3) content.push(note('Golden Hands'));\n\t\t\t\t\tif (item.type !== 'generic' && newHp !== undefined) item.update({ 'data.hp.value': newHp });\n\t\t\t\t\tChatMessage.create({ flavor: content.join('').replaceAll('NaN', '??'), speaker: { alias: actor.name } });\n\t\t\t\t},\n\t\t\t},\n\t\t\tno: {\n\t\t\t\ticon: ``,\n\t\t\t\tlabel: 'Cancel',\n\t\t\t},\n\t\t},\n\t\tdefault: 'yes',\n\t\trender: (html) => {\n\t\t\tconst dcLevel = html.querySelector('#dc-level');\n\t\t\thtml.querySelector('#item')?.addEventListener('change', (ev) => {\n\t\t\t\tconst item = reparableItems[+ev.target.value];\n\t\t\t\tif (item.type === 'generic') {\n\t\t\t\t\tdcLevel.disabled = false;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tdcLevel.disabled = true;\n\t\t\t\tdcLevel.value = item.data.data.level.value;\n\t\t\t});\n\t\t\tif (reparableItems[0].type !== 'generic') {\n\t\t\t\tdcLevel.disabled = true;\n\t\t\t\tdcLevel.value = reparableItems[0].data.data.level.value;\n\t\t\t}\n\t\t},\n\t},\n\t{ jQuery: false }\n);\ndialog.render(true);","folder":null,"sort":0,"permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"flags":{"core":{"sourceId":"Macro.RgSZ8j6sqEYmTNA4"}},"_id":"atc7EE1Z1WftfKGI"} {"name":"Apply Condition","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.YQONuFJApe7CdYns"}},"scope":"global","command":"// apply/raise/lower/remove conditions on multiple tokens at the same time (great for things like dirge of doom). Use with\n// selective-select to quickly select enemies/friendlies\n\nlet applyChanges = false;\nconst compendiumName=\"pf2e.conditionitems\";\nconst conditionCompendium = game.packs.get(compendiumName);\nlet conditionList=[];\n\nasync function getConditionList(){\n if(conditionList.length<=0){\n let compendiumEntries = await conditionCompendium.getContent();\n console.log(compendiumEntries);\n for(let count=0;count'+ list[count].Name + '';\n }\n console.log(optionlist);\n return optionlist;\n}\n\nasync function applyCondition(conditionId, applytype)\n{\n let conditionItem=conditionList[conditionId];\n const item = await fromUuid(conditionItem.UUID);\n for (const token of canvas.tokens.controlled) {\n let existing = token.actor.items.filter(i => i.type === item.type).find(e => e.name === item.name);\n if(applytype==\"subtractive\" && existing){ \n if(existing.data.data.value.isValued && existing.data.data.value.value>1){\n const update = duplicate(existing);\n update.data.value.value--;\n await token.actor.updateEmbeddedEntity('OwnedItem', update);\n }\n else{\n await token.actor.deleteOwnedItem(existing._id);\n }\n }\n else if(existing && applytype==\"additive\" && existing.data.data.value.isValued){\n const update = duplicate(existing);\n update.data.value.value++;\n await token.actor.updateEmbeddedEntity('OwnedItem', update);\n } else if(!existing && applytype==\"additive\") {\n const newCondition = PF2eConditionManager.getCondition(conditionItem.Name);\n newCondition.data.sources.hud = !0,\n await PF2eConditionManager.addConditionToToken(newCondition, token);\n }\n await PF2eConditionManager.processConditions(token);\n }\n}\n\nnew Dialog({\n title: `Apply Condition`,\n content: `\n
\n
\n \n
\n \n
\n \n \n
\n
\n
\n `,\n buttons: {\n yes: {\n icon: \"\",\n label: `Apply Changes`,\n callback: () => applyChanges = true\n },\n no: {\n icon: \"\",\n label: `Cancel Changes`\n },\n },\n default: \"yes\",\n close: html => {\n if (applyChanges) {\n var applytype=\"additive\";\n let conditionid = html.find('[name=\"conditionChoice\"]')[0].value;\n const radios=html.find('[name=\"applyType\"]');\n for(var count=0;count {\n if (actor) {\n for ( let token of canvas.tokens.controlled ) {\n let messageContent = '';\n if ((token.actor.data.data.customModifiers['attack'] || []).some(modifier => modifier.name === 'Premonition Of Avoidance')) {\n await token.actor.removeCustomModifier('attack', 'Premonition Of Avoidance');\n\n await actor.removeCustomModifier(\"fortitude\", \"Premonition Of Avoidance\");\n await actor.removeCustomModifier(\"reflex\", \"Premonition Of Avoidance\");\n await actor.removeCustomModifier(\"will\", \"Premonition Of Avoidance\");\n\n if (token.data.effects.includes(\"https://assets.forge-vtt.com/bazaar/systems/pf2e/assets/icons/equipment/adventuring-gear/hourglass.jpg\")) {\n await token.toggleEffect(\"https://assets.forge-vtt.com/bazaar/systems/pf2e/assets/icons/equipment/adventuring-gear/hourglass.jpg\")\n }\n\n messageContent = 'The blessing from Pharasma begins to fade...'\n \n } else {\n await token.actor.addCustomModifier('attack', 'Premonition Of Avoidance', 0, 'status');\n\n await actor.addCustomModifier(\"fortitude\", \"Premonition Of Avoidance\", +2, \"status\");\n await actor.addCustomModifier(\"reflex\", \"Premonition Of Avoidance\", +2, \"status\");\n await actor.addCustomModifier(\"will\", \"Premonition Of Avoidance\", +2, \"status\");\n\n if (!token.data.effects.includes(\"https://assets.forge-vtt.com/bazaar/systems/pf2e/assets/icons/equipment/adventuring-gear/hourglass.jpg\")) {\n await token.toggleEffect(\"https://assets.forge-vtt.com/bazaar/systems/pf2e/assets/icons/equipment/adventuring-gear/hourglass.jpg\")\n }\n\n messageContent = 'Pharasma attempts to grant Bhelroth foresight!'\n };\n // create the message \n\n if (messageContent !== '') {\n let chatData = {\n user: game.user._id,\n speaker: ChatMessage.getSpeaker(),\n content: messageContent,\n };\n\n await ChatMessage.create(chatData, {});\n }\n }\n } else {\n ui.notifications.warn(\"You must have an actor selected.\");\n }\n})();","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"fIjZHJwTL9sEv1kh"} -{"name":"Specialty Crafting","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.IqzkESkQDD8IWMH9"}},"scope":"global","command":"(async () => {\n if (actor) {\n for ( let token of canvas.tokens.controlled ) {\n let messageContent = '';\n if ((token.actor.data.data.customModifiers['crafting'] || []).some(modifier => modifier.name === 'Specialty Crafting')) {\n await token.actor.removeCustomModifier('crafting', 'Specialty Crafting');\n\n if (token.data.effects.includes(\"systems/pf2e/icons/equipment/adventuring-gear/hammer.jpg\")) {\n await token.toggleEffect(\"systems/pf2e/icons/equipment/adventuring-gear/hammer.jpg\")\n }\n\n } else {\n await token.actor.addCustomModifier('crafting', 'Specialty Crafting', +1, 'status');\n\n if (!token.data.effects.includes(\"systems/pf2e/icons/equipment/adventuring-gear/hammer.jpg\")) {\n await token.toggleEffect(\"systems/pf2e/icons/equipment/adventuring-gear/hammer.jpg\")\n }\n\n };\n // create the message \n\n if (messageContent !== '') {\n let chatData = {\n user: game.user._id,\n speaker: ChatMessage.getSpeaker(),\n content: messageContent,\n };\n\n await ChatMessage.create(chatData, {});\n }\n }\n } else {\n ui.notifications.warn(\"You must have an actor selected.\");\n }\n})();","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"jTsWxD2i1IXjYEnG"} -{"name":"Double Slice AC Shown in Roll","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.kaB2oOu4Q5Gifiin"}},"scope":"global","command":"//Created by Drental, based on a macro by u/Griff4218 \n\n// ------------------ damage output ------------------\nconst DoubleSliceDamage = (roll, strike, dos) => {\n dos.value = roll.data.degreeOfSuccess;\n if(roll.data.degreeOfSuccess === 2) {\n strike.damage({event: event});\n }\n if(roll.data.degreeOfSuccess === 3) {\n strike.critical({event: event});\n }\n}\n\n// ------------------ hit calculation ------------------\nfunction DoubleSliceStrike(weapon1, weapon2) {\n let targetAC = 0;\n var strike1 = actor.data.data.actions.find(a => a.type === 'strike' && a.item === weapon1._id);\n var strike2 = actor.data.data.actions.find(a => a.type === 'strike' && a.item === weapon2._id);\n if(targetSelected) {\n targetAC = target.data.attributes.ac.value;\n }\n var useAgile = (strike1.traits.find(i => i.name === 'agile') || strike2.traits.find(i => i.name === 'agile'));\n\n var options = actor.getRollOptions(['all','attack']);\n\n const dc = {value: targetAC};\n\n var dosFirst = {value: 0};\n var dosSecond = {value: 0};\n\n if (useAgile) {\n if (targetSelected) {\n strike1.attack({event: event, options: options, callback: (roll) => {DoubleSliceDamage(roll, strike1, dosFirst)}, dc: dc});\n strike2.attack({event: event, options: options, callback: (roll) => {DoubleSliceDamage(roll, strike2, dosSecond)}, dc: dc});\n } else {\n strike1.attack({event: event, options: options});\n strike2.attack({event: event, options: options});\n }\n } else {\n (async () => {\n if (targetSelected) {\n strike1.attack({event: event, options: options, callback: (roll) => {DoubleSliceDamage(roll, strike1, dosFirst)}, dc: dc});\n } else {\n strike1.attack({event: event, options: options});\n }\n await actor.addCustomModifier(\n \"attack\",\n \"Double Slice\",\n -2,\n \"untyped\"\n );\n // get a new strike with the modifier\n if (targetSelected) {\n actor.data.data.actions.find(a => a.type === 'strike' && a.item === weapon2._id)\n .attack({event: event, options: options, callback: (roll) => {DoubleSliceDamage(roll, strike2, dosSecond)}, dc: dc});\n } else {\n actor.data.data.actions.find(a => a.type === 'strike' && a.item === weapon2._id)\n .attack({event: event, options: options});\n }\n await actor.removeCustomModifier(\n \"attack\",\n \"Double Slice\"\n );\n\n // apply special feats\n if(CheckFeat('dual-onslaught', false)) {\n if (Math.max(dosFirst.value, dosSecond.value) === 1) {\n let content = 'Dual Onslaught Damage'\n let chatData = {\n user: game.user.id,\n content,\n speaker: ChatMessage.getSpeaker(),\n }\n ChatMessage.create(chatData, {})\n strike1.damage({event: event})\n }\n\n }\n if(CheckFeat('flensing-slice', false)) {\n if (dosFirst.value > 1 && dosSecond.value > 1) {\n let content = 'Flensing Slice available'\n let chatData = {\n user: game.user.id,\n content,\n speaker: ChatMessage.getSpeaker(),\n }\n ChatMessage.create(chatData, {})\n }\n }\n })();\n }\n}\n\n// ------------------ sanity check ------------------\nconst DoubleSliceCheck = ($html) => {\n var sel1 = parseInt($html.find('[name=\"Weapon-ListA\"]')[0].value) || 0;\n var sel2 = parseInt($html.find('[name=\"Weapon-ListB\"]')[0].value) || 0;\n if (sel1 !== sel2) {\n DoubleSliceStrike(weapons[sel1], weapons[sel2]);\n }\n}\n\n// ------------------ dialog function ------------------\n\n//Query all user choices\n//In charge of creating a dialog for the user to select the weapons to use\nfunction DoubleSliceUI() {\n //if there is more than 2 item that is viable to be used, create a dialouge for the user to select which ones to use\n if(weapons.length > 2){\n var options = '';\n \n // create selection entries for weapon selection\n weapons.forEach((value,key,map) => {\n options += \"\";\n });\n \n //Dialouge control\n let applyChanges = false;\n const dialogEditor = new Dialog({\n title: \"Weapon Selection\",\n content: `\n
Please select the weapons you are using for these attacks
\n
\n
\n
\n \n \n
\n
\n \n \n
\n
\n `,\n buttons: {\n yes: {\n icon: \"\",\n label: `Select Weapon`,\n callback: DoubleSliceCheck\n },\n no: {\n icon: \"\",\n label: `Cancel`\n },\n },\n default: \"yes\",\n })\n dialogEditor.render(true);\n }else{\n // it's exactly two weapons here\n DoubleSliceStrike(weapons[\"0\"], weapons[\"1\"]);\n }\n}\n\n// ------------------ helper functions ------------------\n\n//Determines if the Actor selected as the user has the requisite feat to use the ability, returns true if it does and false if it does not\nfunction CheckFeat(slug, required) {\n\n if(token.actor.items.find(i => i.data.data.slug === slug && i.type === 'feat')){\n return true;\n } else if (required) {\n ui.notifications.error('This Creature does not have the Feat required to use this ability');\n return false;\n }\n return false;\n}\n\n//Creates and returns an array of all items that can be used for Hunted Shot (Ranged weapons with Reload 0)\nfunction GetWeapons(){\n let weapons\n \n weapons = actor.items.filter(i => i.type === 'weapon' \n && i.data.data.equipped.value === true);\n \n return weapons;\n}\n\n// ------------------ execution ------------------\n\n//if no token is selected, show an error\nif(!token){\n ui.notifications.error(\"No token selected, please select the token that will use this ability\");\n}else{\n \n //Variable Declaration\n \n var targetSelected = false;\n var targetArray = Array.from(game.user.targets);\n \n //if no target selected show a info notification\n if(targetArray[0]){\n var target = targetArray[0].actor.data;\n targetSelected = true;\n }else{\n ui.notifications.info(\"Tip: You can target another creature to automatically compare your attacks to its AC\");\n }\n\n var weapons = GetWeapons();\n //check if the actor has two melee weapons equipped\n if(weapons[1]){\n if(CheckFeat('double-slice', true)){\n DoubleSliceUI();\n }\n }else{\n ui.notifications.error(\"This actor has no weapons which meet the requirements for this ability\");\n }\n}","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"jYzTbIsPcNrFydVN"} {"name":"Oracular Curse","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.meAFcAcVjbvLawHq"}},"scope":"global","command":"/*\n * This macro is meant to aid Oracle (N)PCs in tracking their current curse level.\n * Clicking the macro with the token selected will increment the curse according to the following:\n * None -> Minor -> Overwhelmed if the Actor does not have the \"Major Curse\" feat,\n * None -> Minor -> Major -> Overwhelmed if the Actor does not have the \"Extreme Curse\" feat,\n * None -> Minor -> Major -> Extreme -> Overwhelmed otherwise.\n *\n * NOTE: you need to add paths to the icons representing the curse conditions, as this is the hacky way\n * in which this macro tracks the curse progression!\n *\n * ALSO NOTE: We try to extract the relevant pieces of information from the actor's Mystery feat. \n * This might not work, so if not, you might want to fill in the curse specifics yourself\n * to make things easier in the heat of battle.\n */\n\nlet messageContent = \"\";\nif (!actor) {\n ui.notifications.warn(\"You must have an actor selected.\");\n}\n\nconst FEAT_ORACLE_CURSE = \"Oracular Curse\";\nconst FEAT_ORACLE_CURSE_MAJOR = \"Major Curse\";\nconst FEAT_ORACLE_CURSE_EXTREME = \"Extreme Curse\";\n\nconst ICON_MINOR = \"/path/to/curse_minor.png\";\nconst ICON_MODERATE = \"/path/to/curse_moderate.png\";\nconst ICON_MAJOR = \"/path/to/curse_major.png\";\nconst ICON_EXTREME = \"/path/to/curse_extreme.png\";\nconst ICON_OVERWHELMED = \"/path/to/curse_overwhelmed.png\";\n\n// RegExps to extract relevant data from the actor's Mystery Feat.\nconst EXPR_MINOR = /(\\s*Minor\\s*Curse.+?)\\s*Moderate/is;\nconst EXPR_MODERATE = /(\\s*Moderate\\s*Curse.+?)\\s*Major/is;\nconst EXPR_MAJOR = /(\\s*Major\\s*Curse.+?)$/is;\n\nconst MSG_NONE = `You are no longer cursed.`;\nconst MSG_MINOR = `

Oracular Curse (Minor):


`;\nconst MSG_MODERATE = `

Oracular Curse (Moderate):


`;\nconst MSG_MAJOR = `

Oracular Curse (Major):


\nYou've learned to better balance the conflicting powers wreaking havoc on your body. Immediately after completing the casting of a revelation spell while you are affected by your moderate curse, your curse progresses to its major effect, rather than overwhelming you. This effect lasts until you Refocus, which reduces your curse to its minor effect. If you cast a revelation spell while under the effects of your major curse, you are overwhelmed by your curse.\n
\nIn addition, increase the number of Focus Points in your focus pool from 2 to 3. If you spend at least 2 Focus Points before you again Refocus, you recover 2 Focus Points when you Refocus instead of 1.\n
\n`;\n\nconst MSG_EXTREME = `

Oracular Curse (Extreme):


\nYou have mastered a perilous balance between the conflicting divine powers of your mystery, gaining the power to change your fate, but straining both body and soul. When you cast a revelation spell while affected by your major curse, your curse intensifies to an extreme effect instead of overwhelming you. All mysteries share the same effects for their extreme curse.\n
\nWhen affected by your extreme curse, you become Doomed 2 (or increase your doomed condition by 2 if you were already doomed). Once every 10 minutes, when you fail an attack roll, skill or Perception check, or saving throw, you can reroll it and use the second result.\n
\nThe reroll has the fortune trait and doesn't require you to spend an action, meaning you can use the reroll even if you can't act. These effects are in addition to all the effects of your major curse, and they can't be removed by any means until you Refocus to reduce your curse to its minor effect.\n
\nIf you cast a revelation spell while under the effects of this extreme curse, you are overwhelmed by your curse, and you remain doomed 2 even if you Refocus.\n
\nAdditionally, if you spend at least 3 Focus Points before you again Refocus, you recover 3 Focus Points when you Refocus instead of 1.\n`;\n\nconst MSG_OVERWHELMED = `

Oracular Curse (Overwhelmed):


\nDrawing upon your mystery\"s power while your curse is at its worst causes an irreconcilable conflict between you and the sources of your power. Immediately after casting a revelation spell while under the moderate effect of your curse, you are overwhelmed.\n
\nWhile overwhelmed, you can't Cast or Sustain any revelation spells — you effectively lose access to those spells. You can still Refocus to reduce the effects of your curse and regain a Focus Point, but doing so doesn\"t allow you to cast further revelation spells. These effects last until you rest for 8 hours and make your daily preparations, at which point your curse returns to its basic state. At higher levels, you can grow to withstand your curse\"s major and even extreme effects, enabling you to cast more revelation spells without becoming overwhelmed.\n
\nYour curse has the curse, divine, and necromancy traits. You can't mitigate, reduce, or remove the effects of your oracular curse by any means other than Refocusing and resting for 8 hours. For example, if your curse makes creatures concealed from you, you can't negate that concealed condition through a magic item or spell, such as true strike (though you would still benefit from the other effects of that item or spell). Likewise, remove curse and similar spells don\"t affect your curse at all.`;\n\n\n(async () => {\n for (const token of canvas.tokens.controlled) {\n // only allow curses for tokens having the requisite feat\n let actorCurseFeat = token.actor.items.find(i => i.name == FEAT_ORACLE_CURSE);\n let canDoCurse = actorCurseFeat !== null;\n if (!canDoCurse) {\n ui.notifications.warn(\"The selected token does not have the Oracular Curse feat!\");\n return;\n }\n\n // XXX: this will probably break horribly\n let actorMysteryFeat = token.actor.items.find(i => i.name.match(\"^.*Mystery\\s*$\"));\n if (actorMysteryFeat === null) {\n ui.notifications.warn(\"The selected token's actor does not have a mystery feat!\");\n return;\n }\n\n let actorMajorFeat = token.actor.items.find(i => i.name == FEAT_ORACLE_CURSE_MAJOR);\n let actorExtremeFeat = token.actor.items.find(i => i.name == FEAT_ORACLE_CURSE_EXTREME);\n let canProgressToMajor = actorMajorFeat !== null;\n let canProgressToExtreme = actorExtremeFeat !== null;\n\n\n let isOverwhelmed = token.data.effects.includes(ICON_OVERWHELMED);\n let hasExtremeCurse = token.data.effects.includes(ICON_EXTREME);\n let hasMajorCurse = token.data.effects.includes(ICON_MAJOR);\n let hasModerateCurse = token.data.effects.includes(ICON_MODERATE);\n let hasMinorCurse = token.data.effects.includes(ICON_MINOR);\n\n // Overwhelmed -> None\n if (isOverwhelmed) {\n messageContent = MSG_NONE;\n await token.toggleEffect(ICON_OVERWHELMED);\n return;\n }\n \n // Extreme -> Overwhelmed\n if (hasExtremeCurse) {\n messageContent = MSG_OVERWHELMED;\n await token.toggleEffect(ICON_OVERWHELMED);\n await token.toggleEffect(ICON_EXTREME);\n return;\n }\n\n // Major -> (Extreme | Overwhelmed)\n if (hasMajorCurse) {\n if (canProgressToExtreme) {\n messageContent = MSG_EXTREME;\n await token.toggleEffect(ICON_MAJOR);\n await token.toggleEffect(ICON_EXTREME);\n return;\n }\n\n // else: straight to overwhelmed\n messageContent = MSG_OVERWHELMED;\n await token.toggleEffect(ICON_OVERWHELMED);\n await token.toggleEffect(ICON_MAJOR);\n return;\n }\n\n\t// Moderate -> (Major | Overwhelmed)\n if (hasModerateCurse) {\n if (canProgressToMajor) {\n messageContent = MSG_MAJOR;\n messageContent += actorMysteryFeat.data.data.description.value.match(EXPR_MAJOR)[1];\n await token.toggleEffect(ICON_MODERATE);\n await token.toggleEffect(ICON_MAJOR);\n return;\n }\n\n // else: straight to overwhelmed\n messageContent = MSG_OVERWHELMED;\n await token.toggleEffect(ICON_MODERATE);\n await token.toggleEffect(ICON_OVERWHELMED);\n return;\n }\n\n\t// Minor -> Moderate\n if (hasMinorCurse) {\n messageContent = MSG_MODERATE;\n messageContent += actorMysteryFeat.data.data.description.value.match(EXPR_MODERATE)[1];\n await token.toggleEffect(ICON_MINOR);\n await token.toggleEffect(ICON_MODERATE);\n return;\n }\n\n // None -> Minor\n messageContent = MSG_MINOR;\n messageContent += actorMysteryFeat.data.data.description.value.match(EXPR_MINOR)[1];\n await token.toggleEffect(ICON_MINOR);\n return;\n }\n})();\n\n// create the message\nif (messageContent !== \"\") {\n let chatData = {\n user: game.user._id,\n speaker: ChatMessage.getSpeaker(),\n content: messageContent,\n };\n ChatMessage.create(chatData, {});\n}","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"llmOKZjt1bLxwnAA"} -{"name":"Change Initiative Skill","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.QMlgEpcmf1KjOkL8"}},"scope":"global","command":"// changes the default initiative skill for the selected tokens. Will even change monsters/NPCs even though the UI does not support this.\n// Useful when staging an ambush for example.\n// If the creature has an inherent bonus to initiative on top of their skill this will not be accounted for.\n\nasync function setInitSkill(skillname)\n{\n canvas.tokens.controlled.forEach(async function(changetoken){\n if(changetoken.actor.data.type!=\"character\"){\n let skillval=changetoken.actor.data.data.skills[skillname].totalModifier;\n await changetoken.actor.update({\n 'data.attributes.initiative.ability':skillname,\n 'data.attributes.initiative.totalModifier':skillval\n }); \n }else{\n await changetoken.actor.update({\n 'data.attributes.initiative.ability':skillname\n });\n }\n });\n}\n\nlet applyChanges=false;\nnew Dialog({\n title: `Set Initiative Skill`,\n content: `\n
\n
\n \n
\n
\n `,\n buttons: {\n yes: {\n icon: \"\",\n label: `Apply Changes`,\n callback: () => applyChanges = true\n },\n no: {\n icon: \"\",\n label: `Cancel Changes`\n },\n },\n default: \"yes\",\n close: html => {\n if (applyChanges) {\n let skillchoice = html.find('[name=\"init-skill\"]')[0].value || \"perception\";\n setInitSkill(skillchoice);\n }\n }\n}).render(true);","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"mRfgTfnbrbuiobL9"} -{"name":"Rain of Embers Stance","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.ZXkDhtGmndEkKKcq"}},"scope":"global","command":"(async () => {\n if (actor) {\n for (let token of canvas.tokens.controlled) {\n if (\n (token.actor.data.data.customModifiers[\"ac\"] || []).some(\n (modifier) => modifier.name === \"Embers Stance\"\n )\n ) {\n await actor.removeCustomModifier(\"ac\", \"Embers Stance\");\n if (\n token.data.effects.includes(\n \"systems/pf2e/icons/spells/fire-shield.jpg\"\n )\n ) {\n token.toggleEffect(\"systems/pf2e/icons/spells/fire-shield.jpg\");\n }\n } else {\n await actor.addCustomModifier(\"ac\", \"Embers Stance\", 1, \"status\");\n if (\n !token.data.effects.includes(\n \"systems/pf2e/icons/spells/fire-shield.jpg\"\n )\n ) {\n token.toggleEffect(\"systems/pf2e/icons/spells/fire-shield.jpg\");\n }\n }\n }\n } else {\n ui.notifications.warn(\"You must have an actor selected.\");\n }\n})();","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"mveb5kExwyh50hWf"} -{"name":"Inspire Courage","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.aEikSH6kV1ojHSt8"}},"scope":"global","command":"(async () => {\n if (actor) {\n for ( let token of canvas.tokens.controlled ) {\n let messageContent = '';\n if ((token.actor.data.data.customModifiers['attack'] || []).some(modifier => modifier.name === 'Inspire Courage')) {\n await token.actor.removeCustomModifier('attack', 'Inspire Courage');\n await token.actor.removeCustomModifier('damage', 'Inspire Courage');\n\n if (token.data.effects.includes(\"systems/pf2e/icons/conditions-2/status_hero.png\")) {\n await token.toggleEffect(\"systems/pf2e/icons/conditions-2/status_hero.png\")\n }\n\n messageContent = 'Is no longer Inspired.'\n } else {\n await token.actor.addCustomModifier('attack', 'Inspire Courage', 1, 'status');\n await token.actor.addCustomModifier('damage', 'Inspire Courage', 1, 'status');\n\n if (!token.data.effects.includes(\"systems/pf2e/icons/conditions-2/status_hero.png\")) {\n await token.toggleEffect(\"systems/pf2e/icons/conditions-2/status_hero.png\")\n }\n\n messageContent = 'Is Inspired!'\n };\n // create the message \n\n if (messageContent !== '') {\n let chatData = {\n user: game.user._id,\n speaker: ChatMessage.getSpeaker(),\n content: messageContent,\n };\n\n await ChatMessage.create(chatData, {});\n }\n }\n } else {\n ui.notifications.warn(\"You must have an actor selected.\");\n }\n})();","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"nqKdRbp0eyj5ltZR"} +{"_id":"mRfgTfnbrbuiobL9","name":"Change Initiative Skill","type":"script","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","scope":"global","command":"// changes the default initiative skill for the selected tokens. Will even change monsters/NPCs even though the UI does not support this.\n// Useful when staging an ambush for example.\n// If the creature has an inherent bonus to initiative on top of their skill this will not be accounted for.\n\nasync function setInitSkill(skillname)\n{\n canvas.tokens.controlled.forEach(async function(changetoken){\n if(changetoken.actor.data.type!=\"character\"){\n let skillval=skillname==\"perception\"? changetoken.actor.data.data.attributes.perception.totalModifier : changetoken.actor.data.data.skills[skillname].totalModifier;\n await changetoken.actor.update({\n 'data.attributes.initiative.ability':skillname,\n 'data.attributes.initiative.totalModifier':skillval\n }); \n }else{\n await changetoken.actor.update({\n 'data.attributes.initiative.ability':skillname\n });\n }\n });\n}\n\nlet applyChanges=false;\nnew Dialog({\n title: `Set Initiative Skill`,\n content: `\n
\n
\n \n
\n
\n `,\n buttons: {\n yes: {\n icon: \"\",\n label: `Apply Changes`,\n callback: () => applyChanges = true\n },\n no: {\n icon: \"\",\n label: `Cancel Changes`\n },\n },\n default: \"yes\",\n close: html => {\n if (applyChanges) {\n let skillchoice = html.find('[name=\"init-skill\"]')[0].value || \"perception\";\n setInitSkill(skillchoice);\n }\n }\n}).render(true);","folder":null,"sort":0,"permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"flags":{"core":{"sourceId":"Macro.QMlgEpcmf1KjOkL8"}}} {"name":"Action: Stand Up","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.ioe3RnGwbFGj0Tbr"}},"scope":"global","command":"ui.notifications.warn(`${token.name} stands up`); \n (async () => { \n if(actor.items.find(entry => (entry.name === \"Prone\"))){\n let prone = actor.items.find(entry => (entry.name === \"Prone\" && entry.type === \"condition\"));\n await PF2eConditionManager.removeConditionFromToken(prone._id, token)\n }\n \n if (actor.items.find(entry => (entry.name === \"Flat-Footed\"))){\n let flatfooted = actor.items.find(entry => (entry.name === \"Flat-Footed\" && entry.type === \"condition\"));\n await PF2eConditionManager.removeConditionFromToken(flatfooted._id, token)\n}\n })();","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"oK5WQxqISJBNRCLf"} {"name":"Take a Breather","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.4eYxlMwpg93wzz2x"}},"scope":"global","command":"let toChat = (content) => {\n let chatData = {\n user: game.user.id,\n content,\n speaker: ChatMessage.getSpeaker(),\n }\n ChatMessage.create(chatData, {})\n}\n\nlet applyChanges = false;\nnew Dialog({\n title: `Take a Breather`,\n content: `\n
Rest for 10 minutes, spend a resolve point, and regain stamina?
\n `,\n buttons: {\n yes: {\n icon: \"\",\n label: `Take a Breather`,\n callback: () => applyChanges = true\n },\n no: {\n icon: \"\",\n label: `Cancel`\n },\n },\n default: \"yes\",\n close: html => {\n if (applyChanges) {\n for ( let token of canvas.tokens.controlled ) {\n const {name} = token;\n console.log(token);\n const {resolve, sp} = token.actor.data.data.attributes;\n console.log(resolve, sp);\n if (resolve.value > 0) {\n let oldSP = sp.value;\n toChat(`${name} has ${sp.value}/${sp.max} SP and spends a resolve point, taking a 10 minute breather. Stamina Refreshed.`);\n token.actor.update({\n 'data.attributes.sp.value': sp.max,\n 'data.attributes.resolve.value': resolve.value-1\n });\n } else {\n toChat(`${name} is tired and needs to go to bed! No resolve points remaining.`);\n }\n }\n }\n }\n}).render(true);","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"r5HZVzkg1B01pohB"} {"name":"Consume Bullets","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.OAAMchW62vKz1Mmg"}},"scope":"global","command":"if (!actor) {\n ui.notifications.warn(\"You must select yourself.\");\n}\n\nlet updates = [];\nlet consumed = \"\";\n// Use Bullets\n(async () => {\nlet item = actor.items.find(i=> i.name===\"Bullets\");\n\nif(item === null) return\n\nif (item.data.data.quantity.value < 1) {\n\n ui.notifications.warn(`${game.user.name} not enough ${name} remaining`);\n} else {\n\n updates.push({\"_id\": item._id, \"data.quantity.value\": item.data.data.quantity.value - 1});\nconsumed += `${item.data.data.quantity.value - 1} bullets left
`;\n}\n\nif (updates.length > 0) {\n actor.updateEmbeddedEntity(\"OwnedItem\", updates);\n}\nChatMessage.create({\n user: game.user._id,\nspeaker: { actor: actor, alias: actor.name },\n content: consumed,\n type: CONST.CHAT_MESSAGE_TYPES.OTHER\n});\n })();","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"uRYKFL0mHpa1uczO"} -{"name":"Terrifying Resistance","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.EgGsbvaFhoitcJGK"}},"scope":"global","command":"(async () => {\n if (actor) {\n for ( let token of canvas.tokens.controlled ) {\n let messageContent = '';\n if ((token.actor.data.data.customModifiers['attack'] || []).some(modifier => modifier.name === 'Terrifying Resistance')) {\n await token.actor.removeCustomModifier('attack', 'Terrifying Resistance');\n\n await actor.removeCustomModifier(\"fortitude\", \"Terrifying Resistance\");\n await actor.removeCustomModifier(\"reflex\", \"Terrifying Resistance\");\n await actor.removeCustomModifier(\"will\", \"Terrifying Resistance\");\n\n if (token.data.effects.includes(\"systems/pf2e/icons/spells/dominate.jpg\")) {\n await token.toggleEffect(\"systems/pf2e/icons/spells/dominate.jpg\")\n }\n\n messageContent = 'The terrifying aura begins to fade...'\n \n } else {\n await token.actor.addCustomModifier('attack', 'Terrifying Resistance', 0, 'status');\n\n await actor.addCustomModifier(\"fortitude\", \"Terrifying Resistance\", +1, \"status\");\n await actor.addCustomModifier(\"reflex\", \"Terrifying Resistance\", +1, \"status\");\n await actor.addCustomModifier(\"will\", \"Terrifying Resistance\", +1, \"status\");\n\n if (!token.data.effects.includes(\"systems/pf2e/icons/spells/dominate.jpg\")) {\n await token.toggleEffect(\"systems/pf2e/icons/spells/dominate.jpg\")\n }\n\n messageContent = 'Your presence eminates an aura of terror!'\n };\n // create the message \n\n if (messageContent !== '') {\n let chatData = {\n user: game.user._id,\n speaker: ChatMessage.getSpeaker(),\n content: messageContent,\n };\n\n await ChatMessage.create(chatData, {});\n }\n }\n } else {\n ui.notifications.warn(\"You must have an actor selected.\");\n }\n})();","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"wGlMJV4UTNvJaFM9"} -{"name":"Sweep Attack","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.K8tR18XJD9rpplqe"}},"scope":"global","command":"(async () => {\n if (actor) {\n for ( let token of canvas.tokens.controlled ) {\n let messageContent = '';\n if ((token.actor.data.data.customModifiers['attack'] || []).some(modifier => modifier.name === 'Sweep Attack')) {\n await token.actor.removeCustomModifier('attack', 'Sweep Attack');\n\n if (token.data.effects.includes(\"systems/pf2e/icons/equipment/weapons/battle-axe.jpg\")) {\n await token.toggleEffect(\"systems/pf2e/icons/equipment/weapons/battle-axe.jpg\")\n }\n } else {\n await token.actor.addCustomModifier('attack', 'Sweep Attack', 1, 'status');\n\n if (!token.data.effects.includes(\"systems/pf2e/icons/equipment/weapons/battle-axe.jpg\")) {\n await token.toggleEffect(\"systems/pf2e/icons/equipment/weapons/battle-axe.jpg\")\n }\n\n messageContent = 'Readies to Strike a new foe!'\n };\n // create the message \n\n if (messageContent !== '') {\n let chatData = {\n user: game.user._id,\n speaker: ChatMessage.getSpeaker(),\n content: messageContent,\n };\n\n await ChatMessage.create(chatData, {});\n }\n }\n } else {\n ui.notifications.warn(\"You must have an actor selected.\");\n }\n})();","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"wIRkYp3pBLiMFNqJ"} {"_id":"wSgi32QJPlpLqnz3","name":"Fall Damage","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{"core":{"sourceId":"Macro.b5tyLRVraNOQ3h4d"}},"scope":"global","command":"When you fall more than 5 feet, you take bludgeoning damage equal to half the distance you fell when you land. If you take any damage from a fall, you land prone.\n\nie. 10 ft. is [[/roll 5]] Bludgeoning Damage\n\nFalling on a Creature:\n\nIf you land on a creature, that creature must attempt a DC 15 Reflex Save. \n\nCritical Success: The creature takes no damage.\n\nSuccess: The creature takes bludgeoning damage equal to one-quarter the falling damage you took.\n\nFailure: The creature takes bludgeoning damage equal to half the falling damage you took.\n\nCritical Failure: The creature takes the same amount of bludgeoning damage you took from the fall.","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} -{"name":"Parry","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"script","flags":{"core":{"sourceId":"Macro.2G9xNOv7e0qOVL3q"}},"scope":"global","command":"(async () => {\n if (actor) {\n for (let token of canvas.tokens.controlled) {\n if (\n (token.actor.data.data.customModifiers[\"ac\"] || []).some(\n (modifier) => modifier.name === \"Parry\"\n )\n ) {\n await actor.removeCustomModifier(\"ac\", \"Parry\");\n if (\n token.data.effects.includes(\n \"systems/pf2e/icons/equipment/weapons/main-gauche.png\"\n )\n ) {\n token.toggleEffect(\"systems/pf2e/icons/equipment/weapons/main-gauche.png\");\n }\n } else {\n await actor.addCustomModifier(\"ac\", \"Parry\", 1, \"circumstance\");\n if (\n !token.data.effects.includes(\n \"systems/pf2e/icons/spells/shield.jpg\"\n )\n ) {\n token.toggleEffect(\"systems/pf2e/icons/equipment/weapons/main-gauche.png\");\n }\n }\n }\n } else {\n ui.notifications.warn(\"You must have an actor selected.\");\n }\n})();","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"xbE7C9i6ycHE9xjS"} -{"$$deleted":true,"_id":"RMhZ3WdSToRNV5cU"} -{"$$deleted":true,"_id":"VgRiPGG3EPKIwY6M"} -{"$$deleted":true,"_id":"jYzTbIsPcNrFydVN"} -{"$$deleted":true,"_id":"6UmG5QdhAKHqcxIw"} -{"$$deleted":true,"_id":"NyeSPiR2vhfDZP08"} -{"$$deleted":true,"_id":"0GEgRnG5EZoSXYsV"} -{"$$deleted":true,"_id":"UYnLO4PslykOyhl9"} -{"$$deleted":true,"_id":"5qF7D6bCiY0Msq1k"} -{"$$deleted":true,"_id":"nqKdRbp0eyj5ltZR"} -{"$$deleted":true,"_id":"xbE7C9i6ycHE9xjS"} -{"$$deleted":true,"_id":"fIjZHJwTL9sEv1kh"} -{"$$deleted":true,"_id":"mveb5kExwyh50hWf"} -{"$$deleted":true,"_id":"jTsWxD2i1IXjYEnG"} -{"$$deleted":true,"_id":"wIRkYp3pBLiMFNqJ"} -{"$$deleted":true,"_id":"wGlMJV4UTNvJaFM9"}