From e730fc2e6f1ac262409968828c52aab5ef6f89f3 Mon Sep 17 00:00:00 2001 From: Mike Bloy Date: Wed, 18 May 2022 22:25:37 -0500 Subject: [PATCH] add shape change macro closes #3 --- .../warpgate_spells/shape-change-warpgate.js | 186 ++++++++++++++++++ packs/macros.db | 5 +- 2 files changed, 189 insertions(+), 2 deletions(-) create mode 100644 macros/warpgate_spells/shape-change-warpgate.js diff --git a/macros/warpgate_spells/shape-change-warpgate.js b/macros/warpgate_spells/shape-change-warpgate.js new file mode 100644 index 0000000..bd7cb3c --- /dev/null +++ b/macros/warpgate_spells/shape-change-warpgate.js @@ -0,0 +1,186 @@ +const ICON = 'icons/creatures/magical/humanoid-silhouette-glowing-pink.webp'; +const ACTORFOLDER = "Shape Change"; +const PHYSICAL_SKILLS = [ + "Athletics", + "Boating", + "Driving", + "Fighting", + "Piloting", + "Riding", + "Shooting", + "Stealth", + "Thievery" +] + +let tokens = []; +let targets = Array.from(game.user.targets); +if (targets.length > 0) { + tokens = targets; +} else if (canvas.tokens.controlled.length > 0) { + tokens = canvas.tokens.controlled; +} +if (tokens.length > 0) { + main(tokens[0]); +} else { + ui.notifications.error("Please select or target a token"); +} + +async function main(token) { + let tokenList = token.name; + let folder = game.folders.find(f => f.data.type == 'Actor' && f.data.name == ACTORFOLDER) + let menuOptions = { + title: "Shape Change", + defaultButton: "cancel", + options: {} + } + let menuData = { + inputs: [ + { type: 'header', label: 'Shape Change' }, + { type: 'info', label: `pick a new form for ${tokenList}` }, + { + type: 'select', + label: "Form", + options: folder.contents.map(a => a.data.name) + }, + ], + buttons: [ + { label: "Apply", value: "apply" }, + { label: "Apply with Raise", value: "raise" }, + { label: "Cancel", value: "cancel" } + ] + } + let {buttons, inputs} = await warpgate.menu(menuData, menuOptions); + let shapeData = { + raise: (buttons == "raise"), + actorName: (inputs[2]), + } + shapeData.actor = folder.contents.find(a => a.data.name == shapeData.actorName); + shapeData.icon = shapeData.actor.data.token.img; + if (buttons && buttons != 'cancel') { + doWork(shapeData, token); + } +} + +async function doWork(shapeData, token) { + let effectData = { + icon: ICON, + id: "shapechange", + label: "Shape Change", + duration: { rounds: 5 }, + flags: { + swade: { + expiration: 3, + loseTurnOnHold: true + } + }, + changes: [ + { + key: 'data.attributes.strength.die.sides', + mode: foundry.CONST.ACTIVE_EFFECT_MODES.UPGRADE, + value: 0, + priority: 0 + }, + { + key: 'data.attributes.strength.die.modifier', + mode: foundry.CONST.ACTIVE_EFFECT_MODES.UPGRADE, + value: 0, + priority: 0 + }, + { + key: 'data.attributes.vigor.die.sides', + mode: foundry.CONST.ACTIVE_EFFECT_MODES.UPGRADE, + value: 0, + priority: 0 + }, + { + key: 'data.attributes.vigor.die.modifier', + mode: foundry.CONST.ACTIVE_EFFECT_MODES.UPGRADE, + value: 0, + priority: 0 + }, + ] + }; + if (shapeData.raise) { + effectData.label = "Hardier Shape Change" + let str = shapeData.actor.data.data.attributes.strength.die; + let vig = shapeData.actor.data.data.attributes.vigor.die; + if (str == 12) { + effectData.changes[1].value = 1 + } else { + effectData.changes[0].value = 2 + } + if (vig == 12) { + effectData.changes[3].value = 1 + } else { + effectData.changes[2].value = 2 + } + } + let mutateOptions = { + comparisonKeys: { 'ActiveEffect': 'label' }, + name: `Shape Change (${shapeData.actor.data.name})`, + permanent: false, + description: "Shape Change", + } + let shapeActor = shapeData.actor; + let shapeToken = shapeActor.data.token; + let mutate = { + actor: { + data: { + attributes: { + agility: {die: {...shapeActor.data.data.attributes.agility.die}}, + strength: {die: {...shapeActor.data.data.attributes.strength.die}}, + vigor: {die: {...shapeActor.data.data.attributes.vigor.die}}, + }, + stats: { + speed: shapeActor.data.data.stats.speed, + size: shapeActor.data.data.stats.size, + scale: shapeActor.data.data.stats.scale, + } + } + }, + token: { + width: shapeToken.width, + height: shapeToken.height, + scale: shapeToken.scale, + img: shapeToken.img, + }, + embedded: { + ActiveEffect: { + [effectData.label]: effectData + }, + Item: { + } + } + } + for (let item of token.actor.items.filter(i => ['weapon', 'armor'].indexOf(i.data.type) > -1)) { + mutate.embedded.Item[item.data.name] = warpgate.CONST.DELETE + } + for (let item of token.actor.items.filter(i => i.data.type == 'ability')) { + if (!shapeData.actor.items.find(i => i.data.name == item.data.name && i.data.type == item.data.type)) { + mutate.embedded.Item[item.data.name] = warpgate.CONST.DELETE + } + } + for (let item of shapeData.actor.items.filter( + i => i.data.type == 'skill' && PHYSICAL_SKILLS.indexOf(i.data.name > -1)) + ) { + mutate.embedded.Item[item.data.name] = { + img: item.data.img, + name: item.data.name, + type: item.data.type, + data: { + description: item.data.data.description, + die: item.data.data.die, + notes: item.data.data.notes, + "wild-die": item.data.data["wild-die"], + } + } + } + for (let item of shapeData.actor.items.filter(i => i.data.type == 'ability')) { + mutate.embedded.Item[item.data.name] = item.data; + } + shapeData.actor.effects.forEach(effect => { + mutate.embedded.ActiveEffect[effect.data.label] = effect.data + }) + console.log(mutate) + await warpgate.mutate(token.document, mutate, {}, mutateOptions); +} \ No newline at end of file diff --git a/packs/macros.db b/packs/macros.db index b895a5e..4b1a13c 100644 --- a/packs/macros.db +++ b/packs/macros.db @@ -4,7 +4,7 @@ {"_id":"79YiKPX3WcmBeMzm","name":"Intangibility","type":"script","author":"HXnQ3GTDyHZ7E1ev","img":"icons/magic/control/debuff-energy-hold-levitate-blue-yellow.webp","scope":"global","command":"const powerEffectApply = game.macros.getName(\"Power Effect\");\n\nreturn await powerEffectApply.execute(\"Intangibility\");","folder":null,"sort":0,"permission":{"default":0,"HXnQ3GTDyHZ7E1ev":3},"flags":{"advanced-macros":{"runAsGM":false},"combat-utility-belt":{"macroTrigger":""},"core":{"sourceId":"Macro.01dTmSPBq0xJQJcm"},"scene-packer":{"sourceId":"Macro.79YiKPX3WcmBeMzm","hash":"48cfba28bf13ab364cef9bdc7953f64c23d9c5e4"},"cf":{"id":"temp_g9ptj9b0b4s","path":"SWADE Spell Effects (WarpGate)","color":"#000000"}}} {"_id":"92LxuCERE2PKwgWn","name":"CancelSpellEffect","type":"script","author":"ygiRButlaf23fX9p","img":"icons/svg/cancel.svg","scope":"global","command":"const effect = args[0];\nconst targetIds = args[1];\nconst extra = args[2];\n\nasync function main() {\n for (const tokenId of targetIds) {\n let token = game.canvas.tokens.get(tokenId);\n let actor = token?.actor;\n if (!actor) continue;\n const active = actor.effects.find(e => e.data.label === effect.label);\n if (active) {\n await token.toggleEffect(effect, { active: false });\n console.log(\"Removed active effect\", effect.label, \"from\", token.name);\n }\n }\n}\nmain();","folder":null,"sort":0,"permission":{"default":0,"ygiRButlaf23fX9p":3},"flags":{"combat-utility-belt":{"macroTrigger":""},"advanced-macros":{"runAsGM":false},"scene-packer":{"sourceId":"Macro.LHPOj1ppx03VgI1R"},"cf":{"id":"temp_p20tzfcm449","path":"Spell Effect Macros","color":"#000000"}}} {"_id":"9KVvOhKLlyZHrQ76","name":"Fly","type":"script","author":"HXnQ3GTDyHZ7E1ev","img":"systems/swade/assets/icons/status/status_flying.svg","scope":"global","command":"const powerEffectApply = game.macros.getName(\"Power Effect\");\n\nreturn await powerEffectApply.execute(\"Fly\");","folder":null,"sort":0,"permission":{"default":0,"HXnQ3GTDyHZ7E1ev":3},"flags":{"advanced-macros":{"runAsGM":false},"combat-utility-belt":{"macroTrigger":""},"core":{"sourceId":"Macro.01dTmSPBq0xJQJcm"},"scene-packer":{"sourceId":"Macro.9KVvOhKLlyZHrQ76","hash":"48cfba28bf13ab364cef9bdc7953f64c23d9c5e4"},"cf":{"id":"temp_g9ptj9b0b4s","path":"SWADE Spell Effects (WarpGate)","color":"#000000"}}} -{"_id":"9RQzK3bB1im4N0Lw","name":"Summon Ally","type":"script","author":"HXnQ3GTDyHZ7E1ev","img":"icons/magic/symbols/circle-ouroboros.webp","scope":"global","command":"const ICON = 'icons/magic/symbols/circle-ouroboros.webp';\nconst ACTORFOLDER = \"Summon Ally\";\n\nlet tokens = [];\nlet targets = Array.from(game.user.targets);\nif (targets.length > 0) {\n tokens = targets;\n} else if (canvas.tokens.controlled.length > 0) {\n tokens = canvas.tokens.controlled;\n}\nif (tokens.length > 0) {\n main(tokens[0]);\n} else {\n ui.notifications.error(\"Please select or target a token\");\n}\n\nasync function main(token) {\n let tokenList = token.name;\n let folder = game.folders.find(f => f.data.type == 'Actor' && f.data.name == ACTORFOLDER)\n let menuOptions = {\n title: \"Summon Ally\",\n defaultButton: \"cancel\",\n options: {}\n }\n let menuData = {\n inputs: [\n { type: 'header', label: 'Summon Ally' },\n { type: 'info', label: `${tokenList} is summoning an ally` },\n {\n type: 'info',\n label: `\n \n \n \n \n \n \n
RankCostServant
Novice2Attendant
Seasoned4Bodyguard
Veteran6*Mirror Self*
Heroic8Sentinel
\n `\n },\n {\n type: 'select',\n label: \"Ally to summon\",\n options: folder.contents.map(a => a.data.name)\n },\n {\n type: 'number',\n label: 'Number to spawn (+half total cost per addtl.)',\n options: 1\n },\n { type: 'checkbox', label: 'Bite/Claw (+1)', options: false }\n ],\n buttons: [\n { label: \"Apply\", value: \"apply\" },\n { label: \"Apply with Raise\", value: \"raise\" },\n { label: \"Cancel\", value: \"cancel\" }\n ]\n }\n\n let {buttons, inputs} = await warpgate.menu(menuData, menuOptions);\n let summonData = {\n raise: (buttons == \"raise\"), \n actorName: (inputs[3]),\n number: inputs[4],\n addBiteClaw: inputs[5],\n }\n summonData.actor = folder.contents.find(a => a.data.name == summonData.actorName);\n summonData.icon = summonData.actor.data.token.img;\n doWork(summonData, token);\n}\n\nasync function doWork(summonData, token) {\n console.log(\"Summon Ally\", token, summonData);\n let effectData = {\n icon: ICON,\n id: \"summonally\",\n label: \"Summoned Ally\",\n duration: { rounds: 5 },\n flags: {\n swade: {\n expiration: 3,\n loseTurnOnHold: true\n }\n },\n changes: [],\n };\n let mutateOptions = {\n comparisonKeys: { 'ActiveEffect': 'label' },\n name: \"Summoned Ally\",\n permanent: true,\n description: \"Summoned Ally\",\n }\n let mutate = {\n embedded: {\n ActiveEffect: {\n \"Summoned Ally\": effectData,\n }\n }\n };\n await warpgate.mutate(token.document, mutate, {}, mutateOptions);\n let spawnOptions = {\n controllingActor: token.actor,\n duplicates: summonData.number,\n comparisonKeys: {'ActiveEffect': 'label'},\n crosshairs: {\n size: 1,\n icon: summonData.icon,\n label: `Summon ${summonData.actorName}`,\n drawOutline: false,\n rememberControlled: true\n }\n }\n let mutateData = {\n token: {\n actorLink: false,\n name: `${token.name}'s ${summonData.actor.data.token.name}`,\n },\n embedded: {\n Item: {}\n }\n }\n if (summonData.raise) {\n mutateData.embedded.Item.Resilient = {\n \"name\": \"Resilient\",\n \"type\": \"ability\",\n \"img\": \"systems/swade/assets/icons/ability.svg\",\n \"data\": {\n \"description\": \"
\\n

