Phase 3: object tracker

Game-file footprint unchanged: still one line in main.lua.

Three-level navigation as specified in D2: Ctrl+PageUp/Down cycles categories,
PageUp/Down cycles kinds, Shift+PageUp/Down cycles instances of that kind, Home
announces where it is.

- categories.lua: flag-first classification into Creatures, Items, Alchemy,
  Interactables, Exits and Terrain features, reusing the game's own 'exit' tag
  and excluding the six nameless mechanical templates.
- tracker.lua: scan, cache, navigation, announcements.

Caching keys on level name, player position and turn: position because
everything sorts by distance to the player, turn because creatures move and
containers empty. Scans run on keypress, never per frame.

FoV is respected throughout -- creatures only when currently visible, tiles when
visible or remembered -- so the tracker never becomes a cheat.

Home reports path length alongside the offset, via the game's own
calculatePathFromPlayer. Straight-line distance is misleading indoors, where a
chest three tiles north through a wall can be twenty steps away.

Corrects D4 in docs/05-decisions.md. I had claimed tile.tryInteract cleanly
separates 'the player can do something here' from scenery. It does not: 'wall'
carries a flavour-only tryInteractFunction ('You touch the wall. It's cold and
rough.'), so every wall segment was promoted into Interactables -- 13 in the
starting house, burying the chest and cauldron. Structural tiles
(blocksMovement and blocksVision) are now excluded before the tryInteract test;
containers and furniture block movement but not sight, so they survive.
12 kinds/19 instances instead of 13/32.

13/13 testbed checks, history unit tests passing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Accessibility Mod
2026-08-01 21:34:13 +02:00
co-authored by Claude Opus 5
parent 8b1eb44c38
commit c17c5277d4
6 changed files with 687 additions and 7 deletions
+39 -2
View File
@@ -109,13 +109,50 @@ Every other template resolves to a name/description and lands in a category. **T
| `isPushable` / `isBreakableByHand` | 17 / 17 | Interactables |
| `isWallFeature` | 22 | Terrain |
Critically, `modules/tile.lua:234` defines the runtime flag as:
`modules/tile.lua:234` defines the runtime flag as:
```lua
self.tryInteract = (self.loot or self.tryInteractFunction) and function(self,actor) ... end
```
So **`tile.tryInteract` alone cleanly separates "the player can do something here" from scenery** — exactly the flag-first classification proposed in `03-implementation-plan.md`, and it's authoritative rather than heuristic.
> ### ⚠ Correction (found during Phase 3 implementation)
>
> I originally claimed here that **`tile.tryInteract` alone cleanly separates
> "the player can do something here" from scenery.** That is **wrong**, and the
> tracker was visibly worse for it.
>
> Plenty of templates carry a *flavour-only* `tryInteractFunction`. `wall` is the
> worst offender:
>
> ```lua
> wall = {
> name = "a stone wall",
> blocksMovement = true, blocksVision = true,
> tryInteractFunction = function(self, actor)
> console.print("You touch the wall. It's cold and rough.")
> return 1
> end,
> }
> ```
>
> So flag-first classification promoted **every wall segment** into
> Interactables — 13 of them in the starting house, burying the chest, cauldron
> and alchemy table under identical noise.
>
> `tryInteract` means *"something happens if you press interact"*, **not**
> *"this is worth walking to"*.
>
> **Fix:** exclude structural tiles — `blocksMovement and blocksVision` — before
> the `tryInteract` test. That is the solid shell (walls, rock); containers and
> furniture block movement but not sight, so they survive. Walls are already
> served by the wall tones and the surroundings scan.
>
> Result in the starting house: 13 kinds / 32 instances → **12 kinds / 19
> instances**, all of them things you would actually want to find.
>
> The flag-first *approach* still holds — it just needs this one structural
> filter ahead of it. The lesson is that flags encode what the game engine does,
> not what the player cares about.
**Residual hand-maintained list** is therefore small: the 6 exclusions above, the existing `exit` tag set (`way_out`, `building_door`, `horse`, `ladder_up`, `ladder_down`), and an ingredient-node list to split **Alchemy** out of **Items**. Dozens of entries, not hundreds — and new content in future versions falls through to a sensible default category rather than vanishing.