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.

241 lines
9.0 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
  1. #![allow(dead_code, unused_variables)]
  2. use rand::{Rng};
  3. use rand::distributions::{WeightedIndex, Distribution};
  4. use serde::{Serialize, Deserialize};
  5. use entity::character::SectionID;
  6. use maps::room::{Difficulty, Episode};
  7. use maps::area::MapArea;
  8. use crate::{ItemDropType, load_data_file};
  9. use maps::object::{MapObject, MapObjectType, FixedBoxDropType};
  10. use crate::rare_drop_table::{RareDropTable, RareDropItem};
  11. use crate::generic_weapon::GenericWeaponTable;
  12. use crate::generic_armor::GenericArmorTable;
  13. use crate::generic_shield::GenericShieldTable;
  14. use crate::generic_unit::GenericUnitTable;
  15. use crate::tool_table::ToolTable;
  16. #[derive(Debug, Serialize, Deserialize)]
  17. struct BoxDropRate {
  18. weapon_rate: u32,
  19. armor_rate: u32,
  20. shield_rate: u32,
  21. unit_rate: u32,
  22. tool_rate: u32,
  23. meseta_rate: u32,
  24. nothing_rate: u32,
  25. min_meseta: u32,
  26. max_meseta: u32,
  27. }
  28. #[derive(Debug, Serialize, Deserialize)]
  29. struct BoxDropRates {
  30. area1: BoxDropRate,
  31. area2: BoxDropRate,
  32. area3: BoxDropRate,
  33. area4: BoxDropRate,
  34. area5: BoxDropRate,
  35. area6: BoxDropRate,
  36. area7: BoxDropRate,
  37. area8: BoxDropRate,
  38. area9: BoxDropRate,
  39. area10: BoxDropRate,
  40. }
  41. impl BoxDropRates {
  42. fn rates_by_area(&self, map_area: &MapArea) -> &BoxDropRate {
  43. match map_area.drop_area_value().unwrap() {
  44. 0 => &self.area1,
  45. 1 => &self.area2,
  46. 2 => &self.area3,
  47. 3 => &self.area1,
  48. 4 => &self.area1,
  49. 5 => &self.area6,
  50. 6 => &self.area7,
  51. 7 => &self.area8,
  52. 8 => &self.area9,
  53. 9 => &self.area10,
  54. _ => panic!()
  55. }
  56. }
  57. }
  58. #[derive(Debug, Serialize, Deserialize)]
  59. struct BoxRareRateRaw {
  60. item: String,
  61. rate: f32,
  62. }
  63. #[derive(Debug, Serialize, Deserialize)]
  64. struct BoxRareRatesRaw {
  65. area1: Vec<BoxRareRateRaw>,
  66. area2: Vec<BoxRareRateRaw>,
  67. area3: Vec<BoxRareRateRaw>,
  68. area4: Vec<BoxRareRateRaw>,
  69. area5: Vec<BoxRareRateRaw>,
  70. area6: Vec<BoxRareRateRaw>,
  71. area7: Vec<BoxRareRateRaw>,
  72. area8: Vec<BoxRareRateRaw>,
  73. area9: Vec<BoxRareRateRaw>,
  74. area10: Vec<BoxRareRateRaw>,
  75. }
  76. struct BoxRareRate {
  77. item: RareDropItem,
  78. rate: f32,
  79. }
  80. struct BoxRareRates {
  81. area1: Vec<BoxRareRate>,
  82. area2: Vec<BoxRareRate>,
  83. area3: Vec<BoxRareRate>,
  84. area4: Vec<BoxRareRate>,
  85. area5: Vec<BoxRareRate>,
  86. area6: Vec<BoxRareRate>,
  87. area7: Vec<BoxRareRate>,
  88. area8: Vec<BoxRareRate>,
  89. area9: Vec<BoxRareRate>,
  90. area10: Vec<BoxRareRate>,
  91. }
  92. impl BoxRareRates {
  93. fn new(raw: BoxRareRatesRaw) -> BoxRareRates {
  94. BoxRareRates {
  95. area1: raw.area1.into_iter().map(|i| { BoxRareRate {item: RareDropItem::from_string(i.item), rate: i.rate} }).collect(),
  96. area2: raw.area2.into_iter().map(|i| { BoxRareRate {item: RareDropItem::from_string(i.item), rate: i.rate} }).collect(),
  97. area3: raw.area3.into_iter().map(|i| { BoxRareRate {item: RareDropItem::from_string(i.item), rate: i.rate} }).collect(),
  98. area4: raw.area4.into_iter().map(|i| { BoxRareRate {item: RareDropItem::from_string(i.item), rate: i.rate} }).collect(),
  99. area5: raw.area5.into_iter().map(|i| { BoxRareRate {item: RareDropItem::from_string(i.item), rate: i.rate} }).collect(),
  100. area6: raw.area6.into_iter().map(|i| { BoxRareRate {item: RareDropItem::from_string(i.item), rate: i.rate} }).collect(),
  101. area7: raw.area7.into_iter().map(|i| { BoxRareRate {item: RareDropItem::from_string(i.item), rate: i.rate} }).collect(),
  102. area8: raw.area8.into_iter().map(|i| { BoxRareRate {item: RareDropItem::from_string(i.item), rate: i.rate} }).collect(),
  103. area9: raw.area9.into_iter().map(|i| { BoxRareRate {item: RareDropItem::from_string(i.item), rate: i.rate} }).collect(),
  104. area10: raw.area10.into_iter().map(|i| { BoxRareRate {item: RareDropItem::from_string(i.item), rate: i.rate} }).collect(),
  105. }
  106. }
  107. fn rates_by_area(&self, map_area: &MapArea) -> &Vec<BoxRareRate> {
  108. match map_area.drop_area_value().unwrap() {
  109. 0 => &self.area1,
  110. 1 => &self.area2,
  111. 2 => &self.area3,
  112. 3 => &self.area1,
  113. 4 => &self.area1,
  114. 5 => &self.area6,
  115. 6 => &self.area7,
  116. 7 => &self.area8,
  117. 8 => &self.area9,
  118. 9 => &self.area10,
  119. _ => panic!()
  120. }
  121. }
  122. }
  123. pub struct BoxDropTable {
  124. box_rates: BoxDropRates,
  125. rare_rates: BoxRareRates,
  126. rare_stats: RareDropTable,
  127. weapon_table: GenericWeaponTable,
  128. armor_table: GenericArmorTable,
  129. shield_table: GenericShieldTable,
  130. unit_table: GenericUnitTable,
  131. tool_table: ToolTable,
  132. }
  133. impl BoxDropTable {
  134. pub fn new(episode: Episode, difficulty: Difficulty, section_id: SectionID) -> BoxDropTable {
  135. let rates = load_data_file(episode, difficulty, section_id, "box_rare_rate.toml");
  136. BoxDropTable {
  137. box_rates: load_data_file(episode, difficulty, section_id, "box_drop_rate.toml"),
  138. rare_rates: BoxRareRates::new(rates),
  139. rare_stats: RareDropTable::new(episode, difficulty, section_id),
  140. weapon_table: GenericWeaponTable::new(episode, difficulty, section_id),
  141. armor_table: GenericArmorTable::new(episode, difficulty, section_id),
  142. shield_table: GenericShieldTable::new(episode, difficulty, section_id),
  143. unit_table: GenericUnitTable::new(episode, difficulty, section_id),
  144. tool_table: ToolTable::new(episode, difficulty, section_id),
  145. }
  146. }
  147. fn rare_drop<R: Rng>(&self, map_area: &MapArea, rng: &mut R) -> Option<ItemDropType> {
  148. self.rare_rates.rates_by_area(map_area).iter()
  149. .filter_map(|rate| {
  150. let rand: f32 = rng.gen();
  151. if rand < rate.rate {
  152. Some(self.rare_stats.apply_item_stats(map_area, rate.item, rng))
  153. }
  154. else {
  155. None
  156. }
  157. }).next()
  158. }
  159. fn random_box_drop<R: Rng>(&self, map_area: &MapArea, rng: &mut R) -> Option<ItemDropType> {
  160. self.rare_drop(map_area, rng).or_else(|| {
  161. let rate = self.box_rates.rates_by_area(map_area);
  162. let type_weights = WeightedIndex::new([rate.weapon_rate, rate.armor_rate, rate.shield_rate, rate.unit_rate,
  163. rate.tool_rate, rate.meseta_rate, rate.nothing_rate]).unwrap();
  164. let btype = type_weights.sample(rng);
  165. match btype {
  166. 0 => self.weapon_table.get_drop(map_area, rng),
  167. 1 => self.armor_table.get_drop(map_area, rng),
  168. 2 => self.shield_table.get_drop(map_area, rng),
  169. 3 => self.unit_table.get_drop(map_area, rng),
  170. 4 => self.tool_table.get_drop(map_area, rng),
  171. 5 => Some(ItemDropType::Meseta(rng.gen_range(rate.min_meseta, rate.max_meseta))),
  172. _ => None,
  173. }
  174. })
  175. }
  176. fn fixed_box_drop<R: Rng>(&self, fixed_drop: FixedBoxDropType, map_area: &MapArea, rng: &mut R) -> Option<ItemDropType> {
  177. match fixed_drop {
  178. FixedBoxDropType::Weapon => self.weapon_table.get_drop(map_area, rng),
  179. FixedBoxDropType::Armor => self.armor_table.get_drop(map_area, rng), // TODO: should this drop shield?
  180. FixedBoxDropType::Tool => self.tool_table.get_drop(map_area, rng),
  181. FixedBoxDropType::Meseta => {
  182. let rate = self.box_rates.rates_by_area(map_area);
  183. Some(ItemDropType::Meseta(rng.gen_range(rate.min_meseta, rate.max_meseta)))
  184. },
  185. FixedBoxDropType::Random => self.random_box_drop(map_area, rng),
  186. FixedBoxDropType::Specific(value) => {
  187. let mut buf: [u8; 16] = [0; 16];
  188. buf[0..4].copy_from_slice(&u32::to_be_bytes(value));
  189. ItemDropType::parse_item_from_bytes(buf)
  190. },
  191. }
  192. }
  193. pub fn get_drop<R: Rng>(&self, map_area: &MapArea, object: &MapObject, rng: &mut R) -> Option<ItemDropType> {
  194. match object.object {
  195. MapObjectType::Box | MapObjectType::EnemyBox | MapObjectType::RuinsBox| MapObjectType::RuinsEnemyBox
  196. | MapObjectType::CcaBox => {
  197. self.random_box_drop(map_area, rng)
  198. },
  199. MapObjectType::FixedBox(f) | MapObjectType::EnemyFixedBox(f) | MapObjectType::RuinsFixedBox(f)
  200. | MapObjectType::RuinsEnemyFixedBox(f) | MapObjectType::CcaFixedBox(f) => {
  201. self.fixed_box_drop(f, map_area, rng)
  202. },
  203. _ => None,
  204. }
  205. }
  206. }
  207. #[cfg(test)]
  208. mod test {
  209. use super::*;
  210. use rand::{SeedableRng};
  211. #[test]
  212. fn test_box_drops() {
  213. let mut rng = rand_chacha::ChaCha20Rng::from_seed([23;32]);
  214. let bdt = BoxDropTable::new(Episode::One, Difficulty::Ultimate, SectionID::Skyly);
  215. println!("{:?}", bdt.get_drop(&MapArea::Forest1, &MapObject {object: MapObjectType::Box, map: MapArea::Forest1, dropped_item: false}, &mut rng));
  216. }
  217. }