DexNav Example
A START-menu overlay listing every species in the merged dex with its seen/owned state, sortable, and publishing a small API other mods can call.
Persona: the Tool Builder. Consume the merged view, never require a
private module, expose a stable inter-mod surface. This example is the
reference for all three.
Try it
python3 tools/modkit.py validate mods/examples/example_dexnav --base imported
luajit mods/examples/example_dexnav/tests/example_dexnav_test.lua
Enable it (example_dexnav = true under mods in options.lua, or the
F10 manager), then press START → DEXNAV. Its two options live in the
manager's per-mod options pane.
What it demonstrates
| Seam | Where |
|---|---|
content.screens:register |
main.lua — a factory the engine instantiates by id |
hooks:wrap("ui.start_menu.items") |
main.lua — decorate, do not replace |
mod.ui.insertBefore |
main.lua — anchor on a label, not a row index |
mod.options:define / :get |
main.lua — auto-rendered rows in the manager |
mod.exports |
main.lua — the inter-mod API |
content.pokemon:each |
main.lua — the whole world, engine records included |
Reading, not reaching
Every fact this mod displays comes from two public sources:
mod.content.pokemon:each()— the merged species view. A tool that hard-codes 151 breaks the moment another mod registers a species; this one just gets longer.game.save.pokedex— the seen/owned tables, handed in by the engine.
No require("src.pokemon.…"), no permission declared, nothing that a later
refactor of an engine module can break.
Anchoring a menu row
mod.hooks:wrap("ui.start_menu.items", function(next, game, items)
local out = next(game, items)
if type(out) ~= "table" then return out end
return mod.ui.insertBefore(out, "SAVE", { label = "DEXNAV", onSelect = ... })
end)
Two rules, both load-bearing:
- Call
nextfirst, then decorate what comes back. Build a fresh list instead and every other mod's row disappears. - Anchor on a stable label, not an index.
insertBeforeappends when the anchor is missing, so the row is always reachable even in a total conversion that renamedSAVE.
Exporting an API
mod.exports.countSeen = function(game) ... end
Another mod reads it as:
local nav = mod.find("example_dexnav")
if nav then print(nav.exports.countSeen(game)) end
mod.find returns nil when the other mod is absent, disabled, failed, or
has not run yet — so a dependent degrades instead of crashing. Declare it
in optional_dependencies if you can live without it and dependencies
if you cannot.
Empty state
SHOW UNSEEN off on a fresh save means an empty list. ListMenu draws
Nothing here. and B still exits — never a blank frame with no way out.
The title still reports DEXNAV 0/0, so the screen explains itself.
Credits
- pret/pokered — the START-menu layout the row is anchored into.