oxygengine_overworld/components/
personal_quests.rs1use 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 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 {}