use rand::seq::{IndexedRandom, SliceRandom};
use crate::state::State;
mod pack_item;
mod rarity;
use rarity::{RarityUpgrader, RngUpgrader};
pub use pack_item::PackItem;
pub use rarity::{PackTemplate, Rarity};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Pack {
Basic,
Premium, }
pub const ALL_PACKS: &[Pack] = &[Pack::Basic, Pack::Premium];
pub const BASIC_PACK_TEMPLATE: PackTemplate = PackTemplate(&[
Rarity::Common,
Rarity::Common,
Rarity::Common,
Rarity::Common,
Rarity::Rare,
]);
pub const PREMIUM_PACK_TEMPLATE: PackTemplate =
PackTemplate(&[Rarity::Rare, Rarity::Rare, Rarity::Epic]);
impl Pack {
pub fn cost(&self) -> u64 {
match self {
Pack::Basic => 1_000,
Pack::Premium => 10_000,
}
}
pub fn name(&self) -> &'static str {
match self {
Pack::Basic => "Basic Pack",
Pack::Premium => "Premium Pack",
}
}
pub fn description(&self) -> &'static str {
match self {
Pack::Basic => "A pack with mostly common items and at least one of higher-rarity.",
Pack::Premium => "A pack with only three items but of higher-rarity.",
}
}
fn template(&self) -> PackTemplate {
match self {
Pack::Basic => BASIC_PACK_TEMPLATE,
Pack::Premium => PREMIUM_PACK_TEMPLATE,
}
}
pub fn open(&self, state: &mut State) -> Vec<PackItem> {
self.open_with_upgrader(state, &mut RngUpgrader::new(rand::rng()))
}
fn open_with_upgrader(
&self,
state: &mut State,
upgrader: &mut impl RarityUpgrader,
) -> Vec<PackItem> {
let template = self.template();
let mut upgraded_rarities = template.upgrade(upgrader);
upgraded_rarities.shuffle(&mut rand::rng());
upgraded_rarities
.into_iter()
.map(|rarity| {
let item = Self::resolve_item(rarity, state);
item.apply(state);
item
})
.collect()
}
fn resolve_item(rarity: Rarity, state: &State) -> PackItem {
let mut rng = rand::rng();
let items = PackItem::available_items(rarity, state);
*items
.choose(&mut rng)
.expect("there should always be at least one unlockable item")
}
}
#[cfg(test)]
mod tests {
use super::*;
struct MockUpgrader<'a> {
will_upgrade: &'a [Rarity],
}
impl<'a> MockUpgrader<'a> {
fn new(will_upgrade: &'a [Rarity]) -> Self {
Self { will_upgrade }
}
}
impl<'a> RarityUpgrader for MockUpgrader<'a> {
fn should_upgrade(&mut self, rarity: &Rarity) -> bool {
self.will_upgrade.contains(rarity)
}
}
#[test]
fn no_upgrades_to_template() {
let template = PackTemplate(&[
Rarity::Common,
Rarity::Rare,
Rarity::Epic,
Rarity::Legendary,
]);
let mut upgrader = MockUpgrader::new(&[]);
let upgraded = template.upgrade(&mut upgrader);
assert_eq!(
upgraded,
&[
Rarity::Common,
Rarity::Rare,
Rarity::Epic,
Rarity::Legendary,
]
);
}
#[test]
fn test_upgrade_only_rares() {
let template = PackTemplate(&[
Rarity::Common,
Rarity::Rare,
Rarity::Epic,
Rarity::Legendary,
]);
let mut upgrader = MockUpgrader::new(&[Rarity::Rare]);
let upgraded = template.upgrade(&mut upgrader);
assert_eq!(
&upgraded,
&[
Rarity::Common,
Rarity::Epic,
Rarity::Epic,
Rarity::Legendary,
]
);
}
#[test]
fn test_full_upgrade() {
let template = PackTemplate(&[
Rarity::Common,
Rarity::Rare,
Rarity::Epic,
Rarity::Legendary,
]);
let mut upgrader = MockUpgrader::new(&[
Rarity::Common,
Rarity::Rare,
Rarity::Epic,
Rarity::Legendary,
]);
let upgraded = template.upgrade(&mut upgrader);
assert_eq!(
upgraded,
&[
Rarity::Legendary,
Rarity::Legendary,
Rarity::Legendary,
Rarity::Legendary,
]
);
}
#[test]
fn test_resolve_items_basic() {
let state = State::default();
for rarity in [
Rarity::Common,
Rarity::Rare,
Rarity::Epic,
Rarity::Legendary,
] {
let item = Pack::resolve_item(rarity, &state);
assert_eq!(item.rarity(), rarity);
}
}
}