steel_registry/advancement/
mod.rs1use 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 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 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}