mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 08:21:02 +02:00
89 lines
3.0 KiB
Markdown
89 lines
3.0 KiB
Markdown
# 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
|
|
|
|
```sh
|
|
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
|
|
|
|
```lua
|
|
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:
|
|
|
|
1. **Call `next` first, then decorate what comes back.** Build a fresh list
|
|
instead and every other mod's row disappears.
|
|
2. **Anchor on a stable label, not an index.** `insertBefore` appends when
|
|
the anchor is missing, so the row is always reachable even in a total
|
|
conversion that renamed `SAVE`.
|
|
|
|
## Exporting an API
|
|
|
|
```lua
|
|
mod.exports.countSeen = function(game) ... end
|
|
```
|
|
|
|
Another mod reads it as:
|
|
|
|
```lua
|
|
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.
|