sevenx_engine 0.2.11

Engine de jogos 2D/3D completa com suporte Android, física, áudio, partículas, tilemap, UI, eventos e sistema 3D avançado com PBR.
Documentation
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Sistema de conquistas/achievements.
#[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(),
        }
    }

    /// Adiciona uma conquista ao sistema.
    pub fn add_achievement(&mut self, achievement: Achievement) {
        self.achievements
            .insert(achievement.id.clone(), achievement);
    }

    /// Incrementa o progresso de uma conquista.
    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
    }

    /// Define o progresso de uma conquista.
    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
    }

    /// Desbloqueia uma conquista.
    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
    }

    /// Verifica se uma conquista está desbloqueada.
    pub fn is_unlocked(&self, id: &str) -> bool {
        self.achievements
            .get(id)
            .map_or(false, |a| a.unlocked)
    }

    /// Retorna o total de pontos desbloqueados.
    pub fn total_points(&self) -> u32 {
        self.achievements
            .values()
            .filter(|a| a.unlocked)
            .map(|a| a.points)
            .sum()
    }

    /// Retorna o total de conquistas desbloqueadas.
    pub fn unlocked_count(&self) -> usize {
        self.unlocked.len()
    }

    /// Retorna o total de conquistas.
    pub fn total_count(&self) -> usize {
        self.achievements.len()
    }

    /// Retorna a porcentagem de conclusão.
    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
    }

    /// Salva as conquistas em JSON.
    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(())
    }

    /// Carrega as conquistas de JSON.
    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()
    }
}