Compare commits
	
		
			4 Commits
		
	
	
		
			master
			...
			move_stuff
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| cd31052b9a | |||
| 04180d1d86 | |||
| f1e1fd72af | |||
| 88959c24f9 | 
| @ -42,7 +42,7 @@ patch_server = { path = "./src/patch_server" } | ||||
| login_server = { path = "./src/login_server" } | ||||
| ship_server = { path = "./src/ship_server" } | ||||
| 
 | ||||
| libpso = { git = "http://git.sharnoth.com/jake/libpso", rev="90246b6" } | ||||
| libpso = { git = "http://git.sharnoth.com/jake/libpso", rev="0e2cac0" } | ||||
| 
 | ||||
| async-std = { version = "1.9.0", features = ["unstable", "attributes"] } | ||||
| futures = "0.3.5" | ||||
|  | ||||
| @ -7,6 +7,8 @@ use login_server::character::CharacterServerState; | ||||
| use patch_server::{PatchServerState, generate_patch_tree, load_config, load_motd}; | ||||
| use ship_server::ShipServerStateBuilder; | ||||
| 
 | ||||
| use libpso::item::weapon::{Weapon, WeaponType, WeaponSpecial, WeaponAttribute, Attribute}; | ||||
| 
 | ||||
| use maps::Holiday; | ||||
| use entity::gateway::{EntityGateway, InMemoryGateway}; | ||||
| use entity::account::{NewUserAccountEntity, NewUserSettingsEntity}; | ||||
| @ -81,8 +83,8 @@ fn main() { | ||||
|                 entity_gateway.create_item( | ||||
|                     item::NewItemEntity { | ||||
|                         item: item::ItemDetail::Weapon( | ||||
|                             item::weapon::Weapon { | ||||
|                                 weapon: item::weapon::WeaponType::Vulcan, | ||||
|                             Weapon { | ||||
|                                 weapon: WeaponType::Vulcan, | ||||
|                                 grind: 0, | ||||
|                                 special: None, | ||||
|                                 attrs: [None, None, None], | ||||
| @ -106,12 +108,12 @@ fn main() { | ||||
|             let item0 = entity_gateway.create_item( | ||||
|                 NewItemEntity { | ||||
|                     item: ItemDetail::Weapon( | ||||
|                         item::weapon::Weapon { | ||||
|                             weapon: item::weapon::WeaponType::Raygun, | ||||
|                         Weapon { | ||||
|                             weapon: WeaponType::Raygun, | ||||
|                             grind: 5, | ||||
|                             special: Some(item::weapon::WeaponSpecial::Hell), | ||||
|                             attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 40}), | ||||
|                                     Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}), | ||||
|                             special: Some(WeaponSpecial::Hell), | ||||
|                             attrs: [Some(WeaponAttribute{attr: Attribute::Hit, value: 40}), | ||||
|                                     Some(WeaponAttribute{attr: Attribute::Dark, value: 30}), | ||||
|                                     None,], | ||||
|                             tekked: false, | ||||
|                         } | ||||
| @ -120,12 +122,12 @@ fn main() { | ||||
|             let item1 = entity_gateway.create_item( | ||||
|                 NewItemEntity { | ||||
|                     item: ItemDetail::Weapon( | ||||
|                         item::weapon::Weapon { | ||||
|                             weapon: item::weapon::WeaponType::Handgun, | ||||
|                         Weapon { | ||||
|                             weapon: WeaponType::Handgun, | ||||
|                             grind: 5, | ||||
|                             special: Some(item::weapon::WeaponSpecial::Charge), | ||||
|                             attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 40}), | ||||
|                                     Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}), | ||||
|                             special: Some(WeaponSpecial::Charge), | ||||
|                             attrs: [Some(WeaponAttribute{attr: Attribute::Hit, value: 40}), | ||||
|                                     Some(WeaponAttribute{attr: Attribute::Dark, value: 30}), | ||||
|                                     None,], | ||||
|                             tekked: true, | ||||
|                         } | ||||
| @ -134,12 +136,12 @@ fn main() { | ||||
|             let item2_w = entity_gateway.create_item( | ||||
|                 NewItemEntity { | ||||
|                     item: ItemDetail::Weapon( | ||||
|                         item::weapon::Weapon { | ||||
|                             weapon: item::weapon::WeaponType::Vjaya, | ||||
|                         Weapon { | ||||
|                             weapon: WeaponType::Vjaya, | ||||
|                             grind: 5, | ||||
|                             special: Some(item::weapon::WeaponSpecial::Charge), | ||||
|                             attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 40}), | ||||
|                                     Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}), | ||||
|                             special: Some(WeaponSpecial::Charge), | ||||
|                             attrs: [Some(WeaponAttribute{attr: Attribute::Hit, value: 40}), | ||||
|                                     Some(WeaponAttribute{attr: Attribute::Dark, value: 100}), | ||||
|                                     None,], | ||||
|                             tekked: true, | ||||
|                         } | ||||
| @ -148,12 +150,12 @@ fn main() { | ||||
|             let item3 = entity_gateway.create_item( | ||||
|                 NewItemEntity { | ||||
|                     item: ItemDetail::Weapon( | ||||
|                         item::weapon::Weapon { | ||||
|                             weapon: item::weapon::WeaponType::Vulcan, | ||||
|                         Weapon { | ||||
|                             weapon: WeaponType::Vulcan, | ||||
|                             grind: 5, | ||||
|                             special: Some(item::weapon::WeaponSpecial::Charge), | ||||
|                             attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 100}), | ||||
|                                     Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}), | ||||
|                             special: Some(WeaponSpecial::Charge), | ||||
|                             attrs: [Some(WeaponAttribute{attr: Attribute::Hit, value: 100}), | ||||
|                                     Some(WeaponAttribute{attr: Attribute::Dark, value: 100}), | ||||
|                                     None,], | ||||
|                             tekked: true, | ||||
|                         } | ||||
| @ -162,13 +164,13 @@ fn main() { | ||||
|             let item4 = entity_gateway.create_item( | ||||
|                 NewItemEntity { | ||||
|                     item: ItemDetail::Weapon( | ||||
|                         item::weapon::Weapon { | ||||
|                             weapon: item::weapon::WeaponType::DarkFlow, | ||||
|                         Weapon { | ||||
|                             weapon: WeaponType::DarkFlow, | ||||
|                             grind: 0, | ||||
|                             special: None, | ||||
|                             attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 100}), | ||||
|                                     Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}), | ||||
|                                     Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),], | ||||
|                             attrs: [Some(WeaponAttribute{attr: Attribute::Hit, value: 100}), | ||||
|                                     Some(WeaponAttribute{attr: Attribute::Dark, value: 100}), | ||||
|                                     Some(WeaponAttribute{attr: Attribute::Native, value: 100}),], | ||||
|                             tekked: true, | ||||
|                         } | ||||
|                     ), | ||||
| @ -289,13 +291,13 @@ fn main() { | ||||
|             let item14 = entity_gateway.create_item( | ||||
|                 NewItemEntity { | ||||
|                     item: ItemDetail::Weapon( | ||||
|                         item::weapon::Weapon { | ||||
|                             weapon: item::weapon::WeaponType::Vulcan, | ||||
|                         Weapon { | ||||
|                             weapon: WeaponType::Vulcan, | ||||
|                             grind: 5, | ||||
|                             special: Some(item::weapon::WeaponSpecial::Charge), | ||||
|                             attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 100}), | ||||
|                                     Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}), | ||||
|                                     Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),], | ||||
|                             special: Some(WeaponSpecial::Charge), | ||||
|                             attrs: [Some(WeaponAttribute{attr: Attribute::Hit, value: 100}), | ||||
|                                     Some(WeaponAttribute{attr: Attribute::Dark, value: 100}), | ||||
|                                     Some(WeaponAttribute{attr: Attribute::Native, value: 100}),], | ||||
|                             tekked: true, | ||||
|                         } | ||||
|                     ), | ||||
|  | ||||
| @ -19,6 +19,8 @@ use shops::{WeaponShopItem, ToolShopItem, ArmorShopItem}; | ||||
| pub enum ClientError { | ||||
|     #[error("not found {0}")] | ||||
|     NotFound(ClientId), | ||||
|     #[error("failed to get multiple clients")] | ||||
|     WithManyFailed, | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| @ -35,10 +37,10 @@ impl Clients { | ||||
| 
 | ||||
|     pub async fn remove(&mut self, client_id: &ClientId) -> Option<ClientState> { | ||||
|         Some(self.0 | ||||
|             .write() | ||||
|             .await | ||||
|             .remove(client_id)? | ||||
|             .into_inner()) | ||||
|              .write() | ||||
|              .await | ||||
|              .remove(client_id)? | ||||
|              .into_inner()) | ||||
|     } | ||||
| 
 | ||||
|     pub async fn with<'a, T, F>(&'a self, client_id: ClientId, func: F) -> Result<T, anyhow::Error> | ||||
| @ -66,25 +68,20 @@ impl Clients { | ||||
|         let clients = self.0 | ||||
|             .read() | ||||
|             .await; | ||||
|         
 | ||||
|         let mut client_states: [std::mem::MaybeUninit<RwLockReadGuard<ClientState>>; N] = unsafe { | ||||
|             std::mem::MaybeUninit::uninit().assume_init() | ||||
|         }; | ||||
| 
 | ||||
|         for (cindex, client_id) in client_ids.iter().enumerate() { | ||||
|         let mut client_states = Vec::new(); | ||||
| 
 | ||||
|         for client_id in client_ids.iter() { | ||||
|             let c = clients | ||||
|                 .get(client_id) | ||||
|                 .ok_or(ClientError::NotFound(*client_id))? | ||||
|                 .read() | ||||
|                 .await; | ||||
|             client_states[cindex].write(c); | ||||
|             client_states.push(c); | ||||
|         } | ||||
|         
 | ||||
|         let client_states = unsafe { | ||||
|             std::mem::transmute_copy(&client_states) | ||||
|         }; | ||||
| 
 | ||||
|         Ok(func(client_states).await) | ||||
|         let result = func(client_states.try_into().map_err(|_| ClientError::WithManyFailed)?).await; | ||||
|         Ok(result) | ||||
|     } | ||||
| 
 | ||||
|     pub async fn with_mut<'a, T, F>(&'a self, client_id: ClientId, func: F) -> Result<T, anyhow::Error> | ||||
|  | ||||
| @ -9,6 +9,8 @@ entity = { workspace = true } | ||||
| maps = { workspace = true } | ||||
| stats = { workspace = true } | ||||
| 
 | ||||
| libpso = { workspace = true } | ||||
| 
 | ||||
| rand = { workspace = true } | ||||
| rand_chacha = { workspace = true } | ||||
| serde = { workspace = true } | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
| use rand::{Rng}; | ||||
| use rand::distributions::{WeightedIndex, Distribution}; | ||||
| use serde::{Serialize, Deserialize}; | ||||
| use entity::character::SectionID; | ||||
| use libpso::character::SectionID; | ||||
| use maps::room::{Difficulty, Episode}; | ||||
| use maps::area::MapArea; | ||||
| use crate::{ItemDropType, load_data_file}; | ||||
|  | ||||
| @ -3,7 +3,7 @@ use serde::{Serialize, Deserialize}; | ||||
| use rand::Rng; | ||||
| use rand::distributions::{WeightedIndex, Distribution}; | ||||
| 
 | ||||
| use entity::character::SectionID; | ||||
| use libpso::character::SectionID; | ||||
| use entity::item::armor::{ArmorType, Armor}; | ||||
| use maps::room::{Difficulty, Episode}; | ||||
| use maps::area::MapArea; | ||||
|  | ||||
| @ -4,7 +4,7 @@ use rand::Rng; | ||||
| use rand::distributions::{WeightedIndex, Distribution}; | ||||
| 
 | ||||
| use entity::item::shield::{ShieldType, Shield}; | ||||
| use entity::character::SectionID; | ||||
| use libpso::character::SectionID; | ||||
| use maps::room::{Difficulty, Episode}; | ||||
| use maps::area::MapArea; | ||||
| use crate::{ItemDropType, load_data_file}; | ||||
|  | ||||
| @ -3,7 +3,7 @@ use serde::{Serialize, Deserialize}; | ||||
| use rand::Rng; | ||||
| use rand::seq::IteratorRandom; | ||||
| 
 | ||||
| use entity::character::SectionID; | ||||
| use libpso::character::SectionID; | ||||
| use entity::item::unit::{UnitType, Unit, UnitModifier}; | ||||
| use maps::room::{Difficulty, Episode}; | ||||
| use maps::area::MapArea; | ||||
|  | ||||
| @ -4,8 +4,8 @@ use rand::Rng; | ||||
| use rand::distributions::{WeightedIndex, Distribution}; | ||||
| use rand::seq::SliceRandom; | ||||
| 
 | ||||
| use entity::character::SectionID; | ||||
| use entity::item::weapon::{Weapon, WeaponType, Attribute, WeaponAttribute, WeaponSpecial}; | ||||
| use libpso::character::SectionID; | ||||
| use libpso::item::weapon::{Weapon, WeaponType, Attribute, WeaponAttribute, WeaponSpecial}; | ||||
| use maps::room::{Difficulty, Episode}; | ||||
| use maps::area::MapArea; | ||||
| use crate::{ItemDropType, load_data_file}; | ||||
|  | ||||
| @ -24,7 +24,7 @@ use rand::{Rng, SeedableRng}; | ||||
| use maps::monster::MonsterType; | ||||
| use maps::room::{Difficulty, Episode}; | ||||
| use maps::area::MapArea; | ||||
| use entity::character::SectionID; | ||||
| use libpso::character::SectionID; | ||||
| use crate::generic_weapon::GenericWeaponTable; | ||||
| use crate::generic_armor::GenericArmorTable; | ||||
| use crate::generic_shield::GenericShieldTable; | ||||
| @ -33,7 +33,8 @@ use crate::tool_table::ToolTable; | ||||
| use crate::rare_drop_table::RareDropTable; | ||||
| use crate::box_drop_table::BoxDropTable; | ||||
| use maps::object::MapObject; | ||||
| use entity::item::{ItemType, weapon, armor, shield, unit, mag, tool, tech, esweapon}; | ||||
| use entity::item::{ItemType, armor, shield, unit, mag, tool, tech, esweapon}; | ||||
| use libpso::item::weapon; | ||||
| 
 | ||||
| 
 | ||||
| fn data_file_path(episode: Episode, difficulty: Difficulty, section_id: SectionID, filename: &str) -> PathBuf { | ||||
|  | ||||
| @ -1,13 +1,13 @@ | ||||
| use std::collections::HashMap; | ||||
| use rand::Rng; | ||||
| use serde::{Serialize, Deserialize}; | ||||
| use entity::item::weapon::{Weapon, WeaponType}; | ||||
| use libpso::item::weapon::{Weapon, WeaponType}; | ||||
| use entity::item::armor::{Armor, ArmorType}; | ||||
| use entity::item::shield::{Shield, ShieldType}; | ||||
| use entity::item::unit::{Unit, UnitType}; | ||||
| use entity::item::tool::{Tool, ToolType}; | ||||
| use entity::item::mag::{Mag, MagType}; | ||||
| use entity::character::SectionID; | ||||
| use libpso::character::SectionID; | ||||
| use maps::monster::MonsterType; | ||||
| use maps::room::{Difficulty, Episode}; | ||||
| use maps::area::MapArea; | ||||
|  | ||||
| @ -6,7 +6,7 @@ use rand::distributions::{WeightedIndex, Distribution}; | ||||
| use entity::item::tech::{Technique, TechniqueDisk}; | ||||
| use maps::room::{Difficulty, Episode}; | ||||
| use maps::area::MapArea; | ||||
| use entity::character::SectionID; | ||||
| use libpso::character::SectionID; | ||||
| use crate::{ItemDropType, load_data_file}; | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -6,7 +6,7 @@ use rand::distributions::{WeightedIndex, Distribution}; | ||||
| use entity::item::tool::{Tool, ToolType}; | ||||
| use maps::room::{Difficulty, Episode}; | ||||
| use maps::area::MapArea; | ||||
| use entity::character::SectionID; | ||||
| use libpso::character::SectionID; | ||||
| use crate::{ItemDropType, load_data_file}; | ||||
| use crate::tech_table::TechniqueTable; | ||||
| 
 | ||||
|  | ||||
| @ -1,146 +1,12 @@ | ||||
| use std::convert::{From, Into}; | ||||
| use std::collections::HashMap; | ||||
| use serde::{Serialize, Deserialize}; | ||||
| 
 | ||||
| use libpso::packet::ship::{UpdateConfig, WriteInfoboard}; | ||||
| use libpso::character::{CharacterClass, SectionID}; | ||||
| use libpso::character::settings::{DEFAULT_PALETTE_CONFIG, DEFAULT_TECH_MENU}; | ||||
| use crate::item::tech::Technique; | ||||
| use crate::account::UserAccountId; | ||||
| 
 | ||||
| #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, enum_utils::FromStr, derive_more::Display, Serialize, Deserialize, Default)] | ||||
| pub enum CharacterClass { | ||||
|     #[default] | ||||
|     HUmar, | ||||
|     HUnewearl, | ||||
|     HUcast, | ||||
|     HUcaseal, | ||||
|     RAmar, | ||||
|     RAmarl, | ||||
|     RAcast, | ||||
|     RAcaseal, | ||||
|     FOmar, | ||||
|     FOmarl, | ||||
|     FOnewm, | ||||
|     FOnewearl, | ||||
| } | ||||
| 
 | ||||
| // TODO: TryFrom
 | ||||
| impl From<u8> 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 From<CharacterClass> for u8 { | ||||
|     fn from(other: CharacterClass) -> u8 { | ||||
|         match other { | ||||
|              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 { | ||||
|         matches!(self, | ||||
|                  CharacterClass::HUmar | | ||||
|                  CharacterClass::RAmar | | ||||
|                  CharacterClass::RAmarl | | ||||
|                  CharacterClass::FOmar | | ||||
|                  CharacterClass::FOmarl) | ||||
|     } | ||||
|     
 | ||||
|     pub fn is_newman(&self) -> bool { | ||||
|         matches!(self, | ||||
|                  CharacterClass::HUnewearl | | ||||
|                  CharacterClass::FOnewm | | ||||
|                  CharacterClass::FOnewearl) | ||||
|     } | ||||
|     
 | ||||
|     pub fn is_android(&self) -> bool { | ||||
|         matches!(self, | ||||
|                  CharacterClass::HUcast | | ||||
|                  CharacterClass::HUcaseal | | ||||
|                  CharacterClass::RAcast | | ||||
|                  CharacterClass::RAcaseal) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, enum_utils::FromStr, derive_more::Display, Serialize, Deserialize, Default)] | ||||
| pub enum SectionID { | ||||
|     #[default] | ||||
|     Viridia, | ||||
|     Greenill, | ||||
|     Skyly, | ||||
|     Bluefull, | ||||
|     Purplenum, | ||||
|     Pinkal, | ||||
|     Redria, | ||||
|     Oran, | ||||
|     Yellowboze, | ||||
|     Whitill, | ||||
| } | ||||
| 
 | ||||
| impl From<u8> 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 From<SectionID> for u8 { | ||||
|     fn from(other: SectionID) -> u8 { | ||||
|         match other { | ||||
|             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 { | ||||
|  | ||||
| @ -271,7 +271,7 @@ fn apply_modifiers(items: &BTreeMap<ItemEntityId, ItemEntity>, | ||||
|                     ItemDetail::Weapon(mut weapon) => { | ||||
|                         if let Some(weapon_modifiers) = weapon_modifiers.get(&item.id) { | ||||
|                             for weapon_modifier in weapon_modifiers.iter() { | ||||
|                                 weapon.apply_modifier(weapon_modifier); | ||||
|                                 weapon::apply_modifier(&mut weapon, weapon_modifier); | ||||
|                             } | ||||
|                         } | ||||
|                         ItemDetail::Weapon(weapon) | ||||
|  | ||||
| @ -6,7 +6,10 @@ use libpso::character::settings; | ||||
| use libpso::util::vec_to_array; | ||||
| use crate::account::*; | ||||
| use crate::character::*; | ||||
| use libpso::character::{CharacterClass, SectionID}; | ||||
| use crate::item::*; | ||||
| use crate::item::weapon::WeaponModifier; | ||||
| use libpso::item::weapon; | ||||
| use crate::room::*; | ||||
| use maps::area::MapArea; | ||||
| use maps::room::{Episode, Difficulty}; | ||||
| @ -326,7 +329,7 @@ impl From<PgWeapon> for weapon::Weapon { | ||||
| #[derive(Debug, sqlx::FromRow)] | ||||
| pub struct PgWeaponModifier { | ||||
|     pub weapon: i32, | ||||
|     pub modifier: sqlx::types::Json<weapon::WeaponModifier>, | ||||
|     pub modifier: sqlx::types::Json<WeaponModifier>, | ||||
| } | ||||
| 
 | ||||
| #[derive(Debug, Serialize, Deserialize)] | ||||
|  | ||||
| @ -92,7 +92,7 @@ async fn apply_item_modifications(conn: &mut sqlx::PgConnection, item: ItemEntit | ||||
| 
 | ||||
|             weapon_modifiers.for_each(|modifier| async move { | ||||
|                 if let Ok(modifier) = modifier { | ||||
|                     weapon.apply_modifier(&modifier.modifier); | ||||
|                     weapon::apply_modifier(&mut weapon, &modifier.modifier); | ||||
|                 } | ||||
|             }).await; | ||||
| 
 | ||||
|  | ||||
| @ -9,7 +9,7 @@ pub enum ItemParseError { | ||||
|     InvalidESWeaponName, | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize, strum_macros::EnumIter)] | ||||
| #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)] | ||||
| pub enum ESWeaponType { | ||||
|     Saber = 0, | ||||
|     Sword, | ||||
| @ -121,7 +121,7 @@ impl ESWeaponType { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize, strum_macros::EnumIter)] | ||||
| #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] | ||||
| pub enum ESWeaponSpecial { | ||||
|     Jellen = 1, | ||||
|     Zalure, | ||||
|  | ||||
| @ -1,8 +1,8 @@ | ||||
| use std::collections::HashMap; | ||||
| use thiserror::Error; | ||||
| use serde::{Serialize, Deserialize}; | ||||
| use libpso::character::{CharacterClass, SectionID}; | ||||
| use crate::item::tool::ToolType; | ||||
| use crate::character::{CharacterClass, SectionID}; | ||||
| use crate::item::ItemEntityId; | ||||
| use std::io::Read; | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,3 @@ | ||||
| #![allow(dead_code)] | ||||
| pub mod weapon; 
 | ||||
| pub mod armor; 
 | ||||
| pub mod shield; | ||||
| @ -13,6 +12,7 @@ use crate::character::CharacterEntityId; | ||||
| use crate::room::RoomEntityId; | ||||
| use maps::area::MapArea; | ||||
| use maps::monster::MonsterType; | ||||
| use libpso::item::ItemBytes; | ||||
| //use crate::ship::drops::ItemDropType;
 | ||||
| 
 | ||||
| #[derive(PartialEq, Eq, Copy, Clone, Debug, Hash, PartialOrd, Ord, Serialize, Deserialize)] | ||||
| @ -108,7 +108,7 @@ impl Meseta { | ||||
| 
 | ||||
| #[derive(Clone, Debug, PartialEq, Eq, Hash)] | ||||
| pub enum ItemType { | ||||
|     Weapon(weapon::WeaponType), | ||||
|     Weapon(libpso::item::weapon::WeaponType), | ||||
|     Armor(armor::ArmorType), | ||||
|     Shield(shield::ShieldType), | ||||
|     Unit(unit::UnitType), | ||||
| @ -125,7 +125,7 @@ pub enum ItemParseError { | ||||
| 
 | ||||
| #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] | ||||
| pub enum ItemDetail { | ||||
|     Weapon(weapon::Weapon), | ||||
|     Weapon(libpso::item::weapon::Weapon), | ||||
|     Armor(armor::Armor), | ||||
|     Shield(shield::Shield), | ||||
|     Unit(unit::Unit), | ||||
|  | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @ -1,7 +1,8 @@ | ||||
| use serde::{Serialize, Deserialize}; | ||||
| 
 | ||||
| 
 | ||||
| use crate::character::{CharacterEntityId, SectionID}; | ||||
| use crate::character::CharacterEntityId; | ||||
| use libpso::character::SectionID; | ||||
| use maps::room::{Episode, Difficulty}; | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -13,6 +13,7 @@ use entity::gateway::{EntityGateway, EntityGatewayTransaction}; | ||||
| use entity::item::{ItemDetail, NewItemEntity, TradeId, ItemModifier}; | ||||
| use entity::item::tool::Tool; | ||||
| use entity::room::RoomEntityId; | ||||
| use entity::item::weapon::apply_modifier; | ||||
| use maps::area::MapArea; | ||||
| use crate::state::{ItemStateProxy, ItemStateError, AddItemResult, StackedItemDetail, IndividualItemDetail}; | ||||
| use crate::bank::{BankItem, BankItemDetail}; | ||||
| @ -1112,7 +1113,7 @@ where | ||||
|         Box::pin(async move { | ||||
|             match (&mut inventory_item.item, modifier) { | ||||
|                 (InventoryItemDetail::Individual(IndividualItemDetail{entity_id, item: ItemDetail::Weapon(ref mut weapon), ..}), ItemModifier::WeaponModifier(modifier)) => { | ||||
|                     weapon.apply_modifier(&modifier); | ||||
|                     apply_modifier(weapon, &modifier); | ||||
|                     transaction.gateway().add_weapon_modifier(entity_id, &modifier).await?; | ||||
|                 }, | ||||
|                 _ => return Err(ItemStateError::InvalidModifier.into()) | ||||
|  | ||||
| @ -10,7 +10,7 @@ use entity::item::mag::{MagCell, MagCellError}; | ||||
| use entity::item::tool::{Tool, ToolType}; | ||||
| use entity::item::tech::TechniqueDisk; | ||||
| use entity::item::{ItemDetail, ItemEntityId}; | ||||
| use entity::item::weapon::WeaponModifier; | ||||
| use entity::item::weapon::{WeaponModifier, apply_modifier}; | ||||
| use crate::state::ItemStateProxy; | ||||
| use crate::inventory::InventoryItemDetail; | ||||
| 
 | ||||
| @ -259,7 +259,7 @@ where | ||||
|     let mut inventory = item_state.inventory(&character.id).await?; | ||||
|     let (weapon_entity_id, weapon) = inventory.equipped_weapon_mut() | ||||
|         .ok_or(ApplyItemError::ItemNotEquipped)?; | ||||
|     weapon.apply_modifier(&modifier); | ||||
|     apply_modifier(weapon, &modifier); | ||||
|     entity_gateway.add_weapon_modifier(&weapon_entity_id, &modifier).await?; | ||||
|     item_state.set_inventory(inventory).await; | ||||
| 
 | ||||
|  | ||||
| @ -1,4 +1,5 @@ | ||||
| use std::cmp::Ordering; | ||||
| use libpso::item::ItemBytes; | ||||
| use libpso::character::character; | ||||
| use crate::ClientItemId; | ||||
| use entity::item::{Meseta, ItemEntityId, ItemDetail, ItemEntity, BankEntity, BankItemEntity}; | ||||
|  | ||||
| @ -8,7 +8,7 @@ use async_std::sync::{Arc, Mutex}; | ||||
| use entity::character::CharacterEntityId; | ||||
| use entity::item::tool::ToolType; | ||||
| use entity::item::mag::Mag; | ||||
| use entity::item::weapon::Weapon; | ||||
| use libpso::item::weapon::Weapon; | ||||
| use shops::{ShopItem, ArmorShopItem, ToolShopItem, WeaponShopItem}; | ||||
| use crate::state::ItemStateError; | ||||
| use crate::state::{IndividualItemDetail, StackedItemDetail, AddItemResult}; | ||||
|  | ||||
| @ -4,11 +4,12 @@ use async_std::sync::{Arc, RwLock, Mutex}; | ||||
| use futures::stream::{FuturesOrdered, StreamExt}; | ||||
| use anyhow::Context; | ||||
| 
 | ||||
| use libpso::item::ItemBytes; | ||||
| use entity::gateway::{EntityGateway, GatewayError}; | ||||
| use entity::character::{CharacterEntity, CharacterEntityId}; | ||||
| use entity::item::{ItemEntityId, ItemDetail, ItemEntity, InventoryItemEntity, BankItemEntity, BankIdentifier}; | ||||
| use entity::item::tool::Tool; | ||||
| use entity::item::weapon::Weapon; | ||||
| use libpso::item::weapon::Weapon; | ||||
| use entity::item::mag::Mag; | ||||
| use drops::ItemDrop; | ||||
| use crate::ClientItemId; | ||||
| @ -240,7 +241,7 @@ impl ItemState { | ||||
|                             item_id: ClientItemId(0), | ||||
|                             item: InventoryItemDetail::Stacked(StackedItemDetail { | ||||
|                                 entity_ids: items.iter().map(|i| i.id).collect(), | ||||
|                                 tool: items.get(0) | ||||
|                                 tool: items.first() | ||||
|                                     .ok_or_else(|| ItemStateError::StackedItemError(items.clone()))? | ||||
|                                     .item | ||||
|                                     .clone() | ||||
| @ -284,7 +285,7 @@ impl ItemState { | ||||
|                     BankItemEntity::Stacked(items) => { | ||||
|                         BankItemDetail::Stacked(StackedItemDetail { | ||||
|                             entity_ids: items.iter().map(|i| i.id).collect(), | ||||
|                             tool: items.get(0) | ||||
|                             tool: items.first() | ||||
|                                 .ok_or_else(|| ItemStateError::StackedItemError(items.clone()))? | ||||
|                                 .item | ||||
|                                 .clone() | ||||
|  | ||||
| @ -317,7 +317,7 @@ impl ClientLocation { | ||||
|             .flatten() | ||||
|             .collect::<Vec<_>>(); | ||||
|         r.sort_by_key(|k| k.time_join); | ||||
|         let c = r.get(0) | ||||
|         let c = r.first() | ||||
|             .ok_or(GetLeaderError::NoClientInArea)?; | ||||
|         Ok(**c) | ||||
|     } | ||||
| @ -332,7 +332,7 @@ impl ClientLocation { | ||||
|             .flatten() | ||||
|             .collect::<Vec<_>>(); | ||||
|         l.sort_by_key(|k| k.time_join); | ||||
|         let c = l.get(0).ok_or(GetLeaderError::NoClientInArea)?; | ||||
|         let c = l.first().ok_or(GetLeaderError::NoClientInArea)?; | ||||
|         Ok(**c) | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -13,6 +13,7 @@ use libpso::packet::ship::{MenuDetail, SmallLeftDialog}; | ||||
| use libpso::{PacketParseError, PSOPacket}; | ||||
| use libpso::crypto::bb::PSOBBCipher; | ||||
| use libpso::character::character; | ||||
| use libpso::character::CharacterClass; | ||||
| use entity::item; | ||||
| 
 | ||||
| use networking::cipherkeys::{ELSEWHERE_PRIVATE_KEY, ELSEWHERE_PARRAY}; | ||||
| @ -24,12 +25,12 @@ use libpso::util::{utf8_to_array, utf8_to_utf16_array}; | ||||
| use entity::gateway::{EntityGateway, GatewayError}; | ||||
| use entity::account::{UserAccountId, UserAccountEntity, NewUserSettingsEntity, USERFLAG_NEWCHAR, USERFLAG_DRESSINGROOM}; | ||||
| use entity::item::{NewItemEntity, ItemDetail, ItemNote, InventoryItemEntity, InventoryEntity, BankEntity, BankIdentifier, EquippedEntity, Meseta}; | ||||
| use entity::item::weapon::Weapon; | ||||
| use libpso::item::weapon::{Weapon, WeaponType}; | ||||
| use entity::item::armor::Armor; | ||||
| use entity::item::tech::Technique; | ||||
| use entity::item::tool::Tool; | ||||
| use entity::item::mag::Mag; | ||||
| use entity::character::{CharacterEntity, NewCharacterEntity, CharacterClass, TechLevel}; | ||||
| use entity::character::{CharacterEntity, NewCharacterEntity, TechLevel}; | ||||
| 
 | ||||
| use crate::login::get_login_status; | ||||
| use networking::interserver::AuthToken; | ||||
| @ -215,9 +216,9 @@ async fn new_character<EG: EntityGateway + Clone>(entity_gateway: &mut EG, user: | ||||
|     entity_gateway.set_bank_meseta(&character.id, &BankIdentifier::Character, Meseta(0)).await?; | ||||
| 
 | ||||
|     let new_weapon = match character.char_class { | ||||
|         CharacterClass::HUmar | CharacterClass::HUnewearl | CharacterClass::HUcast | CharacterClass::HUcaseal => item::weapon::WeaponType::Saber, | ||||
|         CharacterClass::RAmar | CharacterClass::RAmarl | CharacterClass::RAcast | CharacterClass::RAcaseal => item::weapon::WeaponType::Handgun, | ||||
|         CharacterClass::FOmar | CharacterClass::FOmarl | CharacterClass::FOnewm | CharacterClass::FOnewearl => item::weapon::WeaponType::Cane, | ||||
|         CharacterClass::HUmar | CharacterClass::HUnewearl | CharacterClass::HUcast | CharacterClass::HUcaseal => WeaponType::Saber, | ||||
|         CharacterClass::RAmar | CharacterClass::RAmarl | CharacterClass::RAcast | CharacterClass::RAcaseal => WeaponType::Handgun, | ||||
|         CharacterClass::FOmar | CharacterClass::FOmarl | CharacterClass::FOnewm | CharacterClass::FOnewearl => WeaponType::Cane, | ||||
|     }; | ||||
| 
 | ||||
| 
 | ||||
| @ -753,8 +754,8 @@ fn new_character_from_preview(user: &UserAccountEntity, preview: &CharacterPrevi | ||||
|     let mut character = NewCharacterEntity::new(user.id); | ||||
|     character.slot = preview.slot; | ||||
|     character.name = String::from_utf16_lossy(&preview.character.name).trim_matches(char::from(0)).into(); | ||||
|     character.section_id = preview.character.section_id.into(); | ||||
|     character.char_class = preview.character.ch_class.into(); | ||||
|     character.section_id = preview.character.section_id; | ||||
|     character.char_class = preview.character.ch_class; | ||||
|     character.appearance.costume = preview.character.costume; | ||||
|     character.appearance.skin = preview.character.skin; | ||||
|     character.appearance.face = preview.character.face; | ||||
| @ -809,8 +810,8 @@ impl<'a> SelectScreenCharacterBuilder<'a> { | ||||
|             //model: character.model,
 | ||||
|             //_unused: [0; 15],
 | ||||
|             //name_color_checksum: character.name_color_checksum,
 | ||||
|             section_id: character.section_id.into(), | ||||
|             ch_class: character.char_class.into(), | ||||
|             section_id: character.section_id, | ||||
|             ch_class: character.char_class, | ||||
|             //v2flags: character.v2flags,
 | ||||
|             //version: character.version,
 | ||||
|             //v1flags: character.v1flags,
 | ||||
|  | ||||
| @ -62,8 +62,8 @@ impl<'a> CharacterBytesBuilder<'a> { | ||||
|             ata: stats.ata, | ||||
|             lck: stats.lck + character.materials.luck as u16 * 2, | ||||
|             level, | ||||
|             section_id: character.section_id.into(), | ||||
|             ch_class: character.char_class.into(), | ||||
|             section_id: character.section_id, | ||||
|             ch_class: character.char_class, | ||||
|             costume: character.appearance.costume, | ||||
|             skin: character.appearance.skin, | ||||
|             face: character.appearance.face, | ||||
|  | ||||
| @ -1,5 +1,7 @@ | ||||
| use libpso::packet::messages::*; | ||||
| use libpso::packet::ship::*; | ||||
| use libpso::item::weapon; | ||||
| use libpso::item::ItemBytes; | ||||
| use entity::item; | ||||
| use stats::leveltable::CharacterStats; | ||||
| //use crate::ship::ship::{ShipError};
 | ||||
| @ -215,7 +217,7 @@ pub fn shop_list<I: ShopItem>(shop_type: u8, items: &[I]) -> ShopList { | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| pub fn tek_preview(id: ClientItemId, weapon: &item::weapon::Weapon) -> TekPreview { | ||||
| pub fn tek_preview(id: ClientItemId, weapon: &weapon::Weapon) -> TekPreview { | ||||
|     let bytes = weapon.as_bytes(); | ||||
|     TekPreview { | ||||
|         client: 0x79, | ||||
|  | ||||
| @ -10,6 +10,8 @@ quests = { workspace = true } | ||||
| location = { workspace = true } | ||||
| drops = { workspace = true } | ||||
| 
 | ||||
| libpso = { workspace = true } | ||||
| 
 | ||||
| rand = { workspace = true } | ||||
| async-std = { workspace = true } | ||||
| futures = { workspace = true } | ||||
|  | ||||
| @ -10,7 +10,7 @@ use rand::Rng; | ||||
| use quests::{QuestList, QuestLoadError}; | ||||
| use maps::maps::Maps; | ||||
| use drops::DropTable; | ||||
| use entity::character::SectionID; | ||||
| use libpso::character::SectionID; | ||||
| use entity::room::{RoomEntityId, RoomEntityMode}; | ||||
| use maps::monster::{load_monster_stats_table, MonsterType, MonsterStats}; | ||||
| use maps::area::MapAreaLookup; | ||||
|  | ||||
| @ -482,7 +482,7 @@ where | ||||
|                 .as_weapon() | ||||
|                 .ok_or_else(|| ItemStateError::WrongItemType(ClientItemId(tek_request.item_id)))?; | ||||
| 
 | ||||
|             weapon.apply_modifier(&item::weapon::WeaponModifier::Tekked { | ||||
|             item::weapon::apply_modifier(&mut weapon, &item::weapon::WeaponModifier::Tekked { | ||||
|                 special: special_mod, | ||||
|                 percent: percent_mod, | ||||
|                 grind: grind_mod, | ||||
|  | ||||
| @ -30,7 +30,7 @@ use networking::serverstate::{SendServerPacket, RecvServerPacket, ServerState, O | ||||
| use networking::interserver::{AuthToken, Ship, ServerId, InterserverActor, LoginMessage, ShipMessage}; | ||||
| use pktbuilder::ship::SHIP_MENU_ID; | ||||
| use entity::gateway::{EntityGateway, GatewayError}; | ||||
| use entity::character::SectionID; | ||||
| use libpso::character::SectionID; | ||||
| use entity::room::RoomNote; | ||||
| use location::{ClientLocation, RoomLobby, ClientLocationError, RoomId}; | ||||
| use drops::{DropTable, StandardDropTable}; | ||||
|  | ||||
| @ -8,7 +8,7 @@ use libpso::packet::messages::*; | ||||
| use networking::serverstate::ClientId; | ||||
| use stats::leveltable::LEVEL_TABLE; | ||||
| use entity::gateway::EntityGateway; | ||||
| use entity::character::SectionID; | ||||
| use libpso::character::SectionID; | ||||
| use entity::room::{NewRoomEntity, RoomEntityMode, RoomNote}; | ||||
| use drops::DropTable; | ||||
| use crate::SendShipPacket; | ||||
|  | ||||
| @ -8,6 +8,8 @@ maps = { workspace = true } | ||||
| stats = { workspace = true } | ||||
| entity = { workspace = true } | ||||
| 
 | ||||
| libpso = { workspace = true } | ||||
| 
 | ||||
| async-std = { workspace = true } | ||||
| async-trait = { workspace = true } | ||||
| futures = { workspace = true } | ||||
|  | ||||
| @ -7,7 +7,7 @@ use futures::future::OptionFuture; | ||||
| use std::collections::HashMap; | ||||
| use entity::item::ItemDetail; | ||||
| use maps::room::Difficulty; | ||||
| use entity::character::SectionID; | ||||
| use libpso::character::SectionID; | ||||
| 
 | ||||
| pub use weapon::{WeaponShop, WeaponShopItem}; | ||||
| pub use tool::{ToolShop, ToolShopItem}; | ||||
|  | ||||
| @ -231,9 +231,6 @@ impl<R: Rng + SeedableRng> ToolShop<R> { | ||||
|         else { | ||||
|             let mut techs = Vec::new(); | ||||
|             let tier = tier.techs.iter() | ||||
|                 .map(|(tech, entry)| { | ||||
|                     (tech, entry) | ||||
|                 }) | ||||
|                 .collect::<Vec<_>>(); | ||||
| 
 | ||||
|             let tech_choice = WeightedIndex::new(tier.iter().map(|(_, e)| e.probability)).unwrap(); | ||||
|  | ||||
| @ -8,10 +8,10 @@ use serde::Deserialize; | ||||
| use rand::{Rng, SeedableRng}; | ||||
| use rand::distributions::{WeightedIndex, Distribution}; | ||||
| use rand::seq::{SliceRandom, IteratorRandom}; | ||||
| use entity::character::SectionID; | ||||
| use libpso::character::SectionID; | ||||
| use maps::room::Difficulty; | ||||
| use entity::item::ItemDetail; | ||||
| use entity::item::weapon::{Weapon, WeaponType, WeaponSpecial, Attribute, WeaponAttribute}; | ||||
| use libpso::item::weapon::{Weapon, WeaponType, WeaponSpecial, Attribute, WeaponAttribute}; | ||||
| use crate::ShopItem; | ||||
| use stats::items::WEAPON_STATS; | ||||
| 
 | ||||
|  | ||||
| @ -4,6 +4,7 @@ version = "0.1.0" | ||||
| edition = "2021" | ||||
| 
 | ||||
| [dependencies] | ||||
| libpso = { workspace = true } | ||||
| entity = { workspace = true } | ||||
| toml = { workspace = true } | ||||
| serde  = { workspace = true } | ||||
|  | ||||
| @ -4,7 +4,7 @@ use serde::{Serialize, Deserialize}; | ||||
| use std::fs::File; | ||||
| use std::io::Read; | ||||
| 
 | ||||
| use entity::item::weapon::WeaponType; | ||||
| use libpso::item::weapon::WeaponType; | ||||
| use entity::item::armor::ArmorType; | ||||
| use entity::item::shield::ShieldType; | ||||
| use entity::item::unit::UnitType; | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| use std::fs::File; | ||||
| use serde_json::Value; | ||||
| use entity::character::CharacterClass; | ||||
| use libpso::character::CharacterClass; | ||||
| use std::sync::LazyLock; | ||||
| 
 | ||||
| pub static LEVEL_TABLE: LazyLock<CharacterLevelTable> = LazyLock::new(CharacterLevelTable::default); | ||||
|  | ||||
| @ -3,7 +3,8 @@ | ||||
| use networking::serverstate::{ClientId, ServerState}; | ||||
| use entity::gateway::EntityGateway; | ||||
| use entity::account::{UserAccountEntity, NewUserAccountEntity, NewUserSettingsEntity}; | ||||
| use entity::character::{CharacterEntity, NewCharacterEntity, SectionID}; | ||||
| use entity::character::{CharacterEntity, NewCharacterEntity}; | ||||
| use libpso::character::SectionID; | ||||
| use entity::item::{Meseta, BankIdentifier}; | ||||
| use ship_server::{ShipServerState, ShipServerStateBuilder, RecvShipPacket}; | ||||
| use maps::room::{RoomMode, Difficulty, Episode}; | ||||
| @ -16,6 +17,7 @@ use drops::{DropTable, ItemDropType}; | ||||
| use shops::{ItemShops, WeaponShopItem, ToolShopItem, ArmorShopItem}; | ||||
| 
 | ||||
| use entity::item; | ||||
| use libpso::item::weapon; | ||||
| 
 | ||||
| use libpso::packet::ship::*; | ||||
| use libpso::packet::login::{Login, Session}; | ||||
| @ -159,16 +161,16 @@ pub async fn join_room<EG: EntityGateway + Clone>(ship: &mut ShipServerState<EG> | ||||
| 
 | ||||
| 
 | ||||
| pub struct WeaponBuilder { | ||||
|     weapon: item::weapon::WeaponType, | ||||
|     weapon: weapon::WeaponType, | ||||
|     grind: u8, | ||||
|     special: Option<item::weapon::WeaponSpecial>, | ||||
|     attributes: [Option<item::weapon::WeaponAttribute>; 3], | ||||
|     special: Option<weapon::WeaponSpecial>, | ||||
|     attributes: [Option<weapon::WeaponAttribute>; 3], | ||||
|     tekked: bool, | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| impl WeaponBuilder { | ||||
|     fn new(weapon: item::weapon::WeaponType) -> WeaponBuilder { | ||||
|     fn new(weapon: weapon::WeaponType) -> WeaponBuilder { | ||||
|         WeaponBuilder { | ||||
|             weapon, | ||||
|             grind: 0, | ||||
| @ -185,19 +187,19 @@ impl WeaponBuilder { | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn special(self, special: item::weapon::WeaponSpecial) -> WeaponBuilder { | ||||
|     pub fn special(self, special: weapon::WeaponSpecial) -> WeaponBuilder { | ||||
|         WeaponBuilder { | ||||
|             special: Some(special), | ||||
|             ..self | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     pub fn attr(mut self, attr: item::weapon::Attribute, value: i8) -> WeaponBuilder { | ||||
|     pub fn attr(mut self, attr: weapon::Attribute, value: i8) -> WeaponBuilder { | ||||
|         self.attributes | ||||
|             .iter_mut() | ||||
|             .find(|k| k.is_none()) | ||||
|             .map(|empty_attr| { | ||||
|                 *empty_attr = Some(item::weapon::WeaponAttribute { | ||||
|                 *empty_attr = Some(weapon::WeaponAttribute { | ||||
|                     attr, | ||||
|                     value, | ||||
|                 }) | ||||
| @ -216,7 +218,7 @@ impl WeaponBuilder { | ||||
|     pub fn as_new(self) -> item::NewItemEntity { | ||||
|         item::NewItemEntity { | ||||
|             item: item::ItemDetail::Weapon( | ||||
|                 item::weapon::Weapon { | ||||
|                 weapon::Weapon { | ||||
|                     weapon: self.weapon, | ||||
|                     grind: self.grind, | ||||
|                     special: self.special, | ||||
| @ -410,7 +412,7 @@ impl TechBuilder { | ||||
| pub struct ItemBuilder; | ||||
| 
 | ||||
| impl ItemBuilder { | ||||
|     pub fn weapon(weapon: item::weapon::WeaponType) -> WeaponBuilder { | ||||
|     pub fn weapon(weapon: weapon::WeaponType) -> WeaponBuilder { | ||||
|         WeaponBuilder::new(weapon) | ||||
|     } | ||||
| 
 | ||||
|  | ||||
| @ -2,6 +2,7 @@ use std::collections::BTreeSet; | ||||
| use networking::serverstate::{ClientId, ServerState}; | ||||
| use entity::gateway::{EntityGateway, InMemoryGateway}; | ||||
| use entity::item; | ||||
| use libpso::item::weapon; | ||||
| use ship_server::{RecvShipPacket, SendShipPacket}; | ||||
| use shops::StandardItemShops; | ||||
| 
 | ||||
| @ -20,7 +21,7 @@ async fn test_bank_items_sent_in_character_login() { | ||||
|     let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a").await; | ||||
| 
 | ||||
|     let item = entity_gateway.create_item( | ||||
|         ItemBuilder::weapon(item::weapon::WeaponType::Vulcan) | ||||
|         ItemBuilder::weapon(weapon::WeaponType::Vulcan) | ||||
|             .as_new() | ||||
|     ).await.unwrap(); | ||||
| 
 | ||||
| @ -47,7 +48,7 @@ async fn test_request_bank_items() { | ||||
|     let mut bank = Vec::new(); | ||||
|     for _ in 0..3 { | ||||
|         bank.push(entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Vulcan) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Vulcan) | ||||
|                 .as_new() | ||||
|         ).await.unwrap()); | ||||
|     } | ||||
| @ -120,7 +121,7 @@ async fn test_request_bank_items_sorted() { | ||||
|     let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a").await; | ||||
| 
 | ||||
|     let item1 = entity_gateway.create_item( | ||||
|         ItemBuilder::weapon(item::weapon::WeaponType::Vulcan) | ||||
|         ItemBuilder::weapon(weapon::WeaponType::Vulcan) | ||||
|             .as_new() | ||||
|     ).await.unwrap(); | ||||
|     let monomate = entity_gateway.create_item( | ||||
| @ -128,7 +129,7 @@ async fn test_request_bank_items_sorted() { | ||||
|             .as_new() | ||||
|     ).await.unwrap(); | ||||
|     let item2 = entity_gateway.create_item( | ||||
|         ItemBuilder::weapon(item::weapon::WeaponType::Calibur) | ||||
|         ItemBuilder::weapon(weapon::WeaponType::Calibur) | ||||
|             .as_new() | ||||
|     ).await.unwrap(); | ||||
| 
 | ||||
| @ -164,11 +165,11 @@ async fn test_deposit_individual_item() { | ||||
|     let (_user2, _char2) = new_user_character(&mut entity_gateway, "a2", "a").await; | ||||
| 
 | ||||
|     let item0 = entity_gateway.create_item( | ||||
|         ItemBuilder::weapon(item::weapon::WeaponType::Saber) | ||||
|         ItemBuilder::weapon(weapon::WeaponType::Saber) | ||||
|             .as_new() | ||||
|     ).await.unwrap(); | ||||
|     let item1 = entity_gateway.create_item( | ||||
|         ItemBuilder::weapon(item::weapon::WeaponType::Handgun) | ||||
|         ItemBuilder::weapon(weapon::WeaponType::Handgun) | ||||
|             .as_new() | ||||
|     ).await.unwrap(); | ||||
| 
 | ||||
| @ -468,14 +469,14 @@ async fn test_deposit_individual_item_in_full_bank() { | ||||
| 
 | ||||
|     let mut inventory = Vec::new(); | ||||
|     inventory.push(entity_gateway.create_item( | ||||
|         ItemBuilder::weapon(item::weapon::WeaponType::Vulcan) | ||||
|         ItemBuilder::weapon(weapon::WeaponType::Vulcan) | ||||
|             .as_new() | ||||
|     ).await.unwrap()); | ||||
| 
 | ||||
|     let mut bank = Vec::new(); | ||||
|     for _ in 0..200usize { | ||||
|         bank.push(entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Vulcan) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Vulcan) | ||||
|                 .as_new() | ||||
|         ).await.unwrap()); | ||||
|     } | ||||
| @ -533,7 +534,7 @@ async fn test_deposit_stacked_item_in_full_bank() { | ||||
|     let mut full_bank = Vec::new(); | ||||
|     for _ in 0..200usize { | ||||
|         full_bank.push(entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Vulcan) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Vulcan) | ||||
|                 .as_new() | ||||
|         ).await.unwrap()); | ||||
|     } | ||||
| @ -600,7 +601,7 @@ async fn test_deposit_stacked_item_in_full_bank_with_partial_stack() { | ||||
|     let mut almost_full_bank: Vec<item::BankItemEntity> = Vec::new(); | ||||
|     for _ in 0..199usize { | ||||
|         almost_full_bank.push(entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Vulcan) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Vulcan) | ||||
|                 .as_new() | ||||
|         ).await.unwrap().into()); | ||||
|     } | ||||
| @ -758,7 +759,7 @@ async fn test_withdraw_individual_item() { | ||||
| 
 | ||||
|     let mut bank = Vec::new(); | ||||
|     bank.push(entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Saber) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Saber) | ||||
|                 .as_new() | ||||
|     ).await.unwrap()); | ||||
| 
 | ||||
| @ -1049,14 +1050,14 @@ async fn test_withdraw_individual_item_in_full_inventory() { | ||||
| 
 | ||||
|     let mut bank = Vec::new(); | ||||
|     bank.push(entity_gateway.create_item( | ||||
|         ItemBuilder::weapon(item::weapon::WeaponType::Vulcan) | ||||
|         ItemBuilder::weapon(weapon::WeaponType::Vulcan) | ||||
|                 .as_new() | ||||
|     ).await.unwrap()); | ||||
| 
 | ||||
|     let mut inventory = Vec::new(); | ||||
|     for _ in 0..30usize { | ||||
|         inventory.push(entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Vulcan) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Vulcan) | ||||
|                 .as_new() | ||||
|         ).await.unwrap()); | ||||
|     } | ||||
| @ -1110,7 +1111,7 @@ async fn test_withdraw_stacked_item_in_full_inventory() { | ||||
|     let mut inventory = Vec::new(); | ||||
|     for _ in 0..30usize { | ||||
|         inventory.push(entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Vulcan) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Vulcan) | ||||
|                 .as_new() | ||||
|         ).await.unwrap()); | ||||
|     } | ||||
| @ -1171,7 +1172,7 @@ async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() { | ||||
|     let mut items = Vec::new(); | ||||
|     for _i in 0..29usize { | ||||
|         items.push(entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Vulcan) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Vulcan) | ||||
|                 .as_new() | ||||
|         ).await.unwrap().into()); | ||||
|     } | ||||
| @ -1389,12 +1390,12 @@ async fn test_deposit_items_into_shared_banks() { | ||||
| 
 | ||||
|     let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await; | ||||
| 
 | ||||
|     let item0 = entity_gateway.create_item(ItemBuilder::weapon(item::weapon::WeaponType::Saber).as_new()).await.unwrap(); | ||||
|     let item1 = entity_gateway.create_item(ItemBuilder::weapon(item::weapon::WeaponType::Buster).as_new()).await.unwrap(); | ||||
|     let item2 = entity_gateway.create_item(ItemBuilder::weapon(item::weapon::WeaponType::Rifle).as_new()).await.unwrap(); | ||||
|     let item3 = entity_gateway.create_item(ItemBuilder::weapon(item::weapon::WeaponType::Handgun).as_new()).await.unwrap(); | ||||
|     let item4 = entity_gateway.create_item(ItemBuilder::weapon(item::weapon::WeaponType::Autogun).as_new()).await.unwrap(); | ||||
|     let item5 = entity_gateway.create_item(ItemBuilder::weapon(item::weapon::WeaponType::Calibur).as_new()).await.unwrap(); | ||||
|     let item0 = entity_gateway.create_item(ItemBuilder::weapon(weapon::WeaponType::Saber).as_new()).await.unwrap(); | ||||
|     let item1 = entity_gateway.create_item(ItemBuilder::weapon(weapon::WeaponType::Buster).as_new()).await.unwrap(); | ||||
|     let item2 = entity_gateway.create_item(ItemBuilder::weapon(weapon::WeaponType::Rifle).as_new()).await.unwrap(); | ||||
|     let item3 = entity_gateway.create_item(ItemBuilder::weapon(weapon::WeaponType::Handgun).as_new()).await.unwrap(); | ||||
|     let item4 = entity_gateway.create_item(ItemBuilder::weapon(weapon::WeaponType::Autogun).as_new()).await.unwrap(); | ||||
|     let item5 = entity_gateway.create_item(ItemBuilder::weapon(weapon::WeaponType::Calibur).as_new()).await.unwrap(); | ||||
| 
 | ||||
|     entity_gateway.set_character_inventory(&char1.id, &item::InventoryEntity::new(vec![item0, item1, item2, item3, item4, item5])).await.unwrap(); | ||||
| 
 | ||||
| @ -1586,12 +1587,12 @@ async fn test_withdraw_items_from_shared_banks() { | ||||
| 
 | ||||
|     let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await; | ||||
| 
 | ||||
|     let item0 = entity_gateway.create_item(ItemBuilder::weapon(item::weapon::WeaponType::Saber).as_new()).await.unwrap(); | ||||
|     let item1 = entity_gateway.create_item(ItemBuilder::weapon(item::weapon::WeaponType::Buster).as_new()).await.unwrap(); | ||||
|     let item2 = entity_gateway.create_item(ItemBuilder::weapon(item::weapon::WeaponType::Rifle).as_new()).await.unwrap(); | ||||
|     let item3 = entity_gateway.create_item(ItemBuilder::weapon(item::weapon::WeaponType::Handgun).as_new()).await.unwrap(); | ||||
|     let item4 = entity_gateway.create_item(ItemBuilder::weapon(item::weapon::WeaponType::Autogun).as_new()).await.unwrap(); | ||||
|     let item5 = entity_gateway.create_item(ItemBuilder::weapon(item::weapon::WeaponType::Calibur).as_new()).await.unwrap(); | ||||
|     let item0 = entity_gateway.create_item(ItemBuilder::weapon(weapon::WeaponType::Saber).as_new()).await.unwrap(); | ||||
|     let item1 = entity_gateway.create_item(ItemBuilder::weapon(weapon::WeaponType::Buster).as_new()).await.unwrap(); | ||||
|     let item2 = entity_gateway.create_item(ItemBuilder::weapon(weapon::WeaponType::Rifle).as_new()).await.unwrap(); | ||||
|     let item3 = entity_gateway.create_item(ItemBuilder::weapon(weapon::WeaponType::Handgun).as_new()).await.unwrap(); | ||||
|     let item4 = entity_gateway.create_item(ItemBuilder::weapon(weapon::WeaponType::Autogun).as_new()).await.unwrap(); | ||||
|     let item5 = entity_gateway.create_item(ItemBuilder::weapon(weapon::WeaponType::Calibur).as_new()).await.unwrap(); | ||||
| 
 | ||||
|     entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![item0, item1]), &item::BankIdentifier::Character).await.unwrap(); | ||||
|     entity_gateway.set_character_bank(&char1.id, &item::BankEntity::new(vec![item2, item3]), &item::BankIdentifier::Shared(item::BankName("asdf".into()))).await.unwrap(); | ||||
|  | ||||
| @ -8,7 +8,7 @@ use maps::maps::Maps; | ||||
| use maps::area::MapArea; | ||||
| use maps::variant::{MapVariant, MapVariantMode}; | ||||
| use maps::enemy::MapEnemy; | ||||
| use entity::item::weapon::WeaponType; | ||||
| use libpso::item::weapon::WeaponType; | ||||
| 
 | ||||
| use libpso::packet::ship::*; | ||||
| use libpso::packet::messages::*; | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| use networking::serverstate::{ClientId, ServerState}; | ||||
| use entity::gateway::{EntityGateway, InMemoryGateway}; | ||||
| use entity::item; | ||||
| use libpso::item::weapon; | ||||
| use ship_server::RecvShipPacket; | ||||
| 
 | ||||
| use libpso::packet::ship::*; | ||||
| @ -134,7 +135,7 @@ async fn test_using_some_monomates_after_a_convoluted_series_of_leaves_and_joins | ||||
|         p3_items.push( | ||||
|             item::InventoryItemEntity::Individual( | ||||
|                 entity_gateway.create_item( | ||||
|                     ItemBuilder::weapon(item::weapon::WeaponType::Saber) | ||||
|                     ItemBuilder::weapon(weapon::WeaponType::Saber) | ||||
|                         .as_new() | ||||
|                 ).await.unwrap() | ||||
|             )); | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| use networking::serverstate::{ClientId, ServerState}; | ||||
| use entity::gateway::{EntityGateway, InMemoryGateway}; | ||||
| use entity::item; | ||||
| use libpso::item::weapon; | ||||
| use ship_server::RecvShipPacket; | ||||
| 
 | ||||
| use libpso::packet::ship::*; | ||||
| @ -19,7 +20,7 @@ async fn test_pick_up_individual_item() { | ||||
| 
 | ||||
|     let mut p1_inv = Vec::new(); | ||||
|     p1_inv.push(entity_gateway.create_item( | ||||
|         ItemBuilder::weapon(item::weapon::WeaponType::Handgun) | ||||
|         ItemBuilder::weapon(weapon::WeaponType::Handgun) | ||||
|             .as_new() | ||||
|     ).await.unwrap()); | ||||
| 
 | ||||
| @ -200,7 +201,7 @@ async fn test_pick_up_meseta_when_inventory_full() { | ||||
|     let mut p1_items = Vec::new(); | ||||
|     for _ in 0..30usize { | ||||
|         p1_items.push(entity_gateway.create_item( | ||||
|         ItemBuilder::weapon(item::weapon::WeaponType::Saber) | ||||
|         ItemBuilder::weapon(weapon::WeaponType::Saber) | ||||
|             .as_new() | ||||
|         ).await.unwrap()); | ||||
|     } | ||||
| @ -263,7 +264,7 @@ async fn test_pick_up_partial_stacked_item_when_inventory_is_otherwise_full() { | ||||
|     let mut p1_inv = Vec::new(); | ||||
|     for _slot in 0..29usize { | ||||
|         p1_inv.push(entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Saber) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Saber) | ||||
|                 .as_new() | ||||
|         ).await.unwrap().into()); | ||||
|     } | ||||
| @ -328,14 +329,14 @@ async fn test_can_not_pick_up_item_when_inventory_full() { | ||||
|     let mut p1_inv = Vec::new(); | ||||
|     for _slot in 0..30usize { | ||||
|         p1_inv.push(entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Saber) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Saber) | ||||
|                 .as_new() | ||||
|         ).await.unwrap()); | ||||
|     } | ||||
| 
 | ||||
|     let mut p2_inv = Vec::new(); | ||||
|     p2_inv.push(entity_gateway.create_item( | ||||
|         ItemBuilder::weapon(item::weapon::WeaponType::Handgun) | ||||
|         ItemBuilder::weapon(weapon::WeaponType::Handgun) | ||||
|             .as_new() | ||||
|     ).await.unwrap()); | ||||
| 
 | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| use networking::serverstate::{ClientId, ServerState}; | ||||
| use entity::gateway::{EntityGateway, InMemoryGateway}; | ||||
| use entity::item; | ||||
| use libpso::item::weapon; | ||||
| use ship_server::RecvShipPacket; | ||||
| use entity::character::TechLevel; | ||||
| 
 | ||||
| @ -326,7 +327,7 @@ async fn test_use_monogrinder() { | ||||
|     let (_user1, char1) = new_user_character(&mut entity_gateway, "a1", "a").await; | ||||
| 
 | ||||
|     let saber = entity_gateway.create_item( | ||||
|         ItemBuilder::weapon(item::weapon::WeaponType::Saber) | ||||
|         ItemBuilder::weapon(weapon::WeaponType::Saber) | ||||
|             .as_new() | ||||
|     ).await.unwrap(); | ||||
| 
 | ||||
| @ -369,7 +370,7 @@ async fn test_use_monogrinder() { | ||||
|     let inventory_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); | ||||
|     assert_eq!(inventory_items.items.len(), 2); | ||||
| 
 | ||||
|     assert!(matches!(inventory_items.items[0], item::InventoryItemEntity::Individual(item::ItemEntity{ item: item::ItemDetail::Weapon(item::weapon::Weapon {grind: 2, ..}), ..}))); | ||||
|     assert!(matches!(inventory_items.items[0], item::InventoryItemEntity::Individual(item::ItemEntity{ item: item::ItemDetail::Weapon(weapon::Weapon {grind: 2, ..}), ..}))); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
| @ -2,7 +2,7 @@ use networking::serverstate::{ClientId, ServerState}; | ||||
| use entity::gateway::{EntityGateway, InMemoryGateway}; | ||||
| use entity::item; | ||||
| use ship_server::RecvShipPacket; | ||||
| use entity::character::{CharacterClass, SectionID}; | ||||
| use libpso::character::{CharacterClass, SectionID}; | ||||
| 
 | ||||
| use libpso::packet::ship::*; | ||||
| use libpso::packet::messages::*; | ||||
|  | ||||
| @ -1,6 +1,7 @@ | ||||
| use networking::serverstate::{ClientId, ServerState}; | ||||
| use entity::gateway::{EntityGateway, InMemoryGateway}; | ||||
| use entity::item; | ||||
| use libpso::item::weapon; | ||||
| use ship_server::{RecvShipPacket, SendShipPacket}; | ||||
| use maps::room::Difficulty; | ||||
| use items::state::ItemStateError; | ||||
| @ -265,12 +266,12 @@ async fn test_player_sells_3_attr_weapon_to_shop() { | ||||
|     let mut p1_inv = Vec::new(); | ||||
| 
 | ||||
|     p1_inv.push(entity_gateway.create_item( | ||||
|         ItemBuilder::weapon(item::weapon::WeaponType::Vulcan) | ||||
|         ItemBuilder::weapon(weapon::WeaponType::Vulcan) | ||||
|             .grind(5) | ||||
|             .special(item::weapon::WeaponSpecial::Charge) | ||||
|             .attr(item::weapon::Attribute::Hit, 100) | ||||
|             .attr(item::weapon::Attribute::Dark, 100) | ||||
|             .attr(item::weapon::Attribute::Native, 100) | ||||
|             .special(weapon::WeaponSpecial::Charge) | ||||
|             .attr(weapon::Attribute::Hit, 100) | ||||
|             .attr(weapon::Attribute::Dark, 100) | ||||
|             .attr(weapon::Attribute::Native, 100) | ||||
|             .as_new() | ||||
|     ).await.unwrap()); | ||||
| 
 | ||||
| @ -613,13 +614,13 @@ async fn test_player_sells_untekked_weapon() { | ||||
|     let mut p1_inv = Vec::new(); | ||||
| 
 | ||||
|     p1_inv.push(entity_gateway.create_item( | ||||
|         ItemBuilder::weapon(item::weapon::WeaponType::Vulcan) | ||||
|         ItemBuilder::weapon(weapon::WeaponType::Vulcan) | ||||
|             .untekked() | ||||
|             .grind(5) | ||||
|             .special(item::weapon::WeaponSpecial::Charge) | ||||
|             .attr(item::weapon::Attribute::Hit, 100) | ||||
|             .attr(item::weapon::Attribute::Dark, 100) | ||||
|             .attr(item::weapon::Attribute::Native, 100) | ||||
|             .special(weapon::WeaponSpecial::Charge) | ||||
|             .attr(weapon::Attribute::Hit, 100) | ||||
|             .attr(weapon::Attribute::Dark, 100) | ||||
|             .attr(weapon::Attribute::Native, 100) | ||||
|             .as_new() | ||||
|     ).await.unwrap()); | ||||
| 
 | ||||
| @ -651,11 +652,11 @@ async fn test_player_sells_rare_item() { | ||||
|     let mut p1_inv = Vec::new(); | ||||
| 
 | ||||
|     p1_inv.push(entity_gateway.create_item( | ||||
|         ItemBuilder::weapon(item::weapon::WeaponType::DarkFlow) | ||||
|         ItemBuilder::weapon(weapon::WeaponType::DarkFlow) | ||||
|             .grind(5) | ||||
|             .attr(item::weapon::Attribute::Hit, 100) | ||||
|             .attr(item::weapon::Attribute::Dark, 100) | ||||
|             .attr(item::weapon::Attribute::Native, 100) | ||||
|             .attr(weapon::Attribute::Hit, 100) | ||||
|             .attr(weapon::Attribute::Dark, 100) | ||||
|             .attr(weapon::Attribute::Native, 100) | ||||
|             .as_new() | ||||
|     ).await.unwrap()); | ||||
| 
 | ||||
|  | ||||
| @ -2,6 +2,7 @@ use std::convert::TryInto; | ||||
| use networking::serverstate::{ClientId, ServerState}; | ||||
| use entity::gateway::{EntityGateway, InMemoryGateway}; | ||||
| use entity::item; | ||||
| use libpso::item::weapon; | ||||
| use ship_server::{ShipServerState, RecvShipPacket, SendShipPacket}; | ||||
| use entity::item::{Meseta, ItemEntity, InventoryItemEntity}; | ||||
| use ship_server::trade::TradeError; | ||||
| @ -118,7 +119,7 @@ async fn test_trade_one_individual_item() { | ||||
| 
 | ||||
|     let mut p1_inv = Vec::new(); | ||||
|     p1_inv.push(entity_gateway.create_item( | ||||
|         ItemBuilder::weapon(item::weapon::WeaponType::Handgun) | ||||
|         ItemBuilder::weapon(weapon::WeaponType::Handgun) | ||||
|             .as_new() | ||||
|     ).await.unwrap()); | ||||
| 
 | ||||
| @ -209,7 +210,7 @@ async fn test_trade_player2_to_player1() { | ||||
| 
 | ||||
|     let mut p2_inv = Vec::new(); | ||||
|     p2_inv.push(entity_gateway.create_item( | ||||
|         ItemBuilder::weapon(item::weapon::WeaponType::Handgun) | ||||
|         ItemBuilder::weapon(weapon::WeaponType::Handgun) | ||||
|             .as_new() | ||||
|     ).await.unwrap()); | ||||
| 
 | ||||
| @ -300,7 +301,7 @@ async fn test_reverse_trade_ack_order() { | ||||
| 
 | ||||
|     let mut p1_inv = Vec::new(); | ||||
|     p1_inv.push(entity_gateway.create_item( | ||||
|         ItemBuilder::weapon(item::weapon::WeaponType::Handgun) | ||||
|         ItemBuilder::weapon(weapon::WeaponType::Handgun) | ||||
|             .as_new() | ||||
|     ).await.unwrap()); | ||||
| 
 | ||||
| @ -590,12 +591,12 @@ async fn test_trade_individual_both() { | ||||
| 
 | ||||
|     let p1_inv = vec![ | ||||
|         entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Saber) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Saber) | ||||
|                 .as_new() | ||||
|         ).await.unwrap()]; | ||||
|     let p2_inv = vec![ | ||||
|         entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Handgun) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Handgun) | ||||
|                 .as_new() | ||||
|         ).await.unwrap()]; | ||||
| 
 | ||||
| @ -722,10 +723,10 @@ async fn test_trade_individual_both() { | ||||
| 
 | ||||
|     let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); | ||||
|     assert_eq!(p1_items.items.len(), 1); | ||||
|     assert!(matches!(p1_items.items[0].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(item::weapon::Weapon {weapon: item::weapon::WeaponType::Handgun, ..}), ..})); | ||||
|     assert!(matches!(p1_items.items[0].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(weapon::Weapon {weapon: weapon::WeaponType::Handgun, ..}), ..})); | ||||
|     let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); | ||||
|     assert_eq!(p2_items.items.len(), 1); | ||||
|     assert!(matches!(p2_items.items[0].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(item::weapon::Weapon {weapon: item::weapon::WeaponType::Saber, ..}), ..})); | ||||
|     assert!(matches!(p2_items.items[0].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(weapon::Weapon {weapon: weapon::WeaponType::Saber, ..}), ..})); | ||||
| } | ||||
| 
 | ||||
| #[async_std::test] | ||||
| @ -1355,7 +1356,7 @@ async fn test_trade_individual_for_stacked() { | ||||
| 
 | ||||
|     let p1_inv = vec![ | ||||
|         entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Saber) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Saber) | ||||
|                 .as_new() | ||||
|         ).await.unwrap()]; | ||||
| 
 | ||||
| @ -1499,7 +1500,7 @@ async fn test_trade_individual_for_stacked() { | ||||
|     assert!(matches!(p1_items.items[0].with_stacked(|i| i.clone()).unwrap()[0], item::ItemEntity{item: item::ItemDetail::Tool(item::tool::Tool {tool: item::tool::ToolType::Monomate, ..}), ..})); | ||||
|     let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); | ||||
|     assert_eq!(p2_items.items.len(), 1); | ||||
|     assert!(matches!(p2_items.items[0].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(item::weapon::Weapon {weapon: item::weapon::WeaponType::Saber, ..}), ..})); | ||||
|     assert!(matches!(p2_items.items[0].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(weapon::Weapon {weapon: weapon::WeaponType::Saber, ..}), ..})); | ||||
| } | ||||
| 
 | ||||
| #[async_std::test] | ||||
| @ -1511,21 +1512,21 @@ async fn test_trade_multiple_individual() { | ||||
| 
 | ||||
|     let p1_inv = vec![ | ||||
|         entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Saber) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Saber) | ||||
|                 .as_new() | ||||
|         ).await.unwrap(), | ||||
|         entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Buster) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Buster) | ||||
|             .as_new() | ||||
|         ).await.unwrap(), | ||||
|     ]; | ||||
|     let p2_inv = vec![ | ||||
|         entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Handgun) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Handgun) | ||||
|                 .as_new() | ||||
|         ).await.unwrap(), | ||||
|         entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Autogun) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Autogun) | ||||
|                 .as_new() | ||||
|         ).await.unwrap(), | ||||
|     ]; | ||||
| @ -1717,12 +1718,12 @@ async fn test_trade_multiple_individual() { | ||||
| 
 | ||||
|     let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); | ||||
|     assert_eq!(p1_items.items.len(), 2); | ||||
|     assert!(matches!(p1_items.items[0].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(item::weapon::Weapon {weapon: item::weapon::WeaponType::Handgun, ..}), ..})); | ||||
|     assert!(matches!(p1_items.items[1].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(item::weapon::Weapon {weapon: item::weapon::WeaponType::Autogun, ..}), ..})); | ||||
|     assert!(matches!(p1_items.items[0].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(weapon::Weapon {weapon: weapon::WeaponType::Handgun, ..}), ..})); | ||||
|     assert!(matches!(p1_items.items[1].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(weapon::Weapon {weapon: weapon::WeaponType::Autogun, ..}), ..})); | ||||
|     let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); | ||||
|     assert_eq!(p2_items.items.len(), 2); | ||||
|     assert!(matches!(p2_items.items[0].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(item::weapon::Weapon {weapon: item::weapon::WeaponType::Saber, ..}), ..})); | ||||
|     assert!(matches!(p2_items.items[1].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(item::weapon::Weapon {weapon: item::weapon::WeaponType::Buster, ..}), ..})); | ||||
|     assert!(matches!(p2_items.items[0].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(weapon::Weapon {weapon: weapon::WeaponType::Saber, ..}), ..})); | ||||
|     assert!(matches!(p2_items.items[1].with_individual(|i| i.clone()).unwrap(), item::ItemEntity{item: item::ItemDetail::Weapon(weapon::Weapon {weapon: weapon::WeaponType::Buster, ..}), ..})); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| @ -1986,7 +1987,7 @@ async fn test_trade_not_enough_inventory_space_individual() { | ||||
|         let mut entity_gateway = entity_gateway.clone(); | ||||
|         async move { | ||||
|             entity_gateway.create_item( | ||||
|                 ItemBuilder::weapon(item::weapon::WeaponType::Handgun) | ||||
|                 ItemBuilder::weapon(weapon::WeaponType::Handgun) | ||||
|                     .as_new() | ||||
|             ).await | ||||
|         }})) | ||||
| @ -1999,7 +2000,7 @@ async fn test_trade_not_enough_inventory_space_individual() { | ||||
|         let mut entity_gateway = entity_gateway.clone(); | ||||
|         async move { | ||||
|             entity_gateway.create_item( | ||||
|                 ItemBuilder::weapon(item::weapon::WeaponType::Handgun) | ||||
|                 ItemBuilder::weapon(weapon::WeaponType::Handgun) | ||||
|                     .as_new() | ||||
|             ).await | ||||
|         }})) | ||||
| @ -2102,7 +2103,7 @@ async fn test_trade_not_enough_inventory_space_stacked() { | ||||
|         let mut entity_gateway = entity_gateway.clone(); | ||||
|         async move { | ||||
|             entity_gateway.create_item( | ||||
|                 ItemBuilder::weapon(item::weapon::WeaponType::Handgun) | ||||
|                 ItemBuilder::weapon(weapon::WeaponType::Handgun) | ||||
|                     .as_new() | ||||
|             ).await | ||||
|         }})) | ||||
| @ -2553,7 +2554,7 @@ async fn test_back_out_of_trade_last_minute() { | ||||
| 
 | ||||
|     let mut p1_inv = Vec::new(); | ||||
|     p1_inv.push(entity_gateway.create_item( | ||||
|         ItemBuilder::weapon(item::weapon::WeaponType::Handgun) | ||||
|         ItemBuilder::weapon(weapon::WeaponType::Handgun) | ||||
|             .as_new() | ||||
|     ).await.unwrap()); | ||||
| 
 | ||||
| @ -2611,7 +2612,7 @@ async fn test_valid_trade_when_both_inventories_are_full() { | ||||
|         let mut entity_gateway = entity_gateway.clone(); | ||||
|         async move { | ||||
|             entity_gateway.create_item( | ||||
|                 ItemBuilder::weapon(item::weapon::WeaponType::Saber) | ||||
|                 ItemBuilder::weapon(weapon::WeaponType::Saber) | ||||
|                     .as_new() | ||||
|             ).await | ||||
|         }})) | ||||
| @ -2624,7 +2625,7 @@ async fn test_valid_trade_when_both_inventories_are_full() { | ||||
|         let mut entity_gateway = entity_gateway.clone(); | ||||
|         async move { | ||||
|             entity_gateway.create_item( | ||||
|                 ItemBuilder::weapon(item::weapon::WeaponType::Handgun) | ||||
|                 ItemBuilder::weapon(weapon::WeaponType::Handgun) | ||||
|                     .as_new() | ||||
|             ).await | ||||
|         }})) | ||||
| @ -2714,12 +2715,12 @@ async fn test_valid_trade_when_both_inventories_are_full() { | ||||
| 
 | ||||
|     let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); | ||||
|     assert_eq!(p1_items.items.len(), 30); | ||||
|     assert_eq!(p1_items.items.iter().filter(|i| matches!(i.individual().unwrap().item, item::ItemDetail::Weapon(item::weapon::Weapon { weapon: item::weapon::WeaponType::Saber, ..}, ..))).count(), 28); | ||||
|     assert_eq!(p1_items.items.iter().filter(|i| matches!(i.individual().unwrap().item, item::ItemDetail::Weapon(item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, ..}, ..))).count(), 2); | ||||
|     assert_eq!(p1_items.items.iter().filter(|i| matches!(i.individual().unwrap().item, item::ItemDetail::Weapon(weapon::Weapon { weapon: weapon::WeaponType::Saber, ..}, ..))).count(), 28); | ||||
|     assert_eq!(p1_items.items.iter().filter(|i| matches!(i.individual().unwrap().item, item::ItemDetail::Weapon(weapon::Weapon { weapon: weapon::WeaponType::Handgun, ..}, ..))).count(), 2); | ||||
|     let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); | ||||
|     assert_eq!(p2_items.items.len(), 30); | ||||
|     assert_eq!(p2_items.items.iter().filter(|i| matches!(i.individual().unwrap().item, item::ItemDetail::Weapon(item::weapon::Weapon { weapon: item::weapon::WeaponType::Saber, ..}, ..))).count(), 2); | ||||
|     assert_eq!(p2_items.items.iter().filter(|i| matches!(i.individual().unwrap().item, item::ItemDetail::Weapon(item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, ..}, ..))).count(), 28); | ||||
|     assert_eq!(p2_items.items.iter().filter(|i| matches!(i.individual().unwrap().item, item::ItemDetail::Weapon(weapon::Weapon { weapon: weapon::WeaponType::Saber, ..}, ..))).count(), 2); | ||||
|     assert_eq!(p2_items.items.iter().filter(|i| matches!(i.individual().unwrap().item, item::ItemDetail::Weapon(weapon::Weapon { weapon: weapon::WeaponType::Handgun, ..}, ..))).count(), 28); | ||||
| } | ||||
| 
 | ||||
| #[async_std::test] | ||||
| @ -2733,7 +2734,7 @@ async fn test_invalid_trade_when_both_inventories_are_full() { | ||||
|         let mut entity_gateway = entity_gateway.clone(); | ||||
|         async move { | ||||
|             entity_gateway.create_item( | ||||
|                 ItemBuilder::weapon(item::weapon::WeaponType::Saber) | ||||
|                 ItemBuilder::weapon(weapon::WeaponType::Saber) | ||||
|                     .as_new() | ||||
|             ).await | ||||
|         }})) | ||||
| @ -2746,7 +2747,7 @@ async fn test_invalid_trade_when_both_inventories_are_full() { | ||||
|         let mut entity_gateway = entity_gateway.clone(); | ||||
|         async move { | ||||
|             entity_gateway.create_item( | ||||
|                 ItemBuilder::weapon(item::weapon::WeaponType::Handgun) | ||||
|                 ItemBuilder::weapon(weapon::WeaponType::Handgun) | ||||
|                     .as_new() | ||||
|             ).await | ||||
|         }})) | ||||
| @ -2847,10 +2848,10 @@ async fn test_invalid_trade_when_both_inventories_are_full() { | ||||
| 
 | ||||
|     let p1_items = entity_gateway.get_character_inventory(&char1.id).await.unwrap(); | ||||
|     assert_eq!(p1_items.items.len(), 30); | ||||
|     assert_eq!(p1_items.items.iter().filter(|i| matches!(i.individual().unwrap().item, item::ItemDetail::Weapon(item::weapon::Weapon { weapon: item::weapon::WeaponType::Saber, ..}, ..))).count(), 30); | ||||
|     assert_eq!(p1_items.items.iter().filter(|i| matches!(i.individual().unwrap().item, item::ItemDetail::Weapon(weapon::Weapon { weapon: weapon::WeaponType::Saber, ..}, ..))).count(), 30); | ||||
|     let p2_items = entity_gateway.get_character_inventory(&char2.id).await.unwrap(); | ||||
|     assert_eq!(p2_items.items.len(), 30); | ||||
|     assert_eq!(p2_items.items.iter().filter(|i| matches!(i.individual().unwrap().item, item::ItemDetail::Weapon(item::weapon::Weapon { weapon: item::weapon::WeaponType::Handgun, ..}, ..))).count(), 30); | ||||
|     assert_eq!(p2_items.items.iter().filter(|i| matches!(i.individual().unwrap().item, item::ItemDetail::Weapon(weapon::Weapon { weapon: weapon::WeaponType::Handgun, ..}, ..))).count(), 30); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| @ -2932,7 +2933,7 @@ async fn test_add_then_remove_individual_item() { | ||||
|     let mut p1_inv = Vec::new(); | ||||
|     for _ in 0..2 { | ||||
|         p1_inv.push(entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Handgun) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Handgun) | ||||
|                 .as_new() | ||||
|         ).await.unwrap()); | ||||
|     } | ||||
| @ -3357,7 +3358,7 @@ async fn test_items_to_trade_data_does_not_match() { | ||||
| 
 | ||||
|     let mut p1_inv = Vec::new(); | ||||
|     p1_inv.push(entity_gateway.create_item( | ||||
|         ItemBuilder::weapon(item::weapon::WeaponType::Handgun) | ||||
|         ItemBuilder::weapon(weapon::WeaponType::Handgun) | ||||
|             .as_new() | ||||
|     ).await.unwrap()); | ||||
| 
 | ||||
| @ -3394,8 +3395,8 @@ async fn test_items_to_trade_data_does_not_match() { | ||||
|         ItemEntity { | ||||
|             id: p1_items.items[0].with_individual(|i| i.id).unwrap(), | ||||
|             item: item::ItemDetail::Weapon( | ||||
|                 item::weapon::Weapon { | ||||
|                     weapon: item::weapon::WeaponType::Handgun, | ||||
|                 weapon::Weapon { | ||||
|                     weapon: weapon::WeaponType::Handgun, | ||||
|                     grind: 2, | ||||
|                     special: None, | ||||
|                     attrs: [None, None, None], | ||||
| @ -3430,7 +3431,7 @@ async fn test_items_to_trade_id_does_not_match() { | ||||
| 
 | ||||
|     let mut p1_inv = Vec::new(); | ||||
|     p1_inv.push(entity_gateway.create_item( | ||||
|         ItemBuilder::weapon(item::weapon::WeaponType::Handgun) | ||||
|         ItemBuilder::weapon(weapon::WeaponType::Handgun) | ||||
|             .as_new() | ||||
|     ).await.unwrap()); | ||||
| 
 | ||||
| @ -3627,15 +3628,15 @@ async fn test_items_to_trade_count_less_than() { | ||||
| 
 | ||||
|     let p1_inv = vec![ | ||||
|         entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Saber) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Saber) | ||||
|                 .as_new() | ||||
|         ).await.unwrap(), | ||||
|         entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Brand) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Brand) | ||||
|                 .as_new() | ||||
|         ).await.unwrap(), | ||||
|         entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Buster) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Buster) | ||||
|                 .as_new() | ||||
|         ).await.unwrap(), | ||||
|     ]; | ||||
| @ -3705,15 +3706,15 @@ async fn test_items_to_trade_count_greater_than() { | ||||
| 
 | ||||
|     let p1_inv = vec![ | ||||
|         entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Saber) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Saber) | ||||
|                 .as_new() | ||||
|         ).await.unwrap(), | ||||
|         entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Brand) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Brand) | ||||
|                 .as_new() | ||||
|         ).await.unwrap(), | ||||
|         entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Buster) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Buster) | ||||
|                 .as_new() | ||||
|         ).await.unwrap(), | ||||
|     ]; | ||||
| @ -3787,15 +3788,15 @@ async fn test_items_to_trade_count_mismatch_with_meseta() { | ||||
| 
 | ||||
|     let p1_inv = vec![ | ||||
|         entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Saber) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Saber) | ||||
|                 .as_new() | ||||
|         ).await.unwrap(), | ||||
|         entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Brand) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Brand) | ||||
|                 .as_new() | ||||
|         ).await.unwrap(), | ||||
|         entity_gateway.create_item( | ||||
|             ItemBuilder::weapon(item::weapon::WeaponType::Buster) | ||||
|             ItemBuilder::weapon(weapon::WeaponType::Buster) | ||||
|                 .as_new() | ||||
|         ).await.unwrap(), | ||||
|     ]; | ||||
| @ -3863,7 +3864,7 @@ async fn test_dropping_item_after_trade() { | ||||
| 
 | ||||
|     let mut p1_inv = Vec::new(); | ||||
|     p1_inv.push(entity_gateway.create_item( | ||||
|         ItemBuilder::weapon(item::weapon::WeaponType::Handgun) | ||||
|         ItemBuilder::weapon(weapon::WeaponType::Handgun) | ||||
|             .as_new() | ||||
|     ).await.unwrap()); | ||||
| 
 | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user