From cfa31736c6c62af9ea92de75ef93ecc2ad97ed73 Mon Sep 17 00:00:00 2001 From: jake Date: Thu, 26 Mar 2020 00:06:28 -0700 Subject: [PATCH] rare drops --- src/ship/drops/generic_armor.rs | 8 +- src/ship/drops/generic_shield.rs | 6 +- src/ship/drops/generic_weapon.rs | 43 ++++++--- src/ship/drops/mod.rs | 69 +++---------- src/ship/drops/rare_drop_table.rs | 155 ++++++++++++++++++++++++++++++ 5 files changed, 204 insertions(+), 77 deletions(-) diff --git a/src/ship/drops/generic_armor.rs b/src/ship/drops/generic_armor.rs index e3236d5..cebf4e7 100644 --- a/src/ship/drops/generic_armor.rs +++ b/src/ship/drops/generic_armor.rs @@ -80,20 +80,18 @@ impl GenericArmorTable { } } - fn slots(&self, area_map: &MapVariantType, rng: &mut R) -> usize { + pub fn slots(&self, area_map: &MapVariantType, rng: &mut R) -> usize { let slot_weights = WeightedIndex::new(&[self.slot_rates.slot0, self.slot_rates.slot1, self.slot_rates.slot2, self.slot_rates.slot3, self.slot_rates.slot4]).unwrap(); slot_weights.sample(rng) } - // TODO: this needs the pmt file - fn dfp_modifier(&self, armor_type: &ArmorType, rng: &mut R) -> u32 { + pub fn dfp_modifier(&self, armor_type: &ArmorType, rng: &mut R) -> u32 { let stats = self.armor_stats.get(armor_type).unwrap(); rng.gen_range(0, stats.dfp_modifier) } - // TODO: this needs the pmt file - fn evp_modifier(&self, armor_type: &ArmorType, rng: &mut R) -> u32 { + pub fn evp_modifier(&self, armor_type: &ArmorType, rng: &mut R) -> u32 { let stats = self.armor_stats.get(armor_type).unwrap(); rng.gen_range(0, stats.evp_modifier) } diff --git a/src/ship/drops/generic_shield.rs b/src/ship/drops/generic_shield.rs index 9f689d4..a9e34a3 100644 --- a/src/ship/drops/generic_shield.rs +++ b/src/ship/drops/generic_shield.rs @@ -67,14 +67,12 @@ impl GenericShieldTable { } } - // TODO: this needs the pmt file - fn dfp_modifier(&self, shield_type: &ShieldType, rng: &mut R) -> u32 { + pub fn dfp_modifier(&self, shield_type: &ShieldType, rng: &mut R) -> u32 { let stats = self.shield_stats.get(shield_type).unwrap(); rng.gen_range(0, stats.dfp_modifier) } - // TODO: this needs the pmt file - fn evp_modifier(&self, shield_type: &ShieldType, rng: &mut R) -> u32 { + pub fn evp_modifier(&self, shield_type: &ShieldType, rng: &mut R) -> u32 { let stats = self.shield_stats.get(shield_type).unwrap(); rng.gen_range(0, stats.evp_modifier) } diff --git a/src/ship/drops/generic_weapon.rs b/src/ship/drops/generic_weapon.rs index c8687c6..07db115 100644 --- a/src/ship/drops/generic_weapon.rs +++ b/src/ship/drops/generic_weapon.rs @@ -220,7 +220,7 @@ impl PercentRatePatterns { } } -struct AttributeTable { +pub struct AttributeTable { attribute_rates: AttributeRates, percent_rates: PercentRatePatterns, area_percent_patterns: AreaPercentPatterns, @@ -228,7 +228,7 @@ struct AttributeTable { impl AttributeTable { - fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> AttributeTable { + pub fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> AttributeTable { // TODO: new these let attribute_rates: AttributeRates = load_data_file(episode, difficulty, section_id, "attribute_rate.toml"); let percent_rates: PercentRatePatterns = load_data_file(episode, difficulty, section_id, "percent_rate.toml"); @@ -255,7 +255,7 @@ impl AttributeTable { }; let percents = self.percent_rates.get_by_pattern(pattern); - + let value_weights = WeightedIndex::new(&percents.as_array()).unwrap(); let value = value_weights.sample(rng); let percent = ((value + 1) * 5) as i8; @@ -265,11 +265,8 @@ impl AttributeTable { value: percent }) } - - fn generate_attributes(&self, map_area: &MapVariantType, rng: &mut R) -> [Option; 3] { - let percent_pattern = self.area_percent_patterns.get_by_area(map_area); - let attribute_rate = self.attribute_rates.get_by_area(map_area); + fn attributes(&self, percent_pattern: &AttributePercentPattern, attribute_rate: &AttributeRate, rng: &mut R) -> [Option; 3] { let mut percents = vec![ percent_pattern.attribute1.and_then(|pattern_type| { self.generate_attribute(&pattern_type, &attribute_rate, rng) @@ -304,6 +301,22 @@ impl AttributeTable { } }).0 } + + fn generate_attributes(&self, map_area: &MapVariantType, rng: &mut R) -> [Option; 3] { + let percent_pattern = self.area_percent_patterns.get_by_area(map_area); + let attribute_rate = self.attribute_rates.get_by_area(map_area); + self.attributes(&percent_pattern, &attribute_rate, rng) + } + + pub fn generate_rare_attributes(&self, map_area: &MapVariantType, rng: &mut R) -> [Option; 3] { + let percent_pattern = AttributePercentPattern { + attribute1: Some(PercentPatternType::Pattern6), + attribute2: Some(PercentPatternType::Pattern6), + attribute3: Some(PercentPatternType::Pattern6), + }; + let attribute_rate = self.attribute_rates.get_by_area(map_area); + self.attributes(&percent_pattern, &attribute_rate, rng) + } } @@ -347,23 +360,23 @@ impl SpecialRates { _ => self.area10, } } - + fn random_special_by_rank(&self, rank: u32, rng: &mut R) -> WeaponSpecial { let specials = match rank { 1 => vec![WeaponSpecial::Draw, WeaponSpecial::Heart, WeaponSpecial::Ice, WeaponSpecial::Bind, WeaponSpecial::Heat, WeaponSpecial::Shock, WeaponSpecial::Dim, WeaponSpecial::Panic], - 2 => vec![WeaponSpecial::Drain, WeaponSpecial::Mind, WeaponSpecial::Frost, WeaponSpecial::Hold, WeaponSpecial::Fire, WeaponSpecial::Thunder, + 2 => vec![WeaponSpecial::Drain, WeaponSpecial::Mind, WeaponSpecial::Frost, WeaponSpecial::Hold, WeaponSpecial::Fire, WeaponSpecial::Thunder, WeaponSpecial::Shadow, WeaponSpecial::Riot, WeaponSpecial::Masters, WeaponSpecial::Charge], - 3 => vec![WeaponSpecial::Fill, WeaponSpecial::Soul, WeaponSpecial::Freeze, WeaponSpecial::Seize, WeaponSpecial::Flame, WeaponSpecial::Storm, + 3 => vec![WeaponSpecial::Fill, WeaponSpecial::Soul, WeaponSpecial::Freeze, WeaponSpecial::Seize, WeaponSpecial::Flame, WeaponSpecial::Storm, WeaponSpecial::Dark, WeaponSpecial::Havoc, WeaponSpecial::Lords, WeaponSpecial::Charge, WeaponSpecial::Spirit, WeaponSpecial::Devils], - 4 => vec![WeaponSpecial::Gush, WeaponSpecial::Geist, WeaponSpecial::Blizzard, WeaponSpecial::Arrest, WeaponSpecial::Burning, WeaponSpecial::Tempest, + 4 => vec![WeaponSpecial::Gush, WeaponSpecial::Geist, WeaponSpecial::Blizzard, WeaponSpecial::Arrest, WeaponSpecial::Burning, WeaponSpecial::Tempest, WeaponSpecial::Hell, WeaponSpecial::Chaos, WeaponSpecial::Kings, WeaponSpecial::Charge, WeaponSpecial::Berserk, WeaponSpecial::Demons], _ => panic!(), }; *specials.choose(rng).unwrap() } - + fn get_special(&self, map_area: &MapVariantType, rng: &mut R) -> Option { let rate = self.rate_by_area(map_area); if rng.gen_range(0, 100) < rate.rate { @@ -400,7 +413,7 @@ impl GenericWeaponTable { rank_table.insert(WeaponDropType::Cane, vec![WeaponType::Cane, WeaponType::Stick, WeaponType::Mace, WeaponType::Club]); rank_table.insert(WeaponDropType::Rod, vec![WeaponType::Rod, WeaponType::Pole, WeaponType::Pillar, WeaponType::Striker]); rank_table.insert(WeaponDropType::Wand, vec![WeaponType::Wand, WeaponType::Staff, WeaponType::Baton, WeaponType::Scepter]); - + GenericWeaponTable { rank_table: rank_table, weapon_ratio: WeaponRatios::new(episode, difficulty, section_id), @@ -445,7 +458,7 @@ impl GenericWeaponTable { valid_weapons } - + fn weapon_type(&self, possible_weapon_types: &BTreeMap, map_area: &MapVariantType, rng: &mut R) -> WeaponDropType { let mut weapon_rates = possible_weapon_types.iter() .map(|(weapon, stat)| { @@ -536,7 +549,7 @@ mod test { equipped: false, tekked: false, }))); - + let gwt = GenericWeaponTable::new(Episode::One, Difficulty::Ultimate, SectionID::Skyly); assert!(gwt.get_drop(&MapVariantType::DarkFalz, &mut rng) == Some(ItemDetail::Weapon(Weapon { weapon: WeaponType::Vulcan, diff --git a/src/ship/drops/mod.rs b/src/ship/drops/mod.rs index 8c746be..f7be23e 100644 --- a/src/ship/drops/mod.rs +++ b/src/ship/drops/mod.rs @@ -7,7 +7,6 @@ mod generic_unit; mod tool_table; mod tech_table; - use std::collections::HashMap; use std::fs::File; use std::path::PathBuf; @@ -15,14 +14,9 @@ use std::io::Read; use serde::{Serialize, Deserialize}; use rand::{Rng, SeedableRng}; +use crate::entity::item::ItemDetail; use crate::ship::monster::MonsterType; use crate::ship::room::{Difficulty, Episode}; -use crate::entity::item::ItemDetail; -use crate::entity::item::weapon::WeaponType; -use crate::entity::item::armor::ArmorType; -use crate::entity::item::shield::ShieldType; -use crate::entity::item::unit::UnitType; -use crate::entity::item::tool::ToolType; use crate::ship::map::MapVariantType; use crate::entity::character::SectionID; use crate::ship::drops::generic_weapon::GenericWeaponTable; @@ -30,6 +24,7 @@ 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; +use crate::ship::drops::rare_drop_table::RareDropTable; fn data_file_path(episode: Episode, difficulty: Difficulty, section_id: SectionID, filename: &str) -> PathBuf { @@ -73,60 +68,28 @@ pub struct MonsterDropStats { pub max_meseta: u32, } -enum RareDropItem { - Weapon(WeaponType), - Armor(ArmorType), - Shield(ShieldType), - Unit(UnitType), - Tool(ToolType), +enum ItemDropItem { + Weapon, } - -struct RareDrop { - rate: f32, - item: RareDropItem +struct ItemDrop { + x: f32, + y: f32, + z: f32, + item: ItemDropItem, } - - -#[derive(Debug, Serialize, Deserialize)] -pub struct RareDropConfigEntity { - pub rate: f32, - pub item: String, -} - - - -/*#[derive(Serialize, Deserialize)] -pub struct MonsterDar(pub HashMap); - -/*impl MonsterDar { - fn from_f - - -}*/*/ - - -struct RareDropTable { - -} - -impl RareDropTable { - fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> RareDropTable { - RareDropTable { - - } - } - - fn get_drop(&self, monster: &MonsterType) -> Option { - None +impl ItemDropItem { + pub fn as_client_bytes(&self) -> u8 { + 0 } } + struct DropTable { - rare_table: RareDropTable, monster_stats: HashMap, + rare_table: RareDropTable, weapon_table: GenericWeaponTable, armor_table: GenericArmorTable, shield_table: GenericShieldTable, @@ -149,8 +112,8 @@ impl DropTable { let monster_stats = toml::from_str(&s).unwrap(); DropTable { - rare_table: RareDropTable::new(episode, difficulty, section_id), monster_stats: monster_stats, + rare_table: 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), @@ -182,7 +145,7 @@ impl DropTable { return None; } - if let Some(item) = self.rare_table.get_drop(&monster) { + if let Some(item) = self.rare_table.get_drop(map_area, &monster, &mut self.rng) { return Some(item); } diff --git a/src/ship/drops/rare_drop_table.rs b/src/ship/drops/rare_drop_table.rs index e69de29..e919e25 100644 --- a/src/ship/drops/rare_drop_table.rs +++ b/src/ship/drops/rare_drop_table.rs @@ -0,0 +1,155 @@ +use std::collections::HashMap; +use rand::Rng; +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::drops::generic_weapon::AttributeTable; +use crate::ship::drops::generic_armor::GenericArmorTable; +use crate::ship::drops::generic_shield::GenericShieldTable; + + +#[derive(Debug, Copy, Clone)] +enum RareDropItem { + Weapon(WeaponType), + Armor(ArmorType), + Shield(ShieldType), + Unit(UnitType), + Tool(ToolType), +} + +impl RareDropItem { + fn from_string(name: String) -> RareDropItem { + let parse_funcs: [Box Option>; 5] = [ + 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()?)))]; + + for parse in parse_funcs.iter() { + match parse(&name) { + Some(k) => return k, + None => {}, + } + } + + panic!() + } +} + + +struct RareDropRate { + rate: f32, + item: RareDropItem +} + + + +#[derive(Debug, Serialize, Deserialize)] +pub struct RareDropConfigEntity { + pub rate: f32, + pub item: String, +} + + +pub struct RareDropTable { + rates: HashMap>, + attribute_table: AttributeTable, + armor_stats: GenericArmorTable, + 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) + }).collect(); + + RareDropTable { + rates: rates, + attribute_table: AttributeTable::new(episode, difficulty, section_id), + armor_stats: GenericArmorTable::new(episode, difficulty, section_id), + shield_stats: GenericShieldTable::new(episode, difficulty, section_id), + } + } + + fn apply_item_stats(&self, map_area: &MapVariantType, item: RareDropItem, rng: &mut R) -> ItemDetail { + match item { + RareDropItem::Weapon(weapon) => { + ItemDetail::Weapon(Weapon { + weapon: weapon, + special: None, + grind: 0, + attrs: self.attribute_table.generate_rare_attributes(map_area, rng), + equipped: false, + tekked: false, + }) + + }, + RareDropItem::Armor(armor) => { + ItemDetail::Armor(Armor { + armor: armor, + 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, + equipped: false, + }) + }, + RareDropItem::Shield(shield) => { + ItemDetail::Shield(Shield { + shield: shield, + dfp: self.shield_stats.dfp_modifier(&shield, rng) as u8, + evp: self.shield_stats.evp_modifier(&shield, rng) as u8, + equipped: false, + }) + }, + RareDropItem::Unit(unit) => { + ItemDetail::Unit(Unit { + unit: unit, + modifier: None, + equipped: false, + }) + }, + RareDropItem::Tool(tool) => { + ItemDetail::Tool(Tool { + tool: tool, + }) + } + } + } + + pub fn get_drop(&self, map_area: &MapVariantType, monster: &MonsterType, rng: &mut R) -> Option { + self.rates.get(monster) + .and_then(|drop_rates| { + drop_rates.iter() + .filter_map(|drop_rate| { + let rand: f32 = rng.gen(); + if rand < drop_rate.rate { + Some(self.apply_item_stats(map_area, drop_rate.item, rng)) + } + else { + None + } + }).nth(0) + }) + } +}