1use crate::models::resources::{Skill, SkillLevel};
2
3#[derive(Debug, Clone, Copy, PartialEq)]
5pub struct LevelProgress {
6 pub level: u32,
8 pub total_xp: f64,
10 pub xp_into_level: f64,
12 pub xp_to_next: Option<f64>,
14 pub fractional_level: f64,
16}
17
18pub fn level_from_cumulative(total_xp: f64, cumulative: &[f64]) -> LevelProgress {
25 let mut level = 0u32;
26 let mut level_start = 0.0;
27 for &threshold in cumulative {
28 if total_xp < threshold {
29 let span = threshold - level_start;
30 let into = total_xp - level_start;
31 let fractional = if span > 0.0 { into / span } else { 0.0 };
32 return LevelProgress {
33 level,
34 total_xp,
35 xp_into_level: into,
36 xp_to_next: Some(threshold - total_xp),
37 fractional_level: level as f64 + fractional,
38 };
39 }
40 level += 1;
41 level_start = threshold;
42 }
43 LevelProgress {
44 level,
45 total_xp,
46 xp_into_level: total_xp - level_start,
47 xp_to_next: None,
48 fractional_level: level as f64,
49 }
50}
51
52pub fn cumulative_from_levels(levels: &[SkillLevel]) -> Vec<f64> {
54 let mut thresholds: Vec<f64> = levels.iter().map(|l| l.total_exp_required).collect();
55 thresholds.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
56 thresholds
57}
58
59pub fn skill_level(total_xp: f64, skill: &Skill) -> LevelProgress {
62 let cumulative = cumulative_from_levels(&skill.levels);
63 level_from_cumulative(total_xp, &cumulative)
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 fn table() -> Vec<f64> {
71 vec![100.0, 250.0, 500.0]
72 }
73
74 #[test]
75 fn resolves_mid_level() {
76 let p = level_from_cumulative(175.0, &table());
77 assert_eq!(p.level, 1);
78 assert_eq!(p.xp_into_level, 75.0);
79 assert_eq!(p.xp_to_next, Some(75.0));
80 assert_eq!(p.fractional_level, 1.5);
81 }
82
83 #[test]
84 fn caps_at_max_level() {
85 let p = level_from_cumulative(900.0, &table());
86 assert_eq!(p.level, 3);
87 assert_eq!(p.xp_to_next, None);
88 assert_eq!(p.fractional_level, 3.0);
89 }
90
91 #[test]
92 fn handles_zero() {
93 let p = level_from_cumulative(0.0, &table());
94 assert_eq!(p.level, 0);
95 assert_eq!(p.fractional_level, 0.0);
96 }
97}