use std::collections::HashMap; use thiserror::Error; use serde::{Serialize, Deserialize}; use crate::item::tool::ToolType; use crate::character::{CharacterClass, SectionID}; use crate::item::ItemEntityId; use std::io::Read; use std::cmp::Ordering::{Less, Greater, Equal}; #[derive(Debug, Deserialize)] struct MagStats { feed_table: usize, photon_blast: Option } #[derive(Debug, Deserialize)] struct MagFeedTable { def: i16, pow: i16, dex: i16, mnd: i16, iq: i8, syn: i8, } lazy_static::lazy_static! { static ref MAG_STATS: HashMap = { let mut f = std::fs::File::open("data/item_stats/mag_stats.toml").unwrap(); let mut s = String::new(); f.read_to_string(&mut s).unwrap(); let mag_stats: HashMap = toml::from_str(&s).unwrap(); mag_stats.into_iter() .map(|(name, stats)| { (name.parse().unwrap(), stats) }) .collect::>() }; static ref MAG_FEEDING_TABLES: Vec> = { let mut f = std::fs::File::open("data/item_stats/mag_feed_table.toml").unwrap(); let mut s = String::new(); f.read_to_string(&mut s).unwrap(); let mut feed: HashMap>> = toml::from_str(&s).unwrap(); let feed = feed.remove("feedtable").unwrap(); feed.into_iter() .map(|table| { table.into_iter() .map(|(tool, stats)| { (tool.parse().unwrap(), stats) }) .collect() }) .collect::>>() }; } #[derive(Debug, Copy, Clone)] pub enum ItemParseError { InvalidMagType, InvalidMagBytes, } #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, Serialize, Deserialize, enum_utils::FromStr, derive_more::Display, strum_macros::EnumIter)] pub enum MagType { Mag, Varuna, Mitra, Surya, Vayu, Varaha, Kama, Ushasu, Apsaras, Kumara, Kaitabha, Tapas, Bhirava, Kalki, Rudra, Marutah, Yaksa, Sita, Garuda, Nandin, Ashvinau, Ribhava, Soma, Ila, Durga, Vritra, Namuci, Sumba, Naga, Pitri, Kabanda, Ravana, Marica, Soniti, Preta, Andhaka, Bana, Naraka, Madhu, Churel, Robochao, OpaOpa, Pian, Chao, ChuChu, KapuKapu, AngelsWing, DevilsWing, Elenor, MarkIII, MasterSystem, Genesis, SegaSaturn, Dreamcast, Hamburger, PanzersTail, DevilsTail, Deva, Rati, Savitri, Rukmin, Pushan, Diwari, Sato, Bhima, Nidra, GeungSi, Tellusis, StrikerUnit, Pioneer, Puyo, Moro, Rappy, Yahoo, GaelGiel, Agastya, } impl MagType { pub fn value(&self) -> [u8; 3] { match self { MagType::Mag => [0x02, 0x00, 0x00], MagType::Varuna => [0x02, 0x01, 0x00], MagType::Mitra => [0x02, 0x02, 0x00], MagType::Surya => [0x02, 0x03, 0x00], MagType::Vayu => [0x02, 0x04, 0x00], MagType::Varaha => [0x02, 0x05, 0x00], MagType::Kama => [0x02, 0x06, 0x00], MagType::Ushasu => [0x02, 0x07, 0x00], MagType::Apsaras => [0x02, 0x08, 0x00], MagType::Kumara => [0x02, 0x09, 0x00], MagType::Kaitabha => [0x02, 0x0A, 0x00], MagType::Tapas => [0x02, 0x0B, 0x00], MagType::Bhirava => [0x02, 0x0C, 0x00], MagType::Kalki => [0x02, 0x0D, 0x00], MagType::Rudra => [0x02, 0x0E, 0x00], MagType::Marutah => [0x02, 0x0F, 0x00], MagType::Yaksa => [0x02, 0x10, 0x00], MagType::Sita => [0x02, 0x11, 0x00], MagType::Garuda => [0x02, 0x12, 0x00], MagType::Nandin => [0x02, 0x13, 0x00], MagType::Ashvinau => [0x02, 0x14, 0x00], MagType::Ribhava => [0x02, 0x15, 0x00], MagType::Soma => [0x02, 0x16, 0x00], MagType::Ila => [0x02, 0x17, 0x00], MagType::Durga => [0x02, 0x18, 0x00], MagType::Vritra => [0x02, 0x19, 0x00], MagType::Namuci => [0x02, 0x1A, 0x00], MagType::Sumba => [0x02, 0x1B, 0x00], MagType::Naga => [0x02, 0x1C, 0x00], MagType::Pitri => [0x02, 0x1D, 0x00], MagType::Kabanda => [0x02, 0x1E, 0x00], MagType::Ravana => [0x02, 0x1F, 0x00], MagType::Marica => [0x02, 0x20, 0x00], MagType::Soniti => [0x02, 0x21, 0x00], MagType::Preta => [0x02, 0x22, 0x00], MagType::Andhaka => [0x02, 0x23, 0x00], MagType::Bana => [0x02, 0x24, 0x00], MagType::Naraka => [0x02, 0x25, 0x00], MagType::Madhu => [0x02, 0x26, 0x00], MagType::Churel => [0x02, 0x27, 0x00], MagType::Robochao => [0x02, 0x28, 0x00], MagType::OpaOpa => [0x02, 0x29, 0x00], MagType::Pian => [0x02, 0x2A, 0x00], MagType::Chao => [0x02, 0x2B, 0x00], MagType::ChuChu => [0x02, 0x2C, 0x00], MagType::KapuKapu => [0x02, 0x2D, 0x00], MagType::AngelsWing => [0x02, 0x2E, 0x00], MagType::DevilsWing => [0x02, 0x2F, 0x00], MagType::Elenor => [0x02, 0x30, 0x00], MagType::MarkIII => [0x02, 0x31, 0x00], MagType::MasterSystem => [0x02, 0x32, 0x00], MagType::Genesis => [0x02, 0x33, 0x00], MagType::SegaSaturn => [0x02, 0x34, 0x00], MagType::Dreamcast => [0x02, 0x35, 0x00], MagType::Hamburger => [0x02, 0x36, 0x00], MagType::PanzersTail => [0x02, 0x37, 0x00], MagType::DevilsTail => [0x02, 0x38, 0x00], MagType::Deva => [0x02, 0x39, 0x00], MagType::Rati => [0x02, 0x3A, 0x00], MagType::Savitri => [0x02, 0x3B, 0x00], MagType::Rukmin => [0x02, 0x3C, 0x00], MagType::Pushan => [0x02, 0x3D, 0x00], MagType::Diwari => [0x02, 0x3E, 0x00], MagType::Sato => [0x02, 0x3F, 0x00], MagType::Bhima => [0x02, 0x40, 0x00], MagType::Nidra => [0x02, 0x41, 0x00], MagType::GeungSi => [0x02, 0x42, 0x00], MagType::Tellusis => [0x02, 0x44, 0x00], MagType::StrikerUnit => [0x02, 0x45, 0x00], MagType::Pioneer => [0x02, 0x46, 0x00], MagType::Puyo => [0x02, 0x47, 0x00], MagType::Moro => [0x02, 0x48, 0x00], MagType::Rappy => [0x02, 0x49, 0x00], MagType::Yahoo => [0x02, 0x4A, 0x00], MagType::GaelGiel => [0x02, 0x4B, 0x00], MagType::Agastya => [0x02, 0x4C, 0x00], } } pub fn parse_type(data: [u8; 3]) -> Result { match data { [0x02, 0x00, 0x00] => Ok(MagType::Mag), [0x02, 0x01, 0x00] => Ok(MagType::Varuna), [0x02, 0x02, 0x00] => Ok(MagType::Mitra), [0x02, 0x03, 0x00] => Ok(MagType::Surya), [0x02, 0x04, 0x00] => Ok(MagType::Vayu), [0x02, 0x05, 0x00] => Ok(MagType::Varaha), [0x02, 0x06, 0x00] => Ok(MagType::Kama), [0x02, 0x07, 0x00] => Ok(MagType::Ushasu), [0x02, 0x08, 0x00] => Ok(MagType::Apsaras), [0x02, 0x09, 0x00] => Ok(MagType::Kumara), [0x02, 0x0A, 0x00] => Ok(MagType::Kaitabha), [0x02, 0x0B, 0x00] => Ok(MagType::Tapas), [0x02, 0x0C, 0x00] => Ok(MagType::Bhirava), [0x02, 0x0D, 0x00] => Ok(MagType::Kalki), [0x02, 0x0E, 0x00] => Ok(MagType::Rudra), [0x02, 0x0F, 0x00] => Ok(MagType::Marutah), [0x02, 0x10, 0x00] => Ok(MagType::Yaksa), [0x02, 0x11, 0x00] => Ok(MagType::Sita), [0x02, 0x12, 0x00] => Ok(MagType::Garuda), [0x02, 0x13, 0x00] => Ok(MagType::Nandin), [0x02, 0x14, 0x00] => Ok(MagType::Ashvinau), [0x02, 0x15, 0x00] => Ok(MagType::Ribhava), [0x02, 0x16, 0x00] => Ok(MagType::Soma), [0x02, 0x17, 0x00] => Ok(MagType::Ila), [0x02, 0x18, 0x00] => Ok(MagType::Durga), [0x02, 0x19, 0x00] => Ok(MagType::Vritra), [0x02, 0x1A, 0x00] => Ok(MagType::Namuci), [0x02, 0x1B, 0x00] => Ok(MagType::Sumba), [0x02, 0x1C, 0x00] => Ok(MagType::Naga), [0x02, 0x1D, 0x00] => Ok(MagType::Pitri), [0x02, 0x1E, 0x00] => Ok(MagType::Kabanda), [0x02, 0x1F, 0x00] => Ok(MagType::Ravana), [0x02, 0x20, 0x00] => Ok(MagType::Marica), [0x02, 0x21, 0x00] => Ok(MagType::Soniti), [0x02, 0x22, 0x00] => Ok(MagType::Preta), [0x02, 0x23, 0x00] => Ok(MagType::Andhaka), [0x02, 0x24, 0x00] => Ok(MagType::Bana), [0x02, 0x25, 0x00] => Ok(MagType::Naraka), [0x02, 0x26, 0x00] => Ok(MagType::Madhu), [0x02, 0x27, 0x00] => Ok(MagType::Churel), [0x02, 0x28, 0x00] => Ok(MagType::Robochao), [0x02, 0x29, 0x00] => Ok(MagType::OpaOpa), [0x02, 0x2A, 0x00] => Ok(MagType::Pian), [0x02, 0x2B, 0x00] => Ok(MagType::Chao), [0x02, 0x2C, 0x00] => Ok(MagType::ChuChu), [0x02, 0x2D, 0x00] => Ok(MagType::KapuKapu), [0x02, 0x2E, 0x00] => Ok(MagType::AngelsWing), [0x02, 0x2F, 0x00] => Ok(MagType::DevilsWing), [0x02, 0x30, 0x00] => Ok(MagType::Elenor), [0x02, 0x31, 0x00] => Ok(MagType::MarkIII), [0x02, 0x32, 0x00] => Ok(MagType::MasterSystem), [0x02, 0x33, 0x00] => Ok(MagType::Genesis), [0x02, 0x34, 0x00] => Ok(MagType::SegaSaturn), [0x02, 0x35, 0x00] => Ok(MagType::Dreamcast), [0x02, 0x36, 0x00] => Ok(MagType::Hamburger), [0x02, 0x37, 0x00] => Ok(MagType::PanzersTail), [0x02, 0x38, 0x00] => Ok(MagType::DevilsTail), [0x02, 0x39, 0x00] => Ok(MagType::Deva), [0x02, 0x3A, 0x00] => Ok(MagType::Rati), [0x02, 0x3B, 0x00] => Ok(MagType::Savitri), [0x02, 0x3C, 0x00] => Ok(MagType::Rukmin), [0x02, 0x3D, 0x00] => Ok(MagType::Pushan), [0x02, 0x3E, 0x00] => Ok(MagType::Diwari), [0x02, 0x3F, 0x00] => Ok(MagType::Sato), [0x02, 0x40, 0x00] => Ok(MagType::Bhima), [0x02, 0x41, 0x00] => Ok(MagType::Nidra), [0x02, 0x42, 0x00] => Ok(MagType::GeungSi), [0x02, 0x44, 0x00] => Ok(MagType::Tellusis), [0x02, 0x45, 0x00] => Ok(MagType::StrikerUnit), [0x02, 0x46, 0x00] => Ok(MagType::Pioneer), [0x02, 0x47, 0x00] => Ok(MagType::Puyo), [0x02, 0x48, 0x00] => Ok(MagType::Moro), [0x02, 0x49, 0x00] => Ok(MagType::Rappy), [0x02, 0x4A, 0x00] => Ok(MagType::Yahoo), [0x02, 0x4B, 0x00] => Ok(MagType::GaelGiel), [0x02, 0x4C, 0x00] => Ok(MagType::Agastya), _ => Err(ItemParseError::InvalidMagType), } } pub fn can_evolve(&self) -> bool { match self { MagType::Mag => true, MagType::Varuna => true, MagType::Mitra => true, MagType::Surya => true, MagType::Vayu => true, MagType::Varaha => true, MagType::Kama => true, MagType::Ushasu => true, MagType::Apsaras => true, MagType::Kumara => true, MagType::Kaitabha => true, MagType::Tapas => true, MagType::Bhirava => true, MagType::Kalki => true, MagType::Rudra => true, MagType::Marutah => true, MagType::Yaksa => true, MagType::Sita => true, MagType::Garuda => true, MagType::Nandin => true, MagType::Ashvinau => true, MagType::Ribhava => true, MagType::Soma => true, MagType::Ila => true, MagType::Durga => true, MagType::Vritra => true, MagType::Namuci => true, MagType::Sumba => true, MagType::Naga => true, MagType::Pitri => true, MagType::Kabanda => true, MagType::Ravana => true, MagType::Marica => true, MagType::Soniti => true, MagType::Preta => true, MagType::Andhaka => true, MagType::Bana => true, MagType::Naraka => true, MagType::Madhu => true, MagType::Churel => false, MagType::Robochao => false, MagType::OpaOpa => false, MagType::Pian => false, MagType::Chao => false, MagType::ChuChu => false, MagType::KapuKapu => false, MagType::AngelsWing => false, MagType::DevilsWing => false, MagType::Elenor => false, MagType::MarkIII => false, MagType::MasterSystem => false, MagType::Genesis => false, MagType::SegaSaturn => false, MagType::Dreamcast => false, MagType::Hamburger => false, MagType::PanzersTail => false, MagType::DevilsTail => false, MagType::Deva => false, MagType::Rati => false, MagType::Savitri => false, MagType::Rukmin => false, MagType::Pushan => false, MagType::Diwari => false, MagType::Sato => false, MagType::Bhima => false, MagType::Nidra => false, MagType::GeungSi => false, MagType::Tellusis => false, MagType::StrikerUnit => false, MagType::Pioneer => false, MagType::Puyo => false, MagType::Moro => false, MagType::Rappy => false, MagType::Yahoo => false, MagType::GaelGiel => false, MagType::Agastya => false, } } } pub enum MagCell { CellOfMag502, CellOfMag213, PartsOfRobochao, HeartOfOpaOpa, HeartOfPian, HeartOfChao, HeartOfAngel, HeartOfDevil, KitOfHamburger, PanthersSpirit, KitOfMark3, KitOfMasterSystem, KitOfGenesis, KitOfSegaSaturn, KitOfDreamcast, Tablet, DragonScale, HeavenStrikerCoat, PioneerParts, AmitiesMemo, HeartOfMorolian, RappysBeak, YahoosEngine, DPhotonCore, LibertaKit, } impl std::convert::TryFrom for MagCell { type Error = MagCellError; fn try_from(tool: ToolType) -> Result { match tool { ToolType::CellOfMag502 => Ok(MagCell::CellOfMag502), ToolType::CellOfMag213 => Ok(MagCell::CellOfMag213), ToolType::PartsOfRobochao => Ok(MagCell::PartsOfRobochao), ToolType::HeartOfOpaOpa => Ok(MagCell::HeartOfOpaOpa), ToolType::HeartOfPian => Ok(MagCell::HeartOfPian), ToolType::HeartOfChao => Ok(MagCell::HeartOfChao), ToolType::HeartOfAngel => Ok(MagCell::HeartOfAngel), ToolType::HeartOfDevil => Ok(MagCell::HeartOfDevil), ToolType::KitOfHamburger => Ok(MagCell::KitOfHamburger), ToolType::PanthersSpirit => Ok(MagCell::PanthersSpirit), ToolType::KitOfMark3 => Ok(MagCell::KitOfMark3), ToolType::KitOfMasterSystem => Ok(MagCell::KitOfMasterSystem), ToolType::KitOfGenesis => Ok(MagCell::KitOfGenesis), ToolType::KitOfSegaSaturn => Ok(MagCell::KitOfSegaSaturn), ToolType::KitOfDreamcast => Ok(MagCell::KitOfDreamcast), ToolType::Tablet => Ok(MagCell::Tablet), ToolType::DragonScale => Ok(MagCell::DragonScale), ToolType::HeavenStrikerCoat => Ok(MagCell::HeavenStrikerCoat), ToolType::PioneerParts => Ok(MagCell::PioneerParts), ToolType::AmitiesMemo => Ok(MagCell::AmitiesMemo), ToolType::HeartOfMorolian => Ok(MagCell::HeartOfMorolian), ToolType::RappysBeak => Ok(MagCell::RappysBeak), ToolType::YahoosEngine => Ok(MagCell::YahoosEngine), ToolType::DPhotonCore => Ok(MagCell::DPhotonCore), ToolType::LibertaKit => Ok(MagCell::LibertaKit), _ => Err(MagCellError::IsNotMagCell), } } } enum MagAttribute { //Def, Pow, Dex, Mind, } // one day I hope to be cool enough to figure how to enforce that each magattribute in sequence must be unique // (to not need the _ in the match) enum MagAttributeOrdering { Sequence(MagAttribute, MagAttribute, MagAttribute), Primary(MagAttribute), MultiPrimary } impl MagAttributeOrdering { fn new(pow: u16, dex: u16, mnd: u16) -> MagAttributeOrdering { let primary = if pow > dex && pow > mnd { MagAttribute::Pow } else if dex > pow && dex > mnd{ MagAttribute::Dex } else if mnd > pow && mnd > dex { MagAttribute::Mind } else { return MagAttributeOrdering::MultiPrimary }; match primary { MagAttribute::Pow => { match dex.cmp(&mnd) { Greater => MagAttributeOrdering::Sequence(primary, MagAttribute::Dex, MagAttribute::Mind), Equal => MagAttributeOrdering::Primary(primary), Less => MagAttributeOrdering::Sequence(primary, MagAttribute::Mind, MagAttribute::Dex), } }, MagAttribute::Dex => { match pow.cmp(&mnd) { Greater => MagAttributeOrdering::Sequence(primary, MagAttribute::Pow, MagAttribute::Mind), Equal => MagAttributeOrdering::Primary(primary), Less => MagAttributeOrdering::Sequence(primary, MagAttribute::Mind, MagAttribute::Pow), } }, MagAttribute::Mind => { match pow.cmp(&dex) { Greater => MagAttributeOrdering::Sequence(primary, MagAttribute::Pow, MagAttribute::Dex), Equal => MagAttributeOrdering::Primary(primary), Less => MagAttributeOrdering::Sequence(primary, MagAttribute::Dex, MagAttribute::Pow), } }, } } } #[derive(Error, Debug)] pub enum MagCellError { #[error("not a mag cell")] IsNotMagCell, #[error("mag is rare")] IsRareMag, } #[derive(Debug, Clone, PartialEq, Eq)] pub enum MagModifier { FeedMag { food: ItemEntityId, }, BankMag, // when putting a mag in the bank it truncates the values which has applications when raising degenerate mags MagCell(ItemEntityId), OwnerChange(CharacterClass, SectionID) } #[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize, enum_utils::FromStr)] pub enum PhotonBlast { Farlla, Estlla, Leilla, Pilla, Golla, MyllaYoulla, } #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct Mag { pub mag: MagType, def: u16, pow: u16, dex: u16, mnd: u16, pub synchro: u8, iq: u8, photon_blast: [Option; 3], pub color: u8, //modifiers: Vec, pub class: CharacterClass, pub id: SectionID, } impl Mag { pub fn baby_mag(skin: u16) -> Mag { Mag { mag: MagType::Mag, def: 500, pow: 0, dex: 0, mnd: 0, synchro: 20, iq: 0, photon_blast: [None; 3], color: (skin % 18) as u8, //modifiers: Vec::new(), class: CharacterClass::HUmar, id: SectionID::Viridia, } } pub fn as_bytes(&self) -> [u8; 16] { let mut result = [0; 16]; result[0..3].copy_from_slice(&self.mag.value()); result[2] = self.level() as u8; result[3] = self.photon_blast_value(); result[4..6].copy_from_slice(&self.def.to_le_bytes()); result[6..8].copy_from_slice(&self.pow.to_le_bytes()); result[8..10].copy_from_slice(&self.dex.to_le_bytes()); result[10..12].copy_from_slice(&self.mnd.to_le_bytes()); result[12] = self.synchro; result[13] = self.iq; result[14] = self.photon_blast_count(); result[15] = self.color; result } fn photon_blast_value(&self) -> u8 { let mut photon_blast_list = vec![PhotonBlast::Farlla, PhotonBlast::Estlla, PhotonBlast::Golla, PhotonBlast::Pilla, PhotonBlast::Leilla, PhotonBlast::MyllaYoulla]; let mut photon_blast: u8 = 0; if let Some(ref pb_mid) = self.photon_blast[0] { match *pb_mid { PhotonBlast::Farlla => {}, PhotonBlast::Estlla => photon_blast |= 1, PhotonBlast::Golla => photon_blast |= 2, PhotonBlast::Pilla => photon_blast |= 3, PhotonBlast::Leilla => photon_blast |= 4, PhotonBlast::MyllaYoulla => photon_blast |= 5, } photon_blast_list.retain(|k| k != pb_mid); } if let Some(ref pb_right) = self.photon_blast[1] { match *pb_right { PhotonBlast::Farlla => {} PhotonBlast::Estlla => photon_blast |= 1 << 3, PhotonBlast::Golla => photon_blast |= 2 << 3, PhotonBlast::Pilla => photon_blast |= 3 << 3, PhotonBlast::Leilla => photon_blast |= 4 << 3, PhotonBlast::MyllaYoulla => photon_blast |= 5 << 3, } photon_blast_list.retain(|k| k != pb_right); } if let Some(ref pb_left) = self.photon_blast[2] { if let Some(pos) = photon_blast_list.iter().position(|k| k == pb_left) { photon_blast |= (pos as u8) << 6; }; } photon_blast } fn photon_blast_count(&self) -> u8 { let mut count = 0; for i in 0..3 { if self.photon_blast[i].is_some() { count |= 1 << i }; } count } pub fn from_bytes(data: [u8; 16]) -> Result { let m = MagType::parse_type([data[0], data[1], data[2]]); if let Ok(m) = m { let mut def = u16::from_le_bytes([data[4], data[5]]); let mut pow = u16::from_le_bytes([data[6], data[7]]); let mut dex = u16::from_le_bytes([data[8], data[9]]); let mut mind = u16::from_le_bytes([data[10], data[11]]); if (def/100 + dex/100 + pow/100 + mind/100) > 200 { def = 0; pow = 0; dex = 0; mind = 0; } let sync = data[12] % 121; // TODO: handle invalid values. let iq = data[13] % 201; // TODO: handle invalid values. Ok(Mag{ mag: m, def, pow, dex, mnd: mind, synchro: sync, iq, photon_blast: [None, None, None], // TODO: actually get PBs from bytes color: data[15] % 18, //modifiers: Vec::new(), class: CharacterClass::HUmar, id: SectionID::Viridia, }) } else { Err(ItemParseError::InvalidMagBytes) // TODO: error handling if wrong bytes are given } } pub fn def(&self) -> u16 { self.def/100 } pub fn pow(&self) -> u16 { self.pow/100 } pub fn dex(&self) -> u16 { self.dex/100 } pub fn mind(&self) -> u16 { self.mnd/100 } pub fn level(&self) -> u16 { self.def() + self.pow() + self.dex() + self.mind() } fn change_mag_type(&mut self, previous_level: u16) { if !self.mag.can_evolve() { return } if self.level() >= 10 && previous_level < 10 { match self.class { CharacterClass::HUmar | CharacterClass::HUnewearl | CharacterClass::HUcast | CharacterClass::HUcaseal => { self.mag = MagType::Varuna }, CharacterClass::RAmar | CharacterClass::RAmarl | CharacterClass::RAcast | CharacterClass::RAcaseal => { self.mag = MagType::Kalki }, CharacterClass::FOmar | CharacterClass::FOmarl | CharacterClass::FOnewm | CharacterClass::FOnewearl => { self.mag = MagType::Vritra }, } } if self.level() >= 35 && previous_level < 35 { match self.mag { MagType::Varuna => { if self.pow > self.dex && self.pow > self.mnd { self.mag = MagType::Rudra } else if self.dex > self.pow && self.dex > self.mnd { self.mag = MagType::Marutah } else if self.mnd > self.pow && self.mnd > self.dex { self.mag = MagType::Vayu } else { self.mag = MagType::Rudra } }, MagType::Kalki => { if self.pow > self.dex && self.pow > self.mnd { self.mag = MagType::Surya } else if self.dex > self.pow && self.dex > self.mnd { self.mag = MagType::Mitra } else if self.mnd > self.pow && self.mnd > self.dex { self.mag = MagType::Tapas } else { self.mag = MagType::Mitra } }, MagType::Vritra => { #[allow(clippy::if_same_then_else)] if self.pow > self.dex && self.pow > self.mnd { self.mag = MagType::Sumba } else if self.dex > self.pow && self.dex > self.mnd { self.mag = MagType::Ashvinau } else if self.mnd > self.pow && self.mnd > self.dex { self.mag = MagType::Namuci } else { self.mag = MagType::Namuci } }, _ => unreachable!(), } } if self.level() >= 50 && self.level() % 5 == 0 { let mag_attr_ordering = MagAttributeOrdering::new(self.pow, self.dex, self.mnd); self.mag = match self.id { SectionID::Viridia | SectionID::Skyly | SectionID::Purplenum | SectionID::Redria | SectionID::Yellowboze => { match self.class { CharacterClass::HUmar | CharacterClass::HUnewearl | CharacterClass::HUcast | CharacterClass::HUcaseal => { match mag_attr_ordering { MagAttributeOrdering::Primary(MagAttribute::Pow) => MagType::Varaha, MagAttributeOrdering::Primary(MagAttribute::Dex) => MagType::Nandin, MagAttributeOrdering::Primary(MagAttribute::Mind) => MagType::Kabanda, MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Dex, MagAttribute::Mind) => MagType::Varaha, MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Mind, MagAttribute::Dex) => MagType::Bhirava, MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Pow, MagAttribute::Mind) => MagType::Ila, MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Mind, MagAttribute::Pow) => MagType::Nandin, MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Pow, MagAttribute::Dex) => MagType::Kabanda, MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Dex, MagAttribute::Pow) => MagType::Ushasu, MagAttributeOrdering::MultiPrimary => { if self.dex >= self.mnd { MagType::Varaha } else { MagType::Bhirava } }, _ => unreachable!() } }, CharacterClass::RAmar | CharacterClass::RAmarl | CharacterClass::RAcast | CharacterClass::RAcaseal => { match mag_attr_ordering { MagAttributeOrdering::Primary(MagAttribute::Pow) => MagType::Kama, MagAttributeOrdering::Primary(MagAttribute::Dex) => MagType::Kama, MagAttributeOrdering::Primary(MagAttribute::Mind) => MagType::Varaha, MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Dex, MagAttribute::Mind) => MagType::Kama, MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Mind, MagAttribute::Dex) => MagType::Bhirava, MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Pow, MagAttribute::Mind) => MagType::Bhirava, MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Mind, MagAttribute::Pow) => MagType::Kama, MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Pow, MagAttribute::Dex) => MagType::Varaha, MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Dex, MagAttribute::Pow) => MagType::Apsaras, MagAttributeOrdering::MultiPrimary => { if self.mnd >= self.pow { MagType::Kama } else { MagType::Bhirava } } _ => unreachable!() } }, CharacterClass::FOmar | CharacterClass::FOmarl | CharacterClass::FOnewm | CharacterClass::FOnewearl => { match (mag_attr_ordering, self.def() >= 45) { (MagAttributeOrdering::Primary(MagAttribute::Pow), true) => MagType::Andhaka, (MagAttributeOrdering::Primary(MagAttribute::Dex), true) => MagType::Bana, (MagAttributeOrdering::Primary(MagAttribute::Mind), true) => MagType::Bana, (MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Dex, MagAttribute::Mind), true) => MagType::Andhaka, (MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Mind, MagAttribute::Dex), true) => MagType::Andhaka, (MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Pow, MagAttribute::Mind), true) => MagType::Bana, (MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Mind, MagAttribute::Pow), true) => MagType::Bana, (MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Pow, MagAttribute::Dex), true) => MagType::Bana, (MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Dex, MagAttribute::Pow), true) => MagType::Bana, (MagAttributeOrdering::MultiPrimary, true) => MagType::Bana, (MagAttributeOrdering::Primary(MagAttribute::Pow), false) => MagType::Naraka, (MagAttributeOrdering::Primary(MagAttribute::Dex), false) => MagType::Sita, (MagAttributeOrdering::Primary(MagAttribute::Mind), false) => MagType::Naga, (MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Dex, MagAttribute::Mind), false) => MagType::Naraka, (MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Mind, MagAttribute::Dex), false) => MagType::Ravana, (MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Pow, MagAttribute::Mind), false) => MagType::Ribhava, (MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Mind, MagAttribute::Pow), false) => MagType::Sita, (MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Pow, MagAttribute::Dex), false) => MagType::Naga, (MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Dex, MagAttribute::Pow), false) => MagType::Kabanda, (MagAttributeOrdering::MultiPrimary, false) => { if self.pow >= self.dex { MagType::Naga } else { MagType::Kabanda } } _ => unreachable!() } }, } }, SectionID::Greenill | SectionID::Bluefull | SectionID::Pinkal | SectionID::Oran | SectionID::Whitill => { match self.class { CharacterClass::HUmar | CharacterClass::HUnewearl | CharacterClass::HUcast | CharacterClass::HUcaseal => { match mag_attr_ordering { MagAttributeOrdering::Primary(MagAttribute::Pow) => MagType::Kama, MagAttributeOrdering::Primary(MagAttribute::Dex) => MagType::Yaksa, MagAttributeOrdering::Primary(MagAttribute::Mind) => MagType::Bana, MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Dex, MagAttribute::Mind) => MagType::Kama, MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Mind, MagAttribute::Dex) => MagType::Apsaras, MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Pow, MagAttribute::Mind) => MagType::Garuda, MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Mind, MagAttribute::Pow) => MagType::Yaksa, MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Pow, MagAttribute::Dex) => MagType::Bana, MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Dex, MagAttribute::Pow) => MagType::Soma, MagAttributeOrdering::MultiPrimary => { if self.dex >= self.mnd { MagType::Kama } else { MagType::Apsaras } } _ => unreachable!() } }, CharacterClass::RAmar | CharacterClass::RAmarl | CharacterClass::RAcast | CharacterClass::RAcaseal => { match mag_attr_ordering { MagAttributeOrdering::Primary(MagAttribute::Pow) => MagType::Madhu, MagAttributeOrdering::Primary(MagAttribute::Dex) => MagType::Varaha, MagAttributeOrdering::Primary(MagAttribute::Mind) => MagType::Kabanda, MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Dex, MagAttribute::Mind) => MagType::Madhu, MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Mind, MagAttribute::Dex) => MagType::Kaitabha, MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Pow, MagAttribute::Mind) => MagType::Kaitabha, MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Mind, MagAttribute::Pow) => MagType::Varaha, MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Pow, MagAttribute::Dex) => MagType::Kabanda, MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Dex, MagAttribute::Pow) => MagType::Durga, MagAttributeOrdering::MultiPrimary => { if self.pow > self.mnd { MagType::Kaitabha } else { MagType::Varaha } } _ => unreachable!() } }, CharacterClass::FOmar | CharacterClass::FOmarl | CharacterClass::FOnewm | CharacterClass::FOnewearl => { match (mag_attr_ordering, self.def() >= 45) { (MagAttributeOrdering::Primary(MagAttribute::Pow), true) => MagType::Andhaka, (MagAttributeOrdering::Primary(MagAttribute::Dex), true) => MagType::Bana, (MagAttributeOrdering::Primary(MagAttribute::Mind), true) => MagType::Bana, (MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Dex, MagAttribute::Mind), true) => MagType::Andhaka, (MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Mind, MagAttribute::Dex), true) => MagType::Andhaka, (MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Pow, MagAttribute::Mind), true) => MagType::Bana, (MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Mind, MagAttribute::Pow), true) => MagType::Bana, (MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Pow, MagAttribute::Dex), true) => MagType::Bana, (MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Dex, MagAttribute::Pow), true) => MagType::Bana, (MagAttributeOrdering::MultiPrimary, true) => MagType::Bana, (MagAttributeOrdering::Primary(MagAttribute::Pow), false) => MagType::Marica, (MagAttributeOrdering::Primary(MagAttribute::Dex), false) => MagType::Bhirava, (MagAttributeOrdering::Primary(MagAttribute::Mind), false) => MagType::Kumara, (MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Dex, MagAttribute::Mind), false) => MagType::Marica, (MagAttributeOrdering::Sequence(MagAttribute::Pow, MagAttribute::Mind, MagAttribute::Dex), false) => MagType::Naga, (MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Pow, MagAttribute::Mind), false) => MagType::Garuda, (MagAttributeOrdering::Sequence(MagAttribute::Dex, MagAttribute::Mind, MagAttribute::Pow), false) => MagType::Bhirava, (MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Pow, MagAttribute::Dex), false) => MagType::Kumara, (MagAttributeOrdering::Sequence(MagAttribute::Mind, MagAttribute::Dex, MagAttribute::Pow), false) => MagType::Ila, (MagAttributeOrdering::MultiPrimary, false) => { if self.pow >= self.dex { MagType::Kumara } else { MagType::Ila } } _ => unreachable!() } }, } } }; } if self.level() >= 100 && self.level() % 5 == 0 { match self.id { SectionID::Skyly | SectionID::Pinkal | SectionID::Yellowboze => { if self.def() + self.pow() == self.dex() + self.mind() { self.mag = match self.class { CharacterClass::HUmar | CharacterClass::HUcast => { MagType::Rati }, CharacterClass::HUnewearl | CharacterClass::HUcaseal => { MagType::Savitri }, CharacterClass::RAmar | CharacterClass::RAcast => { MagType::Pushan }, CharacterClass::RAmarl | CharacterClass::RAcaseal => { MagType::Diwari }, CharacterClass::FOmar | CharacterClass::FOnewm => { MagType::Nidra }, CharacterClass::FOmarl | CharacterClass::FOnewearl => { MagType::Bhima }, } } }, SectionID::Viridia | SectionID::Bluefull | SectionID::Redria | SectionID::Whitill => { if self.def() + self.dex() == self.pow() + self.mind() { self.mag = match self.class { CharacterClass::HUmar | CharacterClass::HUcast => { MagType::Deva }, CharacterClass::HUnewearl | CharacterClass::HUcaseal => { MagType::Savitri }, CharacterClass::RAmar | CharacterClass::RAcast => { MagType::Pushan }, CharacterClass::RAmarl | CharacterClass::RAcaseal => { MagType::Rukmin }, CharacterClass::FOmar | CharacterClass::FOnewm => { MagType::Nidra }, CharacterClass::FOmarl | CharacterClass::FOnewearl => { MagType::Sato // best mag }, } } }, SectionID::Greenill | SectionID::Purplenum | SectionID::Oran => { if self.def() + self.mind() == self.pow() + self.dex() { self.mag = match self.class { CharacterClass::HUmar | CharacterClass::HUcast => { MagType::Rati }, CharacterClass::HUnewearl | CharacterClass::HUcaseal => { MagType::Savitri }, CharacterClass::RAmar | CharacterClass::RAcast => { MagType::Pushan }, CharacterClass::RAmarl | CharacterClass::RAcaseal => { MagType::Rukmin }, CharacterClass::FOmar | CharacterClass::FOnewm => { MagType::Nidra }, CharacterClass::FOmarl | CharacterClass::FOnewearl => { MagType::Bhima }, } } } } } } pub fn assign_photon_blast(&mut self) { MAG_STATS.get(&self.mag).map(|stats| { stats.photon_blast.map(|photon_blast| { if !self.photon_blast.contains(&Some(photon_blast)) { if let Some(pb_slot) = self.photon_blast.iter_mut().find(|k| k.is_none()) { *pb_slot = Some(photon_blast) } } }) }); } pub fn feed(&mut self, tool: ToolType) { let previous_level = self.level(); MAG_STATS.get(&self.mag).map(|stats| { MAG_FEEDING_TABLES.get(stats.feed_table).map(|feeding_table| { feeding_table.get(&tool).map(|feed_stats| { self.def = std::cmp::max(std::cmp::max((self.def as i16) + feed_stats.def, 0) as u16, self.def()*100); self.pow = std::cmp::max(std::cmp::max((self.pow as i16) + feed_stats.pow, 0) as u16, self.pow()*100); self.dex = std::cmp::max(std::cmp::max((self.dex as i16) + feed_stats.dex, 0) as u16, self.dex()*100); self.mnd = std::cmp::max(std::cmp::max((self.mnd as i16) + feed_stats.mnd, 0) as u16, self.mind()*100); self.iq = std::cmp::min(((self.iq as i16) + feed_stats.iq as i16) as u8, 200); self.synchro = std::cmp::min(((self.synchro as i8) + feed_stats.syn) as u8, 120); }) }) }); //if previous_level != self.level() { self.change_mag_type(previous_level); self.assign_photon_blast(); //} } pub fn change_owner(&mut self, class: CharacterClass, id: SectionID) { self.class = class; self.id = id; } pub fn bank(&mut self) { self.def &= 0xFFFE; self.pow &= 0xFFFE; self.dex &= 0xFFFE; self.mnd &= 0xFFFE; } // TODO: this needs more checks on validity pub fn apply_mag_cell(&mut self, mag_cell: MagCell) -> Result<(), MagCellError> { if self.is_rare_item() { return Err(MagCellError::IsRareMag) } self.mag = match mag_cell { MagCell::CellOfMag502 => { match self.id { SectionID::Viridia | SectionID::Skyly | SectionID::Purplenum | SectionID::Redria | SectionID::Yellowboze => { MagType::Soniti }, SectionID::Greenill | SectionID::Bluefull | SectionID::Pinkal | SectionID::Oran | SectionID::Whitill => { MagType::Pitri } } } MagCell::CellOfMag213 => { match self.id { SectionID::Viridia | SectionID::Skyly | SectionID::Purplenum | SectionID::Redria | SectionID::Yellowboze => { MagType::Churel }, SectionID::Greenill | SectionID::Bluefull | SectionID::Pinkal | SectionID::Oran | SectionID::Whitill => { MagType::Preta } } }, MagCell::PartsOfRobochao => MagType::Robochao, MagCell::HeartOfOpaOpa => MagType::OpaOpa, MagCell::HeartOfPian => MagType::Pian, MagCell::HeartOfChao => MagType::Chao, MagCell::HeartOfAngel => MagType::AngelsWing, MagCell::HeartOfDevil => if self.mag == MagType::DevilsWing { MagType::DevilsTail } else { MagType::DevilsWing }, MagCell::KitOfHamburger => MagType::Hamburger, MagCell::PanthersSpirit => MagType::PanzersTail, MagCell::KitOfMark3 => MagType::MarkIII, MagCell::KitOfMasterSystem => MagType::MasterSystem, MagCell::KitOfGenesis => MagType::Genesis, MagCell::KitOfSegaSaturn => MagType::SegaSaturn, MagCell::KitOfDreamcast => MagType::Dreamcast, MagCell::Tablet => MagType::GeungSi, MagCell::DragonScale => MagType::Tellusis, MagCell::HeavenStrikerCoat => MagType::StrikerUnit, MagCell::PioneerParts => MagType::Pioneer, MagCell::AmitiesMemo => MagType::Puyo, MagCell::HeartOfMorolian => MagType::Moro, MagCell::RappysBeak => MagType::Rappy, MagCell::YahoosEngine => MagType::Yahoo, MagCell::DPhotonCore => MagType::GaelGiel, MagCell::LibertaKit => MagType::Agastya, }; Ok(()) } pub fn is_rare_item(&self) -> bool { matches!( self.mag, MagType::Pitri | MagType::Soniti | MagType::Preta | MagType::Churel | MagType::Robochao | MagType::OpaOpa | MagType::Pian | MagType::Chao | MagType::ChuChu | MagType::KapuKapu | MagType::AngelsWing | MagType::DevilsWing | MagType::Elenor | MagType::MarkIII | MagType::MasterSystem | MagType::Genesis | MagType::SegaSaturn | MagType::Dreamcast | MagType::Hamburger | MagType::PanzersTail | MagType::DevilsTail | MagType::Deva | MagType::Rati | MagType::Savitri | MagType::Rukmin | MagType::Pushan | MagType::Diwari | MagType::Sato | MagType::Bhima | MagType::Nidra | MagType::GeungSi | MagType::Tellusis | MagType::StrikerUnit | MagType::Pioneer | MagType::Puyo | MagType::Moro | MagType::Rappy | MagType::Yahoo | MagType::GaelGiel | MagType::Agastya ) } } #[cfg(test)] mod test { use super::*; use std::io::Read; #[test] fn test_load_mag_stats() { let mut f = std::fs::File::open("data/item_stats/mag_stats.toml").unwrap(); let mut s = String::new(); f.read_to_string(&mut s).unwrap(); let mags: HashMap = toml::from_str(&s).unwrap(); let _mags = mags.into_iter() .map(|(name, stats)| { (name.parse().unwrap(), stats) }) .collect::>(); } #[test] fn test_load_mag_feed_table() { let mut f = std::fs::File::open("data/item_stats/mag_feed_table.toml").unwrap(); let mut s = String::new(); f.read_to_string(&mut s).unwrap(); let mut feed: HashMap>> = toml::from_str(&s).unwrap(); let feed = feed.remove("feedtable".into()).unwrap(); let _feed = feed.into_iter() .map(|table| { table.into_iter() .map(|(tool, stats)| { (tool.parse().unwrap(), stats) }) .collect() }) .collect::>>(); } #[test] fn test_raise_a_sato() { let mut mag = Mag::baby_mag(0); mag.change_owner(CharacterClass::RAcaseal, SectionID::Whitill); for _ in 0..137 { mag.feed(ToolType::Antidote); } for _ in 0..75 { mag.feed(ToolType::Antiparalysis); } mag.change_owner(CharacterClass::FOmarl, SectionID::Whitill); for _ in 0..51 { mag.feed(ToolType::Antiparalysis); } for _ in 0..284 { mag.feed(ToolType::Dimate); } assert!(mag == Mag { mag: MagType::Sato, def: 507, pow: 5019, dex: 4505, mnd: 0, synchro: 120, iq: 200, photon_blast: [Some(PhotonBlast::Estlla), Some(PhotonBlast::Pilla), Some(PhotonBlast::MyllaYoulla)], color: 0, class: CharacterClass::FOmarl, id: SectionID::Whitill, }); } #[test] fn test_mag_does_not_level_down() { } }