use crate::context::{ResourceContext, ServiceContext};
use crate::event::EventBus;
use crate::system::System;
use async_trait::async_trait;
use std::any::Any;
use std::sync::Arc;
use super::config::LootConfig;
use super::events::*;
use super::hook::LootHook;
use super::service::LootService;
#[derive(Clone)]
pub struct LootSystem {
hook: Arc<dyn LootHook>,
}
impl LootSystem {
pub fn new(hook: Arc<dyn LootHook>) -> Self {
Self { hook }
}
pub async fn process_events(
&mut self,
services: &ServiceContext,
resources: &mut ResourceContext,
) {
self.process_loot_generate_requests(services, resources)
.await;
self.process_rarity_roll_requests(services, resources).await;
}
async fn process_loot_generate_requests(
&mut self,
services: &ServiceContext,
resources: &mut ResourceContext,
) {
let requests = {
if let Some(mut bus) = resources.get_mut::<EventBus>().await {
let reader = bus.reader::<LootGenerateRequested>();
reader.iter().cloned().collect::<Vec<_>>()
} else {
Vec::new()
}
};
for request in requests {
let global_multiplier = {
if let Some(config) = resources.get::<LootConfig>().await {
config.global_drop_multiplier
} else {
1.0
}
};
let final_drop_rate = {
let resources_ref = resources as &ResourceContext;
self.hook
.modify_drop_chance(&request.source_id, request.drop_rate, resources_ref)
.await
};
let effective_rate = (final_drop_rate * global_multiplier).min(1.0);
let should_drop = {
if let Some(_service) = services.get_as::<LootService>("loot_service") {
let drop_config = super::types::DropConfig::new(effective_rate, 1.0);
let mut rng = rand::thread_rng();
LootService::should_drop(&drop_config, &mut rng)
} else {
rand::random::<f32>() < effective_rate
}
};
if !should_drop {
if let Some(mut bus) = resources.get_mut::<EventBus>().await {
bus.publish(LootNotGeneratedEvent {
source_id: request.source_id.clone(),
});
}
continue;
}
let rarity = {
if let Some(_service) = services.get_as::<LootService>("loot_service") {
let mut rng = rand::thread_rng();
LootService::select_rarity(&mut rng)
} else {
super::types::Rarity::Common
}
};
let items = {
let resources_ref = resources as &ResourceContext;
self.hook
.generate_loot(&request.source_id, rarity, resources_ref)
.await
};
self.hook
.on_loot_generated(&request.source_id, &items, rarity, resources)
.await;
if let Some(mut bus) = resources.get_mut::<EventBus>().await {
bus.publish(LootGeneratedEvent {
source_id: request.source_id.clone(),
items,
rarity,
});
}
}
}
async fn process_rarity_roll_requests(
&mut self,
services: &ServiceContext,
resources: &mut ResourceContext,
) {
let requests = {
if let Some(mut bus) = resources.get_mut::<EventBus>().await {
let reader = bus.reader::<RarityRollRequested>();
reader.iter().cloned().collect::<Vec<_>>()
} else {
Vec::new()
}
};
for request in requests {
let rarity = {
if let Some(_service) = services.get_as::<LootService>("loot_service") {
let mut rng = rand::thread_rng();
LootService::select_rarity(&mut rng)
} else {
super::types::Rarity::Common
}
};
let items = {
let resources_ref = resources as &ResourceContext;
self.hook
.generate_loot(&request.source_id, rarity, resources_ref)
.await
};
self.hook
.on_loot_generated(&request.source_id, &items, rarity, resources)
.await;
if let Some(mut bus) = resources.get_mut::<EventBus>().await {
bus.publish(LootGeneratedEvent {
source_id: request.source_id.clone(),
items,
rarity,
});
}
}
}
}
#[async_trait]
impl System for LootSystem {
fn name(&self) -> &'static str {
"loot_system"
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}