Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

6 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

HookRegistry

CI

A small, dependency-free event-hook dispatcher for Roblox combat and ability systems.

-- before — every new ability means editing every call site
if attacker:HasAbility("Thorns") then ... end
if attacker:HasAbility("Vampiric") then ... end
if attacker:HasAbility("LastStand") then ... end

-- after — call site is written once, never touched again
Registry:Fire("OnHitDealt", { attacker = plr, dmg = 25 }, gateResolver)

-- ability code is self-contained, added independently, anywhere
Registry:Register("OnHitDealt", "Thorns", function(ctx)
	-- reflect damage back
end)

What it does NOT do

This is deliberately unopinionated:

  • It does not know what an "ability" is, or how your game stores who owns one. You supply a gateResolver(gateKey, ctx) -> boolean function at :Fire() time that answers "should this entry run?".
  • It does not interpret your ctx table beyond one field: ctx._stop, which halts remaining entries for that fire call if set. Everything else in ctx (a cancel flag, a mutable damage number, whatever your event needs) is a convention you define for your own game.
  • It does not touch Roblox services, _G, or any specific data format. It's a plain Luau module with zero external dependencies.

This is a pattern extracted from a larger production combat system — the ability-specific logic (damage formulas, animations, specific ability names) was stripped out; what's left is just the dispatch mechanism.

Install

Via Wally:

[dependencies]
HookRegistry = "ou1ck5cop3/hook-registry@0.1.0"

Or copy src/init.lua directly into your project — it's a single file with no dependencies.

API

HookRegistry.new(hookNames: {string}?) -> Registry

Creates a new registry instance. Each instance has independent state, so you can run multiple registries (e.g. one for combat, one for UI) without them interfering.

Registry:RegisterHook(hookName: string)

Declares a new hook type. Calling it twice is a no-op. You can pass initial hook names to .new() instead of calling this separately.

Registry:Register(hookName, gateKey, fn, priority?)

Registers fn against hookName.

  • gateKey — passed to your gateResolver at fire time. Use "*" for entries that should always run regardless of what the resolver says (they never call the resolver at all).
  • priority — lower numbers fire first. Defaults to 50.

Registry:Unregister(hookName, gateKey, fn) -> boolean

Removes a previously registered entry. Returns whether one was found and removed. Useful for un-equip flows or hot-reloading.

Registry:Fire(hookName, ctx, gateResolver?) -> ctx

Runs every registered entry for hookName in priority order.

  • gateResolver(gateKey, ctx) -> boolean is called once per non-wildcard entry to decide whether it runs. This is where you plug in your own "does this player own this ability" check. Omit it and only "*" entries will fire.
  • Each entry runs inside pcall — an error in one entry is warn'd and does not stop the others.
  • An entry can set ctx._stop = true to halt the remaining chain cooperatively.

Example

See example/example.lua for a worked example with attacker-gated, victim-gated, and wildcard entries.

License

MIT — see LICENSE.

About

A dependency-free event-hook dispatcher for Roblox combat and ability systems

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages