- Author: MisterHims
- Special thanks to: winterwulf, Jaweaver and many others :)
- Version: 0.1.4 Alpha
- Foundry VTT Compatibility: 0.7.9
- System Compatibility: DnD5e 1.2.4+
- Module(s) Requirement(s): The Furnace, DAE, Token Magic FX, Midi-QOL
- Macro(s) Requirement(s): WildShape Effect Macro
- Langage(s): [EN] (current)
WildShape is a macro allowing to polymorph his token with the animations available from Token Magic FX. The actor's capabilities will thus be replaced by those of the desired shape and he will then see his token replaced.
The various DAE effects and Token Magic FX animations already present on your character will be preserved.
If the WildShape effect is removed, the new shape back to the original shape.
- By default, you will transfer the following capabilities from your original form to your new form:
-
Mental abilities scores (Wisdom, Intelligence, Charisma)
-
Masteries of saving throws
-
Skills
-
Biography
-
Class features
-
You can yourself choose which capabilities to remove or add from the macro. More information at the bottom.
Note:
-
Foundry VTT polymorph requires players to have rights to create new actors and tokens. You will need to allow them to "Create new characters" and "Create new tokens" from the Options configuration.
-
You must also give the players the ownership rights to the actor of the desired shape.
-
First, you need to import into Foundry VTT the "WildShape Effect Macro", save the macro with the name of "WildShape Effect Macro". Repeat the operation with the main "WildShape Macro", you will make the necessary modifications thereafter.
let target = canvas.tokens.controlled[0] let actorOriginalFormId = args[1] let actorOriginalForm = game.actors.get(actorOriginalFormId) let actorOriginalFormName = actorOriginalForm.data.name let actorOriginalFormImagePath = args[2] let actorNewForm = game.actors.get(args[3]) let actorNewShapeName = args[4] let transferDAEEffects = async function () { if (actor.data.flags.dnd5e?.isPolymorphed) { let actorNewShape = game.actors.getName(actorNewShapeName) let actorNewShapeEffectsData = actorNewShape.effects.map(ef => ef.data) await actorOriginalForm.createEmbeddedEntity("ActiveEffect", actorNewShapeEffectsData) } } const delay = ms => new Promise(resolve => setTimeout(resolve, ms)) if (actor.data.flags.dnd5e?.isPolymorphed && args[0] === "off") { actorOriginalFormImagePath = actorOriginalForm.data.token.img let paramsBack = [{ filterType: "polymorph", filterId: "polymorphToOriginalForm", type: 6, padding: 70, magnify: 1, imagePath: actorOriginalFormImagePath, animated: { progress: { active: true, animType: "halfCosOscillation", val1: 0, val2: 100, loops: 1, loopDuration: 1000 } } }] target.update({ "width": actorOriginalForm.data.token.width, "height": actorOriginalForm.data.token.height }) async function backAnimation() { await Hooks.once("sightRefresh", async function () { await token.TMFXdeleteFilters("polymorphToOriginalForm") }); await token.TMFXhasFilterId("polymorphToOriginalForm") await token.TMFXaddUpdateFilters(paramsBack) await delay(1100) await actor.revertOriginalForm() } backAnimation() }
// Name of the folder in which the beasts are located let beastsFolder = "Beasts" // Name of your WildShape Effect let wildShapeEffectName = "WildShape Effect" ///////////////////////////////////////////////////// // Declare the target let target = canvas.tokens.controlled[0] // Get the ID of your the actual target (current Actor Form) let currentFormActorId = target.actor.data._id // Declare my WildShape transformation function let wildShapeTransform = async function (actorOriginalForm, actorNewFormId) { // Image's Token associated with the original actor form let actorOriginalFormImagePath = actorOriginalForm.data.token.img // Get the New Form Actor let actorNewForm = game.actors.get(actorNewFormId) // Set the token rotation to default value actorNewForm._data.token.rotation = 0 // Image's Token associated with the new actor form let actorNewFormImagePath = actorNewForm.data.token.img // Get the New Shape Actor Name let actorNewShapeName = actorOriginalForm.data.name + ' (' + actorNewForm.data.name + ')' // Declare the polymorph function let actorPolymorphism = async function () { // For actorNewForm, the ratio's Token scale should be the same of the original form actor.transformInto(actorNewForm, { keepMental: true, mergeSaves: true, mergeSkills: true, keepBio: true, keepClass: true, }) } // Declare the WildShape Effect let applyWildShapeEffect = { label: wildShapeEffectName, icon: "systems/dnd5e/icons/skills/green_13.jpg", changes: [{ "key": "macro.execute", "mode": 1, "value": `"WildShape Effect Macro"` + `"${currentFormActorId}"` + `"${actorOriginalFormImagePath}"` + `"${actorNewFormId}"` + `"${actorNewShapeName}"`, "priority": "20" }], duration: { "seconds": 7200, } } // Declare the delay variable to adjust with animation const delay = ms => new Promise(resolve => setTimeout(resolve, ms)) // If not already polymorphed, launch startAnimation function if (!actor.data.flags.dnd5e?.isPolymorphed) { let paramsStart = [{ filterType: "polymorph", filterId: "polymorphToNewForm", type: 6, padding: 70, magnify: 1, imagePath: actorNewFormImagePath, animated: { progress: { active: true, animType: "halfCosOscillation", val1: 0, val2: 100, loops: 1, loopDuration: 1000 } }, autoDisable: false, autoDestroy: false }] target.update({ "width": actorNewForm.data.token.width, "height": actorNewForm.data.token.height }) async function startAnimation() { await Hooks.once("createActiveEffect", async function () { await token.TMFXdeleteFilters("polymorphToNewForm") }); await token.TMFXhasFilterId("polymorphToNewForm") await TokenMagic.addUpdateFilters(target, paramsStart) await delay(1100) await actorPolymorphism() await Hooks.once("sightRefresh", async function () { let actorNewShape = game.actors.getName(actorNewShapeName) await actorNewShape.createEmbeddedEntity("ActiveEffect", applyWildShapeEffect) }); } startAnimation() // If actor is polymorphed, launch backAnimation function } else { // Image's Token associated with the original actor form actorOriginalFormImagePath = actorOriginalForm.data.token.img let paramsBack = [{ filterType: "polymorph", filterId: "polymorphToOriginalForm", type: 6, padding: 70, magnify: 1, imagePath: actorOriginalFormImagePath, animated: { progress: { active: true, animType: "halfCosOscillation", val1: 0, val2: 100, loops: 1, loopDuration: 1000 } } }] target.update({ "width": actorOriginalForm.data.token.width, "height": actorOriginalForm.data.token.height }) async function backAnimation() { await token.TMFXdeleteFilters("polymorphToOriginalForm") await token.TMFXhasFilterId("polymorphToOriginalForm") await token.TMFXaddUpdateFilters(paramsBack) await delay(1100) await actor.revertOriginalForm() await token.TMFXdeleteFilters("polymorphToOriginalForm") } backAnimation() } } // If not already polymorphed, display the dialog box if (!actor.data.flags.dnd5e?.isPolymorphed) { let actorOriginalForm = game.actors.get(currentFormActorId) let selectBeasts = '<form><div class="form-group"><label>Choose the beast: </label><select id="wildShapeBeasts">'; game.folders.getName(beastsFolder).content.forEach(function (beast) { let optBeast = '<option value="' + beast.data._id + '">' + beast.data.name + '</option>'; selectBeasts += optBeast; }); selectBeasts += '</select></div></form>' new Dialog({ title: "DnD5e-WildShape", content: selectBeasts, buttons: { yes: { icon: '<i class="fas fa-paw"></i>', label: "Roar!", callback: () => { // Get the New Form Actor ID let actorNewFormId = $('#wildShapeBeasts').find(":selected").val(); wildShapeTransform(actorOriginalForm, actorNewFormId); } } } }).render(true); // Else, launch the WildShape transformation function } else { let actorOriginalId = game.actors.get(currentFormActorId)._data.flags.dnd5e.originalActor let actorOriginalForm = game.actors.get(actorOriginalId) let actorNewFormId = _token.actor.data._id wildShapeTransform(actorOriginalForm, actorNewFormId); }
-
Subsequently, you can check in the Midi-QOL configurations if the checkbox "Auto apply item effects to target" and "Add macro to call on use" has been checked.
-
Get the Wild Shape 'item' from the SRD Compendium "Class Features" and import it to your item list.
-
Access the details tab of that item and in the "Feature Attack" section, select the "Utility" or "Other" Action Type to display the "On use macro" field. Then add "WildShape Macro" without the quotes in this field.
-
Now add the Wild Shape 'item' to the original character's sheet and to the shapes you want to adopt (in the "Beasts" folder). You can also drag and drop this item onto your quick access bar.
-
Then let's take the "WildShape Macro" previously added to Foundry VTT, also accessible from the collection WildShape Macro.
-
(Optional) Choose the name of the folder in which yours beasts are located:
// Name of the folder in which the beasts are located let beastsFolder = "Beasts"
-
(Optional) Choose the name of the WildShape effect when it appears on the new shape:
// Name of your WildShape Effect let wildShapeEffectName = "WildShape Effect"
You are free to configure your Wild Shape 'item' as yours needs, you can add for exemple the resource consumption for the original form (Attribute: resources.primary.values).
For better animation quality, make the ratio size of your original token be the same as the new token form (0.5 and 0.5, 0.8 and 0.8, 1 and 1, ...). This could be automated later in a future version.
You can choose different animations from Magic Token FX. There are 9 different types of animations (the one installed by default is number 6):
-
Simple transition
-
Dreamy
-
Twist
-
Water drop
-
TV Noise
-
Morphing
-
Take off/Put on you disguise!
-
Wind
-
Hologram
Then you need to replace the type number 6 by the animation number you want to use. Can be found in two places in the WildShape macro:
filterType: "polymorph",
filterId: "polymorphToNewForm",
type: 6,
padding: 70,
magnify: 1,
filterType: "polymorph",
filterId: "polymorphToOriginalForm",
type: 6,
padding: 70,
magnify: 1,
IMPORTANT | You are strongly advised not to make this modification if you do not have a good experience of javascript.
By default, the size of the start and end shape is automatically calculated. But if needed, you can change this size by modifying the width
and height
values displayed in two places on the macro.
The first is the size of the original shape:
target.update({
"width": actorNewForm.data.token.width,
"height": actorNewForm.data.token.height
The second is the end shape:
target.update({
"width": actorOriginalForm.data.token.width,
"height": actorOriginalForm.data.token.height
You will also have to repeat this operation in the new "WildShape Effect Macro" (with another name and also change on line 54).
You can remove and / or add different abilities that will be transferred to your new form during the polymorph:
keepPhysical: true
: Keep Physical Abilities scores (Str, Dex, Con)keepMental: true
: Keep Mental Abilities scores (Wis, Int, Cha)keepSaves: true
: Keep Saving throw Proficiency of the CharacterkeepSkills: true
: Keep Skill Proficiency of the CharactermergeSaves: true
: Merge Saving throw Proficiencies (take both) this will keep proficiencies of the character intact and also grant any extra proficiencies from the dragged on actormergeSkills: true
: Merge Skill Proficiency (take both) this will keep proficiencies of the character intact and also grant any extra proficiencies from the dragged on actorkeepClass: true
: Keep Proficiency bonus (leaves Class items in sheet) this will leave any Class "item" of the original actor in order to keep the original level and therefore Proficiency bonuskeepFeats: true
: Keep FeatureskeepSpells: true
: Keep SpellskeepItems: true
: Keep EquipmentkeepBio: true
: Keep BiographykeepVision: true
: Keep Vision (Character and Token) if you want to preserve the exact way a token has vision on the map, this will do that. It will also not change the characters senses in the character sheet
Q: I don't understand, I did all the steps one by one after installing the required modules and it still doesn't work, why?
A: It's necessary to have previously correctly configured these different modules for the correct functioning of the macro. It's also required to have checked the box "Auto apply item to targets" in the configuration of Midi-QOL module.
Q: I experience a slight lag when animating my character for the first time, why?
A: During the first animation, the image of the new token is loaded in your browser and may therefore take a little while before appearing. Unfortunately, there is nothing we can do at the moment.
Q: When the transfer effects takes place, the associated macro executions are launched. How to deactivate them?
A: There is currently no long term solution, although this issue is known and is being resolved. When the executed macros concern dialog box choices, simply close the window.
I plan to improve this macro to make it a module. This will allow a much easier installation and will also allow you to quickly create and configure different polymorphs (choice of skills to keep, the name of the macro, the name of the effect, the size of the characters, the animation type, etc.)