konnektoren_core/controller/
game_xp_plugin.rs

1use super::{ControllerPlugin, ControllerPluginError, GameControllerTrait};
2use crate::challenges::ChallengeResult;
3use crate::challenges::Performance;
4use crate::commands::{ChallengeCommand, Command, CommandType};
5use crate::controller::ControllerError;
6use std::sync::Arc;
7
8pub struct GameXpPlugin;
9
10impl GameXpPlugin {
11    fn update_game_xp(
12        game_controller: Arc<dyn GameControllerTrait>,
13        challenge_result: &ChallengeResult,
14    ) -> Result<(), ControllerError> {
15        {
16            let mut game_state = game_controller
17                .game_state()
18                .lock()
19                .map_err(|_| ControllerError::StateLock)?;
20
21            game_state.challenge.challenge_result = challenge_result.clone();
22            let performance = game_state.challenge.performance(challenge_result);
23            let xp_reward = performance / 10;
24
25            game_state.game.xp += xp_reward;
26        }
27
28        game_controller.save_game_state()?;
29        Ok(())
30    }
31}
32
33impl ControllerPlugin for GameXpPlugin {
34    fn name(&self) -> &str {
35        "GameXpPlugin"
36    }
37
38    fn init(&self) -> Result<(), ControllerPluginError> {
39        Ok(())
40    }
41
42    fn load(
43        &self,
44        game_controller: Arc<dyn GameControllerTrait>,
45    ) -> Result<(), ControllerPluginError> {
46        let game_controller_clone = game_controller.clone();
47        game_controller
48            .command_bus()
49            .subscribe(CommandType::Challenge, move |command| {
50                if let Command::Challenge(ChallengeCommand::Finish(Some(result))) = command {
51                    if let Err(e) = Self::update_game_xp(game_controller_clone.clone(), &result) {
52                        log::error!("Error updating game XP: {:?}", e);
53                    }
54                }
55            });
56
57        Ok(())
58    }
59
60    fn unload(
61        &self,
62        _game_controller: Arc<dyn GameControllerTrait>,
63    ) -> Result<(), ControllerPluginError> {
64        Ok(())
65    }
66}
67
68#[cfg(test)]
69mod tests {
70    use super::*;
71    use crate::challenges::{Challenge, ChallengeConfig, ChallengeResult, ChallengeType};
72    use crate::controller::game_controller::MockGameControllerTrait;
73    use std::sync::{Arc, Mutex};
74
75    #[test]
76    fn test_update_game_xp_increases_xp() {
77        let mut mock_controller = MockGameControllerTrait::new();
78
79        // Setup a challenge with 100% performance
80        let mut challenge = Challenge::new(&ChallengeType::default(), &ChallengeConfig::default());
81        challenge.challenge_result = ChallengeResult::default();
82
83        let mut game_state = crate::game::GameState::default();
84        game_state.challenge = challenge.clone();
85        game_state.game.xp = 0;
86
87        let game_state_mutex = Mutex::new(game_state);
88        mock_controller
89            .expect_game_state()
90            .return_const(Arc::new(game_state_mutex));
91        mock_controller
92            .expect_save_game_state()
93            .returning(|| Ok(()));
94
95        let result = ChallengeResult::default();
96        let res = GameXpPlugin::update_game_xp(Arc::new(mock_controller), &result);
97        assert!(res.is_ok());
98        // You could check that XP increased, but with the mock it's not persisted.
99    }
100}