oxygengine_overworld/components/
personal_quests.rs

1use crate::resources::{market::Currency, quests::*};
2use oxygengine_core::{
3    prefab::{Prefab, PrefabComponent},
4    Scalar,
5};
6use serde::{Deserialize, Serialize};
7use std::collections::{HashMap, HashSet};
8
9#[derive(Debug, Clone)]
10pub enum PersonalQuestsError {
11    QuestAlreadyTaken(QuestId),
12    QuestAlreadyCompleted(QuestId),
13    QuestDoesNotExists(QuestId),
14    QuestHasNoObjectives(QuestId),
15    ObjectiveDoesNotExists(ObjectiveId),
16}
17
18#[derive(Debug, Default, Clone, Serialize, Deserialize)]
19pub struct PersonalQuests {
20    /// {quest id: {objective id: progress score}}
21    active_quests: HashMap<QuestId, HashMap<ObjectiveId, Scalar>>,
22    completed_quests: HashSet<QuestId>,
23}
24
25impl PersonalQuests {
26    pub fn take<Q, B, V>(
27        &mut self,
28        id: QuestId,
29        database: &QuestsDatabase<Q, B, V>,
30    ) -> Result<(), PersonalQuestsError>
31    where
32        Q: std::fmt::Debug + Clone + Send + Sync,
33        B: std::fmt::Debug + Clone + Send + Sync,
34        V: Currency + std::fmt::Debug + Clone + Send + Sync,
35    {
36        if self.active_quests.contains_key(&id) {
37            return Err(PersonalQuestsError::QuestAlreadyTaken(id));
38        }
39        if self.completed_quests.contains(&id) {
40            return Err(PersonalQuestsError::QuestAlreadyCompleted(id));
41        }
42        let quest = match database.quest(id) {
43            Some(quest) => quest,
44            None => return Err(PersonalQuestsError::QuestDoesNotExists(id)),
45        };
46        if quest.objectives.is_empty() {
47            return Err(PersonalQuestsError::QuestHasNoObjectives(id));
48        }
49        let objectives = quest.objectives.iter().map(|id| (*id, 0.0)).collect();
50        self.active_quests.insert(id, objectives);
51        Ok(())
52    }
53
54    pub fn leave(&mut self, id: QuestId) -> Result<(), PersonalQuestsError> {
55        if self.active_quests.remove(&id).is_some() {
56            Ok(())
57        } else {
58            Err(PersonalQuestsError::QuestDoesNotExists(id))
59        }
60    }
61
62    pub fn active_quests(&self) -> impl Iterator<Item = QuestId> + '_ {
63        self.active_quests.keys().copied()
64    }
65
66    pub fn completed_quests(&self) -> impl Iterator<Item = QuestId> + '_ {
67        self.completed_quests.iter().copied()
68    }
69
70    pub fn progress(&self, id: QuestId) -> Result<Scalar, PersonalQuestsError> {
71        match self.active_quests.get(&id) {
72            Some(objectives) => {
73                Ok(objectives.values().fold(0.0, |a, v| a + *v) / objectives.len() as Scalar)
74            }
75            None => Err(PersonalQuestsError::QuestDoesNotExists(id)),
76        }
77    }
78
79    pub fn progress_objectives(
80        &self,
81        id: QuestId,
82    ) -> Result<impl Iterator<Item = (ObjectiveId, Scalar)> + '_, PersonalQuestsError> {
83        match self.active_quests.get(&id) {
84            Some(objectives) => Ok(objectives.iter().map(|(id, score)| (*id, *score))),
85            None => Err(PersonalQuestsError::QuestDoesNotExists(id)),
86        }
87    }
88
89    pub fn complete(
90        &mut self,
91        id: ObjectiveId,
92        score: Option<Scalar>,
93    ) -> Result<(), PersonalQuestsError> {
94        for objectives in self.active_quests.values_mut() {
95            if let Some(s) = objectives.get_mut(&id) {
96                *s = (*s + score.unwrap_or(1.0)).max(0.0).min(1.0);
97            }
98        }
99        let to_remove = self
100            .active_quests
101            .iter()
102            .filter(|(_, objectives)| objectives.values().all(|score| *score >= 1.0))
103            .map(|(id, _)| *id)
104            .collect::<Vec<_>>();
105        for id in to_remove {
106            self.active_quests.remove(&id);
107            self.completed_quests.insert(id);
108        }
109        Ok(())
110    }
111
112    pub fn reset(&mut self, id: QuestId) -> Result<(), PersonalQuestsError> {
113        match self.active_quests.get_mut(&id) {
114            Some(objectives) => {
115                for score in objectives.values_mut() {
116                    *score = 0.0;
117                }
118                Ok(())
119            }
120            None => Err(PersonalQuestsError::QuestDoesNotExists(id)),
121        }
122    }
123
124    pub fn transfer(
125        &mut self,
126        receiver: &mut Self,
127        id: QuestId,
128    ) -> Result<(), PersonalQuestsError> {
129        if receiver.active_quests.contains_key(&id) {
130            return Err(PersonalQuestsError::QuestAlreadyTaken(id));
131        }
132        if receiver.completed_quests.contains(&id) {
133            return Err(PersonalQuestsError::QuestAlreadyCompleted(id));
134        }
135        if !self.active_quests.contains_key(&id) {
136            return Err(PersonalQuestsError::QuestDoesNotExists(id));
137        }
138        let objectives = self.active_quests.remove(&id).unwrap();
139        receiver.active_quests.insert(id, objectives);
140        Ok(())
141    }
142}
143
144impl Prefab for PersonalQuests {}
145impl PrefabComponent for PersonalQuests {}