Commit Graph

523 Commits

Author SHA1 Message Date
funman300 1f6994a084 feat(engine): add AchievementPlugin with persistent unlock tracking
On GameWonEvent, build an AchievementContext from StatsResource + GameState
+ wall-clock hour, evaluate ALL_ACHIEVEMENTS, flip newly-satisfied records
to unlocked, persist atomically, and emit AchievementUnlockedEvent for
each new unlock. AnimationPlugin's toast resolves the event's ID to the
achievement's display name via achievement_plugin::display_name_for.

Introduces StatsUpdate system set so AchievementPlugin can reliably run
after StatsResource reflects the win. AchievementPlugin::headless() used
in tests to avoid touching ~/.local/share/solitaire_quest/achievements.json.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-24 12:53:31 -07:00
funman300 4589c52368 feat(data): add AchievementRecord and atomic achievements.json persistence
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-24 12:51:15 -07:00
funman300 82fa584cbb feat(core): add achievement module with 14 unlock conditions
Introduces AchievementContext (stats + last-win snapshot), AchievementDef,
ALL_ACHIEVEMENTS, and check_achievements. Adds undo_count to GameState
so the no_undo and speed_and_skill conditions are evaluable.

Skipped achievements that depend on features not yet built:
daily_devotee (progress), comeback (recycle counter), zen_winner (modes),
perfectionist (max-score calc). They land in later phases.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-24 12:50:46 -07:00
funman300 b9957909b1 docs: mark Phase 3 and Phase 4 complete in session handoff
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-24 12:44:18 -07:00
funman300 2ce11f8f4d feat(engine): add StatsPlugin with persistent stats and toggleable overlay
StatsPlugin loads stats on startup, persists them on every GameWonEvent
and abandoned NewGameRequestEvent (>=1 move, not won), and provides a
full-window overlay toggled with `S` showing games played/won, win rate,
streak, best score, fastest win, and average win time.

The storage path is configurable via StatsPlugin::storage_path: the
default ctor uses dirs::data_dir(); StatsPlugin::headless() disables
I/O entirely so tests don't read or overwrite the user's real
stats.json. record_abandoned runs before GameMutation so it reads
move_count before handle_new_game clobbers it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-24 12:43:49 -07:00
funman300 5ced4c01ce feat(data): add atomic stats persistence (load_stats_from, save_stats_to)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-24 12:37:57 -07:00
funman300 f8cce2433d feat(data): add StatsSnapshot with update_on_win and record_abandoned
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-24 12:37:21 -07:00
Solitaire Quest bef7ab3c13 docs: add Phase 4 statistics implementation plan
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 12:29:03 -07:00
Solitaire Quest 4d2379c426 feat(engine): add AnimationPlugin with slide, cascade, and toast (Phase 3F)
- CardAnim component lerps cards from old to new position on every move
- card_plugin now adds CardAnim instead of teleporting cards on state change
- Snap-back on invalid drag reuses the same mechanism (StateChangedEvent)
- Win cascade flies all 52 cards off-screen with staggered delay on GameWonEvent
- Achievement toast scaffold wired to AchievementUnlockedEvent (Phase 5 content)
- Fix input_plugin test: click Queen's visible strip, not geometric centre

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 21:32:42 -07:00
Solitaire Quest a8a323c6c3 chore(deps): replace bevy_egui+bevy_kira_audio with bevy_ui+kira, drop AssetServer 2026-04-23 21:02:46 -07:00
funman300 b3646d6cad modified: solitaire_engine/src/card_plugin.rs
modified:   solitaire_engine/src/input_plugin.rs
2026-04-23 20:48:57 -07:00
funman300 900de7f376 feat(engine): add InputPlugin with keyboard and stock-click
Keyboard: U=undo, N=new game, D=draw, Escape=pause placeholder (logged
only until the pause screen lands). Mouse: left-click on the stock pile
fires DrawRequestEvent. Cursor coordinates are converted via the active
Camera2d's viewport_to_world_2d so the hit-test works under arbitrary
camera setups.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-23 16:26:40 -07:00
funman300 0a87f0f8f2 feat(engine): add CardPlugin with procedural card rendering
Each card is a parent Sprite (white for face-up, blue for face-down) with
a Text2d child showing rank+suit (e.g. "AH", "10C", "KS"). Hearts and
diamonds render red; clubs and spades black. Face-down labels are hidden.
Tableau cards fan downward; other piles stack at the same position with a
small z-offset.

Sync runs in PostStartup for the initial deal and in Update after every
StateChangedEvent. To avoid a one-frame lag, downstream plugins run after
the new GameMutation system set.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-23 16:22:49 -07:00
funman300 d92b4a8648 feat(engine): add layout, LayoutResource, and TablePlugin
compute_layout is a pure function that maps window size to card size and
the 13 pile positions, with clamping at the 800x600 minimum and seven
tableau columns horizontally aligned with stock/waste (cols 0,1) and the
four foundations (cols 3,4,5,6). TablePlugin spawns a 2D camera, a felt
background sprite, and 13 translucent pile-marker sprites, and
repositions them on WindowResized. Plugin registers WindowResized
explicitly so it works under MinimalPlugins in tests.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-23 16:18:24 -07:00
funman300 c393eab17d feat(engine): add resources, events, and GamePlugin event routing
Introduces the plumbing layer for Phase 3: GameStateResource wraps
solitaire_core::GameState, DragState tracks in-progress drags, and
SyncStatusResource holds runtime sync status. GamePlugin routes
Draw/Move/Undo/NewGame request events into GameState and emits
StateChangedEvent and GameWonEvent for downstream systems.

Also adds the Phase 3 implementation plan under docs/superpowers/plans/.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-23 16:15:38 -07:00
Solitaire Quest 3831fe691c docs: add session handoff document for Phase 3 continuation 2026-04-23 16:01:05 -07:00
Solitaire Quest b8dc7cb21c fix(core): remove stock_recycled limit, replace unwrap, snapshot on recycle, fix derives
- Remove stock_recycled field: recycling is now unlimited; StockEmpty only when both stock and waste are empty
- Replace all .unwrap() in draw() and move_cards() with .ok_or(MoveError::...)? propagation
- Push snapshot before recycling waste→stock so undo can reverse it
- Make StateSnapshot private (struct, not pub struct) and undo_stack private (not pub(crate))
- Add PartialEq, Eq to derives on both StateSnapshot and GameState
- Update draw_from_empty_stock_and_waste_returns_error test to use direct pile clearing since unlimited recycling means the old loop-based approach never reached both-empty

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 11:17:25 -07:00
Solitaire Quest 58f1465927 feat(core): add GameState with draw, move_cards, undo, win/auto-complete detection
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 11:13:49 -07:00
Solitaire Quest 43194b04ac fix(core): use StdRng doc comment, replace expect() with debug_assert in deal_klondike 2026-04-23 11:09:23 -07:00
Solitaire Quest 17bbec054c feat(core): add pile, error, deck, rules, scoring modules with tests
Implements PileType/Pile, MoveError (thiserror), Deck with seeded shuffle,
deal_klondike layout, foundation/tableau placement rules, and Windows XP
Standard scoring — 41 tests, clippy clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 11:07:58 -07:00
Solitaire Quest fcf878b403 feat(core): add Card, Suit, Rank types with tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 11:05:08 -07:00
Solitaire Quest f84d7c5849 fix(workspace): add derives/docs per code review, remove unused thiserror from solitaire_sync 2026-04-23 11:04:15 -07:00
Solitaire Quest 684f07746d feat(workspace): initialize all seven crates with stubs and blank Bevy window
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-23 11:00:42 -07:00