#3D Polymorph
1 messages ยท Page 1 of 1 (latest)
thx
that is target? i thought target is the actor that you drag onto the actor you want transformed?
..you may be right
then we do want to modify the data but we can not update it as it's just an object so manual setting it is
Hooks.on("dnd5e.transformActor", (actor, target, data, options) => {
let 3dmodel = target.data.token.getFlag("levels-3d-preview","model3d");
if (3dmodel){
data.token["levels-3d-preview"].model3d = 3dmodel
}
});
so as the actor is temporary we are sure it is unlinked
correct?
which is why we are allowed to use data.token
um no?
no data isn't a document
or an actor
it's an object
/**
* A hook event that fires just before the actor is transformed.
* @function dnd5e.transformActor
* @memberof hookEvents
* @param {Actor5e} actor The original actor before transformation.
* @param {Actor5e} target The target actor into which to transform.
* @param {object} data The data that will be used to create the new transformed actor.
* @param {TransformationOptions} options Options that determine how the transformation is performed.
*/
I have the doc open
@floral swan do you want the model of the original actor or the model of the target?
because that might need to be swtiched
probably should be target here
ye
i want to polymorph it to the target, as if I dont switch anything up, it currently takes the actors model as base value anyways
gotcha
I still think you need to modify the resultant token not just the prototype of the created actor
(which looks pretty funny as it can appear to change in size by quite a bit if the polymorphed creature is alot bigger. so same model just scaled)
hmmm... how would i get access to the resultant token through the hook?
... good point
I don't think you can
i mean. I can probably set a temporary flag and scan everything for the flag
ye
but that would be really inefficient
tiem to log the hooks
but are you sure that it isnt enough to set the prototypes model?
try it
What I expect to currently happen is that you normally set the flag on the prototype, this makes a token with that flag which is then used to display the model
If you update an actor you aren't changing the token hence the image doesn't change
target.data.token is the prototype token and every actor has a prototype token
aah okay
target.token is the token for unlinked tokens, and null for linked ones
so in general just always use target.data.token to be safe?
?
If you want the prototype sure. But you usually want the token
not the prototype
yeah because you aren't modifying the actual token
I have an idea htough
Hacky idea to modify the resultant token, has the potential to break if multiple people are doing it:
Hooks.on("dnd5e.transformActor", (actor, target, data, options) => {
let 3dmodel = target.data.token.getFlag("levels-3d-preview","model3d");
if (!3dmodel) return
foundry.utils.setProperty(data.token, "flags.levels-3d-preview.model3d", 3dmodel); //modify the prototype of the resultant actor
Hooks.once("preUpdateToken",(tokenDoc,changes,context,userID)=>{ // Hook onto the next token update (since dnd5e updates the regular image) and update the flags of the resultant token doc to have the model
tokenDoc.data.update({"flags.levels-3d-preview.model3d":3dmodel}) // document.data.update is only a client side change but since we are hooking onto preUpdate, ergo before the doc is updated this is what we need.
})
});
let 3dmodel = target.data.token.getFlag("levels-3d-preview","model3d");
if (3dmodel){
data.token["levels-3d-preview"].model3d = 3dmodel;
}
console.log("polymorph log");
});```
does not even display polymorph log in the console
That's gonna error
foundry.utils.setProperty(data.token, "flags.levels-3d-preview.model3d", 3dmodel);
mine?
All of them ๐
what does it say if the console.log("polymorph log") never appears in the console?
errors beforehand zhell already pointed out why
ooooh
Try this:
Actually updates the token
well
changes the data of the next updated token's update which is nearly guaranteed to be the relevant one
Could use Hooks.on and Hooks.off... ๐ or be smart and use preUpdate
That's why I am using preUpdate
so it executes client side
and there's no such issue
*executes on the client updating the token
Either way I expect this to function
is the original program also a client?
?
I'm selfhosting, so if I as gm use it, it works the same way?
oh, urgh
I did not consider that
transformActor doesn't pass the user id either...
Okay yeah no idea
I suppose we could make the entire thing gm only
Get the active player owner, if any?
doesn't work for preUpdate, ust going with gm only
and then regular update
Hooks.on("dnd5e.transformActor", (actor, target, data, options) => {
if (!game.user.isGM) return
let my3dmodel = target.data.token["levels-3d-preview"].model3d
if (!my3dmodel) return
foundry.utils.setProperty(data.token, "flags.levels-3d-preview.model3d", my3dmodel); //modify the prototype of the resultant actor
Hooks.once("updateToken",(tokenDoc,changes,context,userID)=>{ // Hook onto the next token update (since dnd5e updates the regular image) and update the flags of the resultant token doc to have the model
tokenDoc.setFlag("levels-3d-preview","model3d",my3dmodel)
})
});
This should work
what does gm only mean in this context?
(game.user)
non pre hooks execute on all clients. pre hooks only execute on the client applying the change. Filtering there is needed so just executing on the gm client is a decent way to ensure we only execute on one client
unless you have multiple gms at which point I'll probably just give up ๐
anyhow try this and if it fails give me the error
thank you btw
Now THIS might run into issues if you multiple people edit their tokens at the same time or your server is very slow
I don't think there's a way around that though
If you want to write that go ahead. I am honestly not sure how to go about it
(ping me if there's an issue going back to studying otherwise)
@minor rapids okay, now i am confused. seemingly the world script doesnt even fire
i did a console.log right below the Hooks.on, and can see no log
errors in the console?
Scroll way up
yep syntax error
interesting
where do you find the syntax error?
in the console
ah. i reloaded, then it came
first time, the console already seemed too full and cut it
Welp I don't see it
what does Uncaught SyntaxError: Unexpected strict mode reserved word mean?
that is a javascript internal word? o.O
Variable names starting with a number...?
oh
oh that could be
Do you see the errant comma / missing bracket?
Because I don't
oh ffs
curlys
Alright fixed!
damn ๐
but hey, atleast i noticed it aswell^^
Unable to replicate but it does error
it does? o.O
Works!
Actually let me put in more safety
Yep works
Hooks.on("dnd5e.transformActor", (actor, target, data, options) => {
console.log("transformed")
if (!game.user.isGM) return
let my3dmodel = target.data.token["levels-3d-preview"]?.model3d
if (!my3dmodel) return console.log("Could not find model")
foundry.utils.setProperty(data.token, "flags.levels-3d-preview.model3d", my3dmodel); //modify the prototype of the resultant actor
Hooks.once("updateToken",(tokenDoc,changes,context,userID)=>{ // Hook onto the next token update (since dnd5e updates the regular image) and update the flags of the resultant token doc to have the model
tokenDoc.setFlag("levels-3d-preview","model3d",my3dmodel)
})
});
This gets to yelling at me about the model not being found which makes sense since I don't have 3dcanvas
Steps to reproduce. Put as an active worldscript, reload to refresh the code, transform an actor, see the console.log triggering -> works until then
active worldscript means " esmodules": ["./polymorph.js"], in case that the script is called polymorph.js in the main folder of the world, right?
I haven't messed with my world.json in a while but yeah that looks correct
Make sure you save your file after editing it^^
i did. otherwise i couldnt have had the 3dmodel error aswell
so i'm pretty sure that part is correct
Well I am unsure what to tell you as it seems to function as well as possible on my end
oh. what would happen if other modules might use the same hook?
works fine
Literally every script based module uses hooks in some capacity. If you couldn't share them nothing would work
@minor rapids which foundry version are you on? I'm still using v9 as quite a few modules arent upgraded to v10 yet
maybe it has something to do with that
Also v9
Let me update my 5e to be doubly safe
Same result as before
If you aren't even getting the first console.log() your worldscript isn't loaded
Wait, where is the log?
In the hook
.
Ah, goodie
@floral swan Have you restarted foundry yet? json changes are only registered on restart
Okay yeah I can reproduce the previously mentioned behaviour (getting the console.log on actor transform) with 0 modules and the foundry and dnd5e version posted above so it works for as much as I can test
currently restarting
I deactivated all the modules except 3dcanvas and still cannot find the log. it definitely loads though as I did also test it with a console.log before the hook, and that is displayed
3dcanvas+dependencies
Logged in as GM, I assume?
as gm, on the client itself. maybe i should try to log in as a player via browser
you are trying to transform an actor though right?
(also testing as a gm btw)
ooooh
so there is a difference between dragging it from the tokens character sheet and the general actors character sheet
first one does nothing
I mean yeah you need to trigger a transform
yeah, but i thought in order to transform a token, one would rather drag it to the tokens character sheet
so does it work?
it says it couldnt find the model
damnit
on the actor. and does nothing when executed on the token
Hooks.on("dnd5e.transformActor", (actor, target, data, options) => {
console.log("transformed")
if (!game.user.isGM) return
let my3dmodel = foundry.utils.getProperty(target.data.token,"flags.levels-3d-preview.model3d")
if (!my3dmodel) return console.log("Could not find model")
foundry.utils.setProperty(data.token, "flags.levels-3d-preview.model3d", my3dmodel); //modify the prototype of the resultant actor
Hooks.once("updateToken",(tokenDoc,changes,context,userID)=>{ // Hook onto the next token update (since dnd5e updates the regular image) and update the flags of the resultant token doc to have the model
tokenDoc.setFlag("levels-3d-preview","model3d",my3dmodel)
})
});
Try this?
if that still can't find the model my only guess is that the flag path is incorrect which I can't check
Is target.data.token defined?
target is the actor target.data is the actor's data and target.data.token is the prototype token. If the prototype token is undefined I am very concerned but lemme check
target.data.token is very much defined for me
yep which I can't check
going with getProperty here to minimize any syntax weirdness I might have made
Did you try console.log(target) to make sure it's all there?
I just logged target.data.token on my end
and it is the expected prototype token
Code used to test:
Hooks.on("dnd5e.transformActor", (actor, target, data, options) => {
console.log("transformed")
console.log(target.data.token)
if (!game.user.isGM) return
let my3dmodel = foundry.utils.getProperty(target.data.token,"flags.levels-3d-preview.model3d")
if (!my3dmodel) return console.log("Could not find model")
foundry.utils.setProperty(data.token, "flags.levels-3d-preview.model3d", my3dmodel); //modify the prototype of the resultant actor
});
so. the current thing is as follows: if i execute it on the actor sheet, it creates a new polymorphed actor sheet, and if i drag that to the scene, it works
That makes sense as we are updating the prototype
so the Hooks.once isn't working
Or rather not modifying the relevant token
rather not triggering at all
as in that case, the "transformed" log is also nowhere to be found
Race conditions if I had to guess. The transformation and token update is done before the hook call
And considering we don't have access to the resultant actor I don't think I can fix this
but wouldnt console.log still trigger?
which?
console.log("transformed")
if that isn't triggering then your worldscript isn't triggering which brings up back to the previous point of an incorrectly setup worldscript
or does "transformActor" actually never happen in terms of hooking when using a token?
I don't follow
If you actually transform an actor the hook should fire
the "transformed" console log message only triggers if using the polymorph on an actor prototype sheet
if you use the sheet from a token
the hook seemingly doesnt trigger
The function that transforms the actor is the also the one calling the hooks in this case. If one happen, they should both happen.
dnd5e transformation only works for linked actors if that is what you mean
oooooh. npcs cant polymorph?
the restore only works on linked
but, take a very very close look at the order of operations inside the function itself:
Hooks.callAll("dnd5e.transformActor", this, target, d, {
keepPhysical, keepMental, keepSaves, keepSkills, mergeSaves, mergeSkills,
keepClass, keepFeats, keepSpells, keepItems, keepBio, keepVision, transformTokens
});
// Create new Actor with transformed data
const newActor = await this.constructor.create(d, {renderSheet: true});
// Update placed Token instances
if ( !transformTokens ) return;
const tokens = this.getActiveTokens(true);
const updates = tokens.map(t => {
const newTokenData = foundry.utils.deepClone(d.prototypeToken);
newTokenData._id = t.id;
newTokenData.actorId = newActor.id;
newTokenData.actorLink = true;
return newTokenData;
});
return canvas.scene?.updateEmbeddedDocuments("Token", updates);
oh that's useful
so, hook is called, where you can modify any data that is about to be produced, then the actor is created after that, then IF we are transforming the token, the token update is fired after that with predefined token data from d.prototypeToken
So it should work correctly no need for token update calls
so this would suffice
provided the hook fires which only happens for linked actors
yep hook does not fire for unlinked tokens
is that a bug or is that intentional?
ok i see, yea, just above this call --
// Update unlinked Tokens in place since they can simply be re-dropped from the base actor
if ( this.isToken ) {
const tokenData = d.prototypeToken;
delete d.prototypeToken;
tokenData.actorData = d;
return this.token.update(tokenData);
}
dunno either way try if it works for linked
so if its unlinked, the token is simply updated with new stuff (since we can't revert it)
right but the hook isn't called ๐ฆ
which is the isuse here
yes, i was just confirming that myself ๐
Right so my job here is done then
The prototype update apparently works if I understood the comment correctly, hooking onto unlinked transform ain't doable so the script works as well as it can
d.flags.dnd5e.isPolymorphed = true;
you can probably have an update token hook monitoring for unlinked token updates which set that value on the instanced actor
which would be in its actorData
I don't have access to the target token then though
which makes that a moot point as we don't know what data to set
target token? that is the token being updated, right?
target is the actor we update TO
we want the prototype token settings of that actor
to get the value we need to set on the new token
so if I drag a bear on a druid the bear is the target
ok, so that should be present in the actor link update for the token?
nooo, because it wouldnt change the source actor, that's the whole point of the unlinked nonsense
yep
SOoooooooo
@floral swan sorry for the non-ideal solution can't think of a better way and I would like to be done with this chapter by midnight so Imma peace out
you could always get REAL ballsy and use the experimental warpgate "powermorph" approach
that does what? ๐
but...that's kind of like saying that we can fix your car's issues by just having you drive in reverse
technically a solution, but probably not what the goal was, lol
okay ^^
in this case, helps mutate and revert actors regardless of link state
if you are curious, but i do not want to invalidate the time these lovely boys have spent doing it the "core" way
https://ko-fi.com/post/Powermorph-Phase-3-Transform-ALL-the-Things-Z8Z67WTTM
blame dnd5e for their imperfect morphing ๐ /s
Honestly the next step here would be to submit a PR with better morphing but that's a multiple days project
๐
there is one more interesting thing I noticed
restore transformation does not take into account the height of a token, which in a 3d map means said tokens clips into the ground
Well I have bad news
there is no hook for transformation reversion
oooof
So you are out of luck there
open a github issue about respecting height? or is there a reason why it isnt considered?
Next step here is the aforementioned PR or wrapping the transformation function in a module, likely using libwrapper. Also a multi day project
not a core thing
oh right
and other flags might not want to be reverted
height isnt core
sometimes i forget that height itself isnt core.
- make a pr with better transformation logic directly to the repo (insanely complex ask @fair salmon he knows)
- Make a module to wrap the transformation function to do what you want. Still a multi day project but doable
Yell atKindly ask ripper to make a new module that deals with this
- Tune the experimental powermorph script to your use-case needs and use that will full control ๐
hmm I'd swap this with point 3
to keep the order of complexity
okay damn it I need to get this done. Quitting discord for now
Kindly ask ripper to make a new module that deals with this
He understandibly wont open this can of worms as all systems want their own polymorph handling then
What a journey. ๐ฟ
this group's general lack of familiarity with ripper mods didnt help
and the fact that we never use the polymorpher because warpgate.
you polymorph with the powermorph script or what?
Made my own. ๐
so everyone here has his/her own polymorph script? ๐
should also start writing one for good measures then
My use case is a bit special. I got a wizard who likes to cast polymorph, but I don't want a lot of actors in the sidebar. So I wrote a script to get actor data from a compendium, just by inputting a name, and did a whole lotta shenanigans to make it work without destroying the target actor. Recommended? Not at all. Works flawlessly? You betcha.
It's "only" 55 lines
not bad, powermorph v3 is just over 100
Oh it's both mutate and polymorph ๐
Luckily I don't have a druid, I haven't made a default config for it for wildshape.
I am sure you learned something along the way^^
I haven't had to deal with it yet soooo
Back to your books. ๐งน
Yes dad...
or no dinner
๐ ๐ ๐
already ate bro
Also I am done now
But yes Zhell isn't the best person to ask when you wonder how many custom things you should write
Perspective might be a little skewed there ๐ ๐
If I didn't write it myself, how can I trust it
I already wrote quite a few sequencer macros for making 2d animations for most of my spells (atleast all those that are too complex for automated animations). I am currently thinking about doing the same for 3d particle animations for the 10% of scenes where I provide 3d maps
still not 100% convinced of the effort to value ratio
3d particle macros that only get used in 10% of the maps (i will still use 2d 80-90% of the time as making all maps 3d is just unfeasible work if you actually also want to play dnd) might be even worse value^^
@fair salmon where does powermorph store the original actor data to restore it?
on the token's actor
i just now saw this thread, even though i followed @floral swan efforts in my server: a couple of notes:
@fair salmon what needs to be done, in a nutshell, is transfer the flags from the polymorph token to the polymorphed token bacuse 3DC uses it's own flag for the token 3d model
Also, ELEVATION is a core field, if Pholymorphing is resetting that it should be definatelly be handled by core dnd5e and an issue should be opened. You are confusing token height (wall height) with Elevation here i think
Transfering flags during a polymorph could also be a reasonable feature request
does this still exist? ๐
<insert necromancy art>