use std::convert::{From, Into}; use std::collections::HashMap; use serde::{Serialize, Deserialize}; use libpso::packet::ship::{UpdateConfig, WriteInfoboard}; use libpso::character::character::{DEFAULT_PALETTE_CONFIG, DEFAULT_TECH_MENU}; use crate::entity::item::tech::Technique; use crate::entity::account::UserAccountId; #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, enum_utils::FromStr, derive_more::Display, Serialize, Deserialize)] pub enum CharacterClass { HUmar, HUnewearl, HUcast, HUcaseal, RAmar, RAmarl, RAcast, RAcaseal, FOmar, FOmarl, FOnewm, FOnewearl, } // TODO: TryFrom impl From for CharacterClass { fn from(f: u8) -> CharacterClass { match f { 0 => CharacterClass::HUmar, 1 => CharacterClass::HUnewearl, 2 => CharacterClass::HUcast, 3 => CharacterClass::RAmar, 4 => CharacterClass::RAcast, 5 => CharacterClass::RAcaseal, 6 => CharacterClass::FOmarl, 7 => CharacterClass::FOnewm, 8 => CharacterClass::FOnewearl, 9 => CharacterClass::HUcaseal, 10 => CharacterClass::FOmar, 11 => CharacterClass::RAmarl, _ => panic!("unknown class") } } } impl Into for CharacterClass { fn into(self) -> u8 { match self { CharacterClass::HUmar => 0, CharacterClass::HUnewearl => 1, CharacterClass::HUcast => 2, CharacterClass::RAmar => 3, CharacterClass::RAcast => 4, CharacterClass::RAcaseal => 5, CharacterClass::FOmarl => 6, CharacterClass::FOnewm => 7, CharacterClass::FOnewearl => 8, CharacterClass::HUcaseal => 9, CharacterClass::FOmar => 10, CharacterClass::RAmarl => 11, } } } impl CharacterClass { pub fn is_human(&self) -> bool { match self { CharacterClass::HUmar | CharacterClass::RAmar | CharacterClass::RAmarl | CharacterClass::FOmar | CharacterClass::FOmarl => true, _ => false, } } pub fn is_newman(&self) -> bool { match self { CharacterClass::HUnewearl | CharacterClass::FOnewm | CharacterClass::FOnewearl => true, _ => false, } } pub fn is_android(&self) -> bool { match self { CharacterClass::HUcast | CharacterClass::HUcaseal | CharacterClass::RAcast | CharacterClass::RAcaseal => true, _ => false, } } } #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, enum_utils::FromStr, derive_more::Display, Serialize, Deserialize)] pub enum SectionID { Viridia, Greenill, Skyly, Bluefull, Purplenum, Pinkal, Redria, Oran, Yellowboze, Whitill, } impl From for SectionID { fn from(id: u8) -> SectionID { match id { 0 => SectionID::Viridia, 1 => SectionID::Greenill, 2 => SectionID::Skyly, 3 => SectionID::Bluefull, 4 => SectionID::Purplenum, 5 => SectionID::Pinkal, 6 => SectionID::Redria, 7 => SectionID::Oran, 8 => SectionID::Yellowboze, 9 => SectionID::Whitill, _ => panic!(), } } } impl Into for SectionID { fn into(self) -> u8 { match self { SectionID::Viridia => 0, SectionID::Greenill => 1, SectionID::Skyly => 2, SectionID::Bluefull => 3, SectionID::Purplenum => 4, SectionID::Pinkal => 5, SectionID::Redria => 6, SectionID::Oran => 7, SectionID::Yellowboze => 8, SectionID::Whitill => 9, } } } #[derive(Clone, Debug, Default)] pub struct CharacterAppearance { pub costume: u16, pub skin: u16, pub face: u16, pub head: u16, pub hair: u16, pub hair_r: u16, pub hair_g: u16, pub hair_b: u16, pub prop_x: f32, pub prop_y: f32, } #[derive(Clone, Debug)] pub struct TechLevel(pub u8); #[derive(Clone, Debug)] pub struct CharacterTechniques { pub techs: HashMap } impl CharacterTechniques { fn new() -> CharacterTechniques { CharacterTechniques { techs: HashMap::new(), } } pub fn set_tech(&mut self, tech: Technique, level: TechLevel) { self.techs.insert(tech, TechLevel(level.0 - 1)); } // from_bytes pub fn as_bytes(&self) -> [u8; 20] { self.techs.iter() .fold([0xFF; 20], |mut techlist, (tech, level)| { let index = tech.as_value(); techlist[index as usize] = level.0; techlist }) } } #[derive(Clone)] pub struct CharacterConfig { pub raw_data: [u8; 0xE8], } impl CharacterConfig { fn new() -> CharacterConfig { CharacterConfig { raw_data: DEFAULT_PALETTE_CONFIG, } } pub fn update(&mut self, new_config: &UpdateConfig) { self.raw_data = new_config.config; } pub fn as_bytes(&self) -> [u8; 0xE8] { self.raw_data } } #[derive(Clone)] pub struct CharacterInfoboard { pub board: [u16; 172], } impl CharacterInfoboard { fn new() -> CharacterInfoboard { CharacterInfoboard { board: [0; 172] } } pub fn as_bytes(&self) -> [u16; 172] { self.board } pub fn update_infoboard(&mut self, new_board: &WriteInfoboard) { self.board = libpso::utf8_to_utf16_array!(new_board.message, 172); } } #[derive(Clone, Default)] pub struct CharacterGuildCard { pub description: String, } #[derive(Clone)] pub struct CharacterTechMenu { pub tech_menu: [u8; 40], } impl CharacterTechMenu { fn new() -> CharacterTechMenu { CharacterTechMenu { tech_menu: DEFAULT_TECH_MENU, } } pub fn as_bytes(&self) -> [u8; 40] { self.tech_menu } } #[derive(Clone, Default)] pub struct CharacterMaterials { pub power: u32, pub mind: u32, pub def: u32, pub evade: u32, pub luck: u32, pub hp: u32, pub tp: u32, } #[derive(Debug)] pub enum PlayerTraps { FireTrap, FreezeTrap, SlowTrap, ConfuseTrap, } impl PlayerTraps { fn from(id: u16) -> PlayerTraps { match id { 0 => PlayerTraps::FireTrap, 1 => PlayerTraps::FreezeTrap, 2 => PlayerTraps::SlowTrap, 3 => PlayerTraps::ConfuseTrap, _ => panic!(), } } fn into(self) -> u16 { match self { PlayerTraps::FireTrap => 0, PlayerTraps::FreezeTrap => 1, PlayerTraps::SlowTrap => 2, PlayerTraps::ConfuseTrap => 3, } } } #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] pub struct CharacterEntityId(pub u32); #[derive(Clone)] pub struct NewCharacterEntity { pub user_id: UserAccountId, pub slot: u32, pub name: String, pub exp: u32, pub char_class: CharacterClass, pub section_id: SectionID, pub appearance: CharacterAppearance, pub techs: CharacterTechniques, pub config: CharacterConfig, pub info_board: CharacterInfoboard, pub guildcard: CharacterGuildCard, pub materials: CharacterMaterials, pub tech_menu: CharacterTechMenu, pub meseta: u32, pub bank_meseta: u32, pub option_flags: u32, } impl NewCharacterEntity { pub fn new(user: UserAccountId) -> NewCharacterEntity { NewCharacterEntity { user_id: user, slot: 0, name: "".into(), exp: 0, char_class: CharacterClass::HUmar, section_id: SectionID::Viridia, appearance: CharacterAppearance::default(), techs: CharacterTechniques::new(), config: CharacterConfig::new(), info_board: CharacterInfoboard::new(), guildcard: CharacterGuildCard::default(), materials: CharacterMaterials::default(), tech_menu: CharacterTechMenu::new(), meseta: 0, bank_meseta: 0, option_flags: 0, } } } #[derive(Clone)] pub struct CharacterEntity { pub id: CharacterEntityId, pub user_id: UserAccountId, pub slot: u32, pub name: String, pub exp: u32, pub char_class: CharacterClass, pub section_id: SectionID, pub appearance: CharacterAppearance, pub techs: CharacterTechniques, pub config: CharacterConfig, pub info_board: CharacterInfoboard, pub guildcard: CharacterGuildCard, pub materials: CharacterMaterials, pub tech_menu: CharacterTechMenu, pub meseta: u32, // TODO: this should not be tied to the character pub bank_meseta: u32, pub option_flags: u32, }