# PocketDungeon — GameKit + SfxEngine + tutorial levels (2026-07-16/17)

User said "board ending on 112" → WiFi discovery to 192.168.68.112. Repo pull needed from personal remote. Infinite floors, tutorial-by-play intro.

## Repo layout & build
- `Apps/PocketDungeon/CMakeLists.txt`: standard `tactility_project`
- `main/CMakeLists.txt`: GLOB_RECURSE GameKit + SfxEngine via `../../../Libraries/...` + `REQUIRES TactilitySDK esp_driver_i2s esp_driver_gpio`
- `manifest.properties`: sdk=0.8.0-dev (bumped from 0.7 to match board OS 0.8.0-dev, else missing symbol dialog), platforms esp32,esp32s3,esp32c6,esp32p4
- GameKit: Loop, Scene (Tick, InputKind), Input keyToInput/gestureToInput/pointToQuadrant/attachInput, Grid, Draw, Prefs
- Personal remote `git.reynafamily.com/adolforeyna/tactility_apps` commit d976595 feat: add Pocket Dungeon prototype, pull via `git fetch personal && git merge personal/main`
- Build env pitfall: Hermes PYTHONPATH pollution → `PYTHONPATH='' python3 tactility.py Apps/PocketDungeon build esp32s3 --local-sdk` + `IDF_PYTHON_ENV_PATH=idf5.3_py3.9_env`
- Board shorthand .112: ping 192.168.68.112 OK (ESP32-S3 MAC 14:c1:9f:d1:56:90, os 0.8.0-dev), .111 also online. Install needs platform arg `esp32s3` else fails ELF not built for esp32. Dashboard port 80 `PUT /api/apps/install`, dev 6666.

## v2-v4: Intro + fullscreen + input + exit
- Engine enough for MVP: Loop/Scene/Input/Grid, but no scene-stack/tween/proc-gen depth.
- Fullscreen: firmware HideStatusBar flag ignored by ELF parser V1/V2 → cannot hide statusbar without firmware patch. Gain ~40-50px by not calling `tt_lvgl_toolbar_create_for_app`.
- Intro state machine AppPhase Intro/Playing/Paused, introContainer+gameContainer+pauseOverlay, attachInputToCurrent() re-attaches CLICKABLE|GESTURE_BUBBLE to root+active container, rulesBox non-clickable so quadrant taps bubble, start button stop_bubbling.
- Touch dead after fullscreen → fixed via attachInputToCurrent() pattern.
- Exit trap fullscreen → Q/ESC Cancel/Menu → tt_app_stop() (via TactilityCpp/App.h), X top-right 32x26 SYMBOL_CLOSE, long-press LONG_PRESSED → pause overlay (bg #000 OPA_70, box 180px Resume accent #365CF5 grey Exit #252836).
- See references/intro-fullscreen.md v3/v4.

## v5-v6: QVGA left info + right small icons (cramped fix)
User Jul 16: "Still not great layout. I think we can replace text button with icons small on the right, and have the information on the side left."
- mainRow flex ROW flex_grow 1 pad 4/8 column bottom 8, leftCol flex_grow 1 scrollable (pad_row 8 bottom 6), rightRail fixed 44px SIZE_CONTENT column center row 10.
- Left cards initially GOAL + HOW TO PLAY (2 cards), right icons Play 38 accent + Exit 30 muted (SYMBOL_PLAY/CLOSE radius s/3). Verified via /api/screenshot 6-7KB PNG, no max_width/scrollbar_mode (unexported → missing symbol blue OK dialog). SDK bump 0.7→0.8 fixed.
- Symbol hygiene: no `lv_obj_set_style_max_width`, no `set_scrollbar_mode`. Use PCT widths.

## v7: Tutorial levels instead of text explanations (Jul 17)
User: "Let's add three intro levels instead of explanations. First level present the player and the stairs. Next level the bat and the gold. Both with two rows and regular cols. Then go the current first level. So on the initial page, only keep the goal and the controls explanation."

Implementation:
- Model.h: `TUTORIAL_ROWS=2`, `renderRows` in DungeonState, `floor 0,1 = tutorial`, `displayFloor()` maps tutorial→1 real floor-1, `isTutorial()`, `tutorialId()`, `reset(seed, tutorials=true)` floor 0 if tutorials else 2, `generateTutorialFloor(tId)`, `clearAllTiles()`.
- Model.cpp:
  - clearAllTiles border walls
  - Tut0: renderRows=TUTORIAL_ROWS, hide rows >=3 walls, player {1,1}, stairs COLS-2,1 only
  - Tut1: + treasure {3,1} + bat {5,1} hp1 + stairs, no random walls
  - Real >=2: previous logic treasure [5][2+floor%2], walls, enemies scale realFloor=floor-1, best update only floor>=2, moveMonsters skipped when isTutorial
- Renderer.cpp: respects renderRows hides cells HIDDEN, board height = cellPx*TUTORIAL_ROWS centered -10 for tutorial, status "TUTORIAL X/2 HP Gold" message "TUT 1: Move to > @=you" / "TUT 2: Kill b, take $, use >"
- PocketDungeon.cpp: intro only GOAL + CONTROLS (2 cards), no HOW TO PLAY – symbols taught via play. Right rail 44px Play 38 Exit 30 stays.

Flow: Intro (Goal+Controls left, icons right) → Tut1 2×9 @→> movement → Tut2 2×9 @+b+$→> combat/loot → Real F1 infinite (displayFloor). Infinite floors – answer to user "how many floors" = infinite.

Verification: hermes-verify-tutorial*.py checks 2 cards, no HOW TO PLAY, tutorial gen, renderRows, TUTORIAL_ROWS, isTutorial, build package, nm -D 0 missing (89 undef). Installed 192.168.68.112 intro_tutorial.png 6.3K.

## Floors
Infinite unbounded, difficulty scales slime hp floor/3, extra enemy >=3. No win boss. User asked, answered infinite.

## Next todos
- Push to personal/main (requested earlier)
- True HideStatusBar via firmware patch AppManifestParsingV1/V2 flags
- Victory floor 10 boss + win screen
- Cell flash anim Attacked/Blocked via lv_anim (exported since 0.6)
