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.

125 lines
4.4 KiB

5 years ago
5 years ago
  1. use std::collections::HashMap;
  2. use serde::{Serialize, Deserialize};
  3. use rand::Rng;
  4. use rand::distributions::{WeightedIndex, Distribution};
  5. use entity::item::shield::{ShieldType, Shield};
  6. use entity::character::SectionID;
  7. use maps::room::{Difficulty, Episode};
  8. use maps::area::MapArea;
  9. use crate::{ItemDropType, load_data_file};
  10. use stats::items::{shield_stats, ShieldStats};
  11. #[derive(Debug, Serialize, Deserialize)]
  12. struct ShieldRankRates {
  13. rank0: u32,
  14. rank1: u32,
  15. rank2: u32,
  16. rank3: u32,
  17. rank4: u32,
  18. }
  19. #[derive(Debug, Serialize, Deserialize)]
  20. pub struct GenericShieldTable {
  21. rank_rates: ShieldRankRates,
  22. shield_set: u32,
  23. #[serde(skip)]
  24. shield_stats: HashMap<ShieldType, ShieldStats>,
  25. }
  26. impl GenericShieldTable {
  27. pub fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> GenericShieldTable {
  28. let mut gst: GenericShieldTable = load_data_file(episode, difficulty, section_id, "shield_rate.toml");
  29. gst.shield_stats = shield_stats();
  30. gst
  31. }
  32. fn shield_type<R: Rng>(&self, area_map: &MapArea, rng: &mut R) -> ShieldType {
  33. let rank_weights = WeightedIndex::new([self.rank_rates.rank0, self.rank_rates.rank1, self.rank_rates.rank2,
  34. self.rank_rates.rank3, self.rank_rates.rank4]).unwrap();
  35. let rank = rank_weights.sample(rng) as i32;
  36. let shield_level = std::cmp::max(0i32, self.shield_set as i32 - 3i32 + rank + area_map.drop_area_value().unwrap_or(0) as i32);
  37. match shield_level {
  38. 0x00 => ShieldType::Barrier,
  39. 0x01 => ShieldType::Shield,
  40. 0x02 => ShieldType::CoreShield,
  41. 0x03 => ShieldType::GigaShield,
  42. 0x04 => ShieldType::SoulBarrier,
  43. 0x05 => ShieldType::HardShield,
  44. 0x06 => ShieldType::BraveBarrier,
  45. 0x07 => ShieldType::SolidShield,
  46. 0x08 => ShieldType::FlameBarrier,
  47. 0x09 => ShieldType::PlasmaBarrier,
  48. 0x0A => ShieldType::FreezeBarrier,
  49. 0x0B => ShieldType::PsychicBarrier,
  50. 0x0C => ShieldType::GeneralShield,
  51. 0x0D => ShieldType::ProtectBarrier,
  52. 0x0E => ShieldType::GloriousShield,
  53. 0x0F => ShieldType::ImperialBarrier,
  54. 0x10 => ShieldType::GuardianShield,
  55. 0x11 => ShieldType::DivinityBarrier,
  56. 0x12 => ShieldType::UltimateShield,
  57. 0x13 => ShieldType::SpiritualShield,
  58. 0x14 => ShieldType::CelestialShield,
  59. _ => panic!(),
  60. }
  61. }
  62. pub fn dfp_modifier<R: Rng>(&self, shield_type: &ShieldType, rng: &mut R) -> u32 {
  63. let stats = self.shield_stats.get(shield_type).unwrap();
  64. rng.gen_range(0, stats.dfp_modifier)
  65. }
  66. pub fn evp_modifier<R: Rng>(&self, shield_type: &ShieldType, rng: &mut R) -> u32 {
  67. let stats = self.shield_stats.get(shield_type).unwrap();
  68. rng.gen_range(0, stats.evp_modifier)
  69. }
  70. pub fn get_drop<R: Rng>(&self, area_map: &MapArea, rng: &mut R) -> Option<ItemDropType> {
  71. let shield_type = self.shield_type(area_map, rng);
  72. let dfp_modifier = self.dfp_modifier(&shield_type, rng);
  73. let evp_modifier = self.dfp_modifier(&shield_type, rng);
  74. Some(ItemDropType::Shield(Shield {
  75. shield: shield_type,
  76. dfp: dfp_modifier as u8,
  77. evp: evp_modifier as u8,
  78. }))
  79. }
  80. }
  81. #[cfg(test)]
  82. mod test {
  83. use super::*;
  84. use rand::{SeedableRng};
  85. #[test]
  86. fn test_shield_generation() {
  87. let mut rng = rand_chacha::ChaCha20Rng::from_seed([23;32]);
  88. let gst = GenericShieldTable::new(Episode::One, Difficulty::Ultimate, SectionID::Skyly);
  89. assert!(gst.get_drop(&MapArea::Forest1, &mut rng) == Some(ItemDropType::Shield(Shield {
  90. shield: ShieldType::FreezeBarrier,
  91. dfp: 4,
  92. evp: 1,
  93. })));
  94. assert!(gst.get_drop(&MapArea::Caves3, &mut rng) == Some(ItemDropType::Shield(Shield {
  95. shield: ShieldType::PsychicBarrier,
  96. dfp: 3,
  97. evp: 2,
  98. })));
  99. assert!(gst.get_drop(&MapArea::Mines2, &mut rng) == Some(ItemDropType::Shield(Shield {
  100. shield: ShieldType::ImperialBarrier,
  101. dfp: 0,
  102. evp: 4,
  103. })));
  104. assert!(gst.get_drop(&MapArea::DarkFalz, &mut rng) == Some(ItemDropType::Shield(Shield {
  105. shield: ShieldType::DivinityBarrier,
  106. dfp: 1,
  107. evp: 0,
  108. })));
  109. }
  110. }