use super::types::{DropConfig, Rarity};
use rand::Rng;
#[derive(Debug, Clone, issun_macros::Service)]
#[service(name = "loot_service")]
pub struct LootService;
impl LootService {
pub fn new() -> Self {
Self
}
pub fn should_drop(config: &DropConfig, rng: &mut impl Rng) -> bool {
rng.gen_bool(config.final_rate() as f64)
}
pub fn select_rarity(rng: &mut impl Rng) -> Rarity {
let rarities = Rarity::all();
let total_weight: f32 = rarities.iter().map(|r| r.drop_weight()).sum();
let mut roll = rng.gen_range(0.0..total_weight);
for rarity in rarities {
roll -= rarity.drop_weight();
if roll <= 0.0 {
return rarity;
}
}
Rarity::Common
}
pub fn calculate_drop_count(count: usize, config: &DropConfig, rng: &mut impl Rng) -> usize {
(0..count)
.filter(|_| Self::should_drop(config, rng))
.count()
}
pub fn generate_rarities(count: usize, rng: &mut impl Rng) -> Vec<Rarity> {
(0..count).map(|_| Self::select_rarity(rng)).collect()
}
}
impl Default for LootService {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::service::Service;
use rand::rngs::StdRng;
use rand::SeedableRng;
#[test]
fn test_should_drop_deterministic() {
let config = DropConfig::new(1.0, 1.0); let mut rng = StdRng::seed_from_u64(42);
assert!(LootService::should_drop(&config, &mut rng));
}
#[test]
fn test_select_rarity() {
let mut rng = StdRng::seed_from_u64(42);
let rarity = LootService::select_rarity(&mut rng);
assert!(Rarity::all().contains(&rarity));
}
#[test]
fn test_calculate_drop_count() {
let config = DropConfig::new(1.0, 1.0); let mut rng = StdRng::seed_from_u64(42);
let count = LootService::calculate_drop_count(5, &config, &mut rng);
assert_eq!(count, 5); }
#[test]
fn test_generate_rarities() {
let mut rng = StdRng::seed_from_u64(42);
let rarities = LootService::generate_rarities(10, &mut rng);
assert_eq!(rarities.len(), 10);
for rarity in rarities {
assert!(Rarity::all().contains(&rarity));
}
}
#[test]
fn test_service_trait() {
let service = LootService::new();
assert_eq!(service.name(), "loot_service");
}
}