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:
@@ -0,0 +1,20 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AchievementDefinition {
|
||||
pub id: String,
|
||||
pub title: String,
|
||||
pub description: String,
|
||||
pub icon: String,
|
||||
pub trigger: String,
|
||||
pub threshold: i64,
|
||||
pub reward_coins: i64,
|
||||
pub rarity: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, sqlx::FromRow)]
|
||||
pub struct PlayerAchievement {
|
||||
pub id: String,
|
||||
pub achievement_id: String,
|
||||
pub unlocked_at: String,
|
||||
}
|
||||
Reference in New Issue
Block a user