18 data-driven achievements (achievements.json) across 8 trigger categories: matches_played, matches_won, goals_scored, packs_opened, sbcs_completed, cards_owned, level, objectives_completed, drafts_completed. Rarities span common → epic. Coin rewards range from 500 (first_match) to 6000 (win_50). check_and_unlock() queries the relevant metric from existing tables, skips already-earned achievements via INSERT OR IGNORE, grants coin rewards, and fires a persistent notification per unlock. Trigger values are cached per call to avoid redundant DB round-trips for same-trigger achievements. Checks run automatically after every match result (all triggers), every pack open (packs_opened), and every successful SBC submission (sbcs_completed). GET /achievements returns all definitions annotated with unlocked/unlocked_at, plus earned and total counts. POST /matches/result response gains an achievements_unlocked array (empty when nothing new unlocked). AppState gains achievement_defs (Arc<Vec<AchievementDefinition>>) loaded from data/achievements/**/*.json at startup — same pattern as obj_defs. 5 new tests: list endpoint, first_match unlock, first_win unlock, no-dup guard, coin reward verification. Core now at 82 tests. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use crate::models::profile::LevelUpEvent;
|
||||
use crate::models::{achievement::AchievementDefinition, profile::LevelUpEvent};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
@@ -84,4 +84,6 @@ pub struct MatchRewardResult {
|
||||
pub season_end: Option<crate::models::season::SeasonEndSummary>,
|
||||
/// Non-empty when the player levelled up one or more times from this match's XP.
|
||||
pub level_ups: Vec<LevelUpEvent>,
|
||||
/// Achievements unlocked as a result of this match.
|
||||
pub achievements_unlocked: Vec<AchievementDefinition>,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user