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}