From 4f027470642fbc6d8b9d4aabbdc12a576eefd866 Mon Sep 17 00:00:00 2001 From: Hikari Date: Sun, 25 Jan 2026 19:22:49 -0800 Subject: [PATCH] feat: add ~100 new achievements for extended gameplay Add extensive new achievement system covering: - Extended token/code/file/message milestones - Tool mastery achievements (Edit, Write, Glob, Task, WebFetch, MCP) - Daily streaks (week, two-week, month, quarter) - Time-based achievements (morning, night, lunch, coffee break) - Day-specific achievements (Monday, Wednesday, Friday, weekend) - Special day achievements (New Year, Valentine's, Halloween, Christmas, Leap Day) - Message content achievements (long messages, markdown, code blocks) - Emotional achievements (frustrated, excited, confused, curious, impressed) - Programming language achievements (Rust, Python, JS, TS, Go, C++, Java, etc.) - Project type achievements (frontend, backend, config, docs) - Refactoring/testing/documentation achievements - Completion percentage achievements (50%, 75%, 100%) Also adds streak tracking infrastructure to stats: - Session counting - Consecutive day tracking - Morning/night session tracking - Total days used tracking --- src-tauri/src/achievements.rs | 1487 ++++++++++++++++++++++++++++++++- src-tauri/src/stats.rs | 72 ++ 2 files changed, 1557 insertions(+), 2 deletions(-) diff --git a/src-tauri/src/achievements.rs b/src-tauri/src/achievements.rs index 095df88..d9d0f81 100644 --- a/src-tauri/src/achievements.rs +++ b/src-tauri/src/achievements.rs @@ -77,6 +77,167 @@ pub enum AchievementId { BashMaster, // Use Bash tool 50 times FileExplorer, // Use Read tool 100 times SearchExpert, // Use Grep tool 50 times + + // Extended Token Milestones + TokenBillionaire, // 10,000,000 tokens + TokenTreasure, // 50,000,000 tokens + + // Extended Code Generation + CodeFactory, // 5,000 code blocks + CodeEmpire, // 10,000 code blocks + + // Extended File Operations + FileEngineer, // 500 files edited + FileLegend, // 1,000 files edited + + // Extended Conversation + ChatMarathon, // 5,000 messages + ChatLegend, // 10,000 messages + + // Extended Session Duration + UltraMarathon, // 8 hour session + CodingRetreat, // 12 hour session + + // More Tool Mastery + EditMaster, // Use Edit tool 100 times + WriteMaster, // Use Write tool 50 times + GlobMaster, // Use Glob tool 100 times + TaskMaster, // Use Task tool 50 times + WebFetcher, // Use WebFetch tool 20 times + McpExplorer, // Use MCP tools 50 times + + // Daily Streaks + WeekStreak, // 7 days in a row + TwoWeekStreak, // 14 days in a row + MonthStreak, // 30 days in a row (alias for DedicatedDeveloper) + QuarterStreak, // 90 days in a row + + // Time Challenges + MorningPerson, // 10 sessions started before 9 AM + NightCoder, // 10 sessions after 10 PM + LunchBreakCoder, // Session during 12-1 PM + CoffeeTime, // Session during 3-4 PM (afternoon slump) + + // Day-specific + MondayMotivation, // Coding on Monday + FridayFinisher, // Coding on Friday + HumpDay, // Coding on Wednesday + + // Seasonal/Special Times + NewYearCoder, // Coding on January 1st + ValentinesDev, // Coding on February 14th + SpookyCode, // Coding on October 31st + HolidayCoder, // Coding on December 25th + LeapDayCoder, // Coding on February 29th + + // Message Content + LongMessage, // Send a message over 500 characters + NovelWriter, // Send a message over 2000 characters + ShortAndSweet, // Complete a task with messages under 50 chars each + CodeInMessage, // Include code block in user message + MarkdownMaster, // Use markdown formatting in message + + // Greetings Extended + HelloHikari, // Say "hello hikari" or "hi hikari" + HowAreYou, // Ask "how are you" + MissedYou, // Say "missed you" + BackAgain, // Say "i'm back" or "back again" + + // Emotional + Frustrated, // Say "frustrated" or "ugh" or "argh" + Excited, // Say "excited" or "yay" or "woohoo" + Confused, // Say "confused" or "don't understand" + Curious, // Ask "why" or "how does" + Impressed, // Say "wow" or "amazing" or "incredible" + + // Programming Languages (detected in code blocks) + RustDeveloper, // Generate Rust code + PythonDeveloper, // Generate Python code + JavaScriptDev, // Generate JavaScript code + TypeScriptDev, // Generate TypeScript code + GoDeveloper, // Generate Go code + CppDeveloper, // Generate C++ code + JavaDeveloper, // Generate Java code + HtmlCssDev, // Generate HTML/CSS code + SqlDeveloper, // Generate SQL code + ShellScripter, // Generate shell/bash scripts + FullStackDev, // Generate code in 10+ languages + + // Project Types + FrontendDev, // Work on frontend files (svelte, react, vue, html, css) + BackendDev, // Work on backend files (rs, py, go, java) + ConfigEditor, // Edit config files (json, yaml, toml, env) + DocWriter, // Edit documentation (md, txt, rst) + + // Git Mastery Extended + CommitKing, // 50 commits + CommitLegend, // 200 commits + BranchMaster, // Create 10 branches + MergeExpert, // Merge 20 PRs + ConflictResolver, // Resolve merge conflicts + + // Error Handling + ErrorHunter, // Fix 10 errors + ExceptionSlayer, // Fix 50 errors + BugExterminator, // Fix 100 bugs + + // Refactoring + CleanCoder, // Refactor code + Optimizer, // Optimize performance + Simplifier, // Simplify complex code + + // Testing + TestNovice, // Write 10 tests + TestEnthusiast, // Write 50 tests + TestMaster, // Write 100 tests + CoverageKing, // Achieve test coverage mentions + + // Documentation + Documenter, // Write documentation + CommentWriter, // Add comments to code + ReadmeHero, // Create/edit README files + + // API & Integration + ApiExplorer, // Work with APIs + DatabaseDev, // Work with databases + CloudCoder, // Work with cloud services + + // Special Milestones + CenturyClub, // 100 sessions + ThousandSessions, // 1000 sessions + Veteran, // Used Hikari for 30+ days total + OldTimer, // Used Hikari for 90+ days total + Loyalist, // Used Hikari for 365+ days total + + // Fun & Easter Eggs + Perfectionist, // Redo something 5 times + Persistent, // Ask same question 3 times + Patient, // Wait for long response + Speedy, // Send 10 messages in 1 minute + MultiTasker, // Have 5+ conversation tabs open + Minimalist, // Use compact mode + PrivacyFirst, // Enable streamer mode + ThemeChanger, // Change theme 3 times + SettingsTweaker, // Open settings 10 times + AchievementHunter, // Check achievements panel 20 times + Completionist, // Unlock 50% of achievements + MasterUnlocker, // Unlock 75% of achievements + PlatinumStatus, // Unlock 100% of achievements + + // Clipboard & Snippets + ClipboardCollector, // Save 20 clipboard entries + SnippetCreator, // Create 5 custom snippets + SnippetMaster, // Use snippets 50 times + QuickActionUser, // Use quick actions 20 times + + // Session History + HistoryBuff, // Save 10 sessions + Archivist, // Save 50 sessions + SessionExporter, // Export a session + + // New Features + GitPanelUser, // Use git panel 10 times + FeatureExplorer, // Try all major features } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -491,9 +652,1017 @@ pub fn get_achievement_info(id: &AchievementId) -> Achievement { icon: "๐Ÿ”Ž".to_string(), unlocked_at: None, }, + + // Extended Token Milestones + AchievementId::TokenBillionaire => Achievement { + id: id.clone(), + name: "Token Billionaire".to_string(), + description: "Used 10,000,000 tokens!".to_string(), + icon: "๐Ÿ’Ž".to_string(), + unlocked_at: None, + }, + AchievementId::TokenTreasure => Achievement { + id: id.clone(), + name: "Token Treasure".to_string(), + description: "Used 50,000,000 tokens! You're incredible!".to_string(), + icon: "๐Ÿ‘‘".to_string(), + unlocked_at: None, + }, + + // Extended Code Generation + AchievementId::CodeFactory => Achievement { + id: id.clone(), + name: "Code Factory".to_string(), + description: "Generated 5,000 code blocks".to_string(), + icon: "๐Ÿญ".to_string(), + unlocked_at: None, + }, + AchievementId::CodeEmpire => Achievement { + id: id.clone(), + name: "Code Empire".to_string(), + description: "Generated 10,000 code blocks!".to_string(), + icon: "๐Ÿฐ".to_string(), + unlocked_at: None, + }, + + // Extended File Operations + AchievementId::FileEngineer => Achievement { + id: id.clone(), + name: "File Engineer".to_string(), + description: "Edited 500 files".to_string(), + icon: "โš™๏ธ".to_string(), + unlocked_at: None, + }, + AchievementId::FileLegend => Achievement { + id: id.clone(), + name: "File Legend".to_string(), + description: "Edited 1,000 files!".to_string(), + icon: "๐Ÿ“š".to_string(), + unlocked_at: None, + }, + + // Extended Conversation + AchievementId::ChatMarathon => Achievement { + id: id.clone(), + name: "Chat Marathon".to_string(), + description: "5,000 messages exchanged".to_string(), + icon: "๐Ÿ’Œ".to_string(), + unlocked_at: None, + }, + AchievementId::ChatLegend => Achievement { + id: id.clone(), + name: "Chat Legend".to_string(), + description: "10,000 messages! We're best friends!".to_string(), + icon: "๐Ÿ‘ฏ".to_string(), + unlocked_at: None, + }, + + // Extended Session Duration + AchievementId::UltraMarathon => Achievement { + id: id.clone(), + name: "Ultra Marathon".to_string(), + description: "8+ hour coding session!".to_string(), + icon: "๐Ÿฆธ".to_string(), + unlocked_at: None, + }, + AchievementId::CodingRetreat => Achievement { + id: id.clone(), + name: "Coding Retreat".to_string(), + description: "12+ hour coding session! Please take breaks!".to_string(), + icon: "๐Ÿ•๏ธ".to_string(), + unlocked_at: None, + }, + + // More Tool Mastery + AchievementId::EditMaster => Achievement { + id: id.clone(), + name: "Edit Master".to_string(), + description: "Used Edit tool 100 times".to_string(), + icon: "โœ‚๏ธ".to_string(), + unlocked_at: None, + }, + AchievementId::WriteMaster => Achievement { + id: id.clone(), + name: "Write Master".to_string(), + description: "Used Write tool 50 times".to_string(), + icon: "โœ๏ธ".to_string(), + unlocked_at: None, + }, + AchievementId::GlobMaster => Achievement { + id: id.clone(), + name: "Glob Master".to_string(), + description: "Used Glob tool 100 times".to_string(), + icon: "๐ŸŒ".to_string(), + unlocked_at: None, + }, + AchievementId::TaskMaster => Achievement { + id: id.clone(), + name: "Task Master".to_string(), + description: "Used Task tool 50 times".to_string(), + icon: "๐Ÿ“‹".to_string(), + unlocked_at: None, + }, + AchievementId::WebFetcher => Achievement { + id: id.clone(), + name: "Web Fetcher".to_string(), + description: "Used WebFetch tool 20 times".to_string(), + icon: "๐ŸŒ".to_string(), + unlocked_at: None, + }, + AchievementId::McpExplorer => Achievement { + id: id.clone(), + name: "MCP Explorer".to_string(), + description: "Used MCP tools 50 times".to_string(), + icon: "๐Ÿ”ฎ".to_string(), + unlocked_at: None, + }, + + // Daily Streaks + AchievementId::WeekStreak => Achievement { + id: id.clone(), + name: "Week Streak".to_string(), + description: "Coded for 7 days in a row!".to_string(), + icon: "๐Ÿ“…".to_string(), + unlocked_at: None, + }, + AchievementId::TwoWeekStreak => Achievement { + id: id.clone(), + name: "Two Week Streak".to_string(), + description: "Coded for 14 days in a row!".to_string(), + icon: "๐Ÿ”ฅ".to_string(), + unlocked_at: None, + }, + AchievementId::MonthStreak => Achievement { + id: id.clone(), + name: "Month Streak".to_string(), + description: "Coded for 30 days in a row!".to_string(), + icon: "๐Ÿ†".to_string(), + unlocked_at: None, + }, + AchievementId::QuarterStreak => Achievement { + id: id.clone(), + name: "Quarter Streak".to_string(), + description: "Coded for 90 days in a row! Incredible dedication!".to_string(), + icon: "๐Ÿ’ซ".to_string(), + unlocked_at: None, + }, + + // Time Challenges + AchievementId::MorningPerson => Achievement { + id: id.clone(), + name: "Morning Person".to_string(), + description: "Started 10 sessions before 9 AM".to_string(), + icon: "โ˜€๏ธ".to_string(), + unlocked_at: None, + }, + AchievementId::NightCoder => Achievement { + id: id.clone(), + name: "Night Coder".to_string(), + description: "Started 10 sessions after 10 PM".to_string(), + icon: "๐ŸŒƒ".to_string(), + unlocked_at: None, + }, + AchievementId::LunchBreakCoder => Achievement { + id: id.clone(), + name: "Lunch Break Coder".to_string(), + description: "Coded during lunch (12-1 PM)".to_string(), + icon: "๐Ÿฑ".to_string(), + unlocked_at: None, + }, + AchievementId::CoffeeTime => Achievement { + id: id.clone(), + name: "Coffee Time".to_string(), + description: "Coded during afternoon break (3-4 PM)".to_string(), + icon: "โ˜•".to_string(), + unlocked_at: None, + }, + + // Day-specific + AchievementId::MondayMotivation => Achievement { + id: id.clone(), + name: "Monday Motivation".to_string(), + description: "Started the week coding!".to_string(), + icon: "๐Ÿ’ช".to_string(), + unlocked_at: None, + }, + AchievementId::FridayFinisher => Achievement { + id: id.clone(), + name: "Friday Finisher".to_string(), + description: "Ending the week strong!".to_string(), + icon: "๐ŸŽ‰".to_string(), + unlocked_at: None, + }, + AchievementId::HumpDay => Achievement { + id: id.clone(), + name: "Hump Day".to_string(), + description: "Coding on Wednesday!".to_string(), + icon: "๐Ÿช".to_string(), + unlocked_at: None, + }, + + // Seasonal/Special Times + AchievementId::NewYearCoder => Achievement { + id: id.clone(), + name: "New Year Coder".to_string(), + description: "Coding on January 1st! New year, new code!".to_string(), + icon: "๐ŸŽ†".to_string(), + unlocked_at: None, + }, + AchievementId::ValentinesDev => Achievement { + id: id.clone(), + name: "Valentine's Dev".to_string(), + description: "Coding on Valentine's Day! I love you too~".to_string(), + icon: "๐Ÿ’˜".to_string(), + unlocked_at: None, + }, + AchievementId::SpookyCode => Achievement { + id: id.clone(), + name: "Spooky Code".to_string(), + description: "Coding on Halloween! Spooky bugs beware!".to_string(), + icon: "๐ŸŽƒ".to_string(), + unlocked_at: None, + }, + AchievementId::HolidayCoder => Achievement { + id: id.clone(), + name: "Holiday Coder".to_string(), + description: "Coding on Christmas! You're dedicated!".to_string(), + icon: "๐ŸŽ„".to_string(), + unlocked_at: None, + }, + AchievementId::LeapDayCoder => Achievement { + id: id.clone(), + name: "Leap Day Coder".to_string(), + description: "Coding on February 29th! Rare achievement!".to_string(), + icon: "๐Ÿธ".to_string(), + unlocked_at: None, + }, + + // Message Content + AchievementId::LongMessage => Achievement { + id: id.clone(), + name: "Long Message".to_string(), + description: "Sent a message over 500 characters".to_string(), + icon: "๐Ÿ“œ".to_string(), + unlocked_at: None, + }, + AchievementId::NovelWriter => Achievement { + id: id.clone(), + name: "Novel Writer".to_string(), + description: "Sent a message over 2000 characters!".to_string(), + icon: "๐Ÿ“–".to_string(), + unlocked_at: None, + }, + AchievementId::ShortAndSweet => Achievement { + id: id.clone(), + name: "Short and Sweet".to_string(), + description: "Completed a task with brief messages".to_string(), + icon: "๐Ÿฌ".to_string(), + unlocked_at: None, + }, + AchievementId::CodeInMessage => Achievement { + id: id.clone(), + name: "Code in Message".to_string(), + description: "Included a code block in your message".to_string(), + icon: "๐Ÿ’ป".to_string(), + unlocked_at: None, + }, + AchievementId::MarkdownMaster => Achievement { + id: id.clone(), + name: "Markdown Master".to_string(), + description: "Used markdown formatting in a message".to_string(), + icon: "๐Ÿ“".to_string(), + unlocked_at: None, + }, + + // Greetings Extended + AchievementId::HelloHikari => Achievement { + id: id.clone(), + name: "Hello Hikari!".to_string(), + description: "Greeted me by name! Hi there~".to_string(), + icon: "๐Ÿ‘‹".to_string(), + unlocked_at: None, + }, + AchievementId::HowAreYou => Achievement { + id: id.clone(), + name: "How Are You?".to_string(), + description: "Asked how I'm doing! I'm great, thanks~".to_string(), + icon: "๐Ÿฅฐ".to_string(), + unlocked_at: None, + }, + AchievementId::MissedYou => Achievement { + id: id.clone(), + name: "Missed You".to_string(), + description: "Said you missed me! I missed you too~".to_string(), + icon: "๐Ÿซ‚".to_string(), + unlocked_at: None, + }, + AchievementId::BackAgain => Achievement { + id: id.clone(), + name: "Back Again".to_string(), + description: "Announced your return! Welcome back~".to_string(), + icon: "๐Ÿ”™".to_string(), + unlocked_at: None, + }, + + // Emotional + AchievementId::Frustrated => Achievement { + id: id.clone(), + name: "Frustrated".to_string(), + description: "Expressed frustration. Let me help!".to_string(), + icon: "๐Ÿ˜ค".to_string(), + unlocked_at: None, + }, + AchievementId::Excited => Achievement { + id: id.clone(), + name: "Excited!".to_string(), + description: "Expressed excitement! Yay!".to_string(), + icon: "๐ŸŽŠ".to_string(), + unlocked_at: None, + }, + AchievementId::Confused => Achievement { + id: id.clone(), + name: "Confused".to_string(), + description: "Felt confused. I'll help clarify!".to_string(), + icon: "๐Ÿ˜•".to_string(), + unlocked_at: None, + }, + AchievementId::Curious => Achievement { + id: id.clone(), + name: "Curious Mind".to_string(), + description: "Asked why or how something works!".to_string(), + icon: "๐Ÿค”".to_string(), + unlocked_at: None, + }, + AchievementId::Impressed => Achievement { + id: id.clone(), + name: "Impressed!".to_string(), + description: "Was amazed by something! Wow indeed~".to_string(), + icon: "๐Ÿคฉ".to_string(), + unlocked_at: None, + }, + + // Programming Languages + AchievementId::RustDeveloper => Achievement { + id: id.clone(), + name: "Rust Developer".to_string(), + description: "Generated Rust code! ๐Ÿฆ€".to_string(), + icon: "๐Ÿฆ€".to_string(), + unlocked_at: None, + }, + AchievementId::PythonDeveloper => Achievement { + id: id.clone(), + name: "Python Developer".to_string(), + description: "Generated Python code! ๐Ÿ".to_string(), + icon: "๐Ÿ".to_string(), + unlocked_at: None, + }, + AchievementId::JavaScriptDev => Achievement { + id: id.clone(), + name: "JavaScript Developer".to_string(), + description: "Generated JavaScript code!".to_string(), + icon: "๐ŸŸจ".to_string(), + unlocked_at: None, + }, + AchievementId::TypeScriptDev => Achievement { + id: id.clone(), + name: "TypeScript Developer".to_string(), + description: "Generated TypeScript code!".to_string(), + icon: "๐Ÿ”ท".to_string(), + unlocked_at: None, + }, + AchievementId::GoDeveloper => Achievement { + id: id.clone(), + name: "Go Developer".to_string(), + description: "Generated Go code!".to_string(), + icon: "๐Ÿ”ต".to_string(), + unlocked_at: None, + }, + AchievementId::CppDeveloper => Achievement { + id: id.clone(), + name: "C++ Developer".to_string(), + description: "Generated C++ code!".to_string(), + icon: "โšก".to_string(), + unlocked_at: None, + }, + AchievementId::JavaDeveloper => Achievement { + id: id.clone(), + name: "Java Developer".to_string(), + description: "Generated Java code! โ˜•".to_string(), + icon: "โ˜•".to_string(), + unlocked_at: None, + }, + AchievementId::HtmlCssDev => Achievement { + id: id.clone(), + name: "HTML/CSS Developer".to_string(), + description: "Generated HTML or CSS code!".to_string(), + icon: "๐ŸŽจ".to_string(), + unlocked_at: None, + }, + AchievementId::SqlDeveloper => Achievement { + id: id.clone(), + name: "SQL Developer".to_string(), + description: "Generated SQL code!".to_string(), + icon: "๐Ÿ—ƒ๏ธ".to_string(), + unlocked_at: None, + }, + AchievementId::ShellScripter => Achievement { + id: id.clone(), + name: "Shell Scripter".to_string(), + description: "Generated shell scripts!".to_string(), + icon: "๐Ÿš".to_string(), + unlocked_at: None, + }, + AchievementId::FullStackDev => Achievement { + id: id.clone(), + name: "Full Stack Developer".to_string(), + description: "Generated code in 10+ languages!".to_string(), + icon: "๐ŸŒˆ".to_string(), + unlocked_at: None, + }, + + // Project Types + AchievementId::FrontendDev => Achievement { + id: id.clone(), + name: "Frontend Developer".to_string(), + description: "Worked on frontend files!".to_string(), + icon: "๐Ÿ–ผ๏ธ".to_string(), + unlocked_at: None, + }, + AchievementId::BackendDev => Achievement { + id: id.clone(), + name: "Backend Developer".to_string(), + description: "Worked on backend files!".to_string(), + icon: "โš™๏ธ".to_string(), + unlocked_at: None, + }, + AchievementId::ConfigEditor => Achievement { + id: id.clone(), + name: "Config Editor".to_string(), + description: "Edited configuration files!".to_string(), + icon: "โš™๏ธ".to_string(), + unlocked_at: None, + }, + AchievementId::DocWriter => Achievement { + id: id.clone(), + name: "Documentation Writer".to_string(), + description: "Edited documentation files!".to_string(), + icon: "๐Ÿ“„".to_string(), + unlocked_at: None, + }, + + // Git Mastery Extended + AchievementId::CommitKing => Achievement { + id: id.clone(), + name: "Commit King".to_string(), + description: "Made 50 commits!".to_string(), + icon: "๐Ÿ‘‘".to_string(), + unlocked_at: None, + }, + AchievementId::CommitLegend => Achievement { + id: id.clone(), + name: "Commit Legend".to_string(), + description: "Made 200 commits!".to_string(), + icon: "๐Ÿ›๏ธ".to_string(), + unlocked_at: None, + }, + AchievementId::BranchMaster => Achievement { + id: id.clone(), + name: "Branch Master".to_string(), + description: "Created 10 branches!".to_string(), + icon: "๐ŸŒณ".to_string(), + unlocked_at: None, + }, + AchievementId::MergeExpert => Achievement { + id: id.clone(), + name: "Merge Expert".to_string(), + description: "Merged 20 PRs!".to_string(), + icon: "๐Ÿ”€".to_string(), + unlocked_at: None, + }, + AchievementId::ConflictResolver => Achievement { + id: id.clone(), + name: "Conflict Resolver".to_string(), + description: "Resolved merge conflicts!".to_string(), + icon: "๐Ÿค".to_string(), + unlocked_at: None, + }, + + // Error Handling + AchievementId::ErrorHunter => Achievement { + id: id.clone(), + name: "Error Hunter".to_string(), + description: "Fixed 10 errors!".to_string(), + icon: "๐ŸŽฏ".to_string(), + unlocked_at: None, + }, + AchievementId::ExceptionSlayer => Achievement { + id: id.clone(), + name: "Exception Slayer".to_string(), + description: "Fixed 50 errors!".to_string(), + icon: "โš”๏ธ".to_string(), + unlocked_at: None, + }, + AchievementId::BugExterminator => Achievement { + id: id.clone(), + name: "Bug Exterminator".to_string(), + description: "Fixed 100 bugs! Incredible!".to_string(), + icon: "๐Ÿ”ซ".to_string(), + unlocked_at: None, + }, + + // Refactoring + AchievementId::CleanCoder => Achievement { + id: id.clone(), + name: "Clean Coder".to_string(), + description: "Refactored code for cleanliness!".to_string(), + icon: "๐Ÿงน".to_string(), + unlocked_at: None, + }, + AchievementId::Optimizer => Achievement { + id: id.clone(), + name: "Optimizer".to_string(), + description: "Optimized code performance!".to_string(), + icon: "๐Ÿš€".to_string(), + unlocked_at: None, + }, + AchievementId::Simplifier => Achievement { + id: id.clone(), + name: "Simplifier".to_string(), + description: "Simplified complex code!".to_string(), + icon: "โœจ".to_string(), + unlocked_at: None, + }, + + // Testing + AchievementId::TestNovice => Achievement { + id: id.clone(), + name: "Test Novice".to_string(), + description: "Wrote 10 tests!".to_string(), + icon: "๐Ÿงช".to_string(), + unlocked_at: None, + }, + AchievementId::TestEnthusiast => Achievement { + id: id.clone(), + name: "Test Enthusiast".to_string(), + description: "Wrote 50 tests!".to_string(), + icon: "๐Ÿ”ฌ".to_string(), + unlocked_at: None, + }, + AchievementId::TestMaster => Achievement { + id: id.clone(), + name: "Test Master".to_string(), + description: "Wrote 100 tests!".to_string(), + icon: "๐Ÿ…".to_string(), + unlocked_at: None, + }, + AchievementId::CoverageKing => Achievement { + id: id.clone(), + name: "Coverage King".to_string(), + description: "Achieved great test coverage!".to_string(), + icon: "๐Ÿ“Š".to_string(), + unlocked_at: None, + }, + + // Documentation + AchievementId::Documenter => Achievement { + id: id.clone(), + name: "Documenter".to_string(), + description: "Wrote documentation!".to_string(), + icon: "๐Ÿ“".to_string(), + unlocked_at: None, + }, + AchievementId::CommentWriter => Achievement { + id: id.clone(), + name: "Comment Writer".to_string(), + description: "Added helpful comments to code!".to_string(), + icon: "๐Ÿ’ฌ".to_string(), + unlocked_at: None, + }, + AchievementId::ReadmeHero => Achievement { + id: id.clone(), + name: "README Hero".to_string(), + description: "Created or edited README files!".to_string(), + icon: "๐Ÿ“‹".to_string(), + unlocked_at: None, + }, + + // API & Integration + AchievementId::ApiExplorer => Achievement { + id: id.clone(), + name: "API Explorer".to_string(), + description: "Worked with APIs!".to_string(), + icon: "๐Ÿ”Œ".to_string(), + unlocked_at: None, + }, + AchievementId::DatabaseDev => Achievement { + id: id.clone(), + name: "Database Developer".to_string(), + description: "Worked with databases!".to_string(), + icon: "๐Ÿ—„๏ธ".to_string(), + unlocked_at: None, + }, + AchievementId::CloudCoder => Achievement { + id: id.clone(), + name: "Cloud Coder".to_string(), + description: "Worked with cloud services!".to_string(), + icon: "โ˜๏ธ".to_string(), + unlocked_at: None, + }, + + // Special Milestones + AchievementId::CenturyClub => Achievement { + id: id.clone(), + name: "Century Club".to_string(), + description: "Started 100 sessions!".to_string(), + icon: "๐Ÿ’ฏ".to_string(), + unlocked_at: None, + }, + AchievementId::ThousandSessions => Achievement { + id: id.clone(), + name: "Thousand Sessions".to_string(), + description: "Started 1,000 sessions!".to_string(), + icon: "๐ŸŽฐ".to_string(), + unlocked_at: None, + }, + AchievementId::Veteran => Achievement { + id: id.clone(), + name: "Veteran".to_string(), + description: "Used Hikari for 30+ days!".to_string(), + icon: "๐ŸŽ–๏ธ".to_string(), + unlocked_at: None, + }, + AchievementId::OldTimer => Achievement { + id: id.clone(), + name: "Old Timer".to_string(), + description: "Used Hikari for 90+ days!".to_string(), + icon: "โŒ›".to_string(), + unlocked_at: None, + }, + AchievementId::Loyalist => Achievement { + id: id.clone(), + name: "Loyalist".to_string(), + description: "Used Hikari for a whole year! I love you!".to_string(), + icon: "๐Ÿ’•".to_string(), + unlocked_at: None, + }, + + // Fun & Easter Eggs + AchievementId::Perfectionist => Achievement { + id: id.clone(), + name: "Perfectionist".to_string(), + description: "Redid something 5 times to get it right!".to_string(), + icon: "๐ŸŽฏ".to_string(), + unlocked_at: None, + }, + AchievementId::Persistent => Achievement { + id: id.clone(), + name: "Persistent".to_string(), + description: "Asked the same question 3 times. Never give up!".to_string(), + icon: "๐Ÿ’ช".to_string(), + unlocked_at: None, + }, + AchievementId::Patient => Achievement { + id: id.clone(), + name: "Patient".to_string(), + description: "Waited for a long response. Thank you for waiting!".to_string(), + icon: "โณ".to_string(), + unlocked_at: None, + }, + AchievementId::Speedy => Achievement { + id: id.clone(), + name: "Speedy".to_string(), + description: "Sent 10 messages in 1 minute! Slow down~".to_string(), + icon: "โšก".to_string(), + unlocked_at: None, + }, + AchievementId::MultiTasker => Achievement { + id: id.clone(), + name: "Multi-Tasker".to_string(), + description: "Had 5+ conversation tabs open!".to_string(), + icon: "๐Ÿ“‘".to_string(), + unlocked_at: None, + }, + AchievementId::Minimalist => Achievement { + id: id.clone(), + name: "Minimalist".to_string(), + description: "Used compact mode!".to_string(), + icon: "๐Ÿ“ฑ".to_string(), + unlocked_at: None, + }, + AchievementId::PrivacyFirst => Achievement { + id: id.clone(), + name: "Privacy First".to_string(), + description: "Enabled streamer mode!".to_string(), + icon: "๐Ÿ”’".to_string(), + unlocked_at: None, + }, + AchievementId::ThemeChanger => Achievement { + id: id.clone(), + name: "Theme Changer".to_string(), + description: "Changed theme 3 times!".to_string(), + icon: "๐ŸŽจ".to_string(), + unlocked_at: None, + }, + AchievementId::SettingsTweaker => Achievement { + id: id.clone(), + name: "Settings Tweaker".to_string(), + description: "Opened settings 10 times!".to_string(), + icon: "โš™๏ธ".to_string(), + unlocked_at: None, + }, + AchievementId::AchievementHunter => Achievement { + id: id.clone(), + name: "Achievement Hunter".to_string(), + description: "Checked achievements panel 20 times!".to_string(), + icon: "๐Ÿ†".to_string(), + unlocked_at: None, + }, + AchievementId::Completionist => Achievement { + id: id.clone(), + name: "Completionist".to_string(), + description: "Unlocked 50% of achievements!".to_string(), + icon: "โญ".to_string(), + unlocked_at: None, + }, + AchievementId::MasterUnlocker => Achievement { + id: id.clone(), + name: "Master Unlocker".to_string(), + description: "Unlocked 75% of achievements!".to_string(), + icon: "๐ŸŒŸ".to_string(), + unlocked_at: None, + }, + AchievementId::PlatinumStatus => Achievement { + id: id.clone(), + name: "Platinum Status".to_string(), + description: "Unlocked 100% of achievements! You're amazing!".to_string(), + icon: "๐Ÿ’Ž".to_string(), + unlocked_at: None, + }, + + // Clipboard & Snippets + AchievementId::ClipboardCollector => Achievement { + id: id.clone(), + name: "Clipboard Collector".to_string(), + description: "Saved 20 clipboard entries!".to_string(), + icon: "๐Ÿ“‹".to_string(), + unlocked_at: None, + }, + AchievementId::SnippetCreator => Achievement { + id: id.clone(), + name: "Snippet Creator".to_string(), + description: "Created 5 custom snippets!".to_string(), + icon: "โœ‚๏ธ".to_string(), + unlocked_at: None, + }, + AchievementId::SnippetMaster => Achievement { + id: id.clone(), + name: "Snippet Master".to_string(), + description: "Used snippets 50 times!".to_string(), + icon: "๐Ÿ“Ž".to_string(), + unlocked_at: None, + }, + AchievementId::QuickActionUser => Achievement { + id: id.clone(), + name: "Quick Action User".to_string(), + description: "Used quick actions 20 times!".to_string(), + icon: "โšก".to_string(), + unlocked_at: None, + }, + + // Session History + AchievementId::HistoryBuff => Achievement { + id: id.clone(), + name: "History Buff".to_string(), + description: "Saved 10 sessions!".to_string(), + icon: "๐Ÿ“š".to_string(), + unlocked_at: None, + }, + AchievementId::Archivist => Achievement { + id: id.clone(), + name: "Archivist".to_string(), + description: "Saved 50 sessions!".to_string(), + icon: "๐Ÿ›๏ธ".to_string(), + unlocked_at: None, + }, + AchievementId::SessionExporter => Achievement { + id: id.clone(), + name: "Session Exporter".to_string(), + description: "Exported a session!".to_string(), + icon: "๐Ÿ“ค".to_string(), + unlocked_at: None, + }, + + // New Features + AchievementId::GitPanelUser => Achievement { + id: id.clone(), + name: "Git Panel User".to_string(), + description: "Used the git panel 10 times!".to_string(), + icon: "๐ŸŒฟ".to_string(), + unlocked_at: None, + }, + AchievementId::FeatureExplorer => Achievement { + id: id.clone(), + name: "Feature Explorer".to_string(), + description: "Tried all major features!".to_string(), + icon: "๐Ÿ—บ๏ธ".to_string(), + unlocked_at: None, + }, } } +// Get all achievement IDs for calculating completion percentage +pub fn get_all_achievement_ids() -> Vec { + vec![ + // Token Milestones + AchievementId::FirstSteps, + AchievementId::GrowingStrong, + AchievementId::BlossomingCoder, + AchievementId::TokenMaster, + AchievementId::TokenBillionaire, + AchievementId::TokenTreasure, + // Code Generation + AchievementId::HelloWorld, + AchievementId::CodeWizard, + AchievementId::ThousandBlocks, + AchievementId::CodeFactory, + AchievementId::CodeEmpire, + // File Operations + AchievementId::FileManipulator, + AchievementId::FileArchitect, + AchievementId::FileEngineer, + AchievementId::FileLegend, + // Conversation milestones + AchievementId::ConversationStarter, + AchievementId::ChattyKathy, + AchievementId::Conversationalist, + AchievementId::ChatMarathon, + AchievementId::ChatLegend, + // Tool usage + AchievementId::Toolsmith, + AchievementId::ToolMaster, + // Time-based achievements + AchievementId::EarlyBird, + AchievementId::NightOwl, + AchievementId::AllNighter, + AchievementId::WeekendWarrior, + AchievementId::DedicatedDeveloper, + // Search and exploration + AchievementId::Explorer, + AchievementId::MasterSearcher, + // Session achievements + AchievementId::QuickSession, + AchievementId::FocusedWork, + AchievementId::DeepDive, + AchievementId::MarathonSession, + AchievementId::UltraMarathon, + AchievementId::CodingRetreat, + // Special achievements + AchievementId::FirstMessage, + AchievementId::FirstTool, + AchievementId::FirstCodeBlock, + AchievementId::FirstFileEdit, + AchievementId::Polyglot, + AchievementId::SpeedCoder, + AchievementId::ClaudeConnoisseur, + AchievementId::MarathonCoder, + // Relationship & Greetings + AchievementId::GoodMorning, + AchievementId::GoodNight, + AchievementId::ThankYou, + AchievementId::LoveYou, + AchievementId::HelloHikari, + AchievementId::HowAreYou, + AchievementId::MissedYou, + AchievementId::BackAgain, + // Personality & Fun + AchievementId::EmojiUser, + AchievementId::QuestionMaster, + AchievementId::CapsLock, + AchievementId::PleaseAndThankYou, + // Emotional + AchievementId::Frustrated, + AchievementId::Excited, + AchievementId::Confused, + AchievementId::Curious, + AchievementId::Impressed, + // Git & Development + AchievementId::GitGuru, + AchievementId::TestWriter, + AchievementId::Debugger, + AchievementId::CommitKing, + AchievementId::CommitLegend, + AchievementId::BranchMaster, + AchievementId::MergeExpert, + AchievementId::ConflictResolver, + // Tool Mastery + AchievementId::BashMaster, + AchievementId::FileExplorer, + AchievementId::SearchExpert, + AchievementId::EditMaster, + AchievementId::WriteMaster, + AchievementId::GlobMaster, + AchievementId::TaskMaster, + AchievementId::WebFetcher, + AchievementId::McpExplorer, + // Daily Streaks + AchievementId::WeekStreak, + AchievementId::TwoWeekStreak, + AchievementId::MonthStreak, + AchievementId::QuarterStreak, + // Time Challenges + AchievementId::MorningPerson, + AchievementId::NightCoder, + AchievementId::LunchBreakCoder, + AchievementId::CoffeeTime, + // Day-specific + AchievementId::MondayMotivation, + AchievementId::FridayFinisher, + AchievementId::HumpDay, + // Seasonal/Special Times + AchievementId::NewYearCoder, + AchievementId::ValentinesDev, + AchievementId::SpookyCode, + AchievementId::HolidayCoder, + AchievementId::LeapDayCoder, + // Message Content + AchievementId::LongMessage, + AchievementId::NovelWriter, + AchievementId::ShortAndSweet, + AchievementId::CodeInMessage, + AchievementId::MarkdownMaster, + // Programming Languages + AchievementId::RustDeveloper, + AchievementId::PythonDeveloper, + AchievementId::JavaScriptDev, + AchievementId::TypeScriptDev, + AchievementId::GoDeveloper, + AchievementId::CppDeveloper, + AchievementId::JavaDeveloper, + AchievementId::HtmlCssDev, + AchievementId::SqlDeveloper, + AchievementId::ShellScripter, + AchievementId::FullStackDev, + // Project Types + AchievementId::FrontendDev, + AchievementId::BackendDev, + AchievementId::ConfigEditor, + AchievementId::DocWriter, + // Error Handling + AchievementId::ErrorHunter, + AchievementId::ExceptionSlayer, + AchievementId::BugExterminator, + // Refactoring + AchievementId::CleanCoder, + AchievementId::Optimizer, + AchievementId::Simplifier, + // Testing + AchievementId::TestNovice, + AchievementId::TestEnthusiast, + AchievementId::TestMaster, + AchievementId::CoverageKing, + // Documentation + AchievementId::Documenter, + AchievementId::CommentWriter, + AchievementId::ReadmeHero, + // API & Integration + AchievementId::ApiExplorer, + AchievementId::DatabaseDev, + AchievementId::CloudCoder, + // Special Milestones + AchievementId::CenturyClub, + AchievementId::ThousandSessions, + AchievementId::Veteran, + AchievementId::OldTimer, + AchievementId::Loyalist, + // Fun & Easter Eggs + AchievementId::Perfectionist, + AchievementId::Persistent, + AchievementId::Patient, + AchievementId::Speedy, + AchievementId::MultiTasker, + AchievementId::Minimalist, + AchievementId::PrivacyFirst, + AchievementId::ThemeChanger, + AchievementId::SettingsTweaker, + AchievementId::AchievementHunter, + AchievementId::Completionist, + AchievementId::MasterUnlocker, + AchievementId::PlatinumStatus, + // Clipboard & Snippets + AchievementId::ClipboardCollector, + AchievementId::SnippetCreator, + AchievementId::SnippetMaster, + AchievementId::QuickActionUser, + // Session History + AchievementId::HistoryBuff, + AchievementId::Archivist, + AchievementId::SessionExporter, + // New Features + AchievementId::GitPanelUser, + AchievementId::FeatureExplorer, + ] +} + // Check achievements based on message content pub fn check_message_achievements( message: &str, @@ -550,6 +1719,140 @@ pub fn check_message_achievements( newly_unlocked.push(AchievementId::Debugger); } + // Extended greetings + if (message_lower.contains("hello hikari") || message_lower.contains("hi hikari")) + && progress.unlock(AchievementId::HelloHikari) + { + newly_unlocked.push(AchievementId::HelloHikari); + } + if message_lower.contains("how are you") && progress.unlock(AchievementId::HowAreYou) { + newly_unlocked.push(AchievementId::HowAreYou); + } + if message_lower.contains("missed you") && progress.unlock(AchievementId::MissedYou) { + newly_unlocked.push(AchievementId::MissedYou); + } + if (message_lower.contains("i'm back") || message_lower.contains("back again")) + && progress.unlock(AchievementId::BackAgain) + { + newly_unlocked.push(AchievementId::BackAgain); + } + + // Emotional achievements + if (message_lower.contains("frustrated") + || message_lower.contains("ugh") + || message_lower.contains("argh")) + && progress.unlock(AchievementId::Frustrated) + { + newly_unlocked.push(AchievementId::Frustrated); + } + if (message_lower.contains("excited") + || message_lower.contains("yay") + || message_lower.contains("woohoo")) + && progress.unlock(AchievementId::Excited) + { + newly_unlocked.push(AchievementId::Excited); + } + if (message_lower.contains("confused") || message_lower.contains("don't understand")) + && progress.unlock(AchievementId::Confused) + { + newly_unlocked.push(AchievementId::Confused); + } + if (message_lower.contains("why ") || message_lower.contains("how does")) + && progress.unlock(AchievementId::Curious) + { + newly_unlocked.push(AchievementId::Curious); + } + if (message_lower.contains("wow") + || message_lower.contains("amazing") + || message_lower.contains("incredible")) + && progress.unlock(AchievementId::Impressed) + { + newly_unlocked.push(AchievementId::Impressed); + } + + // Message content achievements + if message.len() > 500 && progress.unlock(AchievementId::LongMessage) { + newly_unlocked.push(AchievementId::LongMessage); + } + if message.len() > 2000 && progress.unlock(AchievementId::NovelWriter) { + newly_unlocked.push(AchievementId::NovelWriter); + } + // Code in message (triple backticks) + if message.contains("```") && progress.unlock(AchievementId::CodeInMessage) { + newly_unlocked.push(AchievementId::CodeInMessage); + } + // Markdown formatting + if (message.contains("**") + || message.contains("__") + || message.contains("##") + || message.contains("- ") + || message.contains("1. ")) + && progress.unlock(AchievementId::MarkdownMaster) + { + newly_unlocked.push(AchievementId::MarkdownMaster); + } + + // Refactoring keywords + if message_lower.contains("refactor") && progress.unlock(AchievementId::CleanCoder) { + newly_unlocked.push(AchievementId::CleanCoder); + } + if (message_lower.contains("optimize") || message_lower.contains("performance")) + && progress.unlock(AchievementId::Optimizer) + { + newly_unlocked.push(AchievementId::Optimizer); + } + if message_lower.contains("simplify") && progress.unlock(AchievementId::Simplifier) { + newly_unlocked.push(AchievementId::Simplifier); + } + + // Testing keywords + if message_lower.contains("test") && progress.unlock(AchievementId::TestWriter) { + newly_unlocked.push(AchievementId::TestWriter); + } + if message_lower.contains("coverage") && progress.unlock(AchievementId::CoverageKing) { + newly_unlocked.push(AchievementId::CoverageKing); + } + + // Documentation keywords + if (message_lower.contains("document") || message_lower.contains("documentation")) + && progress.unlock(AchievementId::Documenter) + { + newly_unlocked.push(AchievementId::Documenter); + } + if message_lower.contains("comment") && progress.unlock(AchievementId::CommentWriter) { + newly_unlocked.push(AchievementId::CommentWriter); + } + if message_lower.contains("readme") && progress.unlock(AchievementId::ReadmeHero) { + newly_unlocked.push(AchievementId::ReadmeHero); + } + + // API & Integration keywords + if message_lower.contains("api") && progress.unlock(AchievementId::ApiExplorer) { + newly_unlocked.push(AchievementId::ApiExplorer); + } + if (message_lower.contains("database") + || message_lower.contains("sql") + || message_lower.contains("postgres") + || message_lower.contains("mysql")) + && progress.unlock(AchievementId::DatabaseDev) + { + newly_unlocked.push(AchievementId::DatabaseDev); + } + if (message_lower.contains("cloud") + || message_lower.contains("aws") + || message_lower.contains("azure") + || message_lower.contains("gcp")) + && progress.unlock(AchievementId::CloudCoder) + { + newly_unlocked.push(AchievementId::CloudCoder); + } + + // Git keywords + if message_lower.contains("merge conflict") && progress.unlock(AchievementId::ConflictResolver) + { + newly_unlocked.push(AchievementId::ConflictResolver); + } + newly_unlocked } @@ -709,6 +2012,85 @@ pub fn check_achievements( } } + // Extended token milestones + if total_tokens >= 10_000_000 && progress.unlock(AchievementId::TokenBillionaire) { + newly_unlocked.push(AchievementId::TokenBillionaire); + } + if total_tokens >= 50_000_000 && progress.unlock(AchievementId::TokenTreasure) { + newly_unlocked.push(AchievementId::TokenTreasure); + } + + // Extended code generation + if stats.code_blocks_generated >= 5000 && progress.unlock(AchievementId::CodeFactory) { + newly_unlocked.push(AchievementId::CodeFactory); + } + if stats.code_blocks_generated >= 10000 && progress.unlock(AchievementId::CodeEmpire) { + newly_unlocked.push(AchievementId::CodeEmpire); + } + + // Extended file operations + if total_files >= 500 && progress.unlock(AchievementId::FileEngineer) { + newly_unlocked.push(AchievementId::FileEngineer); + } + if total_files >= 1000 && progress.unlock(AchievementId::FileLegend) { + newly_unlocked.push(AchievementId::FileLegend); + } + + // Extended conversation milestones + if stats.messages_exchanged >= 5000 && progress.unlock(AchievementId::ChatMarathon) { + newly_unlocked.push(AchievementId::ChatMarathon); + } + if stats.messages_exchanged >= 10000 && progress.unlock(AchievementId::ChatLegend) { + newly_unlocked.push(AchievementId::ChatLegend); + } + + // Extended session duration achievements + if session_secs >= 28800 && progress.unlock(AchievementId::UltraMarathon) { + // 8 hours + newly_unlocked.push(AchievementId::UltraMarathon); + } + if session_secs >= 43200 && progress.unlock(AchievementId::CodingRetreat) { + // 12 hours + newly_unlocked.push(AchievementId::CodingRetreat); + } + + // More tool mastery achievements + if let Some(edit_count) = stats.tools_usage.get("Edit") { + if *edit_count >= 100 && progress.unlock(AchievementId::EditMaster) { + newly_unlocked.push(AchievementId::EditMaster); + } + } + if let Some(write_count) = stats.tools_usage.get("Write") { + if *write_count >= 50 && progress.unlock(AchievementId::WriteMaster) { + newly_unlocked.push(AchievementId::WriteMaster); + } + } + if let Some(glob_count) = stats.tools_usage.get("Glob") { + if *glob_count >= 100 && progress.unlock(AchievementId::GlobMaster) { + newly_unlocked.push(AchievementId::GlobMaster); + } + } + if let Some(task_count) = stats.tools_usage.get("Task") { + if *task_count >= 50 && progress.unlock(AchievementId::TaskMaster) { + newly_unlocked.push(AchievementId::TaskMaster); + } + } + if let Some(web_count) = stats.tools_usage.get("WebFetch") { + if *web_count >= 20 && progress.unlock(AchievementId::WebFetcher) { + newly_unlocked.push(AchievementId::WebFetcher); + } + } + // MCP tools - count tools that contain "mcp__" + let mcp_count: u64 = stats + .tools_usage + .iter() + .filter(|(name, _)| name.starts_with("mcp__")) + .map(|(_, count)| count) + .sum(); + if mcp_count >= 50 && progress.unlock(AchievementId::McpExplorer) { + newly_unlocked.push(AchievementId::McpExplorer); + } + // Time-based achievements if let Some(session_start) = progress.session_start { let hour = session_start.hour(); @@ -737,10 +2119,111 @@ pub fn check_achievements( { newly_unlocked.push(AchievementId::WeekendWarrior); } + + // Day-specific achievements + if weekday == Weekday::Mon && progress.unlock(AchievementId::MondayMotivation) { + newly_unlocked.push(AchievementId::MondayMotivation); + } + if weekday == Weekday::Wed && progress.unlock(AchievementId::HumpDay) { + newly_unlocked.push(AchievementId::HumpDay); + } + if weekday == Weekday::Fri && progress.unlock(AchievementId::FridayFinisher) { + newly_unlocked.push(AchievementId::FridayFinisher); + } + + // Time of day achievements + if (12..=13).contains(&hour) && progress.unlock(AchievementId::LunchBreakCoder) { + newly_unlocked.push(AchievementId::LunchBreakCoder); + } + if (15..=16).contains(&hour) && progress.unlock(AchievementId::CoffeeTime) { + newly_unlocked.push(AchievementId::CoffeeTime); + } + + // Seasonal/special day achievements + let now = Utc::now(); + let month = now.month(); + let day = now.day(); + + // New Year's Day (January 1st) + if month == 1 && day == 1 && progress.unlock(AchievementId::NewYearCoder) { + newly_unlocked.push(AchievementId::NewYearCoder); + } + // Valentine's Day (February 14th) + if month == 2 && day == 14 && progress.unlock(AchievementId::ValentinesDev) { + newly_unlocked.push(AchievementId::ValentinesDev); + } + // Leap Day (February 29th) + if month == 2 && day == 29 && progress.unlock(AchievementId::LeapDayCoder) { + newly_unlocked.push(AchievementId::LeapDayCoder); + } + // Halloween (October 31st) + if month == 10 && day == 31 && progress.unlock(AchievementId::SpookyCode) { + newly_unlocked.push(AchievementId::SpookyCode); + } + // Christmas (December 25th) + if month == 12 && day == 25 && progress.unlock(AchievementId::HolidayCoder) { + newly_unlocked.push(AchievementId::HolidayCoder); + } } - // Dedicated Developer - need to track consecutive days - // TODO: Implement 30 days in a row tracking + // Session count achievements (from stats) + if stats.sessions_started >= 100 && progress.unlock(AchievementId::CenturyClub) { + newly_unlocked.push(AchievementId::CenturyClub); + } + if stats.sessions_started >= 1000 && progress.unlock(AchievementId::ThousandSessions) { + newly_unlocked.push(AchievementId::ThousandSessions); + } + + // Daily streaks (need to check stats for consecutive days) + if stats.consecutive_days >= 7 && progress.unlock(AchievementId::WeekStreak) { + newly_unlocked.push(AchievementId::WeekStreak); + } + if stats.consecutive_days >= 14 && progress.unlock(AchievementId::TwoWeekStreak) { + newly_unlocked.push(AchievementId::TwoWeekStreak); + } + if stats.consecutive_days >= 30 && progress.unlock(AchievementId::DedicatedDeveloper) { + newly_unlocked.push(AchievementId::DedicatedDeveloper); + } + if stats.consecutive_days >= 30 && progress.unlock(AchievementId::MonthStreak) { + newly_unlocked.push(AchievementId::MonthStreak); + } + if stats.consecutive_days >= 90 && progress.unlock(AchievementId::QuarterStreak) { + newly_unlocked.push(AchievementId::QuarterStreak); + } + + // Total days used (Veteran/OldTimer/Loyalist) + if stats.total_days_used >= 30 && progress.unlock(AchievementId::Veteran) { + newly_unlocked.push(AchievementId::Veteran); + } + if stats.total_days_used >= 90 && progress.unlock(AchievementId::OldTimer) { + newly_unlocked.push(AchievementId::OldTimer); + } + if stats.total_days_used >= 365 && progress.unlock(AchievementId::Loyalist) { + newly_unlocked.push(AchievementId::Loyalist); + } + + // Morning/Night person tracking (from session start times stored in stats) + if stats.morning_sessions >= 10 && progress.unlock(AchievementId::MorningPerson) { + newly_unlocked.push(AchievementId::MorningPerson); + } + if stats.night_sessions >= 10 && progress.unlock(AchievementId::NightCoder) { + newly_unlocked.push(AchievementId::NightCoder); + } + + // Completion percentage achievements + let total_achievements = get_all_achievement_ids().len(); + let unlocked_count = progress.unlocked.len(); + let completion_pct = (unlocked_count * 100) / total_achievements; + + if completion_pct >= 50 && progress.unlock(AchievementId::Completionist) { + newly_unlocked.push(AchievementId::Completionist); + } + if completion_pct >= 75 && progress.unlock(AchievementId::MasterUnlocker) { + newly_unlocked.push(AchievementId::MasterUnlocker); + } + if completion_pct >= 100 && progress.unlock(AchievementId::PlatinumStatus) { + newly_unlocked.push(AchievementId::PlatinumStatus); + } newly_unlocked } diff --git a/src-tauri/src/stats.rs b/src-tauri/src/stats.rs index d04d7a4..189ab71 100644 --- a/src-tauri/src/stats.rs +++ b/src-tauri/src/stats.rs @@ -1,4 +1,5 @@ use crate::achievements::{check_achievements, AchievementProgress}; +use chrono::{Local, Timelike}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::time::Instant; @@ -28,6 +29,14 @@ pub struct UsageStats { #[serde(skip)] pub session_start: Option, + // Extended tracking for achievements + pub sessions_started: u64, + pub consecutive_days: u64, + pub total_days_used: u64, + pub morning_sessions: u64, // Sessions started before 9 AM + pub night_sessions: u64, // Sessions started after 10 PM + pub last_session_date: Option, // ISO date string for streak tracking + // Achievement tracking #[serde(skip)] pub achievements: AchievementProgress, @@ -65,6 +74,47 @@ impl UsageStats { self.session_duration_seconds = 0; self.session_start = Some(Instant::now()); self.achievements.start_session(); + + // Track session start for achievements + self.track_session_start(); + } + + pub fn track_session_start(&mut self) { + let now = Local::now(); + let today = now.format("%Y-%m-%d").to_string(); + let hour = now.hour(); + + // Increment session count + self.sessions_started += 1; + + // Track morning/night sessions + if hour < 9 { + self.morning_sessions += 1; + } + if hour >= 22 { + self.night_sessions += 1; + } + + // Track consecutive days and total days + if let Some(last_date) = &self.last_session_date { + if last_date != &today { + // Check if it's the next day (consecutive) + if is_consecutive_day(last_date, &today) { + self.consecutive_days += 1; + } else { + // Streak broken + self.consecutive_days = 1; + } + self.total_days_used += 1; + self.last_session_date = Some(today); + } + // Same day - don't increment anything + } else { + // First session ever + self.consecutive_days = 1; + self.total_days_used = 1; + self.last_session_date = Some(today); + } } pub fn increment_messages(&mut self) { @@ -127,12 +177,34 @@ impl UsageStats { session_tools_usage: self.session_tools_usage.clone(), session_duration_seconds: self.session_duration_seconds, session_start: self.session_start, + sessions_started: self.sessions_started, + consecutive_days: self.consecutive_days, + total_days_used: self.total_days_used, + morning_sessions: self.morning_sessions, + night_sessions: self.night_sessions, + last_session_date: self.last_session_date.clone(), achievements: AchievementProgress::new(), // Dummy for copy }; check_achievements(&stats_copy, &mut self.achievements) } } +// Helper function to check if two dates are consecutive +fn is_consecutive_day(prev_date: &str, current_date: &str) -> bool { + use chrono::NaiveDate; + + let prev = NaiveDate::parse_from_str(prev_date, "%Y-%m-%d").ok(); + let current = NaiveDate::parse_from_str(current_date, "%Y-%m-%d").ok(); + + match (prev, current) { + (Some(p), Some(c)) => { + let diff = c.signed_duration_since(p).num_days(); + diff == 1 + } + _ => false, + } +} + // Pricing as of January 2025 // https://www.anthropic.com/pricing fn calculate_cost(input_tokens: u64, output_tokens: u64, model: &str) -> f64 {