From fb6eaeffd20707ab729532719b079555ce52361d Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 1 Dec 2020 22:56:22 -0700 Subject: [PATCH 01/14] remove change_item --- src/entity/gateway/entitygateway.rs | 5 ----- src/entity/gateway/inmemory.rs | 9 --------- src/entity/gateway/postgres/postgres.rs | 8 -------- 3 files changed, 22 deletions(-) diff --git a/src/entity/gateway/entitygateway.rs b/src/entity/gateway/entitygateway.rs index 57d2b8c..7aeb2c3 100644 --- a/src/entity/gateway/entitygateway.rs +++ b/src/entity/gateway/entitygateway.rs @@ -65,11 +65,6 @@ pub trait EntityGateway: Send + Sync + Clone { unimplemented!(); } - // TODO: remove - async fn change_item(&mut self, _id: &ItemEntityId, _item: &ItemDetail) -> Result<(), GatewayError> { - unimplemented!(); - } - async fn change_item_location(&mut self, _item_id: &ItemEntityId, _item_location: ItemLocation) -> Result<(), GatewayError> { unimplemented!(); } diff --git a/src/entity/gateway/inmemory.rs b/src/entity/gateway/inmemory.rs index 04924ab..e3a1c36 100644 --- a/src/entity/gateway/inmemory.rs +++ b/src/entity/gateway/inmemory.rs @@ -223,15 +223,6 @@ impl EntityGateway for InMemoryGateway { Ok(new_item) } - async fn change_item(&mut self, id: &ItemEntityId, item: &ItemDetail) -> Result<(), GatewayError> { - let mut items = self.items.lock().unwrap(); - if let Some((_, ref mut old_item)) = items.iter_mut().find(|(existing_id, _)| **existing_id == *id) { - old_item.item = item.clone(); - } - - Ok(()) - } - async fn change_item_location(&mut self, item_id: &ItemEntityId, item_location: ItemLocation) -> Result<(), GatewayError> { self.items.lock().unwrap().get_mut(&item_id) .map(|item_entity| { diff --git a/src/entity/gateway/postgres/postgres.rs b/src/entity/gateway/postgres/postgres.rs index 00de8ba..b041790 100644 --- a/src/entity/gateway/postgres/postgres.rs +++ b/src/entity/gateway/postgres/postgres.rs @@ -331,14 +331,6 @@ impl EntityGateway for PostgresGateway { */ } - async fn change_item(&mut self, id: &ItemEntityId, item: &ItemDetail) -> Result<(), GatewayError> { - sqlx::query("update item set item = $1 where id = $2") - .bind(sqlx::types::Json(PgItemDetail::from(item.clone()))) - .bind(id.0) - .execute(&self.pool).await?; - Ok(()) - } - async fn change_item_location(&mut self, item_id: &ItemEntityId, item_location: ItemLocation) -> Result<(), GatewayError> { sqlx::query("insert into item_location (item, location) values ($1, $2)") .bind(item_id.0) From 92c34b2d28d1875244c71b0ec6d13eea2ee214ad Mon Sep 17 00:00:00 2001 From: jake Date: Tue, 1 Dec 2020 23:17:07 -0700 Subject: [PATCH 02/14] remove modifiers from weapon and armor --- src/bin/main.rs | 10 +--------- src/entity/gateway/postgres/models.rs | 2 -- src/entity/item/armor.rs | 2 -- src/entity/item/weapon.rs | 3 --- src/login/character.rs | 2 -- src/ship/drops/generic_armor.rs | 5 ----- src/ship/drops/generic_weapon.rs | 5 ----- src/ship/drops/rare_drop_table.rs | 2 -- src/ship/shops/armor.rs | 1 - src/ship/shops/weapon.rs | 1 - tests/test_bank.rs | 15 --------------- tests/test_item_actions.rs | 3 --- tests/test_item_pickup.rs | 4 ---- tests/test_rooms.rs | 2 -- 14 files changed, 1 insertion(+), 56 deletions(-) diff --git a/src/bin/main.rs b/src/bin/main.rs index 1e55016..f5b7823 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -84,7 +84,6 @@ fn main() { special: None, attrs: [None, None, None], tekked: true, - modifiers: Vec::new(), } ), location: item::ItemLocation::Bank { @@ -119,8 +118,7 @@ fn main() { attrs: [Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Hit, value: 40}), Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}), None,], - tekked: true, - modifiers: Vec::new(), + tekked: false, } ), location: ItemLocation::Inventory { @@ -138,7 +136,6 @@ fn main() { Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 30}), None,], tekked: true, - modifiers: Vec::new(), } ), location: ItemLocation::Inventory { @@ -156,7 +153,6 @@ fn main() { Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}), None,], tekked: true, - modifiers: Vec::new(), } ), location: ItemLocation::Inventory { @@ -174,7 +170,6 @@ fn main() { Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}), None,], tekked: true, - modifiers: Vec::new(), } ), location: ItemLocation::Inventory { @@ -192,7 +187,6 @@ fn main() { Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 100}), Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Native, value: 100}),], tekked: true, - modifiers: Vec::new(), } ), location: ItemLocation::Inventory { @@ -257,7 +251,6 @@ fn main() { Some(item::weapon::WeaponAttribute{attr: item::weapon::Attribute::Dark, value: 80}), None,], tekked: false, - modifiers: Vec::new(), } ), location: ItemLocation::Bank { @@ -273,7 +266,6 @@ fn main() { dfp: 0, evp: 0, slots: 4, - modifiers: Vec::new(), } ), location: ItemLocation::Inventory { diff --git a/src/entity/gateway/postgres/models.rs b/src/entity/gateway/postgres/models.rs index 4b47a77..0409955 100644 --- a/src/entity/gateway/postgres/models.rs +++ b/src/entity/gateway/postgres/models.rs @@ -316,7 +316,6 @@ impl Into for PgWeapon { grind: self.grind, attrs: attrs, tekked: self.tekked, - modifiers: Vec::new(), } } } @@ -347,7 +346,6 @@ impl Into for PgArmor { dfp: self.dfp, evp: self.evp, slots: self.slots, - modifiers: Vec::new(), } } } diff --git a/src/entity/item/armor.rs b/src/entity/item/armor.rs index a87c974..62f2c64 100644 --- a/src/entity/item/armor.rs +++ b/src/entity/item/armor.rs @@ -303,7 +303,6 @@ pub struct Armor { pub dfp: u8, pub evp: u8, pub slots: u8, - pub modifiers: Vec } impl Armor { @@ -324,7 +323,6 @@ impl Armor { dfp: data[6], evp: data[8], slots: data[5], - modifiers: Vec::new(), }) } else { diff --git a/src/entity/item/weapon.rs b/src/entity/item/weapon.rs index 3b70e4a..2217a34 100644 --- a/src/entity/item/weapon.rs +++ b/src/entity/item/weapon.rs @@ -1371,7 +1371,6 @@ pub struct Weapon { pub grind: u8, pub attrs: [Option; 3], pub tekked: bool, - pub modifiers: Vec } @@ -1383,7 +1382,6 @@ impl Weapon { grind: 0, attrs: [None; 3], tekked: true, - modifiers: Vec::new(), } } @@ -1452,7 +1450,6 @@ impl Weapon { a[2], ], tekked: t, - modifiers: Vec::new(), }) } else { diff --git a/src/login/character.rs b/src/login/character.rs index 7516514..82a9e28 100644 --- a/src/login/character.rs +++ b/src/login/character.rs @@ -220,7 +220,6 @@ async fn new_character(entity_gateway: &mut EG, user: &UserAc special: None, attrs: [None; 3], tekked: true, - modifiers: Vec::new(), }), location: ItemLocation::Inventory { character_id: character.id, @@ -234,7 +233,6 @@ async fn new_character(entity_gateway: &mut EG, user: &UserAc dfp: 0, evp: 0, slots: 0, - modifiers: Vec::new(), }), location: ItemLocation::Inventory { character_id: character.id, diff --git a/src/ship/drops/generic_armor.rs b/src/ship/drops/generic_armor.rs index 18efbab..2bf5895 100644 --- a/src/ship/drops/generic_armor.rs +++ b/src/ship/drops/generic_armor.rs @@ -107,7 +107,6 @@ impl GenericArmorTable { dfp: dfp_modifier as u8, evp: evp_modifier as u8, slots: slots as u8, - modifiers: Vec::new(), })) } } @@ -127,28 +126,24 @@ mod test { dfp: 0, evp: 0, slots: 1, - modifiers: Vec::new(), }))); assert!(gat.get_drop(&MapArea::Caves3, &mut rng) == Some(ItemDropType::Armor(Armor { armor: ArmorType::AbsorbArmor, dfp: 1, evp: 1, slots: 1, - modifiers: Vec::new(), }))); assert!(gat.get_drop(&MapArea::Forest2, &mut rng) == Some(ItemDropType::Armor(Armor { armor: ArmorType::HyperFrame, dfp: 0, evp: 0, slots: 0, - modifiers: Vec::new(), }))); assert!(gat.get_drop(&MapArea::DarkFalz, &mut rng) == Some(ItemDropType::Armor(Armor { armor: ArmorType::ImperialArmor, dfp: 2, evp: 1, slots: 0, - modifiers: Vec::new(), }))); } } diff --git a/src/ship/drops/generic_weapon.rs b/src/ship/drops/generic_weapon.rs index d00ad14..71e578c 100644 --- a/src/ship/drops/generic_weapon.rs +++ b/src/ship/drops/generic_weapon.rs @@ -503,7 +503,6 @@ impl GenericWeaponTable { grind: weapon_grind as u8, attrs: weapon_attributes, tekked: weapon_special.is_none(), - modifiers: Vec::new(), })) } } @@ -525,7 +524,6 @@ mod test { grind: 0, attrs: [None, None, None], tekked: true, - modifiers: Vec::new(), }))); let gwt = GenericWeaponTable::new(Episode::One, Difficulty::Hard, SectionID::Skyly); @@ -535,7 +533,6 @@ mod test { grind: 2, attrs: [None, None, None], tekked: true, - modifiers: Vec::new(), }))); let gwt = GenericWeaponTable::new(Episode::One, Difficulty::VeryHard, SectionID::Skyly); @@ -545,7 +542,6 @@ mod test { grind: 0, attrs: [None, None, None], tekked: false, - modifiers: Vec::new(), }))); let gwt = GenericWeaponTable::new(Episode::One, Difficulty::Ultimate, SectionID::Skyly); @@ -555,7 +551,6 @@ mod test { grind: 0, attrs: [Some(WeaponAttribute {attr: Attribute::ABeast, value: 30}), Some(WeaponAttribute {attr: Attribute::Dark, value: 30}), None], tekked: true, - modifiers: Vec::new(), }))); } } diff --git a/src/ship/drops/rare_drop_table.rs b/src/ship/drops/rare_drop_table.rs index d4bfe03..df9ec8d 100644 --- a/src/ship/drops/rare_drop_table.rs +++ b/src/ship/drops/rare_drop_table.rs @@ -104,7 +104,6 @@ impl RareDropTable { grind: 0, attrs: self.attribute_table.generate_rare_attributes(map_area, rng), tekked: false, - modifiers: Vec::new(), }) }, @@ -114,7 +113,6 @@ impl RareDropTable { dfp: self.armor_stats.dfp_modifier(&armor, rng) as u8, evp: self.armor_stats.evp_modifier(&armor, rng) as u8, slots: self.armor_stats.slots(map_area, rng) as u8, - modifiers: Vec::new(), }) }, RareDropItem::Shield(shield) => { diff --git a/src/ship/shops/armor.rs b/src/ship/shops/armor.rs index 4ba1291..2a6314f 100644 --- a/src/ship/shops/armor.rs +++ b/src/ship/shops/armor.rs @@ -71,7 +71,6 @@ impl ShopItem for ArmorShopItem { dfp: 0, evp: 0, slots: *slot as u8, - modifiers: Vec::new() }) }, ArmorShopItem::Barrier(barrier) => { diff --git a/src/ship/shops/weapon.rs b/src/ship/shops/weapon.rs index 90ba410..ea2642e 100644 --- a/src/ship/shops/weapon.rs +++ b/src/ship/shops/weapon.rs @@ -142,7 +142,6 @@ impl ShopItem for WeaponShopItem { grind: self.grind as u8, attrs: [self.attributes[0], self.attributes[1], None], tekked: true, - modifiers: Vec::new(), } ) } diff --git a/tests/test_bank.rs b/tests/test_bank.rs index 9300090..486e9d0 100644 --- a/tests/test_bank.rs +++ b/tests/test_bank.rs @@ -27,7 +27,6 @@ async fn test_bank_items_sent_in_character_login() { special: None, attrs: [None, None, None], tekked: true, - modifiers: Vec::new(), } ), location: item::ItemLocation::Bank { @@ -69,7 +68,6 @@ async fn test_request_bank_items() { special: None, attrs: [None, None, None], tekked: true, - modifiers: Vec::new(), } ), location: item::ItemLocation::Bank { @@ -167,7 +165,6 @@ async fn test_request_bank_items_sorted() { special: None, attrs: [None, None, None], tekked: true, - modifiers: Vec::new(), } ), location: item::ItemLocation::Bank { @@ -196,7 +193,6 @@ async fn test_request_bank_items_sorted() { special: None, attrs: [None, None, None], tekked: true, - modifiers: Vec::new(), } ), location: item::ItemLocation::Bank { @@ -247,7 +243,6 @@ async fn test_deposit_individual_item() { special: None, attrs: [None, None, None], tekked: true, - modifiers: Vec::new(), } ), location: item::ItemLocation::Inventory { @@ -263,7 +258,6 @@ async fn test_deposit_individual_item() { special: None, attrs: [None, None, None], tekked: true, - modifiers: Vec::new(), } ), location: item::ItemLocation::Inventory { @@ -623,7 +617,6 @@ async fn test_deposit_individual_item_in_full_bank() { special: None, attrs: [None, None, None], tekked: true, - modifiers: Vec::new(), } ), location: item::ItemLocation::Inventory { @@ -642,7 +635,6 @@ async fn test_deposit_individual_item_in_full_bank() { special: None, attrs: [None, None, None], tekked: true, - modifiers: Vec::new(), } ), location: item::ItemLocation::Bank { @@ -722,7 +714,6 @@ async fn test_deposit_stacked_item_in_full_bank() { special: None, attrs: [None, None, None], tekked: true, - modifiers: Vec::new(), } ), location: item::ItemLocation::Bank { @@ -819,7 +810,6 @@ async fn test_deposit_stacked_item_in_full_bank_with_partial_stack() { special: None, attrs: [None, None, None], tekked: true, - modifiers: Vec::new(), } ), location: item::ItemLocation::Bank { @@ -998,7 +988,6 @@ async fn test_withdraw_individual_item() { special: None, attrs: [None, None, None], tekked: true, - modifiers: Vec::new(), } ), location: item::ItemLocation::Bank { @@ -1358,7 +1347,6 @@ async fn test_withdraw_individual_item_in_full_inventory() { special: None, attrs: [None, None, None], tekked: true, - modifiers: Vec::new(), } ), location: item::ItemLocation::Bank { @@ -1378,7 +1366,6 @@ async fn test_withdraw_individual_item_in_full_inventory() { special: None, attrs: [None, None, None], tekked: true, - modifiers: Vec::new(), } ), location: item::ItemLocation::Inventory { @@ -1454,7 +1441,6 @@ async fn test_withdraw_stacked_item_in_full_inventory() { special: None, attrs: [None, None, None], tekked: true, - modifiers: Vec::new(), } ), location: item::ItemLocation::Inventory { @@ -1537,7 +1523,6 @@ async fn test_withdraw_stacked_item_in_full_inventory_with_partial_stack() { special: None, attrs: [None, None, None], tekked: true, - modifiers: Vec::new(), } ), location: item::ItemLocation::Inventory { diff --git a/tests/test_item_actions.rs b/tests/test_item_actions.rs index a51e305..1cbaf6d 100644 --- a/tests/test_item_actions.rs +++ b/tests/test_item_actions.rs @@ -25,7 +25,6 @@ async fn test_equip_unit_from_equip_menu() { dfp: 0, evp: 0, slots: 4, - modifiers: Vec::new(), }), location: item::ItemLocation::Inventory { character_id: char1.id, @@ -112,7 +111,6 @@ async fn test_unequip_armor_with_units() { dfp: 0, evp: 0, slots: 4, - modifiers: Vec::new(), }), location: item::ItemLocation::Inventory { character_id: char1.id, @@ -190,7 +188,6 @@ async fn test_sort_items() { dfp: 0, evp: 0, slots: 4, - modifiers: Vec::new(), }), location: item::ItemLocation::Inventory { character_id: char1.id, diff --git a/tests/test_item_pickup.rs b/tests/test_item_pickup.rs index 4c40b05..bfac7b9 100644 --- a/tests/test_item_pickup.rs +++ b/tests/test_item_pickup.rs @@ -172,7 +172,6 @@ async fn test_pick_up_meseta_when_inventory_full() { special: None, attrs: [None, None, None], tekked: true, - modifiers: Vec::new(), } ), location: item::ItemLocation::Inventory { @@ -252,7 +251,6 @@ async fn test_pick_up_partial_stacked_item_when_inventory_is_otherwise_full() { special: None, attrs: [None, None, None], tekked: true, - modifiers: Vec::new(), } ), location: item::ItemLocation::Inventory { @@ -345,7 +343,6 @@ async fn test_can_not_pick_up_item_when_inventory_full() { special: None, attrs: [None, None, None], tekked: true, - modifiers: Vec::new(), } ), location: item::ItemLocation::Inventory { @@ -364,7 +361,6 @@ async fn test_can_not_pick_up_item_when_inventory_full() { special: None, attrs: [None, None, None], tekked: true, - modifiers: Vec::new(), } ), location: item::ItemLocation::Inventory { diff --git a/tests/test_rooms.rs b/tests/test_rooms.rs index 0d652dc..a641cbd 100644 --- a/tests/test_rooms.rs +++ b/tests/test_rooms.rs @@ -29,7 +29,6 @@ async fn test_item_ids_reset_when_rejoining_rooms() { special: None, attrs: [None, None, None], tekked: true, - modifiers: Vec::new(), } ), location: item::ItemLocation::Inventory { @@ -49,7 +48,6 @@ async fn test_item_ids_reset_when_rejoining_rooms() { special: None, attrs: [None, None, None], tekked: true, - modifiers: Vec::new(), } ), location: item::ItemLocation::Inventory { From d9f52a73e4e5ff38d01ad9a975cb8d71843c5b65 Mon Sep 17 00:00:00 2001 From: jake Date: Wed, 2 Dec 2020 19:01:53 -0700 Subject: [PATCH 03/14] thanksandy --- src/entity/item/weapon.rs | 89 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/src/entity/item/weapon.rs b/src/entity/item/weapon.rs index 2217a34..b689d43 100644 --- a/src/entity/item/weapon.rs +++ b/src/entity/item/weapon.rs @@ -93,6 +93,95 @@ impl WeaponSpecial { pub fn value(&self) -> u8 { *self as u8 } + pub fn rank_up(&self) -> WeaponSpecial { + match self { + WeaponSpecial::Draw => WeaponSpecial::Drain, + WeaponSpecial::Drain => WeaponSpecial::Fill, + WeaponSpecial::Fill => WeaponSpecial::Gush, + WeaponSpecial::Gush => WeaponSpecial::Gush, + WeaponSpecial::Heart => WeaponSpecial::Mind, + WeaponSpecial::Mind => WeaponSpecial::Soul, + WeaponSpecial::Soul => WeaponSpecial::Geist, + WeaponSpecial::Geist => WeaponSpecial::Geist, + WeaponSpecial::Masters => WeaponSpecial::Lords, + WeaponSpecial::Lords => WeaponSpecial::Kings, + WeaponSpecial::Kings => WeaponSpecial::Kings, + WeaponSpecial::Charge => WeaponSpecial::Charge, + WeaponSpecial::Spirit => WeaponSpecial::Spirit, + WeaponSpecial::Berserk => WeaponSpecial::Berserk, + WeaponSpecial::Ice => WeaponSpecial::Frost, + WeaponSpecial::Frost => WeaponSpecial::Freeze, + WeaponSpecial::Freeze => WeaponSpecial::Blizzard, + WeaponSpecial::Blizzard => WeaponSpecial::Blizzard, + WeaponSpecial::Bind => WeaponSpecial::Hold, + WeaponSpecial::Hold => WeaponSpecial::Seize, + WeaponSpecial::Seize => WeaponSpecial::Arrest, + WeaponSpecial::Arrest => WeaponSpecial::Arrest, + WeaponSpecial::Heat => WeaponSpecial::Fire, + WeaponSpecial::Fire => WeaponSpecial::Flame, + WeaponSpecial::Flame => WeaponSpecial::Burning, + WeaponSpecial::Burning => WeaponSpecial::Burning, + WeaponSpecial::Shock => WeaponSpecial::Thunder, + WeaponSpecial::Thunder => WeaponSpecial::Storm, + WeaponSpecial::Storm => WeaponSpecial::Tempest, + WeaponSpecial::Tempest => WeaponSpecial::Tempest, + WeaponSpecial::Dim => WeaponSpecial::Shadow, + WeaponSpecial::Shadow => WeaponSpecial::Dark, + WeaponSpecial::Dark => WeaponSpecial::Hell, + WeaponSpecial::Hell => WeaponSpecial::Hell, + WeaponSpecial::Panic => WeaponSpecial::Riot, + WeaponSpecial::Riot => WeaponSpecial::Havoc, + WeaponSpecial::Havoc => WeaponSpecial::Chaos, + WeaponSpecial::Chaos => WeaponSpecial::Chaos, + WeaponSpecial::Devils => WeaponSpecial::Demons, + WeaponSpecial::Demons => WeaponSpecial::Demons, + } + } + + pub fn rank_down(&self) -> WeaponSpecial { + match self { + WeaponSpecial::Draw => WeaponSpecial::Draw, + WeaponSpecial::Drain => WeaponSpecial::Draw, + WeaponSpecial::Fill => WeaponSpecial::Drain, + WeaponSpecial::Gush => WeaponSpecial::Fill, + WeaponSpecial::Heart => WeaponSpecial::Heart, + WeaponSpecial::Mind => WeaponSpecial::Heart, + WeaponSpecial::Soul => WeaponSpecial::Mind, + WeaponSpecial::Geist => WeaponSpecial::Soul, + WeaponSpecial::Masters => WeaponSpecial::Masters, + WeaponSpecial::Lords => WeaponSpecial::Masters, + WeaponSpecial::Kings => WeaponSpecial::Lords, + WeaponSpecial::Charge => WeaponSpecial::Charge, + WeaponSpecial::Spirit => WeaponSpecial::Spirit, + WeaponSpecial::Berserk => WeaponSpecial::Berserk, + WeaponSpecial::Ice => WeaponSpecial::Ice, + WeaponSpecial::Frost => WeaponSpecial::Ice, + WeaponSpecial::Freeze => WeaponSpecial::Frost, + WeaponSpecial::Blizzard => WeaponSpecial::Freeze, + WeaponSpecial::Bind => WeaponSpecial::Bind, + WeaponSpecial::Hold => WeaponSpecial::Bind, + WeaponSpecial::Seize => WeaponSpecial::Hold, + WeaponSpecial::Arrest => WeaponSpecial::Seize, + WeaponSpecial::Heat => WeaponSpecial::Heat, + WeaponSpecial::Fire => WeaponSpecial::Heat, + WeaponSpecial::Flame => WeaponSpecial::Fire, + WeaponSpecial::Burning => WeaponSpecial::Flame, + WeaponSpecial::Shock => WeaponSpecial::Shock, + WeaponSpecial::Thunder => WeaponSpecial::Shock, + WeaponSpecial::Storm => WeaponSpecial::Thunder, + WeaponSpecial::Tempest => WeaponSpecial::Storm, + WeaponSpecial::Dim => WeaponSpecial::Dim, + WeaponSpecial::Shadow => WeaponSpecial::Dim, + WeaponSpecial::Dark => WeaponSpecial::Shadow, + WeaponSpecial::Hell => WeaponSpecial::Dark, + WeaponSpecial::Panic => WeaponSpecial::Panic, + WeaponSpecial::Riot => WeaponSpecial::Panic, + WeaponSpecial::Havoc => WeaponSpecial::Riot, + WeaponSpecial::Chaos => WeaponSpecial::Havoc, + WeaponSpecial::Devils => WeaponSpecial::Devils, + WeaponSpecial::Demons => WeaponSpecial::Devils, + } + } pub fn from(data: u8) -> Option { match data { From e4cdd2b95a4bc389845361f271de38d5f5b7855e Mon Sep 17 00:00:00 2001 From: jake Date: Wed, 2 Dec 2020 19:20:33 -0700 Subject: [PATCH 04/14] weapons can have tek modifiers --- src/entity/gateway/entitygateway.rs | 5 +++ src/entity/gateway/inmemory.rs | 20 +++++++++-- src/entity/gateway/postgres/models.rs | 6 ++++ src/entity/gateway/postgres/postgres.rs | 17 ++++++++++ src/entity/item/weapon.rs | 44 +++++++++++++++++++++++++ 5 files changed, 90 insertions(+), 2 deletions(-) diff --git a/src/entity/gateway/entitygateway.rs b/src/entity/gateway/entitygateway.rs index 7aeb2c3..261aadc 100644 --- a/src/entity/gateway/entitygateway.rs +++ b/src/entity/gateway/entitygateway.rs @@ -81,6 +81,11 @@ pub trait EntityGateway: Send + Sync + Clone { unimplemented!(); } + async fn add_weapon_modifier(&mut self, _item_id: &ItemEntityId, _modifier: weapon::WeaponModifier) -> Result<(), GatewayError> { + unimplemented!(); + } + + /* async fn get_items_by_character(&self, _char_id: &CharacterEntityId) -> Result, GatewayError> { unimplemented!(); diff --git a/src/entity/gateway/inmemory.rs b/src/entity/gateway/inmemory.rs index e3a1c36..cedeaf6 100644 --- a/src/entity/gateway/inmemory.rs +++ b/src/entity/gateway/inmemory.rs @@ -18,6 +18,7 @@ pub struct InMemoryGateway { banks: Arc>>, equips: Arc>>, mag_modifiers: Arc>>>, + weapon_modifiers: Arc>>>, } impl InMemoryGateway { @@ -31,6 +32,7 @@ impl InMemoryGateway { banks: Arc::new(Mutex::new(BTreeMap::new())), equips: Arc::new(Mutex::new(BTreeMap::new())), mag_modifiers: Arc::new(Mutex::new(BTreeMap::new())), + weapon_modifiers: Arc::new(Mutex::new(BTreeMap::new())), } } } @@ -42,9 +44,16 @@ impl InMemoryGateway { .map(|item| { item.map_individual(|mut item| { item.item = match item.item { + ItemDetail::Weapon(mut weapon) => { + if let Some(weapon_modifiers) = self.weapon_modifiers.lock().unwrap().get(&item.id) { + for weapon_modifier in weapon_modifiers.iter() { + weapon.apply_modifier(&weapon_modifier); + } + } + ItemDetail::Weapon(weapon) + }, ItemDetail::Mag(mag) => { let mut mag = mag::Mag::baby_mag(mag.color as u16); - println!("mag! {:?}", mag); if let Some(mag_modifiers) = self.mag_modifiers.lock().unwrap().get(&item.id) { for mag_modifier in mag_modifiers.iter() { match mag_modifier { @@ -69,7 +78,6 @@ impl InMemoryGateway { }, _ => {} } - println!("{:?} -> {:?}", mag_modifier, mag); } } ItemDetail::Mag(mag) @@ -257,6 +265,14 @@ impl EntityGateway for InMemoryGateway { Ok(()) } + async fn add_weapon_modifier(&mut self, item_id: &ItemEntityId, modifier: weapon::WeaponModifier) -> Result<(), GatewayError> { + self.weapon_modifiers.lock().unwrap() + .entry(*item_id) + .or_insert(Vec::new()) + .push(modifier); + Ok(()) + } + async fn get_character_inventory(&mut self, char_id: &CharacterEntityId) -> Result { println!("getting inv"); let inventories = self.inventories.lock().unwrap(); diff --git a/src/entity/gateway/postgres/models.rs b/src/entity/gateway/postgres/models.rs index 0409955..892fa24 100644 --- a/src/entity/gateway/postgres/models.rs +++ b/src/entity/gateway/postgres/models.rs @@ -320,6 +320,12 @@ impl Into for PgWeapon { } } +#[derive(Debug, sqlx::FromRow)] +pub struct PgWeaponModifier { + pub weapon: i32, + pub modifier: sqlx::types::Json, +} + #[derive(Debug, Serialize, Deserialize)] pub struct PgArmor { armor: armor::ArmorType, diff --git a/src/entity/gateway/postgres/postgres.rs b/src/entity/gateway/postgres/postgres.rs index b041790..1239f1d 100644 --- a/src/entity/gateway/postgres/postgres.rs +++ b/src/entity/gateway/postgres/postgres.rs @@ -48,6 +48,23 @@ impl PostgresGateway { let ItemEntity {id, item, location} = item; let item = match item { + ItemDetail::Weapon(mut weapon) => { + let q = r#"select weapon, modifier + from weapon_modifier + where weapon = $1 + order by created_at"#; + let weapon_modifiers = sqlx::query_as::<_, PgWeaponModifier>(q) + .bind(id.0 as i32) + .fetch(&self.pool); + + weapon_modifiers.for_each(|modifier| { + if let Ok(modifier) = modifier { + weapon.apply_modifier(&modifier.modifier); + } + }).await; + + ItemDetail::Weapon(weapon) + }, ItemDetail::Mag(mut mag) => { let q = r#"select mag, modifier, item.item -> 'Tool' as feed, item2.item -> 'Tool' as cell from mag_modifier diff --git a/src/entity/item/weapon.rs b/src/entity/item/weapon.rs index b689d43..2cdfcc4 100644 --- a/src/entity/item/weapon.rs +++ b/src/entity/item/weapon.rs @@ -1474,6 +1474,50 @@ impl Weapon { } } + pub fn apply_modifier(&mut self, modifier: &WeaponModifier) { + match modifier { + WeaponModifier::Tekked{special, percents} => { + match special { + TekSpecialModifier::Plus => { + self.special = self.special.map(|special| { + special.rank_up() + }); + }, + TekSpecialModifier::Minus => { + self.special = self.special.map(|special| { + special.rank_down() + }); + }, + TekSpecialModifier::Neutral => { + }, + } + for i in 0..3 { + self.attrs[i] = self.attrs[i].map(|mut attr| { + match percents { + TekPercentModifier::PlusPlus => { + attr.value += 10; + }, + TekPercentModifier::Plus => { + attr.value += 5; + }, + TekPercentModifier::MinusMinus => { + attr.value -= 10; + }, + TekPercentModifier::Minus => { + attr.value -= 5; + }, + TekPercentModifier::Neutral => { + } + } + attr + }); + } + self.tekked = true; + }, + _ => {} + } + } + pub fn as_bytes(&self) -> [u8; 16] { let mut result = [0u8; 16]; result[0..3].copy_from_slice(&self.weapon.value()); From 3b2a7ec73d8bfb9095715a3b61fd446f62bfe6d8 Mon Sep 17 00:00:00 2001 From: jake Date: Wed, 2 Dec 2020 19:25:07 -0700 Subject: [PATCH 05/14] tekking also affects grind --- src/entity/item/weapon.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/entity/item/weapon.rs b/src/entity/item/weapon.rs index 2cdfcc4..506ee72 100644 --- a/src/entity/item/weapon.rs +++ b/src/entity/item/weapon.rs @@ -1450,6 +1450,7 @@ pub enum WeaponModifier { Tekked { special: TekSpecialModifier, percents: TekPercentModifier, + grind: i32, }, } @@ -1476,7 +1477,7 @@ impl Weapon { pub fn apply_modifier(&mut self, modifier: &WeaponModifier) { match modifier { - WeaponModifier::Tekked{special, percents} => { + WeaponModifier::Tekked{special, percents, grind} => { match special { TekSpecialModifier::Plus => { self.special = self.special.map(|special| { @@ -1512,6 +1513,7 @@ impl Weapon { attr }); } + self.grind += grind; self.tekked = true; }, _ => {} From c437c3da5b5549f5465ca7dd61d0d1188e5d1e62 Mon Sep 17 00:00:00 2001 From: jake Date: Wed, 2 Dec 2020 22:51:36 -0700 Subject: [PATCH 06/14] split this into individual/individual_mut --- src/ship/items/inventory.rs | 9 ++++++++- src/ship/items/manager.rs | 2 +- src/ship/items/use_tool.rs | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/ship/items/inventory.rs b/src/ship/items/inventory.rs index 68be036..7c45b9e 100644 --- a/src/ship/items/inventory.rs +++ b/src/ship/items/inventory.rs @@ -198,7 +198,14 @@ impl InventoryItem { Ok(()) } - pub fn individual(&mut self) -> Option<&mut IndividualInventoryItem> { + pub fn individual(&self) -> Option<&IndividualInventoryItem> { + match self { + InventoryItem::Individual(ref individual_inventory_item) => Some(individual_inventory_item), + _ => None + } + } + + pub fn individual_mut(&mut self) -> Option<&mut IndividualInventoryItem> { match self { InventoryItem::Individual(ref mut individual_inventory_item) => Some(individual_inventory_item), _ => None diff --git a/src/ship/items/manager.rs b/src/ship/items/manager.rs index f3986f7..d8aac3d 100644 --- a/src/ship/items/manager.rs +++ b/src/ship/items/manager.rs @@ -611,7 +611,7 @@ impl ItemManager { let individual_item = mag_handle.item_mut() .ok_or(ItemManagerError::NoSuchItemId(mag_id))? - .individual() + .individual_mut() .ok_or(ItemManagerError::WrongItemType(mag_id))?; let mag = individual_item .mag_mut() diff --git a/src/ship/items/use_tool.rs b/src/ship/items/use_tool.rs index 671ca53..6440b84 100644 --- a/src/ship/items/use_tool.rs +++ b/src/ship/items/use_tool.rs @@ -52,7 +52,7 @@ async fn mag_cell(entity_gateway: &mut EG, used_cell: &Consum let mag_item = mag_handle.item_mut() .ok_or(UseItemError::InvalidItem)?; let actual_mag = mag_item - .individual() + .individual_mut() .ok_or(UseItemError::InvalidItem)? .mag_mut() .ok_or(UseItemError::InvalidItem)?; From fb53ccd03abe498e087f91ae11cbeaa5e50a757c Mon Sep 17 00:00:00 2001 From: jake Date: Thu, 3 Dec 2020 14:57:00 -0700 Subject: [PATCH 07/14] percents -> percent --- src/entity/item/weapon.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/entity/item/weapon.rs b/src/entity/item/weapon.rs index 506ee72..ba7090d 100644 --- a/src/entity/item/weapon.rs +++ b/src/entity/item/weapon.rs @@ -1449,7 +1449,7 @@ pub enum WeaponModifier { }, Tekked { special: TekSpecialModifier, - percents: TekPercentModifier, + percent: TekPercentModifier, grind: i32, }, } @@ -1477,7 +1477,7 @@ impl Weapon { pub fn apply_modifier(&mut self, modifier: &WeaponModifier) { match modifier { - WeaponModifier::Tekked{special, percents, grind} => { + WeaponModifier::Tekked{special, percent, grind} => { match special { TekSpecialModifier::Plus => { self.special = self.special.map(|special| { @@ -1494,7 +1494,7 @@ impl Weapon { } for i in 0..3 { self.attrs[i] = self.attrs[i].map(|mut attr| { - match percents { + match percent { TekPercentModifier::PlusPlus => { attr.value += 10; }, From 6410042e482634bfe1bee9fd148efaf6dee80dcc Mon Sep 17 00:00:00 2001 From: jake Date: Thu, 3 Dec 2020 14:57:17 -0700 Subject: [PATCH 08/14] fix grind modification --- src/entity/item/weapon.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/entity/item/weapon.rs b/src/entity/item/weapon.rs index ba7090d..b26da45 100644 --- a/src/entity/item/weapon.rs +++ b/src/entity/item/weapon.rs @@ -1513,7 +1513,7 @@ impl Weapon { attr }); } - self.grind += grind; + self.grind = std::cmp::max(self.grind as i32 + grind, 0) as u8; self.tekked = true; }, _ => {} From 5bdac4f6de72788160c909099533b1f0960692ae Mon Sep 17 00:00:00 2001 From: jake Date: Thu, 3 Dec 2020 14:58:17 -0700 Subject: [PATCH 09/14] postgres add_weapon_modifier --- src/entity/gateway/postgres/postgres.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/entity/gateway/postgres/postgres.rs b/src/entity/gateway/postgres/postgres.rs index 1239f1d..45aee91 100644 --- a/src/entity/gateway/postgres/postgres.rs +++ b/src/entity/gateway/postgres/postgres.rs @@ -407,6 +407,15 @@ impl EntityGateway for PostgresGateway { .execute(&self.pool).await?; Ok(()) } + + async fn add_weapon_modifier(&mut self, item_id: &ItemEntityId, modifier: weapon::WeaponModifier) -> Result<(), GatewayError> { + sqlx::query("insert into weapon_modifier (weapon, modifier) values ($1, $2);") + .bind(item_id.0) + .bind(sqlx::types::Json(modifier)) + .execute(&self.pool).await?; + Ok(()) + } + /* async fn get_items_by_character(&self, char_id: &CharacterEntityId) -> Result, GatewayError> { let q = r#"select * from ( From 5345939b1b2d727e5a6a1b733f32de7c9cddadd3 Mon Sep 17 00:00:00 2001 From: jake Date: Thu, 3 Dec 2020 15:00:38 -0700 Subject: [PATCH 10/14] IndividualInventoryItem::weapon --- src/ship/items/inventory.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/ship/items/inventory.rs b/src/ship/items/inventory.rs index 7c45b9e..ebf3cc6 100644 --- a/src/ship/items/inventory.rs +++ b/src/ship/items/inventory.rs @@ -5,6 +5,7 @@ use crate::entity::character::CharacterEntityId; use crate::entity::item::{ItemEntityId, ItemDetail, ItemEntity, ItemType, ItemLocation, InventoryEntity, InventoryItemEntity, EquippedEntity}; use crate::entity::item::tool::Tool; use crate::entity::item::mag::Mag; +use crate::entity::item::weapon::Weapon; use crate::ship::items::{ClientItemId, BankItem, BankItemHandle}; use crate::ship::items::floor::{IndividualFloorItem, StackedFloorItem}; @@ -30,6 +31,13 @@ impl IndividualInventoryItem { } } + pub fn weapon(&self) -> Option<&Weapon> { + match self.item { + ItemDetail::Weapon(ref weapon) => Some(weapon), + _ => None + } + } + pub fn mag_mut(&mut self) -> Option<&mut Mag> { match self.item { ItemDetail::Mag(ref mut mag) => Some(mag), From d263bf4caa54ed59c2005868e428fcc09bbf9da3 Mon Sep 17 00:00:00 2001 From: jake Date: Thu, 3 Dec 2020 15:01:00 -0700 Subject: [PATCH 11/14] remove item from inventory by id --- src/ship/items/inventory.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/ship/items/inventory.rs b/src/ship/items/inventory.rs index ebf3cc6..35cef9f 100644 --- a/src/ship/items/inventory.rs +++ b/src/ship/items/inventory.rs @@ -655,6 +655,14 @@ impl CharacterInventory { self.items = sorted_items; } + pub fn remove_by_id(&mut self, id: ClientItemId) -> Option { + self.items.iter() + .position(|i| i.item_id() == id) + .map(|position| { + self.items.remove(position) + }) + } + pub fn equip(&mut self, id: &ClientItemId, equip_slot: u8) { for item in &self.items { if let InventoryItem::Individual(inventory_item) = item { From b6ce6296d4039d2393353b9995b96e36257cb0e9 Mon Sep 17 00:00:00 2001 From: jake Date: Thu, 3 Dec 2020 15:01:41 -0700 Subject: [PATCH 12/14] create_item takes generic item rather than floor item --- src/ship/packet/builder/message.rs | 5 +++-- src/ship/packet/handler/direct_message.rs | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/ship/packet/builder/message.rs b/src/ship/packet/builder/message.rs index e57f626..c95f312 100644 --- a/src/ship/packet/builder/message.rs +++ b/src/ship/packet/builder/message.rs @@ -1,5 +1,6 @@ use libpso::packet::messages::*; use libpso::packet::ship::*; +use crate::entity::item; use crate::common::leveltable::CharacterStats; use crate::ship::ship::{ShipError}; use crate::ship::items::{ClientItemId, InventoryItem, StackedFloorItem, FloorItem, CharacterBank}; @@ -26,13 +27,13 @@ pub fn item_drop(client: u8, target: u8, item_drop: &FloorItem) -> Result Result { +pub fn create_item(area_client: AreaClient, item_id: ClientItemId, item: &item::ItemDetail) -> Result { let bytes = item.as_client_bytes(); Ok(CreateItem { client: area_client.local_client.id(), target: 0, item_data: bytes[0..12].try_into()?, - item_id: item.item_id().0, + item_id: item_id.0, item_data2: bytes[12..16].try_into()?, unknown: 0, }) diff --git a/src/ship/packet/handler/direct_message.rs b/src/ship/packet/handler/direct_message.rs index 4e93be7..7337a73 100644 --- a/src/ship/packet/handler/direct_message.rs +++ b/src/ship/packet/handler/direct_message.rs @@ -127,8 +127,10 @@ where let (item, floor_type) = item_manager.get_floor_item_by_id(&client.character, ClientItemId(pickup_item.item_id))?; let remove_item = builder::message::remove_item_from_floor(area_client, &item)?; let create_item = match item { + FloorItem::Individual(individual_floor_item) => Some(builder::message::create_item(area_client, item.item_id(), &individual_floor_item.item)?), + FloorItem::Stacked(stacked_floor_item) => Some(builder::message::create_item(area_client, item.item_id(), &item::ItemDetail::Tool(stacked_floor_item.tool))?), FloorItem::Meseta(_) => None, - _ => Some(builder::message::create_item(area_client, &item)?), + //_ => Some(builder::message::create_item(area_client, &item)?), }; match item_manager.character_picks_up_item(entity_gateway, &mut client.character, ClientItemId(pickup_item.item_id)).await { From b45a0ba1d5c86ae19eea070d34fda62d8f8fa135 Mon Sep 17 00:00:00 2001 From: jake Date: Thu, 3 Dec 2020 15:03:58 -0700 Subject: [PATCH 13/14] clean up directmsg handler --- src/ship/ship.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/ship/ship.rs b/src/ship/ship.rs index 9bd9706..b61fbf5 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -490,41 +490,34 @@ impl ShipServerState { async fn direct_message(&mut self, id: ClientId, msg: &DirectMessage) -> Result + Send>, anyhow::Error> { let target = msg.flag; + let block = self.blocks.with_client(id, &self.clients)?; Ok(match &msg.msg { GameMessage::GuildcardSend(guildcard_send) => { - let block = self.blocks.with_client(id, &self.clients)?; handler::direct_message::guildcard_send(id, guildcard_send, target, &block.client_location, &self.clients) }, GameMessage::RequestItem(request_item) => { - let block = self.blocks.with_client(id, &self.clients)?; handler::direct_message::request_item(id, request_item, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_manager).await? }, GameMessage::PickupItem(pickup_item) => { - let block = self.blocks.with_client(id, &self.clients)?; handler::direct_message::pickup_item(id, pickup_item, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut self.item_manager).await? }, GameMessage::BoxDropRequest(box_drop_request) => { - let block = self.blocks.with_client(id, &self.clients)?; handler::direct_message::request_box_item(id, box_drop_request, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut block.rooms, &mut self.item_manager).await? }, GameMessage::BankRequest(_bank_request) => { handler::direct_message::send_bank_list(id, &self.clients, &mut self.item_manager).await? }, GameMessage::BankInteraction(bank_interaction) => { - let block = self.blocks.with_client(id, &self.clients)?; handler::direct_message::bank_interaction(id, bank_interaction, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await? }, GameMessage::ShopRequest(shop_request) => { - let block = self.blocks.with_client(id, &self.clients)?; handler::direct_message::shop_request(id, shop_request, &block.client_location, &mut self.clients, &block.rooms, &self.level_table, &mut self.shops).await? }, GameMessage::BuyItem(buy_item) => { - let block = self.blocks.with_client(id, &self.clients)?; handler::direct_message::buy_item(id, buy_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await? }, _ => { let cmsg = msg.clone(); - let block = self.blocks.with_client(id, &self.clients)?; Box::new(block.client_location.get_all_clients_by_client(id).unwrap().into_iter() .filter(move |client| client.local_client.id() == target as u8) .map(move |client| { From f4fae162f0d03761c04fb001e70268ccfc21d214 Mon Sep 17 00:00:00 2001 From: jake Date: Thu, 3 Dec 2020 15:04:48 -0700 Subject: [PATCH 14/14] tekking! --- src/ship/items/manager.rs | 35 ++++++++ src/ship/packet/builder/message.rs | 11 +++ src/ship/packet/handler/direct_message.rs | 105 +++++++++++++++++++++- src/ship/packet/handler/message.rs | 67 ++++++++------ src/ship/ship.rs | 12 ++- 5 files changed, 200 insertions(+), 30 deletions(-) diff --git a/src/ship/items/manager.rs b/src/ship/items/manager.rs index d8aac3d..ff554ea 100644 --- a/src/ship/items/manager.rs +++ b/src/ship/items/manager.rs @@ -7,6 +7,7 @@ use crate::entity::item::{ItemDetail, ItemLocation, BankName}; use crate::entity::item::{Meseta, NewItemEntity, ItemEntity, InventoryItemEntity, EquippedEntity, InventoryEntity, BankItemEntity, BankEntity}; use crate::entity::item::tool::{Tool, ToolType}; use crate::entity::item::unit; +use crate::entity::item::weapon; use crate::ship::map::MapArea; use crate::ship::ship::ItemDropLocation; use crate::ship::drops::{ItemDrop, ItemDropType}; @@ -888,4 +889,38 @@ impl ItemManager { entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?; Ok(()) } + + pub async fn replace_item_with_tekked(&mut self, + entity_gateway: &mut EG, + character: &CharacterEntity, + item_id: ClientItemId, + tek: weapon::WeaponModifier) + -> Result { + let inventory = self.character_inventory.get_mut(&character.id).ok_or(ItemManagerError::NoCharacter(character.id))?; + + let item = inventory.remove_by_id(item_id) + .ok_or(ItemManagerError::NoSuchItemId(item_id))?; + let individual = item + .individual() + .ok_or(ItemManagerError::WrongItemType(item_id))?; + + let entity_id = individual.entity_id; + let mut weapon = individual + .weapon() + .ok_or(ItemManagerError::WrongItemType(item_id))? + .clone(); + + weapon.apply_modifier(&tek); + entity_gateway.add_weapon_modifier(&entity_id, tek).await?; + + inventory.add_item(InventoryItem::Individual(IndividualInventoryItem { + entity_id: entity_id, + item_id: item_id, + item: ItemDetail::Weapon(weapon.clone()), + })); + + entity_gateway.set_character_inventory(&character.id, &inventory.as_inventory_entity(&character.id)).await?; + + Ok(weapon) + } } diff --git a/src/ship/packet/builder/message.rs b/src/ship/packet/builder/message.rs index c95f312..91d4149 100644 --- a/src/ship/packet/builder/message.rs +++ b/src/ship/packet/builder/message.rs @@ -163,3 +163,14 @@ pub fn shop_list(shop_type: u8, items: &Vec) -> ShopList { items: items, } } + +pub fn tek_preview(id: ClientItemId, weapon: &item::weapon::Weapon) -> Result { + let bytes = weapon.as_bytes(); + Ok(TekPreview { + client: 0x79, + target: 0, + item_bytes: bytes[0..12].try_into()?, + item_id: id.0, + item_bytes2: bytes[12..16].try_into()?, + }) +} diff --git a/src/ship/packet/handler/direct_message.rs b/src/ship/packet/handler/direct_message.rs index 7337a73..44a8fbf 100644 --- a/src/ship/packet/handler/direct_message.rs +++ b/src/ship/packet/handler/direct_message.rs @@ -1,4 +1,6 @@ use log::warn; +use rand::Rng; +use rand::seq::SliceRandom; use libpso::packet::ship::*; use libpso::packet::messages::*; use crate::common::leveltable::CharacterLevelTable; @@ -6,8 +8,9 @@ use crate::common::serverstate::ClientId; use crate::ship::ship::{SendShipPacket, ShipError, Clients, Rooms, ItemShops}; use crate::ship::location::{ClientLocation, ClientLocationError}; use crate::ship::drops::ItemDrop; -use crate::ship::items::{ItemManager, ClientItemId, TriggerCreateItem, FloorItem, FloorType}; +use crate::ship::items::{ItemManager, ItemManagerError, ClientItemId, TriggerCreateItem, FloorItem, FloorType}; use crate::entity::gateway::EntityGateway; +use crate::entity::item; use libpso::utf8_to_utf16_array; use crate::ship::packet::builder; use crate::ship::shops::{ShopItem, ToolShopItem, ArmorShopItem}; @@ -24,6 +27,13 @@ const BANK_MESETA_CAPACITY: u32 = 999999; //const BANK_ACTION_: u8 = 1; +#[derive(thiserror::Error, Debug)] +#[error("")] +pub enum MessageError { + InvalidTek(ClientItemId), + MismatchedTekIds(ClientItemId, ClientItemId), +} + fn send_to_client(id: ClientId, target: u8, msg: DirectMessage, client_location: &ClientLocation) -> Box + Send> { Box::new(client_location.get_all_clients_by_client(id).unwrap().into_iter() @@ -395,3 +405,96 @@ where }))) } + + +const TEK_SPECIAL_MODIFIER: [item::weapon::TekSpecialModifier; 3] = [item::weapon::TekSpecialModifier::Plus, + item::weapon::TekSpecialModifier::Neutral, + item::weapon::TekSpecialModifier::Minus]; +const TEK_PERCENT_MODIFIER: [item::weapon::TekPercentModifier; 5] = [item::weapon::TekPercentModifier::PlusPlus, + item::weapon::TekPercentModifier::Plus, + item::weapon::TekPercentModifier::Neutral, + item::weapon::TekPercentModifier::Minus, + item::weapon::TekPercentModifier::MinusMinus]; + +pub async fn request_tek_item(id: ClientId, + tek_request: &TekRequest, + entity_gateway: &mut EG, + clients: &mut Clients, + item_manager: &mut ItemManager) + -> Result + Send>, anyhow::Error> +where + EG: EntityGateway +{ + let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; + + let (grind_mod, special_mod, percent_mod) = { + let mut rng = rand::thread_rng(); + + let grind_mod = rng.gen_range(-4, 4); + let special_mod = TEK_SPECIAL_MODIFIER.choose(&mut rng).cloned().unwrap(); + let percent_mod = TEK_PERCENT_MODIFIER.choose(&mut rng).cloned().unwrap(); + (grind_mod, special_mod, percent_mod) + }; + + client.tek = Some((ClientItemId(tek_request.item_id), special_mod, percent_mod, grind_mod)); + + let inventory = item_manager.get_character_inventory(&client.character)?; + let item = inventory.get_item_by_id(ClientItemId(tek_request.item_id)) + .ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))?; + let mut weapon = item.individual() + .ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))? + .weapon() + .ok_or(ItemManagerError::WrongItemType(ClientItemId(tek_request.item_id)))? + .clone(); + + weapon.apply_modifier(&item::weapon::WeaponModifier::Tekked { + special: special_mod, + percent: percent_mod, + grind: grind_mod, + }); + + client.character.meseta -= 100; + entity_gateway.save_character(&client.character).await?; + + let preview_pkt = builder::message::tek_preview(ClientItemId(tek_request.item_id), &weapon)?; + + Ok(Box::new(vec![(id, SendShipPacket::Message(Message::new(GameMessage::TekPreview(preview_pkt))))].into_iter())) +} + +pub async fn accept_tek_item(id: ClientId, + tek_accept: &TekAccept, + entity_gateway: &mut EG, + client_location: &ClientLocation, + clients: &mut Clients, + item_manager: &mut ItemManager) + -> Result + Send>, anyhow::Error> +where + EG: EntityGateway +{ + let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; + let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?; + + if let Some((item_id, special_mod, percent_mod, grind_mod)) = client.tek { + if item_id.0 != tek_accept.item_id { + return Err(MessageError::MismatchedTekIds(item_id, ClientItemId(tek_accept.item_id)).into()); + } + + let modifier = item::weapon::WeaponModifier::Tekked { + special: special_mod, + percent: percent_mod, + grind: grind_mod, + }; + let weapon = item_manager.replace_item_with_tekked(entity_gateway, &client.character, item_id, modifier).await?; + + let create_item_pkt = builder::message::create_item(area_client, item_id, &item::ItemDetail::Weapon(weapon))?; + + let neighbors = client_location.get_client_neighbors(id).map_err(|err| -> ClientLocationError { err.into() })?; + Ok(Box::new(neighbors.into_iter() + .map(move |c| { + (c.client, SendShipPacket::Message(Message::new(GameMessage::CreateItem(create_item_pkt.clone())))) + }))) + } + else { + Err(MessageError::InvalidTek(ClientItemId(tek_accept.item_id)).into()) + } +} diff --git a/src/ship/packet/handler/message.rs b/src/ship/packet/handler/message.rs index 39ab2f4..0ce969a 100644 --- a/src/ship/packet/handler/message.rs +++ b/src/ship/packet/handler/message.rs @@ -114,48 +114,59 @@ pub fn drop_coordinates(id: ClientId, Ok(Box::new(None.into_iter())) } -pub async fn split_item_stack(id: ClientId, - no_longer_has_item: &PlayerNoLongerHasItem, - entity_gateway: &mut EG, - client_location: &ClientLocation, - clients: &mut Clients, - item_manager: &mut ItemManager) - -> Result + Send>, anyhow::Error> +pub async fn no_longer_has_item(id: ClientId, + no_longer_has_item: &PlayerNoLongerHasItem, + entity_gateway: &mut EG, + client_location: &ClientLocation, + clients: &mut Clients, + item_manager: &mut ItemManager) + -> Result + Send>, anyhow::Error> where EG: EntityGateway { let client = clients.get_mut(&id).ok_or(ShipError::ClientNotFound(id))?; let area_client = client_location.get_local_client(id).map_err(|err| -> ClientLocationError { err.into() })?; let room_id = client_location.get_room(id).map_err(|err| -> ClientLocationError { err.into() })?; - let drop_location = client.item_drop_location.ok_or(ShipError::ItemDropLocationNotSet)?; + if let Some(drop_location) = client.item_drop_location { + if drop_location.item_id.0 != no_longer_has_item.item_id { + return Err(ShipError::DropInvalidItemId(no_longer_has_item.item_id).into()); + } - if drop_location.item_id.0 != no_longer_has_item.item_id { - return Err(ShipError::DropInvalidItemId(no_longer_has_item.item_id).into()); - } + if no_longer_has_item.item_id == 0xFFFFFFFF { + let dropped_meseta = item_manager.player_drops_meseta_on_shared_floor(entity_gateway, &mut client.character, drop_location, no_longer_has_item.amount as u32).await?; + + let dropped_meseta_pkt = builder::message::drop_split_meseta_stack(area_client, &dropped_meseta)?; + client.item_drop_location = None; - if no_longer_has_item.item_id == 0xFFFFFFFF { - let dropped_meseta = item_manager.player_drops_meseta_on_shared_floor(entity_gateway, &mut client.character, drop_location, no_longer_has_item.amount as u32).await?; + let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; + Ok(Box::new(clients_in_area.into_iter() + .map(move |c| { + (c.client, SendShipPacket::Message(Message::new(GameMessage::DropSplitStack(dropped_meseta_pkt.clone())))) + }))) + } + else { + let dropped_item = item_manager.player_drops_partial_stack_on_shared_floor(entity_gateway, &client.character, drop_location.item_id, drop_location, no_longer_has_item.amount as usize).await?; - let dropped_meseta_pkt = builder::message::drop_split_meseta_stack(area_client, &dropped_meseta)?; - client.item_drop_location = None; + let dropped_item_pkt = builder::message::drop_split_stack(area_client, dropped_item)?; + client.item_drop_location = None; - let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; - Ok(Box::new(clients_in_area.into_iter() + let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; + Ok(Box::new(clients_in_area.into_iter() + .map(move |c| { + (c.client, SendShipPacket::Message(Message::new(GameMessage::DropSplitStack(dropped_item_pkt.clone())))) + }))) + } + } + else if let Some(_tek) = client.tek { + let neighbors = client_location.get_client_neighbors(id).map_err(|err| -> ClientLocationError { err.into() })?; + let no_longer_has_item = no_longer_has_item.clone(); + Ok(Box::new(neighbors.into_iter() .map(move |c| { - (c.client, SendShipPacket::Message(Message::new(GameMessage::DropSplitStack(dropped_meseta_pkt.clone())))) + (c.client, SendShipPacket::Message(Message::new(GameMessage::PlayerNoLongerHasItem(no_longer_has_item.clone())))) }))) } else { - let dropped_item = item_manager.player_drops_partial_stack_on_shared_floor(entity_gateway, &client.character, drop_location.item_id, drop_location, no_longer_has_item.amount as usize).await?; - - let dropped_item_pkt = builder::message::drop_split_stack(area_client, dropped_item)?; - client.item_drop_location = None; - - let clients_in_area = client_location.get_clients_in_room(room_id).map_err(|err| -> ClientLocationError { err.into() })?; - Ok(Box::new(clients_in_area.into_iter() - .map(move |c| { - (c.client, SendShipPacket::Message(Message::new(GameMessage::DropSplitStack(dropped_item_pkt.clone())))) - }))) + Err(ShipError::InvalidItem(ClientItemId(no_longer_has_item.item_id)).into()) } } diff --git a/src/ship/ship.rs b/src/ship/ship.rs index b61fbf5..f8f08a6 100644 --- a/src/ship/ship.rs +++ b/src/ship/ship.rs @@ -23,6 +23,7 @@ use crate::login::character::SHIP_MENU_ID; use crate::entity::gateway::{EntityGateway, GatewayError}; use crate::entity::account::{UserAccountEntity, UserSettingsEntity}; use crate::entity::character::{CharacterEntity, SectionID}; +use crate::entity::item; use crate::ship::location::{ClientLocation, RoomLobby, MAX_ROOMS, ClientLocationError, GetNeighborError, GetClientsError, GetAreaError}; @@ -71,6 +72,7 @@ pub enum ShipError { UnknownMonster(crate::ship::monster::MonsterType), InvalidShip(usize), InvalidBlock(usize), + InvalidItem(items::ClientItemId), } #[derive(Debug)] @@ -254,6 +256,7 @@ pub struct ClientState { pub weapon_shop: Vec, pub tool_shop: Vec, pub armor_shop: Vec, + pub tek: Option<(items::ClientItemId, item::weapon::TekSpecialModifier, item::weapon::TekPercentModifier, i32)>, } impl ClientState { @@ -273,6 +276,7 @@ impl ClientState { weapon_shop: Vec::new(), tool_shop: Vec::new(), armor_shop: Vec::new(), + tek: None, } } } @@ -445,7 +449,7 @@ impl ShipServerState { }, GameMessage::PlayerNoLongerHasItem(no_longer_has_item) => { let block = self.blocks.with_client(id, &self.clients)?; - handler::message::split_item_stack(id, no_longer_has_item, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut self.item_manager).await? + handler::message::no_longer_has_item(id, no_longer_has_item, &mut self.entity_gateway, &mut block.client_location, &mut self.clients, &mut self.item_manager).await? }, GameMessage::PlayerChangedMap(_) | GameMessage::PlayerChangedMap2(_) | GameMessage::TellOtherPlayerMyLocation(_) | GameMessage::PlayerWarpingToFloor(_) | GameMessage::PlayerTeleported(_) | GameMessage::PlayerStopped(_) | @@ -516,6 +520,12 @@ impl ShipServerState { GameMessage::BuyItem(buy_item) => { handler::direct_message::buy_item(id, buy_item, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await? }, + GameMessage::TekRequest(tek_request) => { + handler::direct_message::request_tek_item(id, tek_request, &mut self.entity_gateway, &mut self.clients, &mut self.item_manager).await? + }, + GameMessage::TekAccept(tek_accept) => { + handler::direct_message::accept_tek_item(id, tek_accept, &mut self.entity_gateway, &block.client_location, &mut self.clients, &mut self.item_manager).await? + }, _ => { let cmsg = msg.clone(); Box::new(block.client_location.get_all_clients_by_client(id).unwrap().into_iter()