use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AchievementSystem {
pub achievements: HashMap<String, Achievement>,
pub unlocked: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Achievement {
pub id: String,
pub name: String,
pub description: String,
pub icon: Option<String>,
pub points: u32,
pub hidden: bool,
pub progress: u32,
pub progress_max: u32,
pub unlocked: bool,
pub unlock_time: Option<u64>,
}
impl Achievement {
pub fn new(id: &str, name: &str, description: &str, points: u32) -> Self {
Self {
id: id.to_string(),
name: name.to_string(),
description: description.to_string(),
icon: None,
points,
hidden: false,
progress: 0,
progress_max: 1,
unlocked: false,
unlock_time: None,
}
}
pub fn with_progress(mut self, max: u32) -> Self {
self.progress_max = max;
self
}
pub fn with_icon(mut self, icon: &str) -> Self {
self.icon = Some(icon.to_string());
self
}
pub fn as_hidden(mut self) -> Self {
self.hidden = true;
self
}
pub fn is_complete(&self) -> bool {
self.progress >= self.progress_max
}
pub fn progress_percentage(&self) -> f32 {
if self.progress_max == 0 {
return 0.0;
}
(self.progress as f32 / self.progress_max as f32 * 100.0).min(100.0)
}
}
impl AchievementSystem {
pub fn new() -> Self {
Self {
achievements: HashMap::new(),
unlocked: Vec::new(),
}
}
pub fn add_achievement(&mut self, achievement: Achievement) {
self.achievements
.insert(achievement.id.clone(), achievement);
}
pub fn add_progress(&mut self, id: &str, amount: u32) -> bool {
if let Some(achievement) = self.achievements.get_mut(id) {
if achievement.unlocked {
return false;
}
achievement.progress += amount;
if achievement.is_complete() && !achievement.unlocked {
return self.unlock(id);
}
}
false
}
pub fn set_progress(&mut self, id: &str, progress: u32) -> bool {
if let Some(achievement) = self.achievements.get_mut(id) {
if achievement.unlocked {
return false;
}
achievement.progress = progress.min(achievement.progress_max);
if achievement.is_complete() && !achievement.unlocked {
return self.unlock(id);
}
}
false
}
pub fn unlock(&mut self, id: &str) -> bool {
if let Some(achievement) = self.achievements.get_mut(id) {
if achievement.unlocked {
return false;
}
achievement.unlocked = true;
achievement.unlock_time = Some(
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap()
.as_secs(),
);
self.unlocked.push(id.to_string());
println!("🏆 Conquista desbloqueada: {}", achievement.name);
println!(" {}", achievement.description);
println!(" +{} pontos", achievement.points);
return true;
}
false
}
pub fn is_unlocked(&self, id: &str) -> bool {
self.achievements
.get(id)
.map_or(false, |a| a.unlocked)
}
pub fn total_points(&self) -> u32 {
self.achievements
.values()
.filter(|a| a.unlocked)
.map(|a| a.points)
.sum()
}
pub fn unlocked_count(&self) -> usize {
self.unlocked.len()
}
pub fn total_count(&self) -> usize {
self.achievements.len()
}
pub fn completion_percentage(&self) -> f32 {
if self.achievements.is_empty() {
return 0.0;
}
self.unlocked_count() as f32 / self.total_count() as f32 * 100.0
}
pub fn save_json(&self, path: &str) -> Result<(), String> {
let json = serde_json::to_string_pretty(self)
.map_err(|e| format!("Erro ao serializar: {}", e))?;
std::fs::write(path, json).map_err(|e| format!("Erro ao escrever: {}", e))?;
Ok(())
}
pub fn load_json(path: &str) -> Result<Self, String> {
let content =
std::fs::read_to_string(path).map_err(|e| format!("Erro ao ler: {}", e))?;
serde_json::from_str(&content).map_err(|e| format!("Erro ao deserializar: {}", e))
}
}
impl Default for AchievementSystem {
fn default() -> Self {
Self::new()
}
}