1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
//! Skills are used to track a meta-progression of a player's abilities.
//!
//! Skills keep track of both the experience points and the level of the skill.
//!
//! Skills are able to be leveled up, and have no level cap but do have a
//! soft cap that makes it harder to level up the higher the skill is (this is
//! automatically handled by the [`game_library::Xp`] component).

use bevy::{
    ecs::{component::Component, system::Resource},
    reflect::Reflect,
    utils::HashMap,
};
use bevy_inspector_egui::inspector_options::{InspectorOptions, ReflectInspectorOptions};
use serde::{Deserialize, Serialize};

use crate::{enums::Skill, Xp};

/// Skills are used to track a meta-progression of a player's abilities.
///
/// Skills are able to be leveled up, and have no level cap but do have a
/// soft cap that makes it harder to level up the higher the skill is.
///
/// Skill levels allocate points to the skill's attributes, which are
/// used when playing the game, making some schools of magic more powerful
/// than others.
///
/// Skills are also used to gate spells, so that a player must have a certain
/// level in a skill before they can unlock a spell. All of that is built on top
/// of this skill system.
#[derive(
    Resource,
    Component,
    Debug,
    Clone,
    PartialEq,
    Eq,
    Serialize,
    Deserialize,
    Reflect,
    InspectorOptions,
)]
#[reflect(InspectorOptions)]
#[allow(clippy::module_name_repetitions)]
pub struct Skills {
    /// Skill tracks for each of the different skills.
    pub tracks: HashMap<Skill, Xp>,
}

impl Skills {
    /// Get xp for a skill.
    #[must_use]
    pub fn get_xp(&self, skill: Skill) -> Option<&Xp> {
        self.tracks.get(&skill)
    }
    /// Get the level for a skill.
    #[must_use]
    pub fn get_level(&self, skill: Skill) -> Option<u32> {
        self.get_xp(skill).map(|xp| xp.current_level)
    }
    /// Get the percentage to the next level for a skill.
    #[must_use]
    pub fn get_percentage_to_next_level(&self, skill: Skill) -> Option<f32> {
        self.get_xp(skill).map(Xp::next_level_progress)
    }
    /// Add xp to a skill.
    pub fn add_xp(&mut self, skill: Skill, xp: u32) {
        if let Some(skill_xp) = self.tracks.get_mut(&skill) {
            skill_xp.add(xp);
        }
    }
    /// Level-up a skill.
    pub fn level_up(&mut self, skill: Skill) {
        if let Some(skill_xp) = self.tracks.get_mut(&skill) {
            skill_xp.level_up();
        }
    }
}