diff --git a/AGENTS.md b/AGENTS.md index 79e9918..e020544 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,75 +3,61 @@ ## Overview Odin + raylib multilingual memory card game for learning Japanese (Hiragana/Katakana), Korean (Hangul), and Thai scripts. +Build and run: `odin run .` (check with `odin check . -vet`) + ## Current Status ### Completed -- [x] Created AGENTS.md project plan -- [x] Created src/ directory structure with subdirectories (themes, card, memory, screens, assets) -- [x] Separated themes into themes/themes.odin (colors, constants) -- [x] Separated card logic into card/card.odin (Card struct, rendering, click detection) -- [x] Separated memory game into memory/memory.odin (game state, logic) -- [x] Separated screens into screens/screens.odin (UI screens, buttons) -- [x] Added font loading in assets/assets.odin -- [x] Fixed card positioning (removed -40 offset that pushed cards off screen) -- [x] Fixed card selection (now allows selection in both WAITING and FIRST states) -- [x] Added flip animations for cards (visual feedback when flipping) -- [x] Improved visual styling (rounded corners, theme colors) - -### In Progress -- [ ] Fix Japanese font rendering (TTF loading fails in raylib with "Failed to process TTF font data") +- [x] Code split into modules under `src/` (themes, assets, audio, ui, card, memory, screens) +- [x] `.odin` files tracked in git (`.gitignore` used to ignore `*.odin`) +- [x] Real font: M PLUS Rounded 1c (Medium + ExtraBold, OFL) loaded via `LoadFontEx` with kana codepoints +- [x] Per-language themes (`themes.Theme`) with flag-based card backs: Japan (Hinomaru), Korea (Taegukgi), Thailand (Trairong) +- [x] Card flip animation: card art is rendered to textures and squashed horizontally, with lift, shadow and lighting; matched cards pulse +- [x] Sound effects synthesized in code (flip, match, mismatch, win, button click), no audio files needed +- [x] Japanese memory game: kana ↔ romaji pairs, 12 pairs picked at random from the 46 basic hiragana +- [x] ESC goes back one screen (quits from the main menu); no longer closes the app everywhere +- [x] Memory layout fixed (header and Back button no longer overlap cards), real shuffle, moves counted per pair +- [x] Win panel with Play Again / Menu ### Pending -- [ ] Add keyboard support for card selection (1-9, A-L, etc.) -- [ ] Add proper Japanese font that works with raylib -- [ ] Add Korean and Thai language support -- [ ] Add more animations (win screen, transitions) -- [ ] Add sound effects -- [ ] Add score tracking and high scores -- [ ] Add difficulty levels -- [ ] Test all changes work together +- [ ] Keyboard support for card selection (1-9, A-L, etc.) +- [ ] Korean memory game (needs a Hangul font, e.g. Noto Sans KR; theme and card back already exist) +- [ ] Thai memory game (needs e.g. Noto Sans Thai; theme and card back already exist) +- [ ] Katakana mode +- [ ] Screen transitions +- [ ] Score tracking and high scores +- [ ] Difficulty levels (number of pairs) -## Known Issues -1. **Font rendering**: Japanese characters show as "?" - raylib's TTF loader fails with CJK fonts. Current workaround: using default font with romanji characters (A, I, U, etc.) -2. **Keyboard support**: Not yet implemented +## Known Issues / Gotchas +1. **Font glyphs must be listed**: raylib only bakes the codepoints passed to `LoadFontEx`. Any new non-ASCII text needs its characters added in `assets.build_codepoints`, otherwise it renders as `?`. +2. **Old font failure root cause**: `DroidSansJapanese.ttf` was a GitHub HTML page saved from a blob URL, not a font. Always download fonts from raw URLs and check them with `file`. +3. **Thai shaping**: raylib does no text shaping, so Thai combining vowels/tone marks may be misplaced. Prefer standalone consonants on Thai cards. +4. `odin.json` is not read by the Odin compiler (imports use relative paths); it is only informational. ## File Structure ``` HanjaKanaMun/ ├── AGENTS.md # Project plan (this file) -├── README.md # Project overview -├── odin.json # Odin config -├── main.odin # Entry point only +├── README.md +├── odin.json # Informational only, not used by the compiler +├── main.odin # Window/audio setup, screen state machine ├── src/ -│ ├── themes/ -│ │ └── themes.odin # Color themes, styling constants -│ ├── card/ -│ │ └── card.odin # Card struct and rendering -│ ├── memory/ -│ │ └── memory.odin # Memory game logic -│ ├── screens/ -│ │ └── screens.odin # Screen management (main menu, language screens) -│ └── assets/ -│ └── assets.odin # Font loading, resource management -└── assets/ # Font files, images (future) - └── DroidSansJapanese.ttf +│ ├── themes/themes.odin # Theme struct, flag colors, Screen/Flag enums, layout constants +│ ├── assets/assets.odin # Font loading (codepoints), get_font +│ ├── audio/audio.odin # Synthesized sound effects, play() +│ ├── ui/ui.odin # Text helpers, buttons, hover cursor +│ ├── card/card.odin # Card struct, flag card-back art, face textures, flip animation +│ ├── memory/memory.odin # Memory game state, logic and drawing +│ └── screens/screens.odin # Main menu and language menus +└── assets/ + ├── MPLUSRounded1c-Medium.ttf + ├── MPLUSRounded1c-ExtraBold.ttf + └── MPLUSRounded1c-OFL.txt ``` -## Next Steps -1. **Font rendering**: Try alternative approaches: - - Use raylib's LoadFontFromImage to create font from bitmap - - Use pre-converted font files - - Use system fonts via platform-specific APIs - - Use a different font format that raylib supports - -2. **Keyboard support**: Add number/letter keys to select cards - -3. **Testing**: Verify all current changes work together - ## Notes - NO rm -rf or starting over - Make small, incremental changes -- Test after each change +- Test after each change (`odin check . -vet`, then run) - Keep existing functionality working -- All code is now properly separated into modules -- Game is playable with mouse (romanji characters display correctly) +- Commit after each working step diff --git a/src/audio/audio.odin b/src/audio/audio.odin index cbdc5b6..b3939c0 100644 --- a/src/audio/audio.odin +++ b/src/audio/audio.odin @@ -110,7 +110,7 @@ load :: proc() { {start = 0.00, freq = C5, duration = 0.40, amp = 0.18}, {start = 0.11, freq = E5, duration = 0.40, amp = 0.18}, {start = 0.22, freq = G5, duration = 0.40, amp = 0.18}, - {start = 0.33, freq = C6, duration = 0.90, amp = 0.2}, + {start = 0.33, freq = C6, duration = 0.90, amp = 0.20}, {start = 0.33, freq = E6, duration = 0.90, amp = 0.07}, }) sounds[.CLICK] = build(0.08, {{start = 0, freq = A5, duration = 0.06, amp = 0.15}})