From 0d42c0ede06b56f99558abd3def82187e050c3ac Mon Sep 17 00:00:00 2001 From: jake Date: Sat, 28 Mar 2020 12:06:24 -0700 Subject: [PATCH] add box drops --- src/ship/drops/box_drop_table.rs | 246 ++++++++++++++++++++++++++++++ src/ship/drops/mod.rs | 10 ++ src/ship/drops/rare_drop_table.rs | 27 +++- 3 files changed, 278 insertions(+), 5 deletions(-) create mode 100644 src/ship/drops/box_drop_table.rs diff --git a/src/ship/drops/box_drop_table.rs b/src/ship/drops/box_drop_table.rs new file mode 100644 index 0000000..ad8dc66 --- /dev/null +++ b/src/ship/drops/box_drop_table.rs @@ -0,0 +1,246 @@ +use std::collections::HashMap; +use rand::{Rng, SeedableRng}; +use rand::distributions::{WeightedIndex, Distribution}; +use serde::{Serialize, Deserialize}; +use crate::entity::item::ItemDetail; +use crate::entity::item::weapon::{Weapon, WeaponType}; +use crate::entity::item::armor::{Armor, ArmorType}; +use crate::entity::item::shield::{Shield, ShieldType}; +use crate::entity::item::unit::{Unit, UnitType}; +use crate::entity::item::tool::{Tool, ToolType}; +use crate::entity::character::SectionID; +use crate::ship::monster::MonsterType; +use crate::ship::room::{Difficulty, Episode}; +use crate::ship::map::MapVariantType; +use crate::ship::drops::load_data_file; +use crate::ship::map::{MapObject, MapObjectType, FixedBoxDropType}; +use crate::ship::drops::rare_drop_table::{RareDropTable, RareDropItem}; +use crate::ship::drops::generic_weapon::GenericWeaponTable; +use crate::ship::drops::generic_armor::GenericArmorTable; +use crate::ship::drops::generic_shield::GenericShieldTable; +use crate::ship::drops::generic_unit::GenericUnitTable; +use crate::ship::drops::tool_table::ToolTable; + + +#[derive(Debug, Serialize, Deserialize)] +struct BoxDropRate { + weapon_rate: u32, + armor_rate: u32, + shield_rate: u32, + unit_rate: u32, + tool_rate: u32, + meseta_rate: u32, + nothing_rate: u32, + min_meseta: u32, + max_meseta: u32, +} + +#[derive(Debug, Serialize, Deserialize)] +struct BoxDropRates { + area1: BoxDropRate, + area2: BoxDropRate, + area3: BoxDropRate, + area4: BoxDropRate, + area5: BoxDropRate, + area6: BoxDropRate, + area7: BoxDropRate, + area8: BoxDropRate, + area9: BoxDropRate, + area10: BoxDropRate, +} + +impl BoxDropRates { + fn rates_by_area(&self, map_area: &MapVariantType) -> &BoxDropRate { + match map_area.area_value().unwrap() { + 0 => &self.area1, + 1 => &self.area2, + 2 => &self.area3, + 3 => &self.area1, + 4 => &self.area1, + 5 => &self.area6, + 6 => &self.area7, + 7 => &self.area8, + 8 => &self.area9, + 9 => &self.area10, + _ => panic!() + } + } +} + +#[derive(Debug, Serialize, Deserialize)] +struct BoxRareRateRaw { + item: String, + rate: f32, +} + +#[derive(Debug, Serialize, Deserialize)] +struct BoxRareRatesRaw { + area1: Vec, + area2: Vec, + area3: Vec, + area4: Vec, + area5: Vec, + area6: Vec, + area7: Vec, + area8: Vec, + area9: Vec, + area10: Vec, +} + + +struct BoxRareRate { + item: RareDropItem, + rate: f32, +} + + +struct BoxRareRates { + area1: Vec, + area2: Vec, + area3: Vec, + area4: Vec, + area5: Vec, + area6: Vec, + area7: Vec, + area8: Vec, + area9: Vec, + area10: Vec, +} + +impl BoxRareRates { + fn new(raw: BoxRareRatesRaw) -> BoxRareRates { + BoxRareRates { + area1: raw.area1.into_iter().map(|i| { BoxRareRate {item: RareDropItem::from_string(i.item), rate: i.rate} }).collect(), + area2: raw.area2.into_iter().map(|i| { BoxRareRate {item: RareDropItem::from_string(i.item), rate: i.rate} }).collect(), + area3: raw.area3.into_iter().map(|i| { BoxRareRate {item: RareDropItem::from_string(i.item), rate: i.rate} }).collect(), + area4: raw.area4.into_iter().map(|i| { BoxRareRate {item: RareDropItem::from_string(i.item), rate: i.rate} }).collect(), + area5: raw.area5.into_iter().map(|i| { BoxRareRate {item: RareDropItem::from_string(i.item), rate: i.rate} }).collect(), + area6: raw.area6.into_iter().map(|i| { BoxRareRate {item: RareDropItem::from_string(i.item), rate: i.rate} }).collect(), + area7: raw.area7.into_iter().map(|i| { BoxRareRate {item: RareDropItem::from_string(i.item), rate: i.rate} }).collect(), + area8: raw.area8.into_iter().map(|i| { BoxRareRate {item: RareDropItem::from_string(i.item), rate: i.rate} }).collect(), + area9: raw.area9.into_iter().map(|i| { BoxRareRate {item: RareDropItem::from_string(i.item), rate: i.rate} }).collect(), + area10: raw.area10.into_iter().map(|i| { BoxRareRate {item: RareDropItem::from_string(i.item), rate: i.rate} }).collect(), + } + } + + fn rates_by_area(&self, map_area: &MapVariantType) -> &Vec { + match map_area.area_value().unwrap() { + 0 => &self.area1, + 1 => &self.area2, + 2 => &self.area3, + 3 => &self.area1, + 4 => &self.area1, + 5 => &self.area6, + 6 => &self.area7, + 7 => &self.area8, + 8 => &self.area9, + 9 => &self.area10, + _ => panic!() + } + } +} + + + + +pub struct BoxDropTable { + box_rates: BoxDropRates, + rare_rates: BoxRareRates, + rare_stats: RareDropTable, + weapon_table: GenericWeaponTable, + armor_table: GenericArmorTable, + shield_table: GenericShieldTable, + unit_table: GenericUnitTable, + tool_table: ToolTable, +} + + +impl BoxDropTable { + pub fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> BoxDropTable { + let rates = load_data_file(episode, difficulty, section_id, "box_rare_rate.toml"); + + BoxDropTable { + box_rates: load_data_file(episode, difficulty, section_id, "box_drop_rate.toml"), + rare_rates: BoxRareRates::new(rates), + rare_stats: RareDropTable::new(episode, difficulty, section_id), + weapon_table: GenericWeaponTable::new(episode, difficulty, section_id), + armor_table: GenericArmorTable::new(episode, difficulty, section_id), + shield_table: GenericShieldTable::new(episode, difficulty, section_id), + unit_table: GenericUnitTable::new(episode, difficulty, section_id), + tool_table: ToolTable::new(episode, difficulty, section_id), + } + + } + + fn rare_drop(&self, map_area: &MapVariantType, rng: &mut R) -> Option { + self.rare_rates.rates_by_area(map_area).iter() + .filter_map(|rate| { + let rand: f32 = rng.gen(); + if rand < rate.rate { + Some(self.rare_stats.apply_item_stats(map_area, rate.item, rng)) + } + else { + None + } + }).nth(0) + } + + fn random_box_drop(&self, map_area: &MapVariantType, rng: &mut R) -> Option { + self.rare_drop(map_area, rng).or_else(|| { + let rate = self.box_rates.rates_by_area(map_area); + let type_weights = WeightedIndex::new(&[rate.weapon_rate, rate.armor_rate, rate.shield_rate, rate.unit_rate, + rate.tool_rate, rate.meseta_rate, rate.nothing_rate]).unwrap(); + let btype = type_weights.sample(rng); + match btype { + 0 => self.weapon_table.get_drop(map_area, rng), + 1 => self.armor_table.get_drop(map_area, rng), + 2 => self.shield_table.get_drop(map_area, rng), + 3 => self.unit_table.get_drop(map_area, rng), + 4 => self.tool_table.get_drop(map_area, rng), + //5 => meseta drop + _ => None, + } + }) + } + + fn fixed_box_drop(&self, fixed_drop: FixedBoxDropType, map_area: &MapVariantType, rng: &mut R) -> Option { + match fixed_drop { + FixedBoxDropType::Weapon => self.weapon_table.get_drop(map_area, rng), + FixedBoxDropType::Armor => self.armor_table.get_drop(map_area, rng), // TODO: should this drop shield? + FixedBoxDropType::Tool => self.tool_table.get_drop(map_area, rng), + FixedBoxDropType::Meseta => panic!(), + FixedBoxDropType::Random => self.random_box_drop(map_area, rng), + FixedBoxDropType::Specific(value) => panic!(), + } + } + + pub fn get_drop(&self, map_area: &MapVariantType, object: &MapObject, rng: &mut R) -> Option { + match object.object { + MapObjectType::Box | MapObjectType::EnemyBox | MapObjectType::RuinsBox| MapObjectType::RuinsEnemyBox + | MapObjectType::CcaBox => { + self.random_box_drop(map_area, rng) + }, + MapObjectType::FixedBox(f) | MapObjectType::EnemyFixedBox(f) | MapObjectType::RuinsFixedBox(f) + | MapObjectType::RuinsEnemyFixedBox(f) | MapObjectType::CcaFixedBox(f) => { + self.fixed_box_drop(f, map_area, rng) + }, + _ => None, + } + } +} + + + +#[cfg(test)] +mod test { + use super::*; + + #[test] + fn test_box_drops() { + let mut rng = rand_chacha::ChaCha20Rng::from_seed([23;32]); + + let bdt = BoxDropTable::new(Episode::One, Difficulty::Ultimate, SectionID::Skyly); + + println!("{:?}", bdt.get_drop(&MapVariantType::Forest1, &MapObject {object: MapObjectType::Box}, &mut rng)); + } +} diff --git a/src/ship/drops/mod.rs b/src/ship/drops/mod.rs index f7be23e..1c69666 100644 --- a/src/ship/drops/mod.rs +++ b/src/ship/drops/mod.rs @@ -6,6 +6,7 @@ mod generic_shield; mod generic_unit; mod tool_table; mod tech_table; +mod box_drop_table; use std::collections::HashMap; use std::fs::File; @@ -25,6 +26,8 @@ use crate::ship::drops::generic_shield::GenericShieldTable; use crate::ship::drops::generic_unit::GenericUnitTable; use crate::ship::drops::tool_table::ToolTable; use crate::ship::drops::rare_drop_table::RareDropTable; +use crate::ship::drops::box_drop_table::BoxDropTable; +use crate::ship::map::MapObject; fn data_file_path(episode: Episode, difficulty: Difficulty, section_id: SectionID, filename: &str) -> PathBuf { @@ -68,6 +71,7 @@ pub struct MonsterDropStats { pub max_meseta: u32, } +// TODO: ItemDropType enum ItemDropItem { Weapon, } @@ -95,6 +99,7 @@ struct DropTable { shield_table: GenericShieldTable, unit_table: GenericUnitTable, tool_table: ToolTable, + box_table: BoxDropTable, rng: R, } @@ -119,6 +124,7 @@ impl DropTable { shield_table: GenericShieldTable::new(episode, difficulty, section_id), unit_table: GenericUnitTable::new(episode, difficulty, section_id), tool_table: ToolTable::new(episode, difficulty, section_id), + box_table: BoxDropTable::new(episode, difficulty, section_id), rng: R::from_entropy(), } } @@ -164,4 +170,8 @@ impl DropTable { _ => panic!() } } + + pub fn get_box_drop(&mut self, map_area: &MapVariantType, object: &MapObject) -> Option { + self.box_table.get_drop(map_area, object, &mut self.rng) + } } diff --git a/src/ship/drops/rare_drop_table.rs b/src/ship/drops/rare_drop_table.rs index e919e25..fc42d8d 100644 --- a/src/ship/drops/rare_drop_table.rs +++ b/src/ship/drops/rare_drop_table.rs @@ -7,6 +7,7 @@ use crate::entity::item::armor::{Armor, ArmorType}; use crate::entity::item::shield::{Shield, ShieldType}; use crate::entity::item::unit::{Unit, UnitType}; use crate::entity::item::tool::{Tool, ToolType}; +use crate::entity::item::mag::{Mag, MagType}; use crate::entity::character::SectionID; use crate::ship::monster::MonsterType; use crate::ship::room::{Difficulty, Episode}; @@ -18,22 +19,25 @@ use crate::ship::drops::generic_shield::GenericShieldTable; #[derive(Debug, Copy, Clone)] -enum RareDropItem { +pub enum RareDropItem { Weapon(WeaponType), Armor(ArmorType), Shield(ShieldType), Unit(UnitType), Tool(ToolType), + Mag(MagType) } impl RareDropItem { - fn from_string(name: String) -> RareDropItem { - let parse_funcs: [Box Option>; 5] = [ + pub fn from_string(name: String) -> RareDropItem { + let parse_funcs: [Box Option>; 6] = [ Box::new(|i| Some(RareDropItem::Weapon(str::parse::(&i).ok()?))), Box::new(|i| Some(RareDropItem::Armor(str::parse::(&i).ok()?))), Box::new(|i| Some(RareDropItem::Shield(str::parse::(&i).ok()?))), Box::new(|i| Some(RareDropItem::Unit(str::parse::(&i).ok()?))), - Box::new(|i| Some(RareDropItem::Tool(str::parse::(&i).ok()?)))]; + Box::new(|i| Some(RareDropItem::Tool(str::parse::(&i).ok()?))), + Box::new(|i| Some(RareDropItem::Mag(str::parse::(&i).ok()?))), + ]; for parse in parse_funcs.iter() { match parse(&name) { @@ -92,7 +96,7 @@ impl RareDropTable { } } - fn apply_item_stats(&self, map_area: &MapVariantType, item: RareDropItem, rng: &mut R) -> ItemDetail { + pub fn apply_item_stats(&self, map_area: &MapVariantType, item: RareDropItem, rng: &mut R) -> ItemDetail { match item { RareDropItem::Weapon(weapon) => { ItemDetail::Weapon(Weapon { @@ -133,6 +137,19 @@ impl RareDropTable { ItemDetail::Tool(Tool { tool: tool, }) + }, + RareDropItem::Mag(mag) => { + ItemDetail::Mag(Mag { + mag: mag, + def: 500, + pow: 0, + dex: 0, + mnd: 0, + iq: 0, + synchro: 20, + photon_blast: [None; 3], + equipped: false, + }) } } }