You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
211 lines
7.2 KiB
211 lines
7.2 KiB
use std::collections::HashMap;
|
|
use rand::Rng;
|
|
use serde::{Serialize, Deserialize};
|
|
use entity::item::weapon::{Weapon, WeaponType};
|
|
use entity::item::armor::{Armor, ArmorType};
|
|
use entity::item::shield::{Shield, ShieldType};
|
|
use entity::item::unit::{Unit, UnitType};
|
|
use entity::item::tool::{Tool, ToolType};
|
|
use entity::item::mag::{Mag, MagType};
|
|
use entity::character::SectionID;
|
|
use maps::monster::MonsterType;
|
|
use maps::room::{Difficulty, Episode};
|
|
use maps::area::MapArea;
|
|
use crate::ship::drops::{ItemDropType, load_data_file};
|
|
use crate::ship::drops::generic_weapon::AttributeTable;
|
|
use crate::ship::drops::generic_armor::GenericArmorTable;
|
|
use crate::ship::drops::generic_shield::GenericShieldTable;
|
|
|
|
type ItemParseFn = Box<dyn Fn(&String) -> Option<RareDropItem>>;
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
pub enum RareDropItem {
|
|
Weapon(WeaponType),
|
|
Armor(ArmorType),
|
|
Shield(ShieldType),
|
|
Unit(UnitType),
|
|
Tool(ToolType),
|
|
Mag(MagType)
|
|
}
|
|
|
|
impl RareDropItem {
|
|
pub fn from_string(name: String) -> RareDropItem {
|
|
let parse_funcs: [ItemParseFn; 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::Mag(str::parse::<MagType>(i).ok()?))),
|
|
];
|
|
|
|
for parse in parse_funcs.iter() {
|
|
if let Some (k) = parse(&name) {
|
|
return k
|
|
}
|
|
}
|
|
|
|
panic!()
|
|
}
|
|
}
|
|
|
|
|
|
pub struct RareDropRate {
|
|
pub rate: f32,
|
|
pub item: RareDropItem
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct RareDropConfigEntity {
|
|
pub rate: f32,
|
|
pub item: String,
|
|
}
|
|
|
|
|
|
pub struct RareDropTable {
|
|
rates: HashMap<MonsterType, Vec<RareDropRate>>,
|
|
attribute_table: AttributeTable,
|
|
armor_stats: GenericArmorTable,
|
|
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 {
|
|
pub fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> RareDropTable {
|
|
RareDropTable {
|
|
rates: load_default_monster_rates(episode, difficulty, section_id),
|
|
attribute_table: AttributeTable::new(episode, difficulty, section_id),
|
|
armor_stats: GenericArmorTable::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 {
|
|
match item {
|
|
RareDropItem::Weapon(weapon) => {
|
|
ItemDropType::Weapon(Weapon {
|
|
weapon,
|
|
special: None,
|
|
grind: 0,
|
|
attrs: self.attribute_table.generate_rare_attributes(map_area, rng),
|
|
tekked: false,
|
|
})
|
|
|
|
},
|
|
RareDropItem::Armor(armor) => {
|
|
ItemDropType::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,
|
|
})
|
|
},
|
|
RareDropItem::Shield(shield) => {
|
|
ItemDropType::Shield(Shield {
|
|
shield,
|
|
dfp: self.shield_stats.dfp_modifier(&shield, rng) as u8,
|
|
evp: self.shield_stats.evp_modifier(&shield, rng) as u8,
|
|
})
|
|
},
|
|
RareDropItem::Unit(unit) => {
|
|
ItemDropType::Unit(Unit {
|
|
unit,
|
|
modifier: None,
|
|
})
|
|
},
|
|
RareDropItem::Tool(tool) => {
|
|
ItemDropType::Tool(Tool {
|
|
tool,
|
|
})
|
|
},
|
|
RareDropItem::Mag(_mag) => {
|
|
ItemDropType::Mag(Mag::baby_mag(rng.gen_range(0, 18)))
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn get_drop<R: Rng>(&self, map_area: &MapArea, monster: &MonsterType, rng: &mut R) -> Option<ItemDropType> {
|
|
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
|
|
}
|
|
}).next()
|
|
})
|
|
}
|
|
}
|
|
|
|
|
|
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)),
|
|
}
|
|
}
|
|
}
|