hypixel_api/api/util/leveling/
network.rs

1//! This module provides utility fuctions to work with Hypixel's network level and network experience.
2//!
3//!
4//! This module follows the java implementation of the Hypixel API.\
5//! See [here](https://github.com/HypixelDev/PublicAPI/blob/master/hypixel-api-core/src/main/java/net/hypixel/api/util/ILeveling.java).
6//!
7//! From the formulas written in this implementation, it can be seen
8//! that the relation between the network level and the network experience can be expressed as:
9//! ```math
10//! \frac{dy}{dx} = gx + b \;\;\;\;\; \text{and} \;\;\;\;\; y(1) = 0
11//! ```
12//! where:
13//! - y = network xp
14//! - x = network lvl
15//! - g = [`GROWTH`]
16//! - b = [`BASE`]
17//!
18//! More specific: the integer part of the network level corresponds to an amount of experience
19//! that follows these equations. The fractional part of a network level corresponds to an amount of
20//! experience that is is linearly interpolated between two experience amounts corresponding to nearest surrounding
21//! integer network levels.
22
23pub const BASE: f64 = 10000.0;
24pub const GROWTH: f64 = 2500.0;
25
26const HALF_GROWTH: f64 = GROWTH * 0.5;
27
28const REVERSE_PQ_PREFIX: f64 = -(BASE - 0.5 * GROWTH) / GROWTH;
29const REVERSE_CONST: f64 = REVERSE_PQ_PREFIX * REVERSE_PQ_PREFIX;
30const GROWTH_DIVIDES_2: f64 = 2.0 / GROWTH;
31
32/// This function returns the level of a player calculated by the
33/// current experience gathered. Unlike [`exact_level`], this function returns
34/// the largest integer smaller than the exact level (= floored).
35///
36/// The result cannot be smaller than `1.0` and negative experience results in `1.0`.
37///
38/// # Examples
39/// ```ignore
40///            0 XP -> 1.0
41///         5000 XP -> 1.0
42///        10000 XP -> 2.0
43///        50000 XP -> 4.0
44///     79342431 XP -> 249.0
45/// ```
46pub fn calculate_level(exp: f64) -> f64 {
47    if exp < 0.0 {
48        1.0
49    } else {
50        (1.0 + REVERSE_PQ_PREFIX + (REVERSE_CONST + GROWTH_DIVIDES_2 * exp).sqrt()).floor()
51    }
52}
53
54/// This function returns the exact level of a player calculated by the
55/// current experience gathered. Unlike [`calculate_level`], this function does
56/// not floor its result and will return an accurate level.
57///
58/// The result cannot be smaller than `1.0` and negative experience results in `1.0`.
59///
60/// # Examples
61/// ```ignore
62///            0 XP -> 1.0
63///         5000 XP -> 1.5
64///        10000 XP -> 2.0
65///        50000 XP -> 4.71...
66///     79342431 XP -> 249.46...
67/// ```
68pub fn exact_level(exp: f64) -> f64 {
69    calculate_level(exp) + percentage_to_next_level(exp)
70}
71
72/// This function returns the amount of experience that is needed to progress from `level` to `level + 1`. (e.g. 5 to 6)
73/// The levels passed *must* be absolute levels with the smallest level being 1.
74/// Smaller values always return the `BASE` constant. The calculation is precise and
75/// if a decimal is passed, it returns the XP from the progress of this level to the next
76/// level with the same progress. (e.g. 5.5 to 6.5)
77///
78/// # Examples
79/// ```ignore
80///       1 (to 2)   =  10000.0 XP
81///       2 (to 3)   =  12500.0 XP
82///       3 (to 4)   =  15000.0 XP
83///       5 (to 6)   =  20000.0 XP
84///     5.5 (to 6.5) =  21250.0 XP
85///     130 (to 131) = 332500.0 XP
86///     250 (to 251) = 632500.0 XP
87/// ```
88pub fn xp_to_next_level(level: f64) -> f64 {
89    if level < 1.0 {
90        BASE
91    } else {
92        GROWTH * (level - 1.0) + BASE
93    }
94}
95
96/// This method returns the experience required to reach that level. This method is precise, that means
97/// you can pass any progress of a level to receive the experience to reach that progress. (e.g. 5.764 returns
98/// the experience required to reach level 5 and 76.4% of the experience required to go from level 5 to level 6).
99///
100/// # Examples
101/// ```ignore
102///        1.0 =        0.0 XP
103///        2.0 =    10000.0 XP
104///        3.0 =    22500.0 XP
105///        5.0 =    55000.0 XP
106///      5.764 =    70280.0 XP
107///      130.0 = 21930000.0 XP
108///     250.43 = 79951975.0 XP
109/// ```
110pub fn total_xp_to_level(level: f64) -> f64 {
111    let lvl = level.floor();
112    let x0 = total_xp_to_full_level(lvl);
113    (total_xp_to_full_level(lvl + 1.0) - x0) * (level % 1.0) + x0
114}
115
116/// Helper method that may only be called by full levels (meaning no fractional part)
117/// and has the same functionality as [`total_xp_to_level`] but doesn't support progress
118/// and will return wrong values due to following a parabola instead of a line in between integer levels.
119pub fn total_xp_to_full_level(level: f64) -> f64 {
120    (HALF_GROWTH * (level - 2.0) + BASE) * (level - 1.0)
121}
122
123/// This method returns the current progress of this level to reach the next level. This method is as
124/// precise as possible due to rounding errors on the mantissa. The first 10 decimals are totally
125/// accurate.
126///
127/// # Examples
128/// ```ìgnore
129///         5000.0 XP   (Lv. 1) = 0.5                               (50 %)
130///        22499.0 XP   (Lv. 2) = 0.99992                       (99.992 %)
131///      5324224.0 XP  (Lv. 62) = 0.856763076923077   (85.6763076923077 %)
132///     23422443.0 XP (Lv. 134) = 0.4304905109489051 (43.04905109489051 %)
133/// ```
134pub fn percentage_to_next_level(exp: f64) -> f64 {
135    let lvl = calculate_level(exp);
136    let x0 = total_xp_to_level(lvl);
137    (exp - x0) / (total_xp_to_level(lvl + 1.0) - x0)
138}