use std::convert::TryFrom; use std::fmt::Debug; use std::fs::File; use std::io::{BufReader, Read}; use hashbrown::HashSet; use num_traits::PrimInt; use project_root::get_project_root; use serde_json::Value; use pkmn_lib::defines::LevelInt; use pkmn_lib::dynamic_data::DynamicLibrary; use pkmn_lib::dynamic_data::Gen7BattleStatCalculator; use pkmn_lib::dynamic_data::Gen7DamageLibrary; use pkmn_lib::dynamic_data::Gen7MiscLibrary; use pkmn_lib::script_implementations::wasm::script_resolver::WebAssemblyScriptResolver; use pkmn_lib::static_data::{ Ability, AbilityLibrary, BattleItemCategory, DataLibrary, EffectParameter, Form, GrowthRateLibrary, Item, ItemLibrary, LearnableMoves, LibrarySettings, LookupGrowthRate, MoveData, MoveLibrary, Nature, NatureLibrary, SecondaryEffect, Species, StaticData, StaticStatisticSet, Statistic, TypeLibrary, }; use pkmn_lib::StringKey; pub fn load_library() -> DynamicLibrary { let mut path = get_project_root().unwrap(); path.push("tests/data/"); let path = path.to_str().unwrap().to_string(); let mut data = StaticData::new(LibrarySettings::new(100)); load_types(&path, data.types_mut()); load_natures(&path, data.natures_mut()); load_items(&path, data.items_mut()); load_growth_rates(&path, data.growth_rates_mut()); load_abilities(&path, data.abilities_mut()); load_moves(&path, &mut data); load_species(&path, &mut data); let mut resolver = WebAssemblyScriptResolver::new(); load_wasm(&path, resolver.as_mut()); DynamicLibrary::new( data, Box::new(Gen7BattleStatCalculator {}), Box::new(Gen7DamageLibrary::new(false)), Box::new(Gen7MiscLibrary::new()), resolver, ) } pub fn load_types(path: &String, type_library: &mut TypeLibrary) { let mut reader = csv::ReaderBuilder::new() .delimiter(b'|') .from_path(path.to_string() + "Types.csv") .unwrap(); let headers = reader.headers().unwrap(); for header in headers.iter().skip(1) { type_library.register_type(&StringKey::new(header.into())); } for record in reader.records() { let record = record.unwrap(); let offensive_type = record.get(0).unwrap(); let offensive_type_id = type_library.get_type_id(&StringKey::new(offensive_type.into())); for (i, v) in record.iter().skip(1).enumerate() { let effectiveness = v.parse::().unwrap(); type_library.set_effectiveness(offensive_type_id, (i as u8).into(), effectiveness); } } } pub fn load_natures(path: &String, nature_library: &mut NatureLibrary) { let mut reader = csv::ReaderBuilder::new() .delimiter(b'|') .from_path(path.to_string() + "Natures.csv") .unwrap(); for record in reader.records() { let record = record.unwrap(); let nature_name = StringKey::new(record.get(0).unwrap().into()); let increased_statistic_str = record.get(1).unwrap(); let decreased_statistic_str = record.get(2).unwrap(); if increased_statistic_str.is_empty() || decreased_statistic_str.is_empty() { nature_library.load_nature(nature_name, Nature::new(Statistic::HP, Statistic::HP, 1.0, 1.0)); } else { let increased_statistic = serde_plain::from_str(increased_statistic_str).unwrap(); let decreased_statistic = serde_plain::from_str(decreased_statistic_str).unwrap(); nature_library.load_nature( nature_name, Nature::new(increased_statistic, decreased_statistic, 1.1, 0.9), ); } } } pub fn load_items(path: &String, lib: &mut ItemLibrary) { let mut file = File::open(path.to_string() + "Items.json").unwrap(); let mut data = String::new(); file.read_to_string(&mut data).unwrap(); let json: Value = serde_json::from_str(&data).unwrap(); let json_array = json.as_array().unwrap(); for v in json_array { let name = StringKey::new(v.get("name").unwrap().as_str().unwrap().into()); let category = serde_json::from_value(v.get("itemType").unwrap().clone()).unwrap(); let mut battle_category = BattleItemCategory::None; if let Some(c) = v.get("battleType") { battle_category = serde_json::from_value(c.clone()).unwrap(); } let price = v.get("price").unwrap().as_i64().unwrap(); let mut flags = HashSet::new(); if let Some(f) = v.get("flags") { let a = f.as_array().unwrap(); for flag in a { flags.insert(StringKey::new(flag.as_str().unwrap().into())); } } lib.add(&name, Item::new(&name, category, battle_category, price as i32, flags)); } } pub fn load_growth_rates(path: &String, growth_rate_library: &mut GrowthRateLibrary) { let mut file = File::open(path.to_string() + "GrowthRates.json").unwrap(); let mut data = String::new(); file.read_to_string(&mut data).unwrap(); let json: Value = serde_json::from_str(&data).unwrap(); let o = json.as_object().unwrap(); for (key, value) in o { let name = StringKey::new(key); let experience_required_json = value.as_array().unwrap(); let mut experience_required = Vec::with_capacity(experience_required_json.len()); for v in experience_required_json { experience_required.push(v.as_i64().unwrap() as u32); } growth_rate_library.add_growth_rate(&name, Box::new(LookupGrowthRate::new(experience_required))); } } pub fn load_abilities(path: &String, ability_library: &mut AbilityLibrary) { let mut file = File::open(path.to_string() + "Abilities.json").unwrap(); let mut data = String::new(); file.read_to_string(&mut data).unwrap(); let json: Value = serde_json::from_str(&data).unwrap(); let o = json.as_object().unwrap(); for (key, value) in o { let name = StringKey::new(key); let mut effect = StringKey::empty(); if let Some(e) = value.get("effect") { effect = StringKey::new(e.as_str().unwrap().into()); } let mut parameters = Vec::new(); if let Some(p) = value.get("parameters") { for par in p.as_array().unwrap() { parameters.push(parse_effect_parameter(par)); } } ability_library.add(&name, Ability::new(&name, &effect, parameters)); } } pub fn load_moves(path: &String, lib: &mut StaticData) { let mut file = File::open(path.to_string() + "Moves.json").unwrap(); let mut data = String::new(); file.read_to_string(&mut data).unwrap(); let json: Value = serde_json::from_str(&data).unwrap(); let data = json.as_object().unwrap().get("data").unwrap().as_array().unwrap(); for move_data in data { let move_data = move_data.as_object().unwrap(); let move_name = StringKey::new(move_data.get("name").unwrap().as_str().unwrap().into()); let move_type = StringKey::new(move_data.get("type").unwrap().as_str().unwrap().into()); let move_type_id = lib.types().get_type_id(&move_type); let move_category = serde_json::from_value(move_data.get("category").unwrap().clone()).unwrap(); let base_power = move_data.get("power").unwrap().as_i64().unwrap() as u8; let accuracy = move_data.get("accuracy").unwrap().as_i64().unwrap() as u8; let pp = move_data.get("pp").unwrap().as_i64().unwrap() as u8; let target = serde_json::from_value(move_data.get("target").unwrap().clone()).unwrap(); let priority = move_data.get("priority").unwrap().as_i64().unwrap() as i8; let secondary_effect = if let Some(v) = move_data.get("effect") { let mut chance = -1.0; if let Some(chance_value) = v.get("chance") { chance = chance_value.as_f64().unwrap() as f32; } let mut parameters = Vec::new(); if let Some(pars) = v.get("parameters") { let pars = pars.as_array().unwrap(); for par in pars { parameters.push(parse_effect_parameter(par)); } } Some(SecondaryEffect::new( chance, StringKey::new(v.get("name").unwrap().as_str().unwrap().into()), parameters, )) } else { None }; let mut flags = HashSet::new(); if let Some(f) = move_data.get("flags") { let f = f.as_array().unwrap(); for flag in f { flags.insert(StringKey::new(flag.as_str().unwrap().into())); } } lib.moves_mut().add( &move_name, MoveData::new( &move_name.clone(), move_type_id, move_category, base_power, accuracy, pp, target, priority, secondary_effect, flags, ), ); } } pub fn load_species(path: &String, library: &mut StaticData) { let mut file = File::open(path.to_string() + "Pokemon.json").unwrap(); let mut data = String::new(); file.read_to_string(&mut data).unwrap(); let json: Value = serde_json::from_str(&data).unwrap(); let o = json.as_object().unwrap(); for (key, value) in o.iter() { if key.starts_with('$') { continue; } let name = StringKey::new(key); let id = value.get("id").unwrap().as_i64().unwrap(); let gender_rate = value.get("genderRatio").unwrap().as_f64().unwrap(); let growth_rate_name = StringKey::new(value.get("growthRate").unwrap().as_str().unwrap().into()); let _base_happiness = value.get("baseHappiness").unwrap().as_i64().unwrap(); let catch_rate = value.get("catchRate").unwrap().as_i64().unwrap(); let _color = value.get("color").unwrap().as_str().unwrap(); // let egg_groups = value.get("eggGroups").unwrap() // .as_array() // .unwrap() // .iter() // .map(|&a| a.as_str().unwrap()) // .collect(); let _egg_cycle = value.get("eggCycles").unwrap().as_i64().unwrap(); // TODO: tags // TODO: evolutions let forms = value.get("formes").unwrap().as_object().unwrap(); let default_form_value = forms.get("default").unwrap(); let default_form = parse_form("default".into(), default_form_value, library); let species = Species::new( id as u16, &name, gender_rate as f32, &growth_rate_name, catch_rate as u8, default_form, Default::default(), ); library.species_mut().add(&name, species); } } fn load_wasm(path: &String, library: &mut WebAssemblyScriptResolver) { let file = File::open(path.to_string() + "gen7_scripts.wasm").unwrap(); let mut reader = BufReader::new(file); let mut buffer = Vec::new(); reader.read_to_end(&mut buffer).unwrap(); library.load_wasm_from_bytes(&buffer); library.finalize(); } fn parse_form(name: StringKey, value: &Value, library: &mut StaticData) -> Form { let mut abilities = Vec::new(); for a in value.get("abilities").unwrap().as_array().unwrap() { abilities.push(StringKey::new(a.as_str().unwrap().into())); } let mut hidden_abilities = Vec::new(); for a in value.get("hiddenAbilities").unwrap().as_array().unwrap() { hidden_abilities.push(StringKey::new(a.as_str().unwrap().into())); } let base_stats = parse_statistics(&value.get("baseStats").unwrap()); // TODO: ev reward let height = value.get("height").unwrap().as_f64().unwrap(); let weight = value.get("weight").unwrap().as_f64().unwrap(); let base_experience = value.get("baseExp").unwrap().as_u64().unwrap(); let types = value .get("types") .unwrap() .as_array() .unwrap() .iter() .map(|a| library.types().get_type_id(&StringKey::new(a.as_str().unwrap().into()))) .collect(); let moves = parse_moves(&value.get("moves").unwrap(), library.moves()); Form::new( &name, height as f32, weight as f32, base_experience as u32, types, base_stats, abilities, hidden_abilities, moves, Default::default(), ) } fn parse_statistics(value: &Value) -> StaticStatisticSet where T: PrimInt + TryFrom, >::Error: Debug, { StaticStatisticSet::new( >::try_from(value.get("hp").unwrap_or(&Value::Number(0.into())).as_u64().unwrap()).unwrap(), >::try_from( value .get("attack") .unwrap_or(&Value::Number(0.into())) .as_u64() .unwrap(), ) .unwrap(), >::try_from( value .get("defense") .unwrap_or(&Value::Number(0.into())) .as_u64() .unwrap(), ) .unwrap(), >::try_from( value .get("specialAttack") .unwrap_or(&Value::Number(0.into())) .as_u64() .unwrap(), ) .unwrap(), >::try_from( value .get("specialDefense") .unwrap_or(&Value::Number(0.into())) .as_u64() .unwrap(), ) .unwrap(), >::try_from(value.get("speed").unwrap_or(&Value::Number(0.into())).as_u64().unwrap()) .unwrap(), ) } fn parse_moves(value: &Value, move_library: &MoveLibrary) -> LearnableMoves { let mut moves = LearnableMoves::default(); let level_moves = value.get("levelMoves").unwrap().as_array().unwrap(); for level_move in level_moves { let name = StringKey::new(level_move.get("name").unwrap().as_str().unwrap().into()); let level = level_move.get("level").unwrap().as_u64().unwrap() as LevelInt; assert!(move_library.get(&name).is_some()); moves.add_level_move(level, &name); } moves } fn parse_effect_parameter(value: &Value) -> EffectParameter { match value { Value::Null => { panic!("Unexpected type") } Value::Bool(b) => EffectParameter::Bool(*b), Value::Number(n) => { if n.is_f64() { EffectParameter::Float(n.as_f64().unwrap() as f32) } else { EffectParameter::Int(n.as_i64().unwrap()) } } Value::String(s) => EffectParameter::String(StringKey::new(s.as_str().into())), Value::Array(_) => { panic!("Unexpected type") } Value::Object(_) => { panic!("Unexpected type") } } } #[test] #[cfg_attr(miri, ignore)] fn test_type_library_loaded() { let mut path = get_project_root().unwrap(); path.push("tests/data/"); let mut lib = TypeLibrary::new(18); load_types(&path.to_str().unwrap().to_string(), &mut lib); assert_eq!( lib.get_effectiveness( lib.get_type_id(&StringKey::new("fire".into())), &[lib.get_type_id(&StringKey::new("grass".into()))], ), 2.0 ); }