From 58da1f87f6827bd52d54a66d5952769ae55a4c63 Mon Sep 17 00:00:00 2001 From: jake Date: Sun, 29 Jan 2023 15:31:49 -0700 Subject: [PATCH] inital drop test --- src/ship/drops/mod.rs | 76 ++++++++++++++++++- src/ship/drops/rare_drop_table.rs | 90 ++++++++++++++++++----- src/ship/packet/handler/direct_message.rs | 1 - tests/test_item_drop.rs | 76 +++++++++++++++++++ 4 files changed, 223 insertions(+), 20 deletions(-) create mode 100644 tests/test_item_drop.rs diff --git a/src/ship/drops/mod.rs b/src/ship/drops/mod.rs index e4f695d..9c20130 100644 --- a/src/ship/drops/mod.rs +++ b/src/ship/drops/mod.rs @@ -6,7 +6,7 @@ mod drop_table; -mod rare_drop_table; +pub mod rare_drop_table; mod generic_weapon; mod generic_armor; mod generic_shield; @@ -141,6 +141,20 @@ impl DropTable { } } + pub fn builder() -> DropTableBuilder { + DropTableBuilder { + monster_stats: None, + rare_table: None, + weapon_table: None, + armor_table: None, + shield_table: None, + unit_table: None, + tool_table: None, + box_table: None, + rng: None, + } + } + fn generate_meseta(&mut self, monster: &MonsterDropStats) -> Option { Some(ItemDropType::Meseta(self.rng.gen_range(monster.min_meseta, monster.max_meseta + 1))) } @@ -189,6 +203,66 @@ impl DropTable { } +pub struct DropTableBuilder { + monster_stats: Option>, + rare_table: Option, + weapon_table: Option, + armor_table: Option, + shield_table: Option, + unit_table: Option, + tool_table: Option, + box_table: Option, + rng: Option, +} + +// TODO: add the rest of these later I just need these ones right now +impl DropTableBuilder { + #[must_use] + pub fn monster_stats(mut self, monster_stats: HashMap) -> DropTableBuilder { + self.monster_stats = Some(monster_stats); + self + } + + #[must_use] + pub fn monster_stat(mut self, monster_type: MonsterType, drop_stats: MonsterDropStats) -> DropTableBuilder { + match &mut self.monster_stats { + Some(monster_stats) => { + monster_stats.insert(monster_type, drop_stats); + }, + None => { + let mut monster_stats = HashMap::default(); + monster_stats.insert(monster_type, drop_stats); + self.monster_stats = Some(monster_stats); + } + } + self + } + + #[must_use] + pub fn rare_table(mut self, rare_table: RareDropTable) -> DropTableBuilder { + self.rare_table = Some(rare_table); + self + } + + pub fn build(self, episode: Episode, difficulty: Difficulty, section_id: SectionID) -> DropTable { + DropTable { + monster_stats: self.monster_stats.unwrap_or_else(|| { + let monster_stats: HashMap = load_data_file(episode, difficulty, section_id, "monster_dar.toml"); + monster_stats.into_iter().map(|(m, s)| (m.parse().unwrap(), s)).collect() + }), + rare_table: self.rare_table.unwrap_or_else(|| RareDropTable::new(episode, difficulty, section_id)), + weapon_table: self.weapon_table.unwrap_or_else(|| GenericWeaponTable::new(episode, difficulty, section_id)), + armor_table: self.armor_table.unwrap_or_else(|| GenericArmorTable::new(episode, difficulty, section_id)), + shield_table: self.shield_table.unwrap_or_else(|| GenericShieldTable::new(episode, difficulty, section_id)), + unit_table: self.unit_table.unwrap_or_else(|| GenericUnitTable::new(episode, difficulty, section_id)), + tool_table: self.tool_table.unwrap_or_else(|| ToolTable::new(episode, difficulty, section_id)), + box_table: self.box_table.unwrap_or_else(|| BoxDropTable::new(episode, difficulty, section_id)), + rng: self.rng.unwrap_or_else(|| rand_chacha::ChaCha20Rng::from_entropy()), + } + } +} + + #[cfg(test)] mod test { use super::*; diff --git a/src/ship/drops/rare_drop_table.rs b/src/ship/drops/rare_drop_table.rs index 5d5f4c8..51151ba 100644 --- a/src/ship/drops/rare_drop_table.rs +++ b/src/ship/drops/rare_drop_table.rs @@ -50,9 +50,9 @@ impl RareDropItem { } -struct RareDropRate { - rate: f32, - item: RareDropItem +pub struct RareDropRate { + pub rate: f32, + pub item: RareDropItem } @@ -71,30 +71,41 @@ pub struct RareDropTable { shield_stats: GenericShieldTable, } -impl RareDropTable { - pub fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> RareDropTable { - let cfg: HashMap> = load_data_file(episode, difficulty, section_id, "rare_rate.toml"); - - let rates = cfg.into_iter() - .map(|(monster, drops)| { - let monster = monster.parse().unwrap(); - let drops = drops.into_iter().map(|drop| { - RareDropRate { - rate: drop.rate, - item: RareDropItem::from_string(drop.item), - } - }).collect(); - (monster, drops) +fn load_default_monster_rates(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> HashMap> { + let cfg: HashMap> = load_data_file(episode, difficulty, section_id, "rare_rate.toml"); + + cfg.into_iter() + .map(|(monster, drops)| { + let monster = monster.parse().unwrap(); + let drops = drops.into_iter().map(|drop| { + RareDropRate { + rate: drop.rate, + item: RareDropItem::from_string(drop.item), + } }).collect(); + (monster, drops) + }).collect() +} +impl RareDropTable { + pub fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> RareDropTable { RareDropTable { - rates, + rates: load_default_monster_rates(episode, difficulty, section_id), attribute_table: AttributeTable::new(episode, difficulty, section_id), armor_stats: GenericArmorTable::new(episode, difficulty, section_id), shield_stats: GenericShieldTable::new(episode, difficulty, section_id), } } + pub fn builder() -> RareDropTableBuilder { + RareDropTableBuilder { + rates: None, + attribute_table: None, + armor_stats: None, + shield_stats: None, + } + } + pub fn apply_item_stats(&self, map_area: &MapArea, item: RareDropItem, rng: &mut R) -> ItemDropType { match item { RareDropItem::Weapon(weapon) => { @@ -155,3 +166,46 @@ impl RareDropTable { }) } } + + +pub struct RareDropTableBuilder { + rates: Option>>, + attribute_table: Option, + armor_stats: Option, + shield_stats: Option, +} + + +// TODO: add the rest of these later I just need these ones right now +impl RareDropTableBuilder { + pub fn rates(mut self, rates: HashMap>) -> RareDropTableBuilder { + self.rates = Some(rates); + self + } + + #[must_use] + pub fn rate(mut self, monster_type: MonsterType, drop_rate: RareDropRate) -> RareDropTableBuilder { + match &mut self.rates { + Some(rates) => { + rates.entry(monster_type) + .or_insert(Vec::new()) + .push(drop_rate); + }, + None => { + let mut rates = HashMap::default(); + rates.insert(monster_type, vec![drop_rate]); + self.rates = Some(rates); + } + } + self + } + + pub fn build(self, episode: Episode, difficulty: Difficulty, section_id: SectionID) -> RareDropTable { + RareDropTable { + rates: self.rates.unwrap_or_else(|| load_default_monster_rates(episode, difficulty, section_id)), + attribute_table: self.attribute_table.unwrap_or_else(|| AttributeTable::new(episode, difficulty, section_id)), + armor_stats: self.armor_stats.unwrap_or_else(|| GenericArmorTable::new(episode, difficulty, section_id)), + shield_stats: self.shield_stats.unwrap_or_else(|| GenericShieldTable::new(episode, difficulty, section_id)), + } + } +} diff --git a/src/ship/packet/handler/direct_message.rs b/src/ship/packet/handler/direct_message.rs index 9b9455d..0f37837 100644 --- a/src/ship/packet/handler/direct_message.rs +++ b/src/ship/packet/handler/direct_message.rs @@ -209,7 +209,6 @@ where } let clients_in_area = client_location.get_clients_in_room(room_id).await?; - let client_and_drop = rooms.with_mut(room_id, |room| Box::pin(async move { clients_in_area.into_iter() .filter_map(move |area_client| { diff --git a/tests/test_item_drop.rs b/tests/test_item_drop.rs new file mode 100644 index 0000000..49cd75b --- /dev/null +++ b/tests/test_item_drop.rs @@ -0,0 +1,76 @@ +use elseware::common::serverstate::{ClientId, ServerState}; +use elseware::entity::gateway::{EntityGateway, InMemoryGateway}; +use elseware::entity::character::SectionID; +use elseware::common::leveltable::CharacterLevelTable; +use elseware::ship::ship::{ShipServerState, SendShipPacket, RecvShipPacket}; +use elseware::ship::room::{Episode, Difficulty}; +use elseware::ship::monster::MonsterType; +use elseware::ship::location::RoomId; +use elseware::ship::drops::{DropTable, MonsterDropStats, MonsterDropType}; +use elseware::ship::drops::rare_drop_table::{RareDropTable, RareDropRate, RareDropItem}; +use elseware::ship::map::{Maps, MapVariant, MapArea, MapVariantMode, MapEnemy}; +use elseware::entity::item::weapon::WeaponType; + +use libpso::packet::ship::*; +use libpso::packet::messages::*; + +#[path = "common.rs"] +mod common; +use common::*; + +#[async_std::test] +async fn test_enemy_drops_item() { + let mut entity_gateway = InMemoryGateway::default(); + let (_user1, _char1) = new_user_character(&mut entity_gateway, "a1", "a", 1).await; + + let mut ship = Box::new(ShipServerState::builder() + .gateway(entity_gateway.clone()) + .map_builder(Box::new(|_room_mode, _event| { + Maps::new( + vec![MapVariant::new(MapArea::Forest2, MapVariantMode::Online)], + vec![Some(MapEnemy::new(MonsterType::Hildebear, MapArea::Forest2))], + Vec::new(), + ) + })) + .drop_table_builder(Box::new(|episode, difficulty, section_id| { + DropTable::builder() + .monster_stat(MonsterType::Hildebear, MonsterDropStats { + dar: 100, + drop_type: MonsterDropType::Weapon, + min_meseta: 0, + max_meseta: 0, + }) + .rare_table(RareDropTable::builder() + .rate(MonsterType::Hildebear, RareDropRate { + rate: 1.0, + item: RareDropItem::Weapon(WeaponType::DarkFlow) + }) + .build(episode, difficulty, section_id)) + .build(episode, difficulty, section_id) + })) + .build()); + log_in_char(&mut ship, ClientId(1), "a1", "a").await; + join_lobby(&mut ship, ClientId(1)).await; + create_room(&mut ship, ClientId(1), "room", "").await; + + let pkt = ship.handle(ClientId(1), RecvShipPacket::DirectMessage(DirectMessage::new(0, GameMessage::RequestItem(RequestItem { + client: 0, + target: 0, + map_area: 2, + pt_index: 0, + enemy_id: 0, + x: 0.0, + z: 0.0, + y: 0.0, + })))).await.unwrap(); + + match &pkt[0].1 { + SendShipPacket::Message(Message{msg: GameMessage::ItemDrop(item_drop)}) => { + assert_eq!(item_drop.item_id, 0x810001); + assert_eq!(item_drop.item_bytes[1], 0x9D); + }, + _ => panic!(), + } + + +}