prts/headhunt_tool/banner.rs
1use std::collections::HashMap;
2use rand::{Rng, thread_rng};
3use serde::{Serialize, Deserialize};
4use super::toml_banner::TomlBanner;
5use super::banner_rng::Rarity;
6
7#[derive(Debug)]
8pub struct Banner{
9 pub pool: HashMap<u8, Vec<String>>,
10 pub rate_up: HashMap<u8, Vec<String>>,
11 pub rarity: Rarity,
12 pub(super) banner_type: BannerType
13}
14
15#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
16pub enum BannerType {
17 Standard,
18 Limited
19}
20
21#[allow(dead_code)]
22impl Banner {
23 /// Create new banner from `BannerType`
24 /// `BannerType` can be one of the following:
25 /// - `BannerType::Standard`
26 /// - `BannerType::Limited` <br/>
27 /// You have to set operator pool and operator rate up using
28 /// [`set_pool`](#method.set_pool) and [`set_rate_up`](#method.set_rate_up) yourself <br/>
29 /// The only diffirent is `rate_up_rate`. 70% up for 6 star operator in Limited
30 /// and 50% up for 6 star operator in Standard. Both have 50% rate up rate for 5 star operator
31 ///
32 /// # Example
33 /// ```
34 /// use prts::{Banner, BannerType};
35 ///
36 /// let mut banner = Banner::from_banner_type(BannerType::Limited);
37 /// ```
38 pub fn from_banner_type(banner_type: BannerType) -> Self{
39 Self {
40 pool: HashMap::new(),
41 rate_up: HashMap::new(),
42 rarity: Rarity::from_banner_type(banner_type),
43 banner_type
44 }
45 }
46
47 fn from_toml_banner(banner: &TomlBanner) -> Self{
48 let mut pool = HashMap::new();
49 for (k, v) in banner.pool.iter() {
50 pool.insert(k.parse::<u8>().unwrap(), v.to_owned());
51 };
52
53 let mut rate_up = HashMap::new();
54 for (k, v) in banner.rate_up.iter() {
55 rate_up.insert(k.parse::<u8>().unwrap(), v.to_owned());
56 };
57
58 Self {
59 pool, rate_up,
60 banner_type: banner.banner_type,
61 rarity: Rarity::from_banner_type(banner.banner_type)
62 }
63 }
64
65 /// Create new instance of `Banner` by read `.toml` file <br/>
66 /// See
67 /// [sample file](https://github.com/MinkuruDev/arknights_prts/blob/master/data/operators.toml)
68 ///
69 /// # Example
70 /// ```
71 /// use prts::Banner;
72 ///
73 /// let mut banner = Banner::from_file("./data/operators.toml".to_string());
74 /// let (star, opname, is_up) = banner.gacha_operator();
75 /// println!("{} {} {}", is_up, star, opname);
76 /// ```
77 pub fn from_file(file_name: String) -> Self{
78 Self::from_toml_banner(
79 &TomlBanner::from_toml_file(file_name)
80 )
81 }
82
83 /// Set banner pool with specific operator bound to specific star
84 ///
85 /// # Example
86 /// ```
87 /// use prts::{Banner, BannerType};
88 ///
89 /// let mut banner = Banner::from_banner_type(BannerType::Standard);
90 /// // set pool 6 star have 2 operators
91 /// banner.set_pool(6, vec!["Silver Ash".to_string(), "Angelina".to_string()]);
92 /// // set pool 5 star have 2 operators
93 /// banner.set_pool(5, vec!["Andreana".to_string(), "Projekt Red".to_string()]);
94 /// // set pool 4 star have 2 operators
95 /// banner.set_pool(4, vec!["Utage".to_string(), "Myrtle".to_string()]);
96 /// // set pool 3 star have 2 operators
97 /// banner.set_pool(3, vec!["Lava".to_string(), "Hibicus".to_string()]);
98 ///
99 /// // set rate up 6 star have 2 operator
100 /// banner.set_rate_up(6, vec!["Surtr".to_string(), "Skadi".to_string()]);
101 /// // set rate up 5 star have 3 operator
102 /// banner.set_rate_up(5, vec!["Specter".to_string(), "Ptilopsis".to_string(), "Lappland".to_string()]);
103 ///
104 /// let (star, opname, is_up) = banner.gacha_operator();
105 /// println!("{} {} {}", is_up, star, opname);
106 /// ```
107 pub fn set_pool(&mut self, star: u8, operators: Vec<String>) {
108 self.pool.insert(star, operators);
109 }
110
111 /// Set banner rate up operator with specific operator bound to specific star
112 ///
113 /// # Example
114 /// ```
115 /// use prts::{Banner, BannerType};
116 ///
117 /// let mut banner = Banner::from_banner_type(BannerType::Standard);
118 /// // set pool 6 star have 2 operators
119 /// banner.set_pool(6, vec!["Silver Ash".to_string(), "Angelina".to_string()]);
120 /// // set pool 5 star have 2 operators
121 /// banner.set_pool(5, vec!["Andreana".to_string(), "Projekt Red".to_string()]);
122 /// // set pool 4 star have 2 operators
123 /// banner.set_pool(4, vec!["Utage".to_string(), "Myrtle".to_string()]);
124 /// // set pool 3 star have 2 operators
125 /// banner.set_pool(3, vec!["Lava".to_string(), "Hibicus".to_string()]);
126 ///
127 /// // set rate up 6 star have 2 operator
128 /// banner.set_rate_up(6, vec!["Surtr".to_string(), "Skadi".to_string()]);
129 /// // set rate up 5 star have 3 operator
130 /// banner.set_rate_up(5, vec!["Specter".to_string(), "Ptilopsis".to_string(), "Lappland".to_string()]);
131 ///
132 /// let (star, opname, is_up) = banner.gacha_operator();
133 /// println!("{} {} {}", is_up, star, opname);
134 /// ```
135 pub fn set_rate_up(&mut self, star: u8, rateup_operators: Vec<String>){
136 self.rate_up.insert(star, rateup_operators);
137 }
138
139 /// Simulate Arknights headhunt once <br/>
140 /// It have the exact same mechanic in game (or i think that have :)))) ) <br/>
141 /// Mechanism: <br/>
142 /// In a single headhunt, you have the following change to get an operator:
143 /// - 2% to get 6 star operator
144 /// - 8% to get 5 star operator
145 /// - 50% to get 4 star operator
146 /// - 40% to get 3 star operator <br/>
147 /// In the first 10 headhunt in a banner, if you don't get a 5 star or higher star operator
148 /// the 10th headhunt guaranteed a 5 star operator <br/>
149 /// If for 50 headhunt, you don't get a 6 star operator, from the 51st headhunt will increase change
150 /// to get six star operator by 2% (change to get 6 star operator in the 51st headhunt is 4%,
151 /// 52nd is 6%, ...) <br/>
152 /// If the result is 5 star operator or 6 star operator, you will have change to get the rate up operator
153 /// - 50% to get 5 star rate up operator
154 /// - 70% to get 6 star rate up operator in Limited banner
155 /// - 50% to get 6 star rate up operator in Standard banner
156 ///
157 /// Function returns: (star_result, operator_name, is_up)
158 /// - star_result: can be any number in range 3..=6
159 /// - operator_name: name of the operator
160 /// - is_up: the operator is rate up or not
161 ///
162 /// # Example
163 /// ```
164 /// use prts::Banner;
165 ///
166 /// let mut banner = Banner::from_file("./data/operators.toml".to_string());
167 /// let (star, opname, is_up) = banner.gacha_operator();
168 /// println!("{} {} {}", is_up, star, opname);
169 /// ```
170 ///
171 /// # Panics
172 /// May Panik if operator pool or operator rate up is not set
173 ///
174 pub fn gacha_operator(&mut self) -> (u8, String, bool) {
175 let (star_result, is_up) = self.rarity.it_gacha_time();
176 let operators =
177 if is_up {self.rate_up.get(&star_result).unwrap()}
178 else {self.pool.get(&star_result).unwrap()};
179 (
180 star_result,
181 operators.get(thread_rng().gen_range(0..operators.len()))
182 .unwrap().to_owned(),
183 is_up
184 )
185 }
186
187 /// Simulate Arknights headhunt 10 times <br/>
188 ///
189 /// # Example
190 /// ```
191 /// use prts::Banner;
192 ///
193 /// let mut banner = Banner::from_file("./data/operators.toml".to_string());
194 /// let res = banner.gacha_10_times();
195 /// for (star, name, is_up) in &res {
196 /// println!("{} {} {}", is_up, star, name);
197 /// }
198 /// ```
199 ///
200 /// # Panics
201 /// May Panik if operator pool or operator rate up is not set
202 pub fn gacha_10_times(&mut self) -> Vec<(u8, String, bool)> {
203 let mut res = Vec::new();
204 for _ in 0..10 {
205 res.push(self.gacha_operator());
206 }
207 res
208 }
209
210 /// set `non_six_star_count` and `guarantee_five_star` of `Banner`
211 /// can be useful when have many doctor, but need to create only 1 banner
212 ///
213 /// # Example
214 /// ```
215 /// use prts::Banner;
216 ///
217 /// let mut banner = Banner::from_file("./data/operators.toml".to_string());
218 /// let res = banner.gacha_10_times();
219 /// for (star, name, is_up) in &res {
220 /// println!("{} {} {}", is_up, star, name);
221 /// }
222 /// let old_dokutah_infor = (banner.rarity.non_six_star_count, banner.rarity.guarantee_five_star);
223 /// banner.set_dokutah_info(90, 0);
224 /// let res = banner.gacha_10_times();
225 /// for (star, name, is_up) in &res {
226 /// println!("{} {} {}", is_up, star, name);
227 /// }
228 /// ```
229 pub fn set_dokutah_info(&mut self, non_six_star_count: u8, guarantee_five_star: i8) {
230 self.rarity.non_six_star_count = non_six_star_count;
231 self.rarity.guarantee_five_star = guarantee_five_star;
232 }
233}