Skip to main content

steel_registry/advancement/
mod.rs

1use crate::advancement::criterion::AnyCriterion;
2use crate::advancement::display::DisplayInfo;
3use crate::loot_table::LootTableRef;
4use crate::recipe::UntypedRecipeRef;
5use serde::{Deserialize, Serialize};
6use std::borrow::Cow;
7use std::cmp::{Ordering, PartialEq};
8use std::collections::BTreeMap;
9use std::fmt::{Debug, Display, Formatter};
10use std::hash::Hash;
11use std::io::Write;
12use steel_utils::Identifier;
13use steel_utils::codec::VarInt;
14use steel_utils::serial::{PrefixedWrite, WriteTo};
15use steel_utils::translations::CHAT_SQUARE_BRACKETS;
16use text_components::interactivity::HoverEvent;
17use text_components::{Modifier, TextComponent};
18
19pub mod criterion;
20pub mod display;
21pub mod positioner;
22pub mod registry;
23
24#[derive(Default)]
25pub struct Advancement {
26    pub(crate) key: Identifier,
27    pub(crate) parent: Option<Identifier>,
28    pub(crate) criteria: BTreeMap<String, Box<dyn AnyCriterion>>,
29    pub(crate) display: Option<DisplayInfo>,
30    pub(crate) send_telemetry_event: bool,
31    pub(crate) requirements: AdvancementRequirement,
32    pub(crate) rewards: AdvancementRewards,
33}
34
35impl Advancement {
36    #[inline]
37    #[must_use]
38    pub const fn is_root(&self) -> bool {
39        self.parent.is_none()
40    }
41
42    #[must_use]
43    pub fn name(&self) -> TextComponent {
44        self.decorate_name()
45            .unwrap_or(TextComponent::plain(self.key.to_string()))
46    }
47
48    #[must_use]
49    pub fn decorate_name(&self) -> Option<TextComponent> {
50        match &self.display {
51            Some(display) => {
52                let color = display.frame_type.color();
53                let over = display
54                    .title
55                    .clone()
56                    .color(color.clone())
57                    .add_child("\n")
58                    .add_child(display.description.clone());
59                let text = display
60                    .title
61                    .clone()
62                    .hover_event(HoverEvent::show_text(over));
63                Some(TextComponent::translated(CHAT_SQUARE_BRACKETS.message([text])).color(color))
64            }
65            None => None,
66        }
67    }
68
69    #[must_use]
70    pub const fn key(&self) -> &Identifier {
71        &self.key
72    }
73
74    #[must_use]
75    pub fn has_criterion(&self, criterion: &str) -> bool {
76        self.criteria.contains_key(criterion)
77    }
78
79    #[must_use]
80    pub fn get_criterion(&self, criterion: &str) -> Option<&dyn AnyCriterion> {
81        self.criteria.get(criterion).map(Box::as_ref)
82    }
83
84    #[must_use]
85    pub fn criteria_ids(&self) -> impl IntoIterator<Item = &str> {
86        self.criteria.keys().map(String::as_str)
87    }
88
89    #[must_use]
90    pub const fn requirements(&self) -> &AdvancementRequirement {
91        &self.requirements
92    }
93
94    #[must_use]
95    pub const fn display(&self) -> Option<&DisplayInfo> {
96        self.display.as_ref()
97    }
98
99    #[must_use]
100    pub const fn rewards(&self) -> &AdvancementRewards {
101        &self.rewards
102    }
103
104    #[must_use]
105    pub const fn send_telemetry_event(&self) -> bool {
106        self.send_telemetry_event
107    }
108
109    #[must_use]
110    pub const fn parent(&self) -> Option<&Identifier> {
111        self.parent.as_ref()
112    }
113}
114
115impl Hash for Advancement {
116    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
117        self.key.hash(state);
118    }
119}
120
121impl PartialEq for Advancement {
122    fn eq(&self, other: &Self) -> bool {
123        self.key == other.key
124    }
125}
126impl Eq for Advancement {}
127
128impl Display for Advancement {
129    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
130        write!(f, "{}", self.key)
131    }
132}
133
134impl Debug for Advancement {
135    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
136        write!(f, "{}", self.key)
137    }
138}
139
140impl PartialOrd for Advancement {
141    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
142        Some(self.cmp(other))
143    }
144}
145
146impl Ord for Advancement {
147    fn cmp(&self, other: &Self) -> Ordering {
148        self.key.cmp(&other.key)
149    }
150}
151
152impl WriteTo for Advancement {
153    fn write(&self, writer: &mut impl Write) -> std::io::Result<()> {
154        self.key.write(writer)?;
155        self.parent.write(writer)?;
156        self.display.write(writer)?;
157        self.requirements.write(writer)?;
158        self.send_telemetry_event.write(writer)?;
159        Ok(())
160    }
161}
162
163#[derive(Debug, Clone, Default, Deserialize, Serialize)]
164pub struct AdvancementRequirement {
165    pub requirements: Vec<Vec<Cow<'static, str>>>,
166}
167
168impl AdvancementRequirement {
169    #[must_use]
170    pub fn names(&self) -> Vec<Cow<'static, str>> {
171        self.requirements.iter().flatten().cloned().collect()
172    }
173
174    /// test if the requirements is complete
175    pub fn test(&self, predicate: impl Fn(&str) -> bool) -> bool {
176        if self.requirements.is_empty() {
177            false
178        } else {
179            for requirement in &self.requirements {
180                if !Self::any_match(requirement, &predicate) {
181                    return false;
182                }
183            }
184            true
185        }
186    }
187
188    /// check if any test pass
189    fn any_match(requirements: &Vec<Cow<'static, str>>, predicate: impl Fn(&str) -> bool) -> bool {
190        for requirement in requirements {
191            if predicate(requirement) {
192                return true;
193            }
194        }
195        false
196    }
197}
198
199impl WriteTo for AdvancementRequirement {
200    fn write(&self, writer: &mut impl Write) -> std::io::Result<()> {
201        VarInt(self.requirements.len() as i32).write(writer)?;
202        for requirement in &self.requirements {
203            VarInt(requirement.len() as i32).write(writer)?;
204            for req in requirement {
205                req.to_string().write_prefixed::<VarInt>(writer)?;
206            }
207        }
208        Ok(())
209    }
210}
211
212#[derive(Debug, Clone, Default)]
213pub struct AdvancementRewards {
214    pub experience: i32,
215    pub loots: Vec<LootTableRef>,
216    pub recipes: Vec<UntypedRecipeRef>,
217    pub function: Option<Identifier>,
218}
219
220#[derive(Debug, Clone, Serialize, Deserialize)]
221pub struct AdvancementProgressData {
222    pub id: Identifier,
223    pub progress: Vec<Criteria>,
224}
225
226impl WriteTo for AdvancementProgressData {
227    fn write(&self, writer: &mut impl Write) -> std::io::Result<()> {
228        self.id.write(writer)?;
229        self.progress.write(writer)?;
230        Ok(())
231    }
232}
233
234#[derive(Debug, Clone, Serialize, Deserialize)]
235pub struct Criteria {
236    pub criterion_id: Cow<'static, str>,
237    pub achieve_date: Option<i64>,
238}
239
240impl WriteTo for Criteria {
241    fn write(&self, writer: &mut impl Write) -> std::io::Result<()> {
242        self.criterion_id
243            .to_string()
244            .write_prefixed::<VarInt>(writer)?;
245        self.achieve_date.write(writer)?;
246        Ok(())
247    }
248}