2021-02-09 20:42:09 +00:00
|
|
|
use crate::gacha::{Gacha, Rarity, Recruitment};
|
|
|
|
use crate::i18n::{I18nString, Language};
|
|
|
|
use crate::student::Student;
|
|
|
|
use rand::distributions::{Distribution, WeightedIndex};
|
|
|
|
use rand::Rng;
|
|
|
|
use std::convert::{TryFrom, TryInto};
|
|
|
|
|
|
|
|
/// Used to Construct a Banner
|
2021-02-09 04:33:35 +00:00
|
|
|
#[derive(Debug, Default)]
|
|
|
|
pub struct BannerBuilder {
|
|
|
|
name: I18nString,
|
2021-02-09 20:42:09 +00:00
|
|
|
gacha: Option<Gacha>,
|
2021-02-09 04:33:35 +00:00
|
|
|
sparkable: Option<Vec<Student>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl BannerBuilder {
|
2021-02-09 20:42:09 +00:00
|
|
|
/// Creates a new instance of a BannerBuilder
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
/// * `jpn_name` - The name of the Banner as seen in Blue Archive
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
/// ```
|
2021-02-09 20:48:19 +00:00
|
|
|
/// # use bluearch_recruitment::banner::BannerBuilder;
|
2021-02-09 20:42:09 +00:00
|
|
|
/// let banner_builder = BannerBuilder::new("ピックアップ募集");
|
|
|
|
/// ```
|
2021-02-09 04:33:35 +00:00
|
|
|
pub fn new(jpn_name: &str) -> Self {
|
|
|
|
Self {
|
|
|
|
name: I18nString::new(jpn_name),
|
|
|
|
..Default::default()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-09 20:42:09 +00:00
|
|
|
/// Adds a Translation to the internal I18nString
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
/// * `language` - The target language.
|
|
|
|
/// * `name` - The name of the Banner in the target language.
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
/// ```
|
2021-02-09 20:48:19 +00:00
|
|
|
/// # use bluearch_recruitment::banner::BannerBuilder;
|
|
|
|
/// # use bluearch_recruitment::i18n::Language;
|
2021-02-09 20:42:09 +00:00
|
|
|
/// let banner_builder = BannerBuilder::new("ピックアップ募集")
|
|
|
|
/// .with_name_translation(Language::English, "Focus Recruitment");
|
|
|
|
/// ```
|
2021-02-09 04:33:35 +00:00
|
|
|
pub fn with_name_translation(mut self, language: Language, name: &str) -> Self {
|
|
|
|
self.name.update(language, name);
|
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2021-02-09 20:42:09 +00:00
|
|
|
/// Adds an instance of a Gacha struct
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
/// * `gacha` - An instance of a Gacha struct
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
/// ```
|
2021-02-09 20:48:19 +00:00
|
|
|
/// # use bluearch_recruitment::gacha::GachaBuilder;
|
|
|
|
/// # use bluearch_recruitment::banner::BannerBuilder;
|
2021-02-09 20:42:09 +00:00
|
|
|
/// let gacha = GachaBuilder::default()
|
|
|
|
/// .with_pool(Vec::new())
|
|
|
|
/// .finish().unwrap();
|
|
|
|
///
|
|
|
|
/// let banner_builder = BannerBuilder::new("ピックアップ募集")
|
|
|
|
/// .with_gacha(&gacha);
|
|
|
|
/// ```
|
|
|
|
pub fn with_gacha(self, gacha: &Gacha) -> Self {
|
2021-02-09 04:33:35 +00:00
|
|
|
Self {
|
2021-02-09 20:42:09 +00:00
|
|
|
gacha: Some(gacha.to_owned()),
|
2021-02-09 04:33:35 +00:00
|
|
|
..self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-09 20:42:09 +00:00
|
|
|
/// Adds a vector containing all sparkable students in the current Banner
|
|
|
|
///
|
|
|
|
/// # Arguments
|
|
|
|
/// * `students` - A Vector of Students which are sparkable
|
|
|
|
///
|
|
|
|
/// # Examples
|
|
|
|
/// ```
|
2021-02-09 20:48:19 +00:00
|
|
|
/// # use bluearch_recruitment::student::Student;
|
|
|
|
/// # use bluearch_recruitment::banner::BannerBuilder;
|
|
|
|
/// let students = Vec::new();
|
|
|
|
/// let banner_builder = BannerBuilder::new("ピックアップ募集")
|
|
|
|
/// .with_sparkable_students(&students);
|
|
|
|
/// ```
|
2021-02-09 20:42:09 +00:00
|
|
|
pub fn with_sparkable_students(self, students: &[Student]) -> Self {
|
2021-02-09 04:33:35 +00:00
|
|
|
Self {
|
2021-02-09 20:42:09 +00:00
|
|
|
sparkable: Some(students.to_vec()),
|
2021-02-09 04:33:35 +00:00
|
|
|
..self
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn finish(self) -> Option<Banner> {
|
|
|
|
Some(Banner {
|
|
|
|
name: self.name,
|
2021-02-09 20:42:09 +00:00
|
|
|
gacha: self.gacha?,
|
|
|
|
sparkable: self.sparkable,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
enum StudentType {
|
|
|
|
One = 1, // One Star
|
|
|
|
Two, // Two Stars
|
|
|
|
Three, // Three Stars
|
|
|
|
Priority, // Rate-up Student (Presumably 3*)
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<Rarity> for StudentType {
|
|
|
|
fn from(rarity: Rarity) -> Self {
|
|
|
|
match rarity {
|
|
|
|
Rarity::One => Self::One,
|
|
|
|
Rarity::Two => Self::Two,
|
|
|
|
Rarity::Three => Self::Three,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TryFrom<StudentType> for Rarity {
|
|
|
|
type Error = &'static str;
|
|
|
|
|
|
|
|
fn try_from(value: StudentType) -> Result<Self, Self::Error> {
|
|
|
|
Ok(match value {
|
|
|
|
StudentType::One => Self::One,
|
|
|
|
StudentType::Two => Self::Two,
|
|
|
|
StudentType::Three => Self::Three,
|
|
|
|
StudentType::Priority => return Err("Can not convert from Priority to Rarity"),
|
2021-02-09 04:33:35 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Banner {
|
|
|
|
pub name: I18nString,
|
2021-02-09 20:42:09 +00:00
|
|
|
gacha: Gacha,
|
|
|
|
sparkable: Option<Vec<Student>>,
|
2021-02-09 04:33:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Banner {
|
2021-02-09 20:42:09 +00:00
|
|
|
fn get_random_student(&self) -> Student {
|
2021-02-09 04:33:35 +00:00
|
|
|
let mut rng = rand::thread_rng();
|
2021-02-09 20:42:09 +00:00
|
|
|
let priority_rate = self.gacha.priority.as_ref().map_or(0, |tuple| tuple.1);
|
|
|
|
let three_star_rate = self.gacha.get_rate(Rarity::Three) - priority_rate;
|
2021-02-09 04:33:35 +00:00
|
|
|
|
2021-02-09 20:42:09 +00:00
|
|
|
let items: [(StudentType, usize); 4] = [
|
|
|
|
(StudentType::One, self.gacha.get_rate(Rarity::One)),
|
|
|
|
(StudentType::Two, self.gacha.get_rate(Rarity::Two)),
|
|
|
|
(StudentType::Three, three_star_rate),
|
|
|
|
(StudentType::Priority, priority_rate),
|
|
|
|
];
|
2021-02-09 04:33:35 +00:00
|
|
|
|
2021-02-09 20:42:09 +00:00
|
|
|
let dist = WeightedIndex::new(items.iter().map(|item| item.1)).unwrap();
|
|
|
|
let students = &self.gacha.pool;
|
2021-02-09 04:33:35 +00:00
|
|
|
|
2021-02-09 20:42:09 +00:00
|
|
|
match items[dist.sample(&mut rng)] {
|
|
|
|
(StudentType::Priority, _) => {
|
|
|
|
let priority_students = &self.gacha.priority.as_ref().unwrap().0;
|
2021-02-09 04:33:35 +00:00
|
|
|
|
2021-02-09 20:42:09 +00:00
|
|
|
let index: usize = rng.gen_range(0..priority_students.len());
|
|
|
|
priority_students[index].clone()
|
|
|
|
}
|
|
|
|
(rarity, _) => {
|
|
|
|
let students: Vec<&Student> = students
|
|
|
|
.iter()
|
|
|
|
.filter(|student| student.rarity == rarity.try_into().unwrap())
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let index: usize = rng.gen_range(0..students.len());
|
|
|
|
students[index].clone()
|
|
|
|
}
|
2021-02-09 04:33:35 +00:00
|
|
|
}
|
|
|
|
}
|
2021-02-09 20:42:09 +00:00
|
|
|
|
|
|
|
fn get_random_student_of_rarity(&self, rarity: Rarity) -> Student {
|
|
|
|
let students = &self.gacha.pool;
|
|
|
|
let two_star_students: Vec<&Student> = students
|
|
|
|
.iter()
|
|
|
|
.filter(|student| student.rarity == rarity)
|
|
|
|
.collect();
|
|
|
|
|
|
|
|
let index = rand::thread_rng().gen_range(0..two_star_students.len());
|
|
|
|
two_star_students[index].clone()
|
|
|
|
}
|
2021-02-09 04:33:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Recruitment for Banner {
|
|
|
|
fn roll(&self) -> Student {
|
2021-02-09 20:42:09 +00:00
|
|
|
self.get_random_student()
|
2021-02-09 04:33:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn roll10(&self) -> [Student; 10] {
|
|
|
|
let mut students: [Student; 10] = vec![Default::default(); 10].try_into().unwrap();
|
|
|
|
|
|
|
|
// Fill students with 10 random students
|
|
|
|
for student in students.iter_mut() {
|
2021-02-09 20:42:09 +00:00
|
|
|
*student = self.get_random_student()
|
2021-02-09 04:33:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let two_star_present = students.iter().any(|student| student.rarity == Rarity::Two);
|
|
|
|
|
|
|
|
if !two_star_present {
|
|
|
|
if students[students.len() - 1].rarity != Rarity::Three {
|
2021-02-09 20:42:09 +00:00
|
|
|
students[students.len() - 1] = self.get_random_student_of_rarity(Rarity::Two);
|
2021-02-09 04:33:35 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
students
|
|
|
|
}
|
|
|
|
}
|