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.

134 lines
4.9 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
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. use std::collections::{BTreeMap};
  2. use serde::{Serialize, Deserialize};
  3. use rand::{Rng};
  4. use rand::distributions::{WeightedIndex, Distribution};
  5. use crate::entity::item::tech::{Technique, TechniqueDisk};
  6. use crate::ship::room::{Difficulty, Episode};
  7. use crate::ship::map::MapArea;
  8. use crate::entity::character::SectionID;
  9. use crate::ship::drops::{ItemDropType, load_data_file};
  10. #[derive(Debug, Serialize, Deserialize)]
  11. struct TechniqueRateStat {
  12. rate: u32,
  13. min: i32,
  14. max: i32,
  15. }
  16. #[derive(Debug, Serialize, Deserialize)]
  17. struct TechniqueRatesRaw {
  18. area1: BTreeMap<String, TechniqueRateStat>,
  19. area2: BTreeMap<String, TechniqueRateStat>,
  20. area3: BTreeMap<String, TechniqueRateStat>,
  21. area4: BTreeMap<String, TechniqueRateStat>,
  22. area5: BTreeMap<String, TechniqueRateStat>,
  23. area6: BTreeMap<String, TechniqueRateStat>,
  24. area7: BTreeMap<String, TechniqueRateStat>,
  25. area8: BTreeMap<String, TechniqueRateStat>,
  26. area9: BTreeMap<String, TechniqueRateStat>,
  27. area10: BTreeMap<String, TechniqueRateStat>,
  28. }
  29. #[derive(Debug, Serialize, Deserialize)]
  30. struct TechniqueRates {
  31. area1: BTreeMap<Technique, TechniqueRateStat>,
  32. area2: BTreeMap<Technique, TechniqueRateStat>,
  33. area3: BTreeMap<Technique, TechniqueRateStat>,
  34. area4: BTreeMap<Technique, TechniqueRateStat>,
  35. area5: BTreeMap<Technique, TechniqueRateStat>,
  36. area6: BTreeMap<Technique, TechniqueRateStat>,
  37. area7: BTreeMap<Technique, TechniqueRateStat>,
  38. area8: BTreeMap<Technique, TechniqueRateStat>,
  39. area9: BTreeMap<Technique, TechniqueRateStat>,
  40. area10: BTreeMap<Technique, TechniqueRateStat>,
  41. }
  42. impl TechniqueRates {
  43. fn new(rates: TechniqueRatesRaw) -> TechniqueRates {
  44. TechniqueRates {
  45. area1: rates.area1.into_iter().map(|(tech, rate)| (tech.parse().unwrap(), rate)).collect(),
  46. area2: rates.area2.into_iter().map(|(tech, rate)| (tech.parse().unwrap(), rate)).collect(),
  47. area3: rates.area3.into_iter().map(|(tech, rate)| (tech.parse().unwrap(), rate)).collect(),
  48. area4: rates.area4.into_iter().map(|(tech, rate)| (tech.parse().unwrap(), rate)).collect(),
  49. area5: rates.area5.into_iter().map(|(tech, rate)| (tech.parse().unwrap(), rate)).collect(),
  50. area6: rates.area6.into_iter().map(|(tech, rate)| (tech.parse().unwrap(), rate)).collect(),
  51. area7: rates.area7.into_iter().map(|(tech, rate)| (tech.parse().unwrap(), rate)).collect(),
  52. area8: rates.area8.into_iter().map(|(tech, rate)| (tech.parse().unwrap(), rate)).collect(),
  53. area9: rates.area9.into_iter().map(|(tech, rate)| (tech.parse().unwrap(), rate)).collect(),
  54. area10: rates.area10.into_iter().map(|(tech, rate)| (tech.parse().unwrap(), rate)).collect(),
  55. }
  56. }
  57. }
  58. impl TechniqueRates {
  59. fn get_by_area<'a>(&'a self, map_area: &MapArea) -> &'a BTreeMap<Technique, TechniqueRateStat> {
  60. match map_area.drop_area_value().unwrap() {
  61. 0 => &self.area1,
  62. 1 => &self.area2,
  63. 2 => &self.area3,
  64. 3 => &self.area4,
  65. 4 => &self.area5,
  66. 5 => &self.area6,
  67. 6 => &self.area7,
  68. 7 => &self.area8,
  69. 8 => &self.area9,
  70. _ => &self.area10,
  71. }
  72. }
  73. }
  74. pub struct TechniqueTable {
  75. rates: TechniqueRates
  76. }
  77. impl TechniqueTable {
  78. pub fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> TechniqueTable {
  79. let rates: TechniqueRatesRaw = load_data_file(episode, difficulty, section_id, "tech_rate.toml");
  80. TechniqueTable {
  81. rates: TechniqueRates::new(rates),
  82. }
  83. }
  84. pub fn get_drop<R: Rng>(&self, map_area: &MapArea, rng: &mut R) -> Option<ItemDropType> {
  85. let mut tech_rates = self.rates.get_by_area(map_area).iter();
  86. let tech_weights = WeightedIndex::new(tech_rates.clone().map(|(_, stat)| stat.rate)).unwrap();
  87. let (tech, stat) = tech_rates.nth(tech_weights.sample(rng)).unwrap();
  88. let level = rng.gen_range(stat.min, stat.max+1) + 1;
  89. Some(ItemDropType::TechniqueDisk(TechniqueDisk {
  90. tech: *tech,
  91. level: level as u32
  92. }))
  93. }
  94. }
  95. #[cfg(test)]
  96. mod test {
  97. use super::*;
  98. use rand::{SeedableRng};
  99. #[test]
  100. fn test_tech_drops() {
  101. let mut rng = rand_chacha::ChaCha20Rng::from_seed([23;32]);
  102. let tt = TechniqueTable::new(Episode::One, Difficulty::Ultimate, SectionID::Skyly);
  103. let tech_tests = vec![(MapArea::Forest1, Technique::Resta, 14),
  104. (MapArea::Caves3, Technique::Foie, 25),
  105. (MapArea::Mines2, Technique::Gibarta, 21),
  106. (MapArea::DarkFalz, Technique::Razonde, 23)];
  107. for (area, tech, level) in tech_tests {
  108. assert!(tt.get_drop(&area, &mut rng) == Some(ItemDropType::TechniqueDisk(
  109. TechniqueDisk {
  110. tech: tech,
  111. level: level
  112. })));
  113. }
  114. }
  115. }