Skip to main content

yog_book/
lib.rs

1//! yog-book — in-game book/documentation system for Yog mods (Patchouli-like).
2//! Full replacement: books, categories, entries, page types, macros, textures.
3
4use yog_registry::ItemDef;
5
6// ── Macros ───────────────────────────────────────────────────────────────────
7
8/// A macro substitution (e.g. `$(thing)` → red color span).
9#[derive(Debug, Clone)]
10pub struct BookMacro(pub String, pub String);
11
12// ── Page types ───────────────────────────────────────────────────────────────
13
14/// A single page variant inside a book entry.
15#[derive(Debug, Clone)]
16pub enum BookPage {
17    /// Plain formatted text (Markdown-like, with macro support).
18    Text {
19        text: String,
20    },
21    /// Display an item outlined (tooltip on hover).
22    Spotlight {
23        item: ItemDef,
24        title: Option<String>,
25        text: Option<String>,
26    },
27    /// Crafting recipe display (autorenders 3×3 grid).
28    Crafting {
29        recipe_id: String,
30        text: Option<String>,
31    },
32    /// Smelting recipe display.
33    Smelting {
34        recipe_id: String,
35        text: Option<String>,
36    },
37    /// Image overlay page.
38    Image {
39        texture: String,
40        title: Option<String>,
41        text: Option<String>,
42        border: bool,
43    },
44    /// Entity display page (renders a living entity in a box).
45    Entity {
46        entity_type: String,
47        name: Option<String>,
48        text: Option<String>,
49    },
50    /// Link to another entry (like Patchouli's relations).
51    Relations {
52        entries: Vec<String>,
53        text: Option<String>,
54    },
55    /// Empty separator.
56    Empty,
57}
58
59// ── Category ─────────────────────────────────────────────────────────────────
60
61/// Represents a book category tab (e.g. "Basics", "Patterns").
62#[derive(Debug, Clone)]
63pub struct BookCategory {
64    pub id: String,
65    pub name: String,
66    pub description: Option<String>,
67    /// Texture for the category icon (path like "hexcasting:textures/item/...")
68    pub icon: Option<String>,
69}
70
71// ── Entry ────────────────────────────────────────────────────────────────────
72
73/// One entry in a book (like a "page" in the TOC sidebar).
74#[derive(Debug, Clone)]
75pub struct BookEntry {
76    pub id: String,
77    pub name: String,
78    pub category: String,
79    pub pages: Vec<BookPage>,
80    /// Entry icon (item id).
81    pub icon: Option<String>,
82    /// If true, hides from the book (used for unlocks).
83    pub secret: bool,
84    /// Sort priority (lower = first).
85    pub priority: i32,
86}
87
88// ── Book ─────────────────────────────────────────────────────────────────────
89
90/// The top-level book definition — replaces `patchouli_books/<id>/book.json`.
91#[derive(Debug, Clone)]
92pub struct Book {
93    pub id: String,
94    pub name: String,
95    pub nameplate_color: String,
96    pub landing_text: String,
97    pub author: Option<String>,
98    pub book_texture: String,
99    pub filler_texture: String,
100    pub model: String,
101    pub categories: Vec<BookCategory>,
102    pub entries: Vec<BookEntry>,
103    pub macros: Vec<BookMacro>,
104    pub use_resource_pack: bool,
105    pub show_progress: bool,
106    pub i18n: bool,
107    pub creative_tab: Option<String>,
108}
109
110impl Book {
111    pub fn new(id: impl Into<String>, name: impl Into<String>) -> Self {
112        Self {
113            id: id.into(),
114            name: name.into(),
115            nameplate_color: "000000".into(),
116            landing_text: String::new(),
117            author: None,
118            book_texture: "yog:textures/gui/book.png".into(),
119            filler_texture: "yog:textures/gui/book_filler.png".into(),
120            model: "minecraft:book".into(),
121            categories: Vec::new(),
122            entries: Vec::new(),
123            macros: Vec::new(),
124            use_resource_pack: false,
125            show_progress: true,
126            i18n: false,
127            creative_tab: None,
128        }
129    }
130
131    pub fn author(mut self, author: impl Into<String>) -> Self {
132        self.author = Some(author.into());
133        self
134    }
135
136    pub fn book_texture(mut self, tex: impl Into<String>) -> Self {
137        self.book_texture = tex.into();
138        self
139    }
140
141    pub fn filler_texture(mut self, tex: impl Into<String>) -> Self {
142        self.filler_texture = tex.into();
143        self
144    }
145
146    pub fn nameplate(mut self, color: impl Into<String>) -> Self {
147        self.nameplate_color = color.into();
148        self
149    }
150
151    pub fn landing_text(mut self, text: impl Into<String>) -> Self {
152        self.landing_text = text.into();
153        self
154    }
155
156    pub fn model(mut self, model: impl Into<String>) -> Self {
157        self.model = model.into();
158        self
159    }
160
161    pub fn creative_tab(mut self, tab: impl Into<String>) -> Self {
162        self.creative_tab = Some(tab.into());
163        self
164    }
165
166    pub fn show_progress(mut self, show: bool) -> Self {
167        self.show_progress = show;
168        self
169    }
170
171    pub fn i18n(mut self, val: bool) -> Self {
172        self.i18n = val;
173        self
174    }
175
176    pub fn use_resource_pack(mut self, val: bool) -> Self {
177        self.use_resource_pack = val;
178        self
179    }
180
181    pub fn add_macro(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
182        self.macros.push(BookMacro(key.into(), value.into()));
183        self
184    }
185
186    pub fn add_category(mut self, category: BookCategory) -> Self {
187        self.categories.push(category);
188        self
189    }
190
191    pub fn add_entry(mut self, entry: BookEntry) -> Self {
192        self.entries.push(entry);
193        self
194    }
195}
196
197impl Default for Book {
198    fn default() -> Self {
199        Self::new("yog:default", "Unknown Book")
200    }
201}
202
203// ── Registry ─────────────────────────────────────────────────────────────────
204
205/// Global registry for all in-game books.
206#[derive(Debug, Default)]
207pub struct BookRegistry {
208    books: std::collections::HashMap<String, Book>,
209}
210
211impl BookRegistry {
212    pub fn register(&mut self, book: Book) {
213        self.books.insert(book.id.clone(), book);
214    }
215
216    pub fn get(&self, id: &str) -> Option<&Book> {
217        self.books.get(id)
218    }
219
220    pub fn all(&self) -> impl Iterator<Item = &Book> {
221        self.books.values()
222    }
223}
224
225// ── Builder helpers ──────────────────────────────────────────────────────────
226
227pub fn text_page(text: impl Into<String>) -> BookPage {
228    BookPage::Text { text: text.into() }
229}
230
231pub fn spotlight_page(item: ItemDef) -> BookPage {
232    BookPage::Spotlight { item, title: None, text: None }
233}
234
235pub fn crafting_page(recipe_id: impl Into<String>) -> BookPage {
236    BookPage::Crafting { recipe_id: recipe_id.into(), text: None }
237}
238
239pub fn crafting_page_with_text(recipe_id: impl Into<String>, text: impl Into<String>) -> BookPage {
240    BookPage::Crafting { recipe_id: recipe_id.into(), text: Some(text.into()) }
241}
242
243pub fn smelting_page(recipe_id: impl Into<String>) -> BookPage {
244    BookPage::Smelting { recipe_id: recipe_id.into(), text: None }
245}
246
247pub fn image_page(texture: impl Into<String>) -> BookPage {
248    BookPage::Image { texture: texture.into(), title: None, text: None, border: true }
249}
250
251pub fn entity_page(entity_type: impl Into<String>) -> BookPage {
252    BookPage::Entity { entity_type: entity_type.into(), name: None, text: None }
253}
254
255pub fn relations_page(entries: Vec<String>) -> BookPage {
256    BookPage::Relations { entries, text: None }
257}