Some creatures, such as ogres or dire versions of common animals on Golarion, are tough to put down.

\\n

Resilient Extras can take one @Compendium[swpf-core-rules.swpf-rules.Wounds]{Wound} before they’re @Compendium[swpf-core-rules.swpf-rules.Incapacitation]{Incapacitated}, Very Resilient Extras can take two. @Compendium[swpf-core-rules.swpf-rules.Wild Cards and Extras]{Wild Cards} can’t be Resilient or Very Resilient. The abilities exist to bring select Extras a little closer to the heroes and villains who lead them.

\\n
\",\n \"notes\": \"\",\n \"additionalStats\": {},\n \"subtype\": \"special\",\n \"grantsPowers\": false\n },\n \"effects\": {},\n \"flags\": {\n \"core\": {\n \"sourceId\": \"Compendium.swpf-core-rules.swpf-abilities.kiu5yk2aQkUGwlHN\"\n },\n }\n }\n }\n \n if (summonData.addBiteClaw) {\n mutateData.embedded.Item[\"Bite/Claw\"] = {\n \"name\": \"Bite/Claw\",\n \"type\": \"weapon\",\n \"img\": \"icons/creatures/claws/claw-hooked-curved.webp\",\n \"data\": {\n \"description\": \"\",\n \"notes\": \"\",\n \"additionalStats\": {},\n \"quantity\": 1,\n \"weight\": 0,\n \"price\": 0,\n \"equippable\": true,\n \"equipped\": true,\n \"isVehicular\": false,\n \"actions\": {\n \"skill\": \"Fighting\",\n \"skillMod\": \"\",\n \"dmgMod\": \"\",\n \"additional\": {}\n },\n \"bonusDamageDie\": 6,\n \"damage\": \"@str+d6\",\n \"range\": \"\",\n \"rof\": \"1\",\n \"ap\": 0,\n \"parry\": 0,\n \"minStr\": \"\",\n \"shots\": 0,\n \"currentShots\": 0,\n \"ammo\": \"\",\n \"autoReload\": false\n },\n \"effects\": {},\n }\n }\n await warpgate.spawn(summonData.actorName, mutateData, {}, spawnOptions)\n}","folder":null,"sort":0,"permission":{"default":0,"HXnQ3GTDyHZ7E1ev":3},"flags":{"scene-packer":{"hash":"2330aea2aee15edc2f08b0bc010a12096b472242","sourceId":"Macro.9RQzK3bB1im4N0Lw"},"advanced-macros":{"runAsGM":false},"core":{"sourceId":"Macro.9RQzK3bB1im4N0Lw"},"cf":{"id":"temp_g9ptj9b0b4s","path":"SWADE Spell Effects (WarpGate)","color":"#000000"}}} +{"_id":"9RQzK3bB1im4N0Lw","name":"Summon Ally","type":"script","author":"HXnQ3GTDyHZ7E1ev","img":"icons/magic/symbols/circle-ouroboros.webp","scope":"global","command":"const ICON = 'icons/magic/symbols/circle-ouroboros.webp';\nconst ACTORFOLDER = \"Summon Ally\";\n\nlet tokens = [];\nlet targets = Array.from(game.user.targets);\nif (targets.length > 0) {\n tokens = targets;\n} else if (canvas.tokens.controlled.length > 0) {\n tokens = canvas.tokens.controlled;\n}\nif (tokens.length > 0) {\n main(tokens[0]);\n} else {\n ui.notifications.error(\"Please select or target a token\");\n}\n\nasync function main(token) {\n let tokenList = token.name;\n let folder = game.folders.find(f => f.data.type == 'Actor' && f.data.name == ACTORFOLDER)\n let menuOptions = {\n title: \"Summon Ally\",\n defaultButton: \"cancel\",\n options: {}\n }\n let menuData = {\n inputs: [\n { type: 'header', label: 'Summon Ally' },\n { type: 'info', label: `${tokenList} is summoning an ally` },\n {\n type: 'info',\n label: `\n \n \n \n \n \n \n
RankCostServant
Novice2Attendant
Seasoned4Bodyguard
Veteran6*Mirror Self*
Heroic8Sentinel
\n `\n },\n {\n type: 'select',\n label: \"Ally to summon\",\n options: folder.contents.map(a => a.data.name)\n },\n {\n type: 'number',\n label: 'Number to spawn (+half total cost per addtl.)',\n options: 1\n },\n { type: 'checkbox', label: 'Bite/Claw (+1)', options: false }\n ],\n buttons: [\n { label: \"Apply\", value: \"apply\" },\n { label: \"Apply with Raise\", value: \"raise\" },\n { label: \"Cancel\", value: \"cancel\" }\n ]\n }\n\n let {buttons, inputs} = await warpgate.menu(menuData, menuOptions);\n let summonData = {\n raise: (buttons == \"raise\"), \n actorName: (inputs[3]),\n number: inputs[4],\n addBiteClaw: inputs[5],\n }\n summonData.actor = folder.contents.find(a => a.data.name == summonData.actorName);\n summonData.icon = summonData.actor.data.token.img;\n if (buttons && buttons != 'cancel') {\n doWork(summonData, token);\n }\n}\n\nasync function doWork(summonData, token) {\n console.log(\"Summon Ally\", token, summonData);\n let effectData = {\n icon: ICON,\n id: \"summonally\",\n label: \"Summoned Ally\",\n duration: { rounds: 5 },\n flags: {\n swade: {\n expiration: 3,\n loseTurnOnHold: true\n }\n },\n changes: [],\n };\n let mutateOptions = {\n comparisonKeys: { 'ActiveEffect': 'label' },\n name: \"Summoned Ally\",\n permanent: true,\n description: \"Summoned Ally\",\n }\n let mutate = {\n embedded: {\n ActiveEffect: {\n \"Summoned Ally\": effectData,\n }\n }\n };\n await warpgate.mutate(token.document, mutate, {}, mutateOptions);\n let spawnOptions = {\n controllingActor: token.actor,\n duplicates: summonData.number,\n comparisonKeys: {'ActiveEffect': 'label'},\n crosshairs: {\n size: 1,\n icon: summonData.icon,\n label: `Summon ${summonData.actorName}`,\n drawOutline: false,\n rememberControlled: true\n }\n }\n let mutateData = {\n token: {\n actorLink: false,\n name: `${token.name}'s ${summonData.actor.data.token.name}`,\n },\n embedded: {\n Item: {}\n }\n }\n if (summonData.raise) {\n mutateData.embedded.Item.Resilient = {\n \"name\": \"Resilient\",\n \"type\": \"ability\",\n \"img\": \"systems/swade/assets/icons/ability.svg\",\n \"data\": {\n \"description\": \"
\\n

Some creatures, such as ogres or dire versions of common animals on Golarion, are tough to put down.

\\n

Resilient Extras can take one @Compendium[swpf-core-rules.swpf-rules.Wounds]{Wound} before they’re @Compendium[swpf-core-rules.swpf-rules.Incapacitation]{Incapacitated}, Very Resilient Extras can take two. @Compendium[swpf-core-rules.swpf-rules.Wild Cards and Extras]{Wild Cards} can’t be Resilient or Very Resilient. The abilities exist to bring select Extras a little closer to the heroes and villains who lead them.

\\n
\",\n \"notes\": \"\",\n \"additionalStats\": {},\n \"subtype\": \"special\",\n \"grantsPowers\": false\n },\n \"effects\": {},\n \"flags\": {\n \"core\": {\n \"sourceId\": \"Compendium.swpf-core-rules.swpf-abilities.kiu5yk2aQkUGwlHN\"\n },\n }\n }\n }\n \n if (summonData.addBiteClaw) {\n mutateData.embedded.Item[\"Bite/Claw\"] = {\n \"name\": \"Bite/Claw\",\n \"type\": \"weapon\",\n \"img\": \"icons/creatures/claws/claw-hooked-curved.webp\",\n \"data\": {\n \"description\": \"\",\n \"notes\": \"\",\n \"additionalStats\": {},\n \"quantity\": 1,\n \"weight\": 0,\n \"price\": 0,\n \"equippable\": true,\n \"equipped\": true,\n \"isVehicular\": false,\n \"actions\": {\n \"skill\": \"Fighting\",\n \"skillMod\": \"\",\n \"dmgMod\": \"\",\n \"additional\": {}\n },\n \"bonusDamageDie\": 6,\n \"damage\": \"@str+d6\",\n \"range\": \"\",\n \"rof\": \"1\",\n \"ap\": 0,\n \"parry\": 0,\n \"minStr\": \"\",\n \"shots\": 0,\n \"currentShots\": 0,\n \"ammo\": \"\",\n \"autoReload\": false\n },\n \"effects\": {},\n }\n }\n await warpgate.spawn(summonData.actorName, mutateData, {}, spawnOptions)\n}","folder":null,"sort":0,"permission":{"default":0,"HXnQ3GTDyHZ7E1ev":3},"flags":{"scene-packer":{"hash":"12adc28d5b19e09fe54b0f1ff35e3097e5e12ccf","sourceId":"Macro.9RQzK3bB1im4N0Lw"},"advanced-macros":{"runAsGM":false},"core":{"sourceId":"Macro.9RQzK3bB1im4N0Lw"},"cf":{"id":"temp_g9ptj9b0b4s","path":"SWADE Spell Effects (WarpGate)","color":"#000000"}}} {"_id":"AVI34dUpDYCEm9w5","name":"AE_Companion_Macro(NAMEOFFORMACTOR)","type":"script","author":"ygiRButlaf23fX9p","img":"icons/svg/dice-target.svg","scope":"global","command":"const macro = game.macros.getName(\"shapeshift_AE_form\");\nlet value = await macro.execute(args[0]);\nreturn value;","folder":null,"sort":0,"permission":{"default":0,"ygiRButlaf23fX9p":3},"flags":{"combat-utility-belt":{"macroTrigger":""},"advanced-macros":{"runAsGM":false},"core":{"sourceId":"Macro.BEcVfWAVnXW0QKTR"},"scene-packer":{"sourceId":"Macro.BEcVfWAVnXW0QKTR"},"cf":{"id":"temp_ocr9zgcmo7","color":"#000000"}}} {"_id":"B07BTxFEkIpxWK3p","name":"Adventure Card","permission":{"default":0,"mrhsZpAiXth4sLah":3},"type":"script","flags":{"core":{"sourceId":"Compendium.swade-macros-simple.SWADE-Macros.dj9ISCPKpZRu43ud"},"cf":{"id":"temp_natl1zonf8"}},"scope":"global","command":"/* Mini Tutorial\r\n1 - Import the cards to a rollable table (i recommend Card Deck Importer - follow the instructions there). Name the rollable table AdventureDeck or change below.\r\n2 - Create an item (gear) named Adventure Card. Give it to the characters that will use it.\r\n3 - Run the macro.\r\n*/\r\n\r\nvar rollTableName = \"AdventureDeck\"; /// name of the rolltable with adventure cards\r\nvar itemCard = \"Adventure Card\"; /// name of the item holding the adventure card\r\n\r\nlet chars = game.actors.entities.filter((t) => t.data.type === \"character\"); /// all the chars\r\nlet optionchars = \"\";\r\nvar allchars = [];\r\n\r\nfor (const char of chars) {\r\n let myitem = char.items.find((i) => i.name === itemCard);\r\n if (myitem !== null) {\r\n /// filters the ones that has the item\r\n optionchars += ``;\r\n allchars.push(char._id);\r\n }\r\n}\r\n\r\nif (!optionchars) {\r\n /// no chars\r\n ui.notifications.warn(`No character has the item ` + itemCard + `.`, {});\r\n}\r\n\r\nlet template =\r\n `

How many cards?

\r\n

For wich character?

`;\r\nnew Dialog({\r\n title: \"Give Adventure Cards\",\r\n content: template,\r\n buttons: {\r\n ok: {\r\n label: \"Give\",\r\n callback: function (html) {\r\n applyFormOptions(html);\r\n },\r\n },\r\n cancel: {\r\n label: \"Cancel\",\r\n },\r\n },\r\n}).render(true);\r\n\r\nfunction drawFromTable(tableName) {\r\n /// thanks to Forien for this. Check his modules https://foundryvtt.com/community/forien\r\n const table = game.tables.getName(tableName);\r\n if (!table) {\r\n ui.notifications.warn(`Table ${tableName} not found.`, {});\r\n return;\r\n }\r\n let results = table.roll().results;\r\n\r\n // if table is without replacemenets, mark results as drawn\r\n if (table.data.replacement === false) {\r\n results = results.map((r) => {\r\n r.drawn = true;\r\n return r;\r\n });\r\n\r\n table.updateEmbeddedEntity(\"TableResult\", results);\r\n }\r\n\r\n return results;\r\n}\r\n\r\nfunction applyFormOptions(html) {\r\n let qtde = html.find(\"#qtde\")[0].value;\r\n let selchar = html.find(\"#jogs\")[0].value;\r\n\r\n if (selchar === \"todos\") {\r\n for (let i = 0; i < allchars.length; i++) {\r\n giveCards(qtde, allchars[i]);\r\n }\r\n } else {\r\n giveCards(qtde, selchar);\r\n }\r\n\r\n let chatData = {\r\n user: game.user._id,\r\n speaker: ChatMessage.getSpeaker(),\r\n content: \"Adventure Cards given\",\r\n };\r\n ChatMessage.create(chatData, {});\r\n}\r\n\r\nfunction giveCards(howmany, actorId) {\r\n let char = game.actors.get(actorId);\r\n let myitem = char.items.find((i) => i.name === itemCard);\r\n let updatedesc = \"\";\r\n\r\n for (let i = 1; i <= howmany; i++) {\r\n let results = drawFromTable(rollTableName);\r\n updatedesc +=\r\n \"

@Compendium[\" + results[0].collection + \".\" + results[0].resultId + \"]{\" + results[0].text + \"}

\";\r\n }\r\n\r\n myitem.update({ [\"data.description\"]: updatedesc });\r\n}\r\n","author":"mrhsZpAiXth4sLah","img":"icons/svg/chest.svg","actorIds":[]} {"_id":"CZvzvDhyY6oUHdKN","name":"Toggle Flying","permission":{"default":0,"goVuB7uyVDPjAwfj":3},"type":"script","flags":{"combat-utility-belt":{"macroTrigger":""},"core":{"sourceId":"Macro.dsr95SKSNCDX70VO"},"cf":{"id":"temp_pswasgs6ygg"}},"scope":"global","command":"main ()\n\nasync function main() {\n\nconst effect = \"systems/swade/assets/icons/status/status_flying.svg\";\nconst effectName = \"Flying\";\n\n //Is a token selected\n if (canvas.tokens.controlled.length == 0) {\n ui.notifications.error(\"No tokens selected\");\n return;\n }\n\n let tokens = canvas.tokens.controlled.map(token => {return token});\n\n for (let token of tokens) {\n await token.toggleEffect(effect);\n } // end for\n} //End main","author":"goVuB7uyVDPjAwfj","img":"systems/swade/assets/icons/status/status_flying.svg","actorIds":[]} @@ -29,6 +29,7 @@ {"_id":"c8lz4DUmw6afCYRC","name":"Environmental Protection","type":"script","author":"HXnQ3GTDyHZ7E1ev","img":"icons/magic/defensive/armor-shield-barrier-steel.webp","scope":"global","command":"const powerEffectApply = game.macros.getName(\"Power Effect\");\n\nreturn await powerEffectApply.execute(\"Environmental Protection\");","folder":null,"sort":0,"permission":{"default":0,"HXnQ3GTDyHZ7E1ev":3},"flags":{"advanced-macros":{"runAsGM":false},"combat-utility-belt":{"macroTrigger":""},"core":{"sourceId":"Macro.01dTmSPBq0xJQJcm"},"scene-packer":{"sourceId":"Macro.c8lz4DUmw6afCYRC","hash":"0df93d946a1860cbef3fffcc10cb152cc5e41735"},"cf":{"id":"temp_g9ptj9b0b4s","path":"SWADE Spell Effects (WarpGate)","color":"#000000"}}} {"name":"Stunned CUB","type":"script","author":"g5E84yQWEXKWBl9L","img":"systems/swade/assets/icons/status/status_stunned.svg","scope":"global","command":"async function tokenEffect(token) {\n if (token.actor.data.data.status.isStunned) {\n let result = await token.actor.rollAttribute('vigor');\n let msg = \"\" + token.data.name + \" \";\n let flavor = \"\";\n if (result.total >= 4) {\n await token.actor.update({\"data.status.isStunned\": false})\n msg += \"is no longer Stunned and will not be Vulnerable at the end of \";\n flavor = \"Shaking it off\";\n if (result.total >= 8) {\n msg += \"THIS turn (raise).\";\n } else {\n msg += \"NEXT turn.\";\n }\n } else {\n msg += \"remains Stunned\";\n flavor = \"Still Stunned\";\n }\n ChatMessage.create({\n user: game.user._id,\n emote: true,\n flavor: flavor,\n speaker: ChatMessage.getSpeaker({token: token}),\n content: msg});\n } else {\n game.cub.addCondition(\"Prone\", token);\n await token.actor.update({\n \"data.status.isStunned\": true,\n \"data.status.isDistracted\": true,\n \"data.status.isVulnerable\": true})\n }\n}\n\nasync function main() {\n if (canvas.tokens.controlled.length == 0) {\n ui.notifications.error(\"No tokens selected\");\n return;\n }\n canvas.tokens.controlled.forEach(token => tokenEffect(token));\n}\n\nmain()","folder":null,"sort":0,"permission":{"default":0,"g5E84yQWEXKWBl9L":3},"flags":{"combat-utility-belt":{"macroTrigger":""},"advanced-macros":{"runAsGM":false},"core":{"sourceId":"Macro.QtQtq20VkzGUeP5F"}},"_id":"d3GjZnYn8tuJ2J7j"} {"_id":"ed7YiOqkaqkGx0CR","name":"shapeshift_AE_form","type":"script","author":"ygiRButlaf23fX9p","img":"icons/svg/dice-target.svg","scope":"global","command":"let summon = args[0].summon;\nlet duplicates = args[0].duplicates;\nlet assignedActor = args[0].assignedActor;\n\nlet data = {\n actor: {},\n token: {},\n embedded: {\n ActiveEffect: {\n \"override\": {\n icon: assignedActor.data.img,\n label: `${assignedActor.data.name} Override`,\n changes: []\n }\n },\n Item: {}\n }\n};\n\nconst name = `${assignedActor.data.token.name} (${summon.data.token.name})`;\n\nconst actorElements = ['wildcard', 'bennies', 'fatigue', 'wounds'];\nconst keptAttributes = ['smarts', 'spirit']\n\nfor (const elem of actorElements) {\n data.actor[`data.${elem}`] = assignedActor.data.data[elem];\n}\n\ndata.actor['name'] = name;\ndata.token['name'] = name;\n\nfor (const attr of keptAttributes) {\n let attrData = assignedActor.data.data.attributes[attr];\n data.embedded.ActiveEffect.override.changes.push({\n key: `data.attributes.${attr}.die.sides`,\n value: assignedActor.data.data.attributes[attr].die.sides,\n mode: foundry.CONST.ACTIVE_EFFECT_MODES.OVERRIDE\n });\n data.embedded.ActiveEffect.override.changes.push({\n key: `data.attributes.${attr}.die.modifier`,\n value: assignedActor.data.data.attributes[attr].die.modifier,\n mode: foundry.CONST.ACTIVE_EFFECT_MODES.OVERRIDE\n });\n let skills = assignedActor.items.filter(\n i => i.type === 'skill' && i.data.data.attribute === attr);\n for(const skill of skills) {\n data.embedded.ActiveEffect.override.changes.push({\n \"key\": `@Skill{${skill.name}}[data.die.sides]`,\n \"value\": skill.data.data.die.sides,\n \"mode\": foundry.CONST.ACTIVE_EFFECT_MODES.OVERRIDE\n });\n data.embedded.ActiveEffect.override.changes.push({\n \"key\": `@Skill{${skill.name}}[data.die.modifier]`,\n \"value\": skill.data.data.die.modifier,\n \"mode\": foundry.CONST.ACTIVE_EFFECT_MODES.OVERRIDE\n });\n }\n}\nconst otherItems = assignedActor.items.filter(\n i => i.type === 'edge' || i.type === 'hindrance')\nfor(const item of otherItems) {\n data.embedded['Item'][item.name] = item.data;\n}\n\nreturn data;","folder":null,"sort":0,"permission":{"default":0,"ygiRButlaf23fX9p":3},"flags":{"combat-utility-belt":{"macroTrigger":""},"advanced-macros":{"runAsGM":false},"core":{"sourceId":"Macro.WzTbhEUdD90hJLnX"},"scene-packer":{"sourceId":"Macro.Iaez53kAVbNCn1FZ","hash":"b2834198d9a69c1b7418e5437e963ba12f9b61ce"},"cf":{"id":"temp_ocr9zgcmo7","color":"#000000","path":"Shape Change Macros for AE"}}} +{"_id":"enWbHGcfUFwdgdVY","name":"Shape Change","type":"script","author":"HXnQ3GTDyHZ7E1ev","img":"icons/creatures/magical/humanoid-silhouette-glowing-pink.webp","scope":"global","command":"const ICON = 'icons/creatures/magical/humanoid-silhouette-glowing-pink.webp';\nconst ACTORFOLDER = \"Shape Change\";\nconst PHYSICAL_SKILLS = [\n \"Athletics\",\n \"Boating\",\n \"Driving\",\n \"Fighting\",\n \"Piloting\",\n \"Riding\",\n \"Shooting\",\n \"Stealth\",\n \"Thievery\"\n]\n\nlet tokens = [];\nlet targets = Array.from(game.user.targets);\nif (targets.length > 0) {\n tokens = targets;\n} else if (canvas.tokens.controlled.length > 0) {\n tokens = canvas.tokens.controlled;\n}\nif (tokens.length > 0) {\n main(tokens[0]);\n} else {\n ui.notifications.error(\"Please select or target a token\");\n}\n\nasync function main(token) {\n let tokenList = token.name;\n let folder = game.folders.find(f => f.data.type == 'Actor' && f.data.name == ACTORFOLDER)\n let menuOptions = {\n title: \"Shape Change\",\n defaultButton: \"cancel\",\n options: {}\n }\n let menuData = {\n inputs: [\n { type: 'header', label: 'Shape Change' },\n { type: 'info', label: `pick a new form for ${tokenList}` },\n {\n type: 'select',\n label: \"Form\",\n options: folder.contents.map(a => a.data.name)\n },\n ],\n buttons: [\n { label: \"Apply\", value: \"apply\" },\n { label: \"Apply with Raise\", value: \"raise\" },\n { label: \"Cancel\", value: \"cancel\" }\n ]\n }\n let {buttons, inputs} = await warpgate.menu(menuData, menuOptions);\n let shapeData = {\n raise: (buttons == \"raise\"), \n actorName: (inputs[2]),\n }\n shapeData.actor = folder.contents.find(a => a.data.name == shapeData.actorName);\n shapeData.icon = shapeData.actor.data.token.img;\n if (buttons && buttons != 'cancel') {\n doWork(shapeData, token);\n }\n}\n\nasync function doWork(shapeData, token) {\n let effectData = {\n icon: ICON,\n id: \"shapechange\",\n label: \"Shape Change\",\n duration: { rounds: 5 },\n flags: {\n swade: {\n expiration: 3,\n loseTurnOnHold: true\n }\n },\n changes: [\n {\n key: 'data.attributes.strength.die.sides',\n mode: foundry.CONST.ACTIVE_EFFECT_MODES.UPGRADE,\n value: 0,\n priority: 0\n },\n {\n key: 'data.attributes.strength.die.modifier',\n mode: foundry.CONST.ACTIVE_EFFECT_MODES.UPGRADE,\n value: 0,\n priority: 0\n },\n {\n key: 'data.attributes.vigor.die.sides',\n mode: foundry.CONST.ACTIVE_EFFECT_MODES.UPGRADE,\n value: 0,\n priority: 0\n },\n {\n key: 'data.attributes.vigor.die.modifier',\n mode: foundry.CONST.ACTIVE_EFFECT_MODES.UPGRADE,\n value: 0,\n priority: 0\n },\n ]\n };\n if (shapeData.raise) {\n effectData.label = \"Hardier Shape Change\"\n let str = shapeData.actor.data.data.attributes.strength.die;\n let vig = shapeData.actor.data.data.attributes.vigor.die;\n if (str == 12) {\n effectData.changes[1].value = 1\n } else {\n effectData.changes[0].value = 2\n }\n if (vig == 12) {\n effectData.changes[3].value = 1\n } else {\n effectData.changes[2].value = 2\n }\n }\n let mutateOptions = {\n comparisonKeys: { 'ActiveEffect': 'label' },\n name: `Shape Change (${shapeData.actor.data.name})`,\n permanent: false,\n description: \"Shape Change\",\n }\n let shapeActor = shapeData.actor;\n let shapeToken = shapeActor.data.token;\n let mutate = {\n actor: {\n data: {\n attributes: {\n agility: {die: {...shapeActor.data.data.attributes.agility.die}},\n strength: {die: {...shapeActor.data.data.attributes.strength.die}},\n vigor: {die: {...shapeActor.data.data.attributes.vigor.die}},\n },\n stats: {\n speed: shapeActor.data.data.stats.speed,\n size: shapeActor.data.data.stats.size,\n scale: shapeActor.data.data.stats.scale,\n }\n }\n },\n token: {\n width: shapeToken.width,\n height: shapeToken.height,\n scale: shapeToken.scale,\n img: shapeToken.img,\n },\n embedded: {\n ActiveEffect: {\n [effectData.label]: effectData\n },\n Item: {\n }\n }\n }\n for (let item of token.actor.items.filter(i => ['weapon', 'armor'].indexOf(i.data.type) > -1)) {\n mutate.embedded.Item[item.data.name] = warpgate.CONST.DELETE\n }\n for (let item of token.actor.items.filter(i => i.data.type == 'ability')) {\n if (!shapeData.actor.items.find(i => i.data.name == item.data.name && i.data.type == item.data.type)) {\n mutate.embedded.Item[item.data.name] = warpgate.CONST.DELETE\n } \n }\n for (let item of shapeData.actor.items.filter(\n i => i.data.type == 'skill' && PHYSICAL_SKILLS.indexOf(i.data.name > -1))\n ) {\n mutate.embedded.Item[item.data.name] = {\n img: item.data.img,\n name: item.data.name,\n type: item.data.type,\n data: {\n description: item.data.data.description,\n die: item.data.data.die,\n notes: item.data.data.notes,\n \"wild-die\": item.data.data[\"wild-die\"],\n }\n }\n }\n for (let item of shapeData.actor.items.filter(i => i.data.type == 'ability')) {\n mutate.embedded.Item[item.data.name] = item.data;\n }\n shapeData.actor.effects.forEach(effect => {\n mutate.embedded.ActiveEffect[effect.data.label] = effect.data\n })\n console.log(mutate)\n await warpgate.mutate(token.document, mutate, {}, mutateOptions);\n}","folder":null,"sort":0,"permission":{"default":0,"HXnQ3GTDyHZ7E1ev":3},"flags":{"scene-packer":{"hash":"291b1d039d89c900e0fbd4be1dba4dc0a0f95650","sourceId":"Macro.enWbHGcfUFwdgdVY"},"advanced-macros":{"runAsGM":false},"cf":{"id":"temp_g9ptj9b0b4s","path":"SWADE Spell Effects (WarpGate)","color":"#000000"}}} {"_id":"fzkUWlTKCJ68nEzL","name":"Boost/Lower Trait","type":"script","author":"HXnQ3GTDyHZ7E1ev","img":"icons/magic/movement/chevrons-down-yellow.webp","scope":"global","command":"const UPICON = 'icons/magic/life/cross-embers-glow-yellow-purple.webp'\nconst DOWNICON = 'icons/magic/movement/chevrons-down-yellow.webp'\n\nlet tokens = [];\nlet targets = Array.from(game.user.targets);\nif (targets.length > 0) {\n tokens = targets;\n} else if (canvas.tokens.controlled.length > 0) {\n tokens = canvas.tokens.controlled;\n}\nif (tokens.length > 0) {\n main(tokens);\n} else {\n ui.notifications.error(\"Please select or target a token\");\n}\n\nasync function main(tokens) {\n let traitOptions = [\n \"Agility\",\n \"Smarts\",\n \"Spirit\",\n \"Strength\",\n \"Vigor\"\n ];\n let allSkills = [];\n let traits = {\n \"Agility\": {\n type: \"attribute\",\n name: \"Agility\",\n modkey: \"data.attributes.agility.die.modifier\",\n diekey: \"data.attributes.agility.die.sides\"\n },\n \"Smarts\": {\n type: \"attribute\",\n name: \"Smarts\",\n modkey: \"data.attributes.smarts.die.modifier\",\n diekey: \"data.attributes.smarts.die.sides\"\n },\n \"Spirit\": {\n type: \"attribute\",\n name: \"Spirit\",\n modkey: \"data.attributes.spirit.die.modifier\",\n diekey: \"data.attributes.spirit.die.sides\"\n },\n \"Strength\": {\n type: \"attribute\",\n name: \"Strength\",\n modkey: \"data.attributes.strength.die.modifier\",\n diekey: \"data.attributes.strength.die.sides\"\n },\n \"Vigor\": {\n type: \"attribute\",\n name: \"Vigor\",\n modkey: \"data.attributes.vigor.die.modifier\",\n diekey: \"data.attributes.vigor.die.sides\"\n }\n };\n let tokenList = tokens.map(t => t.name).join(\", \");\n for (const token of tokens) {\n let skills = token.actor.items.filter(e => e.type == \"skill\");\n for (const skill of skills) {\n let name = skill.data.name\n traits[name] = {\n type: \"skill\",\n name: name,\n modkey: `@Skill{${name}}[data.die.modifier]`,\n diekey: `@Skill{${name}}[data.die.sides]`\n };\n if (name != 'Unskilled' && !allSkills.find(v => v == name)) {\n allSkills.push(name);\n }\n }\n }\n traitOptions = traitOptions.concat(allSkills.sort());\n let menuOptions = {\n title: 'Boost/Lower Trait',\n defaultButton: \"Cancel\",\n options: {}\n };\n let menuData = {\n inputs: [\n {type: 'header', label: 'Boost/Lower Trait'},\n {type: 'info', label: `Affected Tokens: ${tokenList}`}, \n {type: 'select', label: 'Trait', options: traitOptions},\n {type: 'info', label: \"Boost or Lower?\"},\n {type: 'radio', label: 'Boost', options: ['boostlower', true]},\n {type: 'radio', label: 'Lower', options: ['boostlower', false]},\n ],\n buttons: [\n {label: \"Apply\", value: \"apply\"},\n {label: \"Apply with raise\", value: \"raise\"},\n {label: \"Cancel\", value: \"cancel\"}\n ]\n }\n let {buttons, inputs} = await warpgate.menu(menuData, menuOptions);\n if (buttons != \"cancel\") {\n let trait = inputs[2];\n let direction = inputs[4] || inputs[5];\n createEffect(tokens, traits[trait], direction, buttons);\n }\n}\n\nfunction getDottedPart(obj, key) {\n let value = obj;\n for (const part of key.split(\".\")) {\n value = value[part];\n }\n return value;\n}\n\nasync function createEffect(tokens, trait, direction, buttons) {\n const raise = (buttons == 'raise');\n const effectName = `${raise ? \"Major\" : \"Minor\"} ${direction} ${trait.name}`;\n const effectId = `${raise ? \"Major\" : \"Minor\"}${direction}${trait.name}`;\n const effectIcon = (direction == 'Boost' ? UPICON : DOWNICON)\n for (const token of tokens) {\n let tokenDoc = token.document;\n let currentDie = 0;\n let currentMod = 0;\n if (trait.type == 'attribute') {\n currentDie = getDottedPart(token.actor.data, trait.diekey);\n currentMod = getDottedPart(token.actor.data, trait.modkey);\n } else {\n let skill = token.actor.items.filter(s => s.type == 'skill').find(\n s => s.data.name == trait.name)\n if (skill) {\n currentDie = skill.data.data.die.sides;\n currentMod = skill.data.data.die.modifier;\n } else {\n currentDie = 4;\n currentMod = -2;\n }\n }\n if (currentDie == 0) {\n continue;\n }\n if (currentDie == 4 && direction == \"Lower\") {\n continue;\n }\n let diemod = 2;\n let modmod = 0;\n if (direction == \"Lower\") {\n diemod = -2;\n }\n if (currentDie == 6 && direction == \"Lower\" && raise) {\n diemod = -1\n } else if (currentDie == 12 && direction == \"Boost\") {\n diemod = 0;\n modmod = 1;\n }\n if (raise) {\n diemod *= 2;\n modmod *= 2;\n }\n if (currentDie == 10 && direction == \"Boost\" && raise) {\n diemod = 2;\n modmod = 1;\n }\n if (currentDie == 4 && currentMod == -2 && direction == \"Boost\") {\n diemod = (raise ? 2 : 0);\n modmod = 2;\n }\n let effectData = {\n icon: effectIcon,\n id: effectId,\n label: effectName,\n flags: {\n swade: {\n expiration: 3,\n loseTurnOnHold: true\n }\n },\n changes: []\n };\n let mode = foundry.CONST.ACTIVE_EFFECT_MODES.ADD;\n if (diemod != 0) {\n effectData.changes.push({ key: trait.diekey, mode: mode, value: diemod, priority: 0 });\n }\n if (modmod != 0) {\n effectData.changes.push({ key: trait.modkey, mode: mode, value: modmod, priority: 0 });\n }\n if (direction == \"Boost\") {\n effectData.duration = { rounds: 5 };\n }\n let mutate = {\n embedded: {\n ActiveEffect: {\n }\n }\n };\n mutate.embedded.ActiveEffect[effectName] = effectData;\n let mutateOptions = {\n comparisonKeys: { 'ActiveEffect': 'label' },\n name: effectName,\n permanent: false,\n description: effectName,\n }\n if (trait.type == 'skill' && direction == 'Boost' && !token.actor.items.getName(trait.name)) {\n let itemmutate = { embedded: { Item: {} } }\n itemmutate.embedded.Item[trait.name] = {\n \"name\": trait.name,\n \"type\": \"skill\",\n \"img\": \"systems/swade/assets/icons/skills/uncertainty.svg\",\n \"data\": {\n \"description\": \"See SWADE. Auto added as untrained for Boost Trait.\",\n \"notes\": \"\",\n \"additionalStats\": {},\n \"attribute\": trait.attribute,\n \"isCoreSkill\": false,\n \"die\": {\n \"sides\": 4,\n \"modifier\": -2\n },\n \"wild-die\": {\n \"sides\": 6\n }\n }\n }\n await warpgate.mutate(token.document, itemmutate, {}, mutateOptions);\n }\n mutateOptions.permanent = true;\n await warpgate.mutate(token.document, mutate, {}, mutateOptions);\n }\n}","folder":null,"sort":0,"permission":{"default":0,"ygiRButlaf23fX9p":3},"flags":{"scene-packer":{"hash":"de40818571bebaf7d7b661be5880490654328fb3","sourceId":"Macro.ETjGUe4hbN0D9kV3","defaultPermission":2},"cf":{"id":"temp_g9ptj9b0b4s","path":"SWADE Spell Effects (WarpGate)","color":"#000000"},"core":{"sourceId":"Macro.ETjGUe4hbN0D9kV3"}}} {"name":"#[CF_tempEntity]","permission":{"default":0,"mrhsZpAiXth4sLah":3},"type":"chat","flags":{"cf":{"id":"temp_natl1zonf8","folderPath":[],"color":"#000000","fontColor":"#FFFFFF","name":"Cards","children":[],"icon":""}},"scope":"global","command":"","author":"mrhsZpAiXth4sLah","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"hnicuEhZlfMF2upA"} {"name":"Sanctuary CUB","type":"script","author":"g5E84yQWEXKWBl9L","img":"icons/svg/holy-shield.svg","scope":"global","command":"const CONDITION = \"Sanctuary\";\n\n\nasync function tokenEffect(token) {\n console.log(token);\n let message = \"\" + token.data.name;\n if (game.cub.hasCondition(CONDITION, token)) {\n message += \" is no longer under Sanctuary.\";\n game.cub.removeCondition(CONDITION, token);\n } else {\n game.cub.addCondition(CONDITION, token);\n message += \" is under @Compendium[swpf-core-rules.swpf-powers.H82NWO4VNEYTqciG]{Sanctuary}.\";\n }\n ChatMessage.create({\n user: game.user._id,\n emote: true,\n flavor: \"Sanctuary!\",\n speaker: ChatMessage.getSpeaker({token: token}),\n content: message});\n}\n\nasync function main() {\n if (canvas.tokens.controlled.length == 0) {\n ui.notifications.error(\"No tokens selected\");\n return;\n }\n canvas.tokens.controlled.forEach(token => tokenEffect(token));\n}\n\nmain()","folder":null,"sort":0,"permission":{"default":0,"g5E84yQWEXKWBl9L":3},"flags":{"combat-utility-belt":{"macroTrigger":""},"advanced-macros":{"runAsGM":false},"core":{"sourceId":"Macro.T7GXMuVq2JNMne2g"}},"_id":"jAPMvsQemX2LnSWp"} @@ -36,6 +37,6 @@ {"_id":"p94dkSmOgfuoC28K","name":"Deflection (SWPF)","type":"script","author":"HXnQ3GTDyHZ7E1ev","img":"icons/magic/defensive/shield-barrier-deflect-teal.webp","scope":"global","command":"const ICON = 'icons/magic/defensive/shield-barrier-deflect-teal.webp'\n\nlet tokens = [];\nlet targets = Array.from(game.user.targets);\nif (targets.length > 0) {\n tokens = targets;\n} else if (canvas.tokens.controlled.length > 0) {\n tokens = canvas.tokens.controlled;\n}\nif (tokens.length > 0) {\n main(tokens);\n} else {\n ui.notifications.error(\"Please select or target a token\");\n}\n\nasync function main(tokens) {\n let tokenList = tokens.map(t => t.name).join(\", \");\n let dialogOptions = {\n title: \"Deflection\",\n content: `Apply Deflection to ${tokenList}`,\n default: \"cancel\",\n buttons: [\n {label: \"Apply (melee)\", value: \"melee\"},\n {label: \"Apply (ranged)\", value: \"ranged\"},\n {label: \"Apply with raise (both)\", value: \"raise\"},\n {label: \"Cancel\", value: \"cancel\"}\n ]\n }\n let choice = await warpgate.buttonDialog(dialogOptions);\n if (choice != \"cancel\") {\n createEffect(tokens, choice);\n }\n}\n\nasync function createEffect(tokens, choice) {\n let effectName = 'Deflection';\n let effectId = `deflection${choice}`;\n let changes = []\n switch (choice) {\n case 'melee':\n effectName = \"Melee \" + effectName;\n break\n case 'ranged':\n effectName = \"Ranged \" + effectName;\n break \n }\n const effectIcon = ICON;\n for (const token of tokens) {\n let effectData = {\n icon: effectIcon,\n id: effectId,\n label: effectName,\n duration: {rounds: 5},\n flags: {\n swade: {\n expiration: 3,\n loseTurnOnHold: true\n }\n },\n changes: [\n {key: 'data.stats.parry.modifier', mode: foundry.CONST.ACTIVE_EFFECT_MODES.UPGRADE, value: 0, priority: 0}\n ],\n };\n let mutate = {\n embedded: {\n ActiveEffect: {\n }\n }\n };\n mutate.embedded.ActiveEffect[effectName] = effectData;\n let mutateOptions = {\n comparisonKeys: {'ActiveEffect': 'label'},\n name: effectName,\n permanent: true,\n description: effectName,\n }\n await warpgate.mutate(token.document, mutate, {}, mutateOptions);\n }\n}","folder":null,"sort":0,"permission":{"default":0,"HXnQ3GTDyHZ7E1ev":3},"flags":{"scene-packer":{"hash":"9fef11ff303f385dcc039b421c26212cdee832c2","sourceId":"Macro.p94dkSmOgfuoC28K"},"cf":{"id":"temp_g9ptj9b0b4s","path":"SWADE Spell Effects (WarpGate)","color":"#000000"},"advanced-macros":{"runAsGM":false},"core":{"sourceId":"Compendium.swade-mb-shared.macros.p94dkSmOgfuoC28K"}}} {"_id":"tjaqN9gedJpYFgbu","name":"Smite","type":"script","author":"HXnQ3GTDyHZ7E1ev","img":"systems/swade/assets/icons/status/status_smite.svg","scope":"global","command":"const ICON = 'systems/swade/assets/icons/status/status_smite.svg'\n\nlet tokens = [];\nlet targets = Array.from(game.user.targets);\nif (targets.length > 0) {\n tokens = targets;\n} else if (canvas.tokens.controlled.length > 0) {\n tokens = canvas.tokens.controlled;\n}\nif (tokens.length > 0) {\n main(tokens);\n} else {\n ui.notifications.error(\"Please select or target a token\");\n}\n\nasync function main(tokens) {\n let tokenList = tokens.map(t => t.name).join(\", \");\n let menuOptions = {\n title: 'Smite',\n defaultButton: \"cancel\",\n options: {}\n }\n let menuData = {\n inputs: [\n {type: 'header', label: 'Smite'},\n {type: 'info', label: `Apply Smite to ${tokenList}`},\n ],\n buttons: [\n {label: \"Apply\", value: \"apply\"},\n {label: \"Apply with Raise\", value: \"raise\"},\n {label: \"Cancel\", value: \"cancel\"}\n ]\n }\n let tokenWeapons = {};\n let index = 1;\n for (const token of tokens) {\n index += 2;\n tokenWeapons[token.id] = index;\n menuData.inputs.push({type: 'info', label: `

${token.name}

`});\n let weapons = token.actor.items.filter(i => i.type == 'weapon').map(i => i.name);\n weapons.unshift(\"\");\n menuData.inputs.push({type: 'select', label: token.name, options: weapons});\n }\n let {buttons, inputs} = await warpgate.menu(menuData, menuOptions);\n for (let tokenid in tokenWeapons) {\n tokenWeapons[tokenid] = inputs[tokenWeapons[tokenid]];\n }\n if (buttons != \"cancel\") {\n await createEffect(tokens, tokenWeapons, buttons);\n }\n console.log(buttons, tokenWeapons);\n}\n\nasync function createEffect(tokens, tokenWeapons, choice) {\n const effectIcon = ICON;\n const changeValue = (choice == 'raise' ? \"+4\" : \"+2\");\n for (const token of tokens) {\n const weaponName = tokenWeapons[token.id];\n const weaponId = token.actor.items.getName(weaponName)?.id\n const changeKey = `@Weapon{${weaponName}}[data.damage]`\n if (!weaponId) {\n continue\n }\n const effectName = `Smite (${weaponName})`;\n const effectId = `smite${choice}_${weaponId}`;\n let effectData = {\n icon: effectIcon,\n id: effectId,\n label: effectName,\n duration: {rounds: 5},\n flags: {\n swade: {\n expiration: 3,\n loseTurnOnHold: true\n }\n },\n changes: [\n {\n key: changeKey,\n mode: foundry.CONST.ACTIVE_EFFECT_MODES.ADD,\n value: changeValue,\n priority: 0\n }\n ],\n };\n let mutate = {\n embedded: {\n ActiveEffect: {\n }\n }\n };\n mutate.embedded.ActiveEffect[effectName] = effectData;\n let mutateOptions = {\n comparisonKeys: {'ActiveEffect': 'label'},\n name: effectName,\n permanent: true,\n description: effectName,\n }\n await warpgate.mutate(token.document, mutate, {}, mutateOptions);\n }\n}","folder":null,"sort":0,"permission":{"default":0,"ygiRButlaf23fX9p":3},"flags":{"advanced-macros":{"runAsGM":false},"combat-utility-belt":{"macroTrigger":""},"core":{"sourceId":"Macro.MXSe0F5osRtleDV4"},"scene-packer":{"sourceId":"Macro.MXSe0F5osRtleDV4","hash":"a74182d3f48775895d7f8e9b4b0af22194eaa82c"},"cf":{"id":"temp_g9ptj9b0b4s","path":"SWADE Spell Effects (WarpGate)","color":"#000000"}}} {"_id":"uB67GR2V90kmvyVs","name":"Deflection (SWADE)","type":"script","author":"HXnQ3GTDyHZ7E1ev","img":"icons/magic/defensive/shield-barrier-deflect-teal.webp","scope":"global","command":"const ICON = 'icons/magic/defensive/shield-barrier-deflect-teal.webp'\n\nlet tokens = [];\nlet targets = Array.from(game.user.targets);\nif (targets.length > 0) {\n tokens = targets;\n} else if (canvas.tokens.controlled.length > 0) {\n tokens = canvas.tokens.controlled;\n}\nif (tokens.length > 0) {\n main(tokens);\n} else {\n ui.notifications.error(\"Please select or target a token\");\n}\n\nasync function main(tokens) {\n let tokenList = tokens.map(t => t.name).join(\", \");\n let dialogOptions = {\n title: \"Deflection\",\n content: `Apply Deflection to ${tokenList}`,\n default: \"cancel\",\n buttons: [\n {label: \"Apply (-2)\", value: \"apply\"},\n {label: \"Apply with raise (-4)\", value: \"raise\"},\n {label: \"Cancel\", value: \"cancel\"}\n ]\n }\n let choice = await warpgate.buttonDialog(dialogOptions);\n if (choice != \"cancel\") {\n createEffect(tokens, choice);\n }\n}\n\nasync function createEffect(tokens, choice) {\n let effectName = 'Deflection';\n let effectId = `deflection${choice}`;\n let changes = []\n if (choice == \"raise\") {\n effectName = \"Major \" + effectName;\n }\n const effectIcon = ICON;\n for (const token of tokens) {\n let effectData = {\n icon: effectIcon,\n id: effectId,\n label: effectName,\n duration: {rounds: 5},\n flags: {\n swade: {\n expiration: 3,\n loseTurnOnHold: true\n }\n },\n changes: [\n {key: 'data.stats.parry.modifier', mode: foundry.CONST.ACTIVE_EFFECT_MODES.UPGRADE, value: 0, priority: 0}\n ],\n };\n let mutate = {\n embedded: {\n ActiveEffect: {\n }\n }\n };\n mutate.embedded.ActiveEffect[effectName] = effectData;\n let mutateOptions = {\n comparisonKeys: {'ActiveEffect': 'label'},\n name: effectName,\n permanent: true,\n description: effectName,\n }\n await warpgate.mutate(token.document, mutate, {}, mutateOptions);\n }\n}","folder":null,"sort":0,"permission":{"default":0,"HXnQ3GTDyHZ7E1ev":3},"flags":{"scene-packer":{"hash":"5aaf1e3ca287022321b0e6c7b610e1a42175d2b2","sourceId":"Macro.uB67GR2V90kmvyVs"},"cf":{"id":"temp_g9ptj9b0b4s","path":"SWADE Spell Effects (WarpGate)","color":"#000000"},"core":{"sourceId":"Compendium.swade-mb-shared.macros.uB67GR2V90kmvyVs"}}} -{"_id":"wa7ZoYUhNcjrNEmN","name":"#[CF_tempEntity]","type":"chat","author":"HXnQ3GTDyHZ7E1ev","img":"icons/svg/dice-target.svg","scope":"global","command":"","folder":null,"sort":0,"permission":{"default":0,"HXnQ3GTDyHZ7E1ev":3},"flags":{"cf":{"id":"temp_g9ptj9b0b4s","path":"SWADE Spell Effects (WarpGate)","color":"#000000","name":"SWADE Spell Effects (WarpGate)","children":["ye1BuJD7kKGAyReU","fzkUWlTKCJ68nEzL","uB67GR2V90kmvyVs","p94dkSmOgfuoC28K","K51dscyigZyR1Kma","c8lz4DUmw6afCYRC","9KVvOhKLlyZHrQ76","OAC1Ofm7iEjU9KAm","79YiKPX3WcmBeMzm","4SRoZduQrF2o3MZK","VzTYSKRXvpOTquUO","ZMWoKCwGTBTufQaF","U6ysKunxzRsvhnR6","yV3XNCfgHpGrREt0","FngiDIs9HR2p0eY1","tjaqN9gedJpYFgbu",null,"EMnNSHCl45UMUnJu"],"folderPath":[]},"scene-packer":{"hash":"3d9a8396d815dcef72c41f01fa0318a9a20c0bb4"}}} +{"_id":"wa7ZoYUhNcjrNEmN","name":"#[CF_tempEntity]","type":"chat","author":"HXnQ3GTDyHZ7E1ev","img":"icons/svg/dice-target.svg","scope":"global","command":"","folder":null,"sort":0,"permission":{"default":0,"HXnQ3GTDyHZ7E1ev":3},"flags":{"cf":{"id":"temp_g9ptj9b0b4s","path":"SWADE Spell Effects (WarpGate)","color":"#000000","name":"SWADE Spell Effects (WarpGate)","children":["ye1BuJD7kKGAyReU","fzkUWlTKCJ68nEzL","uB67GR2V90kmvyVs","p94dkSmOgfuoC28K","K51dscyigZyR1Kma","c8lz4DUmw6afCYRC","9KVvOhKLlyZHrQ76","OAC1Ofm7iEjU9KAm","79YiKPX3WcmBeMzm","4SRoZduQrF2o3MZK","VzTYSKRXvpOTquUO","ZMWoKCwGTBTufQaF","U6ysKunxzRsvhnR6","yV3XNCfgHpGrREt0",null,"FngiDIs9HR2p0eY1","tjaqN9gedJpYFgbu","9RQzK3bB1im4N0Lw","EMnNSHCl45UMUnJu"],"folderPath":[]},"scene-packer":{"hash":"3d9a8396d815dcef72c41f01fa0318a9a20c0bb4"}}} {"_id":"yV3XNCfgHpGrREt0","name":"Sanctuary","type":"script","author":"HXnQ3GTDyHZ7E1ev","img":"icons/magic/defensive/shield-barrier-flaming-diamond-blue-yellow.webp","scope":"global","command":"const powerEffectApply = game.macros.getName(\"Power Effect\");\n\nreturn await powerEffectApply.execute(\"Sanctuary\");","folder":null,"sort":0,"permission":{"default":0,"ygiRButlaf23fX9p":3},"flags":{"advanced-macros":{"runAsGM":false},"combat-utility-belt":{"macroTrigger":""},"core":{"sourceId":"Macro.01dTmSPBq0xJQJcm"},"scene-packer":{"sourceId":"Macro.fzA6qQ1PyNgV8FqE","hash":"a45f562cc1f65a09386c3a6f9006b1fad3650f78"},"cf":{"id":"temp_g9ptj9b0b4s","path":"SWADE Spell Effects (WarpGate)","color":"#000000"}}} {"_id":"ye1BuJD7kKGAyReU","name":"Arcane Protection","type":"script","author":"HXnQ3GTDyHZ7E1ev","img":"icons/magic/defensive/shield-barrier-deflect-gold.webp","scope":"global","command":"const powerEffectApply = game.macros.getName(\"Power Effect\");\n\nreturn await powerEffectApply.execute(\"Arcane Protection\");","folder":null,"sort":0,"permission":{"default":0,"HXnQ3GTDyHZ7E1ev":3},"flags":{"advanced-macros":{"runAsGM":false},"combat-utility-belt":{"macroTrigger":""},"core":{"sourceId":"Macro.01dTmSPBq0xJQJcm"},"scene-packer":{"sourceId":"Macro.ye1BuJD7kKGAyReU","hash":"0d06d7cc1e1d6d1b9767648e0519e61253496a1c"},"cf":{"id":"temp_g9ptj9b0b4s","path":"SWADE Spell Effects (WarpGate)","color":"#000000"}}}