jake
5 years ago
3 changed files with 278 additions and 5 deletions
@ -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));
|
||||
|
}
|
||||
|
}
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue