Tabletop RPG Toolset
Custom Lua/XML Engine & Game Automation Suite for Tabletop Simulator
Digital tabletop simulators provide physics and 3D space, but lack native mechanics for complex roleplaying systems. In 2019, I started building a dedicated Lua/XML automation system to eliminate manual record-keeping and speed up gameplay.
Over five years of continuous weekly playtesting, the toolset has evolved from basic dice and modifier scripts into an event-driven framework featuring condition tracking, dynamic turn queues, and combat resolvers.
I am currently developing a complete '2.0' architectural rewrite. The new iteration decouples game rules from Tabletop Simulator's proprietary UI runtime via a modular state-machine pattern, making it straightforward to adapt from D&D 5e to Daggerheart and custom rulesets.
Centralized event dispatcher managing decoupled subsystem communications, global hooks registration via Global.call, and isolated module execution.
Decoupled rule evaluation engine allowing support for both traditional D&D 5e mechanics and modern dual-dice narrative systems like Daggerheart without rewriting UI layers.
--- @class MonsterData
--- @field name string
--- @field hp string
--- @field ac string
--- @field mov string
--- @field size string
--- @field side string
--- @field image string (optional)
local Events = {}
-- Dictionary for possible events
Events.EVENT_NAMES = {
parse_monster_data = "parse_monster_data",
create_json_note = "create_json_note",
-- in development
player_hp_update = "player_hp_update",
monster_hp_update = "monster_hp_update",
monster_stress_update = "monster_stress_update",
ruin_update = "ruin_update",
on_roll = "on_roll",
create_monster = "create_monster"
}
--- Subscribes to an event.
--- @param eventName string The name of the event.
--- @param func string The callback function name.
--- @overload fun(eventName: "parse_monster_data", func: fun(data: MonsterData))
--- @overload fun(eventName: "create_json_note", func: fun(data: MonsterData))
function Events.subscribe(eventName, func)
Global.call("event_subscribe", {eventName = eventName, guid = self.getGUID(), functionName = func})
end
--- Broadcasts an event.
--- @param eventName string The name of the event.
--- @param ... any The event data.
--- @overload fun(eventName: "parse_monster_data", data: MonsterData)
--- @overload fun(eventName: "create_json_note", data: MonsterData)
function Events.broadcast(eventName, ...)
Global.call("event_broadcast", {eventName = eventName, args = {...}})
end
return Events