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.

369 lines
9.8 KiB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
  1. use std::fs::File;
  2. use std::io::Read;
  3. use std::path::PathBuf;
  4. use std::convert::TryInto;
  5. use serde::Deserialize;
  6. use rand::{Rng, SeedableRng};
  7. use rand::distributions::{WeightedIndex, Distribution};
  8. use entity::item::ItemDetail;
  9. use entity::item::armor::{Armor, ArmorType};
  10. use entity::item::shield::{Shield, ShieldType};
  11. use entity::item::unit::{Unit, UnitType};
  12. use crate::ShopItem;
  13. use stats::items::{ARMOR_STATS, SHIELD_STATS, UNIT_STATS};
  14. // #[derive(Debug)]
  15. // pub enum ArmorShopItem {
  16. // Frame(ArmorType, usize), // slots
  17. // Barrier(ShieldType),
  18. // Unit(UnitType),
  19. // }
  20. #[derive(Debug)]
  21. pub enum ArmorShopItem {
  22. Frame(Armor), // slots
  23. Barrier(Shield),
  24. Unit(Unit),
  25. }
  26. const ARMOR_MULTIPLIER: f32 = 0.799_999_95;
  27. const SHIELD_MULTIPLIER: f32 = 1.5;
  28. const UNIT_MULTIPLIER: f32 = 1000.0;
  29. // TODO: reduce the number of type casts?
  30. impl ShopItem for ArmorShopItem {
  31. fn price(&self) -> usize {
  32. match self {
  33. ArmorShopItem::Frame(frame) => {
  34. ARMOR_STATS.get(&frame.armor)
  35. .map(|frame_stats| {
  36. let mut price = (frame_stats.dfp + frame_stats.evp + frame.dfp as i32 + frame.evp as i32) as f32;
  37. price *= price;
  38. price /= ARMOR_MULTIPLIER;
  39. price += 70.0 * (frame_stats.level_req + 1) as f32;
  40. price += 70.0 * (frame_stats.level_req + 1) as f32 * frame.slots as f32;
  41. price as usize
  42. })
  43. .unwrap_or(0xFFFF)
  44. },
  45. ArmorShopItem::Barrier(barrier) => {
  46. SHIELD_STATS.get(&barrier.shield)
  47. .map(|barrier_stats| {
  48. let mut price = (barrier_stats.dfp + barrier_stats.evp + barrier.dfp as i32 + barrier.evp as i32) as f32;
  49. price *= price;
  50. price /= SHIELD_MULTIPLIER;
  51. price += 70.0 * (barrier_stats.level_req + 1) as f32;
  52. price as usize
  53. })
  54. .unwrap_or(0xFFFF)
  55. },
  56. ArmorShopItem::Unit(unit) => {
  57. UNIT_STATS.get(&unit.unit)
  58. .map(|unit_stats| {
  59. ((unit_stats.stars as f32 + unit.modifier_stars() as f32) * UNIT_MULTIPLIER) as usize
  60. })
  61. .unwrap_or(0xFFFF)
  62. }
  63. }
  64. }
  65. fn as_bytes(&self) -> [u8; 12] {
  66. self.as_item().as_client_bytes()[0..12].try_into().unwrap()
  67. }
  68. fn as_item(&self) -> ItemDetail {
  69. match self {
  70. ArmorShopItem::Frame(frame) => {
  71. ItemDetail::Armor(Armor {
  72. armor: frame.armor,
  73. dfp: 0,
  74. evp: 0,
  75. slots: frame.slots,
  76. })
  77. },
  78. ArmorShopItem::Barrier(barrier) => {
  79. ItemDetail::Shield(Shield {
  80. shield: barrier.shield,
  81. dfp: 0,
  82. evp: 0,
  83. })
  84. },
  85. ArmorShopItem::Unit(unit) => {
  86. ItemDetail::Unit(Unit {
  87. unit: unit.unit,
  88. modifier: None,
  89. })
  90. },
  91. }
  92. }
  93. }
  94. impl From<&Armor> for ArmorShopItem {
  95. fn from(armor: &Armor) -> ArmorShopItem {
  96. ArmorShopItem::Frame(*armor)
  97. }
  98. }
  99. impl From<&Shield> for ArmorShopItem {
  100. fn from(shield: &Shield) -> ArmorShopItem {
  101. ArmorShopItem::Barrier(*shield)
  102. }
  103. }
  104. impl From<&Unit> for ArmorShopItem {
  105. fn from(unit: &Unit) -> ArmorShopItem {
  106. ArmorShopItem::Unit(*unit)
  107. }
  108. }
  109. #[derive(Debug, Deserialize, Clone)]
  110. struct FrameTierItem {
  111. item: ArmorType,
  112. probability: usize,
  113. }
  114. #[derive(Debug, Deserialize, Clone)]
  115. struct FrameTier {
  116. level: usize,
  117. item: Vec<FrameTierItem>,
  118. }
  119. #[derive(Debug, Deserialize)]
  120. struct SlotRate {
  121. slot: usize,
  122. probability: usize,
  123. }
  124. #[derive(Debug, Deserialize)]
  125. struct FrameTable {
  126. frame: Vec<FrameTier>,
  127. slot_rate: Vec<SlotRate>,
  128. }
  129. #[derive(Debug, Deserialize, Clone)]
  130. struct BarrierTierItem {
  131. item: ShieldType,
  132. probability: usize,
  133. }
  134. #[derive(Debug, Deserialize, Clone)]
  135. struct BarrierTier {
  136. level: usize,
  137. item: Vec<BarrierTierItem>,
  138. }
  139. #[derive(Debug, Deserialize)]
  140. struct BarrierTable {
  141. barrier: Vec<BarrierTier>,
  142. }
  143. #[derive(Debug, Deserialize, Clone)]
  144. struct UnitTierItem {
  145. item: UnitType,
  146. probability: usize,
  147. }
  148. #[derive(Debug, Deserialize, Clone)]
  149. struct UnitTier {
  150. level: usize,
  151. item: Vec<UnitTierItem>,
  152. }
  153. #[derive(Debug, Deserialize)]
  154. struct UnitTable {
  155. unit: Vec<UnitTier>,
  156. }
  157. fn load_frame_table() -> FrameTable {
  158. let path = PathBuf::from("data/shops/frame.toml");
  159. let mut f = File::open(path).unwrap();
  160. let mut s = String::new();
  161. f.read_to_string(&mut s).unwrap();
  162. let table: FrameTable = toml::from_str(s.as_str()).unwrap();
  163. table
  164. }
  165. fn load_barrier_table() -> BarrierTable {
  166. let path = PathBuf::from("data/shops/barrier.toml");
  167. let mut f = File::open(path).unwrap();
  168. let mut s = String::new();
  169. f.read_to_string(&mut s).unwrap();
  170. let table: BarrierTable = toml::from_str(s.as_str()).unwrap();
  171. table
  172. }
  173. fn load_unit_table() -> UnitTable {
  174. let path = PathBuf::from("data/shops/unit.toml");
  175. let mut f = File::open(path).unwrap();
  176. let mut s = String::new();
  177. f.read_to_string(&mut s).unwrap();
  178. let table: UnitTable = toml::from_str(s.as_str()).unwrap();
  179. table
  180. }
  181. fn number_of_frames_to_generate(character_level: usize) -> usize {
  182. if character_level <= 10 {
  183. 4
  184. }
  185. else if character_level <= 25 {
  186. 6
  187. }
  188. else if character_level <= 42 {
  189. 7
  190. }
  191. else {
  192. 8
  193. }
  194. }
  195. fn number_of_barriers_to_generate(character_level: usize) -> usize {
  196. if character_level <= 10 {
  197. 4
  198. }
  199. else if character_level <= 25 {
  200. 5
  201. }
  202. else if character_level <= 42 {
  203. 6
  204. }
  205. else {
  206. 7
  207. }
  208. }
  209. fn number_of_units_to_generate(character_level: usize) -> usize {
  210. if character_level <= 10 {
  211. 0
  212. }
  213. else if character_level <= 25 {
  214. 3
  215. }
  216. else if character_level <= 42 {
  217. 5
  218. }
  219. else {
  220. 6
  221. }
  222. }
  223. #[derive(Debug)]
  224. pub struct ArmorShop<R: Rng + SeedableRng> {
  225. frame: FrameTable,
  226. barrier: BarrierTable,
  227. unit: UnitTable,
  228. rng: R,
  229. }
  230. impl<R: Rng + SeedableRng> Default for ArmorShop<R> {
  231. fn default() -> ArmorShop<R> {
  232. ArmorShop {
  233. frame: load_frame_table(),
  234. barrier: load_barrier_table(),
  235. unit: load_unit_table(),
  236. rng: R::from_entropy(),
  237. }
  238. }
  239. }
  240. impl<R: Rng + SeedableRng> ArmorShop<R> {
  241. fn generate_frame_list(&mut self, character_level: usize) -> Vec<ArmorShopItem> {
  242. let tier = self.frame.frame.iter()
  243. .filter(|t| t.level <= character_level)
  244. .last()
  245. .cloned()
  246. .unwrap();
  247. let frame_choice = WeightedIndex::new(tier.item.iter().map(|f| f.probability)).unwrap();
  248. let slot_choice = WeightedIndex::new(self.frame.slot_rate.iter().map(|sr| sr.probability)).unwrap();
  249. (0..number_of_frames_to_generate(character_level))
  250. .map(|_| {
  251. let frame_detail = tier.item.get(frame_choice.sample(&mut self.rng)).unwrap();
  252. let slot = self.frame.slot_rate.get(slot_choice.sample(&mut self.rng)).unwrap();
  253. ArmorShopItem::Frame(Armor {
  254. armor: frame_detail.item,
  255. dfp: 0,
  256. evp: 0,
  257. slots: slot.slot as u8,
  258. })
  259. })
  260. .collect()
  261. }
  262. fn generate_barrier_list(&mut self, character_level: usize) -> Vec<ArmorShopItem> {
  263. let tier = self.barrier.barrier.iter()
  264. .filter(|t| t.level <= character_level)
  265. .last()
  266. .cloned()
  267. .unwrap();
  268. let barrier_choice = WeightedIndex::new(tier.item.iter().map(|b| b.probability)).unwrap();
  269. (0..number_of_barriers_to_generate(character_level))
  270. .map(|_| {
  271. let barrier_detail = tier.item.get(barrier_choice.sample(&mut self.rng)).unwrap();
  272. ArmorShopItem::Barrier(Shield {
  273. shield: barrier_detail.item,
  274. dfp: 0,
  275. evp: 0,
  276. })
  277. })
  278. .collect()
  279. }
  280. fn generate_unit_list(&mut self, character_level: usize) -> Vec<ArmorShopItem> {
  281. self.unit.unit.iter()
  282. .filter(|t| t.level <= character_level)
  283. .last()
  284. .cloned()
  285. .map(|tier| {
  286. let unit_choice = WeightedIndex::new(tier.item.iter().map(|u| u.probability)).unwrap();
  287. (0..number_of_units_to_generate(character_level))
  288. .map(|_| {
  289. let unit_detail = tier.item.get(unit_choice.sample(&mut self.rng)).unwrap();
  290. ArmorShopItem::Unit(Unit {
  291. unit: unit_detail.item,
  292. modifier: None,
  293. })
  294. })
  295. .collect()
  296. })
  297. .unwrap_or_else(Vec::new)
  298. }
  299. pub fn generate_armor_list(&mut self, character_level: usize) -> Vec<ArmorShopItem> {
  300. self.generate_frame_list(character_level).into_iter()
  301. .chain(self.generate_barrier_list(character_level))
  302. .chain(self.generate_unit_list(character_level))
  303. .collect()
  304. }
  305. }
  306. #[cfg(test)]
  307. mod test {
  308. use super::*;
  309. #[test]
  310. fn test_loading_tool_shop() {
  311. ArmorShop::<rand_chacha::ChaCha20Rng>::default();
  312. }
  313. #[test]
  314. fn test_generating_some_armor() {
  315. let mut fs = ArmorShop::<rand_chacha::ChaCha20Rng>::default();
  316. for i in 0..200 {
  317. fs.generate_armor_list(i);
  318. }
  319. }
  320. }