PDA

View Full Version : addHandler for 5e "battle"



lesliev
December 27th, 2025, 10:23
Hi people

This works great for watching for changes to an encounter window in 5e:


DB.addHandler("battle", "onChildAdded", recalcOpenBattles)
DB.addHandler("battle", "onChildDeleted", recalcOpenBattles)
DB.addHandler("battle", "onChildUpdate", recalcOpenBattles)


Or, more specifically what I am after:


DB.addHandler("battle.*.npclist", "onChildAdded", recalcOpenBattles)
DB.addHandler("battle.*.npclist", "onChildDeleted", recalcOpenBattles)
DB.addHandler("battle.*.npclist", "onChildUpdate", recalcOpenBattles)

DB.addHandler("battle.*.npclist.*.count", "onUpdate", recalcOpenBattles)
DB.addHandler("battle.*.npclist.*.link", "onUpdate", recalcOpenBattles)


However these handlers only fire for encounters I made myself, not for encounters I modify in a module.
How do I watch for changes to those?

Moon Wizard
December 27th, 2025, 18:13
Each record type can actually have multiple paths, depending on how it is exported, or how the ruleset divided up the data, or for legacy or other reasons.
You'll need to iterate over every valid base data path for the "battle" record type.

An example can be found, in CoreRPG - scripts/manager_story_index.lua - function initStoryRecordIndex, for the "story" record type.

Regards,
JPG

lesliev
December 27th, 2025, 22:55
Thank-you! This helps.

However, for some reason these events also do not seem to fire.

I can use Interface.onWindowOpened to see windows being opened.
When I open the Encounter window I want to watch for changes, the path reported by getPath is "battle.id-00015@Alagoran's Gem".

Trying to hook events on this path does not work - the events do not fire.

Using the code you pointed me to, I attempt to hook changes to every kind of "battle":



local function initBattleHandlers()
local tMappings = RecordDataManager.getDataPaths("battle")

for _, sMapping in ipairs(tMappings) do
local base = DB.getPath(sMapping)

Debug.print("initBattleHandlers mapping: " .. sMapping .. ", base: " .. base)

-- Watch creation/deletion
DB.addHandler(base .. ".*@*", "onAdd", recalcOpenBattles)
DB.addHandler(base .. ".*@*", "onDelete", recalcOpenBattles)

-- Watch NPC list changes
DB.addHandler(base .. ".*.npclist", "onChildAdded", recalcOpenBattles)
DB.addHandler(base .. ".*.npclist", "onChildDeleted", recalcOpenBattles)

-- Watch count + link changes
DB.addHandler(base .. ".*.npclist.*.count", "onUpdate", recalcOpenBattles)
DB.addHandler(base .. ".*.npclist.*.link", "onUpdate", recalcOpenBattles)
end
end


As before, this works great for Encounters I create, but they simply do not fire for Encounters in the "Algoran's Gem" module that I modify.
I have even tried the sledgehammer approach:



local function initBattleHandlers()
local tMappings = RecordDataManager.getDataPaths("battle")

for _, sMapping in ipairs(tMappings) do
local base = DB.getPath(sMapping)

Debug.print("initBattleHandlers mapping: " .. sMapping .. ", base: " .. base)

DB.addHandler(base, "onChildAdded", recalcOpenBattles)
DB.addHandler(base, "onChildDeleted", recalcOpenBattles)
DB.addHandler(base, "onChildUpdate", recalcOpenBattles)
DB.addHandler(base, "onUpdate", recalcOpenBattles)
end
end


Again, this fires events for my own Encounters, but not the encounters in the module.

The debug messages reveal that the paths being used are "battle" and "reference.battles":



[12/28/2025 11:48:14 AM] Encounter difficulty extension onInit
[12/28/2025 11:48:14 AM] initBattleHandlers mapping: battle, base: battle
[12/28/2025 11:48:14 AM] initBattleHandlers mapping: reference.battles, base: reference.battles


I guess this is something to do with the overlay records that sit on top of the module records - they are the records that are truly modified?
If that's the case, how do I detect the creation of those records and hook changes to them?

I'm so close!

Trenloe
December 27th, 2025, 22:59
Entries in modules have the module reference after the @ sign - as you can see in your example path "battle.id-00015@Alagoran's Gem". Without the @ reference you will just get events on the campaign data. So you must make sure your path includes @* at the end - you've done that for the first two handlers (onAdd, onDelete), but not for the rest.

lesliev
December 28th, 2025, 02:18
Awesome! I've gotten it working!

Here's an example:
https://youtu.be/m-kz4Hj_8gQ

Code here: https://github.com/lesliev/fantasy-grounds-encounter-difficulty-5e

The one thing I'm not so happy about is that when the Party changes the hooks to "partysheet.partyinformation.*" get called many times, and recalculate for all open encounters. I've made some attempts at debouncing this but since there seems to be no timer interface I can't easily do this. Maybe there's some other technique, or I need to make those hooks more specific.

But in any case, the party does not change often and the calculations are quite fast, so maybe it's fine.