You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

138 lines
4.7 KiB

use std::fs::File;
use serde_json::Value;
use entity::character::CharacterClass;
use std::sync::LazyLock;
pub static LEVEL_TABLE: LazyLock<CharacterLevelTable> = LazyLock::new(CharacterLevelTable::default);
#[derive(Default, Copy, Clone, Debug, PartialEq, Eq)]
pub struct CharacterStats {
pub hp: u16,
pub atp: u16,
pub mst: u16,
pub evp: u16,
pub dfp: u16,
pub ata: u16,
pub lck: u16,
}
#[derive(Default, Copy, Clone, Debug)]
struct CharacterLevelEntry {
hp: u32,
atp: u32,
mst: u32,
evp: u32,
dfp: u32,
ata: u32,
lck: u32,
exp: u32,
}
pub struct CharacterLevelTable {
table: Box<[[CharacterLevelEntry; 200]; 12]>,
}
impl Default for CharacterLevelTable {
fn default() -> CharacterLevelTable {
let file = File::open("data/char_stats.json").unwrap();
let json: Value = serde_json::from_reader(file).unwrap();
let mut table = Box::new([[CharacterLevelEntry::default(); 200]; 12]);
for it in json.as_object().unwrap().iter(){
let cl = match it.0.as_str() {
"HUmar" => CharacterClass::HUmar,
"HUnewearl" => CharacterClass::HUnewearl,
"HUcast" => CharacterClass::HUcast,
"HUcaseal" => CharacterClass::HUcaseal,
"RAmar" => CharacterClass::RAmar,
"RAmarl" => CharacterClass::RAmarl,
"RAcast" => CharacterClass::RAcast,
"RAcaseal" => CharacterClass::RAcaseal,
"FOmar" => CharacterClass::FOmar,
"FOmarl" => CharacterClass::FOmarl,
"FOnewm" => CharacterClass::FOnewm,
"FOnewearl" => CharacterClass::FOnewearl,
_ => panic!("unexpected class in char stats"),
};
let mut statlist = [CharacterLevelEntry::default(); 200];
for (i, stat) in it.1.as_array().unwrap().iter().enumerate() {
statlist[i] = CharacterLevelEntry {
hp: stat["hp"].as_i64().unwrap() as u32,
atp: stat["atp"].as_i64().unwrap() as u32,
mst: stat["mst"].as_i64().unwrap() as u32,
evp: stat["evp"].as_i64().unwrap() as u32,
dfp: stat["dfp"].as_i64().unwrap() as u32,
ata: stat["ata"].as_i64().unwrap() as u32,
lck: stat["lck"].as_i64().unwrap() as u32,
exp: stat["xp"].as_i64().unwrap() as u32,
}
}
table[u8::from(cl) as usize] = statlist;
}
CharacterLevelTable {
table,
}
}
}
impl CharacterLevelTable {
pub fn get_level_from_exp(&self, ch_class: CharacterClass, exp: u32) -> u32 {
if let Some(statlist) = self.table.get(u8::from(ch_class) as usize) {
statlist
.iter()
.filter(|stat| {
stat.exp <= exp
})
.count() as u32
}
else {
0
}
}
pub fn get_stats_from_exp(&self, ch_class: CharacterClass, exp: u32) -> (u32, CharacterStats) {
if let Some(statlist) = self.table.get(u8::from(ch_class) as usize) {
statlist
.iter()
.filter(|stat| {
stat.exp <= exp
})
.fold((0, CharacterStats::default()), |acc, &k| {
(acc.0 + 1, CharacterStats {
hp: acc.1.hp + k.hp as u16,
atp: acc.1.atp + k.atp as u16,
mst: acc.1.mst + k.mst as u16,
evp: acc.1.evp + k.evp as u16,
dfp: acc.1.dfp + k.dfp as u16,
ata: acc.1.ata + k.ata as u16,
lck: acc.1.lck + k.lck as u16,
})
})
}
else {
(0, CharacterStats::default())
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_stat_levels() {
let table = CharacterLevelTable::default();
assert!(table.get_stats_from_exp(CharacterClass::FOmarl, 0) == (1, CharacterStats { hp: 20, atp: 13, mst: 53, evp: 35, dfp: 10, ata: 15, lck: 10 }));
assert!(table.get_stats_from_exp(CharacterClass::FOmarl, 1 << 17) == (36, CharacterStats { hp: 125, atp: 114, mst: 219, evp: 182, dfp: 42, ata: 213, lck: 10 }));
}
#[test]
fn test_levels() {
let table = CharacterLevelTable::default();
assert!(table.get_level_from_exp(CharacterClass::FOmarl, 0) == 1);
assert!(table.get_level_from_exp(CharacterClass::FOmarl, 3000) == 8);
assert!(table.get_level_from_exp(CharacterClass::FOmarl, 3200) == 9);
}
}