-
Notifications
You must be signed in to change notification settings - Fork 2
Reference Hooks
Every hook the engine calls, with the arguments a wrapper receives after
next, what it must return, and the call site. Read from the
Runtime.call sites in src/. Wrap with
mod.hooks:wrap(name, function(next, ...) ... end, priority); see
Concepts: Events and Hooks for chain order
and error isolation.
A wrapper's contract: call next(...) at most once, return the same shape
the vanilla function returns. next() with no arguments forwards the
current arguments unchanged.
| Hook | Wrapper receives (after next) |
Returns | Call site |
|---|---|---|---|
battle.accuracy |
ctx = { battle, ruleset, move, user, target, rng } |
true on hit |
src/battle/BattleState.lua (accuracyRoll) |
battle.damage |
ctx = { battle, ruleset, user, target, move, opts, rng } |
damage, { crit, typeMult } |
src/battle/BattleState.lua (computeDamage) |
battle.crit |
ctx = { ruleset, attacker, moveId, rng, highCrit } |
true on crit |
src/battle/Damage.lua |
battle.turn_order |
playerBattler, playerMove, enemyBattler, enemyMove, ctx = { rng, invertTie? } |
true when the player moves first |
src/battle/BattleState.lua; also src/link/LinkBattle.lua (must stay deterministic across peers) |
battle.enemy_action |
battle |
the enemy action |
src/battle/BattleState.lua (enemyAction — the whole AI choke point) |
battle.run |
ctx = { battle, pSpd, eSpd, attempts, rng } |
true on escape |
src/battle/BattleState.lua (runRoll) |
catch.rate |
ball, mon, def, opts = { rng, rateOverride, battle } |
caught, shakes |
src/battle/BattleState.lua (catchAttempt) |
trainer.party |
trainerClass, partyIndex, party |
the party list ({ { species, level }, ... }) |
src/battle/BattleState.lua (trainer battle setup) |
exp.gain |
ctx = { defeatedDef, level, isTrainer, participants, traded, mon } |
the exp number | src/battle/Experience.lua |
Example — a global difficulty scaler:
mod.hooks:wrap("battle.damage", function(next, ctx)
local dmg, info = next(ctx)
if not ctx.user.isPlayer then
dmg = math.floor(dmg * 1.25)
end
return dmg, info
end)| Hook | Wrapper receives | Returns | Call site |
|---|---|---|---|
evolution.check |
game, mon, evo, trigger |
true when the evolution fires |
src/pokemon/Evolution.lua (pendingFor) |
encounter.roll |
encDef, ctx = { mapId, terrain, rng } |
an encounter ({ species, level }) or nil
|
src/world/OverworldController.lua (rollEncounter) |
encounter.species |
enc, ctx |
the (possibly rewritten) encounter |
src/world/OverworldController.lua (after a non-nil roll, before repel filtering) |
encounter.fishing |
rod, mapId, candidates |
an encounter or nil
|
src/world/OverworldController.lua (goFishing) |
| Hook | Wrapper receives | Returns | Call site |
|---|---|---|---|
movement.collision |
allowed, ctx = { map, mover, dir, fromX, fromY, toX, toY, reason } |
boolean; may rewrite ctx.reason
|
src/world/Collision.lua (canMove) |
warp.destination |
destMap, x, y, ctx = { warp, lastMap, data } |
destMap, x, y |
src/world/Warp.lua (destination) |
map.palette |
name, map |
a palette name | src/world/OverworldController.lua |
| Hook | Wrapper receives | Returns | Call site |
|---|---|---|---|
music.select |
song, ctx = { reason, mapId, mapSong, onBike, surfing, kind, battleKind, trainerId } |
a song id |
src/core/Music.lua (selectSong — every song choice passes through) |
transition.style |
ctx = { trainer, stronger, dungeon, game } |
a transitions registry id |
src/render/BattleTransition.lua (an unknown style falls back to vanilla) |
render.zones |
game, zones |
the zones list |
src/core/Game.lua (palette-zone pass before the blit) |
| Hook | Wrapper receives | Returns | Call site |
|---|---|---|---|
script.command |
ctx, name, args |
the command's jump result (nil, a label, "end") |
src/script/ScriptRunner.lua (every script row) |
save.new_game |
save |
the save table |
src/core/SaveData.lua (New Game skeleton) |
ui.start_menu.items |
game, items |
the items list | src/ui/StartMenu.lua |
ui.party.submenu |
game, items, mon, ctx = { battle, overworld } |
the items list | src/ui/PartyMenu.lua |
ui.options.rows |
game, rows |
the rows list | src/ui/OptionsMenu.lua |
ui.pc.items |
game, items |
the items list |
src/world/OverworldController.lua (the PC menu) |
link.fingerprint |
data, mods |
the digest string |
src/link/Fingerprint.lua (compute) |
The four ui.* list hooks type-check the result: return anything but a
table and the vanilla list is kept with a logged error. Insert with the
mod.ui helpers rather than raw table.insert when position matters:
mod.hooks:wrap("ui.start_menu.items", function(next, game, items)
mod.ui.insertBefore(items, "QUIT", {
label = "QUESTS",
onSelect = function() mod.ui.push(game, "QuestLog") end,
})
return next(game, items)
end)Entries are { label, onSelect }; anchors are stable labels
("POKéDEX", "OPTION", "QUIT", ...), and a missing anchor appends.
Engine 1.0.0 · mod API 2 · repository · Discord
This project ships no ROM data. All game content is decoded on the player's machine from their own verified Pokemon Red ROM; mods ship recipes and original assets, never extracted content.
Start here
Concepts
Tutorials
- The ladder
- 01 Sprite and Text Tweak
- 02 Balance Patch
- 03 New Species
- 04 New Item and Ball
- 05 New Move
- 06 NPC and Dialogue
- 07 New Map
- 08 Quest
- 09 Custom Music
- 10 New Mechanic
- 11 Custom UI Screen
- 12 Mini Total Conversion
Reference
Guides
Playing