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
Calego
Calego3y ago
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.
Ikaguia
Ikaguia3y ago
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?
arbron
arbron3y ago
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 roll
Calego
Calego3y ago
I think yes, definitely feels like a useful thing.
Calego
Calego3y ago
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.
BadIdeasBureau
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)
Calego
Calego3y ago
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
Hooks.on('Item5e.roll', (_item, _chatMessage, _actor, {userId}) => {
if (userId !== game.user.id) return;
// otherwise do stuff
})
Hooks.on('Item5e.roll', (_item, _chatMessage, _actor, {userId}) => {
if (userId !== game.user.id) return;
// otherwise do stuff
})
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 itssomething
BadIdeasBureau
... 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 😛
Calego
Calego3y ago
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.
LukeAbby
LukeAbby3y ago
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
Leo The League Lion
@lukeabby gave vote LeaguePoints™ to @calego (#1 • 1238)
LukeAbby
LukeAbby3y ago
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 damage
Calego
Calego3y ago
takeDamage is an interesting idea
LukeAbby
LukeAbby3y ago
A 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
Calego
Calego3y ago
I would probably hook into Actor5e.applyDamage
LukeAbby
LukeAbby3y ago
right applyDamage might be a better name in that case
Calego
Calego3y ago
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:
Hooks.on('Actor5e.applyDamage', (actor, damageTaken) => {
if (damageTaken.type === 'fire' && actor.resistances.includes('fire') {
return damageTaken / 2
}
})
Hooks.on('Actor5e.applyDamage', (actor, damageTaken) => {
if (damageTaken.type === 'fire' && actor.resistances.includes('fire') {
return damageTaken / 2
}
})
? Mmm this would need a pre hook actually
LukeAbby
LukeAbby3y ago
Yeah Wouldn't work on Item5e.rollDamage
Calego
Calego3y ago
makes sense makes sense
LukeAbby
LukeAbby3y ago
because you can have multiple targets with different resistances yeah
Calego
Calego3y ago
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.
LukeAbby
LukeAbby3y ago
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
arbron
arbron3y ago
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 automatically
Calego
Calego3y ago
Something 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