diff --git a/macros/TODO.md b/macros/TODO.md
index e6b2308..b9826cc 100644
--- a/macros/TODO.md
+++ b/macros/TODO.md
@@ -2,5 +2,6 @@
* [x] Smite
* [x] Protection
-* Boost/Lower Trait
-* Summon Ally
\ No newline at end of file
+* [x] Boost/Lower Trait
+* [x] Summon Ally (Automated Evocations)
+* [x] Shape Change (Automated Evocations)
diff --git a/macros/power_effects/ApplySpellEffect.js b/macros/power_effects/ApplySpellEffect.js
index 96e588f..9d2b0f9 100644
--- a/macros/power_effects/ApplySpellEffect.js
+++ b/macros/power_effects/ApplySpellEffect.js
@@ -5,7 +5,7 @@ if (!extra) {
extra = {};
}
if (!extra.name) { extra.name = effect.label }
-if (!extra.duration) { extra.duration = effect.duration.rounds }
+if (!extra.duration) { extra.duration = effect.duration?.rounds }
if (!extra.startMessage) {
extra.startMessage = `${effect.label}'s effects start`
}
@@ -64,7 +64,7 @@ if (game.modules.get("turnAlert")?.active) {
}
await TurnAlert.create(alertData);
}
-} else if (game.modules.get("about-time")?.active) {
+} else if (game.modules.get("about-time")?.active && extra.duration) {
let duration = extra.duration * 6;
let preDuration = duration - 6;
console.log("effects end in duration:", duration, preDuration)
diff --git a/macros/power_effects/ae_shape_change_form.js b/macros/power_effects/ae_shape_change_form.js
index 07f8b23..6867c9e 100644
--- a/macros/power_effects/ae_shape_change_form.js
+++ b/macros/power_effects/ae_shape_change_form.js
@@ -5,7 +5,16 @@ let assignedActor = args[0].assignedActor;
let data = {
actor: {},
token: {},
- embedded: {Item: {}}
+ embedded: {
+ ActiveEffect: {
+ "override": {
+ icon: assignedActor.data.img,
+ label: `${assignedActor.data.name} Override`,
+ changes: []
+ }
+ },
+ Item: {}
+ }
};
const name = `${assignedActor.data.token.name} (${summon.data.token.name})`;
@@ -22,11 +31,29 @@ data.token['name'] = name;
for (const attr of keptAttributes) {
let attrData = assignedActor.data.data.attributes[attr];
- data.actor[`data.attributes.${attr}`] = attrData;
+ data.embedded.ActiveEffect.override.changes.push({
+ key: `data.attributes.${attr}.die.sides`,
+ value: assignedActor.data.data.attributes[attr].die.sides,
+ mode: foundry.CONST.ACTIVE_EFFECT_MODES.OVERRIDE
+ });
+ data.embedded.ActiveEffect.override.changes.push({
+ key: `data.attributes.${attr}.die.modifier`,
+ value: assignedActor.data.data.attributes[attr].die.modifier,
+ mode: foundry.CONST.ACTIVE_EFFECT_MODES.OVERRIDE
+ });
let skills = assignedActor.items.filter(
- i => i.type === 'skill' && i.data.data.attribute === attr)
+ i => i.type === 'skill' && i.data.data.attribute === attr);
for(const skill of skills) {
- data.embedded['Item'][skill.name] = skill.data;
+ data.embedded.ActiveEffect.override.changes.push({
+ "key": `@Skill{${skill.name}}[data.die.sides]`,
+ "value": skill.data.data.die.sides,
+ "mode": foundry.CONST.ACTIVE_EFFECT_MODES.OVERRIDE
+ });
+ data.embedded.ActiveEffect.override.changes.push({
+ "key": `@Skill{${skill.name}}[data.die.modifier]`,
+ "value": skill.data.data.die.modifier,
+ "mode": foundry.CONST.ACTIVE_EFFECT_MODES.OVERRIDE
+ });
}
}
const otherItems = assignedActor.items.filter(
diff --git a/macros/power_effects/boost-lower-trait.js b/macros/power_effects/boost-lower-trait.js
new file mode 100644
index 0000000..0f687f5
--- /dev/null
+++ b/macros/power_effects/boost-lower-trait.js
@@ -0,0 +1,238 @@
+const version = 'v2.0';
+const UPICON = "icons/magic/life/cross-embers-glow-yellow-purple.webp";
+const DOWNICON = "icons/magic/movement/chevrons-down-yellow.webp";
+
+if ( canvas.tokens.controlled[0]===undefined && Array.from(game.user.targets)[0]===undefined ) {
+ ui.notifications.error("Please, select or target a token."); // No Token is Selected
+} else {
+ main();
+}
+
+async function main() {
+ let tokens = [];
+ tokens = tokens.concat(Array.from(game.user.targets));
+ tokens = tokens.concat(canvas.tokens.controlled);
+
+ let groups = {
+ "Attributes": [
+ "Agility",
+ "Smarts",
+ "Spirit",
+ "Strength",
+ "Vigor"
+ ],
+ "Skills": [],
+ }
+ let traits = {
+ "Agility": {
+ type: "attribute",
+ name: "Agility",
+ modkey: "data.attributes.agility.die.modifier",
+ diekey: "data.attributes.agility.die.sides"
+ },
+ "Smarts": {
+ type: "attribute",
+ name: "Smarts",
+ modkey: "data.attributes.smarts.die.modifier",
+ diekey: "data.attributes.smarts.die.sides"
+ },
+ "Spirit": {
+ type: "attribute",
+ name: "Spirit",
+ modkey: "data.attributes.spirit.die.modifier",
+ diekey: "data.attributes.spirit.die.sides"
+ },
+ "Strength": {
+ type: "attribute",
+ name: "Strength",
+ modkey: "data.attributes.strength.die.modifier",
+ diekey: "data.attributes.strength.die.sides"
+ },
+ "Vigor": {
+ type: "attribute",
+ name: "Vigor",
+ modkey: "data.attributes.vigor.die.modifier",
+ diekey: "data.attributes.vigor.die.sides"
+ }
+ };
+
+ for (var token of tokens) {
+ let skills = token.actor.items.filter(e => e.type == "skill");
+ for (const skill of skills) {
+ let name = skill.data.name;
+ traits[name] = {
+ type: "skill", name: name,
+ modkey: `@Skill{${name}}[data.die.modifier]`,
+ diekey: `@Skill{${name}}[data.die.sides]`
+ };
+ groups.Skills.push(name)
+ }
+ }
+
+ var traitoptions = ``;
+
+ var applyChanges = false;
+ var raise = false;
+ new Dialog({
+ title: `Boost/Lower Trait - ${version}`,
+ content: `
+
+
+
+
+
+
Which Trait?
+
Boost or Lower?
+
+
+
${traitoptions}
+
+
+
+
+
+
+ `,
+ buttons: {
+ apply: {
+ label: "Apply",
+ callback: () => { applyChanges = true; raise }
+ },
+ raise: {
+ label: "Apply with raise",
+ callback: () => { applyChanges = true; raise = true }
+ },
+ cancel: {
+ label: "Cancel"
+ }
+ },
+ default: "apply",
+ close: html => {
+ if (applyChanges) {
+ let direction = html.find('[name="select-direction"]')[0].value;
+ let trait = html.find('[name="select-trait"]')[0].value;
+ createEffect(tokens, traits, direction, trait, raise);
+ }
+ }
+ }).render(true);
+} // end main
+
+async function createEffect(tokens, traits, direction, trait, raise) {
+ trait = traits[trait];
+ for (var tokenD of tokens) {
+ let currentdie = 0;
+ let currentmod = 0;
+ if (trait["type"] == "attribute") {
+ var part;
+ let value = tokenD.actor.data;
+ for (part of trait["diekey"].split(".")) {
+ value = value[part];
+ }
+ currentdie = value
+ value = tokenD.actor.data
+ for (part of trait["modkey"].split(".")) {
+ value = value[part];
+ }
+ currentmod = value;
+ } else {
+ let skill = tokenD.actor.items.filter(s => s.type == "skill").find(s => s.data.name == trait["name"])
+ if (skill) {
+ currentdie = skill.data.data.die.sides;
+ currentmod = skill.data.data.die.modifier;
+ }
+ }
+ if (currentdie == 0) {
+ continue;
+ }
+ if (currentdie == 4 && direction == "Lower") {
+ continue;
+ }
+ let diemod = 2;
+ let modmod = 0;
+ if (direction == "Lower") {
+ diemod = -2;
+ }
+ if (currentdie == 6 && direction == "Lower" && raise) {
+ diemod = -1;
+ } else if (currentdie == 12 && direction == "Boost") {
+ diemod = 0;
+ modmod = 1;
+ }
+ if (raise) {
+ diemod *= 2;
+ modmod *= 2;
+ }
+ if (currentdie == 10 && direction == "Boost" && raise) {
+ diemod = 2;
+ modmod = 1;
+ }
+ var effectData = {
+ label: `${direction} ${trait.name}${raise ? " with raise" : ""}`,
+ id: `${direction}${trait.name}${raise ? "raise" : ""}`,
+ icon: direction == "Lower" ? DOWNICON : UPICON,
+ changes: [{
+ "key": trait["diekey"],
+ "mode": 2,
+ "value": diemod,
+ "priority": 0
+ },{
+ "key": trait["modkey"],
+ "mode": 2,
+ "value": modmod,
+ "priority": 0
+ }]
+ };
+ if (direction == "Boost") {
+ effectData["duration"] = {rounds: 5}
+ }
+ let spellEffect = game.macros.getName("ApplySpellEffect");
+ spellEffect.execute(effectData, [tokenD])
+ }
+}
diff --git a/packs/macros.db b/packs/macros.db
index 94e6dbd..a50b463 100644
--- a/packs/macros.db
+++ b/packs/macros.db
@@ -9,15 +9,17 @@
{"_id":"Fvw5ksJfaqV4sisJ","name":"Toggle Stunned","permission":{"default":0,"g5E84yQWEXKWBl9L":3},"type":"script","flags":{"combat-utility-belt":{"macroTrigger":""},"core":{"sourceId":"Macro.0nnEBhT2P7XeoKl7"},"cf":{"id":"temp_pswasgs6ygg"}},"scope":"global","command":"main ()\n\nasync function main() {\n //Is a token selected\n if (canvas.tokens.controlled.length == 0) {\n ui.notifications.error(\"No tokens selected\");\n return;\n }\n const proneEffectName = \"SWADE.Prone\";\n let proneEffect = CONFIG.statusEffects.find(s => s.label == proneEffectName);\n\n let tokens = canvas.tokens.controlled.map(token => {return token});\n\n for (let token of tokens) {\n if (token.actor.status.isStunned) {\n await token.actor.update({\"data.status.isStunned\": false})\n } else {\n // add stunned and vulnerable and distracted and prone\n await token.actor.update({\"data.status.isStunned\": true})\n await token.actor.update({\"data.status.isDistracted\": true})\n if (!token.actor.effects.find(e => e.data.icon == proneEffect.icon)) {\n await token.toggleEffect(proneEffect);\n }\n await token.actor.update({\"data.status.isVulnerable\": true})\n }\n } // end for\n} //End main","author":"g5E84yQWEXKWBl9L","img":"systems/swade/assets/icons/status/status_stunned.svg","actorIds":[]}
{"_id":"GdDAPaUnymrqdVrM","name":"Toggle Prone","permission":{"default":0,"goVuB7uyVDPjAwfj":3},"type":"script","flags":{"combat-utility-belt":{"macroTrigger":""},"core":{"sourceId":"Macro.0nnEBhT2P7XeoKl7"},"cf":{"id":"temp_pswasgs6ygg"}},"scope":"global","command":"main ()\n\nasync function main() {\n //Is a token selected\n if (canvas.tokens.controlled.length == 0) {\n ui.notifications.error(\"No tokens selected\");\n return;\n }\n const effectName = \"SWADE.Prone\";\n let effect = CONFIG.statusEffects.find(s => s.label == effectName);\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_prone.svg","actorIds":[]}
{"_id":"HRVgvCZuAPR2WCMH","name":"Toggle Bound","permission":{"default":0,"g5E84yQWEXKWBl9L":3},"type":"script","flags":{"combat-utility-belt":{"macroTrigger":""},"core":{"sourceId":"Macro.0nnEBhT2P7XeoKl7"},"cf":{"id":"temp_pswasgs6ygg"}},"scope":"global","command":"main ()\n\nasync function main() {\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 if (token.actor.status.isBound) {\n await token.actor.update({\"data.status.isBound\": false})\n await token.actor.update({\"data.status.isDistracted\": false})\n await token.actor.update({\"data.status.isVulnerable\": false})\n } else {\n await token.actor.update({\"data.status.isBound\": true})\n await token.actor.update({\"data.status.isDistracted\": true})\n await token.actor.update({\"data.status.isEntangled\": false})\n await token.actor.update({\"data.status.isVulnerable\": true})\n }\n } // end for\n} //End main","author":"g5E84yQWEXKWBl9L","img":"systems/swade/assets/icons/status/status_bound.svg","actorIds":[]}
-{"_id":"Ih0MlJcIkTiSWsAm","name":"#[CF_tempEntity]","type":"chat","author":"ygiRButlaf23fX9p","img":"icons/svg/dice-target.svg","scope":"global","command":"","folder":null,"sort":0,"permission":{"default":0,"ygiRButlaf23fX9p":3},"flags":{"cf":{"id":"temp_p20tzfcm449","path":"Spell Effect Macros","color":"#000000","name":"Spell Effect Macros","children":["lm51dm7e9yhTx4os","92LxuCERE2PKwgWn","FntCnwyd0hrKppdd","yV3XNCfgHpGrREt0","tjaqN9gedJpYFgbu"],"folderPath":[]}}}
+{"_id":"Ih0MlJcIkTiSWsAm","name":"#[CF_tempEntity]","type":"chat","author":"ygiRButlaf23fX9p","img":"icons/svg/dice-target.svg","scope":"global","command":"","folder":null,"sort":0,"permission":{"default":0,"ygiRButlaf23fX9p":3},"flags":{"cf":{"id":"temp_p20tzfcm449","path":"Spell Effect Macros","color":"#000000","name":"Spell Effect Macros","children":["lm51dm7e9yhTx4os","fzkUWlTKCJ68nEzL","92LxuCERE2PKwgWn","FntCnwyd0hrKppdd","yV3XNCfgHpGrREt0","tjaqN9gedJpYFgbu"],"folderPath":[]}}}
{"name":"Fear Table","permission":{"default":0,"goVuB7uyVDPjAwfj":3},"type":"script","flags":{"core":{"sourceId":"Macro.tt5wQLZWCHErlY8L"},"combat-utility-belt":{"macroTrigger":""}},"scope":"global","command":"// Ask for Fear Penalty\n// Roll On the Fear Table with the Penalty\n\nmain()\n\nasync function main(){\n let fearTable = await game.packs.get(\"swade-core-rules.swade-tables\").getEntity(game.packs.get(\"swade-core-rules.swade-tables\").index.find(el => el.name == \"Fear Table\")._id)\n new Dialog({\n title:\"Fear Table Modifier\",\n content: `\n
\n
Creature Fear Penalty (Positive Number):
\n
\n
\n `,\n buttons: {\n roll: {\n label: \"Roll\",\n callback: (html) => {\n let mod = html.find(\"#fearPenalty\")[0].value;\n console.log(mod)\n fearTable.draw({roll:new Roll(`1d20 + ${mod}`)})\n }\n }, \n cancel: {\n label: \"Cancel\"\n }\n }\n }).render(true)\n}","author":"goVuB7uyVDPjAwfj","img":"systems/swade/assets/icons/status/status_frightened.svg","actorIds":[],"_id":"Ry6NLK24QaSVA1dM"}
+{"name":"AE_Companion_Macro(Small Air Elemental Form)","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.bA8Q9JRHD6BTBsi2","hash":"ff08fce7de98bccd0cb4b9e4feccf4952428fad6"},"cf":{"id":"temp_ocr9zgcmo7","path":"Shape Change Macros for AE","color":"#000000"}},"_id":"T2zPslXg2KNLoUGI"}
{"name":"#[CF_tempEntity]","permission":{"default":0,"goVuB7uyVDPjAwfj":3},"type":"chat","flags":{"cf":{"id":"temp_pswasgs6ygg","folderPath":[],"color":"#000000","fontColor":"#FFFFFF","name":"States","children":[],"icon":""}},"scope":"global","command":"","author":"goVuB7uyVDPjAwfj","img":"icons/svg/dice-target.svg","actorIds":[],"_id":"T7HZINkdw1Z6u1Fc"}
{"name":"Raise Calculator (Dynamic)","permission":{"default":0,"g5E84yQWEXKWBl9L":3},"type":"script","flags":{"combat-utility-belt":{"macroTrigger":""},"furnace":{"runAsGM":false},"core":{"sourceId":"Macro.onkkfY2zBddVpiLr"},"exportSource":{"world":"swadetest","system":"swade","coreVersion":"0.7.9","systemVersion":"0.18.3"}},"scope":"global","command":"let text = `

Your Raises will show here once you leave the Result field.`;\n\nnew Dialog({\n title: 'Raise Calculator',\n content: `\n
`,\n buttons: {},\n render: ([dialogContent]) => {\n dialogContent.querySelector(`input[name=\"target\"`).focus();\n dialogContent.querySelector(`input[name=\"result\"`).addEventListener(\"input\", (event) => {\n const textInput = event.target;\n const form = textInput.closest(\"form\")\n const calcResult = form.querySelector(\".calculation\");\n const target = form.querySelector('input[name=\"target\"]').value;\n const result = form.querySelector('input[name=\"result\"]').value;\n let raises = Math.floor((parseInt(result) - parseInt(target)) / 4);\n if (parseInt(target) > parseInt(result)) {\n calcResult.innerHTML = `
Failure`;\n }\n else if (parseInt(target) <= parseInt(result) && raises < 1) {\n calcResult.innerHTML = `
Success`;\n }\n else {\n calcResult.innerHTML = `
${raises} Raise(s)`;\n }\n });\n },\n}).render(true);\n\n// v.1.2.0 By SalieriC#8263, with help from Rawny#2166.","author":"g5E84yQWEXKWBl9L","img":"modules/swade-mb-shared/assets/icons/misc/rolling-dices.svg","actorIds":[],"_id":"UB86lMBB3woUkLcb"}
-{"name":"#[CF_tempEntity]","type":"chat","author":"ygiRButlaf23fX9p","img":"icons/svg/dice-target.svg","scope":"global","command":"","folder":null,"sort":0,"permission":{"default":0,"ygiRButlaf23fX9p":3},"flags":{"cf":{"id":"temp_ocr9zgcmo7","folderPath":[],"color":"#000000","fontColor":"#FFFFFF","name":"Shape Change Macros for AE","children":[],"icon":""}},"_id":"Unwe07YlxhFhjXlJ"}
+{"_id":"Unwe07YlxhFhjXlJ","name":"#[CF_tempEntity]","type":"chat","author":"ygiRButlaf23fX9p","img":"icons/svg/dice-target.svg","scope":"global","command":"","folder":null,"sort":0,"permission":{"default":0,"ygiRButlaf23fX9p":3},"flags":{"cf":{"id":"temp_ocr9zgcmo7","folderPath":[],"color":"#000000","fontColor":"#FFFFFF","name":"Shape Change Macros for AE","children":["T2zPslXg2KNLoUGI","ed7YiOqkaqkGx0CR"],"icon":""}}}
{"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: {Item: {}}\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.actor[`data.attributes.${attr}`] = attrData;\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['Item'][skill.name] = skill.data;\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.WzTbhEUdD90hJLnX"},"cf":{"id":"temp_ocr9zgcmo7","color":"#000000"}}}
+{"_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"}}}
+{"name":"Boost/Lower Trait","type":"script","author":"ygiRButlaf23fX9p","img":"icons/magic/movement/chevrons-down-yellow.webp","scope":"global","command":"const version = 'v2.0';\nconst UPICON = \"icons/magic/life/cross-embers-glow-yellow-purple.webp\";\nconst DOWNICON = \"icons/magic/movement/chevrons-down-yellow.webp\";\n\nif ( canvas.tokens.controlled[0]===undefined && Array.from(game.user.targets)[0]===undefined ) {\n ui.notifications.error(\"Please, select or target a token.\"); // No Token is Selected\n} else {\n main();\n}\n\nasync function main() {\n let tokens = [];\n tokens = tokens.concat(Array.from(game.user.targets));\n tokens = tokens.concat(canvas.tokens.controlled);\n\n let groups = {\n \"Attributes\": [\n \"Agility\",\n \"Smarts\",\n \"Spirit\",\n \"Strength\",\n \"Vigor\"\n ],\n \"Skills\": [],\n }\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\n for (var 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\", name: name,\n modkey: `@Skill{${name}}[data.die.modifier]`,\n diekey: `@Skill{${name}}[data.die.sides]`\n };\n groups.Skills.push(name)\n }\n }\n\n var traitoptions = `
`;\n\n var applyChanges = false;\n var raise = false;\n new Dialog({\n title: `Boost/Lower Trait - ${version}`,\n content: `\n \n \n
\n
\n
\n
Which Trait?
\n
Boost or Lower?
\n
\n
\n
${traitoptions}
\n
\n \n
\n
\n
\n \n `,\n buttons: {\n apply: {\n label: \"Apply\",\n callback: () => { applyChanges = true; raise }\n },\n raise: {\n label: \"Apply with raise\",\n callback: () => { applyChanges = true; raise = true }\n },\n cancel: {\n label: \"Cancel\"\n }\n },\n default: \"apply\",\n close: html => {\n if (applyChanges) {\n let direction = html.find('[name=\"select-direction\"]')[0].value;\n let trait = html.find('[name=\"select-trait\"]')[0].value;\n createEffect(tokens, traits, direction, trait, raise);\n }\n }\n }).render(true);\n} // end main\n\nasync function createEffect(tokens, traits, direction, trait, raise) {\n trait = traits[trait];\n for (var tokenD of tokens) {\n let currentdie = 0;\n let currentmod = 0;\n if (trait[\"type\"] == \"attribute\") {\n var part;\n let value = tokenD.actor.data;\n for (part of trait[\"diekey\"].split(\".\")) {\n value = value[part];\n }\n currentdie = value\n value = tokenD.actor.data\n for (part of trait[\"modkey\"].split(\".\")) {\n value = value[part];\n }\n currentmod = value;\n } else {\n let skill = tokenD.actor.items.filter(s => s.type == \"skill\").find(s => s.data.name == trait[\"name\"])\n if (skill) {\n currentdie = skill.data.data.die.sides;\n currentmod = skill.data.data.die.modifier;\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 var effectData = {\n label: `${direction} ${trait.name}${raise ? \" with raise\" : \"\"}`,\n id: `${direction}${trait.name}${raise ? \"raise\" : \"\"}`,\n icon: direction == \"Lower\" ? DOWNICON : UPICON,\n changes: [{ \n \"key\": trait[\"diekey\"],\n \"mode\": 2,\n \"value\": diemod,\n \"priority\": 0\n },{\n \"key\": trait[\"modkey\"],\n \"mode\": 2,\n \"value\": modmod,\n \"priority\": 0\n }]\n };\n if (direction == \"Boost\") {\n effectData[\"duration\"] = {rounds: 5}\n }\n let spellEffect = game.macros.getName(\"ApplySpellEffect\");\n spellEffect.execute(effectData, [tokenD])\n }\n}","folder":null,"sort":0,"permission":{"default":0,"ygiRButlaf23fX9p":3},"flags":{"combat-utility-belt":{"macroTrigger":""},"core":{"sourceId":"Macro.dVj0CSN1HF9MwQ6w"},"scene-packer":{"hash":"4e53849e1ce9e5050e333da5e4a1a34156d2766f","sourceId":"Macro.ZdhNwBdfrTf9xgCM"},"cf":{"id":"temp_p20tzfcm449","path":"Spell Effect Macros","color":"#000000"}},"_id":"fzkUWlTKCJ68nEzL"}
{"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"}
-{"_id":"lm51dm7e9yhTx4os","name":"ApplySpellEffect","type":"script","author":"ygiRButlaf23fX9p","img":"icons/svg/sun.svg","scope":"global","command":"let effect = args[0];\nlet targets = args[1];\nlet extra = args[2];\nif (!extra) {\n extra = {};\n}\nif (!extra.name) { extra.name = effect.label }\nif (!extra.duration) { extra.duration = effect.duration.rounds }\nif (!extra.startMessage) {\n extra.startMessage = `${effect.label}'s effects start`\n}\nif (!extra.endMessage) {\n extra.endMessage = `${effect.label}'s effects end`\n}\nif (!extra.fadeMessage) {\n extra.fadeMessage = `${effect.label}'s effects start to fade`\n}\n\nif (canvas.tokens.controlled.length == 0) {\n ui.notifications.error(\"No tokens selected\");\n return;\n}\nfor (let tgt of targets) {\n let actor = tgt.actor;\n let active = actor.effects.find(i => i.data.label === effect.label);\n let chatData = {\n speaker: ChatMessage.getSpeaker(tgt),\n type: CONST.CHAT_MESSAGE_TYPES.OTHER,\n emote: true\n }\n if (extra.flavor) {\n chatData.flavor = extra.flavor;\n }\n if (active) {\n await tgt.toggleEffect(effect, { active: false })\n chatData.content = extra.endMessage;\n } else {\n await tgt.toggleEffect(effect, { active: true })\n chatData.content = extra.startMessage;\n }\n if (chatData.content) {\n ChatMessage.create(chatData);\n }\n}\nif (game.modules.get(\"turnAlert\")?.active) {\n if (game.combat && extra.duration) {\n let alertData = {\n round: extra.duration - 1,\n roundAbsolute: false,\n turnId: game.combat.combatant._id,\n message: extra.fadeMessage\n }\n await TurnAlert.create(alertData);\n\n const targetIds = targets.map(t => t.id);\n alertData = {\n round: extra.duration - 1,\n roundAbsolute: false,\n turnId: game.combat.combatant._id,\n endOfTurn: true,\n message: extra.endMessage,\n macro: \"CancelSpellEffect\",\n args: [effect, targetIds]\n }\n await TurnAlert.create(alertData);\n }\n} else if (game.modules.get(\"about-time\")?.active) {\n let duration = extra.duration * 6;\n let preDuration = duration - 6;\n console.log(\"effects end in duration:\", duration, preDuration)\n Gametime.doIn({ seconds: preDuration }, () => {\n let chatData = {\n type: CONST.CHAT_MESSAGE_TYPES.OTHER,\n emote: true,\n content: extra.fadeMessage\n }\n if (extra.flavor) {\n chatData.flavor = extra.flavor;\n }\n ChatMessage.create(chatData);\n });\n const targetIds = targets.map(t => t.id);\n Gametime.doIn({ seconds: duration }, () => {\n let macro = game.macros.getName(\"CancelSpellEffect\")\n macro.execute(effect, targetIds)\n })\n}","folder":null,"sort":0,"permission":{"default":0,"ygiRButlaf23fX9p":3},"flags":{"combat-utility-belt":{"macroTrigger":""},"advanced-macros":{"runAsGM":false},"scene-packer":{"sourceId":"Macro.OB9FF0mpgWSYEryu"},"cf":{"id":"temp_p20tzfcm449","path":"Spell Effect Macros","color":"#000000"}}}
+{"_id":"lm51dm7e9yhTx4os","name":"ApplySpellEffect","type":"script","author":"ygiRButlaf23fX9p","img":"icons/svg/sun.svg","scope":"global","command":"let effect = args[0];\nlet targets = args[1];\nlet extra = args[2];\nif (!extra) {\n extra = {};\n}\nif (!extra.name) { extra.name = effect.label }\nif (!extra.duration) { extra.duration = effect.duration?.rounds }\nif (!extra.startMessage) {\n extra.startMessage = `${effect.label}'s effects start`\n}\nif (!extra.endMessage) {\n extra.endMessage = `${effect.label}'s effects end`\n}\nif (!extra.fadeMessage) {\n extra.fadeMessage = `${effect.label}'s effects start to fade`\n}\n\nif (canvas.tokens.controlled.length == 0) {\n ui.notifications.error(\"No tokens selected\");\n return;\n}\nfor (let tgt of targets) {\n let actor = tgt.actor;\n let active = actor.effects.find(i => i.data.label === effect.label);\n let chatData = {\n speaker: ChatMessage.getSpeaker(tgt),\n type: CONST.CHAT_MESSAGE_TYPES.OTHER,\n emote: true\n }\n if (extra.flavor) {\n chatData.flavor = extra.flavor;\n }\n if (active) {\n await tgt.toggleEffect(effect, { active: false })\n chatData.content = extra.endMessage;\n } else {\n await tgt.toggleEffect(effect, { active: true })\n chatData.content = extra.startMessage;\n }\n if (chatData.content) {\n ChatMessage.create(chatData);\n }\n}\nif (game.modules.get(\"turnAlert\")?.active) {\n if (game.combat && extra.duration) {\n let alertData = {\n round: extra.duration - 1,\n roundAbsolute: false,\n turnId: game.combat.combatant._id,\n message: extra.fadeMessage\n }\n await TurnAlert.create(alertData);\n\n const targetIds = targets.map(t => t.id);\n alertData = {\n round: extra.duration - 1,\n roundAbsolute: false,\n turnId: game.combat.combatant._id,\n endOfTurn: true,\n message: extra.endMessage,\n macro: \"CancelSpellEffect\",\n args: [effect, targetIds]\n }\n await TurnAlert.create(alertData);\n }\n} else if (game.modules.get(\"about-time\")?.active && extra.duration) {\n let duration = extra.duration * 6;\n let preDuration = duration - 6;\n console.log(\"effects end in duration:\", duration, preDuration)\n Gametime.doIn({ seconds: preDuration }, () => {\n let chatData = {\n type: CONST.CHAT_MESSAGE_TYPES.OTHER,\n emote: true,\n content: extra.fadeMessage\n }\n if (extra.flavor) {\n chatData.flavor = extra.flavor;\n }\n ChatMessage.create(chatData);\n });\n const targetIds = targets.map(t => t.id);\n Gametime.doIn({ seconds: duration }, () => {\n let macro = game.macros.getName(\"CancelSpellEffect\")\n macro.execute(effect, targetIds)\n })\n}","folder":null,"sort":0,"permission":{"default":0,"ygiRButlaf23fX9p":3},"flags":{"combat-utility-belt":{"macroTrigger":""},"advanced-macros":{"runAsGM":false},"scene-packer":{"sourceId":"Macro.OB9FF0mpgWSYEryu"},"cf":{"id":"temp_p20tzfcm449","path":"Spell Effect Macros","color":"#000000"}}}
{"_id":"tjaqN9gedJpYFgbu","name":"Smite","type":"script","author":"ygiRButlaf23fX9p","img":"systems/swade/assets/icons/status/status_smite.svg","scope":"global","command":"const targets = canvas.tokens.controlled;\nconst extra = { flavor: \"The weapon seems more powerful\" }\nconst spellEffect = game.macros.getName(\"ApplySpellEffect\");\nconst label = \"Smite\";\nconst id = \"smite\";\nconst icon = \"systems/swade/assets/icons/status/status_smite.svg\";\nconst duration = 5;\n\nmain();\n\nasync function main() {\n let effect = {\n changes: [\n ],\n duration: { rounds: duration },\n icon: icon,\n label: label,\n id: id\n }\n\n let applyChanges = false;\n let d = new Dialog({\n title: `Applying ${label} effects`,\n content: `Apply ${label} with raise?`,\n buttons: {\n raise: {\n icon: '
',\n label: 'With raise',\n callback: () => {\n applyChanges = true;\n effect.label = effect.label + \" with raise\";\n }\n },\n noraise: {\n icon: '
',\n label: 'Normal Success',\n callback: () => { applyChanges = true }\n }\n },\n default: \"noraise\",\n close: html => {\n if (applyChanges) {\n spellEffect.execute(effect, targets, extra);\n }\n }\n });\n d.render(true);\n}","folder":null,"sort":0,"permission":{"default":0,"ygiRButlaf23fX9p":3},"flags":{"advanced-macros":{"runAsGM":false},"combat-utility-belt":{"macroTrigger":""},"core":{"sourceId":"Macro.ZNzOrPKDCN9Lq00t"},"scene-packer":{"sourceId":"Macro.ZNzOrPKDCN9Lq00t"},"cf":{"id":"temp_p20tzfcm449","path":"Spell Effect Macros","color":"#000000"}}}
{"_id":"yV3XNCfgHpGrREt0","name":"Sanctuary","type":"script","author":"ygiRButlaf23fX9p","img":"icons/svg/holy-shield.svg","scope":"global","command":"let effect = {\n changes: [\n ],\n duration: {rounds: 5},\n icon: 'icons/svg/holy-shield.svg',\n label: 'Sanctuary',\n id: 'sanctuary'\n}\nconst targets = canvas.tokens.controlled;\nconst extra = { flavor: \"Sanctuary!\" }\nconst spellEffect = game.macros.getName(\"ApplySpellEffect\");\nlet value = await spellEffect.execute(effect, targets, extra);\nreturn value;","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.01dTmSPBq0xJQJcm"},"cf":{"id":"temp_p20tzfcm449","path":"Spell Effect Macros","color":"#000000"}}}