inital drop test
This commit is contained in:
parent
220d3e7185
commit
58da1f87f6
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
|
|
||||||
mod drop_table;
|
mod drop_table;
|
||||||
mod rare_drop_table;
|
pub mod rare_drop_table;
|
||||||
mod generic_weapon;
|
mod generic_weapon;
|
||||||
mod generic_armor;
|
mod generic_armor;
|
||||||
mod generic_shield;
|
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<ItemDropType> {
|
fn generate_meseta(&mut self, monster: &MonsterDropStats) -> Option<ItemDropType> {
|
||||||
Some(ItemDropType::Meseta(self.rng.gen_range(monster.min_meseta, monster.max_meseta + 1)))
|
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<HashMap<MonsterType, MonsterDropStats>>,
|
||||||
|
rare_table: Option<RareDropTable>,
|
||||||
|
weapon_table: Option<GenericWeaponTable>,
|
||||||
|
armor_table: Option<GenericArmorTable>,
|
||||||
|
shield_table: Option<GenericShieldTable>,
|
||||||
|
unit_table: Option<GenericUnitTable>,
|
||||||
|
tool_table: Option<ToolTable>,
|
||||||
|
box_table: Option<BoxDropTable>,
|
||||||
|
rng: Option<rand_chacha::ChaCha20Rng>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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<MonsterType, MonsterDropStats>) -> 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<String, MonsterDropStats> = 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)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -50,9 +50,9 @@ impl RareDropItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct RareDropRate {
|
pub struct RareDropRate {
|
||||||
rate: f32,
|
pub rate: f32,
|
||||||
item: RareDropItem
|
pub item: RareDropItem
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -71,30 +71,41 @@ pub struct RareDropTable {
|
|||||||
shield_stats: GenericShieldTable,
|
shield_stats: GenericShieldTable,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn load_default_monster_rates(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> HashMap<MonsterType, Vec<RareDropRate>> {
|
||||||
|
let cfg: HashMap<String, Vec<RareDropConfigEntity>> = 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 {
|
impl RareDropTable {
|
||||||
pub fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> RareDropTable {
|
pub fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> RareDropTable {
|
||||||
let cfg: HashMap<String, Vec<RareDropConfigEntity>> = 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)
|
|
||||||
}).collect();
|
|
||||||
|
|
||||||
RareDropTable {
|
RareDropTable {
|
||||||
rates,
|
rates: load_default_monster_rates(episode, difficulty, section_id),
|
||||||
attribute_table: AttributeTable::new(episode, difficulty, section_id),
|
attribute_table: AttributeTable::new(episode, difficulty, section_id),
|
||||||
armor_stats: GenericArmorTable::new(episode, difficulty, section_id),
|
armor_stats: GenericArmorTable::new(episode, difficulty, section_id),
|
||||||
shield_stats: GenericShieldTable::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<R: Rng>(&self, map_area: &MapArea, item: RareDropItem, rng: &mut R) -> ItemDropType {
|
pub fn apply_item_stats<R: Rng>(&self, map_area: &MapArea, item: RareDropItem, rng: &mut R) -> ItemDropType {
|
||||||
match item {
|
match item {
|
||||||
RareDropItem::Weapon(weapon) => {
|
RareDropItem::Weapon(weapon) => {
|
||||||
@ -155,3 +166,46 @@ impl RareDropTable {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub struct RareDropTableBuilder {
|
||||||
|
rates: Option<HashMap<MonsterType, Vec<RareDropRate>>>,
|
||||||
|
attribute_table: Option<AttributeTable>,
|
||||||
|
armor_stats: Option<GenericArmorTable>,
|
||||||
|
shield_stats: Option<GenericShieldTable>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: add the rest of these later I just need these ones right now
|
||||||
|
impl RareDropTableBuilder {
|
||||||
|
pub fn rates(mut self, rates: HashMap<MonsterType, Vec<RareDropRate>>) -> 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)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -209,7 +209,6 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
let clients_in_area = client_location.get_clients_in_room(room_id).await?;
|
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 {
|
let client_and_drop = rooms.with_mut(room_id, |room| Box::pin(async move {
|
||||||
clients_in_area.into_iter()
|
clients_in_area.into_iter()
|
||||||
.filter_map(move |area_client| {
|
.filter_map(move |area_client| {
|
||||||
|
76
tests/test_item_drop.rs
Normal file
76
tests/test_item_drop.rs
Normal file
@ -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!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user