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

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. use std::collections::HashMap;
  2. use rand::Rng;
  3. use serde::{Serialize, Deserialize};
  4. use entity::item::weapon::{Weapon, WeaponType};
  5. use entity::item::armor::{Armor, ArmorType};
  6. use entity::item::shield::{Shield, ShieldType};
  7. use entity::item::unit::{Unit, UnitType};
  8. use entity::item::tool::{Tool, ToolType};
  9. use entity::item::mag::{Mag, MagType};
  10. use entity::character::SectionID;
  11. use maps::monster::MonsterType;
  12. use maps::room::{Difficulty, Episode};
  13. use maps::area::MapArea;
  14. use crate::ship::drops::{ItemDropType, load_data_file};
  15. use crate::ship::drops::generic_weapon::AttributeTable;
  16. use crate::ship::drops::generic_armor::GenericArmorTable;
  17. use crate::ship::drops::generic_shield::GenericShieldTable;
  18. type ItemParseFn = Box<dyn Fn(&String) -> Option<RareDropItem>>;
  19. #[derive(Debug, Copy, Clone)]
  20. pub enum RareDropItem {
  21. Weapon(WeaponType),
  22. Armor(ArmorType),
  23. Shield(ShieldType),
  24. Unit(UnitType),
  25. Tool(ToolType),
  26. Mag(MagType)
  27. }
  28. impl RareDropItem {
  29. pub fn from_string(name: String) -> RareDropItem {
  30. let parse_funcs: [ItemParseFn; 6] = [
  31. Box::new(|i| Some(RareDropItem::Weapon(str::parse::<WeaponType>(i).ok()?))),
  32. Box::new(|i| Some(RareDropItem::Armor(str::parse::<ArmorType>(i).ok()?))),
  33. Box::new(|i| Some(RareDropItem::Shield(str::parse::<ShieldType>(i).ok()?))),
  34. Box::new(|i| Some(RareDropItem::Unit(str::parse::<UnitType>(i).ok()?))),
  35. Box::new(|i| Some(RareDropItem::Tool(str::parse::<ToolType>(i).ok()?))),
  36. Box::new(|i| Some(RareDropItem::Mag(str::parse::<MagType>(i).ok()?))),
  37. ];
  38. for parse in parse_funcs.iter() {
  39. if let Some (k) = parse(&name) {
  40. return k
  41. }
  42. }
  43. panic!()
  44. }
  45. }
  46. pub struct RareDropRate {
  47. pub rate: f32,
  48. pub item: RareDropItem
  49. }
  50. #[derive(Debug, Serialize, Deserialize)]
  51. pub struct RareDropConfigEntity {
  52. pub rate: f32,
  53. pub item: String,
  54. }
  55. pub struct RareDropTable {
  56. rates: HashMap<MonsterType, Vec<RareDropRate>>,
  57. attribute_table: AttributeTable,
  58. armor_stats: GenericArmorTable,
  59. shield_stats: GenericShieldTable,
  60. }
  61. fn load_default_monster_rates(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> HashMap<MonsterType, Vec<RareDropRate>> {
  62. let cfg: HashMap<String, Vec<RareDropConfigEntity>> = load_data_file(episode, difficulty, section_id, "rare_rate.toml");
  63. cfg.into_iter()
  64. .map(|(monster, drops)| {
  65. let monster = monster.parse().unwrap();
  66. let drops = drops.into_iter().map(|drop| {
  67. RareDropRate {
  68. rate: drop.rate,
  69. item: RareDropItem::from_string(drop.item),
  70. }
  71. }).collect();
  72. (monster, drops)
  73. }).collect()
  74. }
  75. impl RareDropTable {
  76. pub fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> RareDropTable {
  77. RareDropTable {
  78. rates: load_default_monster_rates(episode, difficulty, section_id),
  79. attribute_table: AttributeTable::new(episode, difficulty, section_id),
  80. armor_stats: GenericArmorTable::new(episode, difficulty, section_id),
  81. shield_stats: GenericShieldTable::new(episode, difficulty, section_id),
  82. }
  83. }
  84. pub fn builder() -> RareDropTableBuilder {
  85. RareDropTableBuilder {
  86. rates: None,
  87. attribute_table: None,
  88. armor_stats: None,
  89. shield_stats: None,
  90. }
  91. }
  92. pub fn apply_item_stats<R: Rng>(&self, map_area: &MapArea, item: RareDropItem, rng: &mut R) -> ItemDropType {
  93. match item {
  94. RareDropItem::Weapon(weapon) => {
  95. ItemDropType::Weapon(Weapon {
  96. weapon,
  97. special: None,
  98. grind: 0,
  99. attrs: self.attribute_table.generate_rare_attributes(map_area, rng),
  100. tekked: false,
  101. })
  102. },
  103. RareDropItem::Armor(armor) => {
  104. ItemDropType::Armor(Armor {
  105. armor,
  106. dfp: self.armor_stats.dfp_modifier(&armor, rng) as u8,
  107. evp: self.armor_stats.evp_modifier(&armor, rng) as u8,
  108. slots: self.armor_stats.slots(map_area, rng) as u8,
  109. })
  110. },
  111. RareDropItem::Shield(shield) => {
  112. ItemDropType::Shield(Shield {
  113. shield,
  114. dfp: self.shield_stats.dfp_modifier(&shield, rng) as u8,
  115. evp: self.shield_stats.evp_modifier(&shield, rng) as u8,
  116. })
  117. },
  118. RareDropItem::Unit(unit) => {
  119. ItemDropType::Unit(Unit {
  120. unit,
  121. modifier: None,
  122. })
  123. },
  124. RareDropItem::Tool(tool) => {
  125. ItemDropType::Tool(Tool {
  126. tool,
  127. })
  128. },
  129. RareDropItem::Mag(_mag) => {
  130. ItemDropType::Mag(Mag::baby_mag(rng.gen_range(0, 18)))
  131. }
  132. }
  133. }
  134. pub fn get_drop<R: Rng>(&self, map_area: &MapArea, monster: &MonsterType, rng: &mut R) -> Option<ItemDropType> {
  135. self.rates.get(monster)
  136. .and_then(|drop_rates| {
  137. drop_rates.iter()
  138. .filter_map(|drop_rate| {
  139. let rand: f32 = rng.gen();
  140. if rand < drop_rate.rate {
  141. Some(self.apply_item_stats(map_area, drop_rate.item, rng))
  142. }
  143. else {
  144. None
  145. }
  146. }).next()
  147. })
  148. }
  149. }
  150. pub struct RareDropTableBuilder {
  151. rates: Option<HashMap<MonsterType, Vec<RareDropRate>>>,
  152. attribute_table: Option<AttributeTable>,
  153. armor_stats: Option<GenericArmorTable>,
  154. shield_stats: Option<GenericShieldTable>,
  155. }
  156. // TODO: add the rest of these later I just need these ones right now
  157. impl RareDropTableBuilder {
  158. pub fn rates(mut self, rates: HashMap<MonsterType, Vec<RareDropRate>>) -> RareDropTableBuilder {
  159. self.rates = Some(rates);
  160. self
  161. }
  162. #[must_use]
  163. pub fn rate(mut self, monster_type: MonsterType, drop_rate: RareDropRate) -> RareDropTableBuilder {
  164. match &mut self.rates {
  165. Some(rates) => {
  166. rates.entry(monster_type)
  167. .or_insert(Vec::new())
  168. .push(drop_rate);
  169. },
  170. None => {
  171. let mut rates = HashMap::default();
  172. rates.insert(monster_type, vec![drop_rate]);
  173. self.rates = Some(rates);
  174. }
  175. }
  176. self
  177. }
  178. pub fn build(self, episode: Episode, difficulty: Difficulty, section_id: SectionID) -> RareDropTable {
  179. RareDropTable {
  180. rates: self.rates.unwrap_or_else(|| load_default_monster_rates(episode, difficulty, section_id)),
  181. attribute_table: self.attribute_table.unwrap_or_else(|| AttributeTable::new(episode, difficulty, section_id)),
  182. armor_stats: self.armor_stats.unwrap_or_else(|| GenericArmorTable::new(episode, difficulty, section_id)),
  183. shield_stats: self.shield_stats.unwrap_or_else(|| GenericShieldTable::new(episode, difficulty, section_id)),
  184. }
  185. }
  186. }