diff --git a/packs/macros-actor.db b/packs/macros-actor.db index 0d6841a..6a6c2d7 100644 --- a/packs/macros-actor.db +++ b/packs/macros-actor.db @@ -5,3 +5,10 @@ {"name":"Rage (shawndibble)","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"// \tDISCLAIMER: This macro is a slightly modified version of the original masterwork written by Felix#6196.\n//\t\t\t\tOriginal version is on the wiki here: \n//\t\t\t\thttps://github.com/foundry-vtt-community/wiki/wiki/Script-Macros#rage-toggle-for-inhabited-character\n//\n// \tDifferences in my version (Norc#5108, 05/02/2020):\n//\n//\t\tBiggest change: Changed macro to work for selected token, NOT the user's official character (game.user.character)\n// \t \tThis eliminated an error I was getting as a GM that prevented me from using the script.\n//\n//\t\tOther changes: 1. Fixed Rage icon toggling, for me it was backwards.\n// \t \t2. Added error messages for trying to rage with no token or no barbarian selected\n//\t\t\t\t\t \t3. Added auto-bear totem detection, with a minor edit to character sheet required.\n\n\n//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n//!!!\tBonus Tip 1: !!!\n//!!!\tIf you chose the Spirit Seeker Primal path, and you chose the Bear totem spirit (resistance to all non-psychic damage). !!!\n//!!!\tin your 5E character sheet, edit the name of your Totem Spirit feature to EXACTLY \"Totem Spirit (Bear)\" instead. !!!\n//!!!\tThis allows you to automatically gain the extra Bear Totem Spirit resistances. !!!\n//!!! !!!\n//!!! \tBonus Tip 2: !!!\n//!!!\tIf you use the Combat Utility Belt module's Condition Lab, add a condition called Raging with the same icon as the optional rage !!!\n//!!!\ticon overlay, 'icons/svg/explosion.svg' by default. See OPTIONAL RAGE ICON section below. !!!\n//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n\t//declarations\n\tlet barb='';\n\tlet chatMsg='';\n\tlet bear = '';\n\tlet rageIconPath = '';\n\n//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n//!!! \tOPTIONAL RAGE ICON - Adds this icon to your character when raging only. Comment out following line to disable (add // before) !!!\n\n\t\trageIconPath = 'icons/svg/explosion.svg'\n\n//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n\n\n//check to see if Actor exists and is a barbarian\nif (actor !== undefined && actor !== null) {\n // get the barbarian class item\n barb = actor.items.find(i => i.name === 'Barbarian');\n if (barb == undefined) { \n ui.notifications.warn(\"Please select a single barbarian token.\");\n }\n \n\tif (barb !== undefined && barb !== null) {\n\t\tchatMsg = '';\n\t\tlet enabled = false;\n\t\t// store the state of the rage toggle in flags\n\t\tif (actor.data.flags.rageMacro !== null && actor.data.flags.rageMacro !== undefined) {\n\t\t\tenabled = true;\n\t\t}\n\t\t// if rage is active, disable it\n\t\tif (enabled) {\n\t\t\tchatMsg = `${actor.name} is no longer raging.`;\n\n\t\t\t// reset resistances\n\t\t\tlet obj = {};\n\t\t\tobj['flags.rageMacro'] = null;\n\t\t\tobj['data.traits.dr'] = actor.data.flags.rageMacro.oldResistances;\n\t\t\tactor.update(obj);\n\n\t\t\t// reset items\n\t\t\tfor (let item of actor.items) {\n\t\t\t\tif (item.data.flags.rageMacro !== null && item.data.flags.rageMacro !== undefined) {\n\t\t\t\t\t// restoring the old value from flags\n\t\t\t\t\tlet oldDmg = item.data.flags.rageMacro.oldDmg;\n\t\t\t\t\tlet obj = {};\n\t\t\t\t\tobj['data.damage.parts'] = oldDmg;\n\t\t\t\t\tobj['flags.rageMacro'] = null;\n\t\t\t\t\titem.update(obj);\n\t\t\t\t}\n\t\t\t}\n\n\n\t\t// if rage is disabled, enable it\n\t\t} else {\n\t\t\tchatMsg = `${actor.name} is RAAAAAGING!`;\n\n\t\t\t// update resistance\n\t\t\tlet obj = {};\n\t\t\t// storing old resistances in flags to restore later\n\t\t\tobj['flags.rageMacro.enabled'] = true;\n\t\t\tobj['flags.rageMacro.oldResistances'] = JSON.parse(JSON.stringify(actor.data.data.traits.dr));\n\n\t\t\t// add bludgeoning, piercing and slashing resistance\n\t\t\tlet newResistance = actor.data.data.traits.dr;\n\t\t\tif (newResistance.value.indexOf('bludgeoning') === -1) newResistance.value.push('bludgeoning');\n\t\t\tif (newResistance.value.indexOf('piercing') === -1) newResistance.value.push('piercing');\n\t\t\tif (newResistance.value.indexOf('slashing') === -1) newResistance.value.push('slashing');\n\n\n\t\t\t\n\t\t\t//If bear totem, add bear totem resistances.\n\t\t\tbear = actor.items.find(i => i.name === \"Totem Spirit (Bear)\")\n\t\t\tif (bear !== undefined) {\n\t\t\t\tif (newResistance.value.indexOf('acid') === -1) newResistance.value.push('acid');\n\t\t\t\tif (newResistance.value.indexOf('cold') === -1) newResistance.value.push('cold');\n\t\t\t\tif (newResistance.value.indexOf('fire') === -1) newResistance.value.push('fire');\n\t\t\t\tif (newResistance.value.indexOf('force') === -1) newResistance.value.push('force');\n\t\t\t\tif (newResistance.value.indexOf('lightning') === -1) newResistance.value.push('lightning');\n\t\t\t\tif (newResistance.value.indexOf('necrotic') === -1) newResistance.value.push('necrotic');\n\t\t\t\tif (newResistance.value.indexOf('poison') === -1) newResistance.value.push('poison');\n\t\t\t\tif (newResistance.value.indexOf('radiant') === -1) newResistance.value.push('radiant');\n\t\t\t\tif (newResistance.value.indexOf('thunder') === -1) newResistance.value.push('thunder');\n\t\t\t}\n\t\t\t\n\t\t\tobj['data.traits.dr'] = newResistance;\n\t\t\tactor.update(obj);\n\n\t\t\t// update items\n\t\t\t// determining the barbarian level\n\t\t\tlet barblvl = barb.data.data.levels;\n\t\t\t// the formula to determin the rage bonus damage depending on barbarian level\n\t\t\tlet ragedmg = 2 + Math.floor(barblvl / 9) - (barblvl === 8 ? 1 : 0);\n\t\t\tfor (let item of actor.items) {\n\t\t\t\tlet isMelee = getProperty(item, 'data.data.actionType') === 'mwak';\n\t\t\t\tif (isMelee && item.data.data.damage.parts.length > 0) {\n\t\t\t\t\tconsole.log('updating ' + item);\n\t\t\t\t\tlet obj = {};\n\t\t\t\t\tlet dmg = item.data.data.damage.parts;\n\t\t\t\t\tobj['flags.rageMacro.oldDmg'] = JSON.parse(JSON.stringify(dmg));\n\t\t\t\t\tdmg[0][0] = `${dmg[0][0]} + ${ragedmg}`;\n\t\t\t\t\tobj['data.damage.parts'] = dmg;\n\t\t\t\t\titem.update(obj);\n\t\t\t\t}\n\t\t\t}\t\n\t\t}\n\t// toggle rage icon\n\t// - this is optional and requires you to set the path for the token icon you want to use for rage\n\ttoken = canvas.tokens.controlled.find(t => t.actor.id === actor.id);\n\ttoken.toggleEffect(rageIconPath);\n\t}\n\n} else ui.notifications.warn(\"Please select a token.\");\n\n// write to chat if needed:\nif (chatMsg !== '') {\n\tlet chatData = {\n\t\tuser: game.user._id,\n\t\tspeaker: ChatMessage.getSpeaker(),\n\t\tcontent: chatMsg\n };\n\tChatMessage.create(chatData, {});\n}\n","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"hW29VmM09DenDXNz"} {"name":"Set Name and Bars (shawndibble)","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"// Update all tokens on the map so that the name shows on hover and the bars always show.\n// Display Modes: ALWAYS, CONTROL, HOVER, NONE, OWNER, OWNER_HOVER\n\nconst tokens = canvas.tokens.placeables.map(token => {\n return {\n _id: token.id,\n \"bar1.attribute\": \"attributes.hp\",\n \"bar2.attribute\": \"attributes.ac.value\",\n \"displayName\": CONST.TOKEN_DISPLAY_MODES.OWNER_HOVER,\n \"displayBars\": CONST.TOKEN_DISPLAY_MODES.OWNER\n };\n});\n\ncanvas.scene.updateEmbeddedEntity('Token', tokens)","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"mwiBoYVqZgJtyv9E"} {"name":"Link Actors (Reaver01)","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"const scene = game.scenes.active;\nconst unlinked = canvas.scene.data.tokens.map(t => {\n const actor = game.actors.entities.find(a => a.name === t.name);\n if (actor) {\n return {\n _id: t._id,\n actorId: actor.id\n }\n } else {\n console.log(t.name);\n return {\n _id: t._id,\n actorId: \"\"\n }\n }\n});\nconst updates = duplicate(unlinked);\n\nscene.updateEmbeddedEntity(\"Token\", updates);\n\nui.notifications.info('Tokens linked to actors.');\nconsole.log(updates);","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"p0KlDQ5Ab2IIsNJY"} +{"_id":"USJZMHU93aFklWYM","name":"Change Disposition","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"let applyChanges = false;\nnew Dialog({\n title: `Token Disposition Changer`,\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 for ( let token of canvas.tokens.controlled ) {\n let dispoType = html.find('[name=\"dispo-type\"]')[0].value || \"none\";\n switch (dispoType) {\n case \"hostile\":\n token.update({\"disposition\": -1});\n break;\n case \"friendly\":\n token.update({\"disposition\": 1});\n break;\n case \"neutral\":\n token.update({\"disposition\": 0});\n break;\n case \"nochange\":\n default:\n }\n }\n }\n }\n}).render(true);\n","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} +{"_id":"p0KlDQ5Ab2IIsNJY","name":"Link Actors","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"const scene = game.scenes.active;\nconst unlinked = canvas.scene.data.tokens.map(t => {\n const actor = game.actors.entities.find(a => a.name === t.name);\n if (actor) {\n return {\n _id: t._id,\n actorId: actor.id\n }\n } else {\n console.log(t.name);\n return {\n _id: t._id,\n actorId: \"\"\n }\n }\n});\nconst updates = duplicate(unlinked);\n\nscene.updateEmbeddedEntity(\"Token\", updates);\n\nui.notifications.info('Tokens linked to actors.');\nconsole.log(updates);","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} +{"_id":"hW29VmM09DenDXNz","name":"Rage","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"// \tDISCLAIMER: This macro is a slightly modified version of the original masterwork written by Felix#6196.\n//\t\t\t\tOriginal version is on the wiki here: \n//\t\t\t\thttps://github.com/foundry-vtt-community/wiki/wiki/Script-Macros#rage-toggle-for-inhabited-character\n//\n// \tDifferences in my version (Norc#5108, 05/02/2020):\n//\n//\t\tBiggest change: Changed macro to work for selected token, NOT the user's official character (game.user.character)\n// \t \tThis eliminated an error I was getting as a GM that prevented me from using the script.\n//\n//\t\tOther changes: 1. Fixed Rage icon toggling, for me it was backwards.\n// \t \t2. Added error messages for trying to rage with no token or no barbarian selected\n//\t\t\t\t\t \t3. Added auto-bear totem detection, with a minor edit to character sheet required.\n\n\n//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n//!!!\tBonus Tip 1: !!!\n//!!!\tIf you chose the Spirit Seeker Primal path, and you chose the Bear totem spirit (resistance to all non-psychic damage). !!!\n//!!!\tin your 5E character sheet, edit the name of your Totem Spirit feature to EXACTLY \"Totem Spirit (Bear)\" instead. !!!\n//!!!\tThis allows you to automatically gain the extra Bear Totem Spirit resistances. !!!\n//!!! !!!\n//!!! \tBonus Tip 2: !!!\n//!!!\tIf you use the Combat Utility Belt module's Condition Lab, add a condition called Raging with the same icon as the optional rage !!!\n//!!!\ticon overlay, 'icons/svg/explosion.svg' by default. See OPTIONAL RAGE ICON section below. !!!\n//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n\t//declarations\n\tlet barb='';\n\tlet chatMsg='';\n\tlet bear = '';\n\tlet rageIconPath = '';\n\n//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n//!!! \tOPTIONAL RAGE ICON - Adds this icon to your character when raging only. Comment out following line to disable (add // before) !!!\n\n\t\trageIconPath = 'icons/svg/explosion.svg'\n\n//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n\n\n\n//check to see if Actor exists and is a barbarian\nif (actor !== undefined && actor !== null) {\n // get the barbarian class item\n barb = actor.items.find(i => i.name === 'Barbarian');\n if (barb == undefined) { \n ui.notifications.warn(\"Please select a single barbarian token.\");\n }\n \n\tif (barb !== undefined && barb !== null) {\n\t\tchatMsg = '';\n\t\tlet enabled = false;\n\t\t// store the state of the rage toggle in flags\n\t\tif (actor.data.flags.rageMacro !== null && actor.data.flags.rageMacro !== undefined) {\n\t\t\tenabled = true;\n\t\t}\n\t\t// if rage is active, disable it\n\t\tif (enabled) {\n\t\t\tchatMsg = `${actor.name} is no longer raging.`;\n\n\t\t\t// reset resistances\n\t\t\tlet obj = {};\n\t\t\tobj['flags.rageMacro'] = null;\n\t\t\tobj['data.traits.dr'] = actor.data.flags.rageMacro.oldResistances;\n\t\t\tactor.update(obj);\n\n\t\t\t// reset items\n\t\t\tfor (let item of actor.items) {\n\t\t\t\tif (item.data.flags.rageMacro !== null && item.data.flags.rageMacro !== undefined) {\n\t\t\t\t\t// restoring the old value from flags\n\t\t\t\t\tlet oldDmg = item.data.flags.rageMacro.oldDmg;\n\t\t\t\t\tlet obj = {};\n\t\t\t\t\tobj['data.damage.parts'] = oldDmg;\n\t\t\t\t\tobj['flags.rageMacro'] = null;\n\t\t\t\t\titem.update(obj);\n\t\t\t\t}\n\t\t\t}\n\n\n\t\t// if rage is disabled, enable it\n\t\t} else {\n\t\t\tchatMsg = `${actor.name} is RAAAAAGING!`;\n\n\t\t\t// update resistance\n\t\t\tlet obj = {};\n\t\t\t// storing old resistances in flags to restore later\n\t\t\tobj['flags.rageMacro.enabled'] = true;\n\t\t\tobj['flags.rageMacro.oldResistances'] = JSON.parse(JSON.stringify(actor.data.data.traits.dr));\n\n\t\t\t// add bludgeoning, piercing and slashing resistance\n\t\t\tlet newResistance = actor.data.data.traits.dr;\n\t\t\tif (newResistance.value.indexOf('bludgeoning') === -1) newResistance.value.push('bludgeoning');\n\t\t\tif (newResistance.value.indexOf('piercing') === -1) newResistance.value.push('piercing');\n\t\t\tif (newResistance.value.indexOf('slashing') === -1) newResistance.value.push('slashing');\n\n\n\t\t\t\n\t\t\t//If bear totem, add bear totem resistances.\n\t\t\tbear = actor.items.find(i => i.name === \"Totem Spirit (Bear)\")\n\t\t\tif (bear !== undefined) {\n\t\t\t\tif (newResistance.value.indexOf('acid') === -1) newResistance.value.push('acid');\n\t\t\t\tif (newResistance.value.indexOf('cold') === -1) newResistance.value.push('cold');\n\t\t\t\tif (newResistance.value.indexOf('fire') === -1) newResistance.value.push('fire');\n\t\t\t\tif (newResistance.value.indexOf('force') === -1) newResistance.value.push('force');\n\t\t\t\tif (newResistance.value.indexOf('lightning') === -1) newResistance.value.push('lightning');\n\t\t\t\tif (newResistance.value.indexOf('necrotic') === -1) newResistance.value.push('necrotic');\n\t\t\t\tif (newResistance.value.indexOf('poison') === -1) newResistance.value.push('poison');\n\t\t\t\tif (newResistance.value.indexOf('radiant') === -1) newResistance.value.push('radiant');\n\t\t\t\tif (newResistance.value.indexOf('thunder') === -1) newResistance.value.push('thunder');\n\t\t\t}\n\t\t\t\n\t\t\tobj['data.traits.dr'] = newResistance;\n\t\t\tactor.update(obj);\n\n\t\t\t// update items\n\t\t\t// determining the barbarian level\n\t\t\tlet barblvl = barb.data.data.levels;\n\t\t\t// the formula to determin the rage bonus damage depending on barbarian level\n\t\t\tlet ragedmg = 2 + Math.floor(barblvl / 9) - (barblvl === 8 ? 1 : 0);\n\t\t\tfor (let item of actor.items) {\n\t\t\t\tlet isMelee = getProperty(item, 'data.data.actionType') === 'mwak';\n\t\t\t\tif (isMelee && item.data.data.damage.parts.length > 0) {\n\t\t\t\t\tconsole.log('updating ' + item);\n\t\t\t\t\tlet obj = {};\n\t\t\t\t\tlet dmg = item.data.data.damage.parts;\n\t\t\t\t\tobj['flags.rageMacro.oldDmg'] = JSON.parse(JSON.stringify(dmg));\n\t\t\t\t\tdmg[0][0] = `${dmg[0][0]} + ${ragedmg}`;\n\t\t\t\t\tobj['data.damage.parts'] = dmg;\n\t\t\t\t\titem.update(obj);\n\t\t\t\t}\n\t\t\t}\t\n\t\t}\n\t// toggle rage icon\n\t// - this is optional and requires you to set the path for the token icon you want to use for rage\n\ttoken = canvas.tokens.controlled.find(t => t.actor.id === actor.id);\n\ttoken.toggleEffect(rageIconPath);\n\t}\n\n} else ui.notifications.warn(\"Please select a token.\");\n\n// write to chat if needed:\nif (chatMsg !== '') {\n\tlet chatData = {\n\t\tuser: game.user._id,\n\t\tspeaker: ChatMessage.getSpeaker(),\n\t\tcontent: chatMsg\n };\n\tChatMessage.create(chatData, {});\n}\n","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} +{"_id":"ZvtYrzlv9dPpq9Pu","name":"Random Mockeries","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"// Courtesy of @Zarek\n// Selected target receives a random mockery from a table called \"Mockeries\" along with the DC and damage.\n\n// Setup variables\nlet tableName = \"Mockeries\";\nlet table = game.tables.entities.find(t => t.name == tableName);\nlet mockery = '';\n// Roll the result, and mark it drawn\nif (table)\n{\n let result = table.roll()[1];\n if (result === null)\n {\n table.reset();\n result = table.roll()[1];\n }\n mockery = result.text;\n markDrawn(table, result);\n}\n\nfunction markDrawn(table, result){\n for (let data of table.data.results){\n if (result._id == data._id)\n {\n //console.log(result.text)\n data.drawn = true;\n }\n } \n}\n\n// Get Targets name\nconst targetId = game.user.targets.ids[0];\nconst targetToken = canvas.tokens.get(targetId);\nconst targetName = targetToken.name;\n\n// Add a message with damage roll\nlet messageContent = '';\nlet result = new Roll(`1d4`).roll().total;\nmessageContent += `${targetName} Roll WIS save DC 14 or take ${result} damage.
`\nmessageContent += `Cas exclaims \"${mockery}\"`\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 //console.log(messageContent)","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} +{"_id":"cVkN7qB4m1mRauBE","name":"Remove Conditions","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"for ( let token of canvas.tokens.controlled ){\n await token.update({\"effects\": []});\n}","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} +{"_id":"mwiBoYVqZgJtyv9E","name":"Set Name and Bars","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"// Update all tokens on the map so that the name shows on hover and the bars always show.\n// Display Modes: ALWAYS, CONTROL, HOVER, NONE, OWNER, OWNER_HOVER\n\nconst tokens = canvas.tokens.placeables.map(token => {\n return {\n _id: token.id,\n \"bar1.attribute\": \"attributes.hp\",\n \"bar2.attribute\": \"attributes.ac.value\",\n \"displayName\": CONST.TOKEN_DISPLAY_MODES.OWNER_HOVER,\n \"displayBars\": CONST.TOKEN_DISPLAY_MODES.OWNER\n };\n});\n\ncanvas.scene.updateEmbeddedEntity('Token', tokens)","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} +{"_id":"V2N0DrQBO5uBc20J","name":"Shrink Or Enlarge","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"// Update selected tokens to flip between a 1x1 or a 2x2 grid.\n\nfor (let token of canvas.tokens.controlled) {\n let newSize = (token.data.height == 1 && token.data.width == 1) ? 2 : 1;\n token.update({\n height: newSize,\n width: newSize\n });\n};","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} diff --git a/packs/macros-item.db b/packs/macros-item.db index c2a1726..e56142e 100644 --- a/packs/macros-item.db +++ b/packs/macros-item.db @@ -1 +1,2 @@ {"name":"Tool Proficiency (shawndibble)","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"/**\n * Grab a list of tools in the selected player's inventory, then all the user to make a roll on the tool.\n * Will take into consideration if the player is proficient in using the tool.\n */\n\n// get the first entry from the array of currently selected tokens. Works best/exclusively with one selected token\nconst target = canvas.tokens.controlled[0].actor;\n// get the abilities of the selected token for ease of access later\nconst { abilities } = target.data.data;\n// Only items set as \"tools\" will be included!\n// get all held and equipped Tools/Kits/Supplies. Might want to replace with /[tT]ools|[kK]it|[sS]upplies|[sS]et$/ if gaming sets should be included\nconst toolsInInventory = target.items.filter( item => item.name.match(/[tT]ools|[kK]it|[sS]upplies$/) && item.data.data.hasOwnProperty(\"proficient\"));\n// const toolProficiencies = target.data.data.traits.toolProf; // Tools have proficiency mod in the object under .data.data.proficient. \nlet tool = undefined;\n\n// Choose ability mod dialog\nconst abilityDialog = (async () => {\n let template = `\n
\n
\n \n \n
\n
`\n\n\n new Dialog({\n title: tool.name,\n content: template,\n buttons: {\n ok: {\n icon: '',\n label: \"OK\",\n callback: async (html) => {\n const selection = html.find(\"#selectedAbility\")[0].value;\n console.log(tool, target);\n let prof = tool.data.data.proficient * target.data.data.attributes.prof; // target might be half or doubly proficient. This will make sure it is accounted for\n\n let messageContent = `${target.name} rolled a [[1d20+${abilities[selection].mod}(${abilities[selection].name})+${prof}(Proficiency)]] for the ${tool.name} check.
`;\n let chatData = {\n user: game.user.id,\n speaker: ChatMessage.getSpeaker(),\n content: messageContent,\n // uncomment the line below to always whisper the roll to the GM\n // whisper: game.users.entities.filter(u => u.isGM).map(u => u._id)\n };\n ChatMessage.create(chatData, {});\n }\n },\n cancel: {\n icon: '',\n label: 'Cancel'\n }\n },\n default: \"cancel\"\n }).render(true);\n})\n\n// Choose tool dialog\nif (toolsInInventory.length) {\n (async () => {\n let template = `\n
\n
\n \n \n
\n
`;\n\n new Dialog({\n title: 'Which tool?',\n content: template,\n buttons: {\n ok: {\n icon: '',\n label: \"OK\",\n callback: async (html) => {\n let selection = html.find(\"#selectedTool\")[0].value;\n tool = toolsInInventory.find( item => item.name === selection )\n abilityDialog();\n }\n },\n cancel: {\n icon: '',\n label: 'Cancel'\n }\n },\n default: \"cancel\"\n }).render(true);\n })() \n}\n\nelse {\n new Dialog({\n title: 'No Tools!',\n content: '

You don\\'t seem to have any tool with you.

',\n buttons: {\n ok: {\n icon: '',\n label: \"OK\"\n }\n },\n default: \"ok\"\n }).render(true);\n}","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"D38Cw70PsNaaQicv"} +{"_id":"D38Cw70PsNaaQicv","name":"Tool Proficiency","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"/**\n * Grab a list of tools in the selected player's inventory, then all the user to make a roll on the tool.\n * Will take into consideration if the player is proficient in using the tool.\n */\n\n// get the first entry from the array of currently selected tokens. Works best/exclusively with one selected token\nconst target = canvas.tokens.controlled[0].actor;\n// get the abilities of the selected token for ease of access later\nconst { abilities } = target.data.data;\n// Only items set as \"tools\" will be included!\n// get all held and equipped Tools/Kits/Supplies. Might want to replace with /[tT]ools|[kK]it|[sS]upplies|[sS]et$/ if gaming sets should be included\nconst toolsInInventory = target.items.filter( item => item.name.match(/[tT]ools|[kK]it|[sS]upplies$/) && item.data.data.hasOwnProperty(\"proficient\"));\n// const toolProficiencies = target.data.data.traits.toolProf; // Tools have proficiency mod in the object under .data.data.proficient. \nlet tool = undefined;\n\n// Choose ability mod dialog\nconst abilityDialog = (async () => {\n let template = `\n
\n
\n \n \n
\n
`\n\n\n new Dialog({\n title: tool.name,\n content: template,\n buttons: {\n ok: {\n icon: '',\n label: \"OK\",\n callback: async (html) => {\n const selection = html.find(\"#selectedAbility\")[0].value;\n console.log(tool, target);\n let prof = tool.data.data.proficient * target.data.data.attributes.prof; // target might be half or doubly proficient. This will make sure it is accounted for\n\n let messageContent = `${target.name} rolled a [[1d20+${abilities[selection].mod}(${abilities[selection].name})+${prof}(Proficiency)]] for the ${tool.name} check.
`;\n let chatData = {\n user: game.user.id,\n speaker: ChatMessage.getSpeaker(),\n content: messageContent,\n // uncomment the line below to always whisper the roll to the GM\n // whisper: game.users.entities.filter(u => u.isGM).map(u => u._id)\n };\n ChatMessage.create(chatData, {});\n }\n },\n cancel: {\n icon: '',\n label: 'Cancel'\n }\n },\n default: \"cancel\"\n }).render(true);\n})\n\n// Choose tool dialog\nif (toolsInInventory.length) {\n (async () => {\n let template = `\n
\n
\n \n \n
\n
`;\n\n new Dialog({\n title: 'Which tool?',\n content: template,\n buttons: {\n ok: {\n icon: '',\n label: \"OK\",\n callback: async (html) => {\n let selection = html.find(\"#selectedTool\")[0].value;\n tool = toolsInInventory.find( item => item.name === selection )\n abilityDialog();\n }\n },\n cancel: {\n icon: '',\n label: 'Cancel'\n }\n },\n default: \"cancel\"\n }).render(true);\n })() \n}\n\nelse {\n new Dialog({\n title: 'No Tools!',\n content: '

You don\\'t seem to have any tool with you.

',\n buttons: {\n ok: {\n icon: '',\n label: \"OK\"\n }\n },\n default: \"ok\"\n }).render(true);\n}","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} diff --git a/packs/macros-misc.db b/packs/macros-misc.db index fc517e9..d17febd 100644 --- a/packs/macros-misc.db +++ b/packs/macros-misc.db @@ -11,3 +11,16 @@ {"name":"Move Walls (Gen Kitty)","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"/* From: @(Busy) Gen Kitty (she/her)\nTo move each node on both axes, you need all 4 parameters listed. \nIn this case, he wanted to move all the walls up and to the left and \nthe foundry grid is sorta vertically flipped to what you'd expect, \nwhich is why all of the operators are \"-=\" If you wanted to move them \nin different directions it'd just be a matter of changing the operator \nnext to the equals sign.\nEach argument is a node's X or Y position, and each wall segment has two nodes. \n0 = Node 1 X \n1 = Node 1 Y \n2 = Node 2 X \n3 = Node 2 Y\n*/\n\nlet walls = canvas.scene.data.walls.map(w => {\n w = duplicate(w);\n w.c[0] -= 50;\n w.c[1] -= 50;\n w.c[2] -= 50;\n w.c[3] -= 50;\n return w;\n});\ncanvas.scene.update({walls: walls});","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"r8G0g61ikT9mJJwF"} {"name":"Token Light Picker (bekriebel)","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"let dialogEditor = new Dialog({\n title: `Token Light Picker`,\n content: `Pick the light source the selected token is holding.`,\n buttons: {\n none: {\n label: `None`,\n callback: () => {\n token.update({\"dimLight\": 0, \"brightLight\": 0, \"lightAngle\": 360,});\n dialogEditor.render(true);\n }\n },\n torch: {\n label: `Torch`,\n callback: () => {\n token.update({\"dimLight\": 40, \"brightLight\": 20, \"lightAngle\": 360,});\n dialogEditor.render(true);\n }\n },\n lamp: {\n label: `Lamp`,\n callback: () => {\n token.update({\"dimLight\": 45, \"brightLight\": 15, \"lightAngle\": 360,});\n dialogEditor.render(true);\n }\n },\n bullseye: {\n label: `Bullseye Lantern`,\n callback: () => {\n token.update({\"dimLight\": 120, \"brightLight\": 60, \"lightAngle\": 45,});\n dialogEditor.render(true);\n }\n },\n hoodedOpen: {\n label: `Hooded Lantern (Open)`,\n callback: () => {\n token.update({\"dimLight\": 60, \"brightLight\": 30, \"lightAngle\": 360,});\n dialogEditor.render(true);\n }\n },\n hoodedClosed: {\n label: `Hooded Lantern (Closed)`,\n callback: () => {\n token.update({\"dimLight\": 5, \"brightLight\": 0, \"lightAngle\": 360,});\n dialogEditor.render(true);\n }\n },\n close: {\n icon: \"\",\n label: `Close`\n },\n },\n default: \"close\",\n close: () => {}\n});\n\ndialogEditor.render(true)","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"vrwyRRA6XPMGfv9G"} {"name":"Ambient Light Quick Edit (yoman987)","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","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":"wosXzUFEMQLD84so"} +{"_id":"wosXzUFEMQLD84so","name":"Ambient Light Quick Edit","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","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":"5XA28twwaRBUFEjC","name":"Breathing Lights","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"// Courtesy of @ohporter\n// Cause lightsources to \"breathe,\" expanding and contracting.\n(async () => {\n let min = 5;\n let max = 10;\n if (game.pulsatingLights) {\n game.pulsatingLights = false;\n } else {\n game.pulsatingLights = true;\n let glyphLights = [];\n let glyphColor = \"#5940b5\"\n let scene = game.scenes.active;\n \n canvas.lighting.placeables.forEach(l => { if (l.data.tintColor === glyphColor && l.scene === scene) glyphLights.push(l.id) })\n \n const updates = []\n \n let radius = min;\n let increment = true;\n let interval = setInterval(async () => {\n glyphLights.forEach(id => {\n updates.push({ _id: id, dim: radius, bright: radius/2});\n })\n await scene.updateEmbeddedEntity(\"AmbientLight\", updates);\n \n if (increment) {radius += 1} else {radius -= 1};\n if (radius === max) {increment = false};\n if (radius === min) {increment = true};\n if (!scene.active || !game.pulsatingLights) {\n // Reset to default glow\n glyphLights.forEach(id => {\n updates.push({ _id: id, dim: min, bright: 0});\n })\n await scene.updateEmbeddedEntity(\"AmbientLight\", updates);\n clearInterval(interval);\n }\n }, 200);\n }\n })()","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} +{"_id":"8iEx21HSVOmkbBFE","name":"Combat Tracker AC HP","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","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();","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} +{"_id":"qlx6N8QD6QliPQbu","name":"Create Ambient Light","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"// Create a (pre-configured) lightsource on the current scene. \n// This example is a blue light for \"activating a stargate.\"\n\nAmbientLight.create({\n t: \"l\", // l for local. The other option is g for global.\n x: 1100, // horizontal positioning\n y: 1150, // vertical positioning\n dim: 20.50, // the total radius of the light, including where it is dim.\n bright: 19.00, // the bright radius of the light\n angle: 360, // the coverage of the light. (Try 30 for a \"spotlight\" effect.)\n rotation: 0, // the beam direction of the light in degrees (if its angle is less than 360 degrees.) \n // Oddly, degrees are counted from the 6 o'clock position.\n tintColor: \"#0080FF\", // Light coloring.\n tintAlpha: 0.5 // Light opacity (or \"brightness,\" depending on how you think about it.) \n});","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} +{"_id":"bRBhm24NyQELbDti","name":"Create Chat Message","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"// Courtesy of @errational\n// Creates a chat message.\nconst content = `

Monster attacks ${controlledToken.name}

`;\n\nChatMessage.create({\n speaker: ChatMessage.getSpeaker(controlledToken),\n content: content,\n type: CONST.CHAT_MESSAGE_TYPES.OTHER\n});","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} +{"_id":"dgSOPUXWUK1701PV","name":"Find Lights By Color","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"// Courtesy of @FloRad\n// Right here's a macro that looks for lights of a certain color, \n// sets a different color and then logs out the ids of the \n// lights it previously found using the marker color\n(async () => {\n let foundLights = [];\n let markingColor = \"#00ff00\"\n let newColor = \"#bbb\"\n let scene = game.scenes.active;\n\n canvas.lighting.placeables.forEach(l => { if (l.data.tintColor === markingColor && l.scene === scene) foundLights.push(l.id) })\n\n const updates = []\n foundLights.forEach(id => {\n updates.push({ _id: id, tintColor: newColor });\n })\n\n await scene.updateEmbeddedEntity(\"AmbientLight\", updates);\n\n console.log(foundLights)\n})()","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} +{"_id":"SPZ5TLx2gSYjFPus","name":"Flip Token Image","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"// Flips the selected token image along the Y axis.\n// Change mirrorY to mirrorX to flip across the X axis\nfor ( let token of canvas.tokens.controlled ) {\n let flip = !token.data.mirrorY || false;\n token.update({mirrorY: flip});\n};","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} +{"_id":"vrwyRRA6XPMGfv9G","name":"Token Light Picker","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"let dialogEditor = new Dialog({\n title: `Token Light Picker`,\n content: `Pick the light source the selected token is holding.`,\n buttons: {\n none: {\n label: `None`,\n callback: () => {\n token.update({\"dimLight\": 0, \"brightLight\": 0, \"lightAngle\": 360,});\n dialogEditor.render(true);\n }\n },\n torch: {\n label: `Torch`,\n callback: () => {\n token.update({\"dimLight\": 40, \"brightLight\": 20, \"lightAngle\": 360,});\n dialogEditor.render(true);\n }\n },\n lamp: {\n label: `Lamp`,\n callback: () => {\n token.update({\"dimLight\": 45, \"brightLight\": 15, \"lightAngle\": 360,});\n dialogEditor.render(true);\n }\n },\n bullseye: {\n label: `Bullseye Lantern`,\n callback: () => {\n token.update({\"dimLight\": 120, \"brightLight\": 60, \"lightAngle\": 45,});\n dialogEditor.render(true);\n }\n },\n hoodedOpen: {\n label: `Hooded Lantern (Open)`,\n callback: () => {\n token.update({\"dimLight\": 60, \"brightLight\": 30, \"lightAngle\": 360,});\n dialogEditor.render(true);\n }\n },\n hoodedClosed: {\n label: `Hooded Lantern (Closed)`,\n callback: () => {\n token.update({\"dimLight\": 5, \"brightLight\": 0, \"lightAngle\": 360,});\n dialogEditor.render(true);\n }\n },\n close: {\n icon: \"\",\n label: `Close`\n },\n },\n default: \"close\",\n close: () => {}\n});\n\ndialogEditor.render(true)","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} +{"_id":"525JVjJGyjIH5gv2","name":"Log Troubleshooting MSG To Console","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"// Courtesy of @fohswe\n// Logs a troubleshooting message to the browser console.\n// (You can usually view the browser console using F12.)\n\nconst gl = canvas.app.renderer.context.gl;\n\nconsole.log(\n`=== Start of troubleshooting ===\nBackground image: ${canvas.background.img.width}x${canvas.background.img.height}\nNumber of walls: ${canvas.scene.data.walls.length}\nNumber of selected vision sources: ${canvas.sight.sources.vision.size}\nNumber of light sources: ${canvas.sight.sources.lights.size}\nWebGL MAX_TEXTURE_SIZE: ${gl.getParameter(gl.MAX_TEXTURE_SIZE)}\n`\n);","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} +{"_id":"e25oI8hASMY8bgu6","name":"Hex Crawler Helper","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"/*\nRequired Rollable Tables:\n*Wilderness Encounters*\n coast\n jungle1\n jungle2\n jungle3\n mountains\n rivers\n ruins\n swamp\n wasteland\n\n*Other Tables*\n weather\n directions\n\n cache\n deadexplorers\n\nCache and Deadexplorers are not mandatory, but if you don't want them search the file for // CACHE LINES or // DEAD EXPLORER LINES and comment out the 2 lines below the comments\n\nExplanation of those tables:\nIf you have an encounter table that has the word cache in it, the cache table will be rolled automatically.\n
The party finds a cache: \nIf you have an encounter table that has DeadExplorers in it, the dead explorer table will be rolled automatically.\n
The party finds: \n\n\nYou can have an automatic moving \"Actual Location\" Marker by creating a Token named \"Actual Location\" and placing it on your hex grid.\nThis will move if the players are \"Lost\". If the players are not lost it will not move.\n\n\n\n*/\n\n\n// Macro requires selecting a token to roll the survival check\n\nif (canvas.tokens.controlled.length === 0)\n return ui.notifications.error(\"Please select the token of the Navigator!\");\n\nconst playerMarker = canvas.scene.data.tokens.find(a => a.name === 'Player Location');\nconst locationMarker = canvas.scene.data.tokens.find(a => a.name === 'Actual Location');\n\nconst gridSize = canvas.grid.size;\nconst vertical = gridSize * 0.866666;\nconst diagVertical = gridSize * 0.433333;\nconst diagHorizontal = gridSize * 0.75;\n\n// The option values below are the names of your rollable tables for each hex type. If these get changed here you will need to change them in the Survival Check DC section too!\n\nlet pace = 'none';\nnew Dialog({\n title: `Hex Crawl Helper`,\n content: `\n
\n
\n \n \n
\n
\n \n \n
\n
\n \n \n
\n
\n `,\n buttons: {\n slow: {\n icon: \"\",\n label: `Slow Pace`,\n callback: () => pace = 'slow'\n },\n average: {\n icon: \"\",\n label: `Average Pace`,\n callback: () => pace = 'average'\n },\n fast: {\n icon: \"\",\n label: `Fast Pace`,\n callback: () => pace = 'fast'\n }\n },\n default: \"average\",\n close: html => {\n // set variables\n let hexType = html.find('[name=\"hex-type\"]')[0].value;\n let travelType = html.find('[name=\"travel-type\"]')[0].value;\n let playerDirection = html.find('[name=\"travel-direction\"]')[0].value;\n const weatherTable = game.tables.entities.find(t => t.name === \"weather\");\n const directionTable = game.tables.entities.find(t => t.name === \"directions\");\n const cacheTable = game.tables.entities.find(t => t.name === \"cache\");\n const deadExplorerTable = game.tables.entities.find(t => t.name === \"deadexplorers\");\n const encounterTable = game.tables.entities.find(t => t.name === hexType);\n let weatherRoll = weatherTable.roll()[1].text;\n let lostDirection = directionTable.roll()[1].text;\n let msgContent = 'Weather ' + weatherRoll + '

';\n let navigator = Actors.instance.get(canvas.tokens.controlled[0].data.actorId);\n let wis = navigator.data.data.abilities.wis.mod;\n let survival = new Roll(`1d20`).roll().total + wis;\n let slowPace = new Roll(`1d4`).roll().total;\n let fastPace = new Roll(`1d2`).roll().total;\n let hexesMoved = 1;\n let encounter = '';\n let hexText = 'hexes';\n\n if (travelType === 'canoe') {\n hexesMoved++;\n }\n\n // build pace message and hex movement\n if (pace === 'slow') {\n if (slowPace === 1)\n hexesMoved--;\n if (hexesMoved === 1)\n hexText = 'hex';\n msgContent += 'Slow pace: Can hide from encounters or approach stealthily.

Party can move: ' + hexesMoved + ' ' + hexText + '.

';\n survival += 5;\n } else if (pace === 'average') {\n if (hexesMoved === 1)\n hexText = 'hex';\n msgContent += 'Average pace: For rivers, upstream and downstream have no effect, and waterfalls occur every 10 to 20 miles (requiring portage of canoes).

Party can move: ' + hexesMoved + ' ' + hexText + '.

';\n } else if (pace === 'fast') {\n if (fastPace === 1)\n hexesMoved++;\n if (hexesMoved === 1)\n hexText = 'hex';\n msgContent += 'Fast pace: -5 to passive Perception.

Party can move: ' + hexesMoved + ' ' + hexText + '.

';\n survival -= 5;\n } else {\n return;\n }\n\n // Survival Check DC for each hex type. If selected token rolls under DC the party is lost!\n if (((hexType === 'coast' || hexType === 'ruins') && survival < 10) || ((hexType === 'jungle1' || hexType === 'jungle2' || hexType === 'jungle3' || hexType === 'mountains' || hexType === 'rivers' || hexType === 'swamp' || hexType === 'wasteland') && survival < 15)) {\n msgContent += 'Party is Lost: Move actual location ' + hexesMoved + ' ' + hexText + ' to the ' + lostDirection + '

';\n if (locationMarker) {\n const locToken = canvas.tokens.get(locationMarker._id);\n switch (lostDirection) {\n case 'South':\n locToken.update({\n x: locToken.x,\n y: locToken.y + (vertical * hexesMoved)\n });\n break;\n\n case 'Southwest':\n locToken.update({\n x: locToken.x - (diagHorizontal * hexesMoved),\n y: locToken.y + (diagVertical * hexesMoved)\n });\n break;\n\n case 'Southeast':\n locToken.update({\n x: locToken.x + (diagHorizontal * hexesMoved),\n y: locToken.y + (diagVertical * hexesMoved)\n });\n break;\n\n case 'North':\n locToken.update({\n x: locToken.x,\n y: locToken.y - (vertical * hexesMoved)\n });\n break;\n\n case 'Northwest':\n locToken.update({\n x: locToken.x - (diagHorizontal * hexesMoved),\n y: locToken.y - (diagVertical * hexesMoved)\n });\n break;\n\n case 'Northeast':\n locToken.update({\n x: locToken.x + (diagHorizontal * hexesMoved),\n y: locToken.y - (diagVertical * hexesMoved)\n });\n break;\n\n default:\n break;\n }\n }\n if (playerMarker) {\n const playerToken = canvas.tokens.get(playerMarker._id);\n switch (playerDirection) {\n case 'South':\n playerToken.update({\n x: playerToken.x,\n y: playerToken.y + (vertical * hexesMoved)\n });\n break;\n\n case 'Southwest':\n playerToken.update({\n x: playerToken.x - (diagHorizontal * hexesMoved),\n y: playerToken.y + (diagVertical * hexesMoved)\n });\n break;\n\n case 'Southeast':\n playerToken.update({\n x: playerToken.x + (diagHorizontal * hexesMoved),\n y: playerToken.y + (diagVertical * hexesMoved)\n });\n break;\n\n case 'North':\n playerToken.update({\n x: playerToken.x,\n y: playerToken.y - (vertical * hexesMoved)\n });\n break;\n\n case 'Northwest':\n playerToken.update({\n x: playerToken.x - (diagHorizontal * hexesMoved),\n y: playerToken.y - (diagVertical * hexesMoved)\n });\n break;\n\n case 'Northeast':\n playerToken.update({\n x: playerToken.x + (diagHorizontal * hexesMoved),\n y: playerToken.y - (diagVertical * hexesMoved)\n });\n break;\n\n default:\n break;\n }\n }\n } else {\n if (playerMarker && locationMarker) {\n const locToken = canvas.tokens.get(locationMarker._id);\n const playerToken = canvas.tokens.get(playerMarker._id);\n\n switch (playerDirection) {\n case 'South':\n playerToken.update({\n x: locToken.x,\n y: locToken.y + (vertical * hexesMoved)\n });\n locToken.update({\n x: locToken.x,\n y: locToken.y + (vertical * hexesMoved)\n });\n break;\n\n case 'Southwest':\n playerToken.update({\n x: locToken.x - (diagHorizontal * hexesMoved),\n y: locToken.y + (diagVertical * hexesMoved)\n });\n locToken.update({\n x: locToken.x - (diagHorizontal * hexesMoved),\n y: locToken.y + (diagVertical * hexesMoved)\n });\n break;\n\n case 'Southeast':\n playerToken.update({\n x: locToken.x + (diagHorizontal * hexesMoved),\n y: locToken.y + (diagVertical * hexesMoved)\n });\n locToken.update({\n x: locToken.x + (diagHorizontal * hexesMoved),\n y: locToken.y + (diagVertical * hexesMoved)\n });\n break;\n\n case 'North':\n playerToken.update({\n x: locToken.x,\n y: locToken.y - (vertical * hexesMoved)\n });\n locToken.update({\n x: locToken.x,\n y: locToken.y - (vertical * hexesMoved)\n });\n break;\n\n case 'Northwest':\n playerToken.update({\n x: locToken.x - (diagHorizontal * hexesMoved),\n y: locToken.y - (diagVertical * hexesMoved)\n });\n locToken.update({\n x: locToken.x - (diagHorizontal * hexesMoved),\n y: locToken.y - (diagVertical * hexesMoved)\n });\n break;\n\n case 'Northeast':\n playerToken.update({\n x: locToken.x + (diagHorizontal * hexesMoved),\n y: locToken.y - (diagVertical * hexesMoved)\n });\n locToken.update({\n x: locToken.x + (diagHorizontal * hexesMoved),\n y: locToken.y - (diagVertical * hexesMoved)\n });\n break;\n\n default:\n break;\n }\n }\n }\n\n msgContent += 'Morning Encounter: ';\n\n if (new Roll(`1d20`).roll().total > 15) {\n encounter = encounterTable.roll()[1].text;\n msgContent += encounter;\n // CACHE LINES comment out the next 2 lines if you don't want to use a cache table!\n if (encounter.indexOf('cache') > -1)\n msgContent += cacheTable.roll()[1].text + '

';\n // DEAD EXPLORER LINES comment out the next 2 lines if you don't want to use a dead explorer table!\n if (encounter.indexOf('DeadExplorers') > -1)\n msgContent += deadExplorerTable.roll()[1].text + '

';\n msgContent += 'Afternoon Encounter: ';\n } else {\n msgContent += 'None.

Afternoon Encounter: ';\n }\n\n if (new Roll(`1d20`).roll().total > 15) {\n encounter = encounterTable.roll()[1].text;\n msgContent += encounter;\n // CACHE LINES comment out the next 2 lines if you don't want to use a cache table!\n if (encounter.indexOf('cache') > -1)\n msgContent += cacheTable.roll()[1].text + '

';\n // DEAD EXPLORER LINES comment out the next 2 lines if you don't want to use a dead explorer table!\n if (encounter.indexOf('DeadExplorers') > -1)\n msgContent += deadExplorerTable.roll()[1].text + '

';\n msgContent += 'Evening Encounter: ';\n } else {\n msgContent += 'None.

Evening Encounter: ';\n }\n\n if (new Roll(`1d20`).roll().total > 15) {\n encounter = encounterTable.roll()[1].text;\n msgContent += encounter;\n // CACHE LINES comment out the next 2 lines if you don't want to use a cache table!\n if (encounter.indexOf('cache') > -1)\n msgContent += cacheTable.roll()[1].text + '

';\n // DEAD EXPLORER LINES comment out the next 2 lines if you don't want to use a dead explorer table!\n if (encounter.indexOf('DeadExplorers') > -1)\n msgContent += deadExplorerTable.roll()[1].text + '

';\n } else {\n msgContent += 'None.';\n }\n\n // create the message\n let chatData = {\n content: msgContent,\n whisper: game.users.entities.filter(u => u.isGM).map(u => u._id)\n };\n ChatMessage.create(chatData, {});\n }\n}).render(true);","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} +{"_id":"r8G0g61ikT9mJJwF","name":"Move Walls","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"/* From: @(Busy) Gen Kitty (she/her)\nTo move each node on both axes, you need all 4 parameters listed. \nIn this case, he wanted to move all the walls up and to the left and \nthe foundry grid is sorta vertically flipped to what you'd expect, \nwhich is why all of the operators are \"-=\" If you wanted to move them \nin different directions it'd just be a matter of changing the operator \nnext to the equals sign.\nEach argument is a node's X or Y position, and each wall segment has two nodes. \n0 = Node 1 X \n1 = Node 1 Y \n2 = Node 2 X \n3 = Node 2 Y\n*/\n\nlet walls = canvas.scene.data.walls.map(w => {\n w = duplicate(w);\n w.c[0] -= 50;\n w.c[1] -= 50;\n w.c[2] -= 50;\n w.c[3] -= 50;\n return w;\n});\ncanvas.scene.update({walls: walls});","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} +{"_id":"XoEEXdzERNqtAepT","name":"Open Beyond Sheet Player","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"// Here's one for your players if you are using Virtual Tabletop Assets - D&D Beyond Integration:\n// Requires https://www.vttassets.com/modules/vtta-dndbeyond with character sheets linked!\n\nif (!game.user.character)\n return ui.notifications.error(\"You must first have a character assigned to your user!\");\n\nlet char = game.user.character;\n\nif (!char.data.flags.vtta && !char.data.flags.vtta.dndbeyond && !char.data.flags.vtta.dndbeyond.url)\n return ui.notifications.error(\"Character must be linked with a D&D Beyond sheet!\");\n\nlet url = char.data.flags.vtta.dndbeyond.url;\nlet ratio = window.innerWidth / window.innerHeight;\nlet width = Math.round(window.innerWidth * 0.5);\nlet height = Math.round(window.innerWidth * 0.5 * ratio);\nconst dndBeyondPopup = window.open(\n url,\n \"ddb_sheet_popup\",\n `resizeable,scrollbars,location=no,width=${width},height=${height},toolbar=1`\n);","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} +{"_id":"msTrIfgZGAzf9uHN","name":"Resolve Surprise","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"// Resolve who is surprised between groups of players and enemies or individual players and enemies\n// This macro requires The Furnance module with \"Advanced Macros\" turned on.\n// ReadMe: https://gitlab.com/x.technocat/foundry-macros/-/blob/e67f9441b2d6d442258db1d51aa2be3ead97569d/surprise-macros/README.md\n\n// Get player character tokens\nconst playerActors = getPlayerActors();\n\n// Get hostile tokens\nconst enemyActors = getEnemyActors();\n\nlet warnMsg = \"\";\n\n// Main function\nconst isSuccessful = checkSurprise(playerActors, enemyActors);\n\nif (!isSuccessful) {\n return ui.notifications.warn(warnMsg);\n}\n\nfunction checkSurprise(playerActors, enemyActors) {\n if (!game.user.isGM) {\n warnMsg = \"You do not have permission to run this macro.\";\n return false;\n }\n if (!playerActors.length > 0) {\n warnMsg =\n \"Could not find player characters in the scene. Please add player characters to the scene.\";\n return false;\n }\n if (!enemyActors.length > 0) {\n warnMsg =\n \"Could not find hostile tokens. You need tokens with hostile dispositions in the scene to check for Surprise!\";\n return false;\n }\n\n // Displays a popup and handles the input. Thanks to PaperPunk for this.\n let calculateSurprise = false;\n\n new Dialog({\n title: `What Group is Sneaking?`,\n content: `\n
\n
\n

\n
\n \n
\n \n
\n
\n
\n
\n
\n `,\n buttons: {\n yes: {\n icon: \"\",\n label: `Calculate Surprise`,\n callback: () => (calculateSurprise = true), //if \"yes\" is selected, apply the selection.\n },\n no: {\n icon: \"\",\n label: `Cancel Surprise`,\n },\n },\n // Defaulting to yes, so that when someone selects something and hits enter, it doesn't cancel:\n default: \"yes\",\n\n // Handle the input\n close: (html) => {\n if (calculateSurprise) {\n // Determine which group is sneaking based on the user's selection\n const sneakyOptions = html.find('[name=\"sneaking-group\"]'); \n let sneakingGroup;\n for (const option of sneakyOptions) {\n if(option.checked) {\n sneakingGroup = option.value;\n break;\n }\n } \n\n let results = ``;\n let lowestStealthCheck = 0;\n\n switch (sneakingGroup) {\n case \"enemies\":\n lowestStealthCheck = rollStealth(enemyActors);\n results = calculateSurpriseResults(\n lowestStealthCheck,\n playerActors\n );\n break;\n\n case \"party\":\n lowestStealthCheck = rollStealth(playerActors);\n results = calculateSurpriseResults(\n lowestStealthCheck, \n enemyActors);\n break;\n }\n\n handleChatMessage(results, lowestStealthCheck);\n }\n },\n }).render(true); // display pop up window\n\n return true; // no issues and we're done here!\n}\n\nfunction handleChatMessage(results, lowestStealth) {\n let messageContent = ``;\n messageContent += `Lowest Stealth: [[${lowestStealth}]]

`;\n messageContent += results;\n\n const surpriseRules =\n \"
Surprise Rules
\" +\n \"Compare the Dexterity (Stealth) checks of anyone hiding with the passive Wisdom (Perception) score of each creature on the opposing side. Any character or monster that doesn't notice a threat is surprised at the start of the encounter.

\" +\n \"If you're surprised, you can't move or take an action on your first turn of the combat, and you can't take a reaction until that turn ends. A member of a group can be surprised even if the other members aren't.

PHB. 189\";\n\n messageContent += surpriseRules;\n\n let chatData = {\n user: game.user._id,\n speaker: game.user.name,\n content: messageContent,\n whisper: game.users.entities.filter((u) => u.isGM).map((u) => u._id),\n };\n\n ChatMessage.create(chatData, {});\n}\n\nfunction getPlayerActors() {\n // Get tokens in the scene that are player characters and not NPCs. Prioritize selected tokens.\n const controlledPlayerActors = canvas.tokens.controlled\n .filter((pc) => pc.actor.isPC && pc.actor.data.type === \"character\")\n .map((a) => a.actor);\n\n if (controlledPlayerActors.length > 0) {\n return controlledPlayerActors;\n }\n\n // If none are selected, use all the player characters in the scene\n return canvas.tokens.children[0].children\n .filter((pc) => pc.actor.isPC && pc.actor.data.type === \"character\")\n .map((a) => a.actor);\n}\n\nfunction getEnemyActors() {\n // Prioritize selected hostile enemies\n const controlledEnemyActors = canvas.tokens.controlled\n .filter((ec) => ec.actor.isPC === false && ec.data.disposition === -1)\n .map((a) => a.actor);\n\n if (controlledEnemyActors.length > 0) {\n return controlledEnemyActors;\n }\n\n // If none are selected, use all the enemies in the scene that are hostile\n return canvas.tokens.children[0].children\n .filter((ec) => ec.actor.isPC === false && ec.data.disposition === -1)\n .map((a) => a.actor);\n}\n\nfunction rollStealth(sneakyGroup) {\n // Roll stealth for the sneaking group\n let stealthResults = [];\n for (let actor of sneakyGroup) {\n let stealth =\n new Roll(\"1d20\").roll().total + actor.data.data.skills.ste.mod;\n stealthResults.push(stealth);\n }\n\n // Return the lowest stealth roll, it's all we need\n return Math.min(...stealthResults);\n}\n\nfunction calculateSurpriseResults(lowestStealth, perceptiveGroup) {\n let resultMsg = ``;\n\n const surprisedTxt = `[Surprised]`;\n const notSurprisedTxt = `[Not Surprised]`;\n const alertTxt = `Alert`;\n\n const lowestPerception = Math.min.apply(\n Math,\n perceptiveGroup.map(function (actor) {\n return actor.data.data.skills.prc.passive;\n })\n );\n\n if (lowestPerception >= lowestStealth) {\n resultMsg += `No one is Surprised!
`;\n } else {\n for (let actor of perceptiveGroup) {\n const name = actor.data.name;\n const prc = actor.data.data.skills.prc.passive;\n let msg = ``;\n\n // If Alert feat not surprised, else if win perception not surprised, else surprised\n const alertFeat = actor.data.items.find((p) => p.name === \"Alert\");\n if (alertFeat) {\n msg = `${name} (${prc}) -- ${notSurprisedTxt} ${alertTxt}`;\n } else if (prc >= lowestStealth) {\n msg = `${name} (${prc}) -- ${notSurprisedTxt}`;\n } else {\n msg = `${name} (${prc}) -- ${surprisedTxt}`;\n }\n\n // Append this actor's surprise result to chat message variable.\n resultMsg += `${msg}

`;\n }\n }\n\n return resultMsg;\n}\n","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} diff --git a/packs/macros-roll.db b/packs/macros-roll.db index 11e0a0a..961eb39 100644 --- a/packs/macros-roll.db +++ b/packs/macros-roll.db @@ -2,3 +2,7 @@ {"name":"Wandering Monsters (Valien)","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"// setting variables\nlet msgContent = 'Wandering Monster roll was: ';\nlet result = '';\n\n// roll to check for wandering monster\nresult = new Roll(`1d20`).roll().total;\n\n// create the message\nif(result !== '') {\n let chatData = {\n content: msgContent + result,\n whisper: game.users.entities.filter(u => u.isGM).map(u => u._id)\n };\n ChatMessage.create(chatData, {});\n}\n\n// This requires you to have an existing Table built in Foundry VTT. Replace the table name below with your table name.\n// In this example, a roll between 17-20 will generate a roll from the Table. Tweak as needed!\nif (result >= 17) {\n const table = game.tables.entities.find(t => t.name === \n \"Wandering Monsters\");\n table.draw();\n}","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"DKxkQhiyOQMCCcGG"} {"name":"Roll Table (Reaver01)","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"// Simple macro example to only roll from a table and whisper the result to the DM\n\nconst table = game.tables.entities.find(t => t.name === \"name of your table\");\nlet roll = table.roll();\n\nlet chatData = {\n user: game.user._id,\n speaker: ChatMessage.getSpeaker(),\n content: roll[1].text,\n whisper: game.users.entities.filter(u => u.isGM).map(u => u._id)\n};\nChatMessage.create(chatData, {});","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"GgFydAwlZwXQocph"} {"name":"Roll Initiatives (shawndibble)","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"/**\n * Takes all selected tokens and adds them to the combat tracker. Then rolls initative for all NPC tokens.\n */\n\nasync function start() {\n for ( let token of canvas.tokens.controlled) { \n if (token.inCombat === false){\n // Change 'rollNPC' to 'rollAll' if you want to roll for your players as well.\n await token.toggleCombat().then(() => game.combat.rollNPC(null, {rollMode: 'gmroll'}));\n }\n }\n}\n\nstart();","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"ZaiiiR9KPBfzHvea"} +{"_id":"ZaiiiR9KPBfzHvea","name":"Roll Initiatives","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"/**\n * Takes all selected tokens and adds them to the combat tracker. Then rolls initative for all NPC tokens.\n */\n\nasync function start() {\n for ( let token of canvas.tokens.controlled) { \n if (token.inCombat === false){\n // Change 'rollNPC' to 'rollAll' if you want to roll for your players as well.\n await token.toggleCombat().then(() => game.combat.rollNPC(null, {rollMode: 'gmroll'}));\n }\n }\n}\n\nstart();","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} +{"_id":"GgFydAwlZwXQocph","name":"Roll Table","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"// Simple macro example to only roll from a table and whisper the result to the DM\n\nconst table = game.tables.entities.find(t => t.name === \"name of your table\");\nlet roll = table.roll();\n\nlet chatData = {\n user: game.user._id,\n speaker: ChatMessage.getSpeaker(),\n content: roll[1].text,\n whisper: game.users.entities.filter(u => u.isGM).map(u => u._id)\n};\nChatMessage.create(chatData, {});","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} +{"_id":"9TsgblYH0WTlDE9S","name":"Stealth Check","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"// getting all actors of selected tokens\nlet actors = canvas.tokens.controlledTokens.map(({ actor }) => actor);\n\n// if there are no selected tokens, roll for the player's character.\nif (actors.length < 1) {\n actors = game.users.entities.map(entity => {\n if (entity.active && entity.character !== null) {\n return entity.character;\n }\n });\n}\nconst validActors = actors.filter(actor => actor != null);\n\nlet messageContent = 'pp = passive perception
';\n\n// roll for every actor\nfor (const selectedActor of validActors) {\n const stealthMod = selectedActor.data.data.skills.ste.mod; \n const stealth = new Roll(`1d20+${stealthMod}`).roll().total; \n messageContent += `

${selectedActor.name} stealth roll was a ${stealth}.

`;\n\n // grab a list of unique tokens then check their passive perception against the rolled stealth.\n const uniqueActor = {};\n const caughtBy = canvas.tokens.placeables\n .filter((token) => { // filter out duplicate token names. ie: we assume all goblins have the same passive perception\n if (!token.actor || uniqueActor[token.actor.name]) {\n return false;\n }\n uniqueActor[token.actor.name] = true;\n return true;\n })\n .filter(token => {\n return selectedActor.id !== token.actor.id; // Don't check to see if the token sees himself.\n })\n .filter(({ actor }) => actor.data.data.skills.prc.passive >= stealth); // check map tokens passives with roller stealth\n\n if (!caughtBy.length) {\n messageContent += 'Stealth successful!
';\n } else {\n messageContent += 'Stealth questionable:
';\n caughtBy.map(({ actor }) => {\n messageContent += `${actor.name} pp(${actor.data.data.skills.prc.passive}).
`;\n });\n }\n}\n\n// create the message\nconst chatData = {\n user: game.user._id,\n speaker: game.user,\n content: messageContent,\n whisper: game.users.entities.filter((u) => u.isGM).map((u) => u._id),\n};\nChatMessage.create(chatData, {});\n","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]} +{"_id":"DKxkQhiyOQMCCcGG","name":"Wandering Monsters","permission":{"default":0,"y5gmtwxmW3A5ZuOP":3},"type":"chat","flags":{},"scope":"global","command":"// setting variables\nlet msgContent = 'Wandering Monster roll was: ';\nlet result = '';\n\n// roll to check for wandering monster\nresult = new Roll(`1d20`).roll().total;\n\n// create the message\nif(result !== '') {\n let chatData = {\n content: msgContent + result,\n whisper: game.users.entities.filter(u => u.isGM).map(u => u._id)\n };\n ChatMessage.create(chatData, {});\n}\n\n// This requires you to have an existing Table built in Foundry VTT. Replace the table name below with your table name.\n// In this example, a roll between 17-20 will generate a roll from the Table. Tweak as needed!\nif (result >= 17) {\n const table = game.tables.entities.find(t => t.name === \n \"Wandering Monsters\");\n table.draw();\n}","author":"y5gmtwxmW3A5ZuOP","img":"icons/svg/dice-target.svg","actorIds":[]}