use bevy::prelude::*;
use issun_core::mechanics::reputation::{ReputationConfig, ReputationInput};
use issun_core::mechanics::{ExecutionHint, Mechanic};
use std::marker::PhantomData;
use super::systems::{log_reputation_events, reputation_system};
use super::types::{
ReputationChangeRequested, ReputationConfigResource, ReputationEventWrapper, ReputationValue,
};
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone, Copy)]
pub struct ReputationSequentialSet;
pub struct ReputationPluginV2<M>
where
M: Mechanic<
Config = ReputationConfig,
State = issun_core::mechanics::reputation::ReputationState,
Input = ReputationInput,
Event = issun_core::mechanics::reputation::ReputationEvent,
>,
{
pub config: ReputationConfig,
_phantom: PhantomData<M>,
}
impl<M> Default for ReputationPluginV2<M>
where
M: Mechanic<
Config = ReputationConfig,
State = issun_core::mechanics::reputation::ReputationState,
Input = ReputationInput,
Event = issun_core::mechanics::reputation::ReputationEvent,
>,
{
fn default() -> Self {
Self {
config: ReputationConfig::default(),
_phantom: PhantomData,
}
}
}
impl<M> ReputationPluginV2<M>
where
M: Mechanic<
Config = ReputationConfig,
State = issun_core::mechanics::reputation::ReputationState,
Input = ReputationInput,
Event = issun_core::mechanics::reputation::ReputationEvent,
>,
{
pub fn with_config(config: ReputationConfig) -> Self {
Self {
config,
_phantom: PhantomData,
}
}
}
impl<M> Plugin for ReputationPluginV2<M>
where
M: Mechanic<
Config = ReputationConfig,
State = issun_core::mechanics::reputation::ReputationState,
Input = ReputationInput,
Event = issun_core::mechanics::reputation::ReputationEvent,
> + Send
+ Sync
+ 'static,
{
fn build(&self, app: &mut App) {
app.insert_resource(ReputationConfigResource::new(self.config.clone()));
app.register_type::<ReputationConfigResource>();
app.register_type::<ReputationValue>();
app.add_message::<ReputationChangeRequested>();
app.add_message::<ReputationEventWrapper>();
if M::Execution::PARALLEL_SAFE {
app.add_systems(Update, (reputation_system::<M>, log_reputation_events));
info!(
"ReputationPluginV2 initialized with mechanic: {} (parallel-safe)",
std::any::type_name::<M>()
);
} else {
if let Some(schedule) = M::Execution::PREFERRED_SCHEDULE {
info!(
"ReputationPluginV2 initialized with mechanic: {} (sequential, schedule: {})",
std::any::type_name::<M>(),
schedule
);
warn!(
"PREFERRED_SCHEDULE '{}' specified but not yet implemented, using Update",
schedule
);
app.add_systems(
Update,
reputation_system::<M>.in_set(ReputationSequentialSet),
);
} else {
info!(
"ReputationPluginV2 initialized with mechanic: {} (sequential)",
std::any::type_name::<M>()
);
app.add_systems(
Update,
reputation_system::<M>.in_set(ReputationSequentialSet),
);
}
app.add_systems(Update, log_reputation_events);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use issun_core::mechanics::reputation::prelude::*;
type TestReputation = ReputationMechanic;
#[test]
fn test_plugin_builds() {
let mut app = App::new();
app.add_plugins(bevy::MinimalPlugins);
app.add_plugins(ReputationPluginV2::<TestReputation>::default());
assert!(app.world().contains_resource::<ReputationConfigResource>());
}
#[test]
fn test_plugin_with_custom_config() {
let mut app = App::new();
app.add_plugins(bevy::MinimalPlugins);
let config = ReputationConfig {
min: -100.0,
max: 100.0,
decay_rate: 0.95,
};
app.add_plugins(ReputationPluginV2::<TestReputation>::with_config(
config.clone(),
));
let resource = app.world().resource::<ReputationConfigResource>();
assert_eq!(resource.config.min, -100.0);
assert_eq!(resource.config.max, 100.0);
assert_eq!(resource.config.decay_rate, 0.95);
}
#[test]
fn test_full_reputation_flow() {
let mut app = App::new();
app.add_plugins(bevy::MinimalPlugins);
app.add_plugins(ReputationPluginV2::<TestReputation>::default());
let entity = app
.world_mut()
.spawn((ReputationValue::new(50.0), Name::new("Knight")))
.id();
app.world_mut().write_message(ReputationChangeRequested {
entity,
delta: 10.0,
elapsed_time: 0,
});
app.update();
let reputation = app.world().get::<ReputationValue>(entity).unwrap();
assert_eq!(reputation.value, 60.0); }
#[test]
fn test_reputation_clamping() {
let mut app = App::new();
app.add_plugins(bevy::MinimalPlugins);
app.add_plugins(ReputationPluginV2::<TestReputation>::default());
let entity = app
.world_mut()
.spawn((ReputationValue::new(95.0), Name::new("Hero")))
.id();
app.world_mut().write_message(ReputationChangeRequested {
entity,
delta: 20.0,
elapsed_time: 0,
});
app.update();
let reputation = app.world().get::<ReputationValue>(entity).unwrap();
assert_eq!(reputation.value, 100.0);
}
#[test]
fn test_skill_progression_plugin() {
type SkillProgression = ReputationMechanic<LogarithmicChange, NoDecay, HardClamp>;
let mut app = App::new();
app.add_plugins(bevy::MinimalPlugins);
app.add_plugins(ReputationPluginV2::<SkillProgression>::default());
let entity = app
.world_mut()
.spawn((ReputationValue::new(50.0), Name::new("Apprentice")))
.id();
app.world_mut().write_message(ReputationChangeRequested {
entity,
delta: 10.0,
elapsed_time: 0,
});
app.update();
let reputation = app.world().get::<ReputationValue>(entity).unwrap();
assert!((reputation.value - 55.0).abs() < 0.1); }
#[test]
fn test_durability_system_plugin() {
type DurabilitySystem = ReputationMechanic<LinearChange, LinearDecay, ZeroClamp>;
let config = ReputationConfig {
min: 0.0,
max: 100.0,
decay_rate: 0.9, };
let mut app = App::new();
app.add_plugins(bevy::MinimalPlugins);
app.add_plugins(ReputationPluginV2::<DurabilitySystem>::with_config(config));
let entity = app
.world_mut()
.spawn((ReputationValue::new(100.0), Name::new("Iron Sword")))
.id();
app.world_mut().write_message(ReputationChangeRequested {
entity,
delta: 0.0,
elapsed_time: 1,
});
app.update();
let reputation = app.world().get::<ReputationValue>(entity).unwrap();
assert_eq!(reputation.value, 90.0); }
}