Skip to main content

yog_cli/design/content/
choices.rs

1//! What the editors' pickers offer: the mod's texts, sound groups,
2//! materials, tags and textures.
3
4use std::borrow::Cow;
5use std::path::Path;
6use yog_data::Value;
7use yog_ids::Format;
8
9use crate::design::assets::Resources;
10use crate::design::project::mod_root;
11
12#[derive(yog_data::Schema)]
13pub struct TextureChoice {
14    /// What goes in the content file.
15    pub value: String,
16    /// "Resources", "Resource sets", "Mod assets", "Minecraft blocks", "Minecraft items".
17    pub group: Cow<'static, str>,
18}
19
20/// The `@<Set>.<name>` texts of the mod's `.yres` files — the name/tooltip picker's choices.
21pub fn strings(file: &Path) -> Vec<String> {
22    let mut out = Vec::new();
23    let Some(root) = mod_root(file) else {
24        return out;
25    };
26    for p in crate::files::of(&root.join(yog_ids::project::RESOURCES), Format::Res) {
27        let Some(set) = p.file_stem().and_then(|s| s.to_str()) else {
28            continue;
29        };
30        if set.contains('.') {
31            continue; // a culture variant
32        }
33        let Ok(text) = crate::files::read_source(&p) else {
34            continue;
35        };
36        let Some(doc) = crate::design::data::read_object(&text) else {
37            continue;
38        };
39        for (name, e) in doc.as_map().into_iter().flatten() {
40            let ty = e.get("type").and_then(Value::as_str);
41            if e.as_str().is_some() || ty.is_none_or(|t| t == "string") {
42                out.push(format!("@{set}.{name}"));
43            }
44        }
45    }
46    out.sort();
47    out
48}
49
50/// What a `.ydef` reference offers: the game's built-ins by short name,
51/// then the mod's own definitions of `kind` as `@<kind>:` refs.
52fn definitions(
53    file: &Path,
54    kind: &str,
55    built_ins: impl IntoIterator<Item = String>,
56) -> Vec<String> {
57    let mut out: Vec<String> = built_ins
58        .into_iter()
59        .map(|id| {
60            id.trim_start_matches(&format!(
61                "{}{}",
62                yog_ids::ns::MINECRAFT,
63                yog_ids::sep::NAMESPACE
64            ))
65            .to_string()
66        })
67        .collect();
68    let Some(root) = mod_root(file) else {
69        return out;
70    };
71    let ns = crate::design::project::mod_id(&root).unwrap_or_else(|| yog_ids::ns::MINECRAFT.into());
72    let mut own = Vec::new();
73    for p in crate::files::of(&root.join(yog_ids::project::CONTENT), Format::Def) {
74        let Some(doc) = crate::files::read_source(&p)
75            .ok()
76            .and_then(|t| crate::design::data::read_object(&t))
77        else {
78            continue;
79        };
80        if doc
81            .get(yog_ids::content_field::KIND)
82            .and_then(Value::as_str)
83            != Some(kind)
84        {
85            continue;
86        }
87        if let Some(id) = doc.get(yog_ids::content_field::ID).and_then(Value::as_str) {
88            own.push(format!(
89                "{}{kind}{}{}",
90                yog_ids::sep::TYPED,
91                yog_ids::sep::NAMESPACE,
92                yog_ids::qualify_in(&ns, id)
93            ));
94        }
95    }
96    own.sort();
97    out.extend(own);
98    out
99}
100
101/// What a block's `sound` offers: the game's sound groups, then the mod's.
102pub fn sound_groups(file: &Path) -> Vec<String> {
103    definitions(
104        file,
105        "sound_group",
106        yog_formats::def::SoundGroup::built_ins()
107            .iter()
108            .map(|g| g.id.clone()),
109    )
110}
111
112/// What a tool's `material` offers: the game's, then the mod's.
113pub fn tool_materials(file: &Path) -> Vec<String> {
114    definitions(
115        file,
116        "tool_material",
117        yog_formats::def::ToolMaterial::built_ins()
118            .iter()
119            .map(|m| m.id.clone()),
120    )
121}
122
123/// What an armor piece's `material` offers: the game's, then the mod's.
124pub fn armor_materials(file: &Path) -> Vec<String> {
125    definitions(
126        file,
127        "armor_material",
128        yog_formats::def::ArmorMaterial::built_ins()
129            .iter()
130            .map(|m| m.id.clone()),
131    )
132}
133
134/// What a `tags` list of content in `registry` (`item`, `block`) offers:
135/// the mod's own tags by bare name (as the build last listed them), then
136/// Yog's common tags.
137pub fn tags(file: &Path, registry: &str) -> Vec<String> {
138    let mut out = Vec::new();
139    if let Some(root) = mod_root(file) {
140        let ns = crate::design::project::mod_id(&root).unwrap_or_default();
141        let listed = crate::content::read_tags(&root)
142            .remove(registry)
143            .unwrap_or_default();
144        out.extend(
145            listed
146                .into_keys()
147                .filter_map(|t| t.strip_prefix(&format!("{ns}:")).map(str::to_string)),
148        );
149    }
150    out.extend(
151        yog_formats::tags::yog_tags(registry)
152            .into_iter()
153            .map(|(p, _)| format!("yog:{p}")),
154    );
155    out
156}
157
158/// Everything the texture picker offers.
159pub fn textures(file: &Path) -> Vec<TextureChoice> {
160    let mut out = Vec::new();
161    let root = mod_root(file);
162    if let Some(root) = &root {
163        out.extend(
164            crate::media::textures(root)
165                .into_iter()
166                .map(|t| TextureChoice {
167                    value: format!("{}{}", yog_ids::reference::TEXTURE, t.id),
168                    group: Cow::Borrowed("Mod textures"),
169                }),
170        );
171    }
172    if let Some(v) = Resources::for_mod(root.as_deref()).vanilla {
173        let (items, blocks) = v.texture_names();
174        out.extend(blocks.into_iter().map(|b| TextureChoice {
175            value: format!(
176                "{}{}",
177                yog_ids::reference::TEXTURE,
178                yog_ids::join(
179                    yog_ids::ns::MINECRAFT,
180                    &format!("{}{b}", yog_ids::res::BLOCK_DIR)
181                )
182            ),
183            group: Cow::Borrowed("Minecraft blocks"),
184        }));
185        out.extend(items.into_iter().map(|i| TextureChoice {
186            value: format!(
187                "{}{}",
188                yog_ids::reference::TEXTURE,
189                yog_ids::join(
190                    yog_ids::ns::MINECRAFT,
191                    &format!("{}{i}", yog_ids::res::ITEM_DIR)
192                )
193            ),
194            group: Cow::Borrowed("Minecraft items"),
195        }));
196    }
197    out
198}