More Hooks 5e
https://github.com/ElfFriend-DnD/foundryvtt-more-hooks-5e
I'm going to try to use this library to hammer out the desires and API for a set of hooks to add to the system itself, I'm very keen on getting feedback about this.
The hooks are named after the method which they are patched into, and here's the initial list:
-
Actor5e.rollAbilitySave
- Actor5e.rollAbilityTest
- Actor5e.rollDeathSave
- Actor5e.rollSkill
- Item5e.roll
- Item5e.rollAttack
- Item5e.rollDamage
- Item5e.rollFormula
- Item5e.rollRecharge
- Item5e.rollToolCheck
I intend to be releasing some small modules coming up which leverage these hooks to do very specific things with the item usage workflow. It's my hope that in time whichever hooks of these prove useful will be added to the core system itself.GitHub
GitHub - ElfFriend-DnD/foundryvtt-more-hooks-5e
Contribute to ElfFriend-DnD/foundryvtt-more-hooks-5e development by creating an account on GitHub.
24 Replies
At the moment, each hook is injected via libwrapper into the method the hook is named after, and each one runs after the operation itself completes.
These are all called with
callAll
, and they all fire on every connected client.
Version 1's goal:
Enable modules that want to react to an operation's outcome to do so without relying on chat messages.My own first usage: - When an attack roll is rolled, check if the targeted tokens are hit. If so, notify the GM. 15 minutes in, already have feedback: Need the originating user's ID somewhere on these hooks. Else I can't get the user's targets.
and each one runs after the operation itself completes.Would it be a good idea to add a
pre
version of each hook as well?Having a series of
pre
hooks that are called after the roll data is prepared but before the roll is executed would probably be helpful, maybe as a call
with the option to cancel the rollI think yes, definitely feels like a useful thing.
Here's a sample project leveraging this library:
https://github.com/ElfFriend-DnD/foundryvtt-attack-roll-check-5e
Still a fair bit to wire up inside the chat card, but the hook makes this work in a clean, logical, expected way.
GitHub
GitHub - ElfFriend-DnD/foundryvtt-attack-roll-check-5e
Contribute to ElfFriend-DnD/foundryvtt-attack-roll-check-5e development by creating an account on GitHub.
Potentially being able to mutate the roll in the pre hook? It's going to be a bit limited since you'll presumably need to keep things synchronous (so no additional dialog prompts), but could be handy for e.g. "+X against giants" (check user's target, add X to roll if target is a giant)
I'm also unsure if having a
local
as well as a global
hook for each of these is worth it.
For the pre
hooks for instance, running those through the socket wouldn't be terribly useful without also having a way to talk back to the original client (which is just a nightmare to think about).
So pre would have to happen locally.
But would it also make sense to have a Item5e.roll.local
?
I'm not convinced, because a module could easily do
On the other hand...
A module could also 'easily' DIY the socket thing, and making all hooks local all the time would drastically reduce the complexity here.
With core hooks, yeah everything must be Synchronous.
But a pre
hook would adopt the same syntax Core uses of return false and the rest of the function stops
I think.
So if you really had to do something asynchronous, you could escape hatch out of the hook and DIY the rest of the flow. Not ideal, but ... yeah, I see how that can be an option, but... Ew. I might need to make a module that does that just to hide away the ugly bit 😛
I haven't looked too hard at warpgate's event system, but I suspect such a thing could be used to great effect here.
However, it's much more likely I can get these changes into the core system if I adopt the same patterns core uses.
I just have to pop in to say thank you @calego more hooks in D&D 5e has definitely been something I've passively wanted for a while.
oh interesting
I think italicized thanks don't trigger the bot
thanks @calego
@lukeabby gave LeaguePoints™ to @calego (#1 • 1238)
sorry for double ping huh
Additionally I think if
Item5e.rollDamage
is added it probably makes sense for a Actor5e.takeDamage
or some equivalent
Midi-Qol amongst others like to modify how damage is taken based upon resists etc. and since that's per actor I don't think you could do that while rolling damagetakeDamage
is an interesting ideaA good use case is if you have your item roll 5d6 fire damage for a total of 16 you can't just halve it unless all of your targets have resistances
I would probably hook into
Actor5e.applyDamage
right applyDamage might be a better name in that case
there's a method, it's what's used by core when you right click on a damage roll
I don't think I follow your use case here
You'd do something like:
?
Mmm this would need a
pre
hook actuallyYeah
Wouldn't work on Item5e.rollDamage
makes sense makes sense
because you can have multiple targets with different resistances
yeah
Mmk. I guess next on my todo list is figuring out pre hooks!
I ❤️ Midi for all it does, but it's just too big a module for me to want to use in the games I don't GM.
My players-turned-gms are real bad at configuring things, so my endgame here is to have a bunch of tiny modules that do little pieces of Midi's workflow.
Oh you can export the workflow as a JSON then import it
but yes it is absolutely a huge module
I've been exporting it from a game where I've gotten it nicely configured and importing it into other games
Any useful damage hooks will need to involve a new damage method, because the current one only takes damage and a multiplier
Maybe something like a new
autoApplyDamage
method that takes an array of damages with types and determines the multiplier automaticallySomething MRE does which is one of its few claims to fame is split the damage card up by damage formula rolled and allow each individual formula to be applied. I think such a change would be a good one for the core system as well, it would at least help with this.
I've reached a conundrum.
Initially I thought the "run every hook on every client" was a good play.
A GM only operation can happen on the GM's hook subscription.However, I'm coming to realize that some things I want to happen require more input on the user's which causes the trigger's side first. So now I'm wondering if these hooks should all occur on the client-side, and expect modules to handle their own socket implemention. Two examples (as they're the only two I know of using this lib so far): Attack Roll Check works when the GM client hears an Attack Roll hook. It then looks up the user's targets with
game.users.get().targets
, and does the monster math. At the end the GM creates a whisper to itself with the results.
This works pretty nicely, though it would also be easy to switch this around to have every client listen for its own attackRoll, then do the math on that client and broadcast a message to the GM from that client. (I wasn't able to make a truly invisible chat message from a different user to the GM when I tried before but I'm sure it's possible). This implementation would not require sockets at all (assuming the chat message problem is solved).
----
Auto NPC Save is prooving to be much more intricate.
On the surface it's identical to the other, except firing on Item roll instead of attack roll.
But the intricacies come with measured templates. I'd like to support using a module like DF QOL's "Auto target on template drop" to get the targets before doing the saves.
The hooks surrounding measured template placement don't get broadcast to all clients though, so that event sequence is much harder to detect on the GM client.
For the Auto NPC Save, I would potentially need a socket event to fire when "Targeting is done" to mark when targets are available, and also allow the GM client to start prompting for saves (with core there's no fast forwarding of these saves). It wouldn't do for the player to be prompted for the NPC saves.
The more I think about it, the more it makes sense to make the library consumers handle their own sockets, instead of having one in the library and also possibly needing them in the consumers.
I've slept on it, and asked in dev-support about the way core thinks about hooks. I've decided to un-do the socket implementation in more-hooks-5e.
This is going to be a breaking change, but we're still in pre-1.0.0 so I'm only bumping the minor version.
I suspect this will be low impact as long as I update the only module i know of which relies on these hooks beforehand.
<#901108458944876584> <- mothership thread if anyone's interested