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 crate::entity::item::ItemDetail; use crate::entity::item::armor::{Armor, ArmorType}; use crate::entity::item::shield::{Shield, ShieldType}; use crate::entity::item::unit::{Unit, UnitType}; use crate::ship::shops::ShopItem; use crate::ship::item_stats::{ARMOR_STATS, SHIELD_STATS, UNIT_STATS}; #[derive(Debug)] pub enum ArmorShopItem { Frame(ArmorType, usize), Barrier(ShieldType), Unit(UnitType), } const ARMOR_MULTIPLIER: f32 = 0.799999952; const SHIELD_MULTIPLIER: f32 = 1.5; const UNIT_MULTIPLIER: f32 = 1000.0; impl ShopItem for ArmorShopItem { fn price(&self) -> usize { match self { ArmorShopItem::Frame(frame, slot) => { ARMOR_STATS.get(&frame) .map(|frame_stats| { let mut price = (frame_stats.dfp + frame_stats.evp) as f32; price *= price; price /= ARMOR_MULTIPLIER; price += 70.0 * frame_stats.level_req as f32; price += 70.0 * frame_stats.level_req as f32 * *slot as f32; price as usize }) .unwrap_or(0xFFFF) }, ArmorShopItem::Barrier(barrier) => { SHIELD_STATS.get(&barrier) .map(|barrier_stats| { let mut price = (barrier_stats.dfp + barrier_stats.evp) as f32; price *= price; price /= SHIELD_MULTIPLIER; price += 70.0 * barrier_stats.level_req as f32; price as usize }) .unwrap_or(0xFFFF) }, ArmorShopItem::Unit(unit) => { UNIT_STATS.get(&unit) .map(|unit_stats| { (unit_stats.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, slot) => { ItemDetail::Armor(Armor { armor: *frame, dfp: 0, evp: 0, slots: *slot as u8, wrapping: None, }) }, ArmorShopItem::Barrier(barrier) => { ItemDetail::Shield(Shield { shield: *barrier, dfp: 0, evp: 0, wrapping: None, }) }, ArmorShopItem::Unit(unit) => { ItemDetail::Unit(Unit { unit: *unit, modifier: None, wrapping: None, }) }, } } } #[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 ArmorShop { pub fn new() -> ArmorShop { ArmorShop { frame: load_frame_table(), barrier: load_barrier_table(), unit: load_unit_table(), rng: R::from_entropy(), } } 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(frame_detail.item, slot.slot) }) .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(barrier_detail.item) }) .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_detail.item) }) .collect() }) .unwrap_or(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).into_iter()) .chain(self.generate_unit_list(character_level).into_iter()) .collect() } } #[cfg(test)] mod test { use super::*; #[test] fn test_loading_tool_shop() { ArmorShop::::new(); } #[test] fn test_generating_some_armor() { let mut fs = ArmorShop::::new(); for i in 0..200 { fs.generate_armor_list(i); } } }