use super::config::EnvironmentModifiers;
use super::types::*;
use crate::service::Service;
use async_trait::async_trait;
use std::any::Any;
#[derive(Clone)]
pub struct EntropyService;
#[async_trait]
impl Service for EntropyService {
fn name(&self) -> &'static str {
"issun:entropy_service"
}
fn clone_box(&self) -> Box<dyn Service> {
Box::new(self.clone())
}
fn as_any(&self) -> &dyn Any {
self
}
fn as_any_mut(&mut self) -> &mut dyn Any {
self
}
}
impl EntropyService {
pub fn calculate_decay(
base_decay_rate: f32,
material: &MaterialType,
environment: &EnvironmentalExposure,
modifiers: &EnvironmentModifiers,
global_multiplier: f32,
delta_time: f32,
) -> f32 {
let mut total_decay = base_decay_rate * delta_time;
let humidity_factor = modifiers.humidity_factor(material);
total_decay += environment.humidity * humidity_factor * delta_time;
let pollution_factor = modifiers.pollution_factor(material);
total_decay += environment.pollution * pollution_factor * delta_time;
if *material == MaterialType::Organic {
let temp_factor = if environment.temperature > 25.0 {
(environment.temperature - 25.0) / 10.0
} else {
0.0
};
total_decay += temp_factor * delta_time;
}
total_decay * global_multiplier
}
pub fn apply_decay(durability: &mut Durability, decay_amount: f32) -> DurabilityChange {
let old_value = durability.current;
let old_status = durability.status.clone();
durability.current = (durability.current - decay_amount).max(0.0);
durability.update_status();
let status_changed = old_status != durability.status;
let destroyed = durability.is_destroyed();
DurabilityChange {
old_value,
new_value: durability.current,
decay_amount,
status_changed,
destroyed,
}
}
pub fn repair(durability: &mut Durability, repair_amount: f32) -> f32 {
let old_value = durability.current;
durability.current = (durability.current + repair_amount).min(durability.max);
durability.update_status();
durability.current - old_value
}
pub fn default_decay_rate_for_material(material: &MaterialType) -> f32 {
match material {
MaterialType::Organic => 0.01, MaterialType::Electronic => 0.005, MaterialType::Metal => 0.002, MaterialType::Plastic => 0.001, MaterialType::Stone => 0.0001, MaterialType::Custom(_) => 0.01, }
}
pub fn should_destroy(durability: &Durability, auto_destroy: bool) -> bool {
auto_destroy && durability.is_destroyed()
}
}
#[cfg(test)]
mod tests {
use super::super::config::EnvironmentModifiers;
use super::*;
#[test]
fn test_calculate_decay_base() {
let modifiers = EnvironmentModifiers::default();
let environment = EnvironmentalExposure {
humidity: 0.0,
pollution: 0.0,
temperature: 20.0,
sunlight_exposure: 0.0,
};
let decay = EntropyService::calculate_decay(
0.01,
&MaterialType::Metal,
&environment,
&modifiers,
1.0,
1.0,
);
assert_eq!(decay, 0.01);
}
#[test]
fn test_calculate_decay_with_humidity() {
let modifiers = EnvironmentModifiers::default();
let environment = EnvironmentalExposure {
humidity: 1.0, pollution: 0.0,
temperature: 20.0,
sunlight_exposure: 0.0,
};
let decay = EntropyService::calculate_decay(
0.01,
&MaterialType::Organic,
&environment,
&modifiers,
1.0,
1.0,
);
assert_eq!(decay, 0.51);
}
#[test]
fn test_calculate_decay_global_multiplier() {
let modifiers = EnvironmentModifiers::default();
let environment = EnvironmentalExposure::default();
let decay = EntropyService::calculate_decay(
0.01,
&MaterialType::Stone,
&environment,
&modifiers,
2.0, 1.0,
);
assert!(decay > 0.01);
}
#[test]
fn test_apply_decay() {
let mut durability = Durability::new(100.0, 0.01, MaterialType::Metal);
let change = EntropyService::apply_decay(&mut durability, 20.0);
assert_eq!(change.old_value, 100.0);
assert_eq!(change.new_value, 80.0);
assert_eq!(change.decay_amount, 20.0);
assert!(!change.status_changed); assert!(!change.destroyed);
assert_eq!(durability.status, DurabilityStatus::Intact);
}
#[test]
fn test_apply_decay_status_change() {
let mut durability = Durability::new(100.0, 0.01, MaterialType::Metal);
let change = EntropyService::apply_decay(&mut durability, 30.0);
assert_eq!(change.old_value, 100.0);
assert_eq!(change.new_value, 70.0);
assert_eq!(change.decay_amount, 30.0);
assert!(change.status_changed); assert!(!change.destroyed);
assert_eq!(durability.status, DurabilityStatus::Worn);
}
#[test]
fn test_apply_decay_to_zero() {
let mut durability = Durability::new(100.0, 0.01, MaterialType::Metal);
let change = EntropyService::apply_decay(&mut durability, 150.0);
assert_eq!(change.new_value, 0.0);
assert!(change.destroyed);
assert_eq!(durability.status, DurabilityStatus::Destroyed);
}
#[test]
fn test_repair() {
let mut durability = Durability {
current: 50.0,
max: 100.0,
decay_rate: 0.01,
material: MaterialType::Metal,
status: DurabilityStatus::Worn,
};
let repaired = EntropyService::repair(&mut durability, 30.0);
assert_eq!(repaired, 30.0);
assert_eq!(durability.current, 80.0);
assert_eq!(durability.status, DurabilityStatus::Intact);
}
#[test]
fn test_repair_clamped_to_max() {
let mut durability = Durability {
current: 90.0,
max: 100.0,
decay_rate: 0.01,
material: MaterialType::Metal,
status: DurabilityStatus::Intact,
};
let repaired = EntropyService::repair(&mut durability, 30.0);
assert_eq!(repaired, 10.0); assert_eq!(durability.current, 100.0);
}
#[test]
fn test_default_decay_rates() {
assert_eq!(
EntropyService::default_decay_rate_for_material(&MaterialType::Organic),
0.01
);
assert_eq!(
EntropyService::default_decay_rate_for_material(&MaterialType::Stone),
0.0001
);
}
#[test]
fn test_should_destroy() {
let destroyed = Durability {
current: 0.0,
max: 100.0,
decay_rate: 0.01,
material: MaterialType::Metal,
status: DurabilityStatus::Destroyed,
};
assert!(EntropyService::should_destroy(&destroyed, true));
assert!(!EntropyService::should_destroy(&destroyed, false));
let intact = Durability::new(100.0, 0.01, MaterialType::Metal);
assert!(!EntropyService::should_destroy(&intact, true));
}
}