use std::fs::File; use std::io::Read; use std::path::PathBuf; use std::convert::TryInto; use serde::Deserialize; use rand::{Rng, SeedableRng}; use rand::distributions::{WeightedIndex, Distribution}; use entity::item::ItemDetail; use entity::item::armor::{Armor, ArmorType}; use entity::item::shield::{Shield, ShieldType}; use entity::item::unit::{Unit, UnitType}; use crate::ShopItem; use stats::items::{ARMOR_STATS, SHIELD_STATS, UNIT_STATS}; // #[derive(Debug)] // pub enum ArmorShopItem { // Frame(ArmorType, usize), // slots // Barrier(ShieldType), // Unit(UnitType), // } #[derive(Debug)] pub enum ArmorShopItem { Frame(Armor), // slots Barrier(Shield), Unit(Unit), } const ARMOR_MULTIPLIER: f32 = 0.799_999_95; const SHIELD_MULTIPLIER: f32 = 1.5; const UNIT_MULTIPLIER: f32 = 1000.0; // TODO: reduce the number of type casts? impl ShopItem for ArmorShopItem { fn price(&self) -> usize { match self { ArmorShopItem::Frame(frame) => { ARMOR_STATS.get(&frame.armor) .map(|frame_stats| { let mut price = (frame_stats.dfp + frame_stats.evp + frame.dfp as i32 + frame.evp as i32) as f32; price *= price; price /= ARMOR_MULTIPLIER; price += 70.0 * (frame_stats.level_req + 1) as f32; price += 70.0 * (frame_stats.level_req + 1) as f32 * frame.slots as f32; price as usize }) .unwrap_or(0xFFFF) }, ArmorShopItem::Barrier(barrier) => { SHIELD_STATS.get(&barrier.shield) .map(|barrier_stats| { let mut price = (barrier_stats.dfp + barrier_stats.evp + barrier.dfp as i32 + barrier.evp as i32) as f32; price *= price; price /= SHIELD_MULTIPLIER; price += 70.0 * (barrier_stats.level_req + 1) as f32; price as usize }) .unwrap_or(0xFFFF) }, ArmorShopItem::Unit(unit) => { UNIT_STATS.get(&unit.unit) .map(|unit_stats| { ((unit_stats.stars as f32 + unit.modifier_stars() as f32) * UNIT_MULTIPLIER) as usize }) .unwrap_or(0xFFFF) } } } fn as_bytes(&self) -> [u8; 12] { self.as_item().as_client_bytes()[0..12].try_into().unwrap() } fn as_item(&self) -> ItemDetail { match self { ArmorShopItem::Frame(frame) => { ItemDetail::Armor(Armor { armor: frame.armor, dfp: 0, evp: 0, slots: frame.slots, }) }, ArmorShopItem::Barrier(barrier) => { ItemDetail::Shield(Shield { shield: barrier.shield, dfp: 0, evp: 0, }) }, ArmorShopItem::Unit(unit) => { ItemDetail::Unit(Unit { unit: unit.unit, modifier: None, }) }, } } } impl From<&Armor> for ArmorShopItem { fn from(armor: &Armor) -> ArmorShopItem { ArmorShopItem::Frame(*armor) } } impl From<&Shield> for ArmorShopItem { fn from(shield: &Shield) -> ArmorShopItem { ArmorShopItem::Barrier(*shield) } } impl From<&Unit> for ArmorShopItem { fn from(unit: &Unit) -> ArmorShopItem { ArmorShopItem::Unit(*unit) } } #[derive(Debug, Deserialize, Clone)] struct FrameTierItem { item: ArmorType, probability: usize, } #[derive(Debug, Deserialize, Clone)] struct FrameTier { level: usize, item: Vec, } #[derive(Debug, Deserialize)] struct SlotRate { slot: usize, probability: usize, } #[derive(Debug, Deserialize)] struct FrameTable { frame: Vec, slot_rate: Vec, } #[derive(Debug, Deserialize, Clone)] struct BarrierTierItem { item: ShieldType, probability: usize, } #[derive(Debug, Deserialize, Clone)] struct BarrierTier { level: usize, item: Vec, } #[derive(Debug, Deserialize)] struct BarrierTable { barrier: Vec, } #[derive(Debug, Deserialize, Clone)] struct UnitTierItem { item: UnitType, probability: usize, } #[derive(Debug, Deserialize, Clone)] struct UnitTier { level: usize, item: Vec, } #[derive(Debug, Deserialize)] struct UnitTable { unit: Vec, } fn load_frame_table() -> FrameTable { let path = PathBuf::from("data/shops/frame.toml"); let mut f = File::open(path).unwrap(); let mut s = String::new(); f.read_to_string(&mut s).unwrap(); let table: FrameTable = toml::from_str(s.as_str()).unwrap(); table } fn load_barrier_table() -> BarrierTable { let path = PathBuf::from("data/shops/barrier.toml"); let mut f = File::open(path).unwrap(); let mut s = String::new(); f.read_to_string(&mut s).unwrap(); let table: BarrierTable = toml::from_str(s.as_str()).unwrap(); table } fn load_unit_table() -> UnitTable { let path = PathBuf::from("data/shops/unit.toml"); let mut f = File::open(path).unwrap(); let mut s = String::new(); f.read_to_string(&mut s).unwrap(); let table: UnitTable = toml::from_str(s.as_str()).unwrap(); table } fn number_of_frames_to_generate(character_level: usize) -> usize { if character_level <= 10 { 4 } else if character_level <= 25 { 6 } else if character_level <= 42 { 7 } else { 8 } } fn number_of_barriers_to_generate(character_level: usize) -> usize { if character_level <= 10 { 4 } else if character_level <= 25 { 5 } else if character_level <= 42 { 6 } else { 7 } } fn number_of_units_to_generate(character_level: usize) -> usize { if character_level <= 10 { 0 } else if character_level <= 25 { 3 } else if character_level <= 42 { 5 } else { 6 } } #[derive(Debug)] pub struct ArmorShop { frame: FrameTable, barrier: BarrierTable, unit: UnitTable, rng: R, } impl Default for ArmorShop { fn default() -> ArmorShop { ArmorShop { frame: load_frame_table(), barrier: load_barrier_table(), unit: load_unit_table(), rng: R::from_entropy(), } } } impl ArmorShop { fn generate_frame_list(&mut self, character_level: usize) -> Vec { let tier = self.frame.frame.iter() .filter(|t| t.level <= character_level) .last() .cloned() .unwrap(); let frame_choice = WeightedIndex::new(tier.item.iter().map(|f| f.probability)).unwrap(); let slot_choice = WeightedIndex::new(self.frame.slot_rate.iter().map(|sr| sr.probability)).unwrap(); (0..number_of_frames_to_generate(character_level)) .map(|_| { let frame_detail = tier.item.get(frame_choice.sample(&mut self.rng)).unwrap(); let slot = self.frame.slot_rate.get(slot_choice.sample(&mut self.rng)).unwrap(); ArmorShopItem::Frame(Armor { armor: frame_detail.item, dfp: 0, evp: 0, slots: slot.slot as u8, }) }) .collect() } fn generate_barrier_list(&mut self, character_level: usize) -> Vec { let tier = self.barrier.barrier.iter() .filter(|t| t.level <= character_level) .last() .cloned() .unwrap(); let barrier_choice = WeightedIndex::new(tier.item.iter().map(|b| b.probability)).unwrap(); (0..number_of_barriers_to_generate(character_level)) .map(|_| { let barrier_detail = tier.item.get(barrier_choice.sample(&mut self.rng)).unwrap(); ArmorShopItem::Barrier(Shield { shield: barrier_detail.item, dfp: 0, evp: 0, }) }) .collect() } fn generate_unit_list(&mut self, character_level: usize) -> Vec { self.unit.unit.iter() .filter(|t| t.level <= character_level) .last() .cloned() .map(|tier| { let unit_choice = WeightedIndex::new(tier.item.iter().map(|u| u.probability)).unwrap(); (0..number_of_units_to_generate(character_level)) .map(|_| { let unit_detail = tier.item.get(unit_choice.sample(&mut self.rng)).unwrap(); ArmorShopItem::Unit(Unit { unit: unit_detail.item, modifier: None, }) }) .collect() }) .unwrap_or_else(Vec::new) } pub fn generate_armor_list(&mut self, character_level: usize) -> Vec { self.generate_frame_list(character_level).into_iter() .chain(self.generate_barrier_list(character_level)) .chain(self.generate_unit_list(character_level)) .collect() } } #[cfg(test)] mod test { use super::*; #[test] fn test_loading_tool_shop() { ArmorShop::::default(); } #[test] fn test_generating_some_armor() { let mut fs = ArmorShop::::default(); for i in 0..200 { fs.generate_armor_list(i); } } }