add box drops
This commit is contained in:
		
							parent
							
								
									89aab63f86
								
							
						
					
					
						commit
						0d42c0ede0
					
				
							
								
								
									
										246
									
								
								src/ship/drops/box_drop_table.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										246
									
								
								src/ship/drops/box_drop_table.rs
									
									
									
									
									
										Normal file
									
								
							@ -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<BoxRareRateRaw>,
 | 
			
		||||
    area2: Vec<BoxRareRateRaw>,
 | 
			
		||||
    area3: Vec<BoxRareRateRaw>,
 | 
			
		||||
    area4: Vec<BoxRareRateRaw>,
 | 
			
		||||
    area5: Vec<BoxRareRateRaw>,
 | 
			
		||||
    area6: Vec<BoxRareRateRaw>,
 | 
			
		||||
    area7: Vec<BoxRareRateRaw>,
 | 
			
		||||
    area8: Vec<BoxRareRateRaw>,
 | 
			
		||||
    area9: Vec<BoxRareRateRaw>,
 | 
			
		||||
    area10: Vec<BoxRareRateRaw>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct BoxRareRate {
 | 
			
		||||
    item: RareDropItem,
 | 
			
		||||
    rate: f32,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
struct BoxRareRates {
 | 
			
		||||
    area1: Vec<BoxRareRate>,
 | 
			
		||||
    area2: Vec<BoxRareRate>,
 | 
			
		||||
    area3: Vec<BoxRareRate>,
 | 
			
		||||
    area4: Vec<BoxRareRate>,
 | 
			
		||||
    area5: Vec<BoxRareRate>,
 | 
			
		||||
    area6: Vec<BoxRareRate>,
 | 
			
		||||
    area7: Vec<BoxRareRate>,
 | 
			
		||||
    area8: Vec<BoxRareRate>,
 | 
			
		||||
    area9: Vec<BoxRareRate>,
 | 
			
		||||
    area10: Vec<BoxRareRate>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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<BoxRareRate> {
 | 
			
		||||
        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<R: Rng>(&self, map_area: &MapVariantType, rng: &mut R) -> Option<ItemDetail> {
 | 
			
		||||
        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<R: Rng>(&self, map_area: &MapVariantType, rng: &mut R) -> Option<ItemDetail> {
 | 
			
		||||
        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<R: Rng>(&self, fixed_drop: FixedBoxDropType, map_area: &MapVariantType, rng: &mut R) -> Option<ItemDetail> {
 | 
			
		||||
        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<R: Rng>(&self, map_area: &MapVariantType, object: &MapObject, rng: &mut R) -> Option<ItemDetail> {
 | 
			
		||||
        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));
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -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<R: Rng + SeedableRng> {
 | 
			
		||||
    shield_table: GenericShieldTable,
 | 
			
		||||
    unit_table: GenericUnitTable,
 | 
			
		||||
    tool_table: ToolTable,
 | 
			
		||||
    box_table: BoxDropTable,
 | 
			
		||||
    rng: R,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -119,6 +124,7 @@ impl<R: Rng + SeedableRng> DropTable<R> {
 | 
			
		||||
            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<R: Rng + SeedableRng> DropTable<R> {
 | 
			
		||||
            _ => panic!()
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn get_box_drop(&mut self, map_area: &MapVariantType, object: &MapObject) -> Option<ItemDetail> {
 | 
			
		||||
        self.box_table.get_drop(map_area, object, &mut self.rng)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -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<dyn Fn(&String) -> Option<RareDropItem>>; 5] = [
 | 
			
		||||
    pub fn from_string(name: String) -> RareDropItem {
 | 
			
		||||
        let parse_funcs: [Box<dyn Fn(&String) -> Option<RareDropItem>>; 6] = [
 | 
			
		||||
            Box::new(|i| Some(RareDropItem::Weapon(str::parse::<WeaponType>(&i).ok()?))),
 | 
			
		||||
            Box::new(|i| Some(RareDropItem::Armor(str::parse::<ArmorType>(&i).ok()?))),
 | 
			
		||||
            Box::new(|i| Some(RareDropItem::Shield(str::parse::<ShieldType>(&i).ok()?))),
 | 
			
		||||
            Box::new(|i| Some(RareDropItem::Unit(str::parse::<UnitType>(&i).ok()?))),
 | 
			
		||||
            Box::new(|i| Some(RareDropItem::Tool(str::parse::<ToolType>(&i).ok()?)))];
 | 
			
		||||
            Box::new(|i| Some(RareDropItem::Tool(str::parse::<ToolType>(&i).ok()?))),
 | 
			
		||||
            Box::new(|i| Some(RareDropItem::Mag(str::parse::<MagType>(&i).ok()?))),
 | 
			
		||||
        ];
 | 
			
		||||
 | 
			
		||||
        for parse in parse_funcs.iter() {
 | 
			
		||||
            match parse(&name) {
 | 
			
		||||
@ -92,7 +96,7 @@ impl RareDropTable {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fn apply_item_stats<R: Rng>(&self, map_area: &MapVariantType, item: RareDropItem, rng: &mut R) -> ItemDetail {
 | 
			
		||||
    pub fn apply_item_stats<R: Rng>(&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,
 | 
			
		||||
                })
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